Apache Commons logo

Apache Commons Threat Model version 0.1.0

Introduction

This page amends the Apache Commons Security page. A condensed version is available in the threat model summary.

Security researchers should use this document (Markdown). Developers should use the threat model summary (Markdown).

For information about reporting or asking questions about security, please see the Apache Commons Security page.

Please note that binary patches are never provided. If you need to apply a source code patch, use the building instructions for the component version that you are using.

If you need help building a component, or other help following the instructions to mitigate the known vulnerabilities listed here, please send your questions to the public user mailing list.

Scope and intended use

Apache Commons is a family of small, focused, reusable Java libraries. Each component is added to an application's classpath and invoked in-process through its public Java API. Most components ship no daemons, no network services, and no long-running processes; a component runs inside the host application's JVM, on data the host handed it.

Here “public Java API” means the API elements whose accessibility (JLS 6.6.1) makes them reachable by an unrelated caller: roughly, public members of public types and protected members exposed to subclasses. Elements that are documented as internal or private but remain reachable for purely technical reasons are not part of the public API.

The notable exception is Commons Daemon, which ships native launchers (Unix jsvc, Windows procrun) that run a JVM as a system service managing host-supplied user code. Its profile diverges from the pure-library baseline; this model calls the divergence out where it matters below.

Two kinds of component

Commons components fall into two kinds, and this model keeps them distinct:

  • Decorators of JDK functionality. The JDK-parity principle applies directly: their behavior follows that of the JDK features they expose.
  • Low-level building blocks of additional functionality (parsing, scripting, configuration, server bootstrap, and so on). Unless a component specifies otherwise, these are not meant to be exposed directly to untrusted data. In general they hand raw data back to the caller without any sanitization. For example, an application that uses Compress to extract an archive to the file system is responsible for preventing symbolic-link and path-traversal slips; the raw data is provided as-is.

User types

This model distinguishes two types of user:

  • Trusted users. Application developers, who put a component on the classpath, write the calling code, and wire its input and output, and operators or administrators, who supply runtime configuration where a component reads it (configuration files, Daemon service definitions, connection-pool settings, upload limits). They have unrestricted access to the features of the component and of the environment it runs in; an attacker who controls either role has won at a layer below Commons.
  • Untrusted users. All other users, in particular whoever authors the content a host application may pass to a component: archives, images, CSV files, multipart request bodies, templates. It is generally unsafe to pass their data to Commons APIs. In some cases the host can validate or sanitize the data before passing it to Commons; if this is not possible, unless otherwise specified, it is not possible to securely process this data with Commons libraries. The exceptions are spelled out per component: the narrow default-configuration promise (see “Assumptions about inputs”), the “secure parsing” guarantee where a component claims it, and FileUpload, whose input is untrusted network data by construction.

The “Recurring untrusted-input surface” column of the family table below identifies, per component group, the data that untrusted users typically author.

Caller trust and the JDK-parity principle

A component is intended to be called in-process, through its public Java API, by a host JVM application, on input a trusted user supplied, validated, or sanitized: the Commons libraries are low-level libraries designed to work with input that is either trusted or validated/sanitized by the application using the library. At its API a component sees a single actor, the in-process caller; it cannot tell which user type originated a given byte, so keeping untrusted content away from an API that is not documented to accept it is the trusted users' responsibility. If a host wraps a component in a service, the service (not Commons) owns the network trust boundary.

The JDK-parity principle is the family's central test for whether a report describes a vulnerability. Calls to the Commons API come with the same caveat as calls to the JDK: they will usually do what the caller asks. A component that reads the file, opens the connection, or instantiates the class its arguments name is behaving like its JDK counterpart, not exhibiting a vulnerability. Correspondingly, do not report a behavior as a Commons component's vulnerability if the same behavior would be considered legitimate for the JDK.

Component family table

Components are grouped by the recurring untrusted-input surface they expose, because that surface, not the component name, determines the threat profile. Every group is in model.

Family group Representative components Touches outside the process? Recurring untrusted-input surface
Pure computation / data structures Lang, Collections, Math, Numbers, Primitives, Statistics, Text (core) No None beyond CPU and memory of the input given to it
Parsers / format readers Compress, CSV, Configuration, Digester, DBCP/Pool (configuration), Imaging, Codec, CLI, BCEL, RDF, XML Reads byte streams, files, or text the host passes in; performs external I/O where the parsed format requires it Malformed or adversarial archives, configuration files, images, CSV, class files, command lines
Class-instantiating configuration / scripting blocks BeanUtils, Configuration, Digester No (instantiates classes named in a text configuration) Class names in a text configuration, as used for server bootstrapping; the input must be trusted
Interpolation evaluators Text (StringSubstitutor), Configuration (interpolation) Can reach out via lookups Interpolation strings that reach a substitutor with active outbound lookups
Script / command execution engines JEXL, JXPath, Jelly (evaluate scripts and expressions), Exec (runs OS commands) Yes: evaluate caller-supplied scripts or spawn child processes The script, expression, or command itself; executing it is the feature
Upload / streaming input handlers FileUpload Reads multipart streams the host feeds it; may spool to disk Multipart bodies, part counts and sizes
OS / network convenience wrappers Net, Email, IO, VFS Yes: sockets, SMTP, file system Hostnames, paths, and addresses the host supplies
Crypto wrappers Crypto Yes: JNI to native cipher libraries Keys, IVs, and ciphertext the host supplies
Native service launcher Daemon (jsvc, procrun) Yes: runs a JVM as an OS service and manages a child process lifecycle Native launcher input and service configuration the operator supplies

Notes on the groupings:

  • Commons Text absorbed Commons Lang's deprecated org.apache.commons.lang3.text package (for example, Text StringSubstitutor supersedes Lang StrSubstitutor). Those Lang classes are deprecated but still maintained and remain in scope: whatever this model says about a Text class applies equally to its deprecated Lang counterpart.

Script and command execution engines: no trust boundary with the caller

JEXL, JXPath, Jelly, and Exec exist to evaluate caller-supplied expressions or scripts, or to run caller-supplied OS commands. For these components the usual code/data distinction collapses by design: the script, expression, or command is the code being run, so “the input got executed” describes the feature, not an injection bug. These engines have no trust boundary with their caller and execute their input as-is; the trust boundary stays entirely in the host, permanently. Unlike the parsers, no future “secure parsing” guarantee can apply to them: the requirement that their input be trusted is intrinsic to what they do, not a provisional default. A report of the shape “JEXL evaluated my expression” or “Exec ran my command” is by design and will be closed as a disclaimed property.

One exception is claimed: JEXL's permission mechanism (JexlPermissions) is a documented security boundary since JEXL 3.7.0. A demonstrated escape from a properly configured JEXL sandbox is a valid finding for JEXL 3.7.0 and later. The other three engines claim no sandbox of any kind.

Commons Daemon

Commons Daemon is itself a process launcher that runs a JVM as an OS service, so its native launcher and service-management surface are in model as a distinct profile. Service configuration is trusted operator input. However, the privilege transitions performed by jsvc and procrun (dropping privileges, managing the service lifecycle) are a claimed boundary: a bug that leaves the child process running with unintended privileges is a valid finding.

Out of scope

  • Validation of untrusted input. It is unsafe to provide possibly malicious input to Commons libraries unless otherwise specified. Input validation at the application's trust boundary is the host's job, not the component's.
  • Deserialization code of Serializable classes. When applications use Java serialization to deserialize untrusted input, that is a vulnerability in the application. Although several Commons classes provide custom readObject methods and can serve as deserialization gadgets, that is not a vulnerability in Commons. This is the single most important out-of-scope statement in the family.
  • Behaviors that would be legitimate for the JDK. Anything a Commons API does that the equivalent JDK call would also do is out of model by the JDK-parity principle.
  • Network and service-level threats. Commons ships no service; TLS, authentication, authorization, rate limiting, and network exposure belong to the host application that wraps a component.
  • Documentation, example, test, and benchmark code shipped in the repositories. Javadoc, src/test, examples, and JMH benchmarks are not part of the supported API surface.

Trust boundaries and data flow

A trust boundary is the point where data from a less-trusted party enters code that must not believe it: the line at which responsibility for validating data changes hands. Commons places that point in the host application, not at the Commons API, except where a component explicitly takes on a “secure parsing” guarantee (see below).

  • The public Java API is an interface, not a trust boundary. Every byte a Commons method receives arrived because the host application chose to pass it; inside the call, data is treated as already vetted by the caller, and responsibility for the arguments stays with the caller. The host and the library are one trust zone; the host's own boundary (its network endpoint, upload form, or downloaded file) is where untrusted data is supposed to stop.
  • No trust transition happens inside a Commons component. A component does not re-authenticate, re-authorize, or sanitize on the host's behalf.
  • Deserialization is no different. The custom readObject methods of Commons Serializable classes are entry points of the same kind: they assume the stream they are handed comes from a trusted source. Deserializing does not move the boundary; a host that feeds an untrusted stream to ObjectInputStream has carried untrusted data across its own boundary unvalidated (see “Out of scope”).
  • A “secure parsing” guarantee moves the boundary into the component. Where a component claims it, the component volunteers to accept data straight from the untrusted side and contain the harms that guarantee names; everywhere else the boundary stays in the host.

Assumptions about the environment

  • Runtime: a conformant JVM at the component's documented minimum Java baseline. Behavior on unsupported or end-of-life JVMs is undefined.
  • Concurrency: thread safety is per-class, not a family guarantee. Treat a Commons class as not thread-safe unless its Javadoc explicitly states that it is; the host must not share a non-thread-safe instance across threads.
  • Memory: the host provides adequate heap; a component makes no promise about behavior under host-imposed memory pressure.
  • Side effects:
    • The pure-computation group performs no I/O beyond consuming the values the caller handed it, opens no sockets, spawns no processes, and installs no signal handlers.
    • Parser groups do perform external I/O when the parsed format requires it. For example, Configuration can include additional properties files via an include key (bounded to local resources in current releases). Such format-driven I/O is a feature, not a vulnerability.
    • Environment variables are read by several components, including Configuration, Exec, JCS, Lang, Net, and Text; “reads no environment variables” is not a family-wide property.
    • The OS/network convenience group (Net, Email, IO, VFS) exists precisely to do I/O with host-supplied targets; the side effects are the feature, and their safety is the host's responsibility.
    • Temporary files are created by FileUpload, Compress, Exec, and IO.
    • Native code is called by Crypto (JNI to native cipher libraries) and by Daemon (native launchers). Exec spawns child processes but is pure Java itself.

Configuration variants that matter to security

Commons components are plain Maven-built JARs with few compile-time knobs; the security-relevant variation is runtime configuration, concentrated in a handful of components.

Knob or mode Component Default Effect on the model
Interpolation lookups that reach out: script, url, dns Text (StringSubstitutor), Configuration Disabled by default Re-enabling such a lookup re-opens a remote-code-execution / SSRF surface if untrusted strings reach the substitutor; findings against that configuration are out of model
Ambient-state lookups: env, sys, file, xml, properties, … Text (StringSubstitutor), Configuration Enabled by default Untrusted templates can embed ambient process state in output; see “Assumptions about outputs”
Upload limits (sizeMax, fileSizeMax, fileCountMax) FileUpload Unset (unbounded) unless the host sets them Internal bounds are a configuration feature the host owns, more than a security property
Per-format decompression limits Compress No global output cap; some formats expose limits Same framing: internal bounds are a feature; safety against malicious archives is the separate “secure parsing” guarantee

FileUpload and Compress are building blocks of pipelines that process both trusted and untrusted data. The mechanism that limits output is normally left to the host, so it can pick the limiting logic that best fits the pipeline. Limiting internal sizes is exposed as a configuration option, but as a feature more than a security property: controlling the size of the input, and in most cases the size of the output, is up to the host.

Assumptions about inputs

The general rule: all input is assumed trusted (not attacker-controllable). The trusted users decide whether to pass attacker-controllable input to a component; the family does not take on an adversary the host itself chose to admit. Commons accepts input only through public API parameters.

The exception, a narrow default-configuration promise: for Compress, Configuration, Text, and FileUpload, passing any data to them in their default configuration will not:

  • cause excessive CPU consumption,
  • cause excessive memory allocation,
  • execute remote code.

This promise covers availability and integrity only. It does not cover confidentiality (see “Assumptions about outputs”), and these components still do not sanitize their output: malicious input can produce malicious output; the garbage-in / garbage-out principle applies.

Representative entry points by family group:

Component / entry point Parameter Attacker-controllable? Caller must enforce
StringUtils, Math (pure computation) value arguments Effectively no: data-in / data-out on trusted input Nothing beyond the documented contract
CompressorInputStream / ArchiveInputStream (Compress) compressed or archive bytes Yes, if the host feeds untrusted archives On versions without the “secure parsing” guarantee: cap decompressed size, entry count, and wall clock. Always: validate entry paths before writing extracted files
StringSubstitutor.replace(...) (Text) template string Yes, if the host passes untrusted templates Do not pass untrusted strings to a substitutor with active outbound lookups; mind ambient-state lookups in output
Configuration interpolation configuration values Yes, if the configuration is untrusted Treat configuration as trusted, or disable interpolation lookups
FileUpload parse multipart stream, part count, sizes Yes; this is network input by construction Set sizeMax, fileSizeMax, fileCountMax; sanitize filenames
Net, Email hostname, address, path Yes, if host-supplied from an untrusted source Validate or allow-list targets; the wrapper does not
Crypto key, IV, ciphertext Host-supplied Key management, IV uniqueness, integrity (AEAD or MAC)

No family-wide input bound exists; individual components may document format-specific limits. Parsers are the acute case: a small input can drive large CPU or memory use (decompression bombs, deeply nested structures, catastrophic regular expressions).

Assumptions about outputs

Commons components are as neutral on the way out as on the way in. Four rules govern outputs:

  1. Output derived from untrusted input is itself untrusted. Components do not sanitize output. Malicious input can produce malicious output; the garbage-in / garbage-out principle applies. Where a component offers both a raw accessor and a safe consumer, choosing is the host's job: for example, Compress exposes ArchiveEntry.resolveIn so the host can consume entry names without path traversal, while getName returns the raw, possibly hostile string. Path validation before writing extracted files is, and will remain, the host's responsibility.
  2. Outputs may embed ambient process state. Wherever an enabled feature reads ambient state, that state can appear in output. The acute case is interpolation: the default lookup sets of Text and Configuration still resolve prefixes such as env, sys, file, xml, and properties, so an untrusted template like ${env:SECRET} embeds process state in the result even in default configuration. The component promises nothing about where output flows. A host that passes untrusted templates and reflects the result to a requester has built the information disclosure itself; confidentiality of output is an output-flow responsibility of the host.
  3. Writers and encoders aim to preserve output structure. Where a component generates a structured format, it aims to prevent untrusted content from breaking the structure of the generated document: CSV's printer quotes cell content so that a cell value does not inject a record or field; Codec's encoders emit well-formed output for their format; Text's StringEscapeUtils escape functions produce output that is inert in the target syntax they document. This is an aim, not a claimed security property: a quoting or escaping bug that lets content break the generated structure is an ordinary bug, not a security vulnerability, unless a component specifies otherwise.
  4. Passive sinks are in model; active sinks are not. A passive sink consumes output in its original format. An active sink reinterprets output in another language: a spreadsheet evaluating a CSV cell that begins with = as a formula, a terminal executing ANSI escape sequences embedded in a string. Commons aims to preserve structure, not safety in every language in which a downstream consumer might reinterpret the output. Defending active sinks is the host's responsibility.

Side-effect outputs (spooled temporary files, extracted files, archives the component writes) follow the same rules: the component creates what it was asked to create, and the host owns where those artifacts land and who can read them.

Adversary model

  • In scope: whoever supplies the content a host passes into a component through a channel the component's own documentation says is safe for untrusted content; primarily, malicious input aimed at a component that claims the “secure parsing” guarantee, and malformed input aimed at APIs documented to tolerate it.
  • The default position is that malicious input is out of scope: it is unsafe to provide possibly malicious input to Commons libraries unless otherwise specified. The in-scope adversary is narrow.
  • Explicitly out of scope:
    • An attacker who already controls the calling code; they have won at a layer below Commons.
    • An attacker whose capability is “the host deserialized my bytes”; that is the host's vulnerability, not Commons's.
    • An attacker exploiting a behavior the JDK would also exhibit.
    • Side-channel, timing, and co-tenant adversaries.
  • Attacker goals considered: availability (CPU or memory exhaustion via crafted parser input) and ambient I/O (making a parser touch resources the caller did not designate) against components claiming the “secure parsing” guarantee; escape from the JEXL sandbox (JEXL 3.7.0+); privilege retention across Daemon's privilege transitions.

Security properties the project provides

These are not security properties:

  • Functional correctness is not a security property. Wrong output on valid input is an ordinary bug.
  • Rejecting malformed input is not a security property. Malformed input should not drive excessive CPU or memory use, but beyond that the garbage-in / garbage-out principle applies.

