detectTautologyInSQL checks raw SQL for tautology patterns (e.g. OR 1=1, 'a'='a'). Because Go's RE2 engine does not support backreferences, equality of the two sides is verified programmatically after the regex captures both groups.
(sql string, result *ScanResult)
| 1017 | // Because Go's RE2 engine does not support backreferences, equality of the two sides |
| 1018 | // is verified programmatically after the regex captures both groups. |
| 1019 | func (s *Scanner) detectTautologyInSQL(sql string, result *ScanResult) { |
| 1020 | // Ensure patterns are initialized |
| 1021 | tautologyCaptureOnce.Do(initTautologyCapturePatterns) |
| 1022 | |
| 1023 | // Guard against very long inputs (ReDoS mitigation) |
| 1024 | input := sql |
| 1025 | if len(input) > maxRegexInputLen { |
| 1026 | input = input[:maxRegexInputLen] |
| 1027 | } |
| 1028 | |
| 1029 | for _, re := range tautologyCapturePatterns { |
| 1030 | matches := re.FindAllStringSubmatch(input, -1) |
| 1031 | for _, m := range matches { |
| 1032 | if len(m) == 3 && strings.EqualFold(m[1], m[2]) { |
| 1033 | finding := Finding{ |
| 1034 | Severity: SeverityCritical, |
| 1035 | Pattern: PatternTautology, |
| 1036 | Description: "Always-true condition detected (tautology): " + m[0], |
| 1037 | Risk: "Authentication bypass via always-true condition", |
| 1038 | Suggestion: "Use parameterized queries to prevent tautology injection", |
| 1039 | } |
| 1040 | if s.shouldInclude(finding.Severity) { |
| 1041 | result.Findings = append(result.Findings, finding) |
| 1042 | } |
| 1043 | // Report at most one tautology finding per pattern to avoid noise |
| 1044 | break |
| 1045 | } |
| 1046 | } |
| 1047 | } |
| 1048 | } |
| 1049 | |
| 1050 | // detectRegexPatterns checks SQL against compiled regex patterns. |
| 1051 | func (s *Scanner) detectRegexPatterns(sql string, patternType PatternType, result *ScanResult) { |