This is an automated email from the ASF dual-hosted git repository. oscerd pushed a commit to branch threat-model in repository https://gitbox.apache.org/repos/asf/commons-vfs.git
commit 8d7f32675d7985f73f45bf6155287fd59add951f Author: Andrea Cosentino <[email protected]> AuthorDate: Tue Oct 27 09:46:03 2026 +0100 Add Commons VFS threat model Add a VFS-specific threat model that specializes the Apache Commons family threat model, plus a machine-readable sidecar for automated/AI triage, and wire both into the project site. The model is ACCEPTED, with all maintainer questions resolved on 2026-10-27. - src/site/markdown/threat-model-0.1.0.md: prose model covering scope and the JDK-parity principle, trust boundaries, the two VFS-authored security properties (NameScope subtree confinement and URI-password masking), disclaimed properties and false friends, downstream responsibilities, known non-findings, and the closed set of triage dispositions. - src/site/markdown/threat-model-0.1.0.yaml: derived triage index. - src/site/site.xml, src/site/xdoc/security.xml: link the page from the site menu and the security page. Relates to VFS-867 (SFTP host-key-checking default hardening). Co-Authored-By: Claude Opus 4.8 <[email protected]> --- src/site/markdown/threat-model-0.1.0.md | 676 ++++++++++++++++++++++++++++++ src/site/markdown/threat-model-0.1.0.yaml | 271 ++++++++++++ src/site/site.xml | 1 + src/site/xdoc/security.xml | 13 +- 4 files changed, 960 insertions(+), 1 deletion(-) diff --git a/src/site/markdown/threat-model-0.1.0.md b/src/site/markdown/threat-model-0.1.0.md new file mode 100644 index 000000000..8800199e4 --- /dev/null +++ b/src/site/markdown/threat-model-0.1.0.md @@ -0,0 +1,676 @@ +<!--- + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +---> +# Apache Commons VFS Threat Model + +**Status:** ACCEPTED by the maintainers 2026-10-27. All §14 questions are +resolved; §12 lists what would trigger a revision. + +## Header + +| Field | Value | +| --- | --- | +| Project | Apache Commons VFS (`org.apache.commons:commons-vfs2` and sibling modules) | +| Version binding | 2.11.0-SNAPSHOT, `master` @ `08d2eff08`. CVE fixes referenced here landed in 2.10.0. | +| Date | 2026-10-26 | +| Author | Drafted via the `threat-model-producer` skill from public artifacts and source, then refined through maintainer review and ratified 2026-10-27. | +| Builds on | the family-level [Apache Commons Threat Model v0.1.0](https://svn.apache.org/repos/asf/commons/cms-site/trunk/content/markdown/threat-model-0.1.0.md). This document is a VFS-specific amendment: where it is silent, the family model governs. Where they conflict, the more recent one wins and the conflict is raised in §14. | + +**Version binding.** This model is versioned alongside the project. A report +against VFS version *N* is triaged against the model as it stood at *N*, not at +HEAD. + +**Reporting cross-reference.** Findings that fall under §8 (claimed properties) +should be reported privately per the +[Apache Commons security process](https://commons.apache.org/security.html) +(not the public issue tracker). Findings that fall under §3 (out of scope) or +§9 (disclaimed properties) will be closed citing this document. Questions about +whether a published CVE affects your setup go to the +[user mailing list](https://commons.apache.org/mail-lists.html), not the +security address. + +**Provenance legend.** Every non-trivial claim carries one tag: + +| Tag | Meaning | +| --- | --- | +| *(documented)* | Stated in the project's own docs, Javadoc, source, security page, or a published CVE. Cited inline. | +| *(maintainer)* | Confirmed by a VFS maintainer in response to this draft. Dated, e.g. *(maintainer, 2026-10-27)*. | +| *(inferred)* | Reasoned from code structure or domain knowledge, not yet confirmed. Each has a matching entry in §14. | + +The family model is published without these tags because it is ratified. This +VFS model is now ratified too (2026-10-27); the tags are **retained** rather than +dropped, so a report closed as out-of-model can cite a dated *(maintainer)* +authority (e.g. the SFTP-default ruling or a false friend) instead of bare prose. + +**Provenance summary.** ~30 *(documented)* / ~39 *(maintainer)* / 0 *(inferred)*. +All §14 questions were ratified on 2026-10-27, so every body claim is now +*(documented)* or *(maintainer)*; the tags are retained for triage citations. + +**Purpose (for someone who has never seen the project).** Apache Commons VFS is +an in-process Java library that presents one uniform API — `FileObject`, +`FileContent`, `FileSystemManager` — over many backing file systems. The caller +names a resource with a URI (for example `sftp://user:pass@host/dir/file`, +`zip:file:///tmp/a.zip!/entry`, or `https://host/path`) and VFS resolves it to a +`FileObject` it can read, write, list, copy, delete, or monitor. Backends +include the local file system, FTP/FTPS, SFTP, HTTP(S), WebDAV, HDFS, and +layered archive/compression formats (zip, jar, tar, gzip, bzip2), plus +in-process ones (ram, res, tmp). It is a library embedded in a host application, +not a daemon or a service. + +--- + +## 1. What this model is + +The implicit contract between Commons VFS and the application that embeds it: +what VFS assumes about its caller and inputs, the few security properties it +actually promises, the many it does not, and the misuses that are syntactically +possible but outside the intended use. It serves the **downstream integrator** +(which threats VFS has taken on, which are theirs) and the **triager** +(classifying a report, scan hit, or AI finding as valid, out of model, or +disclaimed, with a section to cite). + +It is **not** an audit, a bug list, a dependency/CVE register, or a restatement +of the Javadoc. It describes VFS as it *is*, not as it should be. + +--- + +## 2. Scope and intended use + +### 2.1 The JDK-parity principle (inherited from the family model) + +VFS is governed by the family model's central test *(documented: family model +§"Caller trust and the JDK-parity principle")*: **a call to the VFS API comes +with the same caveat as a call to the JDK — it will usually do what the caller +asks.** A VFS provider that opens the file, connects to the host, or reads the +archive its URI names is behaving like its JDK counterpart +(`new File(path)`, `new URL(url).openConnection()`, `new ZipInputStream(...)`), +not exhibiting a vulnerability. Keeping untrusted content away from an API that +is not documented to accept it is the trusted caller's responsibility. **Do not +report a behavior as a VFS vulnerability if the same behavior would be +considered legitimate for the JDK.** *(documented)* + +### 2.2 Two kinds of component, applied to VFS + +Using the family model's distinction *(documented)*: + +- **Almost all of VFS is a decorator of JDK or backend functionality** (Kind + one). The providers are thin adapters over `java.io.File`/`java.nio` + (local, tmp, ram, res), `java.net.URL` (url, http), `java.util.zip` and + stream codecs (zip/jar/tar/gz/bz2), Apache Commons Net (ftp/ftps), JSch + (sftp), Apache HttpClient (http4/http5, webdav), and Apache Hadoop (hdfs). + JDK-parity applies directly to all of these. *(maintainer, 2026-10-27)* +- **Two things in VFS are *not* mere JDK decoration and do make security + promises:** + 1. **URI/name resolution and `NameScope` confinement** — VFS's own path + algebra (`UriParser`, `AbstractFileName.checkName`). `NameScope.CHILD` / + `DESCENDENT` / `DESCENDENT_OR_SELF` promise that a resolved name stays + within a subtree. *(documented — Javadoc of `NameScope`; + `AbstractFileName.java:53`)* + 2. **Credential hygiene** — VFS masks passwords embedded in URIs when it + renders them into its own exceptions and "friendly" URIs. + *(documented — `FileSystemException.java:172`; + `GenericFileName.appendCredentials`)* +- **All bytes a provider hands back are raw and untrusted** — file contents, + directory listings, archive entries, server responses. VFS returns them to + the caller without sanitization, exactly as the family model describes for + low-level building blocks. *(maintainer, 2026-10-27)* + +These two promise-making exceptions are where VFS can have a *real* VFS +vulnerability, and they are precisely where both 2025 CVEs landed (§8). + +### 2.3 Caller trust + +The caller is trusted and in-process. The URI string and the +`FileSystemOptions` passed to `FileSystemManager.resolveFile(...)` are treated +as **trusted configuration**, at the same trust level as the argument to +`new File(...)`. *(maintainer, 2026-10-27)* There is no security boundary *inside* a +single `FileSystemManager`; the API surface is the boundary (§4). + +### 2.4 Component-family table + +| Family | Representative providers / API | Touches outside the process? | Recurring untrusted-input surface | In model? | +| --- | --- | --- | --- | --- | +| Core API & URI/name resolution | `FileSystemManager`, `DefaultFileSystemManager`, `VFS`, `FileObject`, `FileName`, `NameScope`, `UriParser`, `AbstractFileName` | No (pure computation over strings) | The URI string; relative names resolved under `CHILD`/`DESCENDENT` | **Yes — the one family with VFS-authored security promises** | +| Local | `file` (`LocalFileProvider`) | Local filesystem | Paths; symlink targets | Yes (JDK-parity) | +| Remote network | `ftp`, `ftps`, `sftp`, `http`/`https`, `http4`/`http4s`, `http5`/`http5s` | Network | Server responses, listings, redirects; server-presented host keys/certs | Yes (VFS logic); backend bugs → upstream | +| WebDAV | `webdav` (jackrabbit1), `webdav4`/`webdav4s` (jackrabbit2) | Network | Server responses | Yes; separate artifacts, httpclient 3/4 backends | +| HDFS | `hdfs` (commons-vfs2-hdfs) | Network / Hadoop cluster | Cluster responses | Yes (VFS logic); Hadoop → upstream | +| Archive / layered | `zip`, `jar`, `tar`, `tgz`, `tbz2`, `gz`, `bz2` | No (operate on a byte source) | **Archive bytes and entry names** | **Yes — highest untrusted-input surface** | +| In-process | `ram`, `res`, `tmp`, `url` | ram=no; res=classpath; tmp=local FS; url=whatever the URL names | Minimal (but `url` inherits JDK-parity) | Yes | +| Ant tasks | commons-vfs2-ant | Per task | Build script (trusted author) | Yes, at build-time trust | +| Examples | commons-vfs2-examples | Demo | n/a | **No — §3** | +| Sandbox | `mime`, `smb` (commons-vfs2-sandbox) | Varies | n/a | **No — §3** | +| Tests | `src/test/**` across modules | Local | n/a | **No — §3** | + +*(documented: module/provider layout; `providers.xml`. In/out classifications +for examples, sandbox, tests confirmed as model policy — maintainer, +2026-10-27.)* + +**Support tiers** *(maintainer, 2026-10-27)*: **first-class** (core +`commons-vfs2`) — `file`, `ftp`, `ftps`, `sftp`, `http4`/`http4s`, +`http5`/`http5s`, `zip`, `jar`, `tar`/`tgz`/`tbz2`, `gz`, `bz2`, `ram`, `res`, +`tmp`, `url`; **legacy but supported** — `http`/`https` (original +commons-httpclient-3, superseded by `http4`/`http5`) and `webdav` (jackrabbit1, +superseded by `webdav4`); **separate optional modules** — `hdfs` +(commons-vfs2-hdfs) and `webdav`/`webdav4` (jackrabbit1/2). A report against a +legacy provider stays in-model unless it is marked deprecated/out. + +--- + +## 3. Out of scope (explicit non-goals) + +- **Using VFS as a confinement sandbox for an untrusted URI.** VFS is not an + allow-list, egress filter, or jail. Resolving a caller-supplied URI connects + to (or reads) whatever scheme/host/path the URI names. If any part of a URI + can be influenced by an attacker, the resulting SSRF/LFI exposure is the + host's, by the host's choice — JDK-parity with `new URL(...).openConnection()`. + *(maintainer, 2026-10-27)* +- **commons-vfs2-examples** — sample code, separately authored, not a supported + API. Threat-model it separately if ever embedded. *(maintainer, 2026-10-27)* +- **commons-vfs2-sandbox** — the `mime` and `smb` providers ship under an + artifact explicitly named *sandbox* (component id `vfs-sandbox`); experimental + and unsupported. *(documented: artifact name; placed out of model — + maintainer, 2026-10-27)* +- **Test code** across all modules. *(maintainer, 2026-10-27)* +- **Transport-security *management***: choosing/validating host keys, pinning + certificates, supplying `TrustManager`/`KeyManager`. VFS exposes the knobs + (§5a) and picks defaults; getting the policy right for a given deployment is + the caller's. *(maintainer, 2026-10-27)* +- **Dependency and supply-chain hygiene** (backend library versions, pinning, + signing). Out of scope per the family model and the skill. A vulnerability + rooted in a backend (Commons Net, JSch, HttpClient, jackrabbit, Hadoop) is + dispositioned `OUT-OF-MODEL: upstream` (§13), not modeled here. +- **Resource exhaustion from adversarial archives or servers** beyond what VFS + itself bounds — see §9; VFS makes no resource guarantee. + +--- + +## 4. Trust boundaries and data flow + +The **API surface is the trust boundary.** Two distinct kinds of data cross it, +in opposite directions: + +1. **Inbound from the caller — trusted configuration.** The URI and + `FileSystemOptions` handed to `resolveFile(...)`. VFS trusts these the way + the JDK trusts a path or URL argument. *(maintainer, 2026-10-27)* +2. **Inbound from the backend — untrusted data.** Everything a resolved + `FileObject` yields: `FileContent` bytes, directory listings, archive entry + names and sizes, HTTP/WebDAV response bodies and headers, FTP listings. + VFS passes these to the caller without sanitization. *(maintainer, 2026-10-27)* + +**Reachability preconditions per family** (the first test a triager applies to a +tool/AI hit): + +- A finding in **URI/name resolution** is in-model only if reachable from a + relative name resolved under `CHILD`/`DESCENDENT`/`DESCENDENT_OR_SELF` against + a caller-trusted base — i.e. the host was relying on that scope as a + containment boundary. *(documented: this is the CVE-2025-27553 shape.)* +- A finding in a **remote provider** is in-model only if reachable from data a + *malicious backend server* returns (listing, body, redirect, host key/cert), + **not** from the caller having chosen to point at that server. +- A finding in an **archive/layered provider** is in-model only if reachable + from archive bytes a trusted caller deliberately opened, and only if it breaks + a property §8 claims (it almost never does — see §9 on bombs and extraction). +- A finding in **credential handling** is in-model if a password the caller + embedded in a URI reaches a VFS-produced diagnostic (exception message, + `getFriendlyURI()`, log) in cleartext. *(documented: CVE-2025-30474 shape.)* + +--- + +## 5. Assumptions about the environment + +- **Runtime:** a conformant JVM, Java 8 or later. *(documented: + `maven.compiler.source=1.8`.)* +- **Thread-safety:** `FileSystemManager` is intended to be shared across + threads; individual `FileObject`/`FileContent` instances and the caching + layer carry narrower guarantees and are not guaranteed safe under concurrent + use. *(maintainer, 2026-10-27)* +- **Memory model:** pure-Java core inherits JVM memory safety — no out-of-bounds + access on valid or invalid input. Native-backed backends (e.g. compression + via native libraries pulled by a dependency) are upstream. + *(maintainer, 2026-10-27)* +- **What VFS *does* to its host (side-effect inventory).** VFS is unusual among + Commons components in that touching the host is its *purpose*, so the + inventory is long and partly surprising: + - Opens local files and directories; creates and deletes temporary files via + the replicator / `tmp` provider, under `java.io.tmpdir`. + *(maintainer, 2026-10-27)* + - Opens TCP sockets for the network providers. *(maintainer, 2026-10-27)* + - For SFTP, **reads the invoking user's `~/.ssh` material** (known-hosts and, + depending on configuration, identity/config files) via JSch. + *(documented: `SftpClientFactory` known-hosts handling; full set + confirmed — maintainer, 2026-10-27)* + - Reads system properties and environment used by backends (`user.home`, + `java.io.tmpdir`, proxy properties, etc.). *(maintainer, 2026-10-27)* +- **What VFS does *not* do to its host (negative claims, confirmed):** no + process spawning in the core providers (there is no exec/script provider in + core), no `System.exit`, no signal handlers, no mutation of global locale/FPU + state. *(maintainer, 2026-10-27)* + +### 5a. Configuration variants that matter to security + +VFS is really a family of behaviors selected by `FileSystemOptions` and a few +global flags. The knobs that move the security envelope: + +| Knob / mode | Component | Default | Effect on the model | Maintainer stance | +| --- | --- | --- | --- | --- | +| `StrictHostKeyChecking` | sftp | **`"no"`** *(documented: `SftpFileSystemConfigBuilder.java:422`)* | With the default, the SSH **host key is not verified**, so SFTP to an untrusted network is open to MITM until the caller sets `"yes"`/`"ask"` and supplies known-hosts. This is the family model's *insecure-default* case. | **Dev-only default (maintainer, 2026-10-27):** not the supported posture, so a report against the bare default is `OUT-OF-MODEL: non-default-build`; a hardening change [...] +| `setKnownHosts` / `userDirIsRoot` | sftp | none / provider-specific | Where host keys are read from; whether the user dir is treated as root (path rooting). | Confirmed — caller's responsibility (maintainer, 2026-10-27) | +| `TrustManager` | ftps | `TrustManagerUtils.getValidateServerCertificateTrustManager()` *(documented: `FtpsFileSystemConfigBuilder.java:120`)* | FTPS **validates** the server certificate by default — a *secure* default. A caller can override with a permissive manager. | Confirmed secure default (maintainer, 2026-10-27) | +| HttpClient defaults | http4/http5, webdav | library defaults | Redirect-following and hostname verification follow the HttpClient version's defaults (verification on). | Confirmed (maintainer, 2026-10-27) | +| `Cryptor` / encrypted URI passwords | core util | off; built-in default key | "Encrypting" a URI password uses a **built-in, non-secret key** unless the caller supplies their own. Obfuscation, not confidentiality (false friend, §9). | Confirmed false friend (maintainer, 2026-10-27) | +| `VFS.isUriStyle()` | core | provider-dependent | Changes trailing-separator handling in name comparison (`checkName` adjusts `baseLen`). Correctness-relevant to the `NameScope` check. | Confirmed; does not weaken `checkName` (maintainer, 2026-10-27) | + +This is not build hygiene; it is the recognition that a report is only +meaningful against a named member of this family. The **insecure-default case** +(`StrictHostKeyChecking="no"`) was the wave-1 question that most reshaped the +model (§8, §9, §10, §13); the maintainers ruled it developer-only (§14, Q5.2). + +--- + +## 6. Assumptions about inputs + +Default, inherited from the family model: **all input is trusted unless a +specific property says otherwise.** *(documented)* The narrow VFS exceptions are +the `NameScope` confinement and credential-masking promises. + +Per-entry-point trust table: + +| Component / entry point | Parameter | Attacker-controllable? | Caller must enforce | +| --- | --- | --- | --- | +| `FileSystemManager.resolveFile(uri)` | the URI string (scheme, host, path, userinfo) | **No — trusted configuration** | If any part derives from untrusted input: allow-list scheme, host, and path before calling | +| `FileObject.resolveFile(name, scope)` | relative `name` + `NameScope` | the `name` may be attacker-influenced when used as a containment boundary | Rely on `CHILD`/`DESCENDENT` for confinement; VFS enforces the scope (§8.1) | +| `FileContent.getInputStream()` and friends | backend bytes (file / archive / server) | **Yes** | Treat as untrusted; bound total size and time | +| Archive providers (`zip`/`tar`/…) | archive bytes; entry names, sizes, counts, nesting | **Yes** | Cap entry count / uncompressed size / depth; verify extraction destination (§10) | +| `StaticUserAuthenticator`, URI userinfo | username / password | No — trusted configuration | Keep out of logs; prefer not embedding secrets in URIs | +| sftp handshake | server-presented host key | **Yes (the server chooses it)** | Set `StrictHostKeyChecking` + known-hosts (§5a, §10) | +| ftps/https handshake | server certificate | **Yes (the server chooses it)** | Keep the validating default; supply a `TrustManager` only if you know why | + +Size/shape/rate: VFS imposes **no** inherent bound on archive size, entry count, +nesting depth, directory-listing length, or response body size (§9). +*(maintainer, 2026-10-27)* + +--- + +## 7. Adversary model + +**In scope:** + +- **A malicious backend the caller pointed VFS at** — a hostile FTP/SFTP/HTTP/ + WebDAV/HDFS server returning crafted listings, bodies, redirects, oversized + or never-ending responses, or a hostile host key/certificate. This is the + primary VFS-specific adversary. *(maintainer, 2026-10-27)* +- **The author of an archive the host opens** with a layered provider — a + crafted zip/tar designed to exhaust resources (a "bomb") or to carry + traversal-shaped entry names. *(maintainer, 2026-10-27)* +- **A party who can influence a relative name** resolved under `CHILD`/ + `DESCENDENT` where the host relies on that scope as a containment boundary. + *(documented: CVE-2025-27553.)* +- **A party who can read the host's logs/diagnostics**, against the narrow + promise that VFS does not itself print embedded URI passwords in cleartext. + *(documented: CVE-2025-30474.)* + +**Out of scope:** + +- **The in-process caller.** A caller already running in the host JVM has + trivially full control and is not a meaningful adversary at this layer + (family model). *(documented)* +- **A party who controls the URI/scheme/host.** Choosing where to connect is + the host's decision; the resulting SSRF/LFI is the host's exposure, not a VFS + bug (§3, JDK-parity). *(maintainer, 2026-10-27)* +- **Side-channel and co-tenant adversaries** (timing, memory inspection). Not + modeled. *(maintainer, 2026-10-27)* + +VFS is an in-process library, not a consensus/replicated system, so there is no +Byzantine-participant actor to model. *(maintainer, 2026-10-27)* + +--- + +## 8. Security properties the project provides + +For each: the property and its conditions, the violation **symptom**, the +**severity tier** (security-critical vs. correctness-only), and provenance. + +### 8.1 `NameScope` subtree confinement +Resolving a name with `NameScope.CHILD`, `DESCENDENT`, or `DESCENDENT_OR_SELF` +returns a `FileObject` only if the (normalized) result lies within the intended +subtree; otherwise it throws. `AbstractFileName.checkName` enforces this on +absolute, normalized paths. *(documented: `NameScope` Javadoc; +`AbstractFileName.java:53-79`.)* +- **Symptom of a break:** a resolved `FileObject` escapes the base subtree — + path traversal. +- **Tier:** **security-critical.** CVE-2025-27553 (fixed in 2.10.0) was exactly + a bypass of `DESCENDENT` confinement. *(documented.)* + +### 8.2 Credential masking in VFS-produced diagnostics +A password embedded in a URI is replaced by `***` when VFS renders the name +into (a) a `FileSystemException` message — via the `:(?:[^/]+)@` → `:***@` +substitution *(documented: `FileSystemException.java:172-175`, VFS-169)* — and +(b) the "friendly"/public URI from `getFriendlyURI()` / +`appendCredentials(buffer, false)` *(documented: +`GenericFileName.java:66-78`)*. Note the deliberate asymmetry: +`getFriendlyURI()` calls `createURI(false, false)` (masked) while `getURI()` +calls `createURI(false, true)` and returns the **real** password +(round-trippable) by design *(documented: `AbstractFileName.java:148,242,431`)*. +- **Symptom of a break:** a cleartext URI password in a VFS-generated exception, + friendly URI, or log line. +- **Tier:** **security-critical.** CVE-2025-30474 (fixed in 2.10.0) was an FTP + path that leaked the URI password in an error message. *(documented.)* + +### 8.3 URI/path normalization +`UriParser.normalisePath` collapses `.` and `..` elements so names resolve +deterministically within a file system. *(documented: +`UriParser.java:511-575`.)* This underpins §8.1. +- **Symptom:** inconsistent resolution; a `..` that is not collapsed (can feed a + §8.1 break). +- **Tier:** correctness, security-relevant (load-bearing for §8.1). + +### 8.4 FTPS server-certificate validation by default +FTPS validates the server certificate out of the box. *(documented: +`FtpsFileSystemConfigBuilder.java:120`.)* HTTP(S)/WebDAV delegate verification to +the HttpClient defaults (on). *(maintainer, 2026-10-27)* +- **Symptom:** a connection completing against an invalid/foreign certificate + with the default configuration. +- **Tier:** security-relevant default. + +### 8.5 Memory safety (inherited) +The pure-Java core cannot cause out-of-bounds reads/writes on any input; memory +safety is the JVM's. *(maintainer, 2026-10-27)* +- **Tier:** baseline; a genuine break would be security-critical but would + almost certainly be a JVM or native-backend (upstream) issue. + +### 8.6 Content fidelity +Bytes read through `FileContent` are the backend's bytes, unaltered (round-trip +fidelity for read/write/copy). *(maintainer, 2026-10-27)* +- **Tier:** correctness-only. + +**Resource properties — stated as a threshold:** VFS makes **no** resource +guarantee against adversarial input. It does not bound uncompressed archive +size, entry count, nesting depth, directory-listing size, or wall-clock on a +slow/endless server. A hang or OOM on a crafted archive or hostile server is +**not** a violated property (§9). *(maintainer, 2026-10-27)* + +--- + +## 9. Security properties the project does *not* provide + +This is the load-bearing section. The family rule quantifies over all of it +*(documented)*: **no safety against untrusted or malicious input, by default; it +is unsafe to feed possibly-malicious input to VFS unless a specific property +above says otherwise, and the host must validate or sanitize at its boundary.** + +- **CWE-22 — path traversal on *extraction* ("zip-slip").** VFS normalizes `..` + inside its *own* name resolution, so a name cannot escape the layered-FS root + *within VFS's namespace*, and `CHILD`/`DESCENDENT` (§8.1) are available for + confinement. VFS does **not** protect a host that extracts archive entries to + the local filesystem with its own logic over raw entry names. Verifying each + entry's destination before writing is the host's job (family-model Compress + parity). *(maintainer, 2026-10-27)* +- **CWE-918 — SSRF, and local file disclosure.** Resolving a caller-supplied + URI connects to / reads whatever it names, and the HTTP providers follow + redirects per HttpClient. VFS is not an allow-list or egress filter. Attacker + influence over a URI is the host's SSRF/LFI exposure (§3, §7). + *(maintainer, 2026-10-27)* +- **Resource exhaustion (archive bombs, hostile/slow servers).** No + decompression-bomb, entry-count, depth, or response-size defense. The caller + caps size and time. *(maintainer, 2026-10-27)* +- **SFTP MITM under the default configuration.** `StrictHostKeyChecking` + defaults to `"no"` (§5a); host keys are unverified until the caller opts in. + The maintainers rule this default a developer convenience, **not** the + supported production posture: a MITM report against the bare default is + `OUT-OF-MODEL: non-default-build`, and a change to harden the default is + proposed ([VFS-867](https://issues.apache.org/jira/browse/VFS-867), §12). + *(documented: `SftpFileSystemConfigBuilder.java:422`; + maintainer, 2026-10-27.)* +- **CWE-502 — deserialization of untrusted data.** VFS is not a deserialization + sink by design. Do not feed untrusted bytes to Java object deserialization + regardless of which VFS artifacts are on the classpath. *(maintainer, 2026-10-27)* +- **Authentication/integrity of fetched content.** VFS does not authenticate the + bytes a remote file system returns; TLS (where used) authenticates the + transport, not the content's provenance. *(maintainer, 2026-10-27)* +- **Symlink-following policy on the local FS.** The `file` provider follows the + OS's symlink semantics (JDK-parity); it is not a symlink jail. + *(maintainer, 2026-10-27)* + +### 9a. False friends (features mistaken for security primitives) + +- **URI password "encryption" (`Cryptor` / encrypted-password URIs)** looks like + secret protection but uses a **built-in, non-secret default key** unless the + caller supplies one. It keeps cleartext out of a config file; it is **not** + confidentiality against anyone who has the (public) algorithm. *(documented: + `util/Cryptor`; intent confirmed — maintainer, 2026-10-27)* +- **Password masking (`***`)** is diagnostic hygiene (§8.2), not an access + control, and not a guarantee that a password never reaches a log by *any* + path — `getURI()` returns it by design, and CVE-2025-30474 showed a provider + path that bypassed masking. *(documented.)* +- **`ram` / `res` / `tmp` "virtual" file systems** are naming conveniences, not + isolation or sandbox boundaries. *(maintainer, 2026-10-27)* +- **`CHILD`/`DESCENDENT` `NameScope`** is a confinement helper for *names*; it + does not defend against a provider or backend that itself returns out-of-tree + data (e.g. a malicious server reporting a bogus listing). + *(maintainer, 2026-10-27)* + +**Well-known attack classes for this category, left to the caller** (one line +each, to put integrators on notice): zip-slip and decompression bombs for +archive handling; SSRF via URL resolution and redirect-following; symlink +following on local files; SFTP/FTPS/HTTPS MITM when verification is disabled; +credential leakage via URIs that reach logs. *(maintainer, 2026-10-27)* + +--- + +## 10. Downstream responsibilities + +For VFS, "downstream" is the embedding application. For the §5–§7 assumptions to +hold, the host must: + +1. **Allow-list** scheme, host, and path whenever any part of a URI derives from + untrusted input, before calling `resolveFile`. VFS will not do this for you. +2. **Treat all backend bytes as untrusted** — file contents, listings, archive + entries, response bodies — and validate/encode them for their destination. +3. For **archives**, cap uncompressed size, entry count, and nesting depth, and + **verify each entry's destination** before writing it to local disk; prefer + resolving under `DESCENDENT` over trusting raw entry names. +4. Set **timeouts and response-size ceilings** for remote providers; assume a + server may be slow or endless. +5. For **SFTP to any untrusted network**, set `StrictHostKeyChecking` to `"yes"` + and supply a known-hosts source; do not ship the `"no"` default into + production — the maintainers treat it as developer-only, not the supported + posture (§5a). *(maintainer, 2026-10-27)* +6. Keep the **validating TLS defaults** (FTPS/HTTPS); override a `TrustManager` + only with a deliberate, understood reason. +7. **Do not embed secrets in URIs** that you then log; if you log a `FileName`, + use `getFriendlyURI()` (masked), never `getURI()` (cleartext password). +8. **Do not deserialize untrusted bytes**, whatever VFS artifacts are present. +9. Do not rely on **URI password encryption** for confidentiality (§9a). +10. Do not treat VFS as a **sandbox/confinement boundary** for untrusted URIs + (§3). + +--- + +## 11. Known misuse patterns + +1. **Passing an untrusted URI straight to `resolveFile`** — SSRF/LFI. *What it + looks like:* building a URI from a request parameter. *Why unsafe:* VFS + connects/reads wherever it points. *Instead:* allow-list scheme/host/path. +2. **Extracting an archive tree to disk with hand-rolled logic over raw entry + names** — zip-slip. *Instead:* resolve under `DESCENDENT` and/or verify each + destination path is inside the target directory before writing. +3. **Opening untrusted archives with no entry/size/depth caps** — decompression + bomb → OOM/hang. *Instead:* impose caps in the host. +4. **SFTP to untrusted hosts at the default `StrictHostKeyChecking="no"`** — + MITM. *Instead:* enable strict checking with known-hosts. +5. **Logging `getURI()` (or a raw URI built with credentials)** — credential + leak, bypassing VFS's own masking. *Instead:* log `getFriendlyURI()`. +6. **Relying on URI password encryption as real secret protection** (§9a). +7. **Treating VFS as a confinement sandbox for untrusted URIs** (§3). + +--- + +## 11a. Known non-findings (recurring false positives) + +Feed this list to scanners / automated triage as a suppression list. + +1. "**`resolveFile(uri)` connects to an attacker-specified host / reads an + arbitrary local file.**" → `OUT-OF-MODEL: jdk-parity` + `trusted-input`: the + host chose the URI (§2.1, §6). +2. "**An HTTP provider follows a redirect to another host.**" → `OUT-OF-MODEL: + jdk-parity`: HttpClient default behavior (§8.4 note, §9). +3. "**A zip/tar provider OOMs or hangs on a crafted archive.**" → `BY-DESIGN: + property-disclaimed`: VFS makes no resource guarantee (§9). +4. "**`FileSystemException` / a `FileName` is `Serializable`, therefore a + deserialization gadget.**" → `OUT-OF-MODEL: adversary-not-in-scope`: do not + deserialize untrusted bytes (§9). +5. "**A `FileName.getURI()` result contains a cleartext password.**" → + `BY-DESIGN`: `getURI()` is round-trippable by design; the masked + `getFriendlyURI()` and exception messages are the property (§8.2). *(A leak + via VFS's **own** exception/friendly-URI path is instead `VALID` — §8.2.)* +6. "**Non-constant-time comparison / non-cryptographic hash / non-CSPRNG.**" → + `BY-DESIGN`: false friends, not provided (§9a). +7. "**Code in commons-vfs2-examples / -sandbox / tests is insecure.**" → + `OUT-OF-MODEL: unsupported-component` (§3). +8. "**FTPS/HTTPS connection fails against a self-signed certificate.**" → not a + finding: that is the validating default working as intended (§8.4). + +9. "**SFTP accepts an unknown host key with the default configuration.**" → + `OUT-OF-MODEL: non-default-build` *(maintainer, 2026-10-27)*: host-key + verification is opt-in via `StrictHostKeyChecking` (§5a), and the maintainers + treat the `"no"` default as developer-only. A separate hardening change to + the default is proposed; that is a §12 model-changing event, not a finding + against this version. + +--- + +## 12. Conditions that would change this model + +- A new provider or URI scheme (especially one that spawns processes or + evaluates expressions — none exist in core today). +- A **change of default** for a §5a security knob — above all, flipping or + re-interpreting the SFTP `StrictHostKeyChecking` default. +- Promoting a sandbox provider (`smb`, `mime`) into the supported core. +- Adding built-in archive **extraction** with (or without) traversal defenses. +- Gaining a network-service / daemon surface (VFS is in-process today). +- **Evidence the model is incomplete:** any report that cannot be routed to + exactly one §13 disposition is a `MODEL-GAP` — the correct response is to + revise this model (add the property to §8 or §9), not to make an ad-hoc call. + +--- + +## 13. Triage dispositions + +The closed set of outcomes for a report, scan hit, or AI finding. Adapted from +the family model's twelve (dropping `no-secure-parsing-yet`, which has no VFS +analog — VFS makes no "secure parsing" promise). + +| Disposition | Meaning | Licensed by | +| --- | --- | --- | +| `VALID` | Breaks a property VFS claims (§8), via in-scope input and adversary, and the JDK would not call it legitimate. | §8, §6, §7 | +| `VALID-HARDENING` | No §8 property is broken, but a §11 misuse is easy enough that VFS elects to tighten a default/API. Reported privately; maintainer discretion; usually no CVE. | §11 | +| `OUT-OF-MODEL: trusted-input` | Requires attacker control of a parameter the model marks trusted (a URI the host supplied). | §6, §3 | +| `OUT-OF-MODEL: adversary-not-in-scope` | Requires a capability the model excludes (e.g. the host deserializing hostile bytes, or an in-process caller). | §7 | +| `OUT-OF-MODEL: jdk-parity` | The same behavior would be legitimate for the JDK (connect/read/extract what the arguments name). | §2.1, §11a | +| `OUT-OF-MODEL: unsupported-component` | Lands in examples, sandbox, or test code. | §3 | +| `OUT-OF-MODEL: non-default-build` | Manifests only after a risky, off-by-default option is set (e.g. a permissive `TrustManager`). | §5a | +| `OUT-OF-MODEL: upstream` | Root cause is in a backend library (Commons Net, JSch, HttpClient, jackrabbit, Hadoop), not VFS logic. | §8.5, §3 | +| `BY-DESIGN: property-disclaimed` | Targets a property VFS explicitly does not provide (bombs, SSRF, `getURI()` password, false friends). | §9, §9a | +| `KNOWN-NON-FINDING` | Matches a documented recurring false positive. | §11a | +| `MODEL-GAP` | Fits none of the above — the model needs revision. | §12 | + +--- + +## 14. Maintainer questions (resolved record) + +This section is retained as the record of how the model was settled. Each entry +states the answer the maintainers gave and the section it lands in. All waves +were ratified on 2026-10-27; no questions remain open. + +### Wave 1 — scope and the insecure default — RATIFIED 2026-10-27 + +The maintainers ratified the scope framing — Q2.1 (JDK-parity), Q2.2 +(examples/sandbox/tests out of model; `commons-vfs2-ant` in model at build-time +trust), Q2.3 (URI and `FileSystemOptions` are trusted configuration) — and ruled +Q5.2 (SFTP `StrictHostKeyChecking="no"`) a **developer convenience, not the +supported posture**: a report against the bare default is +`OUT-OF-MODEL: non-default-build`, and a hardening change to the default is +proposed (§12; [VFS-867](https://issues.apache.org/jira/browse/VFS-867)). The matching body tags are now +*(maintainer, 2026-10-27)*. + +### Wave 2 — properties and adversary model — RATIFIED 2026-10-27 + +The maintainers confirmed the property and adversary-model answers: `NameScope` +confinement is an intended security boundary and a bypass is CVE-worthy, and +`VFS.isUriStyle()` does not weaken `checkName` (Q6.5); credential masking in +VFS's own exceptions / `getFriendlyURI()` is an intended property while +`getURI()` returns the real password by design (Q8.2a); VFS makes no resource +guarantee against adversarial archives or servers (Q7.1); zip-slip destination +checks on extraction are the host's (Q4.1); provider output is raw and +unauthenticated (Q4.3); `Cryptor` URI-password encryption is obfuscation, not +confidentiality (Q8.4); the transport defaults are as described — FTPS and the +HttpClient 4/5 providers verify by default, with host-key/cert policy left to +the caller (Q5.1); and the adversary model (Q7.2) and the disclaimed-property +list (Q9.5) are complete. The matching body tags are now *(maintainer, +2026-10-27)*. + +### Wave 3 — environment — RATIFIED 2026-10-27 + +The maintainers confirmed: the pure-Java core is memory-safe and only a +native-backed backend could break it, as `upstream` (Q9.1); a +`FileSystemManager` is shareable across threads while per-`FileObject` / +`FileContent` state and the cache are not guaranteed safe under concurrent use +(Q9.2); and the host side-effect inventory is correct — VFS opens files and +sockets, creates temp files under `java.io.tmpdir`, and reads the user's +`~/.ssh` material for SFTP, while the core providers spawn no processes, install +no signal handlers, and never call `System.exit` (Q9.3). The matching body tags +are now *(maintainer, 2026-10-27)*. + +### Meta + +- **Q-meta-1 — provider support tiers.** *Resolved (maintainer, 2026-10-27):* + the proposed tiering was confirmed and recorded in §2.4 — first-class core + providers; `http`/`https` and `webdav` legacy-but-supported; `hdfs` and + `webdav`/`webdav4` as separate optional modules. A report against a legacy + provider stays in-model unless it is marked deprecated/out. +- **Q-meta-2 — publication venue.** *Resolved (maintainer, 2026-10-27):* + published at `src/site/markdown/threat-model-0.1.0.md` (versioned filename, to + match the family model), wired into the site menu (`site.xml`) and linked from + the security page (`security.xml`). +- **Q-meta-3 — relationship to the family model and `security.xml`.** *Resolved + (maintainer, 2026-10-27):* this document sits alongside the family model as the + VFS-specific amendment, and `security.xml` keeps listing fixed CVEs while + linking here for the model. + +--- + +## 15. Appendix — source back-map + +Where each existing security statement lands in this model (per the skill's +§3.1a requirement that the model be a strict superset of prior policy). + +| Existing statement | Source | Lands in | +| --- | --- | --- | +| "No safety against untrusted input by default; host validates at its boundary." | Family model §"…does not provide" | §9 (intro) | +| JDK-parity principle; two kinds of component. | Family model §scope | §2.1, §2.2 | +| False friends (digests, RNG, interpolation, crypto). | Family model §9 | §9a (VFS-specific: `Cryptor`, masking, virtual FS, `NameScope`) | +| Triage dispositions (12 labels). | Family model §triage | §13 (11, minus `no-secure-parsing-yet`) | +| CVE-2025-27553 — path traversal via `NameScope.DESCENDENT`. | `security.xml`; CVE record | §8.1 (break of the claimed property) | +| CVE-2025-30474 — FTP error message reveals URI password. | `security.xml`; CVE record | §8.2 (break of the claimed property) | +| "Binary patches are never provided; build from source." | `security.xml`; family model | Reporting cross-reference (Header) | +| Safe-deserialization pointer (Commons IO). | `security.xml` | §9 (CWE-502) | +| Report privately to the Apache Security Team. | `SECURITY.md`; `security.xml` | Header (reporting cross-reference) | + +A machine-readable companion for automated triage is provided alongside this +file as `threat-model-0.1.0.yaml` (§13 dispositions, §6 entry-point trust, §5a knobs, +§8/§9 properties, §11a non-findings). The prose here is canonical; the sidecar +is a derived index. diff --git a/src/site/markdown/threat-model-0.1.0.yaml b/src/site/markdown/threat-model-0.1.0.yaml new file mode 100644 index 000000000..1b220d082 --- /dev/null +++ b/src/site/markdown/threat-model-0.1.0.yaml @@ -0,0 +1,271 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Machine-readable companion to threat-model-0.1.0.md. DERIVED INDEX for tooling and +# automated/AI triage; the prose document is canonical. Regenerate when the +# prose changes. + +project: Apache Commons VFS +artifacts: [commons-vfs2, commons-vfs2-ant, commons-vfs2-hdfs, commons-vfs2-jackrabbit1, commons-vfs2-jackrabbit2] +version_binding: 2.11.0-SNAPSHOT +commit: 08d2eff08 +status: accepted # maintainer 2026-10-27; all threat-model.md section 14 questions resolved +model_version: 0.1.0 +central_test: jdk-parity # "report it only if the JDK would not call the same behavior legitimate" + +support_tiers: # maintainer 2026-10-27 (Q-meta-1) + first_class: [file, ftp, ftps, sftp, http4, http4s, http5, http5s, zip, jar, tar, tgz, tbz2, gz, bz2, ram, res, tmp, url] + legacy_supported: [http, https, webdav] # http/https superseded by http4/http5; webdav (jackrabbit1) by webdav4 + optional_modules: [hdfs, webdav, webdav4] # commons-vfs2-hdfs; jackrabbit1/2 + in_model_note: a report against a legacy provider stays in-model unless marked deprecated/out + +component_families: + - name: core-api-and-name-resolution + representative: [FileSystemManager, VFS, FileObject, FileName, NameScope, UriParser, AbstractFileName] + touches_outside_process: false + in_model: true + note: the only family with VFS-authored security promises (name confinement, credential masking) + - name: local + representative: [file] + touches_outside_process: filesystem + in_model: true + - name: remote-network + representative: [ftp, ftps, sftp, http, https, http4, http4s, http5, http5s] + touches_outside_process: network + in_model: true + backend_bugs: upstream + - name: webdav + representative: [webdav, webdav4, webdav4s] + touches_outside_process: network + in_model: true + modules: [commons-vfs2-jackrabbit1, commons-vfs2-jackrabbit2] + - name: hdfs + representative: [hdfs] + touches_outside_process: network + in_model: true + backend_bugs: upstream + - name: archive-layered + representative: [zip, jar, tar, tgz, tbz2, gz, bz2] + touches_outside_process: false + in_model: true + note: highest untrusted-input surface (archive bytes and entry names) + - name: in-process + representative: [ram, res, tmp, url] + touches_outside_process: partial # url inherits JDK-parity + in_model: true + - name: ant-tasks + representative: [commons-vfs2-ant] + touches_outside_process: per-task + in_model: true + trust: build-time + - name: examples + representative: [commons-vfs2-examples] + in_model: false + disposition: "OUT-OF-MODEL: unsupported-component" + - name: sandbox + representative: [smb, mime] + in_model: false + disposition: "OUT-OF-MODEL: unsupported-component" + note: ships under artifact commons-vfs2-sandbox (component id vfs-sandbox) + - name: tests + in_model: false + disposition: "OUT-OF-MODEL: unsupported-component" + +entry_points: # per-parameter trust (threat-model-0.1.0.md section 6) + - entry: FileSystemManager.resolveFile(uri) + parameter: uri-string + attacker_controllable: false # trusted configuration + caller_must_enforce: allow-list scheme/host/path if any part is untrusted + - entry: FileObject.resolveFile(name, scope) + parameter: relative-name + NameScope + attacker_controllable: when-used-as-containment-boundary + caller_must_enforce: rely on CHILD/DESCENDENT; VFS enforces the scope + - entry: FileContent.getInputStream + parameter: backend-bytes + attacker_controllable: true + caller_must_enforce: treat as untrusted; bound size and time + - entry: archive-providers + parameter: archive-bytes, entry-names, sizes, counts, nesting + attacker_controllable: true + caller_must_enforce: cap entries/size/depth; verify extraction destination + - entry: StaticUserAuthenticator / URI userinfo + parameter: credentials + attacker_controllable: false # trusted configuration + caller_must_enforce: keep out of logs; avoid secrets in URIs + - entry: sftp-handshake + parameter: server-host-key + attacker_controllable: true # server chooses + caller_must_enforce: set StrictHostKeyChecking + known-hosts + - entry: ftps-https-handshake + parameter: server-certificate + attacker_controllable: true # server chooses + caller_must_enforce: keep validating default + +config_knobs: # section 5a + - knob: StrictHostKeyChecking + component: sftp + default: "no" + security_relevant: true + secure_default: false # INSECURE DEFAULT -- see open question Q5.2 + effect: host key unverified -> SFTP MITM possible until caller sets yes/ask + source: SftpFileSystemConfigBuilder.java:422 + maintainer_ruling: dev-only # maintainer 2026-10-27 + report_vs_default: "OUT-OF-MODEL: non-default-build" + hardening_proposed: "VFS-867: change default to ask/yes" + - knob: TrustManager + component: ftps + default: getValidateServerCertificateTrustManager + security_relevant: true + secure_default: true + effect: FTPS validates server cert by default; override is non-default-build + source: FtpsFileSystemConfigBuilder.java:120 + - knob: httpclient-defaults + component: [http4, http5, webdav] + default: library-defaults + security_relevant: true + secure_default: true # verification on; inferred (Q5.1) + effect: redirect-following and hostname verification per HttpClient version + - knob: Cryptor / encrypted URI password + component: core-util + default: off; built-in non-secret key + security_relevant: true + secure_default: n/a + effect: obfuscation, not confidentiality (false friend) + - knob: VFS.isUriStyle + component: core + default: provider-dependent + security_relevant: true + effect: trailing-separator handling in checkName (name-confinement correctness) + +properties_provided: # section 8 + - id: namescope-confinement + property: CHILD/DESCENDENT/DESCENDENT_OR_SELF keep a resolved name within the subtree + violation_symptom: resolved FileObject escapes the base subtree (path traversal) + severity: security-critical + provenance: documented + source: [NameScope.java, AbstractFileName.java:53] + broken_by_cve: CVE-2025-27553 + - id: credential-masking + property: URI password shown as *** in VFS exception messages and getFriendlyURI() + violation_symptom: cleartext URI password in a VFS-produced diagnostic + severity: security-critical + provenance: documented + source: [FileSystemException.java:172, GenericFileName.java:66] + note: getURI() returns the real password by design (round-trippable) + broken_by_cve: CVE-2025-30474 + - id: path-normalization + property: UriParser.normalisePath collapses . and .. deterministically + violation_symptom: uncollapsed .. feeding a confinement bypass + severity: correctness-security-relevant + provenance: documented + source: UriParser.java:523 + - id: ftps-cert-validation-default + property: FTPS validates the server certificate by default + violation_symptom: connection completes against an invalid cert with defaults + severity: security-relevant-default + provenance: documented + source: FtpsFileSystemConfigBuilder.java:120 + - id: memory-safety + property: pure-Java core cannot cause OOB access + severity: baseline + provenance: inferred + - id: content-fidelity + property: bytes read/written/copied are unaltered + severity: correctness-only + provenance: inferred + +resource_policy: + guarantee: none + statement: > + VFS makes no resource bound against adversarial input: no cap on uncompressed + archive size, entry count, nesting depth, listing size, or wall-clock. A hang + or OOM on a crafted archive or a slow/endless server is BY-DESIGN, not a + violated property. The caller caps size and time. + +properties_not_provided: # section 9 (disclaimed) + - cwe: CWE-22 + topic: path traversal on extraction (zip-slip) + note: VFS normalizes names in its own namespace; destination checks on extraction to disk are the host's + - cwe: CWE-918 + topic: SSRF and local file disclosure via caller-supplied URI + note: resolving/following a URI is jdk-parity; VFS is not an allow-list + - cwe: CWE-400/CWE-409 + topic: resource exhaustion (archive bombs, hostile/slow servers) + - cwe: CWE-300 + topic: SFTP MITM under default StrictHostKeyChecking=no + - cwe: CWE-502 + topic: deserialization of untrusted data (VFS is not a deserialization sink) + - cwe: CWE-345 + topic: no authentication/integrity of fetched content + - topic: symlink following on local FS (jdk-parity, not a jail) + +false_friends: + - uri-password-encryption-Cryptor # built-in key: obfuscation not confidentiality + - password-masking # diagnostic hygiene, not access control + - ram-res-tmp-virtual-filesystems # not isolation boundaries + - namescope-child-descendent # name helper, not a defense against a lying backend + +known_non_findings: # section 11a -- suppression list + - id: nf-ssrf-lfi + pattern: resolveFile connects to attacker-specified host or reads arbitrary local file + disposition: "OUT-OF-MODEL: jdk-parity" + - id: nf-redirect + pattern: HTTP provider follows a redirect to another host + disposition: "OUT-OF-MODEL: jdk-parity" + - id: nf-archive-bomb + pattern: zip/tar provider OOMs or hangs on a crafted archive + disposition: "BY-DESIGN: property-disclaimed" + - id: nf-serializable-gadget + pattern: FileSystemException/FileName is Serializable therefore a deserialization gadget + disposition: "OUT-OF-MODEL: adversary-not-in-scope" + - id: nf-geturi-password + pattern: FileName.getURI() returns a cleartext password + disposition: "BY-DESIGN: property-disclaimed" + caveat: a leak via VFS's own exception/friendly-URI path is VALID (property namescope credential-masking) + - id: nf-non-crypto-primitive + pattern: non-constant-time comparison / non-crypto hash / non-CSPRNG + disposition: "BY-DESIGN: property-disclaimed" + - id: nf-unsupported-component + pattern: insecure code in examples / sandbox / tests + disposition: "OUT-OF-MODEL: unsupported-component" + - id: nf-tls-rejects-selfsigned + pattern: FTPS/HTTPS fails against a self-signed certificate + disposition: not-a-finding # validating default working as intended + - id: sftp-unknown-hostkey-default + pattern: SFTP accepts an unknown host key with the default configuration + disposition: "OUT-OF-MODEL: non-default-build" # maintainer 2026-10-27: host-key check is opt-in (StrictHostKeyChecking); default is developer-only; hardening proposed + +dispositions: # section 13 -- closed set + - {label: "VALID", licensed_by: [properties_provided, entry_points, adversary_model]} + - {label: "VALID-HARDENING", licensed_by: [known_misuse_patterns]} + - {label: "OUT-OF-MODEL: trusted-input", licensed_by: [entry_points, out_of_scope]} + - {label: "OUT-OF-MODEL: adversary-not-in-scope", licensed_by: [adversary_model]} + - {label: "OUT-OF-MODEL: jdk-parity", licensed_by: [scope, known_non_findings]} + - {label: "OUT-OF-MODEL: unsupported-component", licensed_by: [out_of_scope]} + - {label: "OUT-OF-MODEL: non-default-build", licensed_by: [config_knobs]} + - {label: "OUT-OF-MODEL: upstream", licensed_by: [memory-safety, out_of_scope]} + - {label: "BY-DESIGN: property-disclaimed", licensed_by: [properties_not_provided, false_friends]} + - {label: "KNOWN-NON-FINDING", licensed_by: [known_non_findings]} + - {label: "MODEL-GAP", licensed_by: [conditions_that_change_the_model]} + +cves: + - id: CVE-2025-27553 + class: CWE-22 + broke: namescope-confinement + fixed_in: 2.10.0 + - id: CVE-2025-30474 + class: credential-disclosure + broke: credential-masking + fixed_in: 2.10.0 diff --git a/src/site/site.xml b/src/site/site.xml index 16143a66d..2f7cb6d45 100644 --- a/src/site/site.xml +++ b/src/site/site.xml @@ -48,6 +48,7 @@ <item name="File Systems" href="/filesystems.html"/> <item name="Ant Tasks" href="/anttasks.html"/> <item name="Testing" href="/testing.html"/> + <item name="Threat Model" href="/threat-model-0.1.0.html"/> <item name="Wiki" href="https://cwiki.apache.org/confluence/display/commons/VFS"/> </menu> <menu name="Commons VFS Modules" inherit="top"> diff --git a/src/site/xdoc/security.xml b/src/site/xdoc/security.xml index 374ec1834..f0baa20fc 100644 --- a/src/site/xdoc/security.xml +++ b/src/site/xdoc/security.xml @@ -41,7 +41,18 @@ <a href="mail-lists.html">user mailing list</a>. </p> <p>If you have encountered an unlisted security vulnerability or other unexpected behavior that has security impact, or if the descriptions here are - incomplete, please report them privately to the Apache Security Team. Thank you. + incomplete, please report them privately to the Apache Security Team. Thank you. + </p> + </section> + <section name="Threat Model"> + <p> + The <a href="threat-model-0.1.0.html">Apache Commons VFS Threat Model</a> describes the implicit + security contract between Commons VFS and the applications that embed it: what the library assumes + about its caller and inputs, the security properties it provides and those it deliberately does not, + and how inbound vulnerability reports, scanner output, and AI-generated findings are triaged against + that model. It is a VFS-specific amendment to the Apache Commons family threat model; see + <a href="https://commons.apache.org/security.html">Apache Commons Security</a> for the + foundation-wide security policy. </p> </section> <section name="Security Vulnerabilities">
