CWE-95 + frontend/template XSS precision - #73
Open
leenk7991 wants to merge 5 commits into
Open
Conversation
Skip the taint pass when a pack has no taint rules, and match template text on auto-detected HTML so CLI/Fusion no longer drop search findings. Co-authored-by: Cursor <cursoragent@cursor.com>
Search-mode vm patterns and setInterval(string) TPs; treat textContent then innerHTML as a sanitizer. Identifier setTimeout callbacks stay quiet. Co-authored-by: Cursor <cursoragent@cursor.com>
leenk7991
marked this pull request as ready for review
August 20, 2026 08:45
juangaitanv
reviewed
Aug 20, 2026
juangaitanv
reviewed
Aug 24, 2026
Comment on lines
+158
to
+184
| patterns: Some([ | ||
| "settimeout-eval-sink=", | ||
| "regex:setTimeout\\(\\s*['\"]", | ||
| "regex:setInterval\\(\\s*['\"]" | ||
| ]), | ||
| finding_type: Some("Code Injection"), | ||
| severity: Some("Critical"), | ||
| confidence: Some("High"), | ||
| cwe_id: Some("cwe-95"), | ||
| description: Some("setTimeout/setInterval with a string argument evaluates attacker-controlled code"), | ||
| file_types: Some((extensions: Some([".js", ".jsx", ".ts", ".tsx"]))), | ||
| tags: Some(["code-injection", "frontend", "cwe-95"]) | ||
| ), | ||
| ( | ||
| id: Some("js-code-injection-004"), | ||
| name: Some("Code injection via vm.runIn*"), | ||
| category: Some("code-injection"), | ||
| mode: "search", | ||
| // Dummy `=` names pass the call-name prefilter (escaped regex dots | ||
| // are not a substring of `vm.runInNewContext`). | ||
| patterns: Some([ | ||
| "vm.runInNewContext=", | ||
| "vm.runInThisContext=", | ||
| "vm.runInContext=", | ||
| "regex:vm\\.runIn(?:New|This)?Context\\(\\s*[A-Za-z_$]", | ||
| "regex:vm\\.runIn(?:New|This)?Context\\(\\s*['\"]" | ||
| ]), |
Contributor
There was a problem hiding this comment.
constant timer strings and trusted compiled scripts match as Critical CWE-95. could we require dynamic or untrusted input and leave identifiers to taint analysis?
|
|
||
| sinks: Some([ | ||
| // Direct code execution | ||
| // Eval injection only. Template engines / dynamic require are not CWE-95. |
Contributor
There was a problem hiding this comment.
removing these sinks leaves request-sourced server template injection uncovered. should we retain them under CWE-94 or include a replacement rule?
| // names like `th:utext`, `th:replace`, or tag names like `textarea` | ||
| // resolve as the matchable "function" name for search rules. | ||
| match node.kind() { | ||
| "text" => django_template_name_from_text(get_node_text_slice(node, source)), |
Contributor
There was a problem hiding this comment.
Django filters in attribute values do not reach the safe-filter rule. could we extract template tokens from attribute values as well?
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.
Summary
Stop Sighthound from reporting DOM APIs as CWE-95 eval injection, and cut Django/HTMX XSS noise without dropping real DOM XSS.
Problem: CWE-95 findings were innerHTML/htmx/DOMPurify writes, rated Critical. HTML-only files failed combined mode (
No taint flow rules found) so Django|safenever ran.vm.runInNewContext(userInput)was silent (taint-only sink; frontend sources are DOM, not generic params; search prefilter dropped regex-escapedvm.runIn*names).escapeHtml(textContentthen readinnerHTML) was CWE-79.Root cause: CWE-95 sink list too broad (any HTML write ≈ eval). Combined mode required taint rules even when the language only has search rules. HTML language did not visit
textnodes. No search-modevmpattern that survived the call-name prefilter.Changes
setTimeout(handler, n)((setTimeout('…')); identifier callbacks stay quiet. Taint still flags a timer when the first arg is a known sourcevm.runInNewContext=(and This/Context) names so the search prefilter keepsvm.runIn*(escaped regex dots are not a substring of the call name)escapeHtml: textContent write then innerHTML read is a sanitizer, not CWE-79|safe/|mark_safeon request.GET/POST/COOKIES onlytextnodes so|safematches when.htmlauto-detects ashtmlejs.render/ handlebars / mustache) is no longer CWE-95 (eval-family only; CWE-94 follow-up)build.rsreruns compile whenrules/change/sighthound_release/(local platform export; do not commit the binary)setTimeout(handler); TP eval / Function / string-literal timers / vm /|safeTest plan
escapeHtml, DOMPurify,template.innerHTML,htmx.trigger, callback timers)escapeHtmlis not CWE-79innerHTML = location.hashremains CWE-79setTimeout(userInput)is CWE-95;setTimeout(function () {…})/setTimeout(handler)is notsetInterval(userInput)is CWE-95;setInterval(function () {…})is noteval/new Function/Function('return '+x)/eval(location.hash)/vm.runInNewContext(userInput)are CWE-95|safeand| mark_safeon request data are CWE-79; autoescape /json_script/hx-swapare nothtmlstill flags|safecargo test --test strictness_tests -- cwe95_xss_sink_precisionE2E (fixture Django + JS app, this branch vs
main):eval/Functionreported as CWE-95 (were CWE-94 onmain)setTimeout(userInput)with a string argument reported as CWE-95 (missing onmain)innerHTML = location.hashstill reported as CWE-79escapeHtml/createElement/ parse-onlyinnerHTMLhelpers no longer reported as CWE-95setTimeout(function () {…})callback not CWE-95|safe/|mark_safeon request data reported as CWE-79 (missing onmain)Checklist
make cipasses locally (the same command CI runs — see CONTRIBUTING.md)make bootstraponce so pre-commit/pre-push hooks are activelinear ticket: https://linear.app/corgea/issue/COR-1802/