Skip to content

Harden ZookeeperDistributedQueue against insecure deserialization (CWE-502)#42

Open
devin-ai-integration[bot] wants to merge 1 commit into
develop-7.0.xfrom
devin/1780958208-fix-zk-queue-insecure-deserialization
Open

Harden ZookeeperDistributedQueue against insecure deserialization (CWE-502)#42
devin-ai-integration[bot] wants to merge 1 commit into
develop-7.0.xfrom
devin/1780958208-fix-zk-queue-insecure-deserialization

Conversation

@devin-ai-integration

@devin-ai-integration devin-ai-integration Bot commented Jun 8, 2026

Copy link
Copy Markdown

A Brief Overview

ZookeeperDistributedQueue.deserialize() called ObjectInputStream.readObject() directly on bytes read from Zookeeper with no type filtering. Since queue/config data in Zookeeper must be treated as untrusted, this is an insecure deserialization vulnerability (CWE-502) that can lead to RCE via gadget chains.

This PR restricts deserialization to an explicit allow list of classes, rejecting everything else.

What changed

  • deserialize() now builds the stream via a new overridable createObjectInputStream(InputStream) which returns an AllowListObjectInputStream.
  • AllowListObjectInputStream extends ObjectInputStream and overrides:
    • resolveClass(...): unwraps array/primitive types and throws InvalidClassException for any class not on the allow list.
    • resolveProxyClass(...): always rejects dynamic proxy deserialization.
  • The allow list (DEFAULT_DESERIALIZATION_ALLOW_LIST) covers exactly the types actually placed on this queue:
    • JDK payload types: java.lang., java.util., java.time., java.math.
    • Queue payloads: org.apache.solr.common. (SolrInputDocument/SolrInputField) and org.broadleafcommerce. (SolrUpdateCommand subtypes); config payloads are Integer.
  • Extensibility preserved: subclasses placing custom Serializable types on the queue can override getDeserializationAllowList() (e.g. return a superset of the default) instead of weakening the base list.

Pseudocode of the core check:

protected Class<?> resolveClass(ObjectStreamClass desc) {
    String type = stripArrayAndPrimitiveWrappers(desc.getName());
    if (type.length() > 1 && !isAllowed(type)) {   // matched by exact name or prefix
        throw new InvalidClassException(desc.getName(), "CWE-502 protection");
    }
    return super.resolveClass(desc);
}

Tests

Added ZookeeperDistributedQueueDeserializationTest (4 tests, all passing):

  • allow-listed JDK types (Integer, String, ArrayList) round-trip,
  • allow-listed array types (String[]) round-trip,
  • allow-listed Solr command (IncrementalUpdateCommand containing a SolrInputDocument) round-trips,
  • a non-allow-listed but Serializable class (java.io.File) is rejected with InvalidClassException.

mvn -pl core/broadleaf-framework -am test -Dtest=ZookeeperDistributedQueueDeserializationTest → Tests run: 4, Failures: 0, Errors: 0.

Labels

  • Security
  • Severity: critical
  • Status: ready-for-code-review

Additional context

No behavior change for legitimate queue traffic; only the set of resolvable classes during deserialization is constrained.

Link to Devin session: https://app.devin.ai/sessions/a5ed15631bc24c738f47afca59e7e5b3
Requested by: @Colhodm


Devin Review

Status Commit
🟢 Reviewed 083f957
Open in Devin Review (Staging)

…n (CWE-502)

Restrict ObjectInputStream.readObject() in deserialize() to an allow list of
permitted classes via AllowListObjectInputStream, rejecting non-allow-listed
classes and dynamic proxies. Adds unit tests covering allowed and blocked types.

Co-Authored-By: Arjun Mishra <arjunsaxmishra@gmail.com>
@devin-ai-integration

Copy link
Copy Markdown
Author

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment, CI, and merge conflict monitoring

@staging-devin-ai-integration staging-devin-ai-integration Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 1 potential issue.

Open in Devin Review (Staging)
Debug

Playground

Comment on lines +92 to +99
public static final Set<String> DEFAULT_DESERIALIZATION_ALLOW_LIST = Collections.unmodifiableSet(Set.of(
"java.lang.",
"java.util.",
"java.time.",
"java.math.",
"org.apache.solr.common.",
"org.broadleafcommerce."
));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚩 Broad java.util. prefix permits large surface area of JDK classes*

The java.util. prefix in the allow list permits ALL classes in java.util and its subpackages (e.g., java.util.concurrent.*, java.util.logging.*). While gadget chains typically require non-JDK sink classes that ARE blocked, some known deserialization gadgets leverage java.util.PriorityQueue or java.util.Comparator implementations as intermediate steps. In this deployment context (Zookeeper data), the risk is mitigated because an attacker would need to control ZK data AND find a gadget chain using only the allowed prefixes. Nonetheless, narrowing java.util. to specific classes (e.g., java.util.ArrayList, java.util.LinkedHashMap, java.util.CollSer) would reduce the attack surface.

Open in Devin Review (Staging)

Was this helpful? React with 👍 or 👎 to provide feedback.

Debug

Playground

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant