Spaces:
Running
Running
Commit Β·
1d6ebcd
1
Parent(s): 77238d9
Add permanent FP suppression layer (_FP_SUPPRESSIONS)
Browse filesEliminates all recurring false positive categories at the architecture
level β no more per-repo patches after every scan:
- Dynamic ? placeholder: emails.map(()=>'?') β bound values, not injected
- Ternary SQL keyword: ${flag ? 'UNIQUE' : ''} β fixed literals
- file:// SSRF: local module loading, not HTTP vector
- Type-only File Upload: import type / : BusboyFileStream declarations
- CREATE INDEX boolean flag: internal flag, not user input
- Open Redirect: variable assignment line β redirect issuance
Architecture: _is_suppressed(vuln, line) called once per match before
any finding is emitted. New FP categories can be added to _FP_SUPPRESSIONS
without touching any other code.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
app.py
CHANGED
|
@@ -1522,6 +1522,65 @@ _SQL_KEYWORDS = re.compile(r'\b(SELECT|INSERT|UPDATE|DELETE|FROM|WHERE|JOIN)\b',
|
|
| 1522 |
_TEMPLATE_VAR = re.compile(r'[$][{][^}]+[}]') # JS/TS template literals: ${var}
|
| 1523 |
_FSTRING_VAR = re.compile(r'[{][a-zA-Z_]\w*[^}]*[}]') # Python f-strings: {var}
|
| 1524 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1525 |
|
| 1526 |
def scan_file_content(content, filename):
|
| 1527 |
findings = []
|
|
@@ -1537,6 +1596,11 @@ def scan_file_content(content, filename):
|
|
| 1537 |
if not re.search(pattern, line):
|
| 1538 |
continue
|
| 1539 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1540 |
# ββ Fix 1: SQL Injection mislabeled as Hardcoded Secret ββ
|
| 1541 |
# If the matched line contains SQL keywords AND a template/f-string
|
| 1542 |
# interpolation with user input, it's SQL Injection, not a secret.
|
|
|
|
| 1522 |
_TEMPLATE_VAR = re.compile(r'[$][{][^}]+[}]') # JS/TS template literals: ${var}
|
| 1523 |
_FSTRING_VAR = re.compile(r'[{][a-zA-Z_]\w*[^}]*[}]') # Python f-strings: {var}
|
| 1524 |
|
| 1525 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 1526 |
+
# PERMANENT FALSE-POSITIVE SUPPRESSION LAYER
|
| 1527 |
+
# Applied per-line before any finding is emitted.
|
| 1528 |
+
# Each entry: (vuln_type_or_None, compiled_regex, reason)
|
| 1529 |
+
# vuln_type=None β applies to ALL vulnerability types
|
| 1530 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 1531 |
+
_FP_SUPPRESSIONS = [
|
| 1532 |
+
|
| 1533 |
+
# ββ SQL: dynamic ? placeholder builder βββββββββββββββββ
|
| 1534 |
+
# emails.map(() => '?') / ids.map((_) => '?')
|
| 1535 |
+
# Generates "?,?,?" bound values β never injects user data
|
| 1536 |
+
(None,
|
| 1537 |
+
re.compile(r'\$\{[^}]+\.map\s*\(\s*[^)]*\)\s*\.?(join\s*\(|map\s*\()?[^}]*\?[^}]*\}', re.IGNORECASE),
|
| 1538 |
+
"Dynamic ? placeholder builder β values are bound, not interpolated"),
|
| 1539 |
+
|
| 1540 |
+
# ββ SQL: ternary returning SQL keyword literals only ββββ
|
| 1541 |
+
# ${isUnique ? 'UNIQUE ' : ''} / ${desc ? 'DESC' : 'ASC'}
|
| 1542 |
+
(None,
|
| 1543 |
+
re.compile(r'\$\{\s*\w+\s*\?\s*[\'"][A-Z_\s]*[\'"]\s*:\s*[\'"][A-Z_\s]*[\'"]\s*\}', re.IGNORECASE),
|
| 1544 |
+
"Ternary returning fixed SQL keyword literals β not user-controlled"),
|
| 1545 |
+
|
| 1546 |
+
# ββ SSRF: file:// scheme used for module/script loading β
|
| 1547 |
+
# file://${entrypointPath} β local filesystem, not HTTP
|
| 1548 |
+
("SSRF",
|
| 1549 |
+
re.compile(r'file://\$\{', re.IGNORECASE),
|
| 1550 |
+
"file:// URL scheme for local module loading β not an HTTP SSRF vector"),
|
| 1551 |
+
|
| 1552 |
+
# ββ File Upload: type-only declarations, not handlers βββ
|
| 1553 |
+
# import type { BusboyFileStream } / : BusboyFileStream
|
| 1554 |
+
("Insecure File Upload",
|
| 1555 |
+
re.compile(r'(?:import\s+type\b|:\s*(?:BusboyFileStream|Readable|Stream|FileStream)\b)', re.IGNORECASE),
|
| 1556 |
+
"Type-only reference β no actual upload handler present on this line"),
|
| 1557 |
+
|
| 1558 |
+
# ββ SQL: CREATE INDEX with boolean flag only ββββββββββββ
|
| 1559 |
+
# CREATE ${flag ? 'UNIQUE ' : ''}INDEX β flag is internal boolean
|
| 1560 |
+
(None,
|
| 1561 |
+
re.compile(r'CREATE\s+\$\{\s*\w+\s*\?\s*[\'"]UNIQUE\s*[\'"]\s*:\s*[\'"][\'"]', re.IGNORECASE),
|
| 1562 |
+
"CREATE INDEX with internal boolean flag β not user-controlled"),
|
| 1563 |
+
|
| 1564 |
+
# ββ Open Redirect: variable assigned but not yet used βββ
|
| 1565 |
+
# let redirect = req.query[...] β assignment line alone is not a redirect
|
| 1566 |
+
("Open Redirect",
|
| 1567 |
+
re.compile(r'^\s*(let|const|var)\s+\w+\s*=\s*req\.(query|body|params)', re.IGNORECASE),
|
| 1568 |
+
"Variable assignment only β redirect not issued on this line"),
|
| 1569 |
+
]
|
| 1570 |
+
|
| 1571 |
+
|
| 1572 |
+
def _is_suppressed(vuln: str, line: str) -> tuple:
|
| 1573 |
+
"""
|
| 1574 |
+
Returns (True, reason) if this finding should be suppressed as a known false positive.
|
| 1575 |
+
Returns (False, '') otherwise.
|
| 1576 |
+
"""
|
| 1577 |
+
for fp_vuln, fp_re, reason in _FP_SUPPRESSIONS:
|
| 1578 |
+
if fp_vuln and fp_vuln != vuln:
|
| 1579 |
+
continue
|
| 1580 |
+
if fp_re.search(line):
|
| 1581 |
+
return True, reason
|
| 1582 |
+
return False, ""
|
| 1583 |
+
|
| 1584 |
|
| 1585 |
def scan_file_content(content, filename):
|
| 1586 |
findings = []
|
|
|
|
| 1596 |
if not re.search(pattern, line):
|
| 1597 |
continue
|
| 1598 |
|
| 1599 |
+
# ββ Permanent FP suppression layer ββββββββββββββ
|
| 1600 |
+
suppressed, _ = _is_suppressed(vuln, line)
|
| 1601 |
+
if suppressed:
|
| 1602 |
+
continue
|
| 1603 |
+
|
| 1604 |
# ββ Fix 1: SQL Injection mislabeled as Hardcoded Secret ββ
|
| 1605 |
# If the matched line contains SQL keywords AND a template/f-string
|
| 1606 |
# interpolation with user input, it's SQL Injection, not a secret.
|