@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
|
@@ -35,7 +35,29 @@ const CATEGORY_DOCS: Partial<Record<VulnerabilityCategory, string>> = {
|
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
/**
|
|
38
|
-
*
|
|
38
|
+
* Helper to determine language from file path
|
|
39
|
+
*/
|
|
40
|
+
function getLanguageFromPath(filePath: string): string {
|
|
41
|
+
const ext = filePath.split('.').pop()?.toLowerCase() || ''
|
|
42
|
+
const langMap: Record<string, string> = {
|
|
43
|
+
ts: 'typescript',
|
|
44
|
+
tsx: 'typescript',
|
|
45
|
+
js: 'javascript',
|
|
46
|
+
jsx: 'javascript',
|
|
47
|
+
py: 'python',
|
|
48
|
+
go: 'go',
|
|
49
|
+
java: 'java',
|
|
50
|
+
rb: 'ruby',
|
|
51
|
+
php: 'php',
|
|
52
|
+
yaml: 'yaml',
|
|
53
|
+
yml: 'yaml',
|
|
54
|
+
json: 'json',
|
|
55
|
+
}
|
|
56
|
+
return langMap[ext] || ''
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Format a single finding as a markdown section with actionable info (PRO-82)
|
|
39
61
|
*/
|
|
40
62
|
function formatFinding(finding: Vulnerability, options: { showFile?: boolean; showDocs?: boolean } = {}): string {
|
|
41
63
|
const { showFile = true, showDocs = true } = options
|
|
@@ -44,18 +66,43 @@ function formatFinding(finding: Vulnerability, options: { showFile?: boolean; sh
|
|
|
44
66
|
? `\`${finding.filePath}:${finding.lineNumber}\``
|
|
45
67
|
: `Line ${finding.lineNumber}`
|
|
46
68
|
|
|
47
|
-
let md =
|
|
48
|
-
md +=
|
|
49
|
-
|
|
69
|
+
let md = `#### ${badge} ${finding.title}\n\n`
|
|
70
|
+
md += `📍 ${location}\n\n`
|
|
71
|
+
|
|
72
|
+
// Impact (why this matters) - shown if available
|
|
73
|
+
if (finding.impact) {
|
|
74
|
+
md += `**Impact:** ${finding.impact}\n\n`
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Code snippet in collapsible
|
|
78
|
+
if (finding.lineContent && finding.lineContent.trim()) {
|
|
79
|
+
const language = getLanguageFromPath(finding.filePath)
|
|
80
|
+
md += `<details>\n<summary>View code</summary>\n\n`
|
|
81
|
+
md += `\`\`\`${language}\n${finding.lineContent.trim()}\n\`\`\`\n\n`
|
|
82
|
+
md += `</details>\n\n`
|
|
83
|
+
}
|
|
50
84
|
|
|
51
|
-
|
|
52
|
-
|
|
85
|
+
// Fix steps - shown as numbered list (PRO-82)
|
|
86
|
+
if (finding.fixSteps && finding.fixSteps.length > 0) {
|
|
87
|
+
md += `**Fix:**\n`
|
|
88
|
+
finding.fixSteps.forEach((step, i) => {
|
|
89
|
+
md += `${i + 1}. ${step}\n`
|
|
90
|
+
})
|
|
91
|
+
md += '\n'
|
|
92
|
+
} else if (finding.suggestedFix) {
|
|
93
|
+
// Fallback to legacy field
|
|
94
|
+
md += `💡 **Fix:** ${finding.suggestedFix}\n\n`
|
|
53
95
|
}
|
|
54
96
|
|
|
55
|
-
//
|
|
97
|
+
// Documentation links
|
|
56
98
|
const docsUrl = CATEGORY_DOCS[finding.category]
|
|
57
|
-
|
|
58
|
-
|
|
99
|
+
const referenceUrl = finding.references && finding.references.length > 0 ? finding.references[0] : null
|
|
100
|
+
|
|
101
|
+
if (showDocs && (docsUrl || referenceUrl)) {
|
|
102
|
+
const links: string[] = []
|
|
103
|
+
if (docsUrl) links.push(`[Learn more](${docsUrl})`)
|
|
104
|
+
if (referenceUrl && referenceUrl !== docsUrl) links.push(`[OWASP/CWE](${referenceUrl})`)
|
|
105
|
+
md += links.join(' · ') + '\n\n'
|
|
59
106
|
}
|
|
60
107
|
|
|
61
108
|
return md
|
|
@@ -103,7 +150,7 @@ export interface GitHubCommentOptions {
|
|
|
103
150
|
maxFindingsPerGroup?: number
|
|
104
151
|
showAllFindings?: boolean
|
|
105
152
|
includeFooter?: boolean
|
|
106
|
-
scanDepth?: '
|
|
153
|
+
scanDepth?: 'local' | 'verified' | 'deep'
|
|
107
154
|
previousScanCounts?: {
|
|
108
155
|
critical: number
|
|
109
156
|
high: number
|
|
@@ -293,8 +340,8 @@ function formatScanMetadata(result: ScanResult, scanDepth?: string): string {
|
|
|
293
340
|
md += `| Scan duration | ${(result.scanDuration / 1000).toFixed(1)}s |\n`
|
|
294
341
|
if (scanDepth) {
|
|
295
342
|
const depthLabels: Record<string, string> = {
|
|
296
|
-
|
|
297
|
-
|
|
343
|
+
local: 'Fast (pattern matching)',
|
|
344
|
+
verified: 'Verified (AI-assisted)',
|
|
298
345
|
deep: 'Deep (full semantic)',
|
|
299
346
|
}
|
|
300
347
|
md += `| Scan depth | ${depthLabels[scanDepth] || scanDepth} |\n`
|
|
@@ -357,7 +404,7 @@ export function formatShortStatus(result: ScanResult): string {
|
|
|
357
404
|
}
|
|
358
405
|
|
|
359
406
|
/**
|
|
360
|
-
* Format as inline annotation for GitHub check run
|
|
407
|
+
* Format as inline annotation for GitHub check run (PRO-82: actionable output)
|
|
361
408
|
*/
|
|
362
409
|
export function formatAnnotation(finding: Vulnerability): {
|
|
363
410
|
path: string
|
|
@@ -371,12 +418,33 @@ export function formatAnnotation(finding: Vulnerability): {
|
|
|
371
418
|
finding.severity === 'critical' || finding.severity === 'high' ? 'failure' :
|
|
372
419
|
finding.severity === 'medium' ? 'warning' : 'notice'
|
|
373
420
|
|
|
421
|
+
// Build actionable message
|
|
422
|
+
let message = ''
|
|
423
|
+
|
|
424
|
+
// Impact first (why this matters)
|
|
425
|
+
if (finding.impact) {
|
|
426
|
+
message += `Impact: ${finding.impact}\n\n`
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
// Description
|
|
430
|
+
message += finding.description
|
|
431
|
+
|
|
432
|
+
// Fix steps or legacy suggestedFix
|
|
433
|
+
if (finding.fixSteps && finding.fixSteps.length > 0) {
|
|
434
|
+
message += '\n\n💡 Fix:\n'
|
|
435
|
+
finding.fixSteps.forEach((step, i) => {
|
|
436
|
+
message += `${i + 1}. ${step}\n`
|
|
437
|
+
})
|
|
438
|
+
} else if (finding.suggestedFix) {
|
|
439
|
+
message += `\n\n💡 Fix: ${finding.suggestedFix}`
|
|
440
|
+
}
|
|
441
|
+
|
|
374
442
|
return {
|
|
375
443
|
path: finding.filePath,
|
|
376
444
|
start_line: finding.lineNumber,
|
|
377
445
|
end_line: finding.lineNumber,
|
|
378
446
|
annotation_level: level,
|
|
379
447
|
title: `${SEVERITY_BADGE[finding.severity]} ${finding.title}`,
|
|
380
|
-
message
|
|
448
|
+
message,
|
|
381
449
|
}
|
|
382
450
|
}
|
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* IDE Integration Tests
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { existsSync, mkdirSync, writeFileSync, rmSync } from 'fs'
|
|
6
|
+
import { join } from 'path'
|
|
7
|
+
import { tmpdir } from 'os'
|
|
8
|
+
import {
|
|
9
|
+
formatCursorRules,
|
|
10
|
+
formatWindsurfRules,
|
|
11
|
+
formatClaudeCodeSection,
|
|
12
|
+
detectIDEConfigs,
|
|
13
|
+
writeIDEFile,
|
|
14
|
+
updateClaudeMdSection,
|
|
15
|
+
clearIDEFiles,
|
|
16
|
+
} from '../index'
|
|
17
|
+
import type { ScanResult, Vulnerability } from '../../../types'
|
|
18
|
+
|
|
19
|
+
const createMockVulnerability = (overrides: Partial<Vulnerability> = {}): Vulnerability => ({
|
|
20
|
+
id: 'test-vuln-1',
|
|
21
|
+
filePath: 'src/api/users.ts',
|
|
22
|
+
lineNumber: 42,
|
|
23
|
+
lineContent: 'const query = `SELECT * FROM users WHERE id = ${userId}`',
|
|
24
|
+
severity: 'high',
|
|
25
|
+
category: 'sql_injection',
|
|
26
|
+
title: 'SQL Injection Vulnerability',
|
|
27
|
+
description: 'User input is directly concatenated into SQL query without parameterization.',
|
|
28
|
+
suggestedFix: 'Use parameterized queries instead of string concatenation.',
|
|
29
|
+
confidence: 'high',
|
|
30
|
+
layer: 2,
|
|
31
|
+
fixSteps: [
|
|
32
|
+
'Replace string concatenation with parameterized query',
|
|
33
|
+
'Use prepared statements or an ORM',
|
|
34
|
+
],
|
|
35
|
+
...overrides,
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
const createMockScanResult = (vulnerabilities: Vulnerability[] = []): ScanResult => ({
|
|
39
|
+
repoName: 'test-repo',
|
|
40
|
+
repoUrl: 'https://github.com/test/test-repo',
|
|
41
|
+
branch: 'main',
|
|
42
|
+
filesScanned: 50,
|
|
43
|
+
filesSkipped: 5,
|
|
44
|
+
vulnerabilities,
|
|
45
|
+
severityCounts: {
|
|
46
|
+
critical: vulnerabilities.filter(v => v.severity === 'critical').length,
|
|
47
|
+
high: vulnerabilities.filter(v => v.severity === 'high').length,
|
|
48
|
+
medium: vulnerabilities.filter(v => v.severity === 'medium').length,
|
|
49
|
+
low: vulnerabilities.filter(v => v.severity === 'low').length,
|
|
50
|
+
info: vulnerabilities.filter(v => v.severity === 'info').length,
|
|
51
|
+
},
|
|
52
|
+
categoryCounts: {},
|
|
53
|
+
hasBlockingIssues: vulnerabilities.some(v => v.severity === 'critical' || v.severity === 'high'),
|
|
54
|
+
scanDuration: 1500,
|
|
55
|
+
timestamp: '2024-01-15T10:30:00.000Z',
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
describe('formatCursorRules', () => {
|
|
59
|
+
it('should generate MDC format with frontmatter', () => {
|
|
60
|
+
const vuln = createMockVulnerability()
|
|
61
|
+
const result = createMockScanResult([vuln])
|
|
62
|
+
|
|
63
|
+
const output = formatCursorRules(result)
|
|
64
|
+
|
|
65
|
+
// Check frontmatter
|
|
66
|
+
expect(output).toContain('---')
|
|
67
|
+
expect(output).toContain('description: Oculum Security Findings')
|
|
68
|
+
expect(output).toContain('alwaysApply: false')
|
|
69
|
+
expect(output).toContain('---')
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
it('should include glob patterns for affected files', () => {
|
|
73
|
+
const vulns = [
|
|
74
|
+
createMockVulnerability({ filePath: 'src/api/users.ts' }),
|
|
75
|
+
createMockVulnerability({ id: 'v2', filePath: 'src/api/orders.ts' }),
|
|
76
|
+
createMockVulnerability({ id: 'v3', filePath: 'src/config/db.ts' }),
|
|
77
|
+
]
|
|
78
|
+
const result = createMockScanResult(vulns)
|
|
79
|
+
|
|
80
|
+
const output = formatCursorRules(result)
|
|
81
|
+
|
|
82
|
+
// Should include globs for affected directories
|
|
83
|
+
expect(output).toContain('globs:')
|
|
84
|
+
expect(output).toContain('src/api/**')
|
|
85
|
+
expect(output).toContain('src/config/**')
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
it('should include security findings', () => {
|
|
89
|
+
const vuln = createMockVulnerability()
|
|
90
|
+
const result = createMockScanResult([vuln])
|
|
91
|
+
|
|
92
|
+
const output = formatCursorRules(result)
|
|
93
|
+
|
|
94
|
+
expect(output).toContain('SQL Injection Vulnerability')
|
|
95
|
+
expect(output).toContain('src/api/users.ts:42')
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
it('should include fix instructions', () => {
|
|
99
|
+
const vuln = createMockVulnerability()
|
|
100
|
+
const result = createMockScanResult([vuln])
|
|
101
|
+
|
|
102
|
+
const output = formatCursorRules(result)
|
|
103
|
+
|
|
104
|
+
expect(output).toContain('DO NOT')
|
|
105
|
+
// The fix shows example code with '?' instead of the word 'parameterized'
|
|
106
|
+
expect(output).toContain('GOOD')
|
|
107
|
+
expect(output).toContain("'SELECT * FROM users WHERE id = ?'")
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
it('should return minimal output for zero findings', () => {
|
|
111
|
+
const result = createMockScanResult([])
|
|
112
|
+
|
|
113
|
+
const output = formatCursorRules(result)
|
|
114
|
+
|
|
115
|
+
expect(output).toContain('description: Oculum Security Findings')
|
|
116
|
+
expect(output).toContain('No security issues found')
|
|
117
|
+
})
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
describe('formatWindsurfRules', () => {
|
|
121
|
+
it('should generate markdown security rules', () => {
|
|
122
|
+
const vuln = createMockVulnerability()
|
|
123
|
+
const result = createMockScanResult([vuln])
|
|
124
|
+
|
|
125
|
+
const output = formatWindsurfRules(result)
|
|
126
|
+
|
|
127
|
+
expect(output).toContain('# Oculum Security Rules')
|
|
128
|
+
expect(output).toContain('## Security Issues')
|
|
129
|
+
})
|
|
130
|
+
|
|
131
|
+
it('should list findings by file', () => {
|
|
132
|
+
const vulns = [
|
|
133
|
+
createMockVulnerability({ filePath: 'src/api/users.ts' }),
|
|
134
|
+
createMockVulnerability({ id: 'v2', filePath: 'src/api/orders.ts' }),
|
|
135
|
+
]
|
|
136
|
+
const result = createMockScanResult(vulns)
|
|
137
|
+
|
|
138
|
+
const output = formatWindsurfRules(result)
|
|
139
|
+
|
|
140
|
+
expect(output).toContain('src/api/users.ts')
|
|
141
|
+
expect(output).toContain('src/api/orders.ts')
|
|
142
|
+
expect(output).toContain('Line 42')
|
|
143
|
+
})
|
|
144
|
+
|
|
145
|
+
it('should include code patterns section', () => {
|
|
146
|
+
const vuln = createMockVulnerability()
|
|
147
|
+
const result = createMockScanResult([vuln])
|
|
148
|
+
|
|
149
|
+
const output = formatWindsurfRules(result)
|
|
150
|
+
|
|
151
|
+
expect(output).toContain('## Code Patterns')
|
|
152
|
+
expect(output).toContain('ALWAYS')
|
|
153
|
+
})
|
|
154
|
+
})
|
|
155
|
+
|
|
156
|
+
describe('formatClaudeCodeSection', () => {
|
|
157
|
+
it('should return section with markers', () => {
|
|
158
|
+
const vuln = createMockVulnerability()
|
|
159
|
+
const result = createMockScanResult([vuln])
|
|
160
|
+
|
|
161
|
+
const output = formatClaudeCodeSection(result)
|
|
162
|
+
|
|
163
|
+
expect(output).toContain('<!-- OCULUM_SECURITY_START -->')
|
|
164
|
+
expect(output).toContain('<!-- OCULUM_SECURITY_END -->')
|
|
165
|
+
})
|
|
166
|
+
|
|
167
|
+
it('should include security issues', () => {
|
|
168
|
+
const vuln = createMockVulnerability()
|
|
169
|
+
const result = createMockScanResult([vuln])
|
|
170
|
+
|
|
171
|
+
const output = formatClaudeCodeSection(result)
|
|
172
|
+
|
|
173
|
+
expect(output).toContain('## Security Issues')
|
|
174
|
+
expect(output).toContain('SQL Injection')
|
|
175
|
+
expect(output).toContain('src/api/users.ts:42')
|
|
176
|
+
})
|
|
177
|
+
|
|
178
|
+
it('should include verification instructions', () => {
|
|
179
|
+
const vuln = createMockVulnerability()
|
|
180
|
+
const result = createMockScanResult([vuln])
|
|
181
|
+
|
|
182
|
+
const output = formatClaudeCodeSection(result)
|
|
183
|
+
|
|
184
|
+
expect(output).toContain('oculum scan')
|
|
185
|
+
})
|
|
186
|
+
})
|
|
187
|
+
|
|
188
|
+
describe('detectIDEConfigs', () => {
|
|
189
|
+
const testDir = join(tmpdir(), 'oculum-ide-detect-test-' + Date.now())
|
|
190
|
+
|
|
191
|
+
beforeAll(() => {
|
|
192
|
+
mkdirSync(testDir, { recursive: true })
|
|
193
|
+
})
|
|
194
|
+
|
|
195
|
+
afterAll(() => {
|
|
196
|
+
try {
|
|
197
|
+
rmSync(testDir, { recursive: true, force: true })
|
|
198
|
+
} catch {
|
|
199
|
+
// Ignore cleanup errors
|
|
200
|
+
}
|
|
201
|
+
})
|
|
202
|
+
|
|
203
|
+
it('should detect .cursor directory', () => {
|
|
204
|
+
// Create .cursor directory
|
|
205
|
+
mkdirSync(join(testDir, '.cursor'), { recursive: true })
|
|
206
|
+
|
|
207
|
+
const detected = detectIDEConfigs(testDir)
|
|
208
|
+
|
|
209
|
+
expect(detected).toContain('cursor')
|
|
210
|
+
})
|
|
211
|
+
|
|
212
|
+
it('should detect CLAUDE.md file', () => {
|
|
213
|
+
// Create CLAUDE.md file
|
|
214
|
+
writeFileSync(join(testDir, 'CLAUDE.md'), '# Claude Code\n')
|
|
215
|
+
|
|
216
|
+
const detected = detectIDEConfigs(testDir)
|
|
217
|
+
|
|
218
|
+
expect(detected).toContain('claude-code')
|
|
219
|
+
})
|
|
220
|
+
|
|
221
|
+
it('should return array of detected IDEs', () => {
|
|
222
|
+
const detected = detectIDEConfigs(testDir)
|
|
223
|
+
|
|
224
|
+
expect(Array.isArray(detected)).toBe(true)
|
|
225
|
+
})
|
|
226
|
+
})
|
|
227
|
+
|
|
228
|
+
describe('IDE file operations', () => {
|
|
229
|
+
const testDir = join(tmpdir(), 'oculum-ide-ops-test-' + Date.now())
|
|
230
|
+
|
|
231
|
+
beforeAll(() => {
|
|
232
|
+
mkdirSync(testDir, { recursive: true })
|
|
233
|
+
})
|
|
234
|
+
|
|
235
|
+
afterAll(() => {
|
|
236
|
+
try {
|
|
237
|
+
rmSync(testDir, { recursive: true, force: true })
|
|
238
|
+
} catch {
|
|
239
|
+
// Ignore cleanup errors
|
|
240
|
+
}
|
|
241
|
+
})
|
|
242
|
+
|
|
243
|
+
it('should write IDE file and create directories', () => {
|
|
244
|
+
const result = writeIDEFile(
|
|
245
|
+
testDir,
|
|
246
|
+
'.cursor/rules/security.mdc',
|
|
247
|
+
'# Security Rules'
|
|
248
|
+
)
|
|
249
|
+
|
|
250
|
+
expect(result.success).toBe(true)
|
|
251
|
+
expect(existsSync(join(testDir, '.cursor/rules/security.mdc'))).toBe(true)
|
|
252
|
+
})
|
|
253
|
+
|
|
254
|
+
it('should update CLAUDE.md section between markers', () => {
|
|
255
|
+
// Create initial CLAUDE.md
|
|
256
|
+
const initialContent = `# Project
|
|
257
|
+
|
|
258
|
+
Some existing content.
|
|
259
|
+
|
|
260
|
+
<!-- OCULUM_SECURITY_START -->
|
|
261
|
+
Old security content
|
|
262
|
+
<!-- OCULUM_SECURITY_END -->
|
|
263
|
+
|
|
264
|
+
More content.
|
|
265
|
+
`
|
|
266
|
+
writeFileSync(join(testDir, 'CLAUDE.md'), initialContent)
|
|
267
|
+
|
|
268
|
+
// Update section
|
|
269
|
+
const result = updateClaudeMdSection(
|
|
270
|
+
testDir,
|
|
271
|
+
'<!-- OCULUM_SECURITY_START -->\nNew security content\n<!-- OCULUM_SECURITY_END -->'
|
|
272
|
+
)
|
|
273
|
+
|
|
274
|
+
expect(result.success).toBe(true)
|
|
275
|
+
|
|
276
|
+
// Read and verify
|
|
277
|
+
const { readFileSync } = require('fs')
|
|
278
|
+
const updated = readFileSync(join(testDir, 'CLAUDE.md'), 'utf-8')
|
|
279
|
+
|
|
280
|
+
expect(updated).toContain('New security content')
|
|
281
|
+
expect(updated).not.toContain('Old security content')
|
|
282
|
+
expect(updated).toContain('Some existing content')
|
|
283
|
+
expect(updated).toContain('More content')
|
|
284
|
+
})
|
|
285
|
+
|
|
286
|
+
it('should append section if no markers exist', () => {
|
|
287
|
+
// Create CLAUDE.md without markers
|
|
288
|
+
const noMarkersDir = join(testDir, 'no-markers')
|
|
289
|
+
mkdirSync(noMarkersDir, { recursive: true })
|
|
290
|
+
writeFileSync(join(noMarkersDir, 'CLAUDE.md'), '# Project\n\nSome content.\n')
|
|
291
|
+
|
|
292
|
+
const result = updateClaudeMdSection(
|
|
293
|
+
noMarkersDir,
|
|
294
|
+
'<!-- OCULUM_SECURITY_START -->\nSecurity content\n<!-- OCULUM_SECURITY_END -->'
|
|
295
|
+
)
|
|
296
|
+
|
|
297
|
+
expect(result.success).toBe(true)
|
|
298
|
+
|
|
299
|
+
const { readFileSync } = require('fs')
|
|
300
|
+
const updated = readFileSync(join(noMarkersDir, 'CLAUDE.md'), 'utf-8')
|
|
301
|
+
|
|
302
|
+
expect(updated).toContain('Security content')
|
|
303
|
+
expect(updated).toContain('Some content')
|
|
304
|
+
})
|
|
305
|
+
|
|
306
|
+
it('should clear IDE files', () => {
|
|
307
|
+
// Create some IDE files
|
|
308
|
+
const clearDir = join(testDir, 'clear-test')
|
|
309
|
+
mkdirSync(join(clearDir, '.cursor/rules'), { recursive: true })
|
|
310
|
+
writeFileSync(join(clearDir, '.cursor/rules/security.mdc'), 'content')
|
|
311
|
+
writeFileSync(join(clearDir, '.windsurfrules'), 'content')
|
|
312
|
+
|
|
313
|
+
const result = clearIDEFiles(clearDir)
|
|
314
|
+
|
|
315
|
+
expect(result.success).toBe(true)
|
|
316
|
+
expect(existsSync(join(clearDir, '.cursor/rules/security.mdc'))).toBe(false)
|
|
317
|
+
expect(existsSync(join(clearDir, '.windsurfrules'))).toBe(false)
|
|
318
|
+
})
|
|
319
|
+
})
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Claude Code IDE Integration
|
|
3
|
+
* Generates CLAUDE.md section format
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { ScanResult, Vulnerability, VulnerabilityCategory } from '../../types'
|
|
7
|
+
import { sortBySeverity } from '../grouping'
|
|
8
|
+
|
|
9
|
+
/** Start marker for Oculum section in CLAUDE.md */
|
|
10
|
+
export const OCULUM_SECTION_START = '<!-- OCULUM_SECURITY_START -->'
|
|
11
|
+
|
|
12
|
+
/** End marker for Oculum section in CLAUDE.md */
|
|
13
|
+
export const OCULUM_SECTION_END = '<!-- OCULUM_SECURITY_END -->'
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Get "ALWAYS" rule for a category
|
|
17
|
+
*/
|
|
18
|
+
function getAlwaysRule(category: VulnerabilityCategory): string | null {
|
|
19
|
+
const rules: Partial<Record<VulnerabilityCategory, string>> = {
|
|
20
|
+
sql_injection: 'Use parameterized queries for all database operations',
|
|
21
|
+
xss: 'Escape or sanitize user input before rendering in HTML',
|
|
22
|
+
hardcoded_secret: 'Use environment variables for secrets and API keys',
|
|
23
|
+
high_entropy_string: 'Store credentials in secure vaults, not in code',
|
|
24
|
+
missing_auth: 'Add authentication middleware to API endpoints',
|
|
25
|
+
dangerous_function: 'Avoid eval() and exec() - use safe alternatives',
|
|
26
|
+
command_injection: 'Sanitize all input passed to shell commands',
|
|
27
|
+
data_exposure: 'Never log sensitive data or expose it in responses',
|
|
28
|
+
weak_crypto: 'Use modern cryptographic algorithms (SHA-256+, AES-256)',
|
|
29
|
+
ai_prompt_injection: 'Sanitize user input before including in prompts',
|
|
30
|
+
ai_unsafe_execution: 'Validate AI-generated code before execution',
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return rules[category] || null
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Format scan result as CLAUDE.md section
|
|
38
|
+
*
|
|
39
|
+
* @param result - The scan result to format
|
|
40
|
+
* @returns Markdown string with markers for CLAUDE.md
|
|
41
|
+
*/
|
|
42
|
+
export function formatClaudeCodeSection(result: ScanResult): string {
|
|
43
|
+
const { vulnerabilities, timestamp } = result
|
|
44
|
+
|
|
45
|
+
// Sort by severity
|
|
46
|
+
const sorted = sortBySeverity(vulnerabilities)
|
|
47
|
+
|
|
48
|
+
let md = ''
|
|
49
|
+
|
|
50
|
+
// Section markers
|
|
51
|
+
md += `${OCULUM_SECTION_START}\n`
|
|
52
|
+
md += `## Security Issues (Auto-Generated)\n\n`
|
|
53
|
+
md += `> Last scan: ${timestamp}\n\n`
|
|
54
|
+
|
|
55
|
+
// No findings case
|
|
56
|
+
if (vulnerabilities.length === 0) {
|
|
57
|
+
md += `No security issues detected.\n\n`
|
|
58
|
+
md += `Run \`oculum scan\` to scan for vulnerabilities.\n`
|
|
59
|
+
md += `${OCULUM_SECTION_END}\n`
|
|
60
|
+
return md
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Summary
|
|
64
|
+
const { severityCounts, hasBlockingIssues } = result
|
|
65
|
+
if (hasBlockingIssues) {
|
|
66
|
+
md += `**BLOCKING ISSUES:** ${severityCounts.critical + severityCounts.high} issues must be fixed.\n\n`
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// DO NOT section - list findings
|
|
70
|
+
md += `**DO NOT** ignore these findings:\n\n`
|
|
71
|
+
|
|
72
|
+
let count = 0
|
|
73
|
+
for (const vuln of sorted.slice(0, 10)) {
|
|
74
|
+
count++
|
|
75
|
+
const severityLabel =
|
|
76
|
+
vuln.severity === 'critical' ? 'CRITICAL' :
|
|
77
|
+
vuln.severity === 'high' ? 'HIGH' :
|
|
78
|
+
vuln.severity.toUpperCase()
|
|
79
|
+
|
|
80
|
+
md += `${count}. **${vuln.title}** [${severityLabel}] in \`${vuln.filePath}:${vuln.lineNumber}\`\n`
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (sorted.length > 10) {
|
|
84
|
+
md += `\n... and ${sorted.length - 10} more issues.\n`
|
|
85
|
+
}
|
|
86
|
+
md += '\n'
|
|
87
|
+
|
|
88
|
+
// ALWAYS section - best practices
|
|
89
|
+
const alwaysRules = new Set<string>()
|
|
90
|
+
for (const vuln of vulnerabilities) {
|
|
91
|
+
const rule = getAlwaysRule(vuln.category)
|
|
92
|
+
if (rule) {
|
|
93
|
+
alwaysRules.add(rule)
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (alwaysRules.size > 0) {
|
|
98
|
+
md += `**ALWAYS:**\n`
|
|
99
|
+
for (const rule of alwaysRules) {
|
|
100
|
+
md += `- ${rule}\n`
|
|
101
|
+
}
|
|
102
|
+
md += '\n'
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Verification instructions
|
|
106
|
+
md += `Run \`oculum scan\` to verify fixes.\n`
|
|
107
|
+
md += `${OCULUM_SECTION_END}\n`
|
|
108
|
+
|
|
109
|
+
return md
|
|
110
|
+
}
|