@oculum/scanner 1.0.10 → 1.0.12
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/ai-context/index.d.ts +6 -0
- package/dist/ai-context/index.d.ts.map +1 -0
- package/dist/ai-context/index.js +13 -0
- package/dist/ai-context/index.js.map +1 -0
- package/dist/ai-context/manager.d.ts +67 -0
- package/dist/ai-context/manager.d.ts.map +1 -0
- package/dist/ai-context/manager.js +104 -0
- package/dist/ai-context/manager.js.map +1 -0
- package/dist/baseline/diff.d.ts +32 -0
- package/dist/baseline/diff.d.ts.map +1 -0
- package/dist/baseline/diff.js +119 -0
- package/dist/baseline/diff.js.map +1 -0
- package/dist/baseline/index.d.ts +9 -0
- package/dist/baseline/index.d.ts.map +1 -0
- package/dist/baseline/index.js +19 -0
- package/dist/baseline/index.js.map +1 -0
- package/dist/baseline/manager.d.ts +67 -0
- package/dist/baseline/manager.d.ts.map +1 -0
- package/dist/baseline/manager.js +180 -0
- package/dist/baseline/manager.js.map +1 -0
- package/dist/baseline/types.d.ts +91 -0
- package/dist/baseline/types.d.ts.map +1 -0
- package/dist/baseline/types.js +12 -0
- package/dist/baseline/types.js.map +1 -0
- package/dist/category-filter.d.ts +125 -0
- package/dist/category-filter.d.ts.map +1 -0
- package/dist/category-filter.js +360 -0
- package/dist/category-filter.js.map +1 -0
- package/dist/filtering/context-adjustments.d.ts +23 -0
- package/dist/filtering/context-adjustments.d.ts.map +1 -0
- package/dist/filtering/context-adjustments.js +100 -0
- package/dist/filtering/context-adjustments.js.map +1 -0
- package/dist/filtering/index.d.ts +3 -0
- package/dist/filtering/index.d.ts.map +1 -0
- package/dist/filtering/index.js +8 -0
- package/dist/filtering/index.js.map +1 -0
- package/dist/filtering/pipeline.d.ts +48 -0
- package/dist/filtering/pipeline.d.ts.map +1 -0
- package/dist/filtering/pipeline.js +76 -0
- package/dist/filtering/pipeline.js.map +1 -0
- package/dist/formatters/ai-context.d.ts +23 -0
- package/dist/formatters/ai-context.d.ts.map +1 -0
- package/dist/formatters/ai-context.js +238 -0
- package/dist/formatters/ai-context.js.map +1 -0
- package/dist/formatters/cli-terminal.d.ts +38 -0
- package/dist/formatters/cli-terminal.d.ts.map +1 -1
- package/dist/formatters/cli-terminal.js +365 -42
- package/dist/formatters/cli-terminal.js.map +1 -1
- package/dist/formatters/github-comment.d.ts +2 -2
- package/dist/formatters/github-comment.d.ts.map +1 -1
- package/dist/formatters/github-comment.js +77 -13
- package/dist/formatters/github-comment.js.map +1 -1
- package/dist/formatters/ide/claude-code.d.ts +17 -0
- package/dist/formatters/ide/claude-code.d.ts.map +1 -0
- package/dist/formatters/ide/claude-code.js +94 -0
- package/dist/formatters/ide/claude-code.js.map +1 -0
- package/dist/formatters/ide/cursor.d.ts +13 -0
- package/dist/formatters/ide/cursor.d.ts.map +1 -0
- package/dist/formatters/ide/cursor.js +125 -0
- package/dist/formatters/ide/cursor.js.map +1 -0
- package/dist/formatters/ide/index.d.ts +62 -0
- package/dist/formatters/ide/index.d.ts.map +1 -0
- package/dist/formatters/ide/index.js +184 -0
- package/dist/formatters/ide/index.js.map +1 -0
- package/dist/formatters/ide/windsurf.d.ts +13 -0
- package/dist/formatters/ide/windsurf.d.ts.map +1 -0
- package/dist/formatters/ide/windsurf.js +117 -0
- package/dist/formatters/ide/windsurf.js.map +1 -0
- package/dist/formatters/index.d.ts +3 -1
- package/dist/formatters/index.d.ts.map +1 -1
- package/dist/formatters/index.js +20 -1
- package/dist/formatters/index.js.map +1 -1
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +423 -56
- package/dist/index.js.map +1 -1
- package/dist/layer1/comments.d.ts +4 -1
- package/dist/layer1/comments.d.ts.map +1 -1
- package/dist/layer1/comments.js +1 -1
- package/dist/layer1/comments.js.map +1 -1
- package/dist/layer1/config-audit.d.ts +4 -1
- package/dist/layer1/config-audit.d.ts.map +1 -1
- package/dist/layer1/config-audit.js +65 -14
- package/dist/layer1/config-audit.js.map +1 -1
- package/dist/layer1/config-mcp-audit.d.ts +23 -0
- package/dist/layer1/config-mcp-audit.d.ts.map +1 -0
- package/dist/layer1/config-mcp-audit.js +239 -0
- package/dist/layer1/config-mcp-audit.js.map +1 -0
- package/dist/layer1/entropy.d.ts +4 -1
- package/dist/layer1/entropy.d.ts.map +1 -1
- package/dist/layer1/entropy.js +212 -1
- package/dist/layer1/entropy.js.map +1 -1
- package/dist/layer1/file-flags.d.ts +4 -1
- package/dist/layer1/file-flags.d.ts.map +1 -1
- package/dist/layer1/file-flags.js +12 -5
- package/dist/layer1/file-flags.js.map +1 -1
- package/dist/layer1/index.d.ts +1 -0
- package/dist/layer1/index.d.ts.map +1 -1
- package/dist/layer1/index.js +22 -19
- package/dist/layer1/index.js.map +1 -1
- package/dist/layer1/patterns.d.ts +4 -1
- package/dist/layer1/patterns.d.ts.map +1 -1
- package/dist/layer1/patterns.js +34 -4
- package/dist/layer1/patterns.js.map +1 -1
- package/dist/layer1/urls.d.ts +4 -1
- package/dist/layer1/urls.d.ts.map +1 -1
- package/dist/layer1/urls.js +162 -14
- package/dist/layer1/urls.js.map +1 -1
- package/dist/layer1/weak-crypto.d.ts +4 -1
- package/dist/layer1/weak-crypto.d.ts.map +1 -1
- package/dist/layer1/weak-crypto.js +144 -7
- package/dist/layer1/weak-crypto.js.map +1 -1
- package/dist/layer2/ai-agent-tools.d.ts +4 -1
- package/dist/layer2/ai-agent-tools.d.ts.map +1 -1
- package/dist/layer2/ai-agent-tools.js +964 -2
- package/dist/layer2/ai-agent-tools.js.map +1 -1
- package/dist/layer2/ai-endpoint-protection.d.ts +2 -0
- package/dist/layer2/ai-endpoint-protection.d.ts.map +1 -1
- package/dist/layer2/ai-endpoint-protection.js +18 -4
- package/dist/layer2/ai-endpoint-protection.js.map +1 -1
- package/dist/layer2/ai-execution-sinks.d.ts +4 -1
- package/dist/layer2/ai-execution-sinks.d.ts.map +1 -1
- package/dist/layer2/ai-execution-sinks.js +688 -29
- package/dist/layer2/ai-execution-sinks.js.map +1 -1
- package/dist/layer2/ai-fingerprinting.d.ts +4 -1
- package/dist/layer2/ai-fingerprinting.d.ts.map +1 -1
- package/dist/layer2/ai-fingerprinting.js +28 -32
- package/dist/layer2/ai-fingerprinting.js.map +1 -1
- package/dist/layer2/ai-mcp-security.d.ts +20 -0
- package/dist/layer2/ai-mcp-security.d.ts.map +1 -0
- package/dist/layer2/ai-mcp-security.js +877 -0
- package/dist/layer2/ai-mcp-security.js.map +1 -0
- package/dist/layer2/ai-package-hallucination.d.ts +22 -0
- package/dist/layer2/ai-package-hallucination.d.ts.map +1 -0
- package/dist/layer2/ai-package-hallucination.js +828 -0
- package/dist/layer2/ai-package-hallucination.js.map +1 -0
- package/dist/layer2/ai-prompt-hygiene.d.ts +4 -1
- package/dist/layer2/ai-prompt-hygiene.d.ts.map +1 -1
- package/dist/layer2/ai-prompt-hygiene.js +817 -17
- package/dist/layer2/ai-prompt-hygiene.js.map +1 -1
- package/dist/layer2/ai-rag-safety.d.ts +4 -1
- package/dist/layer2/ai-rag-safety.d.ts.map +1 -1
- package/dist/layer2/ai-rag-safety.js +454 -3
- package/dist/layer2/ai-rag-safety.js.map +1 -1
- package/dist/layer2/ai-schema-validation.d.ts +4 -1
- package/dist/layer2/ai-schema-validation.d.ts.map +1 -1
- package/dist/layer2/ai-schema-validation.js +2 -2
- package/dist/layer2/ai-schema-validation.js.map +1 -1
- package/dist/layer2/auth-antipatterns.d.ts +2 -0
- package/dist/layer2/auth-antipatterns.d.ts.map +1 -1
- package/dist/layer2/auth-antipatterns.js +209 -20
- package/dist/layer2/auth-antipatterns.js.map +1 -1
- package/dist/layer2/byok-patterns.d.ts +4 -1
- package/dist/layer2/byok-patterns.d.ts.map +1 -1
- package/dist/layer2/byok-patterns.js +5 -2
- package/dist/layer2/byok-patterns.js.map +1 -1
- package/dist/layer2/dangerous-functions/child-process.d.ts +16 -0
- package/dist/layer2/dangerous-functions/child-process.d.ts.map +1 -0
- package/dist/layer2/dangerous-functions/child-process.js +74 -0
- package/dist/layer2/dangerous-functions/child-process.js.map +1 -0
- package/dist/layer2/dangerous-functions/dom-xss.d.ts +34 -0
- package/dist/layer2/dangerous-functions/dom-xss.d.ts.map +1 -0
- package/dist/layer2/dangerous-functions/dom-xss.js +230 -0
- package/dist/layer2/dangerous-functions/dom-xss.js.map +1 -0
- package/dist/layer2/dangerous-functions/index.d.ts +16 -0
- package/dist/layer2/dangerous-functions/index.d.ts.map +1 -0
- package/dist/layer2/dangerous-functions/index.js +1152 -0
- package/dist/layer2/dangerous-functions/index.js.map +1 -0
- package/dist/layer2/dangerous-functions/json-parse.d.ts +31 -0
- package/dist/layer2/dangerous-functions/json-parse.d.ts.map +1 -0
- package/dist/layer2/dangerous-functions/json-parse.js +319 -0
- package/dist/layer2/dangerous-functions/json-parse.js.map +1 -0
- package/dist/layer2/dangerous-functions/math-random.d.ts +111 -0
- package/dist/layer2/dangerous-functions/math-random.d.ts.map +1 -0
- package/dist/layer2/dangerous-functions/math-random.js +684 -0
- package/dist/layer2/dangerous-functions/math-random.js.map +1 -0
- package/dist/layer2/dangerous-functions/patterns.d.ts +21 -0
- package/dist/layer2/dangerous-functions/patterns.d.ts.map +1 -0
- package/dist/layer2/dangerous-functions/patterns.js +163 -0
- package/dist/layer2/dangerous-functions/patterns.js.map +1 -0
- package/dist/layer2/dangerous-functions/request-validation.d.ts +13 -0
- package/dist/layer2/dangerous-functions/request-validation.d.ts.map +1 -0
- package/dist/layer2/dangerous-functions/request-validation.js +119 -0
- package/dist/layer2/dangerous-functions/request-validation.js.map +1 -0
- package/dist/layer2/dangerous-functions/utils/control-flow.d.ts +24 -0
- package/dist/layer2/dangerous-functions/utils/control-flow.d.ts.map +1 -0
- package/dist/layer2/dangerous-functions/utils/control-flow.js +70 -0
- package/dist/layer2/dangerous-functions/utils/control-flow.js.map +1 -0
- package/dist/layer2/dangerous-functions/utils/helpers.d.ts +31 -0
- package/dist/layer2/dangerous-functions/utils/helpers.d.ts.map +1 -0
- package/dist/layer2/dangerous-functions/utils/helpers.js +147 -0
- package/dist/layer2/dangerous-functions/utils/helpers.js.map +1 -0
- package/dist/layer2/dangerous-functions/utils/index.d.ts +9 -0
- package/dist/layer2/dangerous-functions/utils/index.d.ts.map +1 -0
- package/dist/layer2/dangerous-functions/utils/index.js +23 -0
- package/dist/layer2/dangerous-functions/utils/index.js.map +1 -0
- package/dist/layer2/dangerous-functions/utils/schema-validation.d.ts +22 -0
- package/dist/layer2/dangerous-functions/utils/schema-validation.d.ts.map +1 -0
- package/dist/layer2/dangerous-functions/utils/schema-validation.js +102 -0
- package/dist/layer2/dangerous-functions/utils/schema-validation.js.map +1 -0
- package/dist/layer2/data-exposure.d.ts +4 -1
- package/dist/layer2/data-exposure.d.ts.map +1 -1
- package/dist/layer2/data-exposure.js +14 -38
- package/dist/layer2/data-exposure.js.map +1 -1
- package/dist/layer2/framework-checks.d.ts +4 -1
- package/dist/layer2/framework-checks.d.ts.map +1 -1
- package/dist/layer2/framework-checks.js +5 -2
- package/dist/layer2/framework-checks.js.map +1 -1
- package/dist/layer2/index.d.ts +12 -1
- package/dist/layer2/index.d.ts.map +1 -1
- package/dist/layer2/index.js +110 -45
- package/dist/layer2/index.js.map +1 -1
- package/dist/layer2/logic-gates.d.ts +4 -1
- package/dist/layer2/logic-gates.d.ts.map +1 -1
- package/dist/layer2/logic-gates.js +58 -20
- package/dist/layer2/logic-gates.js.map +1 -1
- package/dist/layer2/model-supply-chain.d.ts +23 -0
- package/dist/layer2/model-supply-chain.d.ts.map +1 -0
- package/dist/layer2/model-supply-chain.js +444 -0
- package/dist/layer2/model-supply-chain.js.map +1 -0
- package/dist/layer2/risky-imports.d.ts +4 -1
- package/dist/layer2/risky-imports.d.ts.map +1 -1
- package/dist/layer2/risky-imports.js +6 -2
- package/dist/layer2/risky-imports.js.map +1 -1
- package/dist/layer2/variables.d.ts +4 -1
- package/dist/layer2/variables.d.ts.map +1 -1
- package/dist/layer2/variables.js +6 -2
- package/dist/layer2/variables.js.map +1 -1
- package/dist/layer3/anthropic/auto-dismiss.d.ts +24 -0
- package/dist/layer3/anthropic/auto-dismiss.d.ts.map +1 -0
- package/dist/layer3/anthropic/auto-dismiss.js +199 -0
- package/dist/layer3/anthropic/auto-dismiss.js.map +1 -0
- package/dist/layer3/anthropic/clients.d.ts +44 -0
- package/dist/layer3/anthropic/clients.d.ts.map +1 -0
- package/dist/layer3/anthropic/clients.js +81 -0
- package/dist/layer3/anthropic/clients.js.map +1 -0
- package/dist/layer3/anthropic/index.d.ts +41 -0
- package/dist/layer3/anthropic/index.d.ts.map +1 -0
- package/dist/layer3/anthropic/index.js +141 -0
- package/dist/layer3/anthropic/index.js.map +1 -0
- package/dist/layer3/anthropic/prompts/index.d.ts +8 -0
- package/dist/layer3/anthropic/prompts/index.d.ts.map +1 -0
- package/dist/layer3/anthropic/prompts/index.js +14 -0
- package/dist/layer3/anthropic/prompts/index.js.map +1 -0
- package/dist/layer3/anthropic/prompts/semantic-analysis.d.ts +15 -0
- package/dist/layer3/anthropic/prompts/semantic-analysis.d.ts.map +1 -0
- package/dist/layer3/anthropic/prompts/semantic-analysis.js +169 -0
- package/dist/layer3/anthropic/prompts/semantic-analysis.js.map +1 -0
- package/dist/layer3/anthropic/prompts/validation.d.ts +12 -0
- package/dist/layer3/anthropic/prompts/validation.d.ts.map +1 -0
- package/dist/layer3/anthropic/prompts/validation.js +421 -0
- package/dist/layer3/anthropic/prompts/validation.js.map +1 -0
- package/dist/layer3/anthropic/providers/anthropic.d.ts +21 -0
- package/dist/layer3/anthropic/providers/anthropic.d.ts.map +1 -0
- package/dist/layer3/anthropic/providers/anthropic.js +266 -0
- package/dist/layer3/anthropic/providers/anthropic.js.map +1 -0
- package/dist/layer3/anthropic/providers/index.d.ts +8 -0
- package/dist/layer3/anthropic/providers/index.d.ts.map +1 -0
- package/dist/layer3/anthropic/providers/index.js +15 -0
- package/dist/layer3/anthropic/providers/index.js.map +1 -0
- package/dist/layer3/anthropic/providers/openai.d.ts +18 -0
- package/dist/layer3/anthropic/providers/openai.d.ts.map +1 -0
- package/dist/layer3/anthropic/providers/openai.js +340 -0
- package/dist/layer3/anthropic/providers/openai.js.map +1 -0
- package/dist/layer3/anthropic/request-builder.d.ts +20 -0
- package/dist/layer3/anthropic/request-builder.d.ts.map +1 -0
- package/dist/layer3/anthropic/request-builder.js +134 -0
- package/dist/layer3/anthropic/request-builder.js.map +1 -0
- package/dist/layer3/anthropic/types.d.ts +88 -0
- package/dist/layer3/anthropic/types.d.ts.map +1 -0
- package/dist/layer3/anthropic/types.js +38 -0
- package/dist/layer3/anthropic/types.js.map +1 -0
- package/dist/layer3/anthropic/utils/index.d.ts +9 -0
- package/dist/layer3/anthropic/utils/index.d.ts.map +1 -0
- package/dist/layer3/anthropic/utils/index.js +24 -0
- package/dist/layer3/anthropic/utils/index.js.map +1 -0
- package/dist/layer3/anthropic/utils/path-helpers.d.ts +21 -0
- package/dist/layer3/anthropic/utils/path-helpers.d.ts.map +1 -0
- package/dist/layer3/anthropic/utils/path-helpers.js +69 -0
- package/dist/layer3/anthropic/utils/path-helpers.js.map +1 -0
- package/dist/layer3/anthropic/utils/response-parser.d.ts +40 -0
- package/dist/layer3/anthropic/utils/response-parser.d.ts.map +1 -0
- package/dist/layer3/anthropic/utils/response-parser.js +285 -0
- package/dist/layer3/anthropic/utils/response-parser.js.map +1 -0
- package/dist/layer3/anthropic/utils/retry.d.ts +15 -0
- package/dist/layer3/anthropic/utils/retry.d.ts.map +1 -0
- package/dist/layer3/anthropic/utils/retry.js +62 -0
- package/dist/layer3/anthropic/utils/retry.js.map +1 -0
- package/dist/layer3/index.d.ts +1 -0
- package/dist/layer3/index.d.ts.map +1 -1
- package/dist/layer3/index.js +16 -6
- package/dist/layer3/index.js.map +1 -1
- package/dist/layer3/osv-check.d.ts +75 -0
- package/dist/layer3/osv-check.d.ts.map +1 -0
- package/dist/layer3/osv-check.js +308 -0
- package/dist/layer3/osv-check.js.map +1 -0
- package/dist/modes/incremental.js +1 -1
- package/dist/rules/framework-fixes.d.ts +48 -0
- package/dist/rules/framework-fixes.d.ts.map +1 -0
- package/dist/rules/framework-fixes.js +439 -0
- package/dist/rules/framework-fixes.js.map +1 -0
- package/dist/rules/index.d.ts +8 -0
- package/dist/rules/index.d.ts.map +1 -0
- package/dist/rules/index.js +18 -0
- package/dist/rules/index.js.map +1 -0
- package/dist/rules/metadata.d.ts +43 -0
- package/dist/rules/metadata.d.ts.map +1 -0
- package/dist/rules/metadata.js +734 -0
- package/dist/rules/metadata.js.map +1 -0
- package/dist/suppression/config-loader.d.ts +74 -0
- package/dist/suppression/config-loader.d.ts.map +1 -0
- package/dist/suppression/config-loader.js +424 -0
- package/dist/suppression/config-loader.js.map +1 -0
- package/dist/suppression/hash.d.ts +48 -0
- package/dist/suppression/hash.d.ts.map +1 -0
- package/dist/suppression/hash.js +88 -0
- package/dist/suppression/hash.js.map +1 -0
- package/dist/suppression/index.d.ts +11 -0
- package/dist/suppression/index.d.ts.map +1 -0
- package/dist/suppression/index.js +39 -0
- package/dist/suppression/index.js.map +1 -0
- package/dist/suppression/inline-parser.d.ts +39 -0
- package/dist/suppression/inline-parser.d.ts.map +1 -0
- package/dist/suppression/inline-parser.js +218 -0
- package/dist/suppression/inline-parser.js.map +1 -0
- package/dist/suppression/manager.d.ts +94 -0
- package/dist/suppression/manager.d.ts.map +1 -0
- package/dist/suppression/manager.js +292 -0
- package/dist/suppression/manager.js.map +1 -0
- package/dist/suppression/types.d.ts +151 -0
- package/dist/suppression/types.d.ts.map +1 -0
- package/dist/suppression/types.js +28 -0
- package/dist/suppression/types.js.map +1 -0
- package/dist/tiers.d.ts +3 -3
- package/dist/tiers.d.ts.map +1 -1
- package/dist/tiers.js +34 -7
- package/dist/tiers.js.map +1 -1
- package/dist/types.d.ts +140 -9
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +34 -0
- package/dist/types.js.map +1 -1
- package/dist/utils/code-analysis.d.ts +39 -0
- package/dist/utils/code-analysis.d.ts.map +1 -0
- package/dist/utils/code-analysis.js +159 -0
- package/dist/utils/code-analysis.js.map +1 -0
- package/dist/utils/comment-analyzer.d.ts +38 -0
- package/dist/utils/comment-analyzer.d.ts.map +1 -0
- package/dist/utils/comment-analyzer.js +218 -0
- package/dist/utils/comment-analyzer.js.map +1 -0
- package/dist/utils/context-helpers.d.ts +112 -1
- package/dist/utils/context-helpers.d.ts.map +1 -1
- package/dist/utils/context-helpers.js +364 -11
- package/dist/utils/context-helpers.js.map +1 -1
- package/dist/utils/environment-context.d.ts +76 -0
- package/dist/utils/environment-context.d.ts.map +1 -0
- package/dist/utils/environment-context.js +271 -0
- package/dist/utils/environment-context.js.map +1 -0
- package/dist/utils/intent-detector.d.ts +66 -0
- package/dist/utils/intent-detector.d.ts.map +1 -0
- package/dist/utils/intent-detector.js +282 -0
- package/dist/utils/intent-detector.js.map +1 -0
- package/dist/utils/parsed-file.d.ts +51 -0
- package/dist/utils/parsed-file.d.ts.map +1 -0
- package/dist/utils/parsed-file.js +95 -0
- package/dist/utils/parsed-file.js.map +1 -0
- package/dist/utils/route-hierarchy.d.ts +50 -0
- package/dist/utils/route-hierarchy.d.ts.map +1 -0
- package/dist/utils/route-hierarchy.js +226 -0
- package/dist/utils/route-hierarchy.js.map +1 -0
- package/dist/utils/schema-semantics.d.ts +45 -0
- package/dist/utils/schema-semantics.d.ts.map +1 -0
- package/dist/utils/schema-semantics.js +193 -0
- package/dist/utils/schema-semantics.js.map +1 -0
- package/package.json +4 -2
- package/src/__tests__/benchmark/fixtures/layer1/mcp-config-audit.json +31 -0
- package/src/__tests__/benchmark/fixtures/layer2/ai-execution-sinks.ts +1489 -82
- package/src/__tests__/benchmark/fixtures/layer2/ai-mcp-security.ts +495 -0
- package/src/__tests__/benchmark/fixtures/layer2/ai-package-hallucination.ts +255 -0
- package/src/__tests__/benchmark/fixtures/layer2/ai-prompt-hygiene.ts +300 -1
- package/src/__tests__/benchmark/fixtures/layer2/ai-rag-safety.ts +139 -0
- package/src/__tests__/benchmark/fixtures/layer2/byok-patterns.ts +7 -0
- package/src/__tests__/benchmark/fixtures/layer2/data-exposure.ts +63 -0
- package/src/__tests__/benchmark/fixtures/layer2/excessive-agency.ts +221 -0
- package/src/__tests__/benchmark/fixtures/layer2/index.ts +30 -0
- package/src/__tests__/benchmark/fixtures/layer2/model-supply-chain.ts +204 -0
- package/src/__tests__/benchmark/fixtures/layer2/phase1-enhancements.ts +157 -0
- package/src/__tests__/benchmark/fixtures/layer2/phase5-excessive-agency.ts +580 -0
- package/src/__tests__/benchmark/fixtures/layer2/sprint6-ai-enhancements.ts +515 -0
- package/src/__tests__/benchmark/run-depth-validation.ts +9 -9
- package/src/__tests__/category-filter.test.ts +478 -0
- package/src/__tests__/regression/known-false-positives.test.ts +490 -0
- package/src/__tests__/snapshots/__snapshots__/anthropic-validation-refactor.test.ts.snap +762 -0
- package/src/__tests__/snapshots/__snapshots__/dangerous-functions-refactor.test.ts.snap +503 -0
- package/src/__tests__/snapshots/__snapshots__/scan-depth.test.ts.snap +0 -9
- package/src/__tests__/snapshots/anthropic-validation-refactor.test.ts +321 -0
- package/src/__tests__/snapshots/dangerous-functions-refactor.test.ts +439 -0
- package/src/__tests__/validation/run-validation.ts +7 -7
- package/src/ai-context/__tests__/manager.test.ts +193 -0
- package/src/ai-context/index.ts +15 -0
- package/src/ai-context/manager.ts +145 -0
- package/src/baseline/__tests__/diff.test.ts +261 -0
- package/src/baseline/__tests__/manager.test.ts +225 -0
- package/src/baseline/diff.ts +135 -0
- package/src/baseline/index.ts +29 -0
- package/src/baseline/manager.ts +230 -0
- package/src/baseline/types.ts +97 -0
- package/src/category-filter.ts +400 -0
- package/src/filtering/__tests__/pipeline.test.ts +134 -0
- package/src/filtering/context-adjustments.ts +111 -0
- package/src/filtering/index.ts +10 -0
- package/src/filtering/pipeline.ts +130 -0
- package/src/formatters/__tests__/ai-context.test.ts +254 -0
- package/src/formatters/ai-context.ts +302 -0
- package/src/formatters/cli-terminal.ts +444 -41
- package/src/formatters/github-comment.ts +82 -14
- package/src/formatters/ide/__tests__/ide.test.ts +319 -0
- package/src/formatters/ide/claude-code.ts +110 -0
- package/src/formatters/ide/cursor.ts +147 -0
- package/src/formatters/ide/index.ts +216 -0
- package/src/formatters/ide/windsurf.ts +135 -0
- package/src/formatters/index.ts +28 -0
- package/src/index.ts +506 -45
- package/src/layer1/comments.ts +3 -1
- package/src/layer1/config-audit.ts +74 -14
- package/src/layer1/config-mcp-audit.ts +278 -0
- package/src/layer1/entropy.ts +234 -1
- package/src/layer1/file-flags.ts +17 -6
- package/src/layer1/index.ts +29 -23
- package/src/layer1/patterns.ts +42 -4
- package/src/layer1/urls.ts +188 -14
- package/src/layer1/weak-crypto.ts +168 -16
- package/src/layer2/ai-agent-tools.ts +1043 -2
- package/src/layer2/ai-endpoint-protection.ts +19 -4
- package/src/layer2/ai-execution-sinks.ts +755 -29
- package/src/layer2/ai-fingerprinting.ts +33 -33
- package/src/layer2/ai-mcp-security.ts +933 -0
- package/src/layer2/ai-package-hallucination.ts +940 -0
- package/src/layer2/ai-prompt-hygiene.ts +898 -17
- package/src/layer2/ai-rag-safety.ts +467 -5
- package/src/layer2/ai-schema-validation.ts +4 -2
- package/src/layer2/auth-antipatterns.ts +235 -20
- package/src/layer2/byok-patterns.ts +9 -3
- package/src/layer2/dangerous-functions/child-process.ts +98 -0
- package/src/layer2/dangerous-functions/dom-xss.ts +292 -0
- package/src/layer2/dangerous-functions/index.ts +1533 -0
- package/src/layer2/dangerous-functions/json-parse.ts +385 -0
- package/src/layer2/dangerous-functions/math-random.ts +789 -0
- package/src/layer2/dangerous-functions/patterns.ts +176 -0
- package/src/layer2/dangerous-functions/request-validation.ts +145 -0
- package/src/layer2/dangerous-functions/utils/control-flow.ts +35 -0
- package/src/layer2/dangerous-functions/utils/helpers.ts +170 -0
- package/src/layer2/dangerous-functions/utils/index.ts +25 -0
- package/src/layer2/dangerous-functions/utils/schema-validation.ts +106 -0
- package/src/layer2/data-exposure.ts +18 -39
- package/src/layer2/framework-checks.ts +9 -2
- package/src/layer2/index.ts +124 -43
- package/src/layer2/logic-gates.ts +64 -22
- package/src/layer2/model-supply-chain.ts +531 -0
- package/src/layer2/risky-imports.ts +9 -2
- package/src/layer2/variables.ts +9 -2
- package/src/layer3/__tests__/osv-check.test.ts +384 -0
- package/src/layer3/anthropic/auto-dismiss.ts +223 -0
- package/src/layer3/anthropic/clients.ts +84 -0
- package/src/layer3/anthropic/index.ts +170 -0
- package/src/layer3/anthropic/prompts/index.ts +14 -0
- package/src/layer3/anthropic/prompts/semantic-analysis.ts +173 -0
- package/src/layer3/anthropic/prompts/validation.ts +419 -0
- package/src/layer3/anthropic/providers/anthropic.ts +310 -0
- package/src/layer3/anthropic/providers/index.ts +8 -0
- package/src/layer3/anthropic/providers/openai.ts +384 -0
- package/src/layer3/anthropic/request-builder.ts +150 -0
- package/src/layer3/anthropic/types.ts +148 -0
- package/src/layer3/anthropic/utils/index.ts +26 -0
- package/src/layer3/anthropic/utils/path-helpers.ts +68 -0
- package/src/layer3/anthropic/utils/response-parser.ts +322 -0
- package/src/layer3/anthropic/utils/retry.ts +75 -0
- package/src/layer3/index.ts +18 -5
- package/src/layer3/osv-check.ts +420 -0
- package/src/modes/incremental.ts +1 -1
- package/src/rules/__tests__/framework-fixes.test.ts +689 -0
- package/src/rules/__tests__/metadata.test.ts +218 -0
- package/src/rules/framework-fixes.ts +470 -0
- package/src/rules/index.ts +21 -0
- package/src/rules/metadata.ts +831 -0
- package/src/suppression/__tests__/config-loader.test.ts +382 -0
- package/src/suppression/__tests__/hash.test.ts +166 -0
- package/src/suppression/__tests__/inline-parser.test.ts +212 -0
- package/src/suppression/__tests__/manager.test.ts +415 -0
- package/src/suppression/config-loader.ts +462 -0
- package/src/suppression/hash.ts +95 -0
- package/src/suppression/index.ts +51 -0
- package/src/suppression/inline-parser.ts +273 -0
- package/src/suppression/manager.ts +379 -0
- package/src/suppression/types.ts +174 -0
- package/src/tiers.ts +45 -9
- package/src/types.ts +212 -8
- package/src/utils/__tests__/code-analysis.test.ts +165 -0
- package/src/utils/__tests__/parsed-file.test.ts +124 -0
- package/src/utils/code-analysis.ts +179 -0
- package/src/utils/comment-analyzer.ts +249 -0
- package/src/utils/context-helpers.ts +421 -11
- package/src/utils/environment-context.ts +304 -0
- package/src/utils/intent-detector.ts +318 -0
- package/src/utils/parsed-file.ts +103 -0
- package/src/utils/route-hierarchy.ts +250 -0
- package/src/utils/schema-semantics.ts +233 -0
- package/dist/layer2/dangerous-functions.d.ts +0 -7
- package/dist/layer2/dangerous-functions.d.ts.map +0 -1
- package/dist/layer2/dangerous-functions.js +0 -1701
- package/dist/layer2/dangerous-functions.js.map +0 -1
- package/dist/layer3/anthropic.d.ts +0 -87
- package/dist/layer3/anthropic.d.ts.map +0 -1
- package/dist/layer3/anthropic.js +0 -1948
- package/dist/layer3/anthropic.js.map +0 -1
- package/dist/layer3/openai.d.ts +0 -25
- package/dist/layer3/openai.d.ts.map +0 -1
- package/dist/layer3/openai.js +0 -238
- package/dist/layer3/openai.js.map +0 -1
- package/src/layer2/dangerous-functions.ts +0 -1940
- package/src/layer3/anthropic.ts +0 -2257
|
@@ -9,11 +9,16 @@
|
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
import type { Vulnerability, VulnerabilitySeverity } from '../types'
|
|
12
|
+
import type { ParsedFile } from '../utils/parsed-file'
|
|
12
13
|
import type { MiddlewareAuthConfig } from '../utils/middleware-detector'
|
|
13
14
|
import { isRouteProtectedByMiddleware, getRoutePathFromFile } from '../utils/middleware-detector'
|
|
14
15
|
import type { AuthHelper, AuthHelperContext } from '../utils/auth-helper-detector'
|
|
15
16
|
import { hasAuthHelperCallBefore, isUserIdAlreadyValidated } from '../utils/auth-helper-detector'
|
|
16
17
|
import type { FileAuthImports } from '../utils/imported-auth-detector'
|
|
18
|
+
import { isScannerOrFixtureFile } from '../utils/context-helpers'
|
|
19
|
+
import { getRouteProtectionContext, isAuthenticatedOnlyComponent } from '../utils/route-hierarchy'
|
|
20
|
+
import { is2FAOrValidation } from '../utils/schema-semantics'
|
|
21
|
+
import { isPasswordErrorCode, hasPasswordValueInError } from '../utils/intent-detector'
|
|
17
22
|
|
|
18
23
|
interface AuthAntiPattern {
|
|
19
24
|
name: string
|
|
@@ -78,13 +83,10 @@ const AUTH_ANTIPATTERNS: AuthAntiPattern[] = [
|
|
|
78
83
|
description: 'Session configuration may lack secure flag',
|
|
79
84
|
suggestedFix: 'Set secure: true for cookies in production',
|
|
80
85
|
},
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
description: 'Cookie set without httpOnly flag',
|
|
86
|
-
suggestedFix: 'Add httpOnly: true to prevent XSS access to cookies',
|
|
87
|
-
},
|
|
86
|
+
// NOTE: Cookie httpOnly detection removed - causes false positives
|
|
87
|
+
// Client-side code (document.cookie) cannot set httpOnly - it's a server-only flag
|
|
88
|
+
// Server-side cookie libraries have proper defaults
|
|
89
|
+
// The pattern was triggering on client-side cookie access which is conceptually wrong
|
|
88
90
|
|
|
89
91
|
// Authorization issues
|
|
90
92
|
// NOTE: We intentionally do NOT flag "if (!user)" or "if (!userId)" patterns as issues
|
|
@@ -122,12 +124,17 @@ const AUTH_ANTIPATTERNS: AuthAntiPattern[] = [
|
|
|
122
124
|
},
|
|
123
125
|
|
|
124
126
|
// OAuth/Social auth issues
|
|
127
|
+
// NOTE: OAuth detection narrowed significantly to reduce false positives.
|
|
128
|
+
// Previously matched any line containing "oauth" which flagged variable names, imports, etc.
|
|
129
|
+
// Now only matches actual OAuth authorization URL construction.
|
|
125
130
|
{
|
|
126
131
|
name: 'OAuth state parameter missing',
|
|
127
|
-
|
|
132
|
+
// Only match actual OAuth authorization URL construction without state parameter
|
|
133
|
+
// Must have: authorize endpoint + client_id/redirect_uri but missing state
|
|
134
|
+
pattern: /['"`]https?:\/\/[^'"]*\/(?:oauth|authorize|auth)[^'"]*client_id=[^'"]*(?!.*state=)/gi,
|
|
128
135
|
severity: 'medium',
|
|
129
|
-
description: 'OAuth
|
|
130
|
-
suggestedFix: 'Include a random state parameter in OAuth requests',
|
|
136
|
+
description: 'OAuth authorization URL may lack state parameter for CSRF protection',
|
|
137
|
+
suggestedFix: 'Include a random state parameter in OAuth authorization requests',
|
|
131
138
|
},
|
|
132
139
|
|
|
133
140
|
// Password handling issues
|
|
@@ -138,13 +145,10 @@ const AUTH_ANTIPATTERNS: AuthAntiPattern[] = [
|
|
|
138
145
|
description: 'Password may be logged to console',
|
|
139
146
|
suggestedFix: 'Never log passwords or sensitive credentials',
|
|
140
147
|
},
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
description: 'Password may be included in error message',
|
|
146
|
-
suggestedFix: 'Never include passwords in error messages',
|
|
147
|
-
},
|
|
148
|
+
// NOTE: 'Password in error message' pattern now uses smart intent detection
|
|
149
|
+
// Error CODES like 'SAME_PASSWORD' are not flagged (they're codes, not values)
|
|
150
|
+
// Only actual password values concatenated into errors are flagged
|
|
151
|
+
// This is handled specially in detectAuthAntipatterns() below
|
|
148
152
|
|
|
149
153
|
// Rate limiting
|
|
150
154
|
{
|
|
@@ -183,6 +187,37 @@ function isAuthRelatedFile(filePath: string): boolean {
|
|
|
183
187
|
return authKeywords.some(keyword => lowerPath.includes(keyword))
|
|
184
188
|
}
|
|
185
189
|
|
|
190
|
+
/**
|
|
191
|
+
* Check if file is an auth implementation/library file
|
|
192
|
+
* These files ARE the auth system - flagging them for "missing auth" is wrong
|
|
193
|
+
*
|
|
194
|
+
* Examples:
|
|
195
|
+
* - packages/auth/src/handlers.ts
|
|
196
|
+
* - lib/auth/middleware.ts
|
|
197
|
+
* - services/authentication/index.ts
|
|
198
|
+
*/
|
|
199
|
+
function isAuthImplementationFile(filePath: string): boolean {
|
|
200
|
+
const authImplPatterns = [
|
|
201
|
+
/\/packages\/auth\//i,
|
|
202
|
+
/\/lib\/auth\//i,
|
|
203
|
+
/\/utils\/auth\//i,
|
|
204
|
+
/\/services\/auth/i,
|
|
205
|
+
/\/authentication\//i,
|
|
206
|
+
/\/auth-provider/i,
|
|
207
|
+
/\/auth-helpers/i,
|
|
208
|
+
/\/auth-utils/i,
|
|
209
|
+
/\/passport-/i, // Passport.js strategy files
|
|
210
|
+
/\/next-auth\//i, // NextAuth config
|
|
211
|
+
/\/lucia\//i, // Lucia auth
|
|
212
|
+
/\/better-auth\//i, // Better Auth
|
|
213
|
+
/\/supabase\/auth/i, // Supabase auth
|
|
214
|
+
/\/clerk\//i, // Clerk auth
|
|
215
|
+
/\/auth0\//i, // Auth0
|
|
216
|
+
/\/keycloak\//i, // Keycloak
|
|
217
|
+
]
|
|
218
|
+
return authImplPatterns.some(p => p.test(filePath))
|
|
219
|
+
}
|
|
220
|
+
|
|
186
221
|
// Check if endpoint is a known public endpoint (health checks, webhooks, cron)
|
|
187
222
|
function isKnownPublicEndpoint(lineContent: string, filePath: string): boolean {
|
|
188
223
|
const PUBLIC_ENDPOINTS = [
|
|
@@ -193,20 +228,60 @@ function isKnownPublicEndpoint(lineContent: string, filePath: string): boolean {
|
|
|
193
228
|
/\/live\b/i,
|
|
194
229
|
/\/ping\b/i,
|
|
195
230
|
/\/status\b/i,
|
|
231
|
+
/\/_health/i,
|
|
196
232
|
|
|
197
233
|
// Webhooks (receive external calls)
|
|
198
234
|
/\/webhook\b/i,
|
|
199
235
|
/\/webhooks\//i,
|
|
200
236
|
/\/callback\b/i,
|
|
237
|
+
/\/stripe\/webhook/i,
|
|
238
|
+
/\/clerk\/webhook/i,
|
|
239
|
+
/\/svix\//i,
|
|
201
240
|
|
|
202
241
|
// Cron/scheduled tasks
|
|
203
242
|
/\/cron\//i,
|
|
204
243
|
/\/scheduled\//i,
|
|
205
244
|
/\/tasks\//i,
|
|
245
|
+
/\/jobs?\//i,
|
|
206
246
|
|
|
207
|
-
// Public APIs
|
|
247
|
+
// Public APIs - intentionally unauthenticated
|
|
208
248
|
/\/public\//i,
|
|
249
|
+
/\/openpage/i, // OpenPage API pattern (intentionally public)
|
|
250
|
+
/\/open-api\//i,
|
|
251
|
+
/\/api\/public\//i,
|
|
252
|
+
/\/api\/v\d+\/public\//i,
|
|
209
253
|
/\bGET\b.*\/api\/\w+\/\[id\]/i, // Public resource reads with ID param
|
|
254
|
+
|
|
255
|
+
// Auth endpoints (must be public for users to authenticate)
|
|
256
|
+
/\/api\/auth\//i,
|
|
257
|
+
/\/auth\//i,
|
|
258
|
+
/\/login\b/i,
|
|
259
|
+
/\/signup\b/i,
|
|
260
|
+
/\/register\b/i,
|
|
261
|
+
/\/forgot-password/i,
|
|
262
|
+
/\/reset-password/i,
|
|
263
|
+
/\/verify-email/i,
|
|
264
|
+
/\/magic-link/i,
|
|
265
|
+
/\/oauth\//i,
|
|
266
|
+
|
|
267
|
+
// RSS/Atom feeds (typically public)
|
|
268
|
+
/\/feed\b/i,
|
|
269
|
+
/\/rss\b/i,
|
|
270
|
+
/\/atom\b/i,
|
|
271
|
+
|
|
272
|
+
// Sitemap/robots (always public)
|
|
273
|
+
/\/sitemap/i,
|
|
274
|
+
/\/robots/i,
|
|
275
|
+
|
|
276
|
+
// OpenGraph/meta endpoints
|
|
277
|
+
/\/og\//i,
|
|
278
|
+
/\/opengraph/i,
|
|
279
|
+
/\/meta\//i,
|
|
280
|
+
|
|
281
|
+
// Share/embed endpoints
|
|
282
|
+
/\/share\//i,
|
|
283
|
+
/\/embed\//i,
|
|
284
|
+
/\/widget\//i,
|
|
210
285
|
]
|
|
211
286
|
|
|
212
287
|
return PUBLIC_ENDPOINTS.some(pattern =>
|
|
@@ -243,6 +318,47 @@ function hasAuthCheckNearby(lines: string[], lineIndex: number): boolean {
|
|
|
243
318
|
/userApiKey|user_api_key|clientApiKey/i,
|
|
244
319
|
/req\.body\.(?:apiKey|api_key|openaiKey|anthropicKey)/i,
|
|
245
320
|
/headers\[['"`]x-(?:openai|api|anthropic)-key['"`]\]/i,
|
|
321
|
+
|
|
322
|
+
// Next.js / React auth patterns (expanded)
|
|
323
|
+
/const\s+session\s*=\s*await\s+auth\s*\(\)/, // const session = await auth()
|
|
324
|
+
/const\s+\{\s*session\s*\}\s*=\s*await\s+auth\s*\(\)/, // const { session } = await auth()
|
|
325
|
+
/if\s*\(\s*!session\?\.user/, // if (!session?.user)
|
|
326
|
+
/if\s*\(\s*!session\s*\)/, // if (!session)
|
|
327
|
+
/session\s*\?\.\s*user/, // session?.user
|
|
328
|
+
/getServerSession\s*\(\s*authOptions/, // getServerSession(authOptions)
|
|
329
|
+
/getServerSession\s*\(\s*req\s*,\s*res/, // getServerSession(req, res, ...)
|
|
330
|
+
/useSession\s*\(\)/, // useSession()
|
|
331
|
+
/signIn\s*\(/, // signIn()
|
|
332
|
+
/signOut\s*\(/, // signOut()
|
|
333
|
+
|
|
334
|
+
// Clerk auth patterns
|
|
335
|
+
/currentUser\s*\(\)/, // currentUser()
|
|
336
|
+
/auth\s*\(\)\s*\.\s*protect/, // auth().protect
|
|
337
|
+
/auth\s*\(\)\s*\.\s*userId/, // auth().userId
|
|
338
|
+
/clerkClient/i,
|
|
339
|
+
/getAuth\s*\(/,
|
|
340
|
+
/ClerkProvider/,
|
|
341
|
+
|
|
342
|
+
// Supabase auth patterns
|
|
343
|
+
/supabase\s*\.\s*auth\s*\.\s*getUser/, // supabase.auth.getUser()
|
|
344
|
+
/supabase\s*\.\s*auth\s*\.\s*getSession/, // supabase.auth.getSession()
|
|
345
|
+
/createServerClient/, // Supabase server client
|
|
346
|
+
/createRouteHandlerClient/,
|
|
347
|
+
|
|
348
|
+
// Lucia auth patterns
|
|
349
|
+
/lucia\s*\.\s*validateSession/,
|
|
350
|
+
/validateRequest/,
|
|
351
|
+
|
|
352
|
+
// Better Auth patterns
|
|
353
|
+
/betterAuth/,
|
|
354
|
+
/auth\.api\./,
|
|
355
|
+
|
|
356
|
+
// Throwing auth helpers (if these are called, route is authenticated)
|
|
357
|
+
/throw\s+new\s+Error\s*\(\s*['"]unauthorized/i,
|
|
358
|
+
/throw\s+new\s+Error\s*\(\s*['"]unauthenticated/i,
|
|
359
|
+
/ChatSDKError\s*\(\s*['"]unauthorized/i,
|
|
360
|
+
/return\s+new\s+Response\s*\(\s*.*401/,
|
|
361
|
+
/return\s+NextResponse\s*\.\s*json\s*\(\s*.*401/,
|
|
246
362
|
]
|
|
247
363
|
|
|
248
364
|
return searchWindow.some(line =>
|
|
@@ -254,6 +370,7 @@ export interface AuthAntipatternOptions {
|
|
|
254
370
|
middlewareConfig?: MiddlewareAuthConfig
|
|
255
371
|
authHelpers?: AuthHelperContext
|
|
256
372
|
fileAuthImports?: Map<string, FileAuthImports>
|
|
373
|
+
parsed?: ParsedFile
|
|
257
374
|
}
|
|
258
375
|
|
|
259
376
|
export function detectAuthAntipatterns(
|
|
@@ -261,10 +378,18 @@ export function detectAuthAntipatterns(
|
|
|
261
378
|
filePath: string,
|
|
262
379
|
options: AuthAntipatternOptions = {}
|
|
263
380
|
): Vulnerability[] {
|
|
264
|
-
const { middlewareConfig, authHelpers, fileAuthImports } = options
|
|
381
|
+
const { middlewareConfig, authHelpers, fileAuthImports, parsed } = options
|
|
265
382
|
const vulnerabilities: Vulnerability[] = []
|
|
266
|
-
|
|
383
|
+
|
|
384
|
+
// Skip scanner/fixture files to avoid self-detection
|
|
385
|
+
if (isScannerOrFixtureFile(filePath)) return vulnerabilities
|
|
386
|
+
|
|
387
|
+
const lines = parsed?.lines ?? content.split('\n')
|
|
267
388
|
const isAuthFile = isAuthRelatedFile(filePath)
|
|
389
|
+
const isAuthImpl = isAuthImplementationFile(filePath)
|
|
390
|
+
|
|
391
|
+
// Check framework route hierarchy protection (Remix, Next.js route groups)
|
|
392
|
+
const routeHierarchy = getRouteProtectionContext(filePath)
|
|
268
393
|
|
|
269
394
|
// Check if this route is protected by global middleware
|
|
270
395
|
const routePath = getRoutePathFromFile(filePath)
|
|
@@ -279,6 +404,9 @@ export function detectAuthAntipatterns(
|
|
|
279
404
|
// Check if file uses throwing auth helpers
|
|
280
405
|
const helpersList = authHelpers?.helpers || []
|
|
281
406
|
|
|
407
|
+
// Check if this is a component only used in authenticated contexts
|
|
408
|
+
const isAuthOnlyComponent = isAuthenticatedOnlyComponent(filePath)
|
|
409
|
+
|
|
282
410
|
lines.forEach((line, index) => {
|
|
283
411
|
// Skip comment lines
|
|
284
412
|
if (isComment(line)) return
|
|
@@ -291,6 +419,12 @@ export function detectAuthAntipatterns(
|
|
|
291
419
|
if (pattern.name === 'Unprotected API route' ||
|
|
292
420
|
pattern.name === 'Express route without auth middleware') {
|
|
293
421
|
|
|
422
|
+
// PRIORITY -1: Skip auth implementation files entirely
|
|
423
|
+
// These files ARE the auth system - flagging them for "missing auth" is conceptually wrong
|
|
424
|
+
if (isAuthImpl) {
|
|
425
|
+
break // Skip this pattern
|
|
426
|
+
}
|
|
427
|
+
|
|
294
428
|
// PRIORITY 0: Check if this is actually a route file
|
|
295
429
|
// In Next.js, routes must be in `route.ts/js` files. Files like `handlers.ts`,
|
|
296
430
|
// `safe-handlers.ts`, `utils.ts` etc. are NOT actual API routes even if they
|
|
@@ -308,6 +442,32 @@ export function detectAuthAntipatterns(
|
|
|
308
442
|
break
|
|
309
443
|
}
|
|
310
444
|
|
|
445
|
+
// PRIORITY 0.5: Check if route is in a protected route hierarchy
|
|
446
|
+
// Framework route conventions (Remix _authenticated+, Next.js route groups)
|
|
447
|
+
if (routeHierarchy.isInProtectedHierarchy) {
|
|
448
|
+
// Route is in a protected hierarchy - cap severity at info
|
|
449
|
+
vulnerabilities.push({
|
|
450
|
+
id: `auth-antipattern-${filePath}-${index + 1}-${pattern.name}`,
|
|
451
|
+
filePath,
|
|
452
|
+
lineNumber: index + 1,
|
|
453
|
+
lineContent: line.trim(),
|
|
454
|
+
severity: 'info',
|
|
455
|
+
category: 'missing_auth',
|
|
456
|
+
title: pattern.name + ' (in protected route hierarchy)',
|
|
457
|
+
description: `This route is within a protected route hierarchy (${routeHierarchy.protectionSource.join(', ')}). Authentication is likely handled by parent layout/middleware.`,
|
|
458
|
+
suggestedFix: 'Verify parent layout enforces authentication. If not, add auth check here.',
|
|
459
|
+
confidence: 'low',
|
|
460
|
+
layer: 2,
|
|
461
|
+
})
|
|
462
|
+
break // Only report once per line
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
// PRIORITY 0.75: Check if this is a component only used in authenticated contexts
|
|
466
|
+
if (isAuthOnlyComponent) {
|
|
467
|
+
// Component is in admin/dashboard/etc - skip entirely
|
|
468
|
+
break
|
|
469
|
+
}
|
|
470
|
+
|
|
311
471
|
// PRIORITY 1: Check if route is protected by global middleware
|
|
312
472
|
// This is the STRONGEST signal - if middleware protects the route, suppress entirely
|
|
313
473
|
if (middlewareProtection.isProtected) {
|
|
@@ -390,5 +550,60 @@ export function detectAuthAntipatterns(
|
|
|
390
550
|
}
|
|
391
551
|
})
|
|
392
552
|
|
|
553
|
+
// Special handling: Password in error message with smart intent detection
|
|
554
|
+
// Only flag actual password VALUES, not error CODES like 'SAME_PASSWORD'
|
|
555
|
+
const passwordErrorPattern = /throw\s+new\s+Error\s*\([^)]*password|Error\s*\([^)]*password/gi
|
|
556
|
+
lines.forEach((line, index) => {
|
|
557
|
+
if (isComment(line)) return
|
|
558
|
+
|
|
559
|
+
if (passwordErrorPattern.test(line)) {
|
|
560
|
+
// Reset regex state
|
|
561
|
+
passwordErrorPattern.lastIndex = 0
|
|
562
|
+
|
|
563
|
+
// Skip if this is an error CODE, not a VALUE
|
|
564
|
+
if (isPasswordErrorCode(line)) {
|
|
565
|
+
return // This is fine - 'SAME_PASSWORD' is a code, not a value
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
// Only flag if actual password value is in the error
|
|
569
|
+
if (hasPasswordValueInError(line)) {
|
|
570
|
+
vulnerabilities.push({
|
|
571
|
+
id: `auth-antipattern-${filePath}-${index + 1}-password-in-error`,
|
|
572
|
+
filePath,
|
|
573
|
+
lineNumber: index + 1,
|
|
574
|
+
lineContent: line.trim(),
|
|
575
|
+
severity: 'high',
|
|
576
|
+
category: 'missing_auth',
|
|
577
|
+
title: 'Password value in error message',
|
|
578
|
+
description: 'Actual password value may be included in error message, exposing sensitive data.',
|
|
579
|
+
suggestedFix: 'Never include actual password values in error messages. Use error codes instead.',
|
|
580
|
+
confidence: 'high',
|
|
581
|
+
layer: 2,
|
|
582
|
+
})
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
})
|
|
586
|
+
|
|
587
|
+
// Special handling: 2FA optional fields with OR validation
|
|
588
|
+
// Don't flag .optional() on 2FA fields if there's .refine() enforcing OR logic
|
|
589
|
+
const twoFAOptionalPattern = /\.(totp|otp|backupCode|recoveryCode|twoFactor|2fa|mfa).*\.optional\s*\(\)/gi
|
|
590
|
+
lines.forEach((line, index) => {
|
|
591
|
+
if (isComment(line)) return
|
|
592
|
+
|
|
593
|
+
if (twoFAOptionalPattern.test(line)) {
|
|
594
|
+
// Reset regex state
|
|
595
|
+
twoFAOptionalPattern.lastIndex = 0
|
|
596
|
+
|
|
597
|
+
// Check if this is legitimate OR validation (either TOTP or backup code required)
|
|
598
|
+
if (is2FAOrValidation(content, index)) {
|
|
599
|
+
// This is legitimate OR validation - skip or report as info
|
|
600
|
+
return
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
// Only flag if this truly allows bypassing 2FA
|
|
604
|
+
// Most cases with .refine() are fine - this is handled by schema-semantics.ts
|
|
605
|
+
}
|
|
606
|
+
})
|
|
607
|
+
|
|
393
608
|
return vulnerabilities
|
|
394
609
|
}
|
|
@@ -5,8 +5,9 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import type { Vulnerability, VulnerabilitySeverity } from '../types'
|
|
8
|
+
import type { ParsedFile } from '../utils/parsed-file'
|
|
8
9
|
import type { MiddlewareAuthConfig } from '../utils/middleware-detector'
|
|
9
|
-
import { isComment, isTestOrMockFile, isExampleFile, isPlaceholderValue } from '../utils/context-helpers'
|
|
10
|
+
import { isComment, isTestOrMockFile, isExampleFile, isPlaceholderValue, isScannerOrFixtureFile } from '../utils/context-helpers'
|
|
10
11
|
import { isRouteProtectedByMiddleware, getRoutePathFromFile, detectUserScopingPatterns } from '../utils/middleware-detector'
|
|
11
12
|
|
|
12
13
|
/**
|
|
@@ -221,10 +222,15 @@ function isKeyLogged(content: string, lineNumber: number): boolean {
|
|
|
221
222
|
export function detectBYOKPatterns(
|
|
222
223
|
content: string,
|
|
223
224
|
filePath: string,
|
|
224
|
-
middlewareConfig?: MiddlewareAuthConfig
|
|
225
|
+
middlewareConfig?: MiddlewareAuthConfig,
|
|
226
|
+
options?: { parsed?: ParsedFile }
|
|
225
227
|
): Vulnerability[] {
|
|
226
228
|
const vulnerabilities: Vulnerability[] = []
|
|
227
|
-
|
|
229
|
+
|
|
230
|
+
// Skip scanner/fixture files to avoid self-detection
|
|
231
|
+
if (isScannerOrFixtureFile(filePath)) return vulnerabilities
|
|
232
|
+
|
|
233
|
+
const lines = options?.parsed?.lines ?? content.split('\n')
|
|
228
234
|
const isTestFile = isTestOrMockFile(filePath)
|
|
229
235
|
|
|
230
236
|
// Skip example/demo files entirely - they contain placeholder credentials by design
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Child Process Detection
|
|
3
|
+
*
|
|
4
|
+
* Detection logic for child_process functions (exec, spawn, execFile, etc.)
|
|
5
|
+
* that can lead to command injection vulnerabilities.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Check if exec() call is from child_process (dangerous) vs RegExp.exec (safe)
|
|
10
|
+
* Returns true if this is a child_process exec call that should be flagged
|
|
11
|
+
*/
|
|
12
|
+
export function isChildProcessExec(content: string, lineContent: string): boolean {
|
|
13
|
+
// Check for child_process import
|
|
14
|
+
const hasChildProcessImport =
|
|
15
|
+
/require\s*\(\s*['"]child_process['"]\s*\)/.test(content) ||
|
|
16
|
+
/from\s+['"]child_process['"]/.test(content) ||
|
|
17
|
+
/import\s+.*child_process/.test(content) ||
|
|
18
|
+
/require\s*\(\s*['"]node:child_process['"]\s*\)/.test(content) ||
|
|
19
|
+
/from\s+['"]node:child_process['"]/.test(content)
|
|
20
|
+
|
|
21
|
+
// If no child_process import, this is likely RegExp.exec or similar
|
|
22
|
+
if (!hasChildProcessImport) {
|
|
23
|
+
return false
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Check if this specific line is RegExp.exec pattern
|
|
27
|
+
// RegExp.exec is called as: regex.exec(string) or /pattern/.exec(string)
|
|
28
|
+
const isRegExpExec =
|
|
29
|
+
/\.\s*exec\s*\(/.test(lineContent) && // Method call on an object
|
|
30
|
+
!/\bexec\s*\(/.test(lineContent.replace(/\.\s*exec\s*\(/, '')) // Not a standalone exec()
|
|
31
|
+
|
|
32
|
+
// Also check for common RegExp patterns
|
|
33
|
+
const isRegExpPattern =
|
|
34
|
+
/\/[^/]+\/[gimsuy]*\.exec\s*\(/.test(lineContent) || // /pattern/.exec()
|
|
35
|
+
/new\s+RegExp\s*\([^)]+\)\.exec\s*\(/.test(lineContent) || // new RegExp().exec()
|
|
36
|
+
/regex\.exec\s*\(/i.test(lineContent) || // regex.exec()
|
|
37
|
+
/pattern\.exec\s*\(/i.test(lineContent) || // pattern.exec()
|
|
38
|
+
/match\.exec\s*\(/i.test(lineContent) || // match.exec()
|
|
39
|
+
/re\.exec\s*\(/i.test(lineContent) // re.exec()
|
|
40
|
+
|
|
41
|
+
if (isRegExpExec || isRegExpPattern) {
|
|
42
|
+
return false
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Check if exec is imported/destructured from child_process
|
|
46
|
+
const execImported =
|
|
47
|
+
/\{\s*[^}]*\bexec\b[^}]*\}\s*=\s*require\s*\(\s*['"]child_process['"]/.test(
|
|
48
|
+
content
|
|
49
|
+
) ||
|
|
50
|
+
/\{\s*[^}]*\bexec\b[^}]*\}\s*=\s*require\s*\(\s*['"]node:child_process['"]/.test(
|
|
51
|
+
content
|
|
52
|
+
) ||
|
|
53
|
+
/import\s+\{\s*[^}]*\bexec\b[^}]*\}\s+from\s+['"]child_process['"]/.test(
|
|
54
|
+
content
|
|
55
|
+
) ||
|
|
56
|
+
/import\s+\{\s*[^}]*\bexec\b[^}]*\}\s+from\s+['"]node:child_process['"]/.test(
|
|
57
|
+
content
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
// If exec is directly imported from child_process, standalone exec() is dangerous
|
|
61
|
+
if (execImported && /\bexec\s*\(/.test(lineContent)) {
|
|
62
|
+
return true
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Check for child_process.exec() pattern
|
|
66
|
+
if (
|
|
67
|
+
/child_process\.exec\s*\(/.test(lineContent) ||
|
|
68
|
+
/cp\.exec\s*\(/.test(lineContent) ||
|
|
69
|
+
/childProcess\.exec\s*\(/.test(lineContent)
|
|
70
|
+
) {
|
|
71
|
+
return true
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// If we have child_process import but can't determine usage, be conservative
|
|
75
|
+
// Only flag if it looks like a standalone exec() call
|
|
76
|
+
return /\bexec\s*\(/.test(lineContent) && !/\.\s*exec\s*\(/.test(lineContent)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Check if spawn/execFile/execSync is from child_process
|
|
81
|
+
*/
|
|
82
|
+
export function isChildProcessSpawn(content: string, lineContent: string): boolean {
|
|
83
|
+
// Check for child_process import
|
|
84
|
+
const hasChildProcessImport =
|
|
85
|
+
/require\s*\(\s*['"]child_process['"]\s*\)/.test(content) ||
|
|
86
|
+
/from\s+['"]child_process['"]/.test(content) ||
|
|
87
|
+
/require\s*\(\s*['"]node:child_process['"]\s*\)/.test(content) ||
|
|
88
|
+
/from\s+['"]node:child_process['"]/.test(content)
|
|
89
|
+
|
|
90
|
+
if (!hasChildProcessImport) {
|
|
91
|
+
return false
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// These functions are always from child_process when that module is imported
|
|
95
|
+
return /\b(spawn|spawnSync|execSync|execFile|execFileSync)\s*\(/.test(
|
|
96
|
+
lineContent
|
|
97
|
+
)
|
|
98
|
+
}
|