The claimed properties are:

  1. Memory safety. Pure-Java components inherit memory safety from the JVM. The two native components, Crypto (JNI to native cipher libraries) and Daemon (native launchers), claim memory safety for Commons's own JNI and native glue code: a reproducible crash or memory corruption in that code, reachable through the component's API, is security-critical and a valid report. Crashes originating inside the underlying native library itself (for example, OpenSSL) are findings against that upstream project, not against Commons.

  2. Expected resource use for the requested operation. A component uses the memory and CPU one would expect for the specific usage, with no surprises. Often that is linear in the input, but the guarantee is per-operation: Statistics, for example, is expected to use constant memory for degree moments, linear memory for rank statistics, and CPU in line with a decent sorting algorithm. Violations on in-contract input are correctness bugs; for a component that claims the “secure parsing” guarantee below, the same blowup on malicious input is a valid security finding. This property exists chiefly so that the numeric components, Math and Statistics, have a property to be evaluated against; without it those libraries would have no trust boundary or security property to check at all. The math-related components (Math and Statistics, and also RNG, Numbers, and Geometry) should additionally follow Oracle's Secure Coding Guidelines for Java SE, Guideline 3-9 / INJECT-9 (“Prevent injection of exceptional floating point values”): check for infinities and NaN and raise an exception where the value does not make sense. This property is under discussion on the security list and may be revised or withdrawn.

  3. “Secure parsing” (per-component property). This is not a family-wide property. Where a component takes it on, passing arbitrary (including malicious) input to it will not:

    • cause excessive CPU consumption,
    • cause excessive memory allocation,
    • execute code,
    • cause reads from, or connections to, anything the caller did not explicitly designate (the no-ambient-I/O clause, which covers XML external entities, include-file chasing, and server-side request forgery driven through a parser).

    Catastrophic regular-expression backtracking triggered by malicious input counts against the excessive-CPU clause for components claiming this guarantee. The guarantee is introduced component by component, in waves; each component's threat model documents it with a “since” value that states the initial version that guarantees it.

    Experimental scan configuration (2026-08-12): for the current LLM security scan only, the parser components (BCEL, CLI, Codec, Compress, Configuration, CSV, DBCP/Pool configuration parsing, Digester, FileUpload, Imaging, RDF, XML) and the interpolation/expression engines (Text's StringSubstitutor, Configuration interpolation, JEXL, JXPath, Jelly) are treated as if they claimed this guarantee, to maximize findings. For the expression engines the code-execution clause does not apply to by-design evaluation of the supplied expression; the resource clauses, the no-ambient-I/O clause (the documented default lookup set counts as caller-designated), and the JEXL 3.7.0+ sandbox do the work there. This is a scan-harness setting, not a commitment; it may be narrowed or withdrawn per component after the results are reviewed, and findings that violate only this experimental claim are hardening input, not vulnerabilities against the baseline.

  4. The JEXL sandbox, since JEXL 3.7.0. JexlPermissions is a documented security boundary; an escape from a properly configured sandbox is a valid finding for JEXL 3.7.0 and later.

  5. Daemon privilege transitions. jsvc and procrun performing a documented privilege drop must not leave the managed child with unintended privileges.

There is deliberately no family-level guarantee of: constant-time behavior, collision resistance of non-cryptographic hashes and checksums, cryptographic randomness from non-cryptographic RNGs, or safe handling of untrusted serialized, expression, or archive input beyond the per-component “secure parsing” waves.

Security properties the project does not provide

This is the load-bearing section for the family, and one rule quantifies over all of it: no safety against untrusted or malicious input, by default. It is unsafe to provide possibly malicious input to Commons libraries unless otherwise specified; the host must validate or sanitize at its boundary. The “secure parsing” guarantee above is the per-component, versioned exception, not the rule. The subsections that follow name the recurring weakness classes by their CWE identifiers; each is an instance of the general rule, recorded here with the refinements that apply to it.

CWE-502: Deserialization of Untrusted Data

Commons provides no deserialization safety. Java deserialization was never designed with security in mind. It is de facto a parser whose behavior is driven by whatever classes happen to be on the classpath. Commons itself does not deserialize anything. Its Serializable classes carry deserialization code (custom readObject methods included) only so that hosts that already chose Java serialization can use them; that code is not hardened against untrusted streams and is out of scope (see “Out of scope”).

Known deserialization gadgets may be documented as warnings, but removing them is not a goal and would break backward compatibility; fully hardening Java serialization is a lost cause. Safer mechanisms have existed for a long time (Commons Digester introduced over twenty years ago). If you must deserialize semi-trusted data, Commons IO's ValidatingObjectInputStream provides an allow/deny-list wrapper. The safe default remains: do not deserialize untrusted data at all.

CWE-22: Path Traversal (“zip-slip”)

Commons provides no path-traversal protection. Validating extraction targets is, and will remain, the host's job, including for components that claim “secure parsing”. Compress supports this with a safe API (ArchiveEntry.resolveIn) but does not enforce it; the raw entry name remains available via getName.

CWE-917: Expression Language Injection (interpolation and expression evaluation)

Commons provides no expression or interpolation sandbox for untrusted templates, with the sole exception of the JEXL permission mechanism (3.7.0+). Passing untrusted strings into Text's StringSubstitutor, Configuration's interpolation, JXPath, or Jelly can reach outbound lookups or code evaluation if the host has enabled them, and can read ambient process state through the default lookups (see “Assumptions about outputs”).

CWE-611: XML External Entity Reference (XXE)

Commons provides no XXE hardening guarantee at the baseline. Components that claim “secure parsing” cover XXE through the no-ambient-I/O clause. For all other XML-consuming components, an inventory of which components configure their underlying parser defensively is in progress; until a component's documentation states otherwise, assume that safe external-entity and DTD handling is your parser configuration's job.

CWE-1333: Inefficient Regular Expression Complexity (ReDoS)

Commons provides no ReDoS protection at the baseline. Catastrophic regular-expression backtracking on untrusted input is a disclaimed denial-of-service surface, except against components claiming “secure parsing”, where it counts as excessive CPU.

CWE-918: Server-Side Request Forgery (SSRF)

The OS and network wrappers provide no transport security, authentication, or target validation. Net, Email, IO, and VFS act on the hosts, addresses, and paths they are given; they do not allow-list targets and provide no SSRF defense. This is by design and permanent; target validation belongs to the host.

False friends (features mistaken for security primitives)

  • Codec digests, CRCs, and checksums are integrity-error detection, not MACs. A CRC32 or a plain MD5/SHA digest over data is not tamper evidence against an adversary (CWE-345).
  • Lang's RandomStringUtils and RandomUtils are not CSPRNGs. Do not use them to generate secrets or tokens (CWE-338).
  • StringSubstitutor and interpolation are templating conveniences, not a sandbox. They look like harmless string substitution but evaluate lookups; this was the root of CVE-2022-42889 (“Text4Shell”); see CWE-917 above.
  • Crypto is a thin wrapper over native ciphers, not a misuse-resistant cryptography framework. It will happily let you reuse an IV (CWE-323, CWE-329), use ECB mode (CWE-327), or omit a MAC (CWE-353).

Downstream responsibilities

For the assumptions above to hold, the host application must:

  1. Validate or sanitize untrusted input before passing it to any Commons component, except where a component version claims the “secure parsing” guarantee.
  2. Never deserialize untrusted data, regardless of which Commons components are on the classpath.
  3. Cap decompression and parse resource use (decompressed size, entry count, nesting depth, wall clock) when feeding untrusted streams to a component that does not claim “secure parsing”.
  4. Sanitize archive-entry paths against traversal before writing extracted files; prefer ArchiveEntry.resolveIn over raw getName.
  5. Keep untrusted strings out of interpolation and expression evaluators, leave outbound lookups disabled unless the input is trusted, and treat interpolated output as capable of embedding ambient process state.
  6. Control where output flows. Output derived from untrusted input is untrusted; do not reflect it to less-trusted parties without encoding for the destination, and account for active sinks (spreadsheets, terminals) that reinterpret content.
  7. Set FileUpload limits (sizeMax, fileSizeMax, fileCountMax); these are configuration features the host owns.
  8. Configure XML parsers to disable external entities and DTDs where a component consumes host-supplied XML, unless the component documents that it does so itself.
  9. Allow-list network, SMTP, and file-system targets given to Net, Email, and VFS, and never pass Exec (or JEXL, JXPath, Jelly) input you would not run yourself.
  10. Own key management, IV uniqueness, and integrity (AEAD or MAC) when using Crypto.
  11. Direct “does this published CVE affect me?” questions to the user mailing list, not the security list.

Known misuse patterns

  • Deserializing untrusted data with Collections or BeanUtils on the classpath. Looks like ordinary object I/O; is a remote-code-execution gadget surface. Instead: never deserialize untrusted bytes; use an allow-list filter or a safe format.
  • Extracting untrusted archives and writing entries verbatim. Enables zip-slip path traversal and, on versions without “secure parsing”, decompression bombs. Instead: resolve entry paths safely (ArchiveEntry.resolveIn), cap output size and entry count.
  • Interpolating untrusted strings via StringSubstitutor or Configuration with active lookups. Looks like string templating; can be remote code execution or SSRF with outbound lookups enabled, and information disclosure through ambient-state lookups even in default configuration. Instead: never interpolate untrusted input; restrict the lookup set.
  • Parsing untrusted XML without hardening the parser. XXE, SSRF, denial of service. Instead: disable external entities and DTDs.
  • Running FileUpload with no size or count limits on a public endpoint. Memory exhaustion, disk fill. Instead: set the limits.
  • Using a non-cryptographic digest or RNG as a security primitive. See the false friends above.
  • Serving generated CSV to spreadsheet users without considering formula injection. The CSV is structurally valid; the spreadsheet is an active sink. Instead: encode or prefix risky cell values for the destination.

Known non-findings (recurring false positives)

This list can be fed to scanners and automated triage as a suppression list.

  • “Collections InvokerTransformer enables RCE via deserialization.” Not a Commons finding: the host deserializing untrusted data is the vulnerability; Commons classes being usable as gadgets is out of model.
  • “Class X is Serializable and could be a gadget” or “the custom readObject method of class X misbehaves on a crafted stream.” Same disposition; containing Serializable classes is an implementation detail, not a component's purpose, and the deserialization code of those classes is out of scope: a crafted stream can only reach it because the host deserialized untrusted data.
  • “API Y does what its arguments tell it to (reflection, class loading, file access), therefore dangerous.” If the equivalent JDK call would be considered legitimate, it is not a Commons vulnerability, by the JDK-parity principle.
  • “Component throws or runs out of memory on fuzzed malicious input.” Out of model unless the component version claims the “secure parsing” guarantee.
  • “A functional-correctness bug reported as a vulnerability.” Wrong output on valid input is an ordinary bug, not a security finding.
  • StringSubstitutor can execute lookups.” With outbound lookups disabled (the default) and trusted templates, this is by design.
  • “Generated CSV opens as formulas in a spreadsheet” / “output contains terminal escape sequences.” Active-sink reinterpretation; writers and encoders aim to preserve structure only, not safety in every language in which a downstream consumer may evaluate the output.
  • “Non-constant-time comparison, non-cryptographic hash, or non-CSPRNG.” By design; these are not security primitives.

Conditions that would change this model

  • A component starts accepting a new input format or gains a network surface (for example, a server mode): re-scope the family table and boundaries.
  • A component changes a security-relevant default (for example, FileUpload ships a default size cap, or a lookup is re-enabled by default): re-rule the relevant cases.
  • A component takes on the “secure parsing” guarantee (the next wave lands): promote the relevant properties from disclaimed to claimed, with a “since” tag.
  • A component publishes or updates a per-component security page that diverges from this baseline: that page wins for that component and is reflected back here. The per-component pass proceeds wave-parsers first: Compress, then Configuration, FileUpload, and Text.
  • The XML-consumer inventory completes: the XXE baseline statement is finalized per component.
  • Any report that cannot be routed to a single disposition below is a model gap and triggers a revision of this document rather than an ad-hoc call.

Triage dispositions

Every incoming report receives exactly one of these dispositions, citing the licensing section of this model.

Disposition Meaning Licensed by
VALID Violates a claimed property (including “secure parsing” on a claiming version, the JEXL 3.7.0+ sandbox, or Daemon privilege transitions), reachable from attacker-controllable input by an in-scope adversary, and would not be legitimate for the JDK. Security properties provided; Assumptions about inputs/outputs; Adversary model
VALID-HARDENING No claimed property broken, but the API makes a known misuse easy enough that the component elects to harden (for example, disabling a risky lookup by default). Private report; maintainer discretion; usually no CVE. Known misuse patterns
OUT-OF-MODEL: trusted-input Requires attacker control of input the model treats as host-validated or trusted. Assumptions about inputs; Out of scope
OUT-OF-MODEL: adversary-not-in-scope Requires a capability the adversary model excludes, including “the host deserialized untrusted bytes.” Adversary model
OUT-OF-MODEL: jdk-parity The behavior would be considered legitimate for the JDK. Scope; Known non-findings
OUT-OF-MODEL: unsupported-component Lands in test, example, or benchmark code. Out of scope
OUT-OF-MODEL: non-default-build Only manifests under a discouraged, non-default configuration knob (for example, a re-enabled outbound lookup). Configuration variants
OUT-OF-MODEL: no-secure-parsing-yet Malicious-input finding against a parser version that does not claim the “secure parsing” guarantee. Security properties provided/not provided
OUT-OF-MODEL: upstream The defect lies in an underlying native or third-party library (for example, OpenSSL), not in Commons's own code. Security properties provided (memory safety)
BY-DESIGN: property-disclaimed Concerns a property this model explicitly does not provide (deserialization safety, interpolation sandbox, active-sink safety, false friends, functional correctness as security). Security properties not provided; Assumptions about outputs
KNOWN-NON-FINDING Matches a recurring false positive above. Known non-findings
MODEL-GAP Cannot be cleanly routed above. Triggers a model revision

The family-specific OUT-OF-MODEL: jdk-parity disposition encodes the Commons rubric's central test and is the correct call for a large share of automated findings. The OUT-OF-MODEL: no-secure-parsing-yet disposition tracks the wave rollout: as each component takes the guarantee on, its findings migrate from this disposition to VALID.