Skip to content

feat(rules): Update rules to use star operator - #314

Open
misonijnik wants to merge 12 commits into
saloed/2-star-rewritefrom
misonijnik/3-rules
Open

feat(rules): Update rules to use star operator#314
misonijnik wants to merge 12 commits into
saloed/2-star-rewritefrom
misonijnik/3-rules

Conversation

@misonijnik

Copy link
Copy Markdown
Member

No description provided.

@misonijnik
misonijnik force-pushed the misonijnik/3-rules branch 3 times, most recently from f5be3ef to 97adcc7 Compare July 30, 2026 08:10
@misonijnik
misonijnik force-pushed the misonijnik/3-rules branch 2 times, most recently from 9508d6a to c3403d7 Compare August 12, 2026 09:24
@misonijnik
misonijnik changed the base branch from main to saloed/2-star-rewrite August 12, 2026 09:46
@misonijnik
misonijnik marked this pull request as ready for review August 14, 2026 10:15
@Saloed
Saloed force-pushed the misonijnik/3-rules branch from 11ca418 to bc5cc77 Compare August 17, 2026 19:25
@misonijnik
misonijnik force-pushed the misonijnik/3-rules branch 2 times, most recently from 41185cc to 56224bf Compare August 18, 2026 15:45
…e star

Replaces the two hard-coded Spring hacks with rule-level star operators: the
controller parameter source is now `$*UNTRUSTED`, and the controller-return
any-field sinks are expressed with a starred metavar. Both the source hack and
the sink hack are deleted.

Also restores the Z2F-gate bypass for controller-return sinks and tightens the
source `$TYPE` regex, which the hack had been masking.
Keeps array and primitive parameters as plain value sources, stars the
untrusted-path-source pattern-not with a fresh metavar, drops the List adapter
overloads from the command-injection sink, and collapses the servlet upload
source read-back -- all expressible directly now that a starred metavar means
whole-object taint.

Documents the pattern-not star limitation, the sink focus requirement and the
Go parity story in the rules README.
resolveArrayPosition was the last implicit type-triggered array mechanism: it
silently gave every array- or Object-typed source ASSIGN position an element
twin. The star operator expresses the same thing from the rules, and does it
better -- the any-field star is recursive, so it also catches the deep
Map<String,String[]> flows the element-only twin missed.

Array and vararg sink args are now starred explicitly, the implicit sink
any-field emission is gone, and the Go side drops its blanket any-accessor
emission in favour of explicit variadic taint in the Go model config.
Makes the servlet source whole-object and adds the channel-model getter
passthroughs it reads back, and stars the xss and response-injection value
sanitizers so a sanitized wrapper is recognised as clean at every depth.
Collapses the source down to a single focused form, focuses and stars the
session-store sink, and flags a tainted attribute NAME as well as a tainted
value -- previously only the value was considered.
Makes the java.io.File model field-sensitive with starred path sinks, and
migrates every starred metavar in the ruleset, the Spring rule provider and the
rules README to the $*VAR spelling the parser accepts.
The one false positive the rule-tests have been carrying since before this batch:
InsecureDesignSamples#validateBeforeCrossingTrustBoundarySecure reads a value back
out of the session and stores it again, which is the textbook safe shape, and it
reported.

The source pattern was `$*RESULT = $REQ.$FUNC(...)` with $REQ bound by the sibling
pattern-inside and getSession excluded by a $FUNC regex. Probing the analyzer with
three rewritten regexes shows what actually happens:

  ^zzzNoSuchMethodzzz$  -> 0 findings   (the regex constraint is applied)
  ^getSession$          -> 2 findings   (getSession calls do reach the sink)
  ^getAttribute$        -> 2 findings, including the FP

The last one is the answer: `session.getAttribute(..)` matches `$REQ.$FUNC(..)`
because the $REQ binding from pattern-inside is not enforced in the sibling pattern,
so any receiver matches. Reading back out of the session became a source, and the
getSession exclusion never saw the call it was meant to stop.

Typing the receiver in the pattern itself - `(HttpServletRequest $REQ).$FUNC(...)`,
which is what the sink patterns already do - keeps the three true positives and drops
the false one. The rule-tests are now 0 FP / 0 FN for the first time.
@Saloed
Saloed force-pushed the misonijnik/3-rules branch from 56224bf to 44877fb Compare August 19, 2026 22:10
`StringUtils.arrayToCommaDelimitedString(dir.listFiles())` joins `String.valueOf(element)`
for every element, so a mark on an element *object* -- or on any field of it, e.g.
`java.io.File#path` -- belongs on the returned String as a plain value.

The library model instead copies `arg(0)[*]` verbatim, which carries the element's own
accessors over into a String result; `result.java.io.File#path` is correctly rejected by
the type checker and the flow dies there. WebGoat's

    ResponseEntity.status(..).body(
        StringUtils.arrayToCommaDelimitedString(catPicture.getParentFile().listFiles())
                   .getBytes())

lost its `xss-in-spring-app` finding at ProfileUploadRetrieval.java:114 that way.

A propagator with a **starred** `from` reads through the element's fields and assigns a
plain value, which is exactly the join's semantics. The star has to sit on the pattern
occurrence -- the `from:`/`to:` YAML fields stay starless, like `focus-metavariable`.
Verified: dropping the star loses the shape again; on WebGoat the propagator adds exactly
one finding, the lost one (65 vs 64 results).

Propagators are per-rule, and only `mode: taint` lib rules -- the sink libs -- have the
slot, so this repeats in each sink lib where the helper can appear on a flow.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@Saloed
Saloed force-pushed the misonijnik/3-rules branch from fd0a4b7 to e9f5c2b Compare August 19, 2026 22:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant