Skip to content

Add server.request.body.filenames support for Jetty#10988

Merged
gh-worker-dd-mergequeue-cf854d[bot] merged 55 commits into
masterfrom
alejandro.gonzalez/APPSEC-61873-3
Jun 22, 2026
Merged

Add server.request.body.filenames support for Jetty#10988
gh-worker-dd-mergequeue-cf854d[bot] merged 55 commits into
masterfrom
alejandro.gonzalez/APPSEC-61873-3

Conversation

@jandro996

@jandro996 jandro996 commented Mar 27, 2026

Copy link
Copy Markdown
Member

What Does This Do

Instruments Jetty's multipart request handling to fire both requestFilesFilenames and requestBodyProcessed AppSec gateway events, enabling WAF rules that act on uploaded filenames and multipart form-field values.

Jetty parses multipart bodies through two code paths depending on how the application accesses the request:

  • getParameterMap()extractContentParameters() → internal getParts(MultiMap)
  • Direct getParts() / getPart(String) call from user code

Both paths are instrumented where they exist. Guards on internal state fields (_contentParameters, _multiParts, _multiPartInputStream) prevent double-firing when the same method is called more than once per request.

Module coverage

Module Status Servlet API Coverage Notes
jetty-appsec-7.0 unchanged javax ≤ 8.1.2 getParts() does not exist in Jetty 7.x
jetty-appsec-8.1.3 rewritten javax (3.0) [8.1.3, 9.2.0.RC0) See below
jetty-appsec-9.2 unchanged javax [9.2, 9.3) Already fires requestBodyProcessed; no filename support needed at this range
jetty-appsec-9.3 modified javax [9.3, 9.4.10) Narrowed from [9.3, 12). Added GetFilenamesAdvice + GetFilenamesFromMultiPartAdvice. Double-fire guard uses _multiPartInputStream.
jetty-appsec-9.4 new javax [9.4.10, 11.0) Covers Jetty 9.4.10–10.x. OR-muzzle reference handles _multiParts: MultiParts (9.4.10+, 10.0.10+) and _multiParts: MultiPartFormInputStream (10.0.0–10.0.9). Bytecode discriminator: _dispatcherType: Ljavax/servlet/DispatcherType;.
jetty-appsec-11.0 new jakarta [11.0, 12.0) Jakarta Servlet. Muzzle discriminator: _dispatcherType: Ljakarta/servlet/DispatcherType; in Request.class bytecode — checking classpath presence of jakarta.servlet was unreliable when both javax and jakarta were present simultaneously.

jetty-appsec-8.1.3 details

Replaced brittle ASM bytecode injection (GetPartsMethodVisitor) with clean exit advice:

  • GetFilenamesAdvice on getParts() — fires requestBodyProcessed (form fields) + requestFilesFilenames
  • GetPartAdvice on getPart(String) — same, for single-part uploads that never call getParts(); in Jetty 8.x getPart(String) calls _multiPartInputStream.getPart() directly
  • _multiPartInputStream == null guard prevents re-firing on repeated getParts() calls
  • Classloader matcher uses ASM to inspect Request.class bytecode at agent startup, detecting the getParameters() call inside getParts() that is unique to Jetty 8.x

PartHelper (replacing ParameterCollector):

  • filenameFromPart(): quote-aware Content-Disposition parser; Part.getSubmittedFileName() is Servlet 3.1+ and unavailable here
  • extractFormFields(): reads form-field body values, skips file-upload parts
  • fireBodyProcessedEvent() / fireFilenamesEvent(): encapsulate the full IG callback + blocking-commit logic, returning BlockingException|null for use with @Advice.Thrown(readOnly=false)

Helpers and tests

  • PartHelper (8.1.3) and MultipartHelper (9.3/9.4/11.0) are unit-tested independently in PartHelperTest / MultipartHelperTest
  • Integration tests in jetty-server-7.6, jetty-server-9.3, jetty-server-9.4.21, jetty-server-10.0, jetty-server-11.0 cover both the getParts() and getParameterMap() code paths; a dedicated Jetty10EarlyDepForkedTest covers Jetty 10.0.0–10.0.9

Motivation

Part of APPSEC-61873server.request.body.filenames implementation across server frameworks.

Additional Notes

Bytecode discriminator rationale (9.4 vs 11.0): checking for jakarta.servlet.http.Part on the classpath was unreliable when a Jetty 9.4/10 app ships both javax and jakarta on the classpath (common with Spring Boot 3 migration tooling). The _dispatcherType field descriptor inside Request.class bytecode is unambiguous regardless of classpath contents.

GetFilenamesFromMultiPartAdvice vs GetFilenamesAdvice: in all 9.3/9.4/11.0 modules, extractContentParameters() sets _contentParameters before calling getParts(MultiMap), so the _contentParameters != null guard in GetFilenamesAdvice correctly prevents double-firing when the getParameterMap() path is used.

Part.getSubmittedFileName() availability: Servlet 3.1+ (Jetty 9.1+). For Jetty 8.x (Servlet 3.0), PartHelper.filenameFromPart() parses the Content-Disposition header manually with a quote-aware parser to handle filenames like filename="shell;evil.php" correctly.

Depends on #10973 (merged)

Contributor Checklist

Jira ticket: APPSEC-61873

Note: Once your PR is ready to merge, add it to the merge queue by commenting /merge. /merge -c cancels the queue request. /merge -f --reason "reason" skips all merge queue checks; please use this judiciously, as some checks do not run at the PR-level. For more information, see this doc.

@jandro996 jandro996 added comp: asm waf Application Security Management (WAF) type: enhancement Enhancements and improvements labels Mar 27, 2026
@pr-commenter

pr-commenter Bot commented Mar 27, 2026

Copy link
Copy Markdown

