Skip to content

Add draft threat model + SECURITY.md/AGENTS.md discoverability#6709

Open
potiuk wants to merge 6 commits into
apache:masterfrom
potiuk:asf-security/threat-model-2026-06-02
Open

Add draft threat model + SECURITY.md/AGENTS.md discoverability#6709
potiuk wants to merge 6 commits into
apache:masterfrom
potiuk:asf-security/threat-model-2026-06-02

Conversation

@potiuk
Copy link
Copy Markdown
Member

@potiuk potiuk commented Jun 2, 2026

What this is

A draft threat model for Apache JMeter, proposed by the ASF Security team for the JMeter PMC to review, correct, or reject — drafted by the Security team's threat-model tooling from JMeter's public docs and security.html, following the ASF Security threat-model rubric.

It is built on the existing security.html: that page's "Security Model" statements are lifted in verbatim as the documented core, and this document adds the surrounding threat-model structure (adversary model, in/out scope, properties, known non-findings, triage dispositions). It is a strict superset — nothing in security.html is weakened; that page stays the canonical reporting/policy page and should link here for the expanded model.

This PR:

  • adds THREAT_MODEL.md — the draft model;
  • adds SECURITY.md — a short security policy linking the threat model (and security.html);
  • adds AGENTS.md with a ## Security section, so the chain AGENTS.md → SECURITY.md → THREAT_MODEL.md is mechanically discoverable by automated security scanners.

How to read it

Every claim is provenance-tagged: (documented) (from JMeter's docs/security.html/repo), (inferred) (reasoned from architecture, not yet confirmed), (maintainer) (confirmed by the PMC). This v0 is ~10 documented / ~26 inferred. The §14 Open questions section collects the inferred claims into waves for the PMC to confirm or correct. The model leads with the documented BY-DESIGN core — JMeter executes the (trusted) test plan it is given, and isolating untrusted .jmx is the user's responsibility — and then focuses the in-model boundaries on:

  • the distributed-mode RMI surface (jmeter-server) and its RMI-over-SSL + Security-Manager defaults (wave 1);
  • the opening-vs-running line for non-.jmx files (wave 2);
  • whether a hostile system-under-test's responses are in scope for JMeter's parsers (wave 2);
  • the forward isolation story given the JDK Security Manager's deprecation (wave 2).

Nothing here is a requirement — the model is for the PMC to own. Comment inline, edit the branch, or reply on the email thread.

@vlsi
Copy link
Copy Markdown
Collaborator

vlsi commented Jun 2, 2026

Thanks for the fast turnaround. If you have a public skill to create the model, it would be great if you could share it (sure we can run, steer it and so on).

I will check the output soon, however, my immediate thought is that we must forbid license headers in AGENTS.md.

They are pure waste of tokens. AGENTS.md loads in every session, so the files must contain only the bare minimum. Sure license header is a pure waste when it comes to AGENTS.md and any other reference files the model consumes.
There's .ratignore and friends (sure I can configure it, it is not like I am asking you to update the PR). If you created other AGENTS.md with license headers, I would recommend removing the headers.

PS. I consider using https://github.com/microsoft/apm for managing agent configurations and sharing skills, however, it is orthogonal to the PR.

@potiuk
Copy link
Copy Markdown
Member Author

potiuk commented Jun 2, 2026

Thanks for the fast turnaround. If you have a public skill to create the model, it would be great if you could share it (sure we can run, steer it and so on).

I will check the output soon, however, my immediate thought is that we must forbid license headers in AGENTS.md.

They are pure waste of tokens. AGENTS.md loads in every session, so the files must contain only the bare minimum. Sure license header is a pure waste when it comes to AGENTS.md and any other reference files the model consumes. There's .ratignore and friends (sure I can configure it, it is not like I am asking you to update the PR). If you created other AGENTS.md with license headers, I would recommend removing the headers.

PS. I consider using https://github.com/microsoft/apm for managing agent configurations and sharing skills, however, it is orthogonal to the PR.

IN Airlfow we are using SPDX headers for all agentic skills for that very reason. If you want I can change ti this way in your PR as well, not sure if you are checking licences but this seemed to be result of the discussion we had on this subject in https://lists.apache.org/thread/j1tn63r2lf13v3d1tnnqff8fkcl4nx53 - while we do not have formal legal policy on that , seems that we can interpret the existing poliicy this way.

@potiuk
Copy link
Copy Markdown
Member Author

potiuk commented Jun 2, 2026

I am absolutely happy to update the PR :)

