@@ -91,11 +91,32 @@ Validation at boot:
9191 extra matches.
9292- The basePath context registration is unchanged.
9393
94+ ## Path-traversal protection
95+
96+ Before any matching, the router validates the decoded request path
97+ (` HttpExchange.getRequestURI().getPath() ` , which is already
98+ percent-decoded by the JDK) against these rules:
99+
100+ - No segment equals ` . ` or ` .. ` .
101+ - No empty segment (no ` // ` in the path).
102+ - No NUL byte (U+0000) anywhere in the path.
103+
104+ A violation throws ` BadRequestException ` and the ` ExceptionFilter ` renders
105+ the standard problem+json 400. The check runs once per request inside the
106+ ` ExtrasRouter ` , before exact-or-wildcard dispatch, so even handlers that
107+ chose to read the URI cannot see a traversal-laden path.
108+
109+ The same validation does NOT run inside the basePath spec context — spec
110+ paths are matched against an explicit template set, so a ` .. ` segment
111+ simply fails the exact/template match and yields a normal 404. Adding the
112+ 400 check there is out of scope (mentioned for clarity, not implemented).
113+
94114## Error handling
95115
96- Unchanged. Misses inside the router throw ` NotFoundException ` , which the
97- ` ExceptionFilter ` (still in front of the router) renders as the standard
98- problem+json 404.
116+ Unchanged for normal misses: a request that passes validation but matches
117+ no extra throws ` NotFoundException ` , rendered by the ` ExceptionFilter ` as
118+ problem+json 404. Traversal violations throw ` BadRequestException ` → 400
119+ (see above).
99120
100121## Testing
101122
@@ -117,6 +138,8 @@ Tests added under `src/test/java/com/retailsvc/http/`:
117138 - ` /schemas/**/openapi.yaml ` serves the spec at various depths
118139 - exact extras still work (regression for ` ExactUrlMatchingIT ` scenarios)
119140 - basePath spec routes still take precedence over extras
141+ - path-traversal: ` /files/../etc/passwd ` , ` /files/%2e%2e/etc/passwd ` ,
142+ ` /files/. ` , ` /files//x ` all return 400
120143
121144## Out of scope
122145
0 commit comments