Add server.request.body.files_content AppSec address for Jersey and RESTEasy#11229
Draft
Add server.request.body.files_content AppSec address for Jersey and RESTEasy#11229
Conversation
… reuse across integrations
…entDecoder Replaces hardcoded UTF-8 (no-charset default) and ISO-8859-1 (fallback) with Charset.defaultCharset() in both cases, per reviewer feedback.
…ltipart Read up to 4096 bytes per file (ISO-8859-1, max 25 files) and fire the server.request.body.files_content callback after the filenames callback, only when the request has not already been blocked. - ParameterCollector: add readContent() via reflection on getInputStream(), getContents() getter, and MAX_FILES_TO_INSPECT guard - ParsePartsInstrumentation: dispatch requestFilesContent() after requestFilesFilenames() when t == null - HttpPostRequestDecoderInstrumentation: collect file content inline in the getBodyHttpDatas() loop (in-memory via ByteBuf, on-disk via FileInputStream) and dispatch requestFilesContent() when thr == null after filenames - Add ParameterCollectorImplTest covering truncation, 25-file cap, form-field skip, IOException fallback, and ISO-8859-1 round-trip
…tation Issue 1 — Netty: extract MAX_CONTENT_BYTES=4096 and MAX_FILES_TO_INSPECT=25 as named constants in ParseBodyAdvice, replacing raw magic literals. Issue 3 — Tomcat + Netty: call effectivelyBlocked() after tryCommitBlockingResponse in both the filenames and files_content blocking paths, matching the behaviour already present in CommonsFileUploadAppSecInstrumentation. Issue 4 — Netty: collect file content for ALL FileUpload data items regardless of whether filename is present or empty; HttpDataType.FileUpload is the file-vs-form-field discriminator in Netty's model, so the filename-empty check was incorrectly skipping content for unnamed file parts (inconsistent with FileItemContentReader which only skips isFormField() items).
…ation Issue 1 — Tomcat: inspect file content even when submitted filename is empty. getSubmittedFileName() returns null for form fields (no filename parameter) and "" for file uploads whose client explicitly sent filename="". Null is now the only skip condition, so file parts with an empty filename are inspected but not added to the filenames list, matching the commons-fileupload and Netty behaviour. Issue 2 — Netty: remove unnecessary (long) casts in Math.min; both operands are already int so Math.min(int,int) resolves directly. Issue 3 — Netty: replace manual try/finally on FileInputStream with try-with-resources, consistent with FileItemContentReader style.
…tty files_content Move BlockingException assignment inside the brf != null guard so it is only thrown when the blocking response was actually committed. Also add effectivelyBlocked() to the Tomcat body-processed path, and remove the redundant if (t == null) guard there. For the Netty body-processed path (urlencoded), effectivelyBlocked() is intentionally omitted: tryCommitBlockingResponse() finishes the Netty span synchronously, so calling effectivelyBlocked() afterwards would interact with an already-closed TraceSegment.
…callback is unregistered Pre-fetch the requestFilesContent callback before the body-data loop and guard ByteBuf/FileInputStream reads behind it, consistent with how CommonsFileUploadAppSecInstrumentation already does. Also flattens the post-loop dispatch block by one nesting level.
…ad loop Move thr = new BlockingException(...) for the filenames path in Netty inside the brf != null guard, consistent with the body-processed and content paths in the same advice and with the Tomcat/Liberty pattern. Merge the two FileItem iterations in CommonsFileUploadAppSecInstrumentation into a single loop that builds both filenames and filesContent in one pass, calling FileItemContentReader.readContent() per item instead of a separate FileItemContentReader.readContents() sweep.
The bulk readContents(List<FileItem>) method is no longer called — the advice iterates file items inline calling readContent(FileItem) per item. Remove the method, its now-unused ArrayList/List imports, and the corresponding test cases. Also fix redundant type witness on ArrayList construction in CommonsFileUploadAppSecInstrumentation.
…oad guard style Skip filesContent ArrayList allocation in Netty ParseBodyAdvice when the requestFilesContent callback is not registered, matching the null-sentinel pattern already used in CommonsFileUploadAppSecInstrumentation. Replace the early-return in CommonsFileUploadAppSecInstrumentation's filenames blocking branch with a t == null guard on the content dispatch block, aligning control flow with ParsePartsInstrumentation and HttpPostRequestDecoderInstrumentation.
… callback is unregistered Add an inspectContent boolean to ParameterCollectorImpl, set from the presence of the requestFilesContent callback in ParsePartsInstrumentation before(). When false, addPart() still collects filenames but skips the getInputStream() read, matching the lazy-init approach already used by the Netty and commons-fileupload integrations.
… paths The content path in Netty and both the filenames and content paths in Tomcat were setting the BlockingException outside the if (brf != null) block, meaning the exception would be thrown even when no blocking response had actually been committed. Align all three with the canonical pattern: tryCommitBlockingResponse + effectivelyBlocked + exception assignment all inside if (brf != null). The Netty body-processed path intentionally still omits effectivelyBlocked() because tryCommitBlockingResponse() closes the Netty span synchronously in the test environment, making a subsequent effectivelyBlocked() call fail.
…meterCollector - Replace per-call getMethod() with volatile (Class, Method) entry cache so reflection resolves once per concrete Part class rather than per file per request. - Use try-with-resources for InputStream in readContent() instead of try/finally. - Add unit test covering the Tomcat 7 getFilename() fallback path.
… file content Also fix HttpPostRequestDecoderInstrumentation to fire on PREEPILOGUE+isLastChunk so that multipart requests delivered as a FullHttpRequest (single shot) trigger the requestBodyProcessed/requestFilesFilenames/requestFilesContent callbacks. The PREEPILOGUE→EPILOGUE transition requires a second parseBody() call which never comes when the full request arrives in one shot; PREEPILOGUE+isLastChunk is equivalent to EPILOGUE in that scenario.
…adContentReader helper Move the in-memory/disk-backed FileUpload content reading logic out of ParseBodyAdvice into a testable static helper class. Also fixes the partial-read risk on disk-backed uploads (single fis.read() replaced by a loop) and broadens the catch to Exception to handle IllegalReferenceCountException from released ByteBufs.
…large multipart payloads
…ocessed subscriber
… content blocking paths
…d content blocking paths
…k is absent When only requestFilesContent is registered, avoid calling getValue() on form attributes (which may trigger disk reads for large fields).
…MAX_FILE_CONTENT_BYTES/COUNT Replace hardcoded constants MAX_CONTENT_BYTES=4096 and MAX_FILES_TO_INSPECT=25 (duplicated across commons-fileupload, Netty, and Tomcat) with two Config-backed variables: appsec.max.file-content.bytes and appsec.max.file-content.count.
…onfigurations.json
…meterCollectorImplTest
…ix muzzle validation Static fields in @RequiresRequestContext advice inner classes trigger muzzle to treat the advice class itself as a user-classpath class; moving the constant to NettyFileUploadContentReader (a helper) avoids the self-referential muzzle failure.
…rn gate requestFilesFilenames() was consulted after data collection, so when it was the only registered AppSec callback (no requestBodyProcessed, no requestFilesContent) the early-return gate fired and filenames were never published to the WAF. Fetch all three callbacks upfront and gate on all three.
Introduces MultipartContentDecoder (internal-api) to decode multipart file bytes using the charset declared in each part's Content-Type header, with JVM-default fallback and REPLACE on malformed input. Mirrors the approach in PR #11212 for commons-fileupload so all three integrations share the same decoding logic.
…ContentDecoder Add readInputStream(InputStream, int, String) to eliminate the identical bounded read loop duplicated across FileItemContentReader, NettyFileUploadContentReader and ParameterCollector. Each caller still owns its own MAX_CONTENT_BYTES constant. Also consolidate the two volatile reflection caches in ParameterCollector.readContent (getInputStream + getContentType) into a single Method[] entry to halve volatile reads per invocation.
…etty and Tomcat helpers
… reflective method resolution
…n filenames callback is absent
…tipartHelper and add unit tests
…h decoder charset expectation
Deduplicates the identical RequestBlockingAction dispatch pattern that appeared three times in the Netty multipart advice.
…asy (#11171) Add server.request.body.filenames support for Jersey and RESTEasy Accept MIME linear whitespace around filename= parameter in Content-Disposition Tabs after ';' and optional SP/HT around '=' are valid per MIME and are delivered by RESTEasy as-is; the previous parser only skipped literal spaces, so those variants bypassed server.request.body.filenames detection. Fix misleading BlockingException message and add missing test coverage - Correct BlockingException message in jersey-appsec-2.0 and jersey-appsec-3.0: was "MultiPartReaderClientSide/readFrom", now "MultiPartReaderServerSide/readMultiPart" - Add combined body-map + filenames test to jersey-appsec-2.0 and jersey-appsec-3.0 MultiPartHelperTest to cover the case where both outputs are populated simultaneously - Add null input test to resteasy MultipartHelperTest for filenameFromContentDisposition Merge branch 'master' into alejandro.gonzalez/APPSEC-61873-5-jersey-resteasy Apply spotless formatting Co-authored-by: devflow.devflow-routing-intake <devflow.devflow-routing-intake@kubernetes.us1.ddbuild.io>
Adds server.request.body.files_content support to Jersey 2/3 and RESTEasy multipart instrumentations, following the canonical three-callback ordering (body always fires; filenames always fires; content fires only if body did not block). - MultiPartHelper (Jersey 2/3): add MAX_CONTENT_BYTES/MAX_FILES_TO_INSPECT constants, extend collectBodyPart to accept a filesContent list, add readContent and tryBlock helpers, remove unused filenameFromBodyPart - MultiPartReaderServerSideInstrumentation (Jersey 2/3): obtain and dispatch requestFilesContent callback gated on t == null - MultipartHelper (RESTEasy): add collectFilesContent, readContent, rawFilenameFromContentDisposition, tryBlock helpers and MAX constants; refactor filenameFromContentDisposition to delegate to raw variant - MultipartFormDataReaderInstrumentation (RESTEasy): obtain and dispatch requestFilesContent callback; replace inline blocking with tryBlock helper
- MultiPartHelperTest (Jersey 2/3): update collectBodyPart call sites to four-arg signature; add content tests (empty/null/non-empty filename, exception in getFormDataContentDisposition, MAX_FILES_TO_INSPECT limit); add tryBlock tests (non-blocking, blocking with brf, verifies tryCommitBlockingResponse + effectivelyBlocked, null brf) - MultipartHelperTest (RESTEasy): add rawFilenameFromContentDisposition tests (absent/empty/non-empty); add same four tryBlock tests
…STEasy helpers - tryBlock: create BlockingException before calling effectivelyBlocked() so the exception object exists before the span is marked; aligns with the canonical ordering documented for Servlet-based blocking paths - readContent (Jersey 2/3): wrap getEntityAs stream in try-with-resources for correctness, even though BodyPartEntity streams are in-memory - readContent (RESTEasy): use try-with-resources so file-backed InputPart streams are closed after reading, avoiding potential file descriptor leaks
1765cbe to
499a7bf
Compare
…Easy advices The requestFilesContent callback apply() was incorrectly gated on t == null, meaning it would not fire if a prior callback (body or filenames) had already triggered a blocking action. The three callbacks must always dispatch data to the WAF; only tryCommitBlockingResponse should be gated on t == null.
…ipart helper Covers: non-empty filename includes content, empty filename (security fix - content yes, filenames no), absent filename attribute skips part, and MAX_FILES_TO_INSPECT limit. Uses MultivaluedMapImpl for getHeaders() stubs so the reflection-based GET_HEADERS.invoke() call returns the expected type.
BenchmarksStartupParameters
See matching parameters
SummaryFound 0 performance improvements and 0 performance regressions! Performance is the same for 62 metrics, 9 unstable metrics. Startup time reports for petclinicgantt
title petclinic - global startup overhead: candidate=1.62.0-SNAPSHOT~58954d0769, baseline=1.62.0-SNAPSHOT~9d737609c4
dateFormat X
axisFormat %s
section tracing
Agent [baseline] (1.061 s) : 0, 1061059
Total [baseline] (11.095 s) : 0, 11094590
Agent [candidate] (1.065 s) : 0, 1064569
Total [candidate] (11.12 s) : 0, 11120364
section appsec
Agent [baseline] (1.27 s) : 0, 1270111
Total [baseline] (11.129 s) : 0, 11128559
Agent [candidate] (1.268 s) : 0, 1267741
Total [candidate] (11.119 s) : 0, 11118873
section iast
Agent [baseline] (1.246 s) : 0, 1246431
Total [baseline] (11.257 s) : 0, 11257406
Agent [candidate] (1.247 s) : 0, 1246994
Total [candidate] (11.363 s) : 0, 11363378
section profiling
Agent [baseline] (1.196 s) : 0, 1196297
Total [baseline] (11.113 s) : 0, 11113470
Agent [candidate] (1.189 s) : 0, 1188828
Total [candidate] (11.011 s) : 0, 11011194
gantt
title petclinic - break down per module: candidate=1.62.0-SNAPSHOT~58954d0769, baseline=1.62.0-SNAPSHOT~9d737609c4
dateFormat X
axisFormat %s
section tracing
crashtracking [baseline] (1.241 ms) : 0, 1241
crashtracking [candidate] (1.244 ms) : 0, 1244
BytebuddyAgent [baseline] (635.276 ms) : 0, 635276
BytebuddyAgent [candidate] (635.965 ms) : 0, 635965
AgentMeter [baseline] (29.481 ms) : 0, 29481
AgentMeter [candidate] (29.471 ms) : 0, 29471
GlobalTracer [baseline] (248.386 ms) : 0, 248386
GlobalTracer [candidate] (249.036 ms) : 0, 249036
AppSec [baseline] (32.571 ms) : 0, 32571
AppSec [candidate] (32.717 ms) : 0, 32717
Debugger [baseline] (60.195 ms) : 0, 60195
Debugger [candidate] (60.647 ms) : 0, 60647
Remote Config [baseline] (584.582 µs) : 0, 585
Remote Config [candidate] (598.084 µs) : 0, 598
Telemetry [baseline] (9.139 ms) : 0, 9139
Telemetry [candidate] (9.945 ms) : 0, 9945
Flare Poller [baseline] (8.195 ms) : 0, 8195
Flare Poller [candidate] (8.882 ms) : 0, 8882
section appsec
crashtracking [baseline] (1.222 ms) : 0, 1222
crashtracking [candidate] (1.22 ms) : 0, 1220
BytebuddyAgent [baseline] (678.336 ms) : 0, 678336
BytebuddyAgent [candidate] (676.223 ms) : 0, 676223
AgentMeter [baseline] (12.3 ms) : 0, 12300
AgentMeter [candidate] (12.229 ms) : 0, 12229
GlobalTracer [baseline] (249.904 ms) : 0, 249904
GlobalTracer [candidate] (249.646 ms) : 0, 249646
IAST [baseline] (24.751 ms) : 0, 24751
IAST [candidate] (24.642 ms) : 0, 24642
AppSec [baseline] (185.332 ms) : 0, 185332
AppSec [candidate] (185.558 ms) : 0, 185558
Debugger [baseline] (65.058 ms) : 0, 65058
Debugger [candidate] (64.7 ms) : 0, 64700
Remote Config [baseline] (572.959 µs) : 0, 573
Remote Config [candidate] (559.601 µs) : 0, 560
Telemetry [baseline] (7.906 ms) : 0, 7906
Telemetry [candidate] (7.777 ms) : 0, 7777
Flare Poller [baseline] (6.316 ms) : 0, 6316
Flare Poller [candidate] (8.563 ms) : 0, 8563
section iast
crashtracking [baseline] (1.225 ms) : 0, 1225
crashtracking [candidate] (1.24 ms) : 0, 1240
BytebuddyAgent [baseline] (824.343 ms) : 0, 824343
BytebuddyAgent [candidate] (825.115 ms) : 0, 825115
AgentMeter [baseline] (11.334 ms) : 0, 11334
AgentMeter [candidate] (11.325 ms) : 0, 11325
GlobalTracer [baseline] (238.553 ms) : 0, 238553
GlobalTracer [candidate] (238.36 ms) : 0, 238360
IAST [baseline] (25.751 ms) : 0, 25751
IAST [candidate] (27.435 ms) : 0, 27435
AppSec [baseline] (33.99 ms) : 0, 33990
AppSec [candidate] (32.227 ms) : 0, 32227
Debugger [baseline] (63.283 ms) : 0, 63283
Debugger [candidate] (63.178 ms) : 0, 63178
Remote Config [baseline] (515.958 µs) : 0, 516
Remote Config [candidate] (520.625 µs) : 0, 521
Telemetry [baseline] (7.917 ms) : 0, 7917
Telemetry [candidate] (8.008 ms) : 0, 8008
Flare Poller [baseline] (3.42 ms) : 0, 3420
Flare Poller [candidate] (3.489 ms) : 0, 3489
section profiling
crashtracking [baseline] (1.186 ms) : 0, 1186
crashtracking [candidate] (1.183 ms) : 0, 1183
BytebuddyAgent [baseline] (696.903 ms) : 0, 696903
BytebuddyAgent [candidate] (694.048 ms) : 0, 694048
AgentMeter [baseline] (8.994 ms) : 0, 8994
AgentMeter [candidate] (8.955 ms) : 0, 8955
GlobalTracer [baseline] (209.722 ms) : 0, 209722
GlobalTracer [candidate] (208.338 ms) : 0, 208338
AppSec [baseline] (33.339 ms) : 0, 33339
AppSec [candidate] (32.755 ms) : 0, 32755
Debugger [baseline] (66.535 ms) : 0, 66535
Debugger [candidate] (65.827 ms) : 0, 65827
Remote Config [baseline] (579.032 µs) : 0, 579
Remote Config [candidate] (575.504 µs) : 0, 576
Telemetry [baseline] (8.174 ms) : 0, 8174
Telemetry [candidate] (8.093 ms) : 0, 8093
Flare Poller [baseline] (3.59 ms) : 0, 3590
Flare Poller [candidate] (3.615 ms) : 0, 3615
ProfilingAgent [baseline] (95.463 ms) : 0, 95463
ProfilingAgent [candidate] (94.053 ms) : 0, 94053
Profiling [baseline] (96.023 ms) : 0, 96023
Profiling [candidate] (94.603 ms) : 0, 94603
Startup time reports for insecure-bankgantt
title insecure-bank - global startup overhead: candidate=1.62.0-SNAPSHOT~58954d0769, baseline=1.62.0-SNAPSHOT~9d737609c4
dateFormat X
axisFormat %s
section tracing
Agent [baseline] (1.065 s) : 0, 1064963
Total [baseline] (8.845 s) : 0, 8845326
Agent [candidate] (1.062 s) : 0, 1062075
Total [candidate] (8.845 s) : 0, 8845431
section iast
Agent [baseline] (1.245 s) : 0, 1244866
Total [baseline] (9.552 s) : 0, 9551511
Agent [candidate] (1.247 s) : 0, 1246687
Total [candidate] (9.505 s) : 0, 9505130
gantt
title insecure-bank - break down per module: candidate=1.62.0-SNAPSHOT~58954d0769, baseline=1.62.0-SNAPSHOT~9d737609c4
dateFormat X
axisFormat %s
section tracing
crashtracking [baseline] (1.226 ms) : 0, 1226
crashtracking [candidate] (1.212 ms) : 0, 1212
BytebuddyAgent [baseline] (636.838 ms) : 0, 636838
BytebuddyAgent [candidate] (634.805 ms) : 0, 634805
AgentMeter [baseline] (29.466 ms) : 0, 29466
AgentMeter [candidate] (29.472 ms) : 0, 29472
GlobalTracer [baseline] (249.166 ms) : 0, 249166
GlobalTracer [candidate] (248.885 ms) : 0, 248885
AppSec [baseline] (32.845 ms) : 0, 32845
AppSec [candidate] (32.783 ms) : 0, 32783
Debugger [baseline] (59.809 ms) : 0, 59809
Debugger [candidate] (59.459 ms) : 0, 59459
Remote Config [baseline] (601.589 µs) : 0, 602
Remote Config [candidate] (592.903 µs) : 0, 593
Telemetry [baseline] (9.919 ms) : 0, 9919
Telemetry [candidate] (9.116 ms) : 0, 9116
Flare Poller [baseline] (9.123 ms) : 0, 9123
Flare Poller [candidate] (9.861 ms) : 0, 9861
section iast
crashtracking [baseline] (1.224 ms) : 0, 1224
crashtracking [candidate] (1.228 ms) : 0, 1228
BytebuddyAgent [baseline] (824.168 ms) : 0, 824168
BytebuddyAgent [candidate] (825.809 ms) : 0, 825809
AgentMeter [baseline] (11.3 ms) : 0, 11300
AgentMeter [candidate] (11.298 ms) : 0, 11298
GlobalTracer [baseline] (238.724 ms) : 0, 238724
GlobalTracer [candidate] (238.429 ms) : 0, 238429
AppSec [baseline] (31.687 ms) : 0, 31687
AppSec [candidate] (32.317 ms) : 0, 32317
Debugger [baseline] (63.393 ms) : 0, 63393
Debugger [candidate] (62.295 ms) : 0, 62295
Remote Config [baseline] (518.699 µs) : 0, 519
Remote Config [candidate] (526.822 µs) : 0, 527
Telemetry [baseline] (7.952 ms) : 0, 7952
Telemetry [candidate] (7.887 ms) : 0, 7887
Flare Poller [baseline] (3.363 ms) : 0, 3363
Flare Poller [candidate] (3.327 ms) : 0, 3327
IAST [baseline] (26.551 ms) : 0, 26551
IAST [candidate] (27.515 ms) : 0, 27515
LoadParameters
See matching parameters
SummaryFound 1 performance improvements and 0 performance regressions! Performance is the same for 18 metrics, 17 unstable metrics.
Request duration reports for petclinicgantt
title petclinic - request duration [CI 0.99] : candidate=1.62.0-SNAPSHOT~58954d0769, baseline=1.62.0-SNAPSHOT~9d737609c4
dateFormat X
axisFormat %s
section baseline
no_agent (18.361 ms) : 18173, 18549
. : milestone, 18361,
appsec (18.923 ms) : 18729, 19117
. : milestone, 18923,
code_origins (17.849 ms) : 17672, 18027
. : milestone, 17849,
iast (18.021 ms) : 17843, 18199
. : milestone, 18021,
profiling (18.818 ms) : 18631, 19004
. : milestone, 18818,
tracing (17.532 ms) : 17359, 17705
. : milestone, 17532,
section candidate
no_agent (18.605 ms) : 18415, 18794
. : milestone, 18605,
appsec (19.218 ms) : 19025, 19410
. : milestone, 19218,
code_origins (17.776 ms) : 17601, 17950
. : milestone, 17776,
iast (18.011 ms) : 17830, 18192
. : milestone, 18011,
profiling (18.559 ms) : 18376, 18742
. : milestone, 18559,
tracing (17.897 ms) : 17718, 18075
. : milestone, 17897,
Request duration reports for insecure-bankgantt
title insecure-bank - request duration [CI 0.99] : candidate=1.62.0-SNAPSHOT~58954d0769, baseline=1.62.0-SNAPSHOT~9d737609c4
dateFormat X
axisFormat %s
section baseline
no_agent (1.249 ms) : 1236, 1261
. : milestone, 1249,
iast (3.294 ms) : 3248, 3340
. : milestone, 3294,
iast_FULL (5.979 ms) : 5918, 6041
. : milestone, 5979,
iast_GLOBAL (3.675 ms) : 3614, 3737
. : milestone, 3675,
profiling (2.169 ms) : 2148, 2190
. : milestone, 2169,
tracing (1.952 ms) : 1935, 1969
. : milestone, 1952,
section candidate
no_agent (1.255 ms) : 1243, 1267
. : milestone, 1255,
iast (3.353 ms) : 3298, 3409
. : milestone, 3353,
iast_FULL (5.793 ms) : 5735, 5851
. : milestone, 5793,
iast_GLOBAL (3.676 ms) : 3616, 3736
. : milestone, 3676,
profiling (2.118 ms) : 2099, 2136
. : milestone, 2118,
tracing (1.837 ms) : 1823, 1852
. : milestone, 1837,
DacapoParameters
See matching parameters
SummaryFound 0 performance improvements and 0 performance regressions! Performance is the same for 10 metrics, 2 unstable metrics. Execution time for biojavagantt
title biojava - execution time [CI 0.99] : candidate=1.62.0-SNAPSHOT~58954d0769, baseline=1.62.0-SNAPSHOT~9d737609c4
dateFormat X
axisFormat %s
section baseline
no_agent (15.548 s) : 15548000, 15548000
. : milestone, 15548000,
appsec (14.858 s) : 14858000, 14858000
. : milestone, 14858000,
iast (18.569 s) : 18569000, 18569000
. : milestone, 18569000,
iast_GLOBAL (18.005 s) : 18005000, 18005000
. : milestone, 18005000,
profiling (15.087 s) : 15087000, 15087000
. : milestone, 15087000,
tracing (14.809 s) : 14809000, 14809000
. : milestone, 14809000,
section candidate
no_agent (14.873 s) : 14873000, 14873000
. : milestone, 14873000,
appsec (14.888 s) : 14888000, 14888000
. : milestone, 14888000,
iast (18.358 s) : 18358000, 18358000
. : milestone, 18358000,
iast_GLOBAL (18.059 s) : 18059000, 18059000
. : milestone, 18059000,
profiling (14.867 s) : 14867000, 14867000
. : milestone, 14867000,
tracing (14.895 s) : 14895000, 14895000
. : milestone, 14895000,
Execution time for tomcatgantt
title tomcat - execution time [CI 0.99] : candidate=1.62.0-SNAPSHOT~58954d0769, baseline=1.62.0-SNAPSHOT~9d737609c4
dateFormat X
axisFormat %s
section baseline
no_agent (1.489 ms) : 1477, 1500
. : milestone, 1489,
appsec (2.559 ms) : 2503, 2614
. : milestone, 2559,
iast (2.283 ms) : 2213, 2353
. : milestone, 2283,
iast_GLOBAL (2.337 ms) : 2267, 2407
. : milestone, 2337,
profiling (2.538 ms) : 2373, 2704
. : milestone, 2538,
tracing (2.084 ms) : 2031, 2138
. : milestone, 2084,
section candidate
no_agent (1.493 ms) : 1481, 1504
. : milestone, 1493,
appsec (3.854 ms) : 3632, 4076
. : milestone, 3854,
iast (2.299 ms) : 2228, 2369
. : milestone, 2299,
iast_GLOBAL (2.331 ms) : 2260, 2401
. : milestone, 2331,
profiling (2.117 ms) : 2062, 2173
. : milestone, 2117,
tracing (2.093 ms) : 2039, 2147
. : milestone, 2093,
|
…rom Jersey tests java.io.* is auto-imported in Groovy; the explicit import triggers CodeNarc UnnecessaryGroovyImport violation.
Member
Author
|
@codex review |
|
Codex Review: Didn't find any major issues. Already looking forward to the next diff. ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
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". |
Base automatically changed from
alejandro.gonzalez/APPSEC-61875-files-content-tomcat-netty
to
master
April 30, 2026 13:58
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What Does This Do
Extends the
server.request.body.files_contentWAF address to Jersey 2.0, Jersey 3.0, and RESTEasy 3.0 multipart instrumentations, complementing the Tomcat + Netty implementation (PR #11198).Jersey 2.0 and 3.0
collectBodyPart()with a fourthList<String> filesContentparameter; content is collected whenrawFilename != null(filename attribute present, even if empty) and the count limit has not been reachedreadContent(FormDataBodyPart)usingtry-with-resourcesaroundgetEntityAs(InputStream.class)— backed byBodyPartEntity, which supports re-reading from an in-memory buffertryBlock(RequestContext, Flow<Void>, String)helper with canonical ordering:BlockingExceptioncreated beforeeffectivelyBlocked()MultiPartReaderServerSideInstrumentationto fetchcontentCallback, add the three-way guard (callback == null && filenamesCallback == null && contentCallback == null), collectfilesContent, and dispatch the content callback gated ont == null(fires only if neither body nor filenames blocked)RESTEasy 3.0
collectFilesContent(MultipartFormDataInput)mirroringcollectFilenames(): uses the cachedstatic final GET_HEADERSreflection handle to readContent-Dispositionper part; collects content whenrawFilenameFromContentDisposition()returns non-null (present-but-empty filename still yields content)readContent(InputPart, String)withtry-with-resourcesaroundInputPart.getBody(InputStream.class, null)tryBlock(RequestContext, Flow<Void>, String)with the same canonical ordering as JerseyMultipartFormDataReaderInstrumentationwith the three-way guard and content callback dispatch gated ont == nullTests
MultiPartHelperTest(jersey-appsec-2.0 and -3.0): new tests forreadContent()(empty/null/non-empty filename gate, graceful skip on exception,MAX_FILES_TO_INSPECTlimit) and fourtryBlocktests (null brf, blocking action, interaction verification, null action)MultipartHelperTest(resteasy-appsec-3.0): new tests forrawFilenameFromContentDisposition(absent / present-but-empty / non-empty, via reflection) and fourtryBlocktestsMotivation
Part of APPSEC-61875 — full
server.request.body.files_contentcoverage for all supported multipart frameworks. This PR covers Jersey and RESTEasy; Tomcat and Netty are covered by PR #11198.Additional Notes
RESTEasy uses a cached
static final Method GET_HEADERS(initialized once in astatic {}block) to callInputPart.getHeaders()via reflection. This avoids a direct bytecode reference toMultivaluedMap, whose package differs between RESTEasy 3.x (javax.ws.rs.core) and RESTEasy 6.x (jakarta.ws.rs.core). A direct reference would cause muzzle validation to fail for one of the two version families.This PR is stacked on
alejandro.gonzalez/APPSEC-61875-files-content-tomcat-netty(PR #11198).Contributor Checklist
type:and (comp:orinst:) labels in addition to any other useful labelsclose,fix, or any linking keywords when referencing an issueUse
solvesinstead, and assign the PR milestone to the issueJira ticket: APPSEC-61875
Note: Once your PR is ready to merge, add it to the merge queue by commenting
/merge./merge -ccancels 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.