Benchmarks

Startup

Parameters

Baseline Candidate
baseline_or_candidate baseline candidate
git_branch master alejandro.gonzalez/APPSEC-61873-3
git_commit_date 1777558537 1777557999
git_commit_sha d1c44d515b 9b0ff19a18
release_version 1.62.0-SNAPSHOT~0d1c44d515b 1.62.0-SNAPSHOT~9b0ff19a18
See matching parameters
Baseline Candidate
application insecure-bank insecure-bank
ci_job_date 1777559828 1777559828
ci_job_id 1646426464 1646426464
ci_pipeline_id 110710464 110710464
cpu_model Intel(R) Xeon(R) Platinum 8259CL CPU @ 2.50GHz Intel(R) Xeon(R) Platinum 8259CL CPU @ 2.50GHz
kernel_version Linux runner-zfyrx7zua-project-304-concurrent-2-argm8r4n 6.8.0-1031-aws #33~22.04.1-Ubuntu SMP Thu Jun 26 14:22:30 UTC 2025 x86_64 x86_64 x86_64 GNU/Linux Linux runner-zfyrx7zua-project-304-concurrent-2-argm8r4n 6.8.0-1031-aws #33~22.04.1-Ubuntu SMP Thu Jun 26 14:22:30 UTC 2025 x86_64 x86_64 x86_64 GNU/Linux
module Agent Agent
parent None None

Summary

Found 0 performance improvements and 0 performance regressions! Performance is the same for 64 metrics, 7 unstable metrics.

Startup time reports for insecure-bank
gantt
    title insecure-bank - global startup overhead: candidate=1.62.0-SNAPSHOT~9b0ff19a18, baseline=1.62.0-SNAPSHOT~0d1c44d515b

    dateFormat X
    axisFormat %s
section tracing
Agent [baseline] (1.062 s) : 0, 1062389
Total [baseline] (8.876 s) : 0, 8875655
Agent [candidate] (1.068 s) : 0, 1068271
Total [candidate] (8.908 s) : 0, 8907523
section iast
Agent [baseline] (1.24 s) : 0, 1240195
Total [baseline] (9.577 s) : 0, 9577286
Agent [candidate] (1.235 s) : 0, 1234803
Total [candidate] (9.528 s) : 0, 9527909
Loading
  • baseline results
Module Variant Duration Δ tracing
Agent tracing 1.062 s -
Agent iast 1.24 s 177.806 ms (16.7%)
Total tracing 8.876 s -
Total iast 9.577 s 701.631 ms (7.9%)
  • candidate results
Module Variant Duration Δ tracing
Agent tracing 1.068 s -
Agent iast 1.235 s 166.531 ms (15.6%)
Total tracing 8.908 s -
Total iast 9.528 s 620.386 ms (7.0%)
gantt
    title insecure-bank - break down per module: candidate=1.62.0-SNAPSHOT~9b0ff19a18, baseline=1.62.0-SNAPSHOT~0d1c44d515b

    dateFormat X
    axisFormat %s
section tracing
crashtracking [baseline] (1.255 ms) : 0, 1255
crashtracking [candidate] (1.242 ms) : 0, 1242
BytebuddyAgent [baseline] (639.515 ms) : 0, 639515
BytebuddyAgent [candidate] (642.042 ms) : 0, 642042
AgentMeter [baseline] (29.588 ms) : 0, 29588
AgentMeter [candidate] (29.715 ms) : 0, 29715
GlobalTracer [baseline] (250.334 ms) : 0, 250334
GlobalTracer [candidate] (251.25 ms) : 0, 251250
AppSec [baseline] (32.529 ms) : 0, 32529
AppSec [candidate] (32.72 ms) : 0, 32720
Debugger [baseline] (59.935 ms) : 0, 59935
Debugger [candidate] (60.245 ms) : 0, 60245
Remote Config [baseline] (606.238 µs) : 0, 606
Remote Config [candidate] (602.292 µs) : 0, 602
Telemetry [baseline] (8.012 ms) : 0, 8012
Telemetry [candidate] (8.154 ms) : 0, 8154
Flare Poller [baseline] (4.332 ms) : 0, 4332
Flare Poller [candidate] (5.959 ms) : 0, 5959
section iast
crashtracking [baseline] (1.252 ms) : 0, 1252
crashtracking [candidate] (1.222 ms) : 0, 1222
BytebuddyAgent [baseline] (816.817 ms) : 0, 816817
BytebuddyAgent [candidate] (812.794 ms) : 0, 812794
AgentMeter [baseline] (11.5 ms) : 0, 11500
AgentMeter [candidate] (11.409 ms) : 0, 11409
GlobalTracer [baseline] (240.264 ms) : 0, 240264
GlobalTracer [candidate] (239.458 ms) : 0, 239458
IAST [baseline] (29.038 ms) : 0, 29038
IAST [candidate] (30.121 ms) : 0, 30121
AppSec [baseline] (29.149 ms) : 0, 29149
AppSec [candidate] (29.231 ms) : 0, 29231
Debugger [baseline] (64.101 ms) : 0, 64101
Debugger [candidate] (62.85 ms) : 0, 62850
Remote Config [baseline] (538.745 µs) : 0, 539
Remote Config [candidate] (528.611 µs) : 0, 529
Telemetry [baseline] (7.828 ms) : 0, 7828
Telemetry [candidate] (7.705 ms) : 0, 7705
Flare Poller [baseline] (3.481 ms) : 0, 3481
Flare Poller [candidate] (3.411 ms) : 0, 3411
Loading
Startup time reports for petclinic
gantt
    title petclinic - global startup overhead: candidate=1.62.0-SNAPSHOT~9b0ff19a18, baseline=1.62.0-SNAPSHOT~0d1c44d515b

    dateFormat X
    axisFormat %s
section tracing
Agent [baseline] (1.061 s) : 0, 1061332
Total [baseline] (11.066 s) : 0, 11065511
Agent [candidate] (1.057 s) : 0, 1057252
Total [candidate] (11.041 s) : 0, 11041446
section appsec
Agent [baseline] (1.267 s) : 0, 1266839
Total [baseline] (11.05 s) : 0, 11050431
Agent [candidate] (1.261 s) : 0, 1261356
Total [candidate] (11.066 s) : 0, 11065620
section iast
Agent [baseline] (1.232 s) : 0, 1232242
Total [baseline] (11.243 s) : 0, 11243326
Agent [candidate] (1.234 s) : 0, 1233920
Total [candidate] (11.35 s) : 0, 11350091
section profiling
Agent [baseline] (1.195 s) : 0, 1194902
Total [baseline] (11.058 s) : 0, 11058385
Agent [candidate] (1.184 s) : 0, 1183963
Total [candidate] (11.046 s) : 0, 11045662
Loading
  • baseline results
Module Variant Duration Δ tracing
Agent tracing 1.061 s -
Agent appsec 1.267 s 205.506 ms (19.4%)
Agent iast 1.232 s 170.909 ms (16.1%)
Agent profiling 1.195 s 133.57 ms (12.6%)
Total tracing 11.066 s -
Total appsec 11.05 s -15.08 ms (-0.1%)
Total iast 11.243 s 177.815 ms (1.6%)
Total profiling 11.058 s -7.126 ms (-0.1%)
  • candidate results
Module Variant Duration Δ tracing
Agent tracing 1.057 s -
Agent appsec 1.261 s 204.104 ms (19.3%)
Agent iast 1.234 s 176.668 ms (16.7%)
Agent profiling 1.184 s 126.711 ms (12.0%)
Total tracing 11.041 s -
Total appsec 11.066 s 24.173 ms (0.2%)
Total iast 11.35 s 308.645 ms (2.8%)
Total profiling 11.046 s 4.215 ms (0.0%)
gantt
    title petclinic - break down per module: candidate=1.62.0-SNAPSHOT~9b0ff19a18, baseline=1.62.0-SNAPSHOT~0d1c44d515b

    dateFormat X
    axisFormat %s
section tracing
crashtracking [baseline] (1.252 ms) : 0, 1252
crashtracking [candidate] (1.226 ms) : 0, 1226
BytebuddyAgent [baseline] (638.76 ms) : 0, 638760
BytebuddyAgent [candidate] (635.801 ms) : 0, 635801
AgentMeter [baseline] (29.337 ms) : 0, 29337
AgentMeter [candidate] (29.453 ms) : 0, 29453
GlobalTracer [baseline] (249.891 ms) : 0, 249891
GlobalTracer [candidate] (248.588 ms) : 0, 248588
AppSec [baseline] (32.663 ms) : 0, 32663
AppSec [candidate] (32.235 ms) : 0, 32235
Debugger [baseline] (60.819 ms) : 0, 60819
Debugger [candidate] (60.665 ms) : 0, 60665
Remote Config [baseline] (602.287 µs) : 0, 602
Remote Config [candidate] (603.281 µs) : 0, 603
Telemetry [baseline] (8.1 ms) : 0, 8100
Telemetry [candidate] (8.853 ms) : 0, 8853
Flare Poller [baseline] (3.583 ms) : 0, 3583
Flare Poller [candidate] (3.566 ms) : 0, 3566
section appsec
crashtracking [baseline] (1.225 ms) : 0, 1225
crashtracking [candidate] (1.218 ms) : 0, 1218
BytebuddyAgent [baseline] (677.745 ms) : 0, 677745
BytebuddyAgent [candidate] (674.9 ms) : 0, 674900
AgentMeter [baseline] (12.386 ms) : 0, 12386
AgentMeter [candidate] (12.308 ms) : 0, 12308
GlobalTracer [baseline] (249.663 ms) : 0, 249663
GlobalTracer [candidate] (249.14 ms) : 0, 249140
IAST [baseline] (24.271 ms) : 0, 24271
IAST [candidate] (24.243 ms) : 0, 24243
AppSec [baseline] (187.128 ms) : 0, 187128
AppSec [candidate] (187.695 ms) : 0, 187695
Debugger [baseline] (65.925 ms) : 0, 65925
Debugger [candidate] (63.847 ms) : 0, 63847
Remote Config [baseline] (580.033 µs) : 0, 580
Remote Config [candidate] (571.177 µs) : 0, 571
Telemetry [baseline] (7.958 ms) : 0, 7958
Telemetry [candidate] (7.735 ms) : 0, 7735
Flare Poller [baseline] (3.477 ms) : 0, 3477
Flare Poller [candidate] (3.434 ms) : 0, 3434
section iast
crashtracking [baseline] (1.225 ms) : 0, 1225
crashtracking [candidate] (1.238 ms) : 0, 1238
BytebuddyAgent [baseline] (809.553 ms) : 0, 809553
BytebuddyAgent [candidate] (809.698 ms) : 0, 809698
AgentMeter [baseline] (11.378 ms) : 0, 11378
AgentMeter [candidate] (11.404 ms) : 0, 11404
GlobalTracer [baseline] (238.51 ms) : 0, 238510
GlobalTracer [candidate] (238.921 ms) : 0, 238921
IAST [baseline] (28.347 ms) : 0, 28347
IAST [candidate] (27.68 ms) : 0, 27680
AppSec [baseline] (30.353 ms) : 0, 30353
AppSec [candidate] (29.587 ms) : 0, 29587
Debugger [baseline] (64.924 ms) : 0, 64924
Debugger [candidate] (67.133 ms) : 0, 67133
Remote Config [baseline] (535.877 µs) : 0, 536
Remote Config [candidate] (558.104 µs) : 0, 558
Telemetry [baseline] (7.811 ms) : 0, 7811
Telemetry [candidate] (7.945 ms) : 0, 7945
Flare Poller [baseline] (3.526 ms) : 0, 3526
Flare Poller [candidate] (3.572 ms) : 0, 3572
section profiling
crashtracking [baseline] (1.185 ms) : 0, 1185
crashtracking [candidate] (1.181 ms) : 0, 1181
BytebuddyAgent [baseline] (699.262 ms) : 0, 699262
BytebuddyAgent [candidate] (691.419 ms) : 0, 691419
AgentMeter [baseline] (9.07 ms) : 0, 9070
AgentMeter [candidate] (8.933 ms) : 0, 8933
GlobalTracer [baseline] (208.663 ms) : 0, 208663
GlobalTracer [candidate] (207.639 ms) : 0, 207639
AppSec [baseline] (32.756 ms) : 0, 32756
AppSec [candidate] (32.451 ms) : 0, 32451
Debugger [baseline] (66.0 ms) : 0, 66000
Debugger [candidate] (65.41 ms) : 0, 65410
Remote Config [baseline] (574.953 µs) : 0, 575
Remote Config [candidate] (570.796 µs) : 0, 571
Telemetry [baseline] (7.829 ms) : 0, 7829
Telemetry [candidate] (7.73 ms) : 0, 7730
Flare Poller [baseline] (3.536 ms) : 0, 3536
Flare Poller [candidate] (3.493 ms) : 0, 3493
ProfilingAgent [baseline] (94.038 ms) : 0, 94038
ProfilingAgent [candidate] (93.623 ms) : 0, 93623
Profiling [baseline] (94.601 ms) : 0, 94601
Profiling [candidate] (94.183 ms) : 0, 94183
Loading