potiuk added 3 commits June 2, 2026 20:24
AGENTS.md is loaded by automated agents in every session, so the
Apache license header is pure token overhead; drop it and exclude
the file from RAT via .ratignore, per review feedback.
Strip the Apache license headers from the security docs as well, and
revert the .ratignore AGENTS.md entry so RAT exclusions can be
configured separately, per review feedback.
AGENTS.md, SECURITY.md and THREAT_MODEL.md no longer carry license
headers, so list them in .ratignore to keep the RAT check green.
@potiuk
Copy link
Copy Markdown
Member Author

potiuk commented Jun 2, 2026

Done — stripped the Apache license headers from AGENTS.md, SECURITY.md, and THREAT_MODEL.md (they're reference docs consumed by agents/scanners, so the headers were just token overhead), and added all three to .ratignore so the RAT check stays green.

@potiuk
Copy link
Copy Markdown
Member Author

potiuk commented Jun 2, 2026

Ad the SKILL is linked above in the PR description

@milamberspace milamberspace self-requested a review June 2, 2026 19:20
Comment thread THREAT_MODEL.md Outdated
Comment thread THREAT_MODEL.md Outdated
Comment thread THREAT_MODEL.md Outdated
## §14 Open questions for the maintainers

**Wave 1 — confirm the security.html core + distributed defaults:**
1. Confirm the lifted `security.html` statements are the canonical core: trusted `.jmx`, opening may execute, untrusted-plan isolation is the user's job — so "`.jmx`/scripting runs code" is `BY-DESIGN`. Proposed: yes. → §3/§9/§11a.
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

LGTM

Comment thread THREAT_MODEL.md Outdated
Comment thread THREAT_MODEL.md Outdated
3. Confirm `examples/`/demos/third-party plugins are out of scope. → §3.

**Wave 2 — the in-model boundaries:**
4. **Opening vs running:** where exactly is the line? security.html says even *opening* a `.jmx` may execute — so is the in-model guarantee only about **result/JTL files** and other non-`.jmx` inputs not triggering execution? → §8.
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Opening a file involves test plan deserialization, thus it triggers class loading, and class initialization. The current design forces us to call getters/setters on the UI controls and the accompanying logic classes, so a mere "opening and browsing" the file would involve certain code execution.


I would say the line is "opening jmx is safe provided you trust all existing classes in jmeter distrubution". However, if opening a jmx file would generate a completely new class and trigger its execution (e.g. somehow create a class with some jmx trick), then we probably consider this a vulnerability.
@milamberspace , WDYT?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I would say the line is "opening jmx is safe provided you trust all existing classes in jmeter distribution". However, if opening a jmx file would generate a completely new class and trigger its execution (e.g. somehow create a class with some jmx trick), then we probably consider this a vulnerability.

I fully agree with this definition. It is precise and useful for triage. Worth formalising in the model.

After digging into the code, there is a concrete mechanism today that makes the "new class" risk real:

JMeterUtils.setupXStreamSecurityPolicy() is effectively a no-op.

xstream.addPermission(NoTypePermission.NONE);  // deny all...
xstream.addPermission(AnyTypePermission.ANY);  // ...then allow ALL

The code even carries a 12-year-old comment: // TODO : How much are we concerned by CVE-2013-7285. With AnyTypePermission.ANY, XStream will instantiate any class on the JVM classpath during .jmx deserialization. Any class whose constructor or static initialiser has side effects can execute code before the user clicks "Run". This is exactly the "new class triggered by a jmx trick" scenario you described.

The fix: replace AnyTypePermission.ANY with a proper allowlist scoped to JMeter's own packages:

xstream.addPermission(NoTypePermission.NONE);
xstream.addPermission(NullPermission.NULL);
xstream.addPermission(PrimitiveValuePermission.PRIMITIVES);
xstream.addPermission(ArrayTypePermission.ARRAYS);
xstream.addPermission(CollectionTypePermission.COLLECTIONS);
xstream.allowTypesByWildcard(new String[]{
    "org.apache.jmeter.**",
    "org.apache.jorphan.**",
    "java.lang.*",
    "java.util.*",
    "java.net.URL",
});

The challenge is ensuring the allowlist is complete enough that existing .jmx files keep working — it needs integration testing across a range of test plans before merging.

Secondary finding (lower severity): BeanShellTestElement.readResolve() calls init() which creates a BeanShellInterpreter during XStream deserialization. It does not compile or execute the script, but it does instantiate BeanShell itself on open. Making this lazy (initialise only on the first getBeanShellInterpreter() call, i.e. at test execution time) would shrink that surface.

Good news: JSR223TestElement is already clean — no readResolve(), and getScriptEngine() is only called from processFileOrScript() at test execution. Nothing to change there.

Would the PMC consider a follow-up GitHub Issue to track the XStream allowlist work? Given JMeter 6.0 targets JDK 17+, this is a good moment to close a 12-year-old TODO.


Proposed interim THREAT_MODEL.md edits (to document the current state while the fix is tracked separately):

§8 — refine the second property to make the two sub-cases explicit:

No unintended code execution from opening a non-executed file

  • Result/JTL files: opening for display should not instantiate arbitrary classes; this is the stronger guarantee. (inferred)
  • .jmx files: the in-model guarantee is "opening is safe with respect to existing JMeter distribution classes". If a crafted .jmx triggers instantiation of a class outside the JMeter distribution (via an XStream gadget chain), that is treated as a VALID vulnerability. Current status: the XStream policy (AnyTypePermission.ANY) does not yet enforce this boundary — a scoped allowlist is a tracked improvement (see §12). (inferred — PMC confirmation requested)

§9 — add one bullet after the "False friend" entry:

XStream class-loading boundary not yet enforced: the current setupXStreamSecurityPolicy() applies NoTypePermission.NONE then immediately overrides it with AnyTypePermission.ANY, allowing instantiation of any JVM class during .jmx deserialization. Replacing this with a package-scoped allowlist (org.apache.jmeter.** etc.) is a tracked improvement; until then, this boundary is aspirational, not enforced. (inferred)

§11a — refine the second entry (currently a blanket BY-DESIGN):

"Opening a .jmx triggers code execution"split into two cases:

  • Execution via existing JMeter distribution classes during deserialization/UI initialisation: BY-DESIGN, consistent with security.html.
  • Execution via a class outside the JMeter distribution (XStream gadget chain, dynamically compiled script triggered before "Run"): VALID — this is the boundary the model intends to hold.

§12 — add one condition:

Merging an XStream package-scoped allowlist (replacing AnyTypePermission.ANY) — would upgrade the §8 .jmx-open guarantee from aspirational to enforced, and tighten the §11a triage split above.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

The challenge is ensuring the allowlist

The issue is that allowlist should include third-party-plugin classes, otherwise the jmx won't deserialize.
So if we add a tightened allowlist, then the test plans with custom plugins won't load.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@vlsi — you are absolutely right, and this is the constraint I hadn't fully thought through. A static org.apache.jmeter.** allowlist breaks every third-party plugin whose classes sit outside that namespace, which is a hard blocker.

A few directions for a proper fix (none blocking the threat model doc itself):

  1. Dynamic allowlist from the plugin classloader — at startup, JMeter loads plugins from lib/ext/. XStream could be configured to accept any class that was actually loaded by the plugin classloader, rather than a static package list. Standard-library gadget-chain classes (com.sun.**, etc.) don't pass through that classloader, so they'd still be blocked. This is the least invasive path for existing plugins.

  2. Manifest-based registration — each plugin jar declares its XStream-serialisable packages in META-INF/jmeter-xstream-packages.txt; JMeter builds the allowlist at startup from those declarations. Requires a one-time migration for existing plugins, but explicit and auditable.

  3. Targeted denylist — instead of allowlisting, block specific packages known to supply gadget chains (the CVE-2013-7285 class). Less strict than an allowlist but non-breaking.

For the threat model doc: I'd suggest adding a note in §9 or §12 that setupXStreamSecurityPolicy() does not currently enforce a class-loading boundary (the effective policy is AnyTypePermission.ANY — a no-op), and tracking the classloader-based approach as a §12 condition that would strengthen the §8 "opening is safe given trust in existing classes" property. The aspirational guarantee is already correctly scoped in §8; we just need to be honest that the implementation hasn't caught up yet.

Comment thread THREAT_MODEL.md Outdated
Copy link
Copy Markdown
Contributor

@milamberspace milamberspace left a comment

Choose a reason for hiding this comment

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

PMC review of the draft threat model (v0) — 6 inline comments below addressing the open questions in §14, one identified gap (HTTPS recording proxy), and a correction on the Security Manager forward story for JDK 24+.

Comment thread THREAT_MODEL.md Outdated
- **`jmeter-server` bind / exposure** — what interface the remote engine binds to by default, and whether it requires the SSL keystore to start. *(inferred)*
- **Scripting elements** (JSR223/Groovy/BeanShell, `__groovy`/`__BeanShell` functions) — always available; gated only by who controls the `.jmx` (the user). *(documented design)*

**Insecure-default check:** if `jmeter-server` ships accepting RMI connections without SSL/auth by default, a report against that default is `VALID`; if SSL/keystore is required by default, an unprotected deployment is `OUT-OF-MODEL: non-default-build`. Wave-1 question (§14). (Cf. CVE-2019-0187, the historical RMI-without-SSL issue.)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Wave 1 / Q2 — RMI SSL default posture: answer from source code

After reviewing src/core/src/main/java/org/apache/jmeter/rmi/RmiUtils.java:

  • server.rmi.ssl.disable defaults to false in code → SSL is required by default
  • server.rmi.ssl.keystore.file defaults to rmi_keystore.jks, a file that is not shipped with the JMeter distribution (it must be generated by running create-rmi-keystore.sh / create-rmi-keystore.bat)
  • Without the keystore file present on disk, the SSL connection fails for both a legitimate controller and an attacker
  • An unprotected (no-SSL) deployment requires an explicit opt-out: server.rmi.ssl.disable=true

Proposed triage: OUT-OF-MODEL: non-default-build — an attacker cannot reach an unprotected engine without the operator actively disabling SSL.

However, disabling SSL to simplify setup is a widespread real-world antipattern found in many tutorials and quick-start guides. Suggest adding to §11 (Known misuse patterns):

"Disabling RMI SSL (server.rmi.ssl.disable=true) to simplify distributed setup, then exposing jmeter-server on an untrusted network."

Comment thread THREAT_MODEL.md Outdated
**Wave 2 — the in-model boundaries:**
4. **Opening vs running:** where exactly is the line? security.html says even *opening* a `.jmx` may execute — so is the in-model guarantee only about **result/JTL files** and other non-`.jmx` inputs not triggering execution? → §8.
5. **Hostile SUT:** is a malicious *system under test* (hostile HTTP/JDBC responses hitting JMeter's XPath/JSON/regex extractors — XXE, ReDoS, size bombs) **in scope**, or is the SUT assumed trusted/owned by the tester? Proposed: SUT semi-trusted; parser robustness is best-effort, not a claimed property. → §6/§7/§8.
6. The platform **Security Manager is deprecated/removed in modern JDKs**, yet it is the documented isolation recommendation. What is the forward isolation story for distributed mode? → §5/§9.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Wave 2 / Q6 — Security Manager: precise deprecation timeline + recommended forward path

The phrasing "deprecated/removed in modern JDKs" should be more precise:

JDK Status
17 Security Manager deprecated for removal (JEP 411)
18–23 Deprecated, still functional (prints a warning on startup)
24 Security Manager fully removed (JEP 486) — no longer available at all

Since JMeter 6.0 sets JDK 17 as the minimum and users may run on JDK 24+, the documented isolation recommendation is not actionable on JDK 24+.


Recommended forward path for the PMC (JDK 24+):

The Security Manager enforced a policy at the JVM level: it restricted which classes could perform privileged operations (file I/O, network, reflection, exec, etc.). The equivalent isolation must now come from outside the JVM. In order of preference:

1. Container isolation (preferred)

Run jmeter-server in a Docker or Podman container:

docker run --rm   --user 1000:1000   --read-only   --tmpfs /tmp   --cap-drop=ALL   --network=<controller-network>   -v /path/to/results:/results   jmeter-server-image

Key settings: non-root user, read-only root filesystem, no Linux capabilities, network scoped to the controller. A reference Dockerfile for jmeter-server would be a concrete and low-effort PMC deliverable.

2. systemd service hardening (bare-metal alternative)

A systemd unit for jmeter-server with:

[Service]
NoNewPrivileges=true
CapabilityBoundingSet=
PrivateTmp=true
ProtectSystem=strict
ReadWritePaths=/path/to/results

3. Dedicated OS user (minimum baseline)

Run jmeter-server as a locked-down system account with minimal filesystem permissions (principle of least privilege).


Suggested model updates:

  • §5: clarify that Security Manager is removed (not just deprecated) on JDK 24+
  • §9: add — "On JDK 24+, the Security Manager is unavailable; OS-level isolation (container, systemd hardening, or dedicated OS user) is the recommended replacement"
  • Open a GitHub Issue to track the documentation of this migration path and a potential reference Dockerfile

Comment thread THREAT_MODEL.md
| Protocol client + response processing | HTTP/JDBC/JMS/etc. samplers; XPath/JSON/regex extractors | network (egress to SUT); parses responses | **In — JMeter as a client parsing SUT responses** *(inferred)* |
| Report/results generation | listeners, dashboard | filesystem | **In (parsing result files)** *(inferred)* |
| Plugins / properties / functions | `user.properties`, plugins, `__function` calls | varies | **In core; third-party plugins out** *(inferred)* |
| `examples/`, test resources, demos | — | — | **Out** *(see §3)* |
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Missing component: HTTPS recording proxy (ProxyControl)

The component-family table does not include the HTTPS recording proxy, which is a significant JMeter feature with its own trust boundary and security surface.

When used in recording mode (HTTPS), JMeter:

  1. Generates a local Root CA certificate (ApacheJMeterTemporaryRootCA.*) stored in a keystore on the user's machine
  2. Dynamically issues per-host TLS certificates signed by that CA (on the fly, per target hostname)
  3. Requires the user to install the JMeter CA into their OS or browser trust store

The security risk: if the JMeter CA private key (stored on disk in the keystore) is compromised while the CA certificate is still trusted by the browser/OS, an attacker can issue fraudulent certificates for any domain — accepted silently by the browser, enabling MITM attacks.

Good news on defaults: proxy.cert.validity defaults to 7 days (ProxyControl.java, line 202: JMeterUtils.getPropDefault("proxy.cert.validity", 7)). This limits the exposure window and is a sound security default. The risk scenario of a very long-lived CA (e.g. 10 years via proxy.cert.validity=3650) is an opt-in misconfiguration, not the default — the model should note this positive default explicitly.

Suggested additions to the model:

  • §2 component table: add a row —
    | HTTPS recording proxy | ProxyControl (GUI or CLI) | local filesystem (CA keystore) + MITM on local HTTPS traffic | **In — local trust boundary (CA private key)** *(inferred)* |
  • §4: note the JMeter CA private key as a local trust boundary (analogous to running a private CA on the workstation)
  • §10 (downstream responsibilities): "Remove the JMeter CA certificate from your OS/browser trust store after recording sessions; do not set proxy.cert.validity to a duration longer than your recording session needs"
  • §11 (known misuse patterns): "Leaving the JMeter CA certificate permanently installed in the OS/browser trust store after a recording session; setting proxy.cert.validity to a long duration (e.g. years)"

Comment thread THREAT_MODEL.md
- "Opening a `.jmx` triggers code execution" — documented in the security model; the user must trust/isolate plans. `BY-DESIGN`.
- "Scripting/reflection/`Runtime.exec` present in the codebase" — needed to execute test plans; reachable only via a trusted plan. `KNOWN-NON-FINDING` unless reachable from the distributed-RMI surface or a non-executed file.
- "RMI deserialization in distributed mode" — `VALID` only if it works *without* the documented SSL/Security-Manager protections; otherwise `OUT-OF-MODEL: non-default-build` (cf. the post-CVE-2019-0187 posture).
- "JMeter makes outbound requests / can be pointed anywhere" — by-design; it is a load generator (§3).
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

§11a — Additional non-findings to seed

Some recurring scanner/fuzzer false positives not yet listed:

  • Runtime.exec() calls in the codebase — used by the OS Process Sampler (by design: it executes system commands the user configured) and by KeyToolUtils to invoke keytool for proxy keystore management. BY-DESIGN / KNOWN-NON-FINDING.

  • Java reflection — required for plugin loading and test element instantiation via the plugin architecture. KNOWN-NON-FINDING.

  • JMeter listening on a local port as an HTTP/HTTPS proxy — this is the HTTPS recording proxy (ProxyControl); it is user-initiated and BY-DESIGN. Not an unintended listening service.

  • Keystore file with a default password (changeit) — scanners may flag the changeit value in commented-out lines of user.properties / jmeter.properties. The rmi_keystore.jks file is not shipped with the distribution; it is generated by the operator via create-rmi-keystore.sh/bat. KNOWN-NON-FINDING for the property comment; operators should of course choose a strong password for their generated keystore.

  • XML parsing — external entity configuration — worth a targeted audit of JMeter's XML processors (XPath Extractor, XStream .jmx deserialization). If FEATURE_SECURE_PROCESSING is already enforced everywhere, add to §11a. If not, this may be a VALID-HARDENING candidate (XXE protection against hostile SUT responses).

Comment thread THREAT_MODEL.md
- **Version binding:** versioned with the project; a report against version *N* is triaged against the model as it stood at *N*.
- **Reporting cross-reference:** §8-property violations → report privately per ASF process (`security@apache.org` → `private@jmeter.apache.org`); §3/§9 findings are closed citing this document and `security.html`.
- **Provenance legend:** *(documented)* = JMeter's own docs/`security.html`/repo; *(maintainer)* = confirmed by a JMeter PMC member through this process; *(inferred)* = reasoned from architecture, not yet confirmed — each has a matching §14 open question.
- **Coexistence:** this model is a strict superset of `security.html`; nothing there is weakened. `security.html` stays the canonical reporting/policy page and should link here for the expanded model.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

§1 Coexistence — backlink from security.html not included in this PR

This line states that security.html "should link here for the expanded model" — but xdocs/security.xml (the Velocity/Maven source that generates security.html) is not modified in this PR.

Without the backlink, the threat model exists in the repository but is not discoverable from the canonical reporting page, reducing its effectiveness as a triage reference for security reporters and automated scanners.

Suggested resolution:

Add to the "Security Model" section of xdocs/security.xml:

<p>
  For the full threat model, adversary model, and triage dispositions,
  see <a href="https://github.com/apache/jmeter/blob/master/THREAT_MODEL.md">THREAT_MODEL.md</a>.
</p>

Either include this in the current PR or open a GitHub Issue as a tracked follow-up. Please do not leave it as an informal note — the link is what makes the model actionable for reporters.

Comment thread THREAT_MODEL.md Outdated
Security-relevant configuration *(documented unless noted):*

- **Distributed RMI security** — the controller↔engine channel: RMI-over-SSL with a keystore and client auth (default on in recent versions?), plus the recommended **Security Manager** policy on engines. Confirm the current defaults. *(documented that the security manager is recommended; SSL default inferred)*
- **`jmeter-server` bind / exposure** — what interface the remote engine binds to by default, and whether it requires the SSL keystore to start. *(inferred)*
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

§5a — jmeter-server bind interface: answer to the (inferred) item

jmeter-server does not bind to a restricted interface by default. The RMI registry and server socket bind to the address that java.rmi.server.hostname resolves to — typically the machine's primary hostname or IP. On a multi-homed host or a machine with a public IP, this can expose the RMI port on all interfaces.

Relevant properties for operators (all configurable in jmeter.properties or user.properties):

Property Default Purpose
java.rmi.server.hostname (machine hostname) Restricts which IP the RMI server advertises and binds to; set to 127.0.0.1 for loopback-only
server.rmi.port 1099 RMI registry port
server.rmi.localport (dynamic) Server socket port for the remote engine object; fix this value if you need precise firewall rules

Suggested model clarification in §5a: note that jmeter-server does not bind to a restricted interface by default, and that operators should use java.rmi.server.hostname and/or perimeter firewall rules to limit exposure — especially on multi-homed or internet-facing machines. This is particularly important when SSL is disabled (see the server.rmi.ssl.disable discussion).

@milamberspace
Copy link
Copy Markdown
Contributor

milamberspace commented Jun 2, 2026

On ASF license headers in AI agent-facing files

Following up on @vlsi's comment about token waste in AGENTS.md: the PR already handles this correctly (headers removed, .ratignore updated), but it is worth stating the rationale explicitly for future reference.

Why license headers should be omitted from AI-facing files:

Files such as AGENTS.md, CLAUDE.md, COPILOT-INSTRUCTIONS.md, or any file whose primary purpose is to be loaded as instructions by an AI agent should not carry ASF license headers. The reasons:

  1. Every token has a cost. These files are loaded into the AI model's context window at the start of every session. A standard Apache license header is ~80–100 tokens. For a file like AGENTS.md (currently 9 lines of actual content), the header would exceed the useful payload. At scale — many contributors using AI tooling daily — this is a measurable and entirely avoidable waste.

  2. Zero informational value for the consumer. An AI agent reading AGENTS.md needs instructions, not license metadata. The header adds noise that dilutes the signal-to-noise ratio of the file and may marginally affect how the model prioritises the actual instructions.

  3. .ratignore is the right mechanism. ASF RAT exists to ensure source files carry attribution. Files designed for machine consumption are not "source files" in that sense — excluding them from RAT via .ratignore is exactly the intended use of that escape hatch.

Recommendation for future files of this type: any file whose primary consumer is an AI agent (not a human reader or a build tool) should be added to .ratignore from the start and kept as concise as possible. This applies to AGENTS.md, SECURITY.md and THREAT_MODEL.md as added in this PR, and to any similar files added in the future.

The current PR's approach — removing the headers and updating .ratignore — is the correct one.


Broader suggestion: an ASF-level discussion on AI-facing files

This raises a question that goes beyond JMeter alone. As AI tooling becomes standard in open source development, it would be worth opening a discussion within the ASF (e.g. via the dev@community.apache.org list or the ASF Infrastructure / Legal teams) to establish a shared policy on how AI-facing files should be treated across all Apache projects.

The current full license header is clearly overkill for these files. A reasonable middle ground could be a lightweight attribution line — something like:

# Part of the Apache JMeter project — https://jmeter.apache.org — Apache License 2.0

This would:

  • Preserve attribution and licence discoverability for any agent or tool that reads the file
  • Cost only a handful of tokens instead of ~100
  • Satisfy the spirit of ASF attribution without the overhead of the full boilerplate

A shared ASF convention on this point would benefit all projects adopting AGENTS.md, CLAUDE.md, or similar files, and would give RAT maintainers clear guidance on how to handle this growing category of files.

@potiuk
Copy link
Copy Markdown
Member Author

potiuk commented Jun 2, 2026

Recommendation for future files of this type: any file whose primary consumer is an AI agent (not a human reader or a build tool) should be added to .ratignore from the start and kept as concise as possible. This applies to AGENTS.md, SECURITY.md and THREAT_MODEL.md as added in this PR, and to any similar files added in the future.

@milamberspace - of course - I 100% agree with it. Chiming in in https://lists.apache.org/thread/j1tn63r2lf13v3d1tnnqff8fkcl4nx53 and stating your position in the thread might be a good first start - as well as anyone else who thinks like it.

This is in the camp of legal now to clarify it - and we need people like you to take the lead and drive it to policy change. I am 100% happy to support you!

@potiuk
Copy link
Copy Markdown
Member Author

potiuk commented Jun 2, 2026

Generally - the more people we have stating their poistion in those discussions - the better! Please do so.

potiuk added 2 commits June 3, 2026 00:05
… Manager, open-vs-run, hostile SUT)

Folds in the Wave-1/2 answers from @vlsi and @milamberspace (2026-06-03):
- RMI: SSL on by default + mutual client-auth + keystore-not-shipped, so an
  unprotected jmeter-server requires an explicit server.rmi.ssl.disable=true
  opt-out -> OUT-OF-MODEL: non-default-config (not VALID, not 'build').
- Security Manager: deprecated (JEP 411), no longer the forward defense; the
  real distributed defense is RMI-over-SSL.
- Opening vs running: opening a .jmx is safe given trust in existing
  distribution classes; only generating+executing a NEW class on open is in model.
- Hostile SUT: out of model (test only systems you own); parser robustness is
  hardening, not a claimed property (pending milamberspace's final word).
- Adds the 'disable RMI SSL then expose jmeter-server' misuse; §14 waves 1-2
  marked answered.

Generated-by: Claude Code
@potiuk
Copy link
Copy Markdown
Member Author

potiuk commented Jun 2, 2026

Pushed a v1 folding in your review — thanks @vlsi @milamberspace. What changed:

  • RMI SSL defaults (your source review): adopted in full — §4/§5a/§8 now state server.rmi.ssl.disable=false, mutual client-auth, keystore-not-shipped (engine won't start unprotected), resolved-address bind. "Unauthenticated/RCE-by-default" is now explicitly not valid; an exposed engine needs server.rmi.ssl.disable=true, classified OUT-OF-MODEL: non-default-config (@vlsi's point — runtime opt-out, no rebuild), added as a distinct §13 disposition. CVE-2019-0187 noted as the class these defaults close.
  • Security Manager: dropped as a recommended defense (§5/§9/§10) — flagged deprecated for removal (JEP 411); the real distributed defense is RMI-over-SSL. Noted security.html's recommendation should be updated.
  • Opening vs running (@vlsi): adopted your line — opening a .jmx is safe given trust in the distribution's existing classes; in-model only if opening generates and executes a new class.
  • Hostile SUT (@vlsi@milamberspace): moved out of model — the tester targets only systems they own/are authorised to test, so parser robustness vs a hostile SUT is hardening, not a claimed property. Marked "pending @milamberspace" in the doc; happy to add a narrow in-model carve-out if you'd both prefer.
  • Added @milamberspace's "disable RMI SSL then expose jmeter-server" misuse (§11). §14 waves 1–2 marked answered.

On the broader "AI-facing files" point (@milamberspace): strongly agree it's worth an ASF-level convention. Happy to help take it to the relevant list.

@milamberspace
Copy link
Copy Markdown
Contributor

Q5 (hostile SUT) — confirmed out of model

Agreed — no in-model carve-out needed.

The trust boundary is clear: an operator tests only systems they own or are authorised to test. Parser robustness against a hostile SUT (XXE on XPath Extractor, ReDoS on regex patterns, unbounded response sizes) is desirable hardening, but classifying it as a claimed security property would essentially mean saying "JMeter is safe to point at attacker-controlled infrastructure" — which is not the intended use case and would be an untenable claim.

Classification: VALID-HARDENING (internal audit track, not an in-model threat). §9 "safe response handling — UNSPECIFIED / best-effort" is the right framing.

The current v1 wording works well. §14 wave 2 can be fully marked answered.

Copy link
Copy Markdown
Contributor

@milamberspace milamberspace left a comment

Choose a reason for hiding this comment

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

One additional suggestion on §2 scope — see inline comment below.

Comment thread THREAT_MODEL.md

## §2 Scope and intended use

- **Primary use:** a **user-run tool** — the person running JMeter authors (or obtains) the `.jmx`, points it at a target they are authorised to test, and runs it locally or across machines they control. *(documented — security.html)*
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

§2 — Suggested addition: authorized penetration testing as a recognized use case

JMeter is widely used — and distributed in penetration testing toolkits — as a tool for authorized security assessments, in addition to its primary load/performance testing purpose. Its scripting capabilities (JSR223/Groovy/BeanShell), parameterized datasets (CSV Dataset Config), and broad protocol support (HTTP, JDBC, LDAP, JMS, …) make it naturally suited for this work.

This does not change the trust model: whether the operator is a performance engineer or an authorized pentester, they are the trusted party running a .jmx they control against a target they are authorized to test.

Suggested addition to §2 (after the "Primary use" bullet):

Secondary / dual-use: JMeter is used by security professionals as a tool for authorized penetration testing and security assessments — rate-limit testing, parameterized enumeration, fuzzing, and custom scenarios via scripting. This is a recognized legitimate use; the operator-trust model is unchanged. Conducting such tests against systems without authorization is a legal and ethical violation, already covered by §11.

Why it matters for triage: without this acknowledgment, a report such as "JMeter can be used to enumerate endpoints / perform credential stuffing / stress-test authentication" might be filed as a vulnerability. With this addition, the triage answer is immediate: BY-DESIGN / OUT-OF-MODEL: trusted-input for authorized pentesting; §11 misuse for unauthorized use.

Note: §11 already carries "Load-testing a third party's system without authorisation (also a legal issue)" (line 123). The suggested §2 addition is the positive counterpart — acknowledging the legitimate use case that makes §11's boundary meaningful.

Comment thread AGENTS.md
Comment on lines +1 to +11
# Agent Guide for jmeter

This file is read by automated agents (security scanners, code
analyzers, AI assistants) operating on this repository.

## Security

Security model: [SECURITY.md](./SECURITY.md)

Agents that scan this repository should consult `SECURITY.md` and the
threat model it links before reporting issues.
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Suggested change
# Agent Guide for jmeter
This file is read by automated agents (security scanners, code
analyzers, AI assistants) operating on this repository.
## Security
Security model: [SECURITY.md](./SECURITY.md)
Agents that scan this repository should consult `SECURITY.md` and the
threat model it links before reporting issues.
Security model: [SECURITY.md](./SECURITY.md)

Frankly, we do not have an overwhelming amount of AI-reported issues (lucky we), so I would prefer to keep a bare minimum in agents.md

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Agree with @vlsi. The surrounding prose explains what AGENTS.md is — useful for a human browsing the repo, redundant for the agent loading it. A single pointer is all that's needed.

If you want to keep the heading # Agent Guide for jmeter for repo discoverability (GitHub's file listing renders it), that's fine but not required. The suggestion works as-is.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants