Skip to content

Commit 624d0bb

Browse files
filipi86claude
andcommitted
fix(engine): eliminate false positives from invalid regex fallback (#24)
Go's RE2 does not support negative lookahead. Two rules (JS-014 and K8S-003) used `(?!...)` patterns that failed to compile and fell back to `$^`, which still matches empty strings, causing findings on every blank line. Reported in issue #24 on plain Next.js config files. - mustCompile: replace `$^` fallback with `[^\s\S]` (truly never matches) - JS-014: rewrite using Pattern `.then(` + AntiPattern `.catch(`, reusing the existing AntiPattern mechanism - K8S-003: remove the rule; its multi-line YAML check cannot be expressed in RE2 and the engine scans line-by-line. Will return when a YAML-aware check lands. Closes #24 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
1 parent 4c2f2d1 commit 624d0bb

4 files changed

Lines changed: 21 additions & 18 deletions

File tree

internal/engine/engine.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -184,11 +184,13 @@ func buildSnippet(lines []string, lineNum, context int) string {
184184
return strings.Join(lines[start:end], "\n")
185185
}
186186

187-
// mustCompile compiles regex or returns a never-matching pattern on error
187+
// mustCompile compiles regex or returns a never-matching pattern on error.
188+
// The fallback uses [^\s\S] because `$^` still matches empty strings (blank
189+
// lines), which caused false positives for rules with invalid regex.
188190
func mustCompile(pattern string) *regexp.Regexp {
189191
re, err := regexp.Compile(pattern)
190192
if err != nil {
191-
return regexp.MustCompile(`$^`) // never matches
193+
return regexp.MustCompile(`[^\s\S]`) // truly never matches
192194
}
193195
return re
194196
}

internal/engine/rules_javascript.go

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -239,18 +239,21 @@ func javascriptRules() []Rule {
239239
Remediation: "Remove sensitive data from logs. Use structured logging with field-level masking. Implement a centralized logging strategy.",
240240
},
241241

242-
// A10:2025 - Unhandled promise rejection
242+
// A10:2025 - Unhandled promise rejection.
243+
// Go RE2 has no negative lookahead, so we detect `.then(` with Pattern
244+
// and suppress when `.catch(` is present on the same line via AntiPattern.
243245
{
244246
ID: "JS-014",
245247
Language: config.LangJavaScript,
246248
Severity: config.SeverityLow,
247249
Title: "Unhandled promise rejection",
248250
Description: "Promise chains without .catch() or async functions without try/catch can " +
249251
"cause unexpected application behavior and hide security errors.",
250-
Pattern: mustCompile(`\.then\s*\([^)]+\)\s*(?!\.catch)`),
251-
OWASP: config.OWASP_A10_MishandlingExceptionalConditions,
252-
CWE: "CWE-390",
253-
CVSS: 3.7,
252+
Pattern: mustCompile(`\.then\s*\(`),
253+
AntiPattern: mustCompile(`\.catch\s*\(`),
254+
OWASP: config.OWASP_A10_MishandlingExceptionalConditions,
255+
CWE: "CWE-390",
256+
CVSS: 3.7,
254257
References: []string{
255258
"https://cwe.mitre.org/data/definitions/390.html",
256259
},

internal/engine/rules_other.go

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -349,15 +349,11 @@ func kubernetesRules() []Rule {
349349
References: []string{"https://cwe.mitre.org/data/definitions/250.html"},
350350
Remediation: "Remove privileged: true. Use specific capabilities (add: [NET_BIND_SERVICE]) instead of full privileges.",
351351
},
352-
{
353-
ID: "K8S-003", Language: config.LangKubernetes, Severity: config.SeverityMedium,
354-
Title: "Resource limits not set",
355-
Description: "Containers without CPU/memory limits can be used for resource exhaustion DoS.",
356-
Pattern: mustCompile(`(?i)containers:\s*\n(?:(?!resources:)[\s\S]){1,500}image:`),
357-
OWASP: config.OWASP_A02_SecurityMisconfiguration, CWE: "CWE-770", CVSS: 5.3,
358-
References: []string{"https://cwe.mitre.org/data/definitions/770.html"},
359-
Remediation: "Always set resources.limits.cpu and resources.limits.memory for all containers.",
360-
},
352+
// K8S-003 (Resource limits not set) was removed: the original pattern used
353+
// a multi-line negative lookahead which RE2 does not support, and the
354+
// engine scans line-by-line, so this check cannot be implemented as a
355+
// regex. Proper detection requires a YAML parser and will be reintroduced
356+
// when that support lands.
361357
{
362358
ID: "K8S-004", Language: config.LangKubernetes, Severity: config.SeverityHigh,
363359
Title: "Hardcoded secret in environment variable",

internal/leaks/detector.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -559,11 +559,13 @@ func (d *Detector) loadRules() {
559559
}
560560
}
561561

562-
// mustCompile compiles a regex or returns never-matching pattern
562+
// mustCompile compiles a regex or returns never-matching pattern.
563+
// `$^` still matches empty strings (blank lines), so we use [^\s\S]
564+
// which cannot match any character.
563565
func mustCompile(pattern string) *regexp.Regexp {
564566
re, err := regexp.Compile(pattern)
565567
if err != nil {
566-
return regexp.MustCompile(`$^`)
568+
return regexp.MustCompile(`[^\s\S]`)
567569
}
568570
return re
569571
}

0 commit comments

Comments
 (0)