Load

Parameters

Baseline Candidate
baseline_or_candidate baseline candidate
git_branch master alejandro.gonzalez/APPSEC-61873-3
git_commit_date 1777558537 1777557999
git_commit_sha d1c44d515b 9b0ff19a18
release_version 1.62.0-SNAPSHOT~0d1c44d515b 1.62.0-SNAPSHOT~9b0ff19a18
See matching parameters
Baseline Candidate
application insecure-bank insecure-bank
ci_job_date 1777560310 1777560310
ci_job_id 1646426467 1646426467
ci_pipeline_id 110710464 110710464
cpu_model Intel(R) Xeon(R) Platinum 8259CL CPU @ 2.50GHz Intel(R) Xeon(R) Platinum 8259CL CPU @ 2.50GHz
kernel_version Linux runner-zfyrx7zua-project-304-concurrent-1-kf08f9go 6.8.0-1031-aws #33~22.04.1-Ubuntu SMP Thu Jun 26 14:22:30 UTC 2025 x86_64 x86_64 x86_64 GNU/Linux Linux runner-zfyrx7zua-project-304-concurrent-1-kf08f9go 6.8.0-1031-aws #33~22.04.1-Ubuntu SMP Thu Jun 26 14:22:30 UTC 2025 x86_64 x86_64 x86_64 GNU/Linux

Summary

Found 0 performance improvements and 2 performance regressions! Performance is the same for 18 metrics, 16 unstable metrics.

scenario Δ mean agg_http_req_duration_p50 Δ mean agg_http_req_duration_p95 Δ mean throughput candidate mean agg_http_req_duration_p50 candidate mean agg_http_req_duration_p95 candidate mean throughput baseline mean agg_http_req_duration_p50 baseline mean agg_http_req_duration_p95 baseline mean throughput
scenario:load:petclinic:iast:high_load worse
[+438.363µs; +920.810µs] or [+2.539%; +5.334%]
same
[-117.116µs; +972.792µs] or [-0.409%; +3.398%]
unstable
[-35.773op/s; +21.023op/s] or [-13.576%; +7.979%]
17.942ms 29.054ms 256.125op/s 17.262ms 28.626ms 263.500op/s
scenario:load:petclinic:appsec:high_load worse
[+385.892µs; +1241.615µs] or [+2.109%; +6.785%]
same
[-199.017µs; +1310.303µs] or [-0.659%; +4.339%]
unstable
[-35.971op/s; +18.159op/s] or [-14.432%; +7.285%]
19.113ms 30.751ms 240.344op/s 18.299ms 30.195ms 249.250op/s
Request duration reports for petclinic
gantt
    title petclinic - request duration [CI 0.99] : candidate=1.62.0-SNAPSHOT~9b0ff19a18, baseline=1.62.0-SNAPSHOT~0d1c44d515b
    dateFormat X
    axisFormat %s
section baseline
no_agent (19.044 ms) : 18853, 19236
.   : milestone, 19044,
appsec (18.724 ms) : 18532, 18916
.   : milestone, 18724,
code_origins (17.881 ms) : 17704, 18058
.   : milestone, 17881,
iast (17.707 ms) : 17531, 17884
.   : milestone, 17707,
profiling (18.937 ms) : 18745, 19129
.   : milestone, 18937,
tracing (17.495 ms) : 17323, 17666
.   : milestone, 17495,
section candidate
no_agent (18.287 ms) : 18098, 18476
.   : milestone, 18287,
appsec (19.426 ms) : 19230, 19621
.   : milestone, 19426,
code_origins (18.015 ms) : 17835, 18195
.   : milestone, 18015,
iast (18.222 ms) : 18043, 18400
.   : milestone, 18222,
profiling (18.315 ms) : 18132, 18499
.   : milestone, 18315,
tracing (17.728 ms) : 17554, 17903
.   : milestone, 17728,
Loading
  • baseline results
