-
-
Notifications
You must be signed in to change notification settings - Fork 38
Audit lane: add an adversarial-verify stage (refute each finding vs code) before findings become cards #2730
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| ### Added | ||
|
|
||
| - An adversarial-verify stage in the static security analysis pipeline: each finding produced by the code analyzers is now re-examined against its source line to refute false positives inside comments, string literals, or known example values before the findings are surfaced to the user. |
| Original file line number | Diff line number | Diff line change | ||||||
|---|---|---|---|---|---|---|---|---|
|
|
@@ -462,16 +462,102 @@ def analyze_app_source(files: dict[str, str]) -> list[Finding]: | |||||||
| content. Findings are returned in a stable order: file (as given in the | ||||||||
| dict), then line number, then detector registration order -- so repeat | ||||||||
| calls against the same input are deterministic and diffable. | ||||||||
|
|
||||||||
| After the initial detector pass, findings are run through an | ||||||||
| adversarial-verify stage that refutes false positives by checking | ||||||||
| whether the detected pattern appears inside a string literal, comment, | ||||||||
| or known example value. | ||||||||
| """ | ||||||||
| findings: list[Finding] = [] | ||||||||
| for filename, content in files.items(): | ||||||||
| for detector in _ALL_DETECTORS: | ||||||||
| findings.extend(detector(filename, content)) | ||||||||
| file_order = {name: idx for idx, name in enumerate(files)} | ||||||||
| findings.sort(key=lambda f: (file_order[f.file], f.line)) | ||||||||
| return findings | ||||||||
| return adversarial_verify(findings, files) | ||||||||
|
|
||||||||
|
|
||||||||
| def has_critical(findings: list[Finding]) -> bool: | ||||||||
| """Return True if any finding is severity "critical".""" | ||||||||
| return any(f.severity == "critical" for f in findings) | ||||||||
|
|
||||||||
|
|
||||||||
| # --------------------------------------------------------------------------- # | ||||||||
| # Adversarial verification | ||||||||
| # --------------------------------------------------------------------------- # | ||||||||
|
|
||||||||
| # Trigger tokens used to decide whether a finding's pattern sits inside a | ||||||||
| # string literal. The token is the shortest unique substring the detector | ||||||||
| # matches on that line. | ||||||||
| _TRIGGER_TOKENS: dict[str, str] = { | ||||||||
| "eval-like-execution": "eval(", | ||||||||
| "network-exfil": "fetch(", | ||||||||
| "dom-xss-sink": ".innerHTML", | ||||||||
| "dangerous-url-scheme": "javascript:", | ||||||||
| "inline-event-handler-injection": "setAttribute(", | ||||||||
| "hardcoded-secret": "AKIA", | ||||||||
| "sandbox-escape-attempt": "window.parent", | ||||||||
| "postmessage-no-origin-check": "postMessage(", | ||||||||
| "storage-exfil": "localStorage.", | ||||||||
| } | ||||||||
|
|
||||||||
| # Known example/placeholder values that should never trigger a real finding. | ||||||||
| _KNOWN_EXAMPLES: tuple[str, ...] = ( | ||||||||
| "AKIAIOSFODNN7EXAMPLE", | ||||||||
| ) | ||||||||
|
|
||||||||
|
|
||||||||
| def adversarial_verify(findings: list[Finding], files: dict[str, str]) -> list[Finding]: | ||||||||
| """Second-pass adversarial check that refutes false-positive findings. | ||||||||
|
|
||||||||
| Each finding is re-examined against its source line. A finding is | ||||||||
| dropped when the line context clearly shows the detected pattern is | ||||||||
| inert -- e.g. it lives inside a string literal, a comment, or matches | ||||||||
| a known example/placeholder value. | ||||||||
| """ | ||||||||
| verified: list[Finding] = [] | ||||||||
| for f in findings: | ||||||||
| content = files.get(f.file, "") | ||||||||
| if not content: | ||||||||
| verified.append(f) | ||||||||
| continue | ||||||||
| lines = content.splitlines() | ||||||||
| if f.line < 1 or f.line > len(lines): | ||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. WARNING: Out-of-range findings are silently dropped. Reply with |
||||||||
| continue | ||||||||
| line = lines[f.line - 1] | ||||||||
| if _is_clearly_false_positive(f, line): | ||||||||
| continue | ||||||||
| verified.append(f) | ||||||||
| return verified | ||||||||
|
|
||||||||
|
|
||||||||
| def _is_clearly_false_positive(finding: Finding, line: str) -> bool: | ||||||||
| stripped = line.lstrip() | ||||||||
|
|
||||||||
| if stripped.startswith("//"): | ||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. WARNING: Comment detection only inspects lines that start with
If a finding's pattern sits inside a comment anywhere on the line, the refute logic should detect that. Consider scanning the line for a Reply with |
||||||||
| return True | ||||||||
| if stripped.startswith("/*"): | ||||||||
| return True | ||||||||
| if stripped.startswith("*") and not stripped.startswith("*/"): | ||||||||
| return True | ||||||||
|
Comment on lines
+537
to
+542
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift Handle inline and multi-line comments. These checks only reject lines that start with a comment prefix. For example, 🤖 Prompt for AI Agents |
||||||||
|
|
||||||||
| if _trigger_is_inside_string(finding, line): | ||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -eu
printf '%s\n' '--- repository conventions and learnings ---'
find /tmp/coderabbit-repo-knowledge/jaylfc-taos-a13cd95b -type f -name '*.md' -maxdepth 3 -print
printf '%s\n' '--- analyzer definitions and verification path ---'
sed -n '1,180p' tinyagentos/code_analyzer.py
sed -n '430,565p' tinyagentos/code_analyzer.py
printf '%s\n' '--- focused tests ---'
sed -n '250,365p' tests/test_code_analyzer.pyRepository: jaylfc/taOS Length of output: 16993 Sensitive Data Exposure (CWE-798): Use of Hard-coded Credentials Reachability: External · Exploitability: Trivial Keep real hardcoded-secret findings. Exclude 🤖 Prompt for AI Agents🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -eu
printf '%s\n' '--- analyzer structure and relevant implementation ---'
ast-grep outline tinyagentos/code_analyzer.py
printf '%s\n' '--- detector and verifier definitions ---'
rg -n -A45 -B12 'detect_dangerous_url_scheme|_trigger_is_inside_string|_is_clearly_false_positive|adversarial_verify|_ALL_DETECTORS|dangerous-url-scheme' tinyagentos/code_analyzer.py
printf '%s\n' '--- repository convention scope ---'
head -5 /tmp/coderabbit-repo-knowledge/jaylfc-taos-a13cd95b/*/*.md 2>/dev/null || trueRepository: jaylfc/taOS Length of output: 12932 XSS (CWE-79): Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting') Reachability: External · Exploitability: Trivial Keep The verifier drops normal matches such as 🤖 Prompt for AI Agents |
||||||||
| return True | ||||||||
|
|
||||||||
| if finding.rule_id == "hardcoded-secret": | ||||||||
| for example in _KNOWN_EXAMPLES: | ||||||||
| if example in line: | ||||||||
| return True | ||||||||
|
|
||||||||
| return False | ||||||||
|
|
||||||||
|
|
||||||||
| def _trigger_is_inside_string(finding: Finding, line: str) -> bool: | ||||||||
| token = _TRIGGER_TOKENS.get(finding.rule_id) | ||||||||
| if not token: | ||||||||
| return False | ||||||||
| idx = line.find(token) | ||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift 🧩 Analysis chain🏁 Script executed: sed -n '500,570p' tinyagentos/code_analyzer.py
printf '\nDetector definitions and Finding fields:\n'
rg -n -C 3 'class Finding|network-exfil|dangerous-url-scheme|hardcoded-secret|rule_id|token' tinyagentos/code_analyzer.pyRepository: jaylfc/taOS Length of output: 7047 🏁 Script executed: sed -n '130,260p' tinyagentos/code_analyzer.py
printf '\nDetector registration and nearby pattern definitions:\n'
sed -n '1,130p' tinyagentos/code_analyzer.pyRepository: jaylfc/taOS Length of output: 11490 Other (CWE-693) Reachability: External · Exploitability: Moderate Verify the actual detector match, not the first token on the line.
🤖 Prompt for AI Agents |
||||||||
| if idx == -1: | ||||||||
| return False | ||||||||
| before = line[:idx] | ||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. SUGGESTION: The quote-count heuristic does not handle escaped quotes ( Reply with |
||||||||
| return before.count('"') % 2 == 1 or before.count("'") % 2 == 1 | ||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. CRITICAL: String-detection heuristic is inverted — An odd quote count in The test
Suggested change
Reply with There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift 🧩 Analysis chain🏁 Script executed: sed -n '430,570p' tinyagentos/code_analyzer.py
printf '\n--- focused tests ---\n'
sed -n '250,365p' tests/test_code_analyzer.pyRepository: jaylfc/taOS Length of output: 8974 Other (CWE-693) Reachability: External · Exploitability: Trivial Use lexical state instead of quote counts. When a line contains 🤖 Prompt for AI Agents |
||||||||
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.
SUGGESTION: The adversarial-verify test suite has no coverage for the most common false-positive trap the heuristic must handle — a trigger token that follows a closed string literal on the same line (e.g.
let s = "hi"; eval(x)). Adding such a test would have surfaced the inverted-odd-count bug above. Also add coverage for: Python#comments, trailing inline//comments, escaped quotes, and multi-line block-comment middle lines.Reply with
@kilocode-bot fix itto have Kilo Code address this issue.