-
Notifications
You must be signed in to change notification settings - Fork 4
feat(rules): Tune XSS rules, separate ERROR and NOTE version #113
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
misonijnik
wants to merge
67
commits into
main
Choose a base branch
from
misonijnik/xss-rework
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
67 commits
Select commit
Hold shift + click to select a range
4381d24
chore(core): Add passThrough rules for Spring DeferredResult
misonijnik 7b29007
chore(core): Move DeferredResult passThrough rules to spring-web jar-…
misonijnik af0d922
feat: add new xss sinks rules
misonijnik 710594c
test: add tests for xss sinks
misonijnik e240a53
docs: Spring XSS rule dynamic verification design
misonijnik 97f4532
docs: plan for Spring XSS rule dynamic verification
misonijnik 65c29c3
docs: record Appendix A runtime verdict table for Spring XSS matrix
misonijnik 2925850
docs: record Appendix B raw-vs-generic ResponseEntity probe outcome
misonijnik ea42d90
test: cover 21 Spring XSS sink variants verified dynamically
misonijnik baf29ad
feat: discriminate non-HTML produces in Spring XSS sink
misonijnik da5c86d
docs: record Appendix D post-rule-update matrix
misonijnik c02eb73
docs: add Appendix A.2 — real-browser re-verification
misonijnik e1bed2f
feat: honest TP/FP labels + extended non-HTML content type coverage
misonijnik 89ce902
docs: extend Appendix D/E with honest gap matrix
misonijnik 9296a67
Improve XSS rules: programmatic safe-CT sanitizers, @RestController, …
misonijnik 2d3ea58
Fix metavariable binding in XSS pattern-(not)-inside and sanitizer focus
misonijnik 28d92bf
Rewrite XSS sanitizers to assignment-form / multi-statement style
misonijnik 15cc25f
Compact XSS sinks; add engine-limitation regression tests
misonijnik c989ce4
Rewrite XSS html-response sinks to single-pattern shape
misonijnik 649ec82
Add regression tests for two more engine issues
misonijnik ca04e58
Add minimal repro for taint-through-concat in `return` sinks
misonijnik b644a9e
Refactor Spring XSS html-response sink with typed return types
misonijnik 3b22909
Collapse XSS sink negatives via grouped pattern-not-inside + metavari…
misonijnik 7530e66
Collapse Spring XSS Branch 2 (response writer) into pattern-inside + …
misonijnik 77b5634
Fix Spring XSS FNs: @ExceptionHandler source coverage + simpler sink …
misonijnik b99879b
Re-enable forward-compatible ResponseEntity content-type sanitizers
misonijnik 79ac406
Trim verbose comments in XSS sink rules
misonijnik 961bc8e
Cover DeferredResult<$T> XSS sink shape
misonijnik b82d8f5
Cover setHeader/addHeader 2-arg via metavar-inside-quotes form
misonijnik 1149863
Merge DeferredResult sink branch into Branch 1
misonijnik c5665bd
Pair return-type shapes with their dedicated sink statements
misonijnik f1a34ec
Tighten Spring XSS Branch 1 to body-typed return types only
misonijnik 2f57a76
Use NOTE severity for XSS sink rules and example
misonijnik 46b27b8
Remove docs/specs and docs/superpowers
misonijnik ee769f9
Rename potential-xss rule IDs to response-injection
misonijnik 9a1352a
Extend servlet HTML-response XSS sink to broad sink shapes
misonijnik ea89d37
Use metavariable-pattern for setContentType safe-CT exclusion
misonijnik 7393aca
Drop chained-writer alternatives from Spring XSS HTML-response Branch 2
misonijnik 7365b9a
Reformat sendError sink as block scalar in spring-xss-sinks
misonijnik 2fe2774
Drop @ExceptionHandler coverage from spring XSS rules and tests
misonijnik 5b6b7e8
Disable known-engine-FP negative samples in spring/servlet XSS tests
misonijnik fead47d
Remove issue 98-103 sample tests
misonijnik a67651e
XSS rules: tighten Spring sink with type-arg discrimination and @Rest…
misonijnik 1088eba
Fix XSS rule parse errors, add builder-chain HTML branch
misonijnik e3faa19
clean: Remove unrelated changes
misonijnik dafaf6a
XSS rules: scope @ResponseBody exclusion to String return shape
misonijnik 4c0d8e3
ci: Bump OWASP analyzer expected trace count to 3835
misonijnik a881551
XSS rules: cover raw `ResponseEntity` return shape in Spring sink
misonijnik 7340b62
XSS rules: drop @ResponseBody exclusion on Spring String return sink
misonijnik 87d8f35
XSS rules: subtract parameterized returns from raw ResponseEntity sink
misonijnik 9acc2da
XSS rules: hoist raw ResponseEntity branch to top-level patterns
misonijnik fe3d2e2
XSS rules: nest raw ResponseEntity branch under shared Branch 1 scope
misonijnik 8324328
wip: Test the rule
misonijnik 8d99d4a
wip: extend Spring XSS sink coverage (Resource family, raw, async)
misonijnik 9b624a8
XSS rules: drop ResponseEntity<?> / wrapper wildcard branches
misonijnik 36cbb8d
XSS rules: drop dead Branch 1d (raw ResponseEntity)
misonijnik 564aa92
XSS rules: trim comments and consolidate Spring XSS tests into the ru…
misonijnik 4c3c09e
XSS rules: restore raw ResponseEntity positive test
misonijnik f1c16e9
XSS rules: re-engineering
misonijnik e35063f
XSS rules: fix Branch 1c scoping and rule-load typo
misonijnik fb2cd55
XSS rules: restore response-injection-in-servlet-app firing
misonijnik 9aafe1f
XSS rules: subtract non-HTML builder-chain content types
misonijnik 5879faa
XSS rules: catch ResponseEntity.ok(body) convenience overload
misonijnik e32b1fb
engine test: pattern-not-inside ignored for chained constrained-arity…
misonijnik 0256f78
issue 98: tighten engine test to wire metavariables through positive …
misonijnik ad77cd9
issue 98: narrow scope to metavariable-pattern-narrowed pattern-not-i…
misonijnik 52d3e2e
issue 98: drop the Builder type qualifier from pattern-inside
misonijnik File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -21,7 +21,7 @@ concurrency: | |
| cancel-in-progress: true | ||
|
|
||
| env: | ||
| EXPECTED_TRACES: 3011 | ||
| EXPECTED_TRACES: 3835 | ||
|
|
||
| jobs: | ||
| owasp: | ||
|
|
||
9 changes: 9 additions & 0 deletions
9
core/opentaint-java-querylang/samples/src/main/java/issues/i98/Builder.java
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| package issues.i98; | ||
|
|
||
| public class Builder { | ||
| public void cfg(String key, String value) { } | ||
|
|
||
| public void sink(String taint) { } | ||
|
|
||
| public static String source() { return "oops"; } | ||
| } |
64 changes: 64 additions & 0 deletions
64
core/opentaint-java-querylang/samples/src/main/java/issues/issue98.java
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,64 @@ | ||
| package issues; | ||
|
|
||
| import base.RuleSample; | ||
| import base.RuleSet; | ||
| import issues.i98.Builder; | ||
|
|
||
| /** | ||
| * Engine bug: a `pattern-not-inside` whose discriminator-arg metavariable is | ||
| * narrowed by a sibling `metavariable-pattern` is silently ignored. | ||
| * | ||
| * The rule (issue98.yaml): | ||
| * - binds `$X` via `pattern-inside: Builder $X = new Builder(); ...` | ||
| * - binds `$UNTRUSTED` via the positive sink `$X.sink($UNTRUSTED)` | ||
| * - subtracts via `pattern-not-inside: $X.cfg("Content-Type", $V)` plus | ||
| * `metavariable-pattern: $V matches "application/json"` | ||
| * | ||
| * Every metavariable in the pattern-not-inside is bound: `$X` from | ||
| * `pattern-inside`, `$V` constrained by `metavariable-pattern`. With the | ||
| * literal-arg form `pattern-not-inside: $X.cfg("Content-Type", | ||
| * "application/json")` (no metavariable-pattern) the engine correctly | ||
| * subtracts the match — this test passes. Replacing the literal arg with a | ||
| * metavariable + `metavariable-pattern` constraint (the same shape the XSS | ||
| * rule uses for `$CT_SAFE`) loses the subtraction. | ||
| * | ||
| * Expected: | ||
| * - PositiveStmtNoCfg fires (no `.cfg` call in scope). | ||
| * - NegativeStmtWithCfg is subtracted (`.cfg("Content-Type", | ||
| * "application/json")` matches the pattern-not-inside, and | ||
| * `metavariable-pattern` accepts the literal). | ||
| * | ||
| * Actual: NegativeStmtWithCfg also fires. | ||
| * | ||
| * Motivation: matches the `(HttpServletResponse $R).setContentType($CT_SAFE); …` | ||
| * + `metavariable-pattern: $CT_SAFE ∈ {non-HTML strings}` subtractor in | ||
| * `servlet-xss-html-response-sinks.yaml` that fails to clear the | ||
| * `SafeChainedWriterJsonServlet` / `SafeOutputStreamOctetServlet` FPs. | ||
| */ | ||
| @RuleSet("issues/issue98.yaml") | ||
| public abstract class issue98 implements RuleSample { | ||
| /** No discriminator call in scope — sink should fire. */ | ||
| static class PositiveStmtNoCfg extends issue98 { | ||
| @Override | ||
| public void entrypoint() { | ||
| Builder b = new Builder(); | ||
| b.sink(Builder.source()); | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Discriminator `b.cfg("Content-Type", "application/json")` is present in | ||
| * the method body. The pattern-not-inside + metavariable-pattern pair | ||
| * should subtract this match — but does not. Disabled until the engine | ||
| * honors metavariable-pattern-narrowed arguments inside | ||
| * pattern-not-inside. | ||
| */ | ||
| static class NegativeStmtWithCfg extends issue98 { | ||
| @Override | ||
| public void entrypoint() { | ||
| Builder b = new Builder(); | ||
| b.cfg("Content-Type", "application/json"); | ||
| b.sink(Builder.source()); | ||
| } | ||
| } | ||
| } |
30 changes: 30 additions & 0 deletions
30
core/opentaint-java-querylang/samples/src/main/resources/issues/issue98.yaml
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| rules: | ||
| - id: i98 | ||
| message: > | ||
| pattern-not-inside whose discriminator-arg metavariable is narrowed by | ||
| a sibling metavariable-pattern is silently ignored. Replacing the | ||
| metavariable with the equivalent literal makes the subtraction work. | ||
| See issue98.java for the demonstration and the motivating XSS rule | ||
| context (`servlet-xss-html-response-sinks.yaml` subtractor on | ||
| `(HttpServletResponse $R).setContentType($CT_SAFE); ...` plus | ||
| `metavariable-pattern: $CT_SAFE ∈ {non-HTML strings}`). | ||
| severity: ERROR | ||
| languages: | ||
| - java | ||
| mode: taint | ||
| pattern-sources: | ||
| - pattern: source() | ||
| pattern-sinks: | ||
| - patterns: | ||
| - pattern-inside: | | ||
| $X = new Builder(); | ||
| ... | ||
| - patterns: | ||
| - pattern-not-inside: $X.cfg("Content-Type", $V) | ||
| - metavariable-pattern: | ||
| metavariable: $V | ||
| patterns: | ||
| - pattern-either: | ||
| - pattern: '"application/json"' | ||
| - pattern: $X.sink($UNTRUSTED) | ||
| - focus-metavariable: $UNTRUSTED | ||
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
108 changes: 108 additions & 0 deletions
108
rules/ruleset/java/lib/generic/servlet-xss-html-response-sinks.yaml
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,108 @@ | ||
| rules: | ||
| - id: java-servlet-xss-html-response-sink | ||
| options: | ||
| lib: true | ||
| severity: NOTE | ||
| message: Direct write of unvalidated user input into a response without safe content type | ||
| metadata: | ||
| provenance: | ||
| - https://github.com/github/codeql/blob/main/java/ql/lib/semmle/code/java/security/XSS.qll | ||
| languages: | ||
| - java | ||
| mode: taint | ||
|
|
||
| # Servlet defaults Content-Type to text/html — writer/OutputStream is XSS | ||
| # unless setContentType pins non-HTML. Spring sibling for raw HttpServlet handlers. | ||
|
|
||
| pattern-sanitizers: | ||
| - patterns: | ||
| - pattern-either: | ||
| - pattern: Encode.forHtml(..., $UNTRUSTED, ...) | ||
| - pattern: (PolicyFactory $POLICY).sanitize(..., $UNTRUSTED, ...) | ||
| - pattern: (AntiSamy $AS).scan(..., $UNTRUSTED, ...) | ||
| - pattern: JSoup.clean(..., $UNTRUSTED, ...) | ||
| - pattern: HtmlUtils.htmlEscape(..., $UNTRUSTED, ...) | ||
| - pattern: org.apache.commons.lang.StringEscapeUtils.escapeHtml(..., $UNTRUSTED, ...) | ||
| - pattern: org.apache.commons.text.StringEscapeUtils.escapeHtml3(..., $UNTRUSTED, ...) | ||
| - pattern: org.apache.commons.text.StringEscapeUtils.escapeHtml4(..., $UNTRUSTED, ...) | ||
| - pattern: org.owasp.esapi.ESAPI.encoder().encodeForHTML(..., $UNTRUSTED, ...) | ||
| - focus-metavariable: $UNTRUSTED | ||
|
|
||
| pattern-sinks: | ||
| - patterns: | ||
| - patterns: | ||
| - pattern-either: | ||
| - pattern-inside: | | ||
| $RETURNTYPE $ENTRYPOINT(..., javax.servlet.http.HttpServletResponse $RESPONSE, ...) { | ||
| ... | ||
| } | ||
| - pattern-inside: | | ||
| $RETURNTYPE $ENTRYPOINT(..., jakarta.servlet.http.HttpServletResponse $RESPONSE, ...) { | ||
| ... | ||
| } | ||
| - metavariable-pattern: | ||
| metavariable: $ENTRYPOINT | ||
| pattern-either: | ||
| - pattern: doDelete | ||
| - pattern: doGet | ||
| - pattern: doPost | ||
| - pattern: doPut | ||
| - pattern: doTrace | ||
| - pattern: _jspService | ||
| - pattern-either: | ||
| - patterns: | ||
| - pattern-either: | ||
| - pattern-inside: | | ||
| $W = (javax.servlet.http.HttpServletResponse $RESPONSE).getWriter(...); | ||
| ... | ||
| - pattern-inside: | | ||
| $W = (jakarta.servlet.http.HttpServletResponse $RESPONSE).getWriter(...); | ||
| ... | ||
| - pattern: | | ||
| $W.$WRITE(..., $UNTRUSTED, ...); | ||
| - patterns: | ||
| - pattern-either: | ||
| - pattern-inside: | | ||
| $S = (javax.servlet.http.HttpServletResponse $RESPONSE).getOutputStream(...); | ||
| ... | ||
| - pattern-inside: | | ||
| $S = (jakarta.servlet.http.HttpServletResponse $RESPONSE).getOutputStream(...); | ||
| ... | ||
| - pattern: | | ||
| $S.$WRITE(..., $UNTRUSTED, ...); | ||
| - pattern: (HttpServletResponse $RESPONSE).sendError($CODE, $UNTRUSTED) | ||
| - pattern: (jakarta.servlet.jsp.JspWriter $W).$WRITE(..., $UNTRUSTED, ...) | ||
| - pattern: (javax.servlet.jsp.JspWriter $W).$WRITE(..., $UNTRUSTED, ...) | ||
|
|
||
| # $CT_SAFE OUTSIDE quotes binds to the whole string-literal expression; | ||
| # bare `application/json` can't be used — engine parses `/` `-` as Java operators. | ||
| - patterns: | ||
| - pattern-not-inside: | | ||
| (HttpServletResponse $RESPONSE).setContentType($CT_SAFE); | ||
| ... | ||
| - metavariable-pattern: | ||
| metavariable: $CT_SAFE | ||
| patterns: | ||
| - pattern-either: | ||
| - pattern: '"application/json"' | ||
| - pattern: '"application/json;charset=UTF-8"' | ||
| - pattern: '"text/plain"' | ||
| - pattern: '"application/pdf"' | ||
| - pattern: '"application/octet-stream"' | ||
| - pattern: '"application/xml"' | ||
| - pattern: '"text/xml"' | ||
| - pattern: '"image/png"' | ||
| - pattern: '"image/jpeg"' | ||
| - pattern: '"image/gif"' | ||
| # MediaType.X_VALUE constants — dev analyzer constant-folds; kept | ||
| # explicit for analyzer-regression / older-build robustness. | ||
| - pattern: MediaType.APPLICATION_JSON_VALUE | ||
| - pattern: MediaType.TEXT_PLAIN_VALUE | ||
| - pattern: MediaType.APPLICATION_PDF_VALUE | ||
| - pattern: MediaType.APPLICATION_OCTET_STREAM_VALUE | ||
| - pattern: MediaType.APPLICATION_XML_VALUE | ||
| - pattern: MediaType.TEXT_XML_VALUE | ||
| - pattern: MediaType.IMAGE_PNG_VALUE | ||
| - pattern: MediaType.IMAGE_JPEG_VALUE | ||
| - pattern: MediaType.IMAGE_GIF_VALUE | ||
| - focus-metavariable: $UNTRUSTED |
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
Oops, something went wrong.
Oops, something went wrong.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is important to use
"$V"in quotes