@oculum/scanner 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/formatters/cli-terminal.d.ts +27 -0
- package/dist/formatters/cli-terminal.d.ts.map +1 -0
- package/dist/formatters/cli-terminal.js +412 -0
- package/dist/formatters/cli-terminal.js.map +1 -0
- package/dist/formatters/github-comment.d.ts +41 -0
- package/dist/formatters/github-comment.d.ts.map +1 -0
- package/dist/formatters/github-comment.js +306 -0
- package/dist/formatters/github-comment.js.map +1 -0
- package/dist/formatters/grouping.d.ts +52 -0
- package/dist/formatters/grouping.d.ts.map +1 -0
- package/dist/formatters/grouping.js +152 -0
- package/dist/formatters/grouping.js.map +1 -0
- package/dist/formatters/index.d.ts +9 -0
- package/dist/formatters/index.d.ts.map +1 -0
- package/dist/formatters/index.js +35 -0
- package/dist/formatters/index.js.map +1 -0
- package/dist/formatters/vscode-diagnostic.d.ts +103 -0
- package/dist/formatters/vscode-diagnostic.d.ts.map +1 -0
- package/dist/formatters/vscode-diagnostic.js +151 -0
- package/dist/formatters/vscode-diagnostic.js.map +1 -0
- package/dist/index.d.ts +52 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +648 -0
- package/dist/index.js.map +1 -0
- package/dist/layer1/comments.d.ts +8 -0
- package/dist/layer1/comments.d.ts.map +1 -0
- package/dist/layer1/comments.js +203 -0
- package/dist/layer1/comments.js.map +1 -0
- package/dist/layer1/config-audit.d.ts +8 -0
- package/dist/layer1/config-audit.d.ts.map +1 -0
- package/dist/layer1/config-audit.js +252 -0
- package/dist/layer1/config-audit.js.map +1 -0
- package/dist/layer1/entropy.d.ts +8 -0
- package/dist/layer1/entropy.d.ts.map +1 -0
- package/dist/layer1/entropy.js +500 -0
- package/dist/layer1/entropy.js.map +1 -0
- package/dist/layer1/file-flags.d.ts +7 -0
- package/dist/layer1/file-flags.d.ts.map +1 -0
- package/dist/layer1/file-flags.js +112 -0
- package/dist/layer1/file-flags.js.map +1 -0
- package/dist/layer1/index.d.ts +36 -0
- package/dist/layer1/index.d.ts.map +1 -0
- package/dist/layer1/index.js +132 -0
- package/dist/layer1/index.js.map +1 -0
- package/dist/layer1/patterns.d.ts +8 -0
- package/dist/layer1/patterns.d.ts.map +1 -0
- package/dist/layer1/patterns.js +482 -0
- package/dist/layer1/patterns.js.map +1 -0
- package/dist/layer1/urls.d.ts +8 -0
- package/dist/layer1/urls.d.ts.map +1 -0
- package/dist/layer1/urls.js +296 -0
- package/dist/layer1/urls.js.map +1 -0
- package/dist/layer1/weak-crypto.d.ts +7 -0
- package/dist/layer1/weak-crypto.d.ts.map +1 -0
- package/dist/layer1/weak-crypto.js +291 -0
- package/dist/layer1/weak-crypto.js.map +1 -0
- package/dist/layer2/ai-agent-tools.d.ts +19 -0
- package/dist/layer2/ai-agent-tools.d.ts.map +1 -0
- package/dist/layer2/ai-agent-tools.js +528 -0
- package/dist/layer2/ai-agent-tools.js.map +1 -0
- package/dist/layer2/ai-endpoint-protection.d.ts +36 -0
- package/dist/layer2/ai-endpoint-protection.d.ts.map +1 -0
- package/dist/layer2/ai-endpoint-protection.js +332 -0
- package/dist/layer2/ai-endpoint-protection.js.map +1 -0
- package/dist/layer2/ai-execution-sinks.d.ts +18 -0
- package/dist/layer2/ai-execution-sinks.d.ts.map +1 -0
- package/dist/layer2/ai-execution-sinks.js +496 -0
- package/dist/layer2/ai-execution-sinks.js.map +1 -0
- package/dist/layer2/ai-fingerprinting.d.ts +7 -0
- package/dist/layer2/ai-fingerprinting.d.ts.map +1 -0
- package/dist/layer2/ai-fingerprinting.js +654 -0
- package/dist/layer2/ai-fingerprinting.js.map +1 -0
- package/dist/layer2/ai-prompt-hygiene.d.ts +19 -0
- package/dist/layer2/ai-prompt-hygiene.d.ts.map +1 -0
- package/dist/layer2/ai-prompt-hygiene.js +356 -0
- package/dist/layer2/ai-prompt-hygiene.js.map +1 -0
- package/dist/layer2/ai-rag-safety.d.ts +21 -0
- package/dist/layer2/ai-rag-safety.d.ts.map +1 -0
- package/dist/layer2/ai-rag-safety.js +459 -0
- package/dist/layer2/ai-rag-safety.js.map +1 -0
- package/dist/layer2/ai-schema-validation.d.ts +25 -0
- package/dist/layer2/ai-schema-validation.d.ts.map +1 -0
- package/dist/layer2/ai-schema-validation.js +375 -0
- package/dist/layer2/ai-schema-validation.js.map +1 -0
- package/dist/layer2/auth-antipatterns.d.ts +20 -0
- package/dist/layer2/auth-antipatterns.d.ts.map +1 -0
- package/dist/layer2/auth-antipatterns.js +333 -0
- package/dist/layer2/auth-antipatterns.js.map +1 -0
- package/dist/layer2/byok-patterns.d.ts +12 -0
- package/dist/layer2/byok-patterns.d.ts.map +1 -0
- package/dist/layer2/byok-patterns.js +299 -0
- package/dist/layer2/byok-patterns.js.map +1 -0
- package/dist/layer2/dangerous-functions.d.ts +7 -0
- package/dist/layer2/dangerous-functions.d.ts.map +1 -0
- package/dist/layer2/dangerous-functions.js +1375 -0
- package/dist/layer2/dangerous-functions.js.map +1 -0
- package/dist/layer2/data-exposure.d.ts +16 -0
- package/dist/layer2/data-exposure.d.ts.map +1 -0
- package/dist/layer2/data-exposure.js +279 -0
- package/dist/layer2/data-exposure.js.map +1 -0
- package/dist/layer2/framework-checks.d.ts +7 -0
- package/dist/layer2/framework-checks.d.ts.map +1 -0
- package/dist/layer2/framework-checks.js +388 -0
- package/dist/layer2/framework-checks.js.map +1 -0
- package/dist/layer2/index.d.ts +58 -0
- package/dist/layer2/index.d.ts.map +1 -0
- package/dist/layer2/index.js +380 -0
- package/dist/layer2/index.js.map +1 -0
- package/dist/layer2/logic-gates.d.ts +7 -0
- package/dist/layer2/logic-gates.d.ts.map +1 -0
- package/dist/layer2/logic-gates.js +182 -0
- package/dist/layer2/logic-gates.js.map +1 -0
- package/dist/layer2/risky-imports.d.ts +7 -0
- package/dist/layer2/risky-imports.d.ts.map +1 -0
- package/dist/layer2/risky-imports.js +161 -0
- package/dist/layer2/risky-imports.js.map +1 -0
- package/dist/layer2/variables.d.ts +8 -0
- package/dist/layer2/variables.d.ts.map +1 -0
- package/dist/layer2/variables.js +152 -0
- package/dist/layer2/variables.js.map +1 -0
- package/dist/layer3/anthropic.d.ts +83 -0
- package/dist/layer3/anthropic.d.ts.map +1 -0
- package/dist/layer3/anthropic.js +1745 -0
- package/dist/layer3/anthropic.js.map +1 -0
- package/dist/layer3/index.d.ts +24 -0
- package/dist/layer3/index.d.ts.map +1 -0
- package/dist/layer3/index.js +119 -0
- package/dist/layer3/index.js.map +1 -0
- package/dist/layer3/openai.d.ts +25 -0
- package/dist/layer3/openai.d.ts.map +1 -0
- package/dist/layer3/openai.js +238 -0
- package/dist/layer3/openai.js.map +1 -0
- package/dist/layer3/package-check.d.ts +63 -0
- package/dist/layer3/package-check.d.ts.map +1 -0
- package/dist/layer3/package-check.js +508 -0
- package/dist/layer3/package-check.js.map +1 -0
- package/dist/modes/incremental.d.ts +66 -0
- package/dist/modes/incremental.d.ts.map +1 -0
- package/dist/modes/incremental.js +200 -0
- package/dist/modes/incremental.js.map +1 -0
- package/dist/tiers.d.ts +125 -0
- package/dist/tiers.d.ts.map +1 -0
- package/dist/tiers.js +234 -0
- package/dist/tiers.js.map +1 -0
- package/dist/types.d.ts +175 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +50 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/auth-helper-detector.d.ts +56 -0
- package/dist/utils/auth-helper-detector.d.ts.map +1 -0
- package/dist/utils/auth-helper-detector.js +360 -0
- package/dist/utils/auth-helper-detector.js.map +1 -0
- package/dist/utils/context-helpers.d.ts +96 -0
- package/dist/utils/context-helpers.d.ts.map +1 -0
- package/dist/utils/context-helpers.js +493 -0
- package/dist/utils/context-helpers.js.map +1 -0
- package/dist/utils/diff-detector.d.ts +53 -0
- package/dist/utils/diff-detector.d.ts.map +1 -0
- package/dist/utils/diff-detector.js +104 -0
- package/dist/utils/diff-detector.js.map +1 -0
- package/dist/utils/diff-parser.d.ts +80 -0
- package/dist/utils/diff-parser.d.ts.map +1 -0
- package/dist/utils/diff-parser.js +202 -0
- package/dist/utils/diff-parser.js.map +1 -0
- package/dist/utils/imported-auth-detector.d.ts +37 -0
- package/dist/utils/imported-auth-detector.d.ts.map +1 -0
- package/dist/utils/imported-auth-detector.js +251 -0
- package/dist/utils/imported-auth-detector.js.map +1 -0
- package/dist/utils/middleware-detector.d.ts +55 -0
- package/dist/utils/middleware-detector.d.ts.map +1 -0
- package/dist/utils/middleware-detector.js +260 -0
- package/dist/utils/middleware-detector.js.map +1 -0
- package/dist/utils/oauth-flow-detector.d.ts +41 -0
- package/dist/utils/oauth-flow-detector.d.ts.map +1 -0
- package/dist/utils/oauth-flow-detector.js +202 -0
- package/dist/utils/oauth-flow-detector.js.map +1 -0
- package/dist/utils/path-exclusions.d.ts +55 -0
- package/dist/utils/path-exclusions.d.ts.map +1 -0
- package/dist/utils/path-exclusions.js +222 -0
- package/dist/utils/path-exclusions.js.map +1 -0
- package/dist/utils/project-context-builder.d.ts +119 -0
- package/dist/utils/project-context-builder.d.ts.map +1 -0
- package/dist/utils/project-context-builder.js +534 -0
- package/dist/utils/project-context-builder.js.map +1 -0
- package/dist/utils/registry-clients.d.ts +93 -0
- package/dist/utils/registry-clients.d.ts.map +1 -0
- package/dist/utils/registry-clients.js +273 -0
- package/dist/utils/registry-clients.js.map +1 -0
- package/dist/utils/trpc-analyzer.d.ts +78 -0
- package/dist/utils/trpc-analyzer.d.ts.map +1 -0
- package/dist/utils/trpc-analyzer.js +297 -0
- package/dist/utils/trpc-analyzer.js.map +1 -0
- package/package.json +45 -0
- package/src/__tests__/benchmark/fixtures/false-positives.ts +227 -0
- package/src/__tests__/benchmark/fixtures/index.ts +68 -0
- package/src/__tests__/benchmark/fixtures/layer1/config-audit.ts +364 -0
- package/src/__tests__/benchmark/fixtures/layer1/hardcoded-secrets.ts +173 -0
- package/src/__tests__/benchmark/fixtures/layer1/high-entropy.ts +234 -0
- package/src/__tests__/benchmark/fixtures/layer1/index.ts +31 -0
- package/src/__tests__/benchmark/fixtures/layer1/sensitive-urls.ts +90 -0
- package/src/__tests__/benchmark/fixtures/layer1/weak-crypto.ts +197 -0
- package/src/__tests__/benchmark/fixtures/layer2/ai-agent-tools.ts +170 -0
- package/src/__tests__/benchmark/fixtures/layer2/ai-endpoint-protection.ts +418 -0
- package/src/__tests__/benchmark/fixtures/layer2/ai-execution-sinks.ts +189 -0
- package/src/__tests__/benchmark/fixtures/layer2/ai-fingerprinting.ts +316 -0
- package/src/__tests__/benchmark/fixtures/layer2/ai-prompt-hygiene.ts +178 -0
- package/src/__tests__/benchmark/fixtures/layer2/ai-rag-safety.ts +184 -0
- package/src/__tests__/benchmark/fixtures/layer2/ai-schema-validation.ts +434 -0
- package/src/__tests__/benchmark/fixtures/layer2/auth-antipatterns.ts +159 -0
- package/src/__tests__/benchmark/fixtures/layer2/byok-patterns.ts +112 -0
- package/src/__tests__/benchmark/fixtures/layer2/dangerous-functions.ts +246 -0
- package/src/__tests__/benchmark/fixtures/layer2/data-exposure.ts +168 -0
- package/src/__tests__/benchmark/fixtures/layer2/framework-checks.ts +346 -0
- package/src/__tests__/benchmark/fixtures/layer2/index.ts +67 -0
- package/src/__tests__/benchmark/fixtures/layer2/injection-vulnerabilities.ts +239 -0
- package/src/__tests__/benchmark/fixtures/layer2/logic-gates.ts +246 -0
- package/src/__tests__/benchmark/fixtures/layer2/risky-imports.ts +231 -0
- package/src/__tests__/benchmark/fixtures/layer2/variables.ts +167 -0
- package/src/__tests__/benchmark/index.ts +29 -0
- package/src/__tests__/benchmark/run-benchmark.ts +144 -0
- package/src/__tests__/benchmark/run-depth-validation.ts +206 -0
- package/src/__tests__/benchmark/run-real-world-test.ts +243 -0
- package/src/__tests__/benchmark/security-benchmark-script.ts +1737 -0
- package/src/__tests__/benchmark/tier-integration-script.ts +177 -0
- package/src/__tests__/benchmark/types.ts +144 -0
- package/src/__tests__/benchmark/utils/test-runner.ts +475 -0
- package/src/__tests__/regression/known-false-positives.test.ts +467 -0
- package/src/__tests__/snapshots/__snapshots__/scan-depth.test.ts.snap +178 -0
- package/src/__tests__/snapshots/scan-depth.test.ts +258 -0
- package/src/__tests__/validation/analyze-results.ts +542 -0
- package/src/__tests__/validation/extract-for-triage.ts +146 -0
- package/src/__tests__/validation/fp-deep-analysis.ts +327 -0
- package/src/__tests__/validation/run-validation.ts +364 -0
- package/src/__tests__/validation/triage-template.md +132 -0
- package/src/formatters/cli-terminal.ts +446 -0
- package/src/formatters/github-comment.ts +382 -0
- package/src/formatters/grouping.ts +190 -0
- package/src/formatters/index.ts +47 -0
- package/src/formatters/vscode-diagnostic.ts +243 -0
- package/src/index.ts +823 -0
- package/src/layer1/comments.ts +218 -0
- package/src/layer1/config-audit.ts +289 -0
- package/src/layer1/entropy.ts +583 -0
- package/src/layer1/file-flags.ts +127 -0
- package/src/layer1/index.ts +181 -0
- package/src/layer1/patterns.ts +516 -0
- package/src/layer1/urls.ts +334 -0
- package/src/layer1/weak-crypto.ts +328 -0
- package/src/layer2/ai-agent-tools.ts +601 -0
- package/src/layer2/ai-endpoint-protection.ts +387 -0
- package/src/layer2/ai-execution-sinks.ts +580 -0
- package/src/layer2/ai-fingerprinting.ts +758 -0
- package/src/layer2/ai-prompt-hygiene.ts +411 -0
- package/src/layer2/ai-rag-safety.ts +511 -0
- package/src/layer2/ai-schema-validation.ts +421 -0
- package/src/layer2/auth-antipatterns.ts +394 -0
- package/src/layer2/byok-patterns.ts +336 -0
- package/src/layer2/dangerous-functions.ts +1563 -0
- package/src/layer2/data-exposure.ts +315 -0
- package/src/layer2/framework-checks.ts +433 -0
- package/src/layer2/index.ts +473 -0
- package/src/layer2/logic-gates.ts +206 -0
- package/src/layer2/risky-imports.ts +186 -0
- package/src/layer2/variables.ts +166 -0
- package/src/layer3/anthropic.ts +2030 -0
- package/src/layer3/index.ts +130 -0
- package/src/layer3/package-check.ts +604 -0
- package/src/modes/incremental.ts +293 -0
- package/src/tiers.ts +318 -0
- package/src/types.ts +284 -0
- package/src/utils/auth-helper-detector.ts +443 -0
- package/src/utils/context-helpers.ts +535 -0
- package/src/utils/diff-detector.ts +135 -0
- package/src/utils/diff-parser.ts +272 -0
- package/src/utils/imported-auth-detector.ts +320 -0
- package/src/utils/middleware-detector.ts +333 -0
- package/src/utils/oauth-flow-detector.ts +246 -0
- package/src/utils/path-exclusions.ts +266 -0
- package/src/utils/project-context-builder.ts +707 -0
- package/src/utils/registry-clients.ts +351 -0
- package/src/utils/trpc-analyzer.ts +382 -0
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Layer 2: Data Exposure Detection
|
|
3
|
+
* Identifies sensitive data in logs vs API responses with appropriate severity
|
|
4
|
+
* Separates "logging concerns" from "response exposure" which have different risk profiles
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { Vulnerability, VulnerabilitySeverity } from '../types'
|
|
8
|
+
import { isComment, isTestOrMockFile } from '../utils/context-helpers'
|
|
9
|
+
|
|
10
|
+
interface DataExposurePattern {
|
|
11
|
+
name: string
|
|
12
|
+
pattern: RegExp
|
|
13
|
+
sink: 'log' | 'response' | 'both'
|
|
14
|
+
severity: VulnerabilitySeverity
|
|
15
|
+
description: string
|
|
16
|
+
suggestedFix: string
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const DATA_EXPOSURE_PATTERNS: DataExposurePattern[] = [
|
|
20
|
+
// ============================================================================
|
|
21
|
+
// LOG SINKS (generally lower severity - server-side only)
|
|
22
|
+
// These are hygiene issues, not security vulnerabilities in most cases
|
|
23
|
+
// ============================================================================
|
|
24
|
+
|
|
25
|
+
// Logging sensitive variables - kept low/info
|
|
26
|
+
// NOTE: Generic "query" or "search" logging is NOT flagged - only actual sensitive data
|
|
27
|
+
{
|
|
28
|
+
name: 'Logging user ID',
|
|
29
|
+
pattern: /console\.(log|info|debug|warn|error)\s*\([^)]*\b(userId|user_id|user\.id)\b/gi,
|
|
30
|
+
sink: 'log',
|
|
31
|
+
severity: 'info',
|
|
32
|
+
description: 'User ID logged to console. Common debugging practice - verify logs are secured.',
|
|
33
|
+
suggestedFix: 'Use structured logging with appropriate access controls. Remove before production if not needed.',
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
name: 'Logging error objects',
|
|
37
|
+
pattern: /console\.(log|error|warn)\s*\(\s*(err|error|e)\s*\)/gi,
|
|
38
|
+
sink: 'log',
|
|
39
|
+
severity: 'info', // Downgraded from 'low' - this is standard practice
|
|
40
|
+
description: 'Error object logged to console. Standard debugging practice, but may expose stack traces in logs.',
|
|
41
|
+
suggestedFix: 'Consider logging only error.message in production. Use structured logging for better control.',
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
name: 'Logging request body',
|
|
45
|
+
pattern: /console\.(log|info|debug)\s*\([^)]*\b(req\.body|request\.body|body)\b/gi,
|
|
46
|
+
sink: 'log',
|
|
47
|
+
severity: 'low',
|
|
48
|
+
description: 'Request body logged to console. May contain sensitive user input.',
|
|
49
|
+
suggestedFix: 'Redact sensitive fields (passwords, tokens) before logging.',
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
name: 'JSON.stringify error to log',
|
|
53
|
+
pattern: /console\.(log|error|warn)\s*\([^)]*JSON\.stringify\s*\(\s*(err|error|e)\s*\)/gi,
|
|
54
|
+
sink: 'log',
|
|
55
|
+
severity: 'info',
|
|
56
|
+
description: 'Error object serialized to JSON for logging. May include stack traces.',
|
|
57
|
+
suggestedFix: 'Consider logging specific error properties instead of the full serialized object.',
|
|
58
|
+
},
|
|
59
|
+
|
|
60
|
+
// ============================================================================
|
|
61
|
+
// RESPONSE SINKS (higher severity - exposed to clients)
|
|
62
|
+
// These are actual information disclosure risks
|
|
63
|
+
// ============================================================================
|
|
64
|
+
|
|
65
|
+
// Error stack traces in responses - CRITICAL
|
|
66
|
+
{
|
|
67
|
+
name: 'Stack trace in response',
|
|
68
|
+
pattern: /res\.(json|send|status\(\d+\)\.json)\s*\([^)]*\.stack|NextResponse\.json\s*\([^)]*\.stack/gi,
|
|
69
|
+
sink: 'response',
|
|
70
|
+
severity: 'high',
|
|
71
|
+
description: 'Stack trace exposed in API response. Reveals internal code paths and file structure to clients.',
|
|
72
|
+
suggestedFix: 'Never return stack traces to clients. Log server-side, return generic error message.',
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
name: 'Full error object in response',
|
|
76
|
+
pattern: /res\.(json|send)\s*\(\s*(err|error|e)\s*\)|NextResponse\.json\s*\(\s*(err|error|e)\s*\)/gi,
|
|
77
|
+
sink: 'response',
|
|
78
|
+
severity: 'high',
|
|
79
|
+
description: 'Entire error object returned to client. May expose stack traces, internal paths, and sensitive details.',
|
|
80
|
+
suggestedFix: 'Return only { error: message } or a structured error response. Never return raw error objects.',
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
name: 'Error object spread in response',
|
|
84
|
+
pattern: /res\.(json|send)\s*\(\s*\{[^}]*\.\.\.\s*(err|error|e)[^}]*\}|NextResponse\.json\s*\(\s*\{[^}]*\.\.\.\s*(err|error|e)/gi,
|
|
85
|
+
sink: 'response',
|
|
86
|
+
severity: 'high',
|
|
87
|
+
description: 'Error object spread into response. May expose stack traces and internal details.',
|
|
88
|
+
suggestedFix: 'Pick only safe properties: { error: err.message, code: err.code }',
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
name: 'Detailed error in response',
|
|
92
|
+
pattern: /res\.(json|send|status\(\d+\)\.json)\s*\(\s*\{[^}]*(details|internal|debug|trace|stack)/gi,
|
|
93
|
+
sink: 'response',
|
|
94
|
+
severity: 'medium',
|
|
95
|
+
description: 'Detailed/internal error information in response may leak implementation details.',
|
|
96
|
+
suggestedFix: 'Remove detailed/internal/debug/trace information from client-facing error responses.',
|
|
97
|
+
},
|
|
98
|
+
|
|
99
|
+
// Error message in response - SAFE PATTERN (info only)
|
|
100
|
+
{
|
|
101
|
+
name: 'Error message in response (safe pattern)',
|
|
102
|
+
pattern: /res\.(json|send)\s*\([^)]*error:\s*(error|err|e)\.message|NextResponse\.json\s*\([^)]*error:\s*(error|err|e)\.message/gi,
|
|
103
|
+
sink: 'response',
|
|
104
|
+
severity: 'info',
|
|
105
|
+
description: 'Error message returned to client. Generally safe - this is the recommended error response pattern.',
|
|
106
|
+
suggestedFix: 'Verify error messages don\'t contain sensitive data. Consider using generic messages for auth errors.',
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
name: 'Error message with toString',
|
|
110
|
+
pattern: /res\.(json|send)\s*\([^)]*error:\s*(error|err|e)\.toString|NextResponse\.json\s*\([^)]*error:\s*(error|err|e)\.toString/gi,
|
|
111
|
+
sink: 'response',
|
|
112
|
+
severity: 'low',
|
|
113
|
+
description: 'Error.toString() returned to client. May include error name and message - verify no sensitive data.',
|
|
114
|
+
suggestedFix: 'Prefer error.message over error.toString() for cleaner output.',
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
name: 'String error message in response',
|
|
118
|
+
pattern: /res\.(json|send|status\(\d+\)\.json)\s*\(\s*\{\s*(error|message):\s*['"`][^'"`]+['"`]\s*\}/gi,
|
|
119
|
+
sink: 'response',
|
|
120
|
+
severity: 'info',
|
|
121
|
+
description: 'Static/string error message returned to client. This is the safest error response pattern.',
|
|
122
|
+
suggestedFix: 'No action needed - static error messages are safe.',
|
|
123
|
+
},
|
|
124
|
+
|
|
125
|
+
// ============================================================================
|
|
126
|
+
// BOTH SINKS (depends on context)
|
|
127
|
+
// ============================================================================
|
|
128
|
+
|
|
129
|
+
// Sensitive data exposure patterns
|
|
130
|
+
{
|
|
131
|
+
name: 'Exposing user object',
|
|
132
|
+
pattern: /res\.(json|send)\s*\([^)]*user\s*\)|NextResponse\.json\s*\([^)]*user\s*\)/gi,
|
|
133
|
+
sink: 'response',
|
|
134
|
+
severity: 'low',
|
|
135
|
+
description: 'User object returned in response. Ensure password hashes and sensitive fields are excluded.',
|
|
136
|
+
suggestedFix: 'Select only necessary user fields. Never expose password hashes, tokens, or internal IDs.',
|
|
137
|
+
},
|
|
138
|
+
]
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Check if file path indicates low-risk logging context
|
|
142
|
+
*/
|
|
143
|
+
function isLowRiskLoggingFile(filePath: string): boolean {
|
|
144
|
+
// Test files
|
|
145
|
+
if (isTestOrMockFile(filePath)) {
|
|
146
|
+
return true
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Scripts, tools, CLI utilities
|
|
150
|
+
if (/\/(scripts?|tools?|cli|bin)\//i.test(filePath)) {
|
|
151
|
+
return true
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Internal services/utilities (not API-facing)
|
|
155
|
+
if (/\/(services?|lib|utils?|helpers?)\//i.test(filePath) &&
|
|
156
|
+
!/\/(api|routes?)\//i.test(filePath)) {
|
|
157
|
+
return true
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Component files (client-side, not API)
|
|
161
|
+
if (/\/(components?|pages?|views?)\//i.test(filePath) &&
|
|
162
|
+
!/route\.(ts|js)$/i.test(filePath)) {
|
|
163
|
+
return true
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
return false
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Detect sensitive data exposure in logs and API responses
|
|
171
|
+
*/
|
|
172
|
+
export function detectDataExposure(
|
|
173
|
+
content: string,
|
|
174
|
+
filePath: string
|
|
175
|
+
): Vulnerability[] {
|
|
176
|
+
const vulnerabilities: Vulnerability[] = []
|
|
177
|
+
const lines = content.split('\n')
|
|
178
|
+
const isTestFile = isTestOrMockFile(filePath)
|
|
179
|
+
const isLowRiskFile = isLowRiskLoggingFile(filePath)
|
|
180
|
+
|
|
181
|
+
// Determine if this is likely an API route file
|
|
182
|
+
const isApiFile = /\/(api|routes?|handlers?|controllers?)\//i.test(filePath) ||
|
|
183
|
+
/route\.(ts|js)$/i.test(filePath)
|
|
184
|
+
|
|
185
|
+
// Track log findings for aggregation
|
|
186
|
+
const logFindings: { lineNumber: number; lineContent: string; name: string }[] = []
|
|
187
|
+
|
|
188
|
+
lines.forEach((line, index) => {
|
|
189
|
+
// Skip comments
|
|
190
|
+
if (isComment(line)) return
|
|
191
|
+
|
|
192
|
+
for (const pattern of DATA_EXPOSURE_PATTERNS) {
|
|
193
|
+
const regex = new RegExp(pattern.pattern.source, pattern.pattern.flags)
|
|
194
|
+
|
|
195
|
+
if (regex.test(line)) {
|
|
196
|
+
let severity = pattern.severity
|
|
197
|
+
let description = pattern.description
|
|
198
|
+
|
|
199
|
+
// Adjust severity based on context
|
|
200
|
+
if (isTestFile) {
|
|
201
|
+
severity = 'info'
|
|
202
|
+
description = `${description} (in test file)`
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// Log sinks get special handling for aggregation
|
|
206
|
+
if (pattern.sink === 'log') {
|
|
207
|
+
// In low-risk files, just aggregate without reporting individual findings
|
|
208
|
+
if (isLowRiskFile && severity === 'info') {
|
|
209
|
+
logFindings.push({ lineNumber: index + 1, lineContent: line.trim(), name: pattern.name })
|
|
210
|
+
break
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// Log sinks in non-API files are lower priority
|
|
214
|
+
if (!isApiFile) {
|
|
215
|
+
if (severity === 'low') severity = 'info'
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// Track for aggregation if info severity
|
|
219
|
+
if (severity === 'info') {
|
|
220
|
+
logFindings.push({ lineNumber: index + 1, lineContent: line.trim(), name: pattern.name })
|
|
221
|
+
break
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// Response sinks in API files are higher priority
|
|
226
|
+
if (pattern.sink === 'response' && isApiFile) {
|
|
227
|
+
// Keep original severity - these are more critical in API routes
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
vulnerabilities.push({
|
|
231
|
+
id: `data-exposure-${filePath}-${index + 1}-${pattern.name}`,
|
|
232
|
+
filePath,
|
|
233
|
+
lineNumber: index + 1,
|
|
234
|
+
lineContent: line.trim(),
|
|
235
|
+
severity,
|
|
236
|
+
category: 'data_exposure',
|
|
237
|
+
title: pattern.name,
|
|
238
|
+
description,
|
|
239
|
+
suggestedFix: pattern.suggestedFix,
|
|
240
|
+
confidence: isTestFile ? 'low' : 'medium',
|
|
241
|
+
layer: 2,
|
|
242
|
+
})
|
|
243
|
+
break // Only one finding per line
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
})
|
|
247
|
+
|
|
248
|
+
// Aggregate info-level log findings if there are many
|
|
249
|
+
if (logFindings.length >= 3) {
|
|
250
|
+
const lineNumbers = logFindings.map(f => f.lineNumber).slice(0, 5)
|
|
251
|
+
const moreText = logFindings.length > 5 ? `... (${logFindings.length} total)` : ''
|
|
252
|
+
|
|
253
|
+
// Group by pattern name
|
|
254
|
+
const patternCounts = new Map<string, number>()
|
|
255
|
+
for (const finding of logFindings) {
|
|
256
|
+
patternCounts.set(finding.name, (patternCounts.get(finding.name) || 0) + 1)
|
|
257
|
+
}
|
|
258
|
+
const patternSummary = Array.from(patternCounts.entries())
|
|
259
|
+
.map(([name, count]) => `${count}x ${name}`)
|
|
260
|
+
.join(', ')
|
|
261
|
+
|
|
262
|
+
vulnerabilities.push({
|
|
263
|
+
id: `data-exposure-aggregated-${filePath}`,
|
|
264
|
+
filePath,
|
|
265
|
+
lineNumber: logFindings[0].lineNumber,
|
|
266
|
+
lineContent: `${logFindings.length} instances across this file`,
|
|
267
|
+
severity: 'info',
|
|
268
|
+
category: 'data_exposure',
|
|
269
|
+
title: `Logging patterns (${logFindings.length} instances)`,
|
|
270
|
+
description: `${patternSummary}. Review for sensitive data exposure.\n\nFound ${logFindings.length} occurrences at lines: ${lineNumbers.join(', ')}${moreText}`,
|
|
271
|
+
suggestedFix: 'Ensure logs have appropriate access controls and do not contain sensitive user data.',
|
|
272
|
+
confidence: 'low',
|
|
273
|
+
layer: 2,
|
|
274
|
+
})
|
|
275
|
+
} else if (logFindings.length > 0) {
|
|
276
|
+
// Report individually for small counts
|
|
277
|
+
for (const finding of logFindings) {
|
|
278
|
+
const pattern = DATA_EXPOSURE_PATTERNS.find(p => p.name === finding.name)
|
|
279
|
+
if (pattern) {
|
|
280
|
+
vulnerabilities.push({
|
|
281
|
+
id: `data-exposure-${filePath}-${finding.lineNumber}-${finding.name}`,
|
|
282
|
+
filePath,
|
|
283
|
+
lineNumber: finding.lineNumber,
|
|
284
|
+
lineContent: finding.lineContent,
|
|
285
|
+
severity: 'info',
|
|
286
|
+
category: 'data_exposure',
|
|
287
|
+
title: pattern.name,
|
|
288
|
+
description: pattern.description,
|
|
289
|
+
suggestedFix: pattern.suggestedFix,
|
|
290
|
+
confidence: 'low',
|
|
291
|
+
layer: 2,
|
|
292
|
+
})
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
return vulnerabilities
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* Check if error handling follows safe patterns
|
|
302
|
+
* Returns true if the error handling appears safe
|
|
303
|
+
*/
|
|
304
|
+
export function isSafeErrorHandling(lineContent: string, surroundingLines: string[]): boolean {
|
|
305
|
+
// Safe patterns: only returning error.message, using generic messages
|
|
306
|
+
const safePatterns = [
|
|
307
|
+
/error:\s*['"`].*['"`]/, // Generic string message
|
|
308
|
+
/error:\s*error\.message/, // Only message property
|
|
309
|
+
/error:\s*err\.message/,
|
|
310
|
+
/message:\s*(error|err)\.message/,
|
|
311
|
+
/status\(\d+\)\.json\(\s*\{\s*error:/, // Status code + error object (common pattern)
|
|
312
|
+
]
|
|
313
|
+
|
|
314
|
+
return safePatterns.some(p => p.test(lineContent))
|
|
315
|
+
}
|