Variant Request duration [CI 0.99] Δ no_agent
no_agent 19.044 ms [18.853 ms, 19.236 ms] -
appsec 18.724 ms [18.532 ms, 18.916 ms] -319.844 µs (-1.7%)
code_origins 17.881 ms [17.704 ms, 18.058 ms] -1.163 ms (-6.1%)
iast 17.707 ms [17.531 ms, 17.884 ms] -1.337 ms (-7.0%)
profiling 18.937 ms [18.745 ms, 19.129 ms] -107.217 µs (-0.6%)
tracing 17.495 ms [17.323 ms, 17.666 ms] -1.549 ms (-8.1%)
  • candidate results
Variant Request duration [CI 0.99] Δ no_agent
no_agent 18.287 ms [18.098 ms, 18.476 ms] -
appsec 19.426 ms [19.23 ms, 19.621 ms] 1.139 ms (6.2%)
code_origins 18.015 ms [17.835 ms, 18.195 ms] -272.167 µs (-1.5%)
iast 18.222 ms [18.043 ms, 18.4 ms] -65.567 µs (-0.4%)
profiling 18.315 ms [18.132 ms, 18.499 ms] 28.177 µs (0.2%)
tracing 17.728 ms [17.554 ms, 17.903 ms] -558.716 µs (-3.1%)
Request duration reports for insecure-bank
gantt
    title insecure-bank - request duration [CI 0.99] : candidate=1.62.0-SNAPSHOT~9b0ff19a18, baseline=1.62.0-SNAPSHOT~0d1c44d515b
    dateFormat X
    axisFormat %s
section baseline
no_agent (1.257 ms) : 1244, 1270
.   : milestone, 1257,
iast (3.314 ms) : 3267, 3362
.   : milestone, 3314,
iast_FULL (5.948 ms) : 5888, 6008
.   : milestone, 5948,
iast_GLOBAL (3.716 ms) : 3654, 3778
.   : milestone, 3716,
profiling (2.293 ms) : 2271, 2314
.   : milestone, 2293,
tracing (1.959 ms) : 1943, 1975
.   : milestone, 1959,
section candidate
no_agent (1.329 ms) : 1315, 1343
.   : milestone, 1329,
iast (3.336 ms) : 3290, 3381
.   : milestone, 3336,
iast_FULL (6.059 ms) : 5997, 6122
.   : milestone, 6059,
iast_GLOBAL (3.742 ms) : 3676, 3808
.   : milestone, 3742,
profiling (2.294 ms) : 2272, 2316
.   : milestone, 2294,
tracing (1.989 ms) : 1969, 2008
.   : milestone, 1989,
Loading
  • baseline results
Variant Request duration [CI 0.99] Δ no_agent
no_agent 1.257 ms [1.244 ms, 1.27 ms] -
iast 3.314 ms [3.267 ms, 3.362 ms] 2.058 ms (163.7%)
iast_FULL 5.948 ms [5.888 ms, 6.008 ms] 4.691 ms (373.2%)
iast_GLOBAL 3.716 ms [3.654 ms, 3.778 ms] 2.459 ms (195.6%)
profiling 2.293 ms [2.271 ms, 2.314 ms] 1.036 ms (82.4%)
tracing 1.959 ms [1.943 ms, 1.975 ms] 702.061 µs (55.9%)
  • candidate results
Variant Request duration [CI 0.99] Δ no_agent
no_agent 1.329 ms [1.315 ms, 1.343 ms] -
iast 3.336 ms [3.29 ms, 3.381 ms] 2.006 ms (150.9%)
iast_FULL 6.059 ms [5.997 ms, 6.122 ms] 4.73 ms (355.8%)
iast_GLOBAL 3.742 ms [3.676 ms, 3.808 ms] 2.413 ms (181.5%)
profiling 2.294 ms [2.272 ms, 2.316 ms] 964.795 µs (72.6%)
tracing 1.989 ms [1.969 ms, 2.008 ms] 659.224 µs (49.6%)

Dacapo

Parameters

Baseline Candidate
baseline_or_candidate baseline candidate
git_branch master alejandro.gonzalez/APPSEC-61873-3
git_commit_date 1777558538 1777557999
git_commit_sha d1c44d515b 9b0ff19a18
release_version 1.62.0-SNAPSHOT~0d1c44d515b 1.62.0-SNAPSHOT~9b0ff19a18
See matching parameters
Baseline Candidate
application biojava biojava
ci_job_date 1777560051 1777560051
ci_job_id 1646426470 1646426470
ci_pipeline_id 110710464 110710464
cpu_model Intel(R) Xeon(R) Platinum 8259CL CPU @ 2.50GHz Intel(R) Xeon(R) Platinum 8259CL CPU @ 2.50GHz
kernel_version Linux runner-zfyrx7zua-project-304-concurrent-2-s54fm3m5 6.8.0-1031-aws #33~22.04.1-Ubuntu SMP Thu Jun 26 14:22:30 UTC 2025 x86_64 x86_64 x86_64 GNU/Linux Linux runner-zfyrx7zua-project-304-concurrent-2-s54fm3m5 6.8.0-1031-aws #33~22.04.1-Ubuntu SMP Thu Jun 26 14:22:30 UTC 2025 x86_64 x86_64 x86_64 GNU/Linux

Summary

Found 0 performance improvements and 0 performance regressions! Performance is the same for 11 metrics, 1 unstable metrics.

Execution time for tomcat
gantt
    title tomcat - execution time [CI 0.99] : candidate=1.62.0-SNAPSHOT~9b0ff19a18, baseline=1.62.0-SNAPSHOT~0d1c44d515b
    dateFormat X
    axisFormat %s
section baseline
no_agent (1.483 ms) : 1471, 1494
.   : milestone, 1483,
appsec (2.535 ms) : 2481, 2590
.   : milestone, 2535,
iast (2.274 ms) : 2204, 2344
.   : milestone, 2274,
iast_GLOBAL (2.319 ms) : 2249, 2390
.   : milestone, 2319,
profiling (2.104 ms) : 2049, 2159
.   : milestone, 2104,
tracing (2.083 ms) : 2029, 2137
.   : milestone, 2083,
section candidate
no_agent (1.486 ms) : 1475, 1498
.   : milestone, 1486,
appsec (3.821 ms) : 3600, 4042
.   : milestone, 3821,
iast (2.277 ms) : 2207, 2347
.   : milestone, 2277,
iast_GLOBAL (2.327 ms) : 2256, 2398
.   : milestone, 2327,
profiling (2.109 ms) : 2054, 2165
.   : milestone, 2109,
tracing (2.075 ms) : 2022, 2129
.   : milestone, 2075,
Loading
  • baseline results
Variant Execution Time [CI 0.99] Δ no_agent
no_agent 1.483 ms [1.471 ms, 1.494 ms] -
appsec 2.535 ms [2.481 ms, 2.59 ms] 1.053 ms (71.0%)
iast 2.274 ms [2.204 ms, 2.344 ms] 791.785 µs (53.4%)
iast_GLOBAL 2.319 ms [2.249 ms, 2.39 ms] 836.655 µs (56.4%)
profiling 2.104 ms [2.049 ms, 2.159 ms] 621.244 µs (41.9%)
tracing 2.083 ms [2.029 ms, 2.137 ms] 600.66 µs (40.5%)
  • candidate results
Variant Execution Time [CI 0.99] Δ no_agent
no_agent 1.486 ms [1.475 ms, 1.498 ms] -
appsec 3.821 ms [3.6 ms, 4.042 ms] 2.335 ms (157.1%)
iast 2.277 ms [2.207 ms, 2.347 ms] 790.515 µs (53.2%)
iast_GLOBAL 2.327 ms [2.256 ms, 2.398 ms] 840.633 µs (56.6%)
profiling 2.109 ms [2.054 ms, 2.165 ms] 622.885 µs (41.9%)
tracing 2.075 ms [2.022 ms, 2.129 ms] 588.954 µs (39.6%)
Execution time for biojava
gantt
    title biojava - execution time [CI 0.99] : candidate=1.62.0-SNAPSHOT~9b0ff19a18, baseline=1.62.0-SNAPSHOT~0d1c44d515b
    dateFormat X
    axisFormat %s
section baseline
no_agent (15.044 s) : 15044000, 15044000
.   : milestone, 15044000,
appsec (14.946 s) : 14946000, 14946000
.   : milestone, 14946000,
iast (18.241 s) : 18241000, 18241000
.   : milestone, 18241000,
iast_GLOBAL (18.239 s) : 18239000, 18239000
.   : milestone, 18239000,
profiling (15.006 s) : 15006000, 15006000
.   : milestone, 15006000,
tracing (15.107 s) : 15107000, 15107000
.   : milestone, 15107000,
section candidate
no_agent (15.11 s) : 15110000, 15110000
.   : milestone, 15110000,
appsec (14.987 s) : 14987000, 14987000
.   : milestone, 14987000,
iast (18.57 s) : 18570000, 18570000
.   : milestone, 18570000,
iast_GLOBAL (17.755 s) : 17755000, 17755000
.   : milestone, 17755000,
profiling (14.923 s) : 14923000, 14923000
.   : milestone, 14923000,
tracing (15.129 s) : 15129000, 15129000
.   : milestone, 15129000,
Loading
  • baseline results
Variant Execution Time [CI 0.99] Δ no_agent
no_agent 15.044 s [15.044 s, 15.044 s] -
appsec 14.946 s [14.946 s, 14.946 s] -98.0 ms (-0.7%)
iast 18.241 s [18.241 s, 18.241 s] 3.197 s (21.3%)
iast_GLOBAL 18.239 s [18.239 s, 18.239 s] 3.195 s (21.2%)
profiling 15.006 s [15.006 s, 15.006 s] -38.0 ms (-0.3%)
tracing 15.107 s [15.107 s, 15.107 s] 63.0 ms (0.4%)
  • candidate results
Variant Execution Time [CI 0.99] Δ no_agent
no_agent 15.11 s [15.11 s, 15.11 s] -
appsec 14.987 s [14.987 s, 14.987 s] -123.0 ms (-0.8%)
iast 18.57 s [18.57 s, 18.57 s] 3.46 s (22.9%)
iast_GLOBAL 17.755 s [17.755 s, 17.755 s] 2.645 s (17.5%)
profiling 14.923 s [14.923 s, 14.923 s] -187.0 ms (-1.2%)
tracing 15.129 s [15.129 s, 15.129 s] 19.0 ms (0.1%)

@jandro996 jandro996 force-pushed the alejandro.gonzalez/APPSEC-61873-3 branch 2 times, most recently from f2998c3 to 629f074 Compare April 6, 2026 10:50
@jandro996 jandro996 marked this pull request as ready for review April 6, 2026 13:08
@jandro996 jandro996 requested a review from a team as a code owner April 6, 2026 13:08

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c732823549

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@jandro996 jandro996 force-pushed the alejandro.gonzalez/APPSEC-61873-3 branch 4 times, most recently from 3ab9ff7 to 77ec572 Compare April 7, 2026 07:33
@jandro996 jandro996 enabled auto-merge April 7, 2026 08:52
@jandro996 jandro996 force-pushed the alejandro.gonzalez/APPSEC-61873-3 branch from 2e72584 to d37e03e Compare April 7, 2026 09:23
@jandro996 jandro996 disabled auto-merge April 7, 2026 09:42
@jandro996 jandro996 marked this pull request as draft April 7, 2026 09:48
@jandro996 jandro996 force-pushed the alejandro.gonzalez/APPSEC-61873-3 branch from d37e03e to d8a92f8 Compare April 7, 2026 09:52
jandro996 added a commit to DataDog/system-tests that referenced this pull request Apr 9, 2026
Jetty multipart file upload instrumentation fixed in
DataDog/dd-trace-java#10988 (Jetty 8.1.3, 9.3–11).
@github-actions

