matrei commented on PR #15967:
URL: https://github.com/apache/grails-core/pull/15967#issuecomment-5816659834

   Re-reviewed at head `8da7b05c58` (base `8.0.x`, merge-base `7ea4cb9d8e`; 
merges cleanly into current `8.0.x` at `a441334507`). This round only looks at 
what changed since `5c8fc21206`.
   
   What I ran locally:
   - `:grails-controllers:cleanTest :grails-controllers:test --no-build-cache`: 
199 tests, 0 failures (100 in `GrailsSecurityHeadersAutoConfigurationSpec`, 10 
in `GrailsSecurityHeadersFilterTomcatSpec`).
   - `:grails-controllers:codeStyle`: clean.
   - My throwaway Tomcat specs from the last round, re-run at the new head with 
more cases added (results below).
   
   Everything from the last round is addressed, and I have nothing blocking. 
Both behaviour problems are fixed on the wire. The PR description now matches 
the code. One optional nit is below.
   
   ### Re-run of the previous findings
   
   Relaxed path chars (`relaxedPathChars='|{}[]^`'`) with `hsts.enabled: true`:
   
   ```
   request                                                  status   nosniff   
HSTS
   GET /p|x                                                 200      sent      
-       (was 500)
   GET /p{x}                                                200      sent      
-       (was 500)
   GET /p|x      X-Forwarded-Proto: https                   200      sent      
sent    (was 500)
   GET /p|x      X-Forwarded-Proto: http                    200      sent      -
   GET /plain    X-Forwarded-Host: [::1, Proto: https       200      sent      
-       (malformed host: IAE caught, HSTS skipped)
   GET /plain    X-Forwarded-Port: abc, Proto: https        200      sent      -
   GET /plain    X-Forwarded-Proto: https:x / "ht tps"      200      sent      -
   GET /plain    Content-Type: text/plain;charset=bogus     200      sent      
sent
   ```
   
   Writer output with `setBufferSize(32768)`, every row showing `nosniff` / 
`X-Frame-Options` on the wire (all were checked at least once; the two CJK rows 
twice each, interleaved so the enlarged buffer is recycled into a 
default-buffer request):
   
   ```
   400 × write('日' * 50)               UTF-8        60 000 B    sent   (was 
<missing>)
   400 × write('日' * 50), default buf  UTF-8        60 000 B    sent   (was 
<missing> after a 32 KB request)
   400 × write('ж' * 50)                UTF-8        40 000 B    sent
   400 × write('😀' * 25)               UTF-8        40 000 B    sent   
(surrogate pairs)
   20000 × write((int) '日')            UTF-8        60 000 B    sent
   400 × printf('%s', '日' * 50)        UTF-8        60 000 B    sent
   400 × write('日' * 50)               Shift_JIS    40 000 B    sent
   400 × write('日' * 50)               UTF-16       40 002 B    sent
   1000 × write('é' * 50)               ISO-8859-1   50 000 B    sent
   ```
   
   ### Nit (optional)
   
   `GrailsSecurityHeadersFilter.java:196-197`: the `catch` comment says "A 
forwarded port that is not a number", but it also catches 
`ForwardedHeaderUtils` rejecting a malformed forwarded host ("Invalid IPv4 
address", the `[::1` row above) and `URI.create` rejecting the scheme. 
"Malformed forwarded headers: no trustworthy scheme to go on" would cover all 
three.
   
   ### Verified as correct
   
   - `isSecure` builds the base URI from `request.getScheme()` alone, so the 
request path and query never reach `java.net.URI`. `URI.create` is inside the 
`try`, and a container scheme of `https` on a connection that isn't marked 
secure still counts. This is covered by the new spec.
   - Writer byte counting: `utf8Length` counts 1, 2 or 3 bytes, and 2 for each 
half of a surrogate pair, so 4 for the pair. Any other encoding counts 
`ceil(maxBytesPerChar)` per character, which can only over-count, so the 
callback fires early rather than late. The charset is read after 
`super.getWriter()`, once the servlet API has locked the encoding. An unknown 
or decode-only charset falls back to UTF-8, and the container fails that writer 
anyway. Once the callback has fired, the counting loop is skipped. 
`CharBuffer.wrap(buf)` with `off` as an absolute index is right. `print`, 
`append`, `printf`/`format` and `println` all go through the counted methods, 
and the `printf` and `write(int)` rows confirm it on Tomcat.
   - The new Tomcat `/multi-byte-writer` row fails at `5c8fc21206` (headers 
missing in every run last round) and passes now, so it pins the fix.
   - The Spring Security ordering paragraph in `security.adoc` describes the 
trade-off accurately. At Tomcat's default buffer, UTF-8 text from about 8 KB of 
encoded bytes now gets the Grails defaults before Spring Security's 
character-counting wrapper fires. With Spring Security's default headers this 
changes nothing: `X-Frame-Options` is written unconditionally, the `nosniff` 
and `X-XSS-Protection: 0` values are the same, and Grails HSTS is off by 
default. Only a non-default Spring Security value for a skip-if-present header 
(for example `Referrer-Policy`) is affected, and the paragraph says how to hand 
that header over.
   - The docs changes: the character-encoding claim is corrected in all three 
places. The "explicitly configured" wording now matches the 
`explicit`/`enabled`/value logic, including `enabled: false` and the CSP/HSTS 
`enabled: true` requirement. Appending the upgrade note as section 73 instead 
of renumbering is reasonable, given the numbered cross-references such as 
"section 53".
   - `REVERSE_PROXY_REQUEST_HEADERS` is package-private. The Tomcat spec cleans 
up with `@Shared @TempDir` and now checks the exact values of all four headers. 
Keeping its own client instead of `HttpClientSupport` makes sense, because the 
new multi-byte row compares the raw byte length.
   - CI was still running at the time of review (5 passed, 66 pending, 0 
failed).
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to