@oculum/scanner 1.0.9 → 1.0.11
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/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/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 +1 -1
- package/dist/formatters/github-comment.d.ts.map +1 -1
- package/dist/formatters/github-comment.js +75 -11
- package/dist/formatters/github-comment.js.map +1 -1
- package/dist/formatters/index.d.ts +1 -1
- package/dist/formatters/index.d.ts.map +1 -1
- package/dist/formatters/index.js +4 -1
- package/dist/formatters/index.js.map +1 -1
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +155 -16
- package/dist/index.js.map +1 -1
- package/dist/layer1/config-audit.d.ts.map +1 -1
- package/dist/layer1/config-audit.js +20 -3
- package/dist/layer1/config-audit.js.map +1 -1
- package/dist/layer1/config-mcp-audit.d.ts +20 -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/index.d.ts +1 -0
- package/dist/layer1/index.d.ts.map +1 -1
- package/dist/layer1/index.js +9 -1
- package/dist/layer1/index.js.map +1 -1
- package/dist/layer2/ai-agent-tools.d.ts.map +1 -1
- package/dist/layer2/ai-agent-tools.js +303 -0
- package/dist/layer2/ai-agent-tools.js.map +1 -1
- package/dist/layer2/ai-endpoint-protection.d.ts.map +1 -1
- package/dist/layer2/ai-endpoint-protection.js +17 -3
- package/dist/layer2/ai-endpoint-protection.js.map +1 -1
- package/dist/layer2/ai-execution-sinks.d.ts.map +1 -1
- package/dist/layer2/ai-execution-sinks.js +462 -12
- package/dist/layer2/ai-execution-sinks.js.map +1 -1
- package/dist/layer2/ai-fingerprinting.d.ts.map +1 -1
- package/dist/layer2/ai-fingerprinting.js +3 -0
- package/dist/layer2/ai-fingerprinting.js.map +1 -1
- package/dist/layer2/ai-mcp-security.d.ts +17 -0
- package/dist/layer2/ai-mcp-security.d.ts.map +1 -0
- package/dist/layer2/ai-mcp-security.js +679 -0
- package/dist/layer2/ai-mcp-security.js.map +1 -0
- package/dist/layer2/ai-package-hallucination.d.ts +19 -0
- package/dist/layer2/ai-package-hallucination.d.ts.map +1 -0
- package/dist/layer2/ai-package-hallucination.js +696 -0
- package/dist/layer2/ai-package-hallucination.js.map +1 -0
- package/dist/layer2/ai-prompt-hygiene.d.ts.map +1 -1
- package/dist/layer2/ai-prompt-hygiene.js +495 -9
- package/dist/layer2/ai-prompt-hygiene.js.map +1 -1
- package/dist/layer2/ai-rag-safety.d.ts.map +1 -1
- package/dist/layer2/ai-rag-safety.js +372 -1
- package/dist/layer2/ai-rag-safety.js.map +1 -1
- package/dist/layer2/auth-antipatterns.d.ts.map +1 -1
- package/dist/layer2/auth-antipatterns.js +4 -0
- package/dist/layer2/auth-antipatterns.js.map +1 -1
- package/dist/layer2/byok-patterns.d.ts.map +1 -1
- package/dist/layer2/byok-patterns.js +3 -0
- 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 +29 -0
- package/dist/layer2/dangerous-functions/dom-xss.d.ts.map +1 -0
- package/dist/layer2/dangerous-functions/dom-xss.js +179 -0
- package/dist/layer2/dangerous-functions/dom-xss.js.map +1 -0
- package/dist/layer2/dangerous-functions/index.d.ts +13 -0
- package/dist/layer2/dangerous-functions/index.d.ts.map +1 -0
- package/dist/layer2/dangerous-functions/index.js +621 -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 +61 -0
- package/dist/layer2/dangerous-functions/math-random.d.ts.map +1 -0
- package/dist/layer2/dangerous-functions/math-random.js +459 -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 +161 -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 +23 -0
- package/dist/layer2/dangerous-functions/utils/control-flow.d.ts.map +1 -0
- package/dist/layer2/dangerous-functions/utils/control-flow.js +149 -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 +124 -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 +89 -0
- package/dist/layer2/dangerous-functions/utils/schema-validation.js.map +1 -0
- package/dist/layer2/data-exposure.d.ts.map +1 -1
- package/dist/layer2/data-exposure.js +3 -0
- package/dist/layer2/data-exposure.js.map +1 -1
- package/dist/layer2/framework-checks.d.ts.map +1 -1
- package/dist/layer2/framework-checks.js +3 -0
- package/dist/layer2/framework-checks.js.map +1 -1
- package/dist/layer2/index.d.ts +3 -0
- package/dist/layer2/index.d.ts.map +1 -1
- package/dist/layer2/index.js +61 -2
- package/dist/layer2/index.js.map +1 -1
- package/dist/layer2/logic-gates.d.ts.map +1 -1
- package/dist/layer2/logic-gates.js +4 -0
- package/dist/layer2/logic-gates.js.map +1 -1
- package/dist/layer2/model-supply-chain.d.ts +20 -0
- package/dist/layer2/model-supply-chain.d.ts.map +1 -0
- package/dist/layer2/model-supply-chain.js +376 -0
- package/dist/layer2/model-supply-chain.js.map +1 -0
- package/dist/layer2/risky-imports.d.ts.map +1 -1
- package/dist/layer2/risky-imports.js +4 -0
- package/dist/layer2/risky-imports.js.map +1 -1
- package/dist/layer2/variables.d.ts.map +1 -1
- package/dist/layer2/variables.js +4 -0
- 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 +188 -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/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 +1 -1
- package/dist/tiers.d.ts.map +1 -1
- package/dist/tiers.js +27 -0
- package/dist/tiers.js.map +1 -1
- package/dist/types.d.ts +62 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/utils/context-helpers.d.ts +4 -0
- package/dist/utils/context-helpers.d.ts.map +1 -1
- package/dist/utils/context-helpers.js +13 -9
- package/dist/utils/context-helpers.js.map +1 -1
- 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 +18 -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__/snapshots/__snapshots__/anthropic-validation-refactor.test.ts.snap +758 -0
- package/src/__tests__/snapshots/__snapshots__/dangerous-functions-refactor.test.ts.snap +503 -0
- package/src/__tests__/snapshots/anthropic-validation-refactor.test.ts +321 -0
- package/src/__tests__/snapshots/dangerous-functions-refactor.test.ts +439 -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/formatters/cli-terminal.ts +444 -41
- package/src/formatters/github-comment.ts +79 -11
- package/src/formatters/index.ts +4 -0
- package/src/index.ts +197 -14
- package/src/layer1/config-audit.ts +24 -3
- package/src/layer1/config-mcp-audit.ts +276 -0
- package/src/layer1/index.ts +16 -6
- package/src/layer2/ai-agent-tools.ts +336 -0
- package/src/layer2/ai-endpoint-protection.ts +16 -3
- package/src/layer2/ai-execution-sinks.ts +516 -12
- package/src/layer2/ai-fingerprinting.ts +5 -1
- package/src/layer2/ai-mcp-security.ts +730 -0
- package/src/layer2/ai-package-hallucination.ts +791 -0
- package/src/layer2/ai-prompt-hygiene.ts +547 -9
- package/src/layer2/ai-rag-safety.ts +382 -3
- package/src/layer2/auth-antipatterns.ts +5 -0
- package/src/layer2/byok-patterns.ts +5 -1
- package/src/layer2/dangerous-functions/child-process.ts +98 -0
- package/src/layer2/dangerous-functions/dom-xss.ts +220 -0
- package/src/layer2/dangerous-functions/index.ts +949 -0
- package/src/layer2/dangerous-functions/json-parse.ts +385 -0
- package/src/layer2/dangerous-functions/math-random.ts +537 -0
- package/src/layer2/dangerous-functions/patterns.ts +174 -0
- package/src/layer2/dangerous-functions/request-validation.ts +145 -0
- package/src/layer2/dangerous-functions/utils/control-flow.ts +162 -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 +91 -0
- package/src/layer2/data-exposure.ts +5 -1
- package/src/layer2/framework-checks.ts +5 -0
- package/src/layer2/index.ts +63 -1
- package/src/layer2/logic-gates.ts +5 -0
- package/src/layer2/model-supply-chain.ts +456 -0
- package/src/layer2/risky-imports.ts +5 -0
- package/src/layer2/variables.ts +5 -0
- package/src/layer3/__tests__/osv-check.test.ts +384 -0
- package/src/layer3/anthropic/auto-dismiss.ts +212 -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/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 +36 -0
- package/src/types.ts +90 -0
- package/src/utils/context-helpers.ts +13 -9
- 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
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Inline Suppression Parser Tests
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
parseInlineSuppressions,
|
|
7
|
+
isLineSuppressed,
|
|
8
|
+
extractSuppressionReasons,
|
|
9
|
+
generateSuppressionComment,
|
|
10
|
+
} from '../inline-parser'
|
|
11
|
+
|
|
12
|
+
describe('Inline Suppression Parser', () => {
|
|
13
|
+
describe('parseInlineSuppressions', () => {
|
|
14
|
+
it('should parse next-line suppression comments', () => {
|
|
15
|
+
const content = `
|
|
16
|
+
const x = 1;
|
|
17
|
+
// oculum-ignore-next-line: false positive
|
|
18
|
+
const secret = "abc123";
|
|
19
|
+
const y = 2;
|
|
20
|
+
`
|
|
21
|
+
const suppressions = parseInlineSuppressions(content)
|
|
22
|
+
|
|
23
|
+
// Line 4 (const secret) should be suppressed by line 3's comment
|
|
24
|
+
expect(suppressions.has(4)).toBe(true)
|
|
25
|
+
expect(suppressions.get(4)?.reason).toBe('false positive')
|
|
26
|
+
expect(suppressions.get(4)?.type).toBe('next-line')
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
it('should parse same-line suppression comments', () => {
|
|
30
|
+
const content = `
|
|
31
|
+
const x = 1;
|
|
32
|
+
const secret = "abc123"; // oculum-ignore: known safe
|
|
33
|
+
const y = 2;
|
|
34
|
+
`
|
|
35
|
+
const suppressions = parseInlineSuppressions(content)
|
|
36
|
+
|
|
37
|
+
// Line 3 should be suppressed
|
|
38
|
+
expect(suppressions.has(3)).toBe(true)
|
|
39
|
+
expect(suppressions.get(3)?.reason).toBe('known safe')
|
|
40
|
+
expect(suppressions.get(3)?.type).toBe('same-line')
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
it('should parse block suppressions', () => {
|
|
44
|
+
const content = `
|
|
45
|
+
const x = 1;
|
|
46
|
+
/* oculum-ignore-block: legacy code */
|
|
47
|
+
const secret1 = "abc";
|
|
48
|
+
const secret2 = "def";
|
|
49
|
+
/* oculum-ignore-block-end */
|
|
50
|
+
const y = 2;
|
|
51
|
+
`
|
|
52
|
+
const suppressions = parseInlineSuppressions(content)
|
|
53
|
+
|
|
54
|
+
// Lines 4 and 5 should be suppressed by the block
|
|
55
|
+
expect(suppressions.has(4)).toBe(true)
|
|
56
|
+
expect(suppressions.has(5)).toBe(true)
|
|
57
|
+
expect(suppressions.get(4)?.reason).toBe('legacy code')
|
|
58
|
+
expect(suppressions.get(5)?.reason).toBe('legacy code')
|
|
59
|
+
|
|
60
|
+
// Lines before and after block should not be suppressed
|
|
61
|
+
expect(suppressions.has(2)).toBe(false)
|
|
62
|
+
expect(suppressions.has(7)).toBe(false)
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
it('should parse rule-specific suppressions', () => {
|
|
66
|
+
const content = `
|
|
67
|
+
// oculum-ignore-next-line [hardcoded_secret]: test data
|
|
68
|
+
const secret = "test123";
|
|
69
|
+
`
|
|
70
|
+
const suppressions = parseInlineSuppressions(content)
|
|
71
|
+
|
|
72
|
+
expect(suppressions.has(3)).toBe(true)
|
|
73
|
+
expect(suppressions.get(3)?.ruleId).toBe('hardcoded_secret')
|
|
74
|
+
expect(suppressions.get(3)?.reason).toBe('test data')
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
it('should support Python-style comments (#)', () => {
|
|
78
|
+
const content = `
|
|
79
|
+
x = 1
|
|
80
|
+
# oculum-ignore-next-line: test data
|
|
81
|
+
secret = "abc123"
|
|
82
|
+
`
|
|
83
|
+
const suppressions = parseInlineSuppressions(content)
|
|
84
|
+
|
|
85
|
+
expect(suppressions.has(4)).toBe(true)
|
|
86
|
+
expect(suppressions.get(4)?.reason).toBe('test data')
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
it('should support SQL-style comments (--)', () => {
|
|
90
|
+
const content = `
|
|
91
|
+
SELECT 1;
|
|
92
|
+
-- oculum-ignore-next-line: intentional
|
|
93
|
+
SELECT password FROM users;
|
|
94
|
+
`
|
|
95
|
+
const suppressions = parseInlineSuppressions(content)
|
|
96
|
+
|
|
97
|
+
expect(suppressions.has(4)).toBe(true)
|
|
98
|
+
expect(suppressions.get(4)?.reason).toBe('intentional')
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
it('should handle empty files', () => {
|
|
102
|
+
const suppressions = parseInlineSuppressions('')
|
|
103
|
+
expect(suppressions.size).toBe(0)
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
it('should not match partial oculum-ignore patterns', () => {
|
|
107
|
+
const content = `
|
|
108
|
+
// This is not oculum-ignore but mentions it
|
|
109
|
+
const x = "oculum-ignore";
|
|
110
|
+
`
|
|
111
|
+
const suppressions = parseInlineSuppressions(content)
|
|
112
|
+
expect(suppressions.size).toBe(0)
|
|
113
|
+
})
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
describe('isLineSuppressed', () => {
|
|
117
|
+
it('should return suppression for suppressed line', () => {
|
|
118
|
+
const content = `
|
|
119
|
+
// oculum-ignore-next-line: reason
|
|
120
|
+
const x = 1;
|
|
121
|
+
`
|
|
122
|
+
const suppressions = parseInlineSuppressions(content)
|
|
123
|
+
const result = isLineSuppressed(suppressions, 3)
|
|
124
|
+
|
|
125
|
+
expect(result).not.toBeNull()
|
|
126
|
+
expect(result?.reason).toBe('reason')
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
it('should return null for non-suppressed line', () => {
|
|
130
|
+
const content = `
|
|
131
|
+
// oculum-ignore-next-line: reason
|
|
132
|
+
const x = 1;
|
|
133
|
+
const y = 2;
|
|
134
|
+
`
|
|
135
|
+
const suppressions = parseInlineSuppressions(content)
|
|
136
|
+
const result = isLineSuppressed(suppressions, 4)
|
|
137
|
+
|
|
138
|
+
expect(result).toBeNull()
|
|
139
|
+
})
|
|
140
|
+
|
|
141
|
+
it('should respect rule-specific suppressions', () => {
|
|
142
|
+
const content = `
|
|
143
|
+
// oculum-ignore-next-line [hardcoded_secret]: reason
|
|
144
|
+
const x = 1;
|
|
145
|
+
`
|
|
146
|
+
const suppressions = parseInlineSuppressions(content)
|
|
147
|
+
|
|
148
|
+
// Should match hardcoded_secret
|
|
149
|
+
expect(isLineSuppressed(suppressions, 3, 'hardcoded_secret')).not.toBeNull()
|
|
150
|
+
|
|
151
|
+
// Should not match other categories
|
|
152
|
+
expect(isLineSuppressed(suppressions, 3, 'high_entropy_string')).toBeNull()
|
|
153
|
+
|
|
154
|
+
// Should match if no category specified
|
|
155
|
+
expect(isLineSuppressed(suppressions, 3)).not.toBeNull()
|
|
156
|
+
})
|
|
157
|
+
})
|
|
158
|
+
|
|
159
|
+
describe('extractSuppressionReasons', () => {
|
|
160
|
+
it('should extract all unique suppression reasons', () => {
|
|
161
|
+
const content = `
|
|
162
|
+
// oculum-ignore-next-line: reason 1
|
|
163
|
+
const a = 1;
|
|
164
|
+
// oculum-ignore-next-line: reason 2
|
|
165
|
+
const b = 2;
|
|
166
|
+
`
|
|
167
|
+
const reasons = extractSuppressionReasons(content)
|
|
168
|
+
|
|
169
|
+
expect(reasons).toHaveLength(2)
|
|
170
|
+
expect(reasons.map(r => r.reason)).toContain('reason 1')
|
|
171
|
+
expect(reasons.map(r => r.reason)).toContain('reason 2')
|
|
172
|
+
})
|
|
173
|
+
|
|
174
|
+
it('should not duplicate block suppression reasons', () => {
|
|
175
|
+
const content = `
|
|
176
|
+
/* oculum-ignore-block: block reason */
|
|
177
|
+
const a = 1;
|
|
178
|
+
const b = 2;
|
|
179
|
+
/* oculum-ignore-block-end */
|
|
180
|
+
`
|
|
181
|
+
const reasons = extractSuppressionReasons(content)
|
|
182
|
+
|
|
183
|
+
// Should only have one entry for the block, not one per line
|
|
184
|
+
expect(reasons).toHaveLength(1)
|
|
185
|
+
expect(reasons[0].reason).toBe('block reason')
|
|
186
|
+
})
|
|
187
|
+
})
|
|
188
|
+
|
|
189
|
+
describe('generateSuppressionComment', () => {
|
|
190
|
+
it('should generate next-line comment', () => {
|
|
191
|
+
const comment = generateSuppressionComment('false positive')
|
|
192
|
+
expect(comment).toBe('// oculum-ignore-next-line: false positive')
|
|
193
|
+
})
|
|
194
|
+
|
|
195
|
+
it('should generate same-line comment', () => {
|
|
196
|
+
const comment = generateSuppressionComment('known safe', { type: 'same-line' })
|
|
197
|
+
expect(comment).toBe('// oculum-ignore: known safe')
|
|
198
|
+
})
|
|
199
|
+
|
|
200
|
+
it('should generate comment with rule ID', () => {
|
|
201
|
+
const comment = generateSuppressionComment('test data', { ruleId: 'hardcoded_secret' })
|
|
202
|
+
expect(comment).toBe('// oculum-ignore-next-line [hardcoded_secret]: test data')
|
|
203
|
+
})
|
|
204
|
+
|
|
205
|
+
it('should support different comment styles', () => {
|
|
206
|
+
expect(generateSuppressionComment('reason', { commentStyle: '#' }))
|
|
207
|
+
.toBe('# oculum-ignore-next-line: reason')
|
|
208
|
+
expect(generateSuppressionComment('reason', { commentStyle: '--' }))
|
|
209
|
+
.toBe('-- oculum-ignore-next-line: reason')
|
|
210
|
+
})
|
|
211
|
+
})
|
|
212
|
+
})
|
|
@@ -0,0 +1,415 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SuppressionManager Tests
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { SuppressionManager } from '../manager'
|
|
6
|
+
import type { SuppressionConfig } from '../types'
|
|
7
|
+
import type { Vulnerability, ScanFile } from '../../types'
|
|
8
|
+
|
|
9
|
+
describe('SuppressionManager', () => {
|
|
10
|
+
// Helper to create a mock vulnerability
|
|
11
|
+
function createVulnerability(overrides: Partial<Vulnerability> = {}): Vulnerability {
|
|
12
|
+
return {
|
|
13
|
+
id: 'test-1',
|
|
14
|
+
filePath: 'src/index.ts',
|
|
15
|
+
lineNumber: 10,
|
|
16
|
+
lineContent: 'const secret = "api_key_12345"',
|
|
17
|
+
severity: 'high',
|
|
18
|
+
category: 'hardcoded_secret',
|
|
19
|
+
title: 'Hardcoded Secret',
|
|
20
|
+
description: 'Found a hardcoded secret',
|
|
21
|
+
confidence: 'high',
|
|
22
|
+
layer: 1,
|
|
23
|
+
...overrides,
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Helper to create a mock scan file
|
|
28
|
+
function createScanFile(overrides: Partial<ScanFile> = {}): ScanFile {
|
|
29
|
+
return {
|
|
30
|
+
path: 'src/index.ts',
|
|
31
|
+
content: 'const secret = "api_key_12345";',
|
|
32
|
+
language: 'typescript',
|
|
33
|
+
size: 100,
|
|
34
|
+
...overrides,
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
describe('constructor', () => {
|
|
39
|
+
it('should accept a pre-loaded config', () => {
|
|
40
|
+
const config: SuppressionConfig = {
|
|
41
|
+
version: 1,
|
|
42
|
+
suppressions: {
|
|
43
|
+
rules: [{
|
|
44
|
+
category: 'hardcoded_secret',
|
|
45
|
+
reason: 'test',
|
|
46
|
+
}],
|
|
47
|
+
},
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const manager = new SuppressionManager({
|
|
51
|
+
projectPath: '/fake/path',
|
|
52
|
+
config,
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
expect(manager.hasSuppressions()).toBe(true)
|
|
56
|
+
expect(manager.getAllSuppressions().rules).toHaveLength(1)
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
it('should return default config when path has no config file', () => {
|
|
60
|
+
const manager = new SuppressionManager({
|
|
61
|
+
projectPath: '/nonexistent/path',
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
expect(manager.hasSuppressions()).toBe(false)
|
|
65
|
+
expect(manager.getConfigPath()).toBeUndefined()
|
|
66
|
+
})
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
describe('isPathIgnored', () => {
|
|
70
|
+
it('should match simple patterns', () => {
|
|
71
|
+
const config: SuppressionConfig = {
|
|
72
|
+
version: 1,
|
|
73
|
+
ignore: ['**/*.test.ts', 'node_modules/**'],
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const manager = new SuppressionManager({
|
|
77
|
+
projectPath: '/fake',
|
|
78
|
+
config,
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
expect(manager.isPathIgnored('src/utils.test.ts')).toBe(true)
|
|
82
|
+
expect(manager.isPathIgnored('node_modules/lodash/index.js')).toBe(true)
|
|
83
|
+
expect(manager.isPathIgnored('src/index.ts')).toBe(false)
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
it('should return false when no ignore patterns', () => {
|
|
87
|
+
const manager = new SuppressionManager({
|
|
88
|
+
projectPath: '/fake',
|
|
89
|
+
config: { version: 1 },
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
expect(manager.isPathIgnored('anything.ts')).toBe(false)
|
|
93
|
+
})
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
describe('isFindingSuppressed', () => {
|
|
97
|
+
it('should suppress by inline comment', () => {
|
|
98
|
+
const config: SuppressionConfig = { version: 1 }
|
|
99
|
+
const manager = new SuppressionManager({ projectPath: '/fake', config })
|
|
100
|
+
|
|
101
|
+
// Line 4 should be suppressed (line 3 has the comment)
|
|
102
|
+
const finding = createVulnerability({ lineNumber: 4 })
|
|
103
|
+
const fileContent = `const x = 1;
|
|
104
|
+
const y = 2;
|
|
105
|
+
// oculum-ignore-next-line: false positive
|
|
106
|
+
const secret = "api_key_12345";
|
|
107
|
+
`
|
|
108
|
+
|
|
109
|
+
const result = manager.isFindingSuppressed(finding, fileContent)
|
|
110
|
+
|
|
111
|
+
expect(result.suppressed).toBe(true)
|
|
112
|
+
expect(result.match?.type).toBe('inline')
|
|
113
|
+
expect(result.match?.reason).toBe('false positive')
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
it('should suppress by config finding hash', () => {
|
|
117
|
+
const finding = createVulnerability()
|
|
118
|
+
// First get the hash
|
|
119
|
+
const tempManager = new SuppressionManager({
|
|
120
|
+
projectPath: '/fake',
|
|
121
|
+
config: { version: 1 },
|
|
122
|
+
})
|
|
123
|
+
const { hash } = tempManager.isFindingSuppressed(finding)
|
|
124
|
+
|
|
125
|
+
// Now create a manager with that hash suppressed
|
|
126
|
+
const config: SuppressionConfig = {
|
|
127
|
+
version: 1,
|
|
128
|
+
suppressions: {
|
|
129
|
+
findings: [{
|
|
130
|
+
hash,
|
|
131
|
+
file: 'src/index.ts',
|
|
132
|
+
reason: 'suppressed by hash',
|
|
133
|
+
}],
|
|
134
|
+
},
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const manager = new SuppressionManager({ projectPath: '/fake', config })
|
|
138
|
+
const result = manager.isFindingSuppressed(finding)
|
|
139
|
+
|
|
140
|
+
expect(result.suppressed).toBe(true)
|
|
141
|
+
expect(result.match?.type).toBe('config-finding')
|
|
142
|
+
expect(result.match?.reason).toBe('suppressed by hash')
|
|
143
|
+
})
|
|
144
|
+
|
|
145
|
+
it('should suppress by config rule', () => {
|
|
146
|
+
const config: SuppressionConfig = {
|
|
147
|
+
version: 1,
|
|
148
|
+
suppressions: {
|
|
149
|
+
rules: [{
|
|
150
|
+
category: 'hardcoded_secret',
|
|
151
|
+
reason: 'all hardcoded secrets suppressed',
|
|
152
|
+
}],
|
|
153
|
+
},
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const manager = new SuppressionManager({ projectPath: '/fake', config })
|
|
157
|
+
const finding = createVulnerability()
|
|
158
|
+
|
|
159
|
+
const result = manager.isFindingSuppressed(finding)
|
|
160
|
+
|
|
161
|
+
expect(result.suppressed).toBe(true)
|
|
162
|
+
expect(result.match?.type).toBe('config-rule')
|
|
163
|
+
expect(result.match?.reason).toBe('all hardcoded secrets suppressed')
|
|
164
|
+
})
|
|
165
|
+
|
|
166
|
+
it('should respect rule path restrictions', () => {
|
|
167
|
+
const config: SuppressionConfig = {
|
|
168
|
+
version: 1,
|
|
169
|
+
suppressions: {
|
|
170
|
+
rules: [{
|
|
171
|
+
category: 'hardcoded_secret',
|
|
172
|
+
reason: 'only in tests',
|
|
173
|
+
paths: ['**/*.test.ts'],
|
|
174
|
+
}],
|
|
175
|
+
},
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const manager = new SuppressionManager({ projectPath: '/fake', config })
|
|
179
|
+
|
|
180
|
+
const testFinding = createVulnerability({ filePath: 'src/utils.test.ts' })
|
|
181
|
+
const srcFinding = createVulnerability({ filePath: 'src/index.ts' })
|
|
182
|
+
|
|
183
|
+
expect(manager.isFindingSuppressed(testFinding).suppressed).toBe(true)
|
|
184
|
+
expect(manager.isFindingSuppressed(srcFinding).suppressed).toBe(false)
|
|
185
|
+
})
|
|
186
|
+
|
|
187
|
+
it('should handle expired suppressions', () => {
|
|
188
|
+
const finding = createVulnerability()
|
|
189
|
+
const tempManager = new SuppressionManager({
|
|
190
|
+
projectPath: '/fake',
|
|
191
|
+
config: { version: 1 },
|
|
192
|
+
})
|
|
193
|
+
const { hash } = tempManager.isFindingSuppressed(finding)
|
|
194
|
+
|
|
195
|
+
const config: SuppressionConfig = {
|
|
196
|
+
version: 1,
|
|
197
|
+
suppressions: {
|
|
198
|
+
findings: [{
|
|
199
|
+
hash,
|
|
200
|
+
file: 'src/index.ts',
|
|
201
|
+
reason: 'expired suppression',
|
|
202
|
+
expires: '2020-01-01', // Already expired
|
|
203
|
+
}],
|
|
204
|
+
},
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const manager = new SuppressionManager({ projectPath: '/fake', config })
|
|
208
|
+
const result = manager.isFindingSuppressed(finding)
|
|
209
|
+
|
|
210
|
+
expect(result.suppressed).toBe(false)
|
|
211
|
+
expect(result.match?.expired).toBe(true)
|
|
212
|
+
})
|
|
213
|
+
|
|
214
|
+
it('should prioritize inline over config', () => {
|
|
215
|
+
// Line 4 is the secret line, line 3 has the comment
|
|
216
|
+
const finding = createVulnerability({ lineNumber: 4 })
|
|
217
|
+
const tempManager = new SuppressionManager({
|
|
218
|
+
projectPath: '/fake',
|
|
219
|
+
config: { version: 1 },
|
|
220
|
+
})
|
|
221
|
+
const { hash } = tempManager.isFindingSuppressed(finding)
|
|
222
|
+
|
|
223
|
+
const config: SuppressionConfig = {
|
|
224
|
+
version: 1,
|
|
225
|
+
suppressions: {
|
|
226
|
+
findings: [{
|
|
227
|
+
hash,
|
|
228
|
+
file: 'src/index.ts',
|
|
229
|
+
reason: 'config reason',
|
|
230
|
+
}],
|
|
231
|
+
},
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
const manager = new SuppressionManager({ projectPath: '/fake', config })
|
|
235
|
+
const fileContent = `const x = 1;
|
|
236
|
+
const y = 2;
|
|
237
|
+
// oculum-ignore-next-line: inline reason
|
|
238
|
+
const secret = "api_key_12345";
|
|
239
|
+
`
|
|
240
|
+
|
|
241
|
+
const result = manager.isFindingSuppressed(finding, fileContent)
|
|
242
|
+
|
|
243
|
+
// Should be suppressed by inline (higher priority)
|
|
244
|
+
expect(result.suppressed).toBe(true)
|
|
245
|
+
expect(result.match?.type).toBe('inline')
|
|
246
|
+
expect(result.match?.reason).toBe('inline reason')
|
|
247
|
+
})
|
|
248
|
+
})
|
|
249
|
+
|
|
250
|
+
describe('applySuppressions', () => {
|
|
251
|
+
it('should filter suppressed findings', () => {
|
|
252
|
+
const config: SuppressionConfig = {
|
|
253
|
+
version: 1,
|
|
254
|
+
suppressions: {
|
|
255
|
+
rules: [{
|
|
256
|
+
category: 'hardcoded_secret',
|
|
257
|
+
reason: 'suppress all secrets',
|
|
258
|
+
}],
|
|
259
|
+
},
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
const manager = new SuppressionManager({ projectPath: '/fake', config })
|
|
263
|
+
|
|
264
|
+
const findings = [
|
|
265
|
+
createVulnerability({ id: '1', category: 'hardcoded_secret' }),
|
|
266
|
+
createVulnerability({ id: '2', category: 'high_entropy_string' }),
|
|
267
|
+
createVulnerability({ id: '3', category: 'hardcoded_secret' }),
|
|
268
|
+
]
|
|
269
|
+
|
|
270
|
+
const files = [createScanFile()]
|
|
271
|
+
|
|
272
|
+
const result = manager.applySuppressions(findings, files)
|
|
273
|
+
|
|
274
|
+
expect(result.findings).toHaveLength(1)
|
|
275
|
+
expect(result.findings[0].category).toBe('high_entropy_string')
|
|
276
|
+
|
|
277
|
+
expect(result.suppressed).toHaveLength(2)
|
|
278
|
+
expect(result.stats.configRuleSuppressed).toBe(2)
|
|
279
|
+
})
|
|
280
|
+
|
|
281
|
+
it('should include suppression details in result', () => {
|
|
282
|
+
const config: SuppressionConfig = {
|
|
283
|
+
version: 1,
|
|
284
|
+
suppressions: {
|
|
285
|
+
rules: [{
|
|
286
|
+
category: 'hardcoded_secret',
|
|
287
|
+
reason: 'test reason',
|
|
288
|
+
}],
|
|
289
|
+
},
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
const manager = new SuppressionManager({ projectPath: '/fake', config })
|
|
293
|
+
const finding = createVulnerability()
|
|
294
|
+
const files = [createScanFile()]
|
|
295
|
+
|
|
296
|
+
const result = manager.applySuppressions([finding], files)
|
|
297
|
+
|
|
298
|
+
expect(result.suppressed).toHaveLength(1)
|
|
299
|
+
expect(result.suppressed[0].suppression.reason).toBe('test reason')
|
|
300
|
+
expect(result.suppressed[0].suppression.type).toBe('config-rule')
|
|
301
|
+
expect(result.suppressed[0].vulnerability.filePath).toBe(finding.filePath)
|
|
302
|
+
})
|
|
303
|
+
|
|
304
|
+
it('should track expired suppressions count', () => {
|
|
305
|
+
const finding = createVulnerability()
|
|
306
|
+
const tempManager = new SuppressionManager({
|
|
307
|
+
projectPath: '/fake',
|
|
308
|
+
config: { version: 1 },
|
|
309
|
+
})
|
|
310
|
+
const { hash } = tempManager.isFindingSuppressed(finding)
|
|
311
|
+
|
|
312
|
+
const config: SuppressionConfig = {
|
|
313
|
+
version: 1,
|
|
314
|
+
suppressions: {
|
|
315
|
+
findings: [{
|
|
316
|
+
hash,
|
|
317
|
+
file: 'src/index.ts',
|
|
318
|
+
reason: 'expired',
|
|
319
|
+
expires: '2020-01-01',
|
|
320
|
+
}],
|
|
321
|
+
},
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
const manager = new SuppressionManager({ projectPath: '/fake', config })
|
|
325
|
+
const files = [createScanFile()]
|
|
326
|
+
|
|
327
|
+
const result = manager.applySuppressions([finding], files)
|
|
328
|
+
|
|
329
|
+
expect(result.findings).toHaveLength(1) // Not suppressed because expired
|
|
330
|
+
expect(result.expiredSuppressions).toBe(1)
|
|
331
|
+
expect(result.stats.expired).toBe(1)
|
|
332
|
+
})
|
|
333
|
+
|
|
334
|
+
it('should correctly count by suppression type', () => {
|
|
335
|
+
const config: SuppressionConfig = {
|
|
336
|
+
version: 1,
|
|
337
|
+
suppressions: {
|
|
338
|
+
rules: [{
|
|
339
|
+
category: 'hardcoded_secret',
|
|
340
|
+
reason: 'rule suppression',
|
|
341
|
+
}],
|
|
342
|
+
},
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
const manager = new SuppressionManager({ projectPath: '/fake', config })
|
|
346
|
+
|
|
347
|
+
// Create a finding that will be suppressed by inline (line 4 - after the comment on line 3)
|
|
348
|
+
const inlineFinding = createVulnerability({
|
|
349
|
+
id: '1',
|
|
350
|
+
category: 'high_entropy_string',
|
|
351
|
+
lineNumber: 4,
|
|
352
|
+
lineContent: 'const entropy = "abcdef123456";',
|
|
353
|
+
})
|
|
354
|
+
|
|
355
|
+
// Create a finding that will be suppressed by rule (line 5)
|
|
356
|
+
const ruleFinding = createVulnerability({
|
|
357
|
+
id: '2',
|
|
358
|
+
category: 'hardcoded_secret',
|
|
359
|
+
lineNumber: 5,
|
|
360
|
+
lineContent: 'const secret = "api_key";',
|
|
361
|
+
})
|
|
362
|
+
|
|
363
|
+
// Create a finding that won't be suppressed (line 6)
|
|
364
|
+
const passedFinding = createVulnerability({
|
|
365
|
+
id: '3',
|
|
366
|
+
category: 'sql_injection',
|
|
367
|
+
lineNumber: 6,
|
|
368
|
+
lineContent: 'const query = "SELECT * FROM users";',
|
|
369
|
+
})
|
|
370
|
+
|
|
371
|
+
const findings = [inlineFinding, ruleFinding, passedFinding]
|
|
372
|
+
|
|
373
|
+
const fileContent = `const x = 1;
|
|
374
|
+
const y = 2;
|
|
375
|
+
// oculum-ignore-next-line: inline reason
|
|
376
|
+
const entropy = "abcdef123456";
|
|
377
|
+
const secret = "api_key";
|
|
378
|
+
const query = "SELECT * FROM users";
|
|
379
|
+
`
|
|
380
|
+
const files = [createScanFile({ content: fileContent })]
|
|
381
|
+
|
|
382
|
+
const result = manager.applySuppressions(findings, files)
|
|
383
|
+
|
|
384
|
+
expect(result.findings).toHaveLength(1)
|
|
385
|
+
expect(result.stats.inlineSuppressed).toBe(1)
|
|
386
|
+
expect(result.stats.configRuleSuppressed).toBe(1)
|
|
387
|
+
})
|
|
388
|
+
})
|
|
389
|
+
|
|
390
|
+
describe('getSummary', () => {
|
|
391
|
+
it('should return accurate summary', () => {
|
|
392
|
+
const config: SuppressionConfig = {
|
|
393
|
+
version: 1,
|
|
394
|
+
suppressions: {
|
|
395
|
+
rules: [
|
|
396
|
+
{ category: 'hardcoded_secret', reason: 'r1' },
|
|
397
|
+
{ category: 'high_entropy_string', reason: 'r2' },
|
|
398
|
+
],
|
|
399
|
+
findings: [
|
|
400
|
+
{ hash: 'abc123def4567890', file: 'a.ts', reason: 'f1' },
|
|
401
|
+
],
|
|
402
|
+
},
|
|
403
|
+
ignore: ['*.test.ts', '*.spec.ts'],
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
const manager = new SuppressionManager({ projectPath: '/fake', config })
|
|
407
|
+
const summary = manager.getSummary()
|
|
408
|
+
|
|
409
|
+
expect(summary.ruleCount).toBe(2)
|
|
410
|
+
expect(summary.findingCount).toBe(1)
|
|
411
|
+
expect(summary.ignorePatternCount).toBe(2)
|
|
412
|
+
expect(summary.hasErrors).toBe(false)
|
|
413
|
+
})
|
|
414
|
+
})
|
|
415
|
+
})
|