Copy link
Copy Markdown
Contributor

Hi! 👋 Looks like you updated a Git Submodule.
If this was not intentional please make sure to:

3 similar comments
@github-actions

Copy link
Copy Markdown
Contributor

Hi! 👋 Looks like you updated a Git Submodule.
If this was not intentional please make sure to:

@github-actions

Copy link
Copy Markdown
Contributor

Hi! 👋 Looks like you updated a Git Submodule.
If this was not intentional please make sure to:

@github-actions

Copy link
Copy Markdown
Contributor

Hi! 👋 Looks like you updated a Git Submodule.
If this was not intentional please make sure to:

@jandro996 jandro996 marked this pull request as ready for review April 20, 2026 08:13
@jandro996 jandro996 requested review from a team as code owners April 20, 2026 08:13
jandro996 added 12 commits June 16, 2026 10:04
…st the singleton

When getPart("field") is the app's first multipart access, Jetty 8 parses the entire
multipart stream and caches all parts in _multiPartInputStream, but only returns the
one requested part. The previous advice forwarded just that singleton to AppSec, so any
co-uploaded file parts were invisible to requestFilesFilenames — a WAF bypass if the
app never called getParts() explicitly.

Fix: read all cached parts via MultiPartInputStream.getParts() (reflected, cached handle)
and fall back to the singleton only when reflection fails. Also remove the part==null
early return: even if the requested field was not found, other file parts may have parsed.

Add PartHelper.getAllParts(Object, Part) + FakeMpi unit tests.
The Part.class depth check only prevents re-entry within a single getPart() invocation;
after the call returns the depth is 0 again, so a second getPart("file") call on the
same request would re-fire requestBodyProcessed and requestFilesFilenames with the same
cached parts. Add the same _multiPartInputStream == null guard that GetFilenamesAdvice
already uses: once the field is set the multipart body was parsed and events were already
dispatched — skip.
…ields

Hard-coded UTF-8 in readPartContent() caused mojibake for fields with
Content-Type: text/plain; charset=ISO-8859-1 (or any non-UTF-8 charset).
Now parses the charset from the part's Content-Type header and falls
back to UTF-8 only when absent or unrecognised.
…p10ForkedTest

The lockfile was out of sync: testCompileClasspath was locked to 10.0.0
instead of 10.0.10, and earlyDep10ForkedTest configurations were missing
entirely. Regenerated with --write-locks.

Also added a resolutionStrategy to force Jetty 10.0.9 for
earlyDep10ForkedTest: without it, Gradle picks 10.0.10 (inherited via
testImplementation), defeating the purpose of the suite which exercises
the MultiPartFormInputStream path present in [10.0.0, 10.0.10).
…t check, per-part try/catch

- Fix body+filenames ordering bug in jetty8 GetFilenamesAdvice and GetPartAdvice:
  both callbacks now always fire even when body blocks (pendingBlock pattern, appsec-ig-events §7)
- Add requestFilesFilenames support for jetty-appsec-9.2 ([9.2, 9.3)): new MultipartHelper,
  GetFilenamesAdvice, GetFilenamesFromMultiPartAdvice, and integration test Jetty92LatestDepForkedTest
- Fix tryCommitBlockingResponse() return not checked before effectivelyBlocked() in
  PartHelper (jetty8) and MultipartHelper (jetty9.3, 9.4, 11.0)
- Add per-part try/catch in extractFilenames() and extractFormFields() loops in
  PartHelper and all MultipartHelper implementations
Satisfies the enforce-groovy-migration CI check. All 8 Groovy test files
introduced in this PR are replaced by Java equivalents:

- jetty-appsec-8.1.3: PartHelperTest (36 unit tests via Mockito)
- jetty-appsec-9.2/9.3/9.4: MultipartHelperTest (8 unit tests each)
- jetty-appsec-11.0: MultipartHelperTest using jakarta.servlet.http.Part
- jetty-server-7.6: Jetty8LatestDepForkedTest (extends Jetty76Test)
- jetty-server-9.0.4: Jetty92LatestDepForkedTest (extends Jetty9Test)
- jetty-server-10.0: Jetty10EarlyDepForkedTest (extends Jetty10Test)

Integration tests extending Groovy Spock base classes are placed in
src/test/groovy/ as .java files so groovyc compiles them together with
the Groovy classes they extend, avoiding the compileTestJava ordering
issue. This pattern is already used elsewhere in the repo.
…ement

- Add explanatory comments to all catch(Exception ignored) blocks in
  PartHelper and MultipartHelper across jetty8/9.2/9.3/9.4/11.0 modules,
  consistent with the pattern used in Jersey and GlassFish helpers
- Remove spurious CallDepthThreadLocalMap.decrementCallDepth(Request.class)
  from jetty92 GetPartsAdvice.after() which had no matching increment and
  was corrupting the thread-local counter
- ArrayList(parts.size()) in extractFilenames() across all 5 modules to
  pre-size the list and avoid rehashing
- Callback-before-extract optimization in fireFilenamesEvent() and
  fireBodyProcessedEvent(): check callback != null before calling
  extractFilenames()/extractFormFields() so we skip Part iteration when
  AppSec is not listening
