@complior/engine 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.well-known/ai-compliance.json +16 -0
- package/COMPLIANCE.md +64 -0
- package/data/data-integrity.test.ts +75 -0
- package/data/eval/eval-mappings.json +33 -0
- package/data/llm/model-pricing.json +15 -0
- package/data/llm/model-routing.json +36 -0
- package/data/onboarding/risk-profile.json +17 -0
- package/data/regulations/eu-ai-act/README.md +245 -0
- package/data/regulations/eu-ai-act/applicability-tree.json +160 -0
- package/data/regulations/eu-ai-act/cross-mapping.json +175 -0
- package/data/regulations/eu-ai-act/localization.json +186 -0
- package/data/regulations/eu-ai-act/obligations.json +3981 -0
- package/data/regulations/eu-ai-act/regulation-meta.json +482 -0
- package/data/regulations/eu-ai-act/scoring.json +342 -0
- package/data/regulations/eu-ai-act/technical-requirements.json +2590 -0
- package/data/regulations/eu-ai-act/timeline.json +160 -0
- package/data/regulations/jurisdictions/at.json +15 -0
- package/data/regulations/jurisdictions/be.json +15 -0
- package/data/regulations/jurisdictions/bg.json +15 -0
- package/data/regulations/jurisdictions/cy.json +15 -0
- package/data/regulations/jurisdictions/cz.json +15 -0
- package/data/regulations/jurisdictions/de.json +15 -0
- package/data/regulations/jurisdictions/dk.json +15 -0
- package/data/regulations/jurisdictions/ee.json +15 -0
- package/data/regulations/jurisdictions/es.json +15 -0
- package/data/regulations/jurisdictions/fi.json +15 -0
- package/data/regulations/jurisdictions/fr.json +15 -0
- package/data/regulations/jurisdictions/gr.json +15 -0
- package/data/regulations/jurisdictions/hr.json +15 -0
- package/data/regulations/jurisdictions/hu.json +15 -0
- package/data/regulations/jurisdictions/ie.json +15 -0
- package/data/regulations/jurisdictions/is.json +15 -0
- package/data/regulations/jurisdictions/it.json +15 -0
- package/data/regulations/jurisdictions/li.json +15 -0
- package/data/regulations/jurisdictions/lt.json +15 -0
- package/data/regulations/jurisdictions/lu.json +15 -0
- package/data/regulations/jurisdictions/lv.json +15 -0
- package/data/regulations/jurisdictions/mt.json +15 -0
- package/data/regulations/jurisdictions/nl.json +15 -0
- package/data/regulations/jurisdictions/no.json +15 -0
- package/data/regulations/jurisdictions/pl.json +15 -0
- package/data/regulations/jurisdictions/pt.json +15 -0
- package/data/regulations/jurisdictions/ro.json +15 -0
- package/data/regulations/jurisdictions/se.json +15 -0
- package/data/regulations/jurisdictions/si.json +15 -0
- package/data/regulations/jurisdictions/sk.json +15 -0
- package/data/scanner/check-id-categories.json +81 -0
- package/data/scanner/confidence-params.json +16 -0
- package/data/scanner/limits.json +4 -0
- package/data/schemas/http-contract-sample.json +79 -0
- package/data/schemas/http-contract.json +144 -0
- package/data/semgrep-rules/bare-call.yaml +37 -0
- package/data/semgrep-rules/injection.yaml +73 -0
- package/data/semgrep-rules/missing-error-handling.yaml +58 -0
- package/data/semgrep-rules/unsafe-deser.yaml +65 -0
- package/data/templates/eu-ai-act/ai-literacy.md +184 -0
- package/data/templates/eu-ai-act/art5-screening.md +131 -0
- package/data/templates/eu-ai-act/data-governance.md +145 -0
- package/data/templates/eu-ai-act/declaration-of-conformity.md +161 -0
- package/data/templates/eu-ai-act/fria.md +127 -0
- package/data/templates/eu-ai-act/gpai-systemic-risk.md +150 -0
- package/data/templates/eu-ai-act/gpai-transparency.md +166 -0
- package/data/templates/eu-ai-act/incident-report.md +188 -0
- package/data/templates/eu-ai-act/instructions-for-use.md +202 -0
- package/data/templates/eu-ai-act/monitoring-policy.md +110 -0
- package/data/templates/eu-ai-act/qms.md +180 -0
- package/data/templates/eu-ai-act/risk-management-system.md +123 -0
- package/data/templates/eu-ai-act/technical-documentation.md +287 -0
- package/data/templates/eu-ai-act/worker-notification.md +143 -0
- package/data/templates/policies/biometrics-ai-policy.md +214 -0
- package/data/templates/policies/critical-infra-ai-policy.md +228 -0
- package/data/templates/policies/education-ai-policy.md +184 -0
- package/data/templates/policies/finance-ai-policy.md +191 -0
- package/data/templates/policies/healthcare-ai-policy.md +197 -0
- package/data/templates/policies/hr-ai-policy.md +178 -0
- package/data/templates/policies/legal-ai-policy.md +189 -0
- package/data/templates/policies/migration-ai-policy.md +239 -0
- package/engine.log +7 -0
- package/package.json +74 -0
- package/src/composition-root.ts +791 -0
- package/src/data/eval/conformity-tests.test.ts +122 -0
- package/src/data/eval/ct-1-transparency.ts +106 -0
- package/src/data/eval/ct-10-gpai.ts +25 -0
- package/src/data/eval/ct-11-industry.ts +42 -0
- package/src/data/eval/ct-2-oversight.ts +41 -0
- package/src/data/eval/ct-3-explanation.ts +14 -0
- package/src/data/eval/ct-4-bias.ts +83 -0
- package/src/data/eval/ct-5-accuracy.ts +41 -0
- package/src/data/eval/ct-6-robustness.ts +81 -0
- package/src/data/eval/ct-7-prohibited.ts +52 -0
- package/src/data/eval/ct-8-logging.ts +68 -0
- package/src/data/eval/ct-9-risk-awareness.ts +33 -0
- package/src/data/eval/deterministic-evaluator.ts +120 -0
- package/src/data/eval/index.ts +55 -0
- package/src/data/eval/judge-prompts.ts +146 -0
- package/src/data/eval/llm-judged-tests.ts +279 -0
- package/src/data/eval/llm-tests.test.ts +83 -0
- package/src/data/eval/remediation/ct-1-transparency.ts +91 -0
- package/src/data/eval/remediation/ct-10-gpai.ts +94 -0
- package/src/data/eval/remediation/ct-11-industry.ts +94 -0
- package/src/data/eval/remediation/ct-2-oversight.ts +71 -0
- package/src/data/eval/remediation/ct-3-explanation.ts +70 -0
- package/src/data/eval/remediation/ct-4-bias.ts +70 -0
- package/src/data/eval/remediation/ct-5-accuracy.ts +70 -0
- package/src/data/eval/remediation/ct-6-robustness.ts +70 -0
- package/src/data/eval/remediation/ct-7-prohibited.ts +94 -0
- package/src/data/eval/remediation/ct-8-logging.ts +94 -0
- package/src/data/eval/remediation/ct-9-risk-awareness.ts +94 -0
- package/src/data/eval/remediation/index.ts +89 -0
- package/src/data/eval/remediation/owasp-art5.ts +15 -0
- package/src/data/eval/remediation/owasp-llm01.ts +72 -0
- package/src/data/eval/remediation/owasp-llm02.ts +72 -0
- package/src/data/eval/remediation/owasp-llm03.ts +15 -0
- package/src/data/eval/remediation/owasp-llm04.ts +15 -0
- package/src/data/eval/remediation/owasp-llm05.ts +15 -0
- package/src/data/eval/remediation/owasp-llm06.ts +15 -0
- package/src/data/eval/remediation/owasp-llm07.ts +15 -0
- package/src/data/eval/remediation/owasp-llm08.ts +15 -0
- package/src/data/eval/remediation/owasp-llm09.ts +15 -0
- package/src/data/eval/remediation/owasp-llm10.ts +15 -0
- package/src/data/eval/remediation/remediation.test.ts +229 -0
- package/src/data/eval/remediation/test-mapping.ts +290 -0
- package/src/data/eval/security-rubrics.ts +381 -0
- package/src/data/finding-explanations.json +453 -0
- package/src/data/industry-patterns.ts +161 -0
- package/src/data/registry-cards.ts +368 -0
- package/src/data/regulation/index.ts +5 -0
- package/src/data/regulation/jurisdiction-data.test.ts +73 -0
- package/src/data/regulation/jurisdiction-data.ts +65 -0
- package/src/data/regulation/regulation-data.ts +19 -0
- package/src/data/regulation/regulation-loader.test.ts +107 -0
- package/src/data/regulation/regulation-loader.ts +56 -0
- package/src/data/scanner-constants.ts +46 -0
- package/src/data/schemas/schemas-core.ts +140 -0
- package/src/data/schemas/schemas-supplementary.ts +211 -0
- package/src/data/schemas/schemas.ts +28 -0
- package/src/data/security/attack-probes.test.ts +62 -0
- package/src/data/security/attack-probes.ts +496 -0
- package/src/data/security/eu-ai-act-security.ts +40 -0
- package/src/data/security/index.ts +19 -0
- package/src/data/security/mitre-atlas.test.ts +43 -0
- package/src/data/security/mitre-atlas.ts +93 -0
- package/src/data/security/nist-ai-rmf.ts +43 -0
- package/src/data/security/owasp-llm-top10.test.ts +60 -0
- package/src/data/security/owasp-llm-top10.ts +138 -0
- package/src/data/template-registry.ts +53 -0
- package/src/data/tool-versions.json +22 -0
- package/src/domain/audit/audit-package.test.ts +152 -0
- package/src/domain/audit/audit-package.ts +166 -0
- package/src/domain/audit/audit-trail.test.ts +121 -0
- package/src/domain/audit/audit-trail.ts +174 -0
- package/src/domain/audit/index.ts +8 -0
- package/src/domain/audit/permissions-matrix.test.ts +136 -0
- package/src/domain/audit/permissions-matrix.ts +121 -0
- package/src/domain/certification/adversarial/bias-tests.ts +95 -0
- package/src/domain/certification/adversarial/evaluators.ts +304 -0
- package/src/domain/certification/adversarial/index.ts +11 -0
- package/src/domain/certification/adversarial/prompt-injection.ts +103 -0
- package/src/domain/certification/adversarial/safety-boundary.ts +132 -0
- package/src/domain/certification/aiuc1-readiness.test.ts +236 -0
- package/src/domain/certification/aiuc1-readiness.ts +298 -0
- package/src/domain/certification/aiuc1-requirements.ts +235 -0
- package/src/domain/certification/index.ts +10 -0
- package/src/domain/certification/redteam-runner.test.ts +97 -0
- package/src/domain/certification/redteam-runner.ts +205 -0
- package/src/domain/certification/test-runner.test.ts +232 -0
- package/src/domain/certification/test-runner.ts +289 -0
- package/src/domain/cost/cost-estimator.test.ts +187 -0
- package/src/domain/cost/cost-estimator.ts +133 -0
- package/src/domain/disclaimer.test.ts +52 -0
- package/src/domain/disclaimer.ts +39 -0
- package/src/domain/documents/ai-enricher.test.ts +120 -0
- package/src/domain/documents/ai-enricher.ts +159 -0
- package/src/domain/documents/document-generator.test.ts +318 -0
- package/src/domain/documents/document-generator.ts +239 -0
- package/src/domain/documents/index.ts +9 -0
- package/src/domain/documents/passport-helpers.ts +25 -0
- package/src/domain/documents/policy-generator.test.ts +252 -0
- package/src/domain/documents/policy-generator.ts +94 -0
- package/src/domain/documents/worker-notification-generator.test.ts +162 -0
- package/src/domain/documents/worker-notification-generator.ts +141 -0
- package/src/domain/eval/adapters/adapter-port.ts +94 -0
- package/src/domain/eval/adapters/adapters.test.ts +303 -0
- package/src/domain/eval/adapters/anthropic-adapter.ts +57 -0
- package/src/domain/eval/adapters/auto-detect.ts +104 -0
- package/src/domain/eval/adapters/create-chat-adapter.ts +106 -0
- package/src/domain/eval/adapters/custom-adapter.ts +74 -0
- package/src/domain/eval/adapters/http-adapter.ts +66 -0
- package/src/domain/eval/adapters/index.ts +7 -0
- package/src/domain/eval/adapters/ollama-adapter.ts +48 -0
- package/src/domain/eval/adapters/openai-adapter.ts +58 -0
- package/src/domain/eval/adapters/with-timeout.ts +25 -0
- package/src/domain/eval/conformity-score.test.ts +161 -0
- package/src/domain/eval/conformity-score.ts +135 -0
- package/src/domain/eval/eval-constants.ts +55 -0
- package/src/domain/eval/eval-evidence.test.ts +85 -0
- package/src/domain/eval/eval-evidence.ts +103 -0
- package/src/domain/eval/eval-fix-generator.test.ts +421 -0
- package/src/domain/eval/eval-fix-generator.ts +205 -0
- package/src/domain/eval/eval-passport.test.ts +82 -0
- package/src/domain/eval/eval-passport.ts +89 -0
- package/src/domain/eval/eval-remediation-report.test.ts +682 -0
- package/src/domain/eval/eval-remediation-report.ts +170 -0
- package/src/domain/eval/eval-report.ts +108 -0
- package/src/domain/eval/eval-runner.test.ts +609 -0
- package/src/domain/eval/eval-runner.ts +593 -0
- package/src/domain/eval/eval-to-findings.test.ts +293 -0
- package/src/domain/eval/eval-to-findings.ts +83 -0
- package/src/domain/eval/index.ts +31 -0
- package/src/domain/eval/llm-judge.test.ts +139 -0
- package/src/domain/eval/llm-judge.ts +168 -0
- package/src/domain/eval/remediation-types.ts +90 -0
- package/src/domain/eval/security-integration.test.ts +196 -0
- package/src/domain/eval/security-integration.ts +136 -0
- package/src/domain/eval/types.test.ts +173 -0
- package/src/domain/eval/types.ts +244 -0
- package/src/domain/eval/verdict-utils.ts +45 -0
- package/src/domain/fixer/create-fixer.ts +101 -0
- package/src/domain/fixer/diff.ts +70 -0
- package/src/domain/fixer/fix-history.ts +23 -0
- package/src/domain/fixer/fixer.test.ts +306 -0
- package/src/domain/fixer/index.ts +9 -0
- package/src/domain/fixer/strategies/bandit-fix.ts +61 -0
- package/src/domain/fixer/strategies/bias-testing.ts +49 -0
- package/src/domain/fixer/strategies/ci-compliance.ts +57 -0
- package/src/domain/fixer/strategies/content-marking.ts +45 -0
- package/src/domain/fixer/strategies/cve-upgrade.ts +66 -0
- package/src/domain/fixer/strategies/data-governance.ts +65 -0
- package/src/domain/fixer/strategies/disclosure.ts +69 -0
- package/src/domain/fixer/strategies/doc-code-sync.ts +53 -0
- package/src/domain/fixer/strategies/documentation.ts +59 -0
- package/src/domain/fixer/strategies/error-handler.ts +63 -0
- package/src/domain/fixer/strategies/hitl-gate.ts +67 -0
- package/src/domain/fixer/strategies/index.ts +61 -0
- package/src/domain/fixer/strategies/kill-switch-test.ts +85 -0
- package/src/domain/fixer/strategies/kill-switch.ts +53 -0
- package/src/domain/fixer/strategies/license-fix.ts +57 -0
- package/src/domain/fixer/strategies/log-retention.ts +40 -0
- package/src/domain/fixer/strategies/logging.ts +59 -0
- package/src/domain/fixer/strategies/metadata.ts +45 -0
- package/src/domain/fixer/strategies/permission-guard.ts +84 -0
- package/src/domain/fixer/strategies/record-keeping.ts +69 -0
- package/src/domain/fixer/strategies/secret-rotation.ts +52 -0
- package/src/domain/fixer/strategies.test.ts +341 -0
- package/src/domain/fixer/template-engine.test.ts +64 -0
- package/src/domain/fixer/template-engine.ts +38 -0
- package/src/domain/fixer/types.ts +88 -0
- package/src/domain/frameworks/aiuc1-framework.test.ts +159 -0
- package/src/domain/frameworks/aiuc1-framework.ts +126 -0
- package/src/domain/frameworks/collect-foundation-metrics.test.ts +96 -0
- package/src/domain/frameworks/collect-foundation-metrics.ts +34 -0
- package/src/domain/frameworks/eu-ai-act-framework.test.ts +117 -0
- package/src/domain/frameworks/eu-ai-act-framework.ts +100 -0
- package/src/domain/frameworks/framework-registry.test.ts +91 -0
- package/src/domain/frameworks/framework-registry.ts +38 -0
- package/src/domain/frameworks/index.ts +8 -0
- package/src/domain/frameworks/mitre-atlas-framework.test.ts +53 -0
- package/src/domain/frameworks/mitre-atlas-framework.ts +53 -0
- package/src/domain/frameworks/owasp-llm-framework.test.ts +77 -0
- package/src/domain/frameworks/owasp-llm-framework.ts +54 -0
- package/src/domain/frameworks/score-plugin-framework.ts +117 -0
- package/src/domain/fria/fria-generator.test.ts +273 -0
- package/src/domain/fria/fria-generator.ts +366 -0
- package/src/domain/import/promptfoo-importer.test.ts +103 -0
- package/src/domain/import/promptfoo-importer.ts +151 -0
- package/src/domain/onboarding/guided-onboarding.test.ts +144 -0
- package/src/domain/onboarding/guided-onboarding.ts +135 -0
- package/src/domain/passport/builder/domain-mapper.ts +9 -0
- package/src/domain/passport/builder/manifest-builder.test.ts +546 -0
- package/src/domain/passport/builder/manifest-builder.ts +535 -0
- package/src/domain/passport/builder/manifest-diff.test.ts +105 -0
- package/src/domain/passport/builder/manifest-diff.ts +89 -0
- package/src/domain/passport/builder/manifest-files.ts +17 -0
- package/src/domain/passport/crypto-signer.test.ts +93 -0
- package/src/domain/passport/crypto-signer.ts +157 -0
- package/src/domain/passport/discovery/agent-discovery.test.ts +296 -0
- package/src/domain/passport/discovery/agent-discovery.ts +325 -0
- package/src/domain/passport/discovery/autonomy-analyzer.test.ts +141 -0
- package/src/domain/passport/discovery/autonomy-analyzer.ts +113 -0
- package/src/domain/passport/discovery/permission-scanner.test.ts +191 -0
- package/src/domain/passport/discovery/permission-scanner.ts +414 -0
- package/src/domain/passport/export/a2a-mapper.ts +75 -0
- package/src/domain/passport/export/aiuc1-mapper.ts +126 -0
- package/src/domain/passport/export/export.test.ts +207 -0
- package/src/domain/passport/export/index.ts +41 -0
- package/src/domain/passport/export/nist-mapper.ts +227 -0
- package/src/domain/passport/import/a2a-importer.test.ts +133 -0
- package/src/domain/passport/import/a2a-importer.ts +156 -0
- package/src/domain/passport/import/index.ts +2 -0
- package/src/domain/passport/index.ts +32 -0
- package/src/domain/passport/obligation-field-map.test.ts +113 -0
- package/src/domain/passport/obligation-field-map.ts +117 -0
- package/src/domain/passport/passport-validator.test.ts +156 -0
- package/src/domain/passport/passport-validator.ts +126 -0
- package/src/domain/passport/scan-to-compliance.test.ts +336 -0
- package/src/domain/passport/scan-to-compliance.ts +166 -0
- package/src/domain/passport/test-generator.test.ts +93 -0
- package/src/domain/passport/test-generator.ts +136 -0
- package/src/domain/proxy/index.ts +11 -0
- package/src/domain/proxy/json-rpc.test.ts +72 -0
- package/src/domain/proxy/json-rpc.ts +53 -0
- package/src/domain/proxy/policy-engine.test.ts +259 -0
- package/src/domain/proxy/policy-engine.ts +137 -0
- package/src/domain/proxy/proxy-bridge.ts +125 -0
- package/src/domain/proxy/proxy-interceptor.test.ts +184 -0
- package/src/domain/proxy/proxy-interceptor.ts +120 -0
- package/src/domain/proxy/proxy-types.ts +35 -0
- package/src/domain/registry/compute-agent-score.test.ts +279 -0
- package/src/domain/registry/compute-agent-score.ts +162 -0
- package/src/domain/reporter/audit-report.test.ts +87 -0
- package/src/domain/reporter/audit-report.ts +116 -0
- package/src/domain/reporter/badge-generator.test.ts +54 -0
- package/src/domain/reporter/badge-generator.ts +40 -0
- package/src/domain/reporter/compliance-md.ts +45 -0
- package/src/domain/reporter/index.ts +7 -0
- package/src/domain/reporter/pdf-renderer.ts +282 -0
- package/src/domain/reporter/share.test.ts +92 -0
- package/src/domain/reporter/share.ts +80 -0
- package/src/domain/scanner/ast/swc-analyzer.test.ts +49 -0
- package/src/domain/scanner/ast/swc-analyzer.ts +124 -0
- package/src/domain/scanner/attestations.ts +97 -0
- package/src/domain/scanner/checks/ai-disclosure.test.ts +90 -0
- package/src/domain/scanner/checks/ai-disclosure.ts +54 -0
- package/src/domain/scanner/checks/ai-literacy.ts +163 -0
- package/src/domain/scanner/checks/behavioral-constraints.test.ts +167 -0
- package/src/domain/scanner/checks/behavioral-constraints.ts +86 -0
- package/src/domain/scanner/checks/compliance-metadata.ts +63 -0
- package/src/domain/scanner/checks/content-marking.ts +74 -0
- package/src/domain/scanner/checks/dep-deep-scan.test.ts +318 -0
- package/src/domain/scanner/checks/dep-deep-scan.ts +137 -0
- package/src/domain/scanner/checks/documentation.test.ts +88 -0
- package/src/domain/scanner/checks/documentation.ts +79 -0
- package/src/domain/scanner/checks/git-history.test.ts +120 -0
- package/src/domain/scanner/checks/git-history.ts +163 -0
- package/src/domain/scanner/checks/gpai-systemic-risk.test.ts +84 -0
- package/src/domain/scanner/checks/gpai-systemic-risk.ts +98 -0
- package/src/domain/scanner/checks/gpai-transparency.ts +94 -0
- package/src/domain/scanner/checks/index.ts +28 -0
- package/src/domain/scanner/checks/industry/index.ts +40 -0
- package/src/domain/scanner/checks/industry/industry.test.ts +287 -0
- package/src/domain/scanner/checks/interaction-logging.test.ts +113 -0
- package/src/domain/scanner/checks/interaction-logging.ts +142 -0
- package/src/domain/scanner/checks/nhi-scanner.test.ts +158 -0
- package/src/domain/scanner/checks/nhi-scanner.ts +78 -0
- package/src/domain/scanner/checks/passport-completeness.test.ts +127 -0
- package/src/domain/scanner/checks/passport-completeness.ts +82 -0
- package/src/domain/scanner/checks/passport-presence.test.ts +56 -0
- package/src/domain/scanner/checks/passport-presence.ts +78 -0
- package/src/domain/scanner/checks/pattern-check-factory.ts +70 -0
- package/src/domain/scanner/checks/permission-scanner.test.ts +279 -0
- package/src/domain/scanner/checks/permission-scanner.ts +90 -0
- package/src/domain/scanner/checks/presence-check-factory.test.ts +124 -0
- package/src/domain/scanner/checks/presence-check-factory.ts +275 -0
- package/src/domain/scanner/compliance-diff.test.ts +165 -0
- package/src/domain/scanner/compliance-diff.ts +138 -0
- package/src/domain/scanner/confidence.test.ts +235 -0
- package/src/domain/scanner/confidence.ts +156 -0
- package/src/domain/scanner/constants.ts +13 -0
- package/src/domain/scanner/create-scanner.ts +573 -0
- package/src/domain/scanner/cross-layer.test.ts +372 -0
- package/src/domain/scanner/cross-layer.ts +232 -0
- package/src/domain/scanner/data/ai-packages.ts +82 -0
- package/src/domain/scanner/debt-calculator.test.ts +89 -0
- package/src/domain/scanner/debt-calculator.ts +111 -0
- package/src/domain/scanner/drift.test.ts +191 -0
- package/src/domain/scanner/drift.ts +73 -0
- package/src/domain/scanner/evidence-store.test.ts +207 -0
- package/src/domain/scanner/evidence-store.ts +195 -0
- package/src/domain/scanner/evidence.test.ts +104 -0
- package/src/domain/scanner/evidence.ts +71 -0
- package/src/domain/scanner/external/bandit-runner.test.ts +45 -0
- package/src/domain/scanner/external/bandit-runner.ts +90 -0
- package/src/domain/scanner/external/checks.ts +321 -0
- package/src/domain/scanner/external/dedup.test.ts +79 -0
- package/src/domain/scanner/external/dedup.ts +94 -0
- package/src/domain/scanner/external/detect-secrets-runner.test.ts +58 -0
- package/src/domain/scanner/external/detect-secrets-runner.ts +81 -0
- package/src/domain/scanner/external/external-scanner.test.ts +221 -0
- package/src/domain/scanner/external/external-scanner.ts +36 -0
- package/src/domain/scanner/external/finding-mapper.test.ts +95 -0
- package/src/domain/scanner/external/finding-mapper.ts +138 -0
- package/src/domain/scanner/external/index.ts +15 -0
- package/src/domain/scanner/external/mappings.ts +93 -0
- package/src/domain/scanner/external/modelscan-runner.test.ts +35 -0
- package/src/domain/scanner/external/modelscan-runner.ts +101 -0
- package/src/domain/scanner/external/path-utils.ts +8 -0
- package/src/domain/scanner/external/runner-port.ts +45 -0
- package/src/domain/scanner/external/semgrep-runner.test.ts +52 -0
- package/src/domain/scanner/external/semgrep-runner.ts +94 -0
- package/src/domain/scanner/external/types.ts +32 -0
- package/src/domain/scanner/finding-attribution.test.ts +444 -0
- package/src/domain/scanner/finding-attribution.ts +195 -0
- package/src/domain/scanner/finding-explainer.test.ts +157 -0
- package/src/domain/scanner/finding-explainer.ts +73 -0
- package/src/domain/scanner/fix-diff-builder.test.ts +272 -0
- package/src/domain/scanner/fix-diff-builder.ts +477 -0
- package/src/domain/scanner/import-graph.test.ts +162 -0
- package/src/domain/scanner/import-graph.ts +198 -0
- package/src/domain/scanner/languages/adapter.test.ts +105 -0
- package/src/domain/scanner/languages/adapter.ts +239 -0
- package/src/domain/scanner/layers/index.ts +24 -0
- package/src/domain/scanner/layers/layer1-files.ts +54 -0
- package/src/domain/scanner/layers/layer2-docs.test.ts +1207 -0
- package/src/domain/scanner/layers/layer2-docs.ts +297 -0
- package/src/domain/scanner/layers/layer2-parsing.ts +217 -0
- package/src/domain/scanner/layers/layer3-config.test.ts +187 -0
- package/src/domain/scanner/layers/layer3-config.ts +279 -0
- package/src/domain/scanner/layers/layer3-parsers.ts +73 -0
- package/src/domain/scanner/layers/layer4-patterns.test.ts +397 -0
- package/src/domain/scanner/layers/layer4-patterns.ts +216 -0
- package/src/domain/scanner/layers/layer5-docs.test.ts +99 -0
- package/src/domain/scanner/layers/layer5-docs.ts +250 -0
- package/src/domain/scanner/layers/layer5-llm.test.ts +146 -0
- package/src/domain/scanner/layers/layer5-llm.ts +262 -0
- package/src/domain/scanner/layers/layer5-targeted.test.ts +93 -0
- package/src/domain/scanner/layers/layer5-targeted.ts +233 -0
- package/src/domain/scanner/layers/lockfile-parsers.test.ts +320 -0
- package/src/domain/scanner/layers/lockfile-parsers.ts +184 -0
- package/src/domain/scanner/regulation-version.test.ts +54 -0
- package/src/domain/scanner/regulation-version.ts +23 -0
- package/src/domain/scanner/role-filter.test.ts +116 -0
- package/src/domain/scanner/role-filter.ts +51 -0
- package/src/domain/scanner/rules/banned-packages-data.ts +553 -0
- package/src/domain/scanner/rules/banned-packages-sdk.ts +65 -0
- package/src/domain/scanner/rules/banned-packages.test.ts +249 -0
- package/src/domain/scanner/rules/banned-packages.ts +55 -0
- package/src/domain/scanner/rules/comment-filter.test.ts +115 -0
- package/src/domain/scanner/rules/comment-filter.ts +297 -0
- package/src/domain/scanner/rules/index.ts +9 -0
- package/src/domain/scanner/rules/nhi-patterns.test.ts +128 -0
- package/src/domain/scanner/rules/nhi-patterns.ts +60 -0
- package/src/domain/scanner/rules/pattern-rules.ts +1152 -0
- package/src/domain/scanner/sbom.test.ts +136 -0
- package/src/domain/scanner/sbom.ts +103 -0
- package/src/domain/scanner/scan-cache.test.ts +136 -0
- package/src/domain/scanner/scan-cache.ts +115 -0
- package/src/domain/scanner/scanner.test.ts +125 -0
- package/src/domain/scanner/score-calculator.test.ts +363 -0
- package/src/domain/scanner/score-calculator.ts +189 -0
- package/src/domain/scanner/security-score.test.ts +107 -0
- package/src/domain/scanner/security-score.ts +116 -0
- package/src/domain/scanner/source-filter.ts +24 -0
- package/src/domain/scanner/validators.ts +223 -0
- package/src/domain/shared/compliance-constants.ts +48 -0
- package/src/domain/shared/disclosure-patterns.ts +16 -0
- package/src/domain/shared/index.ts +6 -0
- package/src/domain/shared/parse-dependencies.ts +21 -0
- package/src/domain/supply-chain/dependency-analyzer.ts +138 -0
- package/src/domain/supply-chain/index.ts +3 -0
- package/src/domain/supply-chain/supply-chain.test.ts +211 -0
- package/src/domain/supply-chain/types.ts +32 -0
- package/src/domain/whatif/config-fixer.ts +187 -0
- package/src/domain/whatif/index.ts +6 -0
- package/src/domain/whatif/scenario-engine.ts +121 -0
- package/src/domain/whatif/simulate-actions.test.ts +161 -0
- package/src/domain/whatif/simulate-actions.ts +114 -0
- package/src/domain/whatif/whatif.test.ts +135 -0
- package/src/e2e/gaps-e2e.test.ts +259 -0
- package/src/e2e/smoke.test.ts +101 -0
- package/src/hooks/hooks-export.test.ts +81 -0
- package/src/hooks/installer.ts +113 -0
- package/src/http/cors.test.ts +38 -0
- package/src/http/create-router.ts +259 -0
- package/src/http/routes/agent.route.ts +380 -0
- package/src/http/routes/audit.route.ts +66 -0
- package/src/http/routes/badge.route.ts +23 -0
- package/src/http/routes/cert.route.ts +66 -0
- package/src/http/routes/chat.route.ts +228 -0
- package/src/http/routes/cost.route.ts +33 -0
- package/src/http/routes/debt.route.ts +29 -0
- package/src/http/routes/disclaimer.route.ts +64 -0
- package/src/http/routes/eval.route.ts +161 -0
- package/src/http/routes/events.route.test.ts +108 -0
- package/src/http/routes/events.route.ts +71 -0
- package/src/http/routes/external-scan.route.ts +24 -0
- package/src/http/routes/file.route.ts +54 -0
- package/src/http/routes/fix.route.ts +219 -0
- package/src/http/routes/frameworks.route.test.ts +66 -0
- package/src/http/routes/frameworks.route.ts +36 -0
- package/src/http/routes/git.route.ts +27 -0
- package/src/http/routes/guided-onboarding.route.ts +65 -0
- package/src/http/routes/import.route.ts +64 -0
- package/src/http/routes/jurisdiction.route.ts +22 -0
- package/src/http/routes/obligations.route.test.ts +122 -0
- package/src/http/routes/obligations.route.ts +110 -0
- package/src/http/routes/onboarding.route.ts +53 -0
- package/src/http/routes/provider.route.ts +42 -0
- package/src/http/routes/proxy.route.ts +40 -0
- package/src/http/routes/redteam.route.ts +84 -0
- package/src/http/routes/report.route.ts +29 -0
- package/src/http/routes/scan.route.ts +104 -0
- package/src/http/routes/share.route.ts +44 -0
- package/src/http/routes/shell.route.ts +27 -0
- package/src/http/routes/status.route.ts +66 -0
- package/src/http/routes/supply-chain.route.ts +121 -0
- package/src/http/routes/sync.route.ts +328 -0
- package/src/http/routes/tools.route.ts +29 -0
- package/src/http/routes/whatif.route.ts +96 -0
- package/src/http/utils/validation.ts +31 -0
- package/src/index.ts +1 -0
- package/src/infra/bundle-fetcher.ts +77 -0
- package/src/infra/cache-storage.ts +34 -0
- package/src/infra/event-bus.ts +31 -0
- package/src/infra/file-collector.ts +61 -0
- package/src/infra/file-ops-adapter.ts +95 -0
- package/src/infra/file-watcher.test.ts +90 -0
- package/src/infra/file-watcher.ts +106 -0
- package/src/infra/git-adapter.ts +93 -0
- package/src/infra/git-history-adapter.ts +41 -0
- package/src/infra/headless-browser.ts +178 -0
- package/src/infra/llm-adapter.test.ts +83 -0
- package/src/infra/llm-adapter.ts +86 -0
- package/src/infra/logger.ts +27 -0
- package/src/infra/project-config.test.ts +74 -0
- package/src/infra/project-config.ts +35 -0
- package/src/infra/rate-limiter.test.ts +36 -0
- package/src/infra/rate-limiter.ts +34 -0
- package/src/infra/retry.ts +46 -0
- package/src/infra/saas-client.ts +123 -0
- package/src/infra/search-adapter.ts +113 -0
- package/src/infra/shell-adapter.ts +68 -0
- package/src/infra/tool-manager.test.ts +99 -0
- package/src/infra/tool-manager.ts +197 -0
- package/src/llm/agents/agent-modes.test.ts +44 -0
- package/src/llm/agents/modes.ts +68 -0
- package/src/llm/routing/cost-routing.test.ts +37 -0
- package/src/llm/routing/cost-tracker.ts +74 -0
- package/src/llm/routing/model-routing.test.ts +79 -0
- package/src/llm/routing/model-routing.ts +38 -0
- package/src/llm/routing/pricing.ts +19 -0
- package/src/llm/sse-protocol.ts +77 -0
- package/src/llm/tool-definitions.ts +83 -0
- package/src/llm/tool-executors.ts +80 -0
- package/src/llm/tools/types.ts +13 -0
- package/src/mcp/create-mcp-stack.ts +82 -0
- package/src/mcp/handlers.ts +245 -0
- package/src/mcp/index.ts +28 -0
- package/src/mcp/mcp-server.test.ts +80 -0
- package/src/mcp/server.ts +79 -0
- package/src/mcp/tools.ts +48 -0
- package/src/onboarding/auto-detect.ts +164 -0
- package/src/onboarding/onboarding.test.ts +89 -0
- package/src/onboarding/profile.ts +169 -0
- package/src/onboarding/questions.ts +112 -0
- package/src/onboarding/wizard.ts +66 -0
- package/src/output/github-issue.ts +32 -0
- package/src/output/json-output.ts +67 -0
- package/src/ports/browser.port.ts +23 -0
- package/src/ports/events.port.ts +28 -0
- package/src/ports/llm.port.ts +23 -0
- package/src/ports/logger.port.ts +6 -0
- package/src/ports/process.port.ts +6 -0
- package/src/ports/scanner.port.ts +15 -0
- package/src/server.ts +134 -0
- package/src/services/badge-service.ts +67 -0
- package/src/services/chat-service.test.ts +162 -0
- package/src/services/chat-service.ts +152 -0
- package/src/services/cost-service.ts +52 -0
- package/src/services/debt-service.ts +65 -0
- package/src/services/eval-integration.test.ts +132 -0
- package/src/services/eval-service.test.ts +373 -0
- package/src/services/eval-service.ts +463 -0
- package/src/services/external-scan-service.ts +60 -0
- package/src/services/file-service.ts +37 -0
- package/src/services/fix-service.test.ts +470 -0
- package/src/services/fix-service.ts +648 -0
- package/src/services/framework-service.test.ts +159 -0
- package/src/services/framework-service.ts +67 -0
- package/src/services/onboarding-service.ts +165 -0
- package/src/services/passport-audit.ts +244 -0
- package/src/services/passport-documents.ts +258 -0
- package/src/services/passport-service-utils.ts +72 -0
- package/src/services/passport-service.test.ts +251 -0
- package/src/services/passport-service.ts +339 -0
- package/src/services/proxy-service.ts +81 -0
- package/src/services/report-service.ts +72 -0
- package/src/services/scan-service.test.ts +470 -0
- package/src/services/scan-service.ts +335 -0
- package/src/services/share-service.ts +108 -0
- package/src/services/shared/backup.ts +23 -0
- package/src/services/status-service.ts +38 -0
- package/src/services/undo-service.test.ts +190 -0
- package/src/services/undo-service.ts +144 -0
- package/src/test-helpers/factories.ts +116 -0
- package/src/types/common.schemas.ts +147 -0
- package/src/types/common.types.ts +292 -0
- package/src/types/contract.test.ts +217 -0
- package/src/types/errors.ts +52 -0
- package/src/types/framework.types.ts +87 -0
- package/src/types/passport-schemas.ts +241 -0
- package/src/types/passport.types.ts +296 -0
- package/src/version.ts +1 -0
- package/tsconfig.json +20 -0
- package/vitest.config.ts +9 -0
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
import type { Finding } from '../../../types/common.types.js';
|
|
2
|
+
|
|
3
|
+
export type PromptType = 'code_pattern_check' | 'documentation_check' | 'config_check' | 'architecture_check' | 'data_handling_check';
|
|
4
|
+
|
|
5
|
+
export interface CodeSnippet {
|
|
6
|
+
readonly file: string;
|
|
7
|
+
readonly startLine: number;
|
|
8
|
+
readonly endLine: number;
|
|
9
|
+
readonly content: string;
|
|
10
|
+
readonly relevance: number;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface L5Result {
|
|
14
|
+
readonly findingId: string;
|
|
15
|
+
readonly originalConfidence: number;
|
|
16
|
+
readonly newConfidence: number;
|
|
17
|
+
readonly verdict: 'pass' | 'fail' | 'uncertain';
|
|
18
|
+
readonly reasoning: string;
|
|
19
|
+
readonly evidence: readonly string[];
|
|
20
|
+
readonly promptType: PromptType;
|
|
21
|
+
readonly cost: number;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface L5AnalyzerDeps {
|
|
25
|
+
readonly callLlm: (prompt: string) => Promise<{ text: string; inputTokens: number; outputTokens: number }>;
|
|
26
|
+
readonly readFile: (path: string) => Promise<string>;
|
|
27
|
+
readonly calculateCost: (model: string, inputTokens: number, outputTokens: number) => number;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const MAX_FINDINGS_PER_SCAN = 20;
|
|
31
|
+
const MAX_SNIPPET_LINES = 500;
|
|
32
|
+
const UNCERTAIN_MIN = 40;
|
|
33
|
+
const UNCERTAIN_MAX = 70;
|
|
34
|
+
|
|
35
|
+
const PROMPT_KEYWORDS: Record<PromptType, readonly string[]> = {
|
|
36
|
+
data_handling_check: ['data', 'privacy', 'retention', 'biometric', 'consent'],
|
|
37
|
+
code_pattern_check: ['disclosure', 'logging', 'transparency', 'interaction', 'content-marking'],
|
|
38
|
+
documentation_check: ['documentation', 'literacy', 'policy', 'report', 'conformity'],
|
|
39
|
+
config_check: ['config', 'metadata', 'environment', 'deployment'],
|
|
40
|
+
architecture_check: ['monitoring', 'oversight', 'audit', 'kill-switch'],
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export const selectPromptType = (finding: Finding): PromptType => {
|
|
44
|
+
const text = `${finding.checkId} ${finding.message}`.toLowerCase();
|
|
45
|
+
|
|
46
|
+
const isPromptType = (s: string): s is PromptType =>
|
|
47
|
+
s in PROMPT_KEYWORDS;
|
|
48
|
+
|
|
49
|
+
for (const [type, keywords] of Object.entries(PROMPT_KEYWORDS)) {
|
|
50
|
+
if (keywords.some((kw) => text.includes(kw)) && isPromptType(type)) {
|
|
51
|
+
return type;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return 'code_pattern_check';
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
export const buildL5Prompt = (promptType: PromptType, finding: Finding, snippets: readonly CodeSnippet[]): string => {
|
|
58
|
+
const snippetText = snippets
|
|
59
|
+
.map((s) => `--- ${s.file}:${s.startLine}-${s.endLine} ---\n${s.content}`)
|
|
60
|
+
.join('\n\n');
|
|
61
|
+
|
|
62
|
+
const base = `You are a compliance auditor analyzing an AI project for EU AI Act compliance.
|
|
63
|
+
|
|
64
|
+
Finding: ${finding.checkId} — ${finding.message}
|
|
65
|
+
Severity: ${finding.severity}
|
|
66
|
+
Article: ${finding.articleReference ?? 'N/A'}
|
|
67
|
+
Current confidence: ${finding.confidence ?? 50}%
|
|
68
|
+
|
|
69
|
+
Analyze the following code snippets and determine if this compliance requirement is met.
|
|
70
|
+
|
|
71
|
+
${snippetText}
|
|
72
|
+
|
|
73
|
+
Respond in JSON format:
|
|
74
|
+
{
|
|
75
|
+
"verdict": "pass" | "fail" | "uncertain",
|
|
76
|
+
"confidence": 0-100,
|
|
77
|
+
"reasoning": "brief explanation",
|
|
78
|
+
"evidence": ["file:line — description"]
|
|
79
|
+
}`;
|
|
80
|
+
|
|
81
|
+
const typeInstructions: Record<PromptType, string> = {
|
|
82
|
+
code_pattern_check: 'Focus on: implementation patterns, components, middleware, function calls.',
|
|
83
|
+
documentation_check: 'Focus on: document structure, required sections, completeness.',
|
|
84
|
+
config_check: 'Focus on: configuration values, environment variables, security settings.',
|
|
85
|
+
architecture_check: 'Focus on: system design, monitoring, logging, human oversight.',
|
|
86
|
+
data_handling_check: 'Focus on: data flow, storage, consent, retention policies.',
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
return `${base}\n\n${typeInstructions[promptType]}`;
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
export const extractSnippets = (
|
|
93
|
+
finding: Finding,
|
|
94
|
+
fileContents: ReadonlyMap<string, string>,
|
|
95
|
+
): readonly CodeSnippet[] => {
|
|
96
|
+
const keywords = finding.checkId.split('-').concat(
|
|
97
|
+
finding.message.toLowerCase().split(/\s+/).filter((w) => w.length > 4),
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
const snippets: CodeSnippet[] = [];
|
|
101
|
+
let totalLines = 0;
|
|
102
|
+
|
|
103
|
+
for (const [file, content] of fileContents) {
|
|
104
|
+
if (totalLines >= MAX_SNIPPET_LINES) break;
|
|
105
|
+
|
|
106
|
+
const lines = content.split('\n');
|
|
107
|
+
let relevance = 0;
|
|
108
|
+
const lowerContent = content.toLowerCase();
|
|
109
|
+
|
|
110
|
+
for (const kw of keywords) {
|
|
111
|
+
if (lowerContent.includes(kw.toLowerCase())) relevance += 0.2;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (relevance > 0) {
|
|
115
|
+
const endLine = Math.min(lines.length, MAX_SNIPPET_LINES - totalLines);
|
|
116
|
+
snippets.push({
|
|
117
|
+
file,
|
|
118
|
+
startLine: 1,
|
|
119
|
+
endLine,
|
|
120
|
+
content: lines.slice(0, endLine).join('\n'),
|
|
121
|
+
relevance: Math.min(1, relevance),
|
|
122
|
+
});
|
|
123
|
+
totalLines += endLine;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return snippets
|
|
128
|
+
.sort((a, b) => b.relevance - a.relevance)
|
|
129
|
+
.slice(0, 5);
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
export const createLayer5 = (deps: L5AnalyzerDeps) => {
|
|
133
|
+
const { callLlm, calculateCost } = deps;
|
|
134
|
+
|
|
135
|
+
const isUncertain = (finding: Finding): boolean => {
|
|
136
|
+
const confidence = finding.confidence ?? 50;
|
|
137
|
+
return confidence >= UNCERTAIN_MIN && confidence <= UNCERTAIN_MAX;
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
const analyzeFindings = async (
|
|
141
|
+
findings: readonly Finding[],
|
|
142
|
+
fileContents: ReadonlyMap<string, string>,
|
|
143
|
+
): Promise<readonly L5Result[]> => {
|
|
144
|
+
const uncertainFindings = findings.filter(isUncertain).slice(0, MAX_FINDINGS_PER_SCAN);
|
|
145
|
+
const results: L5Result[] = [];
|
|
146
|
+
|
|
147
|
+
for (const finding of uncertainFindings) {
|
|
148
|
+
const promptType = selectPromptType(finding);
|
|
149
|
+
const snippets = extractSnippets(finding, fileContents);
|
|
150
|
+
const prompt = buildL5Prompt(promptType, finding, snippets);
|
|
151
|
+
|
|
152
|
+
try {
|
|
153
|
+
const response = await callLlm(prompt);
|
|
154
|
+
const cost = calculateCost('claude-sonnet-4', response.inputTokens, response.outputTokens);
|
|
155
|
+
|
|
156
|
+
// Parse LLM response
|
|
157
|
+
const jsonMatch = response.text.match(/\{[\s\S]*\}/);
|
|
158
|
+
if (jsonMatch) {
|
|
159
|
+
const isRec = (v: unknown): v is Record<string, unknown> =>
|
|
160
|
+
typeof v === 'object' && v !== null;
|
|
161
|
+
const raw: unknown = JSON.parse(jsonMatch[0]);
|
|
162
|
+
const parsed = isRec(raw) ? raw : {};
|
|
163
|
+
|
|
164
|
+
const rawVerdict = typeof parsed['verdict'] === 'string' ? parsed['verdict'] : '';
|
|
165
|
+
const verdict = rawVerdict === 'pass' ? 'pass'
|
|
166
|
+
: rawVerdict === 'fail' ? 'fail'
|
|
167
|
+
: 'uncertain';
|
|
168
|
+
const rawEvidence = Array.isArray(parsed['evidence'])
|
|
169
|
+
? parsed['evidence'].filter((e): e is string => typeof e === 'string')
|
|
170
|
+
: [];
|
|
171
|
+
results.push({
|
|
172
|
+
findingId: finding.checkId,
|
|
173
|
+
originalConfidence: finding.confidence ?? 50,
|
|
174
|
+
newConfidence: typeof parsed['confidence'] === 'number' ? parsed['confidence'] : (finding.confidence ?? 50),
|
|
175
|
+
verdict,
|
|
176
|
+
reasoning: typeof parsed['reasoning'] === 'string' ? parsed['reasoning'] : 'Could not parse LLM response',
|
|
177
|
+
evidence: rawEvidence,
|
|
178
|
+
promptType,
|
|
179
|
+
cost,
|
|
180
|
+
});
|
|
181
|
+
} else {
|
|
182
|
+
results.push({
|
|
183
|
+
findingId: finding.checkId,
|
|
184
|
+
originalConfidence: finding.confidence ?? 50,
|
|
185
|
+
newConfidence: finding.confidence ?? 50,
|
|
186
|
+
verdict: 'uncertain',
|
|
187
|
+
reasoning: 'Could not parse LLM response',
|
|
188
|
+
evidence: [],
|
|
189
|
+
promptType,
|
|
190
|
+
cost,
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
} catch {
|
|
194
|
+
// On error, keep original status
|
|
195
|
+
results.push({
|
|
196
|
+
findingId: finding.checkId,
|
|
197
|
+
originalConfidence: finding.confidence ?? 50,
|
|
198
|
+
newConfidence: finding.confidence ?? 50,
|
|
199
|
+
verdict: 'uncertain',
|
|
200
|
+
reasoning: 'LLM analysis failed',
|
|
201
|
+
evidence: [],
|
|
202
|
+
promptType,
|
|
203
|
+
cost: 0,
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
return results;
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
const applyResults = (
|
|
212
|
+
findings: readonly Finding[],
|
|
213
|
+
l5Results: readonly L5Result[],
|
|
214
|
+
): readonly Finding[] => {
|
|
215
|
+
const resultMap = new Map(l5Results.map((r) => [r.findingId, r]));
|
|
216
|
+
|
|
217
|
+
return findings.map((f) => {
|
|
218
|
+
const result = resultMap.get(f.checkId);
|
|
219
|
+
if (!result) return f;
|
|
220
|
+
|
|
221
|
+
return {
|
|
222
|
+
...f,
|
|
223
|
+
type: result.verdict === 'pass' ? 'pass' : result.verdict === 'fail' ? 'fail' : f.type,
|
|
224
|
+
confidence: result.newConfidence,
|
|
225
|
+
confidenceLevel: result.newConfidence >= 95 ? 'PASS'
|
|
226
|
+
: result.newConfidence >= 70 ? 'LIKELY_PASS'
|
|
227
|
+
: result.newConfidence >= 40 ? 'UNCERTAIN'
|
|
228
|
+
: result.newConfidence >= 5 ? 'LIKELY_FAIL'
|
|
229
|
+
: 'FAIL',
|
|
230
|
+
severity: result.verdict === 'pass' ? 'info' : f.severity,
|
|
231
|
+
// Populate fix from L5 reasoning when verdict is fail and finding lacks a fix
|
|
232
|
+
fix: (result.verdict === 'fail' && !f.fix && result.reasoning)
|
|
233
|
+
? result.reasoning
|
|
234
|
+
: f.fix,
|
|
235
|
+
l5Analyzed: true,
|
|
236
|
+
};
|
|
237
|
+
});
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
const getTotalCost = (results: readonly L5Result[]): number =>
|
|
241
|
+
results.reduce((sum, r) => sum + r.cost, 0);
|
|
242
|
+
|
|
243
|
+
/** Raw LLM call for custom prompts (e.g. doc validation). */
|
|
244
|
+
const callRaw = async (prompt: string): Promise<{ text: string; cost: number }> => {
|
|
245
|
+
const response = await callLlm(prompt);
|
|
246
|
+
const cost = calculateCost('claude-sonnet-4', response.inputTokens, response.outputTokens);
|
|
247
|
+
return { text: response.text, cost };
|
|
248
|
+
};
|
|
249
|
+
|
|
250
|
+
return Object.freeze({
|
|
251
|
+
isUncertain,
|
|
252
|
+
analyzeFindings,
|
|
253
|
+
applyResults,
|
|
254
|
+
getTotalCost,
|
|
255
|
+
selectPromptType,
|
|
256
|
+
extractSnippets,
|
|
257
|
+
buildL5Prompt,
|
|
258
|
+
callRaw,
|
|
259
|
+
});
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
export type Layer5Analyzer = ReturnType<typeof createLayer5>;
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { selectUncertainFindings, buildTargetedPrompts, applyTargetedResults, estimateTargetedCost } from './layer5-targeted.js';
|
|
3
|
+
import type { Finding } from '../../../types/common.types.js';
|
|
4
|
+
|
|
5
|
+
const makeFinding = (overrides: Partial<Finding> = {}): Finding => ({
|
|
6
|
+
checkId: 'l4-kill-switch',
|
|
7
|
+
type: 'fail',
|
|
8
|
+
message: 'No kill switch found',
|
|
9
|
+
severity: 'medium',
|
|
10
|
+
confidence: 65,
|
|
11
|
+
confidenceLevel: 'UNCERTAIN',
|
|
12
|
+
...overrides,
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
describe('selectUncertainFindings', () => {
|
|
16
|
+
it('selects findings with confidence 50-80', () => {
|
|
17
|
+
const findings = [
|
|
18
|
+
makeFinding({ confidence: 30 }), // too low
|
|
19
|
+
makeFinding({ confidence: 65 }), // in range
|
|
20
|
+
makeFinding({ confidence: 90 }), // too high
|
|
21
|
+
makeFinding({ confidence: 75 }), // in range
|
|
22
|
+
];
|
|
23
|
+
const selected = selectUncertainFindings(findings);
|
|
24
|
+
expect(selected.length).toBe(2);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('respects maxFindings limit', () => {
|
|
28
|
+
const findings = Array.from({ length: 30 }, (_, i) =>
|
|
29
|
+
makeFinding({ checkId: `l4-test-${i}`, confidence: 60 }),
|
|
30
|
+
);
|
|
31
|
+
const selected = selectUncertainFindings(findings, { maxFindings: 5, budgetLimit: 0.05, confidenceMin: 50, confidenceMax: 80 });
|
|
32
|
+
expect(selected.length).toBe(5);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('treats undefined confidence as 50', () => {
|
|
36
|
+
const findings = [makeFinding({ confidence: undefined })];
|
|
37
|
+
const selected = selectUncertainFindings(findings);
|
|
38
|
+
expect(selected.length).toBe(1);
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
describe('buildTargetedPrompts', () => {
|
|
43
|
+
it('builds obligation-specific prompts', () => {
|
|
44
|
+
const findings = [makeFinding({ checkId: 'l4-kill-switch', file: 'src/safety.ts', line: 15 })];
|
|
45
|
+
const files = new Map([['src/safety.ts', 'const killSwitch = () => process.exit(0);']]);
|
|
46
|
+
const prompts = buildTargetedPrompts(findings, files);
|
|
47
|
+
expect(prompts.length).toBe(1);
|
|
48
|
+
expect(prompts[0]?.prompt).toContain('Art. 14(4)');
|
|
49
|
+
expect(prompts[0]?.prompt).toContain('kill switch');
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('includes code context in prompt', () => {
|
|
53
|
+
const findings = [makeFinding({ file: 'src/chat.ts', line: 5 })];
|
|
54
|
+
const files = new Map([['src/chat.ts', 'line1\nline2\nline3\nline4\nconst x = 1;\nline6']]);
|
|
55
|
+
const prompts = buildTargetedPrompts(findings, files);
|
|
56
|
+
expect(prompts[0]?.prompt).toContain('line1');
|
|
57
|
+
expect(prompts[0]?.contextFiles).toContain('src/chat.ts');
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
describe('applyTargetedResults', () => {
|
|
62
|
+
it('flips fail to pass when L5 disproves', () => {
|
|
63
|
+
const findings = [makeFinding({ type: 'fail', confidence: 65 })];
|
|
64
|
+
const results = [{ findingId: 'l4-kill-switch', confirmed: false, newConfidence: 90, explanation: 'This is a real kill switch', cost: 0.001 }];
|
|
65
|
+
const updated = applyTargetedResults(findings, results);
|
|
66
|
+
expect(updated[0]?.type).toBe('pass');
|
|
67
|
+
expect(updated[0]?.confidence).toBe(90);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it('increases confidence when L5 confirms fail', () => {
|
|
71
|
+
const findings = [makeFinding({ type: 'fail', confidence: 65 })];
|
|
72
|
+
const results = [{ findingId: 'l4-kill-switch', confirmed: true, newConfidence: 95, explanation: 'Confirmed missing', cost: 0.001 }];
|
|
73
|
+
const updated = applyTargetedResults(findings, results);
|
|
74
|
+
expect(updated[0]?.type).toBe('fail');
|
|
75
|
+
expect(updated[0]?.confidence).toBe(95);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it('leaves unmatched findings unchanged', () => {
|
|
79
|
+
const findings = [makeFinding({ checkId: 'l4-other' })];
|
|
80
|
+
const results = [{ findingId: 'l4-kill-switch', confirmed: true, newConfidence: 95, explanation: 'test', cost: 0 }];
|
|
81
|
+
const updated = applyTargetedResults(findings, results);
|
|
82
|
+
expect(updated[0]).toEqual(findings[0]);
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
describe('estimateTargetedCost', () => {
|
|
87
|
+
it('estimates cost for prompts', () => {
|
|
88
|
+
const prompts = [{ findingId: 'test', prompt: 'x'.repeat(4000), contextFiles: [], estimatedTokens: 1000 }];
|
|
89
|
+
const cost = estimateTargetedCost(prompts);
|
|
90
|
+
expect(cost).toBeGreaterThan(0);
|
|
91
|
+
expect(cost).toBeLessThan(0.01); // Should be cheap
|
|
92
|
+
});
|
|
93
|
+
});
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Targeted L5: Sends ONLY findings with confidence 50-80% to LLM
|
|
3
|
+
* with structured, obligation-specific questions.
|
|
4
|
+
* Cost: ~$0.01 vs $0.10 for full deep scan.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { Finding } from '../../../types/common.types.js';
|
|
8
|
+
import type { ImportGraph } from '../import-graph.js';
|
|
9
|
+
|
|
10
|
+
export interface TargetedL5Config {
|
|
11
|
+
readonly maxFindings: number; // default 20
|
|
12
|
+
readonly budgetLimit: number; // max $ per scan, default 0.05
|
|
13
|
+
readonly confidenceMin: number; // default 50
|
|
14
|
+
readonly confidenceMax: number; // default 80
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface TargetedL5Prompt {
|
|
18
|
+
readonly findingId: string;
|
|
19
|
+
readonly prompt: string;
|
|
20
|
+
readonly contextFiles: readonly string[];
|
|
21
|
+
readonly estimatedTokens: number;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface TargetedL5Result {
|
|
25
|
+
readonly findingId: string;
|
|
26
|
+
readonly confirmed: boolean;
|
|
27
|
+
readonly newConfidence: number;
|
|
28
|
+
readonly explanation: string;
|
|
29
|
+
readonly cost: number;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const DEFAULT_CONFIG: TargetedL5Config = {
|
|
33
|
+
maxFindings: 20,
|
|
34
|
+
budgetLimit: 0.05,
|
|
35
|
+
confidenceMin: 50,
|
|
36
|
+
confidenceMax: 80,
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Obligation-specific prompt templates.
|
|
41
|
+
* Each template asks a concrete yes/no question about a specific EU AI Act article.
|
|
42
|
+
*/
|
|
43
|
+
const OBLIGATION_PROMPTS: Record<string, string> = {
|
|
44
|
+
'kill-switch': `L4 scanner found a potential kill switch / emergency stop mechanism.
|
|
45
|
+
Question: Does this code implement a genuine kill switch that satisfies EU AI Act Art. 14(4) —
|
|
46
|
+
allowing a human to interrupt, stop, or disable the AI system?
|
|
47
|
+
A kill switch must: (1) be accessible to authorized personnel, (2) work reliably under all conditions,
|
|
48
|
+
(3) not require AI involvement to function.`,
|
|
49
|
+
|
|
50
|
+
'disclosure': `L4 scanner found a potential AI disclosure mechanism.
|
|
51
|
+
Question: Does this code implement AI disclosure that satisfies EU AI Act Art. 50(1) —
|
|
52
|
+
informing natural persons that they are interacting with an AI system?
|
|
53
|
+
Disclosure must be: (1) clear and understandable, (2) provided before or at the start of interaction,
|
|
54
|
+
(3) include the AI system's capabilities and limitations.`,
|
|
55
|
+
|
|
56
|
+
'human-oversight': `L4 scanner found a potential human oversight mechanism.
|
|
57
|
+
Question: Does this code implement human oversight that satisfies EU AI Act Art. 14 —
|
|
58
|
+
enabling effective human oversight of the AI system during operation?
|
|
59
|
+
Oversight must include: (1) ability for human to understand AI decisions, (2) ability to intervene,
|
|
60
|
+
(3) ability to override or reverse AI decisions.`,
|
|
61
|
+
|
|
62
|
+
'logging': `L4 scanner found a potential interaction logging mechanism.
|
|
63
|
+
Question: Does this code implement logging that satisfies EU AI Act Art. 12 —
|
|
64
|
+
automatic recording of AI system events (logs) during operation?
|
|
65
|
+
Logging must: (1) capture input/output of AI interactions, (2) be traceable,
|
|
66
|
+
(3) have defined retention period (≥180 days for high-risk).`,
|
|
67
|
+
|
|
68
|
+
'content-marking': `L4 scanner found a potential content marking mechanism.
|
|
69
|
+
Question: Does this code implement content marking that satisfies EU AI Act Art. 50(2) —
|
|
70
|
+
marking AI-generated content as artificially generated or manipulated?
|
|
71
|
+
Marking must be: (1) machine-readable where technically feasible, (2) detectable and interoperable.`,
|
|
72
|
+
|
|
73
|
+
'data-governance': `L4 scanner found a potential data governance mechanism.
|
|
74
|
+
Question: Does this code implement data governance that satisfies EU AI Act Art. 10 —
|
|
75
|
+
ensuring training and validation data meets quality criteria?
|
|
76
|
+
Must include: (1) data quality examination, (2) bias assessment, (3) data provenance.`,
|
|
77
|
+
|
|
78
|
+
'bare-llm': `L4 scanner detected a potential bare LLM API call without compliance wrapper.
|
|
79
|
+
Question: Is this a direct LLM API call without transparency, logging, or safety controls?
|
|
80
|
+
A bare call is non-compliant if it: (1) lacks disclosure to users, (2) has no logging,
|
|
81
|
+
(3) has no content safety filtering.`,
|
|
82
|
+
|
|
83
|
+
'deployer-monitoring': `L4 scanner found a potential deployer monitoring mechanism.
|
|
84
|
+
Question: Does this code implement monitoring that satisfies EU AI Act Art. 26(5) —
|
|
85
|
+
deployer monitoring of AI system operation and reporting incidents?
|
|
86
|
+
Must include: (1) performance monitoring, (2) anomaly detection, (3) incident reporting.`,
|
|
87
|
+
|
|
88
|
+
'security-risk': `L4 scanner detected a potential security vulnerability.
|
|
89
|
+
Question: Does this code introduce a cybersecurity risk per EU AI Act Art. 15(4)?
|
|
90
|
+
Check for: (1) unsafe deserialization, (2) code injection vectors, (3) missing input validation.`,
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Select uncertain findings for targeted L5 analysis.
|
|
95
|
+
*/
|
|
96
|
+
export const selectUncertainFindings = (
|
|
97
|
+
findings: readonly Finding[],
|
|
98
|
+
config: TargetedL5Config = DEFAULT_CONFIG,
|
|
99
|
+
): readonly Finding[] => {
|
|
100
|
+
return findings
|
|
101
|
+
.filter((f) => {
|
|
102
|
+
const conf = f.confidence ?? 50;
|
|
103
|
+
return conf >= config.confidenceMin && conf <= config.confidenceMax;
|
|
104
|
+
})
|
|
105
|
+
.slice(0, config.maxFindings);
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Build structured prompts for targeted L5 analysis.
|
|
110
|
+
* Each prompt is obligation-specific with concrete questions.
|
|
111
|
+
*/
|
|
112
|
+
export const buildTargetedPrompts = (
|
|
113
|
+
findings: readonly Finding[],
|
|
114
|
+
fileContents: ReadonlyMap<string, string>,
|
|
115
|
+
importGraph?: ImportGraph,
|
|
116
|
+
): readonly TargetedL5Prompt[] => {
|
|
117
|
+
return findings.map((finding) => {
|
|
118
|
+
// Determine category for prompt template
|
|
119
|
+
const category = finding.checkId.replace(/^l4-/, '').replace(/-\d+$/, '');
|
|
120
|
+
const template = OBLIGATION_PROMPTS[category] ?? OBLIGATION_PROMPTS['bare-llm'] ?? '';
|
|
121
|
+
|
|
122
|
+
// Gather context: the finding's file + its imports from import-graph
|
|
123
|
+
const contextFiles: string[] = [];
|
|
124
|
+
if (finding.file) {
|
|
125
|
+
contextFiles.push(finding.file);
|
|
126
|
+
|
|
127
|
+
// Add imported files from import-graph (1 level deep)
|
|
128
|
+
if (importGraph) {
|
|
129
|
+
const node = importGraph.nodes.get(finding.file);
|
|
130
|
+
if (node) {
|
|
131
|
+
for (const imp of node.imports.slice(0, 3)) { // Max 3 related files
|
|
132
|
+
contextFiles.push(imp);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Build code context
|
|
139
|
+
const codeSnippets = contextFiles
|
|
140
|
+
.map((file) => {
|
|
141
|
+
const content = fileContents.get(file);
|
|
142
|
+
if (!content) return '';
|
|
143
|
+
// Extract relevant portion (around the finding line, or first 100 lines)
|
|
144
|
+
const lines = content.split('\n');
|
|
145
|
+
const targetLine = finding.line ?? 1;
|
|
146
|
+
const start = Math.max(0, targetLine - 20);
|
|
147
|
+
const end = Math.min(lines.length, targetLine + 30);
|
|
148
|
+
return `--- ${file}:${start + 1}-${end} ---\n${lines.slice(start, end).join('\n')}`;
|
|
149
|
+
})
|
|
150
|
+
.filter(Boolean)
|
|
151
|
+
.join('\n\n');
|
|
152
|
+
|
|
153
|
+
const prompt = `${template}
|
|
154
|
+
|
|
155
|
+
Finding: ${finding.checkId} — ${finding.message}
|
|
156
|
+
Severity: ${finding.severity}
|
|
157
|
+
Article: ${finding.articleReference ?? 'N/A'}
|
|
158
|
+
Current confidence: ${finding.confidence ?? 50}%
|
|
159
|
+
|
|
160
|
+
Code context:
|
|
161
|
+
${codeSnippets || 'No code context available'}
|
|
162
|
+
|
|
163
|
+
Respond ONLY in this JSON format:
|
|
164
|
+
{
|
|
165
|
+
"confirmed": true/false,
|
|
166
|
+
"confidence": 0-100,
|
|
167
|
+
"explanation": "brief explanation why this is/isn't compliant"
|
|
168
|
+
}`;
|
|
169
|
+
|
|
170
|
+
return {
|
|
171
|
+
findingId: finding.checkId,
|
|
172
|
+
prompt,
|
|
173
|
+
contextFiles,
|
|
174
|
+
estimatedTokens: Math.ceil(prompt.length / 4), // rough estimate
|
|
175
|
+
};
|
|
176
|
+
});
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Apply targeted L5 results back to findings.
|
|
181
|
+
*/
|
|
182
|
+
export const applyTargetedResults = (
|
|
183
|
+
findings: readonly Finding[],
|
|
184
|
+
results: readonly TargetedL5Result[],
|
|
185
|
+
): readonly Finding[] => {
|
|
186
|
+
const resultMap = new Map(results.map((r) => [r.findingId, r]));
|
|
187
|
+
|
|
188
|
+
return findings.map((f) => {
|
|
189
|
+
const result = resultMap.get(f.checkId);
|
|
190
|
+
if (!result) return f;
|
|
191
|
+
|
|
192
|
+
// If L5 confirms it's a real finding (fail), keep as fail with higher confidence
|
|
193
|
+
if (result.confirmed && f.type === 'fail') {
|
|
194
|
+
return {
|
|
195
|
+
...f,
|
|
196
|
+
confidence: result.newConfidence,
|
|
197
|
+
confidenceLevel: result.newConfidence >= 95 ? 'FAIL' : 'LIKELY_FAIL',
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// If L5 says it's actually compliant, flip to pass
|
|
202
|
+
if (!result.confirmed && f.type === 'fail') {
|
|
203
|
+
return {
|
|
204
|
+
...f,
|
|
205
|
+
type: 'pass' as const,
|
|
206
|
+
confidence: result.newConfidence,
|
|
207
|
+
confidenceLevel: result.newConfidence >= 95 ? 'PASS' : 'LIKELY_PASS',
|
|
208
|
+
message: `${f.message} [L5 verified: ${result.explanation}]`,
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// If L5 confirms pass, increase confidence
|
|
213
|
+
if (result.confirmed && f.type === 'pass') {
|
|
214
|
+
return {
|
|
215
|
+
...f,
|
|
216
|
+
confidence: result.newConfidence,
|
|
217
|
+
confidenceLevel: result.newConfidence >= 95 ? 'PASS' : 'LIKELY_PASS',
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
return f;
|
|
222
|
+
});
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Estimate total cost for targeted L5 analysis.
|
|
227
|
+
*/
|
|
228
|
+
export const estimateTargetedCost = (prompts: readonly TargetedL5Prompt[]): number => {
|
|
229
|
+
// Approximate: $0.003/1K input tokens + $0.015/1K output tokens (Claude Sonnet)
|
|
230
|
+
const inputCost = prompts.reduce((sum, p) => sum + p.estimatedTokens, 0) * 0.003 / 1000;
|
|
231
|
+
const outputCost = prompts.length * 200 * 0.015 / 1000; // ~200 tokens per response
|
|
232
|
+
return Math.round((inputCost + outputCost) * 10000) / 10000;
|
|
233
|
+
};
|