- WithTypeStructure.structureMatcher() in jetty-appsec-11.0 to replace
  additionalMuzzleReferences() for javax vs jakarta DispatcherType
  discrimination; evaluated per-class at transformation time
- Skip-increment optimization in ExtractContentParametersAdvice and
  GetFilenamesAdvice (jetty-appsec-11.0): return false early when guard
  fields are already non-null, avoiding CallDepthThreadLocalMap overhead
  on cached repeat calls
- IODH + MethodHandle in PartHelper (jetty-appsec-8.1.3) replacing
  volatile Method for MultiPartInputStream.getParts() resolution; inner
  class MpiGetPartsHolder added to helperClassNames()
…hen IODH handle is null

MpiGetPartsHolder resolves MultiPartInputStream.getParts() at class-load time via
Class.forName(). In test environments where jetty-server is not on the test classpath
(compileOnly only), GET_PARTS is null and the MethodHandle path is unavailable.

Add duck-typed reflection as fallback: when GET_PARTS is null, call getParts() via
getClass().getMethod("getParts").invoke(...). In production the fast MethodHandle path
always wins; the reflection fallback only activates when the class could not be resolved.

This restores getAllPartsReturnsAllPartsFromMultiPartInputStream and
getAllPartsPrefersFullCollectionOverSingleton unit tests that regressed after the
IODH refactor.
…ation

The skip-increment optimization introduced in the amarziali review commit
broke the shared Collection.class depth counter that GetFilenamesAdvice
and GetFilenamesFromMultiPartAdvice rely on to coordinate.

In Jetty 11.0.0, public getParts() delegates to the private getParts(MultiMap)
overload. GetFilenamesAdvice increments Collection depth before the public
method runs, which raises the depth so GetFilenamesFromMultiPartAdvice (also
applied to the private overload) sees depth > 0 and skips firing.

When the optimization skipped the increment because _multiParts != null, the
private getParts(MultiMap) call found Collection depth = 0 on the second and
third repeated calls and fired the requestFilesFilenames callback again,
causing _dd.appsec.filenames.cb.calls = 3 (BODY_MULTIPART_REPEATED) and = 2
(BODY_MULTIPART_COMBINED) instead of the expected 1.

Fix: restore the original always-increment/always-decrement pattern in
GetFilenamesAdvice so the depth counter remains balanced on every code path.
… use real MultiPartInputStream in tests

The reflection fallback (getClass().getMethod("getParts").invoke(...)) was
only needed because MultiPartInputStream was not on the test classpath,
causing the IODH MethodHandle to initialize to null. Fix the root cause
instead: add testImplementation for jetty-server 8.1.3.v20120416 and
replace the FakeMpi stub with mock(MultiPartInputStream.class).
@jandro996 jandro996 force-pushed the alejandro.gonzalez/APPSEC-61873-3 branch from 205bb1b to bb8c73f Compare June 16, 2026 08:05
- getAllParts: log.debug on MethodHandle invocation failure
- extractFilenames: log.debug when skipping a malformed part
- extractFormFields: log.debug when skipping a malformed part
- readPartContent: log.debug on IOException before returning null

IODH static block and charsetFromContentType left comment-only:
static initializer context and attacker-controlled input respectively.

@amarziali amarziali left a comment

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.

OK for the LP.

Use CallDepthThreadLocalMap.getCallDepth(Part.class) to read the
current depth without modifying it, instead of the equivalent
incrementCallDepth + decrementCallDepth pair.
@Advice.FieldValue("_contentParameters") final MultiMap<String> contentParameters,
@Advice.FieldValue(value = "_multiParts", typing = Assigner.Typing.DYNAMIC)
final Object multiParts) {
final int callDepth = CallDepthThreadLocalMap.incrementCallDepth(Collection.class);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Out of curiosity, why Collection.class instead of more meaningful class?

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.

good catch!

@jandro996 jandro996 Jun 19, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Thanks Manu!
Changed to MultipartHelper.class in all four modules (jetty92, jetty93, jetty94, jetty11). Each module has its own MultipartHelper in a different package, so they are independent classes created specifically for this PR — no risk of collision with other instrumentations.

…ap key

Replace Collection.class with MultipartHelper.class in all four jetty
appsec modules (9.2, 9.3, 9.4, 11.0). Collection.class is a generic
JDK class that any other instrumentation could use as a key on the same
thread, silently corrupting call depth tracking. MultipartHelper.class
is specific to each module and cannot collide with other instrumentations.
@jandro996 jandro996 added this pull request to the merge queue Jun 22, 2026
@dd-octo-sts

dd-octo-sts Bot commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

/merge

@gh-worker-devflow-routing-ef8351

gh-worker-devflow-routing-ef8351 Bot commented Jun 22, 2026

Copy link
Copy Markdown

View all feedbacks in Devflow UI.

2026-06-22 12:19:47 UTC ℹ️ Start processing command /merge


2026-06-22 12:19:51 UTC ℹ️ MergeQueue: pull request added to the queue

The expected merge time in master is approximately 1h (p90).


2026-06-22 13:23:15 UTC ℹ️ MergeQueue: This merge request was merged

@github-merge-queue github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Jun 22, 2026
@gh-worker-dd-mergequeue-cf854d gh-worker-dd-mergequeue-cf854d Bot merged commit fa8fdf6 into master Jun 22, 2026
582 checks passed
@gh-worker-dd-mergequeue-cf854d gh-worker-dd-mergequeue-cf854d Bot deleted the alejandro.gonzalez/APPSEC-61873-3 branch June 22, 2026 13:23
@github-actions github-actions Bot added this to the 1.64.0 milestone Jun 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp: asm waf Application Security Management (WAF) type: enhancement Enhancements and improvements

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants