@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,97 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import type { Finding } from '../../types/common.types.js';
|
|
3
|
+
import type { ScanContext } from '../../ports/scanner.port.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Manual attestation entry — for checks that cannot be code-verified.
|
|
7
|
+
* Stored in `.complior/attestations.json`.
|
|
8
|
+
*/
|
|
9
|
+
export interface Attestation {
|
|
10
|
+
readonly checkId: string;
|
|
11
|
+
readonly attested: boolean;
|
|
12
|
+
readonly attestedBy: string;
|
|
13
|
+
readonly attestedAt: string;
|
|
14
|
+
readonly expiresAt?: string;
|
|
15
|
+
readonly evidence?: string;
|
|
16
|
+
readonly notes?: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const AttestationSchema = z.object({
|
|
20
|
+
checkId: z.string().min(1),
|
|
21
|
+
attested: z.boolean(),
|
|
22
|
+
attestedBy: z.string().min(1),
|
|
23
|
+
attestedAt: z.string().regex(/^\d{4}-\d{2}-\d{2}/),
|
|
24
|
+
expiresAt: z.string().regex(/^\d{4}-\d{2}-\d{2}/).optional(),
|
|
25
|
+
evidence: z.string().optional(),
|
|
26
|
+
notes: z.string().optional(),
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
const AttestationsFileSchema = z.object({
|
|
30
|
+
attestations: z.array(AttestationSchema),
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
export type AttestationsFile = z.infer<typeof AttestationsFileSchema>;
|
|
34
|
+
|
|
35
|
+
const ATTESTATIONS_PATH = '.complior/attestations.json';
|
|
36
|
+
|
|
37
|
+
/** Default attestation validity period: 12 months. */
|
|
38
|
+
const DEFAULT_EXPIRY_MONTHS = 12;
|
|
39
|
+
|
|
40
|
+
const isExpired = (att: Attestation, now: Date): boolean => {
|
|
41
|
+
if (att.expiresAt) {
|
|
42
|
+
return new Date(att.expiresAt) <= now;
|
|
43
|
+
}
|
|
44
|
+
// No explicit expiry → default 12 months from attestedAt
|
|
45
|
+
const attDate = new Date(att.attestedAt);
|
|
46
|
+
attDate.setMonth(attDate.getMonth() + DEFAULT_EXPIRY_MONTHS);
|
|
47
|
+
return attDate <= now;
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Load attestations from scan context files with Zod validation.
|
|
52
|
+
*/
|
|
53
|
+
const loadAttestations = (ctx: ScanContext): ReadonlyMap<string, Attestation> => {
|
|
54
|
+
const attestationFile = ctx.files.find((f) => f.relativePath === ATTESTATIONS_PATH);
|
|
55
|
+
if (!attestationFile) return new Map();
|
|
56
|
+
|
|
57
|
+
try {
|
|
58
|
+
const raw = JSON.parse(attestationFile.content);
|
|
59
|
+
const parsed = AttestationsFileSchema.parse(raw);
|
|
60
|
+
|
|
61
|
+
const now = new Date();
|
|
62
|
+
const map = new Map<string, Attestation>();
|
|
63
|
+
for (const att of parsed.attestations) {
|
|
64
|
+
if (att.attested && !isExpired(att, now)) {
|
|
65
|
+
map.set(att.checkId, att);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return map;
|
|
69
|
+
} catch {
|
|
70
|
+
return new Map();
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Apply manual attestations to findings: convert matching `fail` results to `pass`
|
|
76
|
+
* when a valid, non-expired attestation exists. Attested findings are marked with a note.
|
|
77
|
+
*/
|
|
78
|
+
export const applyAttestations = (
|
|
79
|
+
findings: readonly Finding[],
|
|
80
|
+
ctx: ScanContext,
|
|
81
|
+
): readonly Finding[] => {
|
|
82
|
+
const attestations = loadAttestations(ctx);
|
|
83
|
+
if (attestations.size === 0) return findings;
|
|
84
|
+
|
|
85
|
+
return findings.map((f) => {
|
|
86
|
+
if (f.type !== 'fail') return f;
|
|
87
|
+
|
|
88
|
+
const att = attestations.get(f.checkId);
|
|
89
|
+
if (!att) return f;
|
|
90
|
+
|
|
91
|
+
return {
|
|
92
|
+
...f,
|
|
93
|
+
type: 'pass' as const,
|
|
94
|
+
message: `${f.message} [manually attested by ${att.attestedBy} on ${att.attestedAt}]`,
|
|
95
|
+
};
|
|
96
|
+
});
|
|
97
|
+
};
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { checkAiDisclosure } from './ai-disclosure.js';
|
|
3
|
+
import { createScanFile, createScanCtx } from '../../../test-helpers/factories.js';
|
|
4
|
+
|
|
5
|
+
describe('checkAiDisclosure', () => {
|
|
6
|
+
it('passes when AI disclosure text found in UI code', () => {
|
|
7
|
+
const ctx = createScanCtx([
|
|
8
|
+
createScanFile('src/components/Chat.tsx', '<p>This is an AI-powered assistant</p>'),
|
|
9
|
+
]);
|
|
10
|
+
|
|
11
|
+
const results = checkAiDisclosure(ctx);
|
|
12
|
+
|
|
13
|
+
expect(results).toHaveLength(1);
|
|
14
|
+
expect(results[0].type).toBe('pass');
|
|
15
|
+
expect(results[0].checkId).toBe('ai-disclosure');
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it('passes when "artificial intelligence" disclosure found', () => {
|
|
19
|
+
const ctx = createScanCtx([
|
|
20
|
+
createScanFile('src/Bot.tsx', 'Powered by artificial intelligence technology'),
|
|
21
|
+
]);
|
|
22
|
+
|
|
23
|
+
const results = checkAiDisclosure(ctx);
|
|
24
|
+
|
|
25
|
+
expect(results).toHaveLength(1);
|
|
26
|
+
expect(results[0].type).toBe('pass');
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('fails when chat code exists without disclosure', () => {
|
|
30
|
+
const ctx = createScanCtx([
|
|
31
|
+
createScanFile('src/components/ChatWidget.tsx', 'function ChatWidget() { return <div>chatbot interface</div> }'),
|
|
32
|
+
]);
|
|
33
|
+
|
|
34
|
+
const results = checkAiDisclosure(ctx);
|
|
35
|
+
|
|
36
|
+
expect(results).toHaveLength(1);
|
|
37
|
+
expect(results[0].type).toBe('fail');
|
|
38
|
+
if (results[0].type === 'fail') {
|
|
39
|
+
expect(results[0].severity).toBe('high');
|
|
40
|
+
expect(results[0].obligationId).toBe('eu-ai-act-OBL-015');
|
|
41
|
+
expect(results[0].articleReference).toBe('Art. 50(1)');
|
|
42
|
+
expect(results[0].fix).toBeDefined();
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it('fails when /api/chat endpoint exists without disclosure', () => {
|
|
47
|
+
const ctx = createScanCtx([
|
|
48
|
+
createScanFile('src/routes/chat.ts', 'app.post("/api/chat", handler)'),
|
|
49
|
+
]);
|
|
50
|
+
|
|
51
|
+
const results = checkAiDisclosure(ctx);
|
|
52
|
+
|
|
53
|
+
expect(results).toHaveLength(1);
|
|
54
|
+
expect(results[0].type).toBe('fail');
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('skips when no chat or AI interaction code found', () => {
|
|
58
|
+
const ctx = createScanCtx([
|
|
59
|
+
createScanFile('src/App.tsx', 'function App() { return <div>Hello World</div> }'),
|
|
60
|
+
]);
|
|
61
|
+
|
|
62
|
+
const results = checkAiDisclosure(ctx);
|
|
63
|
+
|
|
64
|
+
expect(results).toHaveLength(1);
|
|
65
|
+
expect(results[0].type).toBe('skip');
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('passes when disclosure exists alongside chat code', () => {
|
|
69
|
+
const ctx = createScanCtx([
|
|
70
|
+
createScanFile('src/Chat.tsx', 'function Chat() { return <div>chatbot</div> }'),
|
|
71
|
+
createScanFile('src/Disclosure.tsx', '<p>This is an AI-powered system</p>'),
|
|
72
|
+
]);
|
|
73
|
+
|
|
74
|
+
const results = checkAiDisclosure(ctx);
|
|
75
|
+
|
|
76
|
+
expect(results).toHaveLength(1);
|
|
77
|
+
expect(results[0].type).toBe('pass');
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it('ignores non-UI file extensions', () => {
|
|
81
|
+
const ctx = createScanCtx([
|
|
82
|
+
createScanFile('data/chat.json', '{"chatbot": true}'),
|
|
83
|
+
]);
|
|
84
|
+
|
|
85
|
+
const results = checkAiDisclosure(ctx);
|
|
86
|
+
|
|
87
|
+
expect(results).toHaveLength(1);
|
|
88
|
+
expect(results[0].type).toBe('skip');
|
|
89
|
+
});
|
|
90
|
+
});
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type { FileInfo } from '../../../ports/scanner.port.js';
|
|
2
|
+
import { CORE_DISCLOSURE_PATTERNS } from '../../shared/disclosure-patterns.js';
|
|
3
|
+
import { createPatternCheck } from './pattern-check-factory.js';
|
|
4
|
+
|
|
5
|
+
/** Source-code-specific disclosure patterns (beyond core). */
|
|
6
|
+
const SOURCE_CODE_PATTERNS: readonly RegExp[] = [
|
|
7
|
+
/\bautomated system\b/i,
|
|
8
|
+
/\bAI disclosure\b/i,
|
|
9
|
+
/\btransparency notice\b/i,
|
|
10
|
+
/\bautomated decision[- ]?making\b/i,
|
|
11
|
+
/\byou are (?:interacting|chatting|speaking) with (?:an? )?AI\b/i,
|
|
12
|
+
/\bthis (?:is|uses) (?:an? )?(?:AI|artificial intelligence)\b/i,
|
|
13
|
+
/\bgenerated (?:by|using|with) (?:an? )?AI\b/i,
|
|
14
|
+
/\bAI[- ]?assisted\b/i,
|
|
15
|
+
/\bnot a human\b/i,
|
|
16
|
+
/\bdisclosure[_-]?text\b/i,
|
|
17
|
+
/\btransparency[_-]?label\b/i,
|
|
18
|
+
/\bai[_-]?notice\b/i,
|
|
19
|
+
];
|
|
20
|
+
|
|
21
|
+
const CHAT_INDICATORS: readonly RegExp[] = [
|
|
22
|
+
/\bchatbot\b/i,
|
|
23
|
+
/\bchat[- ]?widget\b/i,
|
|
24
|
+
/\bconversational[- ]?ai\b/i,
|
|
25
|
+
/\bvirtual[- ]?assistant\b/i,
|
|
26
|
+
/\bai[- ]?assistant\b/i,
|
|
27
|
+
/\bchat[- ]?endpoint\b/i,
|
|
28
|
+
/\/api\/chat\b/i,
|
|
29
|
+
/\bmessage.*bot\b/i,
|
|
30
|
+
/\bvoice[- ]?assistant\b/i,
|
|
31
|
+
/\bspeech[- ]?to[- ]?text\b/i,
|
|
32
|
+
/\btext[- ]?to[- ]?speech\b/i,
|
|
33
|
+
/\brecommendation[- ]?engine\b/i,
|
|
34
|
+
/\bdecision[- ]?support\b/i,
|
|
35
|
+
/\bautomated[- ]?scoring\b/i,
|
|
36
|
+
/\bai[- ]?agent\b/i,
|
|
37
|
+
];
|
|
38
|
+
|
|
39
|
+
const UI_EXTENSIONS: ReadonlySet<string> = new Set(['.tsx', '.jsx', '.html', '.ts', '.js']);
|
|
40
|
+
const isUiFile = (file: FileInfo): boolean => UI_EXTENSIONS.has(file.extension);
|
|
41
|
+
|
|
42
|
+
export const checkAiDisclosure = createPatternCheck({
|
|
43
|
+
checkId: 'ai-disclosure',
|
|
44
|
+
articleRef: 'Art. 50(1)',
|
|
45
|
+
obligationId: 'eu-ai-act-OBL-015',
|
|
46
|
+
severity: 'high',
|
|
47
|
+
positivePatterns: [...CORE_DISCLOSURE_PATTERNS, ...SOURCE_CODE_PATTERNS],
|
|
48
|
+
contextPatterns: CHAT_INDICATORS,
|
|
49
|
+
passMessage: 'AI disclosure patterns found in UI code',
|
|
50
|
+
failMessage: 'Chat/bot code detected without AI disclosure notice',
|
|
51
|
+
skipReason: 'No chat/bot or AI interaction code detected',
|
|
52
|
+
fix: 'Add a visible disclosure that users are interacting with an AI system',
|
|
53
|
+
fileFilter: isUiFile,
|
|
54
|
+
});
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import type { CheckResult } from '../../../types/common.types.js';
|
|
2
|
+
import type { ScanContext } from '../../../ports/scanner.port.js';
|
|
3
|
+
import { DOCUMENT_VALIDATORS } from '../validators.js';
|
|
4
|
+
|
|
5
|
+
const CHECK_ID = 'ai-literacy';
|
|
6
|
+
const ARTICLE_REF = 'Art. 4';
|
|
7
|
+
const OBLIGATION_ID = 'eu-ai-act-OBL-001';
|
|
8
|
+
|
|
9
|
+
// Derive file patterns from validators.ts (single source of truth)
|
|
10
|
+
const VALIDATOR_FILE_PATTERNS: readonly string[] =
|
|
11
|
+
DOCUMENT_VALIDATORS.find((v) => v.document === 'ai-literacy')?.file_patterns ?? [];
|
|
12
|
+
|
|
13
|
+
const LITERACY_CONTENT_PATTERNS: readonly RegExp[] = [
|
|
14
|
+
/\bai literacy\b/i,
|
|
15
|
+
/\bai training\b.*\bpolicy\b/i,
|
|
16
|
+
/\bai competency\b/i,
|
|
17
|
+
/\bai awareness\b/i,
|
|
18
|
+
/\bstaff training\b.*\bai\b/i,
|
|
19
|
+
/\bai education\b/i,
|
|
20
|
+
];
|
|
21
|
+
|
|
22
|
+
const isPolicyFile = (relativePath: string): boolean => {
|
|
23
|
+
const filename = relativePath.split('/').pop() ?? '';
|
|
24
|
+
return VALIDATOR_FILE_PATTERNS.some((p) => filename.toLowerCase() === p.toLowerCase());
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const isTrainingRecordsDir = (relativePath: string): boolean =>
|
|
28
|
+
/training[-_]?records/i.test(relativePath);
|
|
29
|
+
|
|
30
|
+
const hasLiteracyContent = (content: string): boolean =>
|
|
31
|
+
LITERACY_CONTENT_PATTERNS.some((p) => p.test(content));
|
|
32
|
+
|
|
33
|
+
const GENERATED_DOC_PATHS: readonly RegExp[] = [
|
|
34
|
+
/^\.complior\/documents\/.*ai[-_]?literacy/i,
|
|
35
|
+
/^\.complior\/reports\/.*ai[-_]?literacy/i,
|
|
36
|
+
/^docs\/compliance\/.*ai[-_]?literacy/i,
|
|
37
|
+
];
|
|
38
|
+
|
|
39
|
+
const isGeneratedDoc = (relativePath: string): boolean =>
|
|
40
|
+
GENERATED_DOC_PATHS.some((p) => p.test(relativePath));
|
|
41
|
+
|
|
42
|
+
const TRAINING_STALE_MONTHS = 12;
|
|
43
|
+
|
|
44
|
+
const isTrainingStale = (lastTrainingDate: string): boolean => {
|
|
45
|
+
const last = new Date(lastTrainingDate);
|
|
46
|
+
if (isNaN(last.getTime())) return false; // Invalid date — don't flag
|
|
47
|
+
const now = new Date();
|
|
48
|
+
const monthsDiff = (now.getFullYear() - last.getFullYear()) * 12 + (now.getMonth() - last.getMonth());
|
|
49
|
+
return monthsDiff > TRAINING_STALE_MONTHS;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
export const checkAiLiteracy = (ctx: ScanContext): readonly CheckResult[] => {
|
|
53
|
+
const results: CheckResult[] = [];
|
|
54
|
+
let docFound = false;
|
|
55
|
+
|
|
56
|
+
// Phase 1: Check for policy file by name
|
|
57
|
+
for (const file of ctx.files) {
|
|
58
|
+
if (isPolicyFile(file.relativePath)) {
|
|
59
|
+
docFound = true;
|
|
60
|
+
results.push({
|
|
61
|
+
type: 'pass',
|
|
62
|
+
checkId: CHECK_ID,
|
|
63
|
+
message: `AI literacy policy file found: ${file.relativePath} (${ARTICLE_REF})`,
|
|
64
|
+
});
|
|
65
|
+
break;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Phase 2: Check for training-records directory
|
|
70
|
+
if (!docFound) {
|
|
71
|
+
for (const file of ctx.files) {
|
|
72
|
+
if (isTrainingRecordsDir(file.relativePath)) {
|
|
73
|
+
docFound = true;
|
|
74
|
+
results.push({
|
|
75
|
+
type: 'pass',
|
|
76
|
+
checkId: CHECK_ID,
|
|
77
|
+
message: `AI training records directory found (${ARTICLE_REF})`,
|
|
78
|
+
});
|
|
79
|
+
break;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Phase 3: Check for generated docs in .complior/documents/
|
|
85
|
+
if (!docFound) {
|
|
86
|
+
for (const file of ctx.files) {
|
|
87
|
+
if (isGeneratedDoc(file.relativePath)) {
|
|
88
|
+
docFound = true;
|
|
89
|
+
results.push({
|
|
90
|
+
type: 'pass',
|
|
91
|
+
checkId: CHECK_ID,
|
|
92
|
+
message: `AI literacy document found: ${file.relativePath} (${ARTICLE_REF})`,
|
|
93
|
+
});
|
|
94
|
+
break;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Phase 4: Content scan in docs/
|
|
100
|
+
if (!docFound) {
|
|
101
|
+
const docsFiles = ctx.files.filter((f) =>
|
|
102
|
+
f.relativePath.startsWith('docs/') && f.extension === '.md',
|
|
103
|
+
);
|
|
104
|
+
for (const file of docsFiles) {
|
|
105
|
+
if (hasLiteracyContent(file.content)) {
|
|
106
|
+
docFound = true;
|
|
107
|
+
results.push({
|
|
108
|
+
type: 'pass',
|
|
109
|
+
checkId: CHECK_ID,
|
|
110
|
+
message: `AI literacy content found in docs: ${file.relativePath} (${ARTICLE_REF})`,
|
|
111
|
+
});
|
|
112
|
+
break;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Phase 5: Check passport for training actuality
|
|
118
|
+
if (ctx.passportManifests && ctx.passportManifests.length > 0) {
|
|
119
|
+
for (const manifest of ctx.passportManifests) {
|
|
120
|
+
try {
|
|
121
|
+
const passport = JSON.parse(manifest.content);
|
|
122
|
+
const literacy = passport?.compliance?.ai_literacy;
|
|
123
|
+
if (literacy?.last_training_date && isTrainingStale(literacy.last_training_date)) {
|
|
124
|
+
results.push({
|
|
125
|
+
type: 'fail',
|
|
126
|
+
checkId: 'ai-literacy-stale',
|
|
127
|
+
message: `AI literacy training is overdue — last training: ${literacy.last_training_date} (>${TRAINING_STALE_MONTHS} months) (${ARTICLE_REF})`,
|
|
128
|
+
severity: 'medium',
|
|
129
|
+
obligationId: 'eu-ai-act-OBL-001a',
|
|
130
|
+
articleReference: ARTICLE_REF,
|
|
131
|
+
fix: 'Schedule AI literacy refresher training. Art. 4 requires training proportionate to role and risk level.',
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
if (literacy?.training_completed === false) {
|
|
135
|
+
results.push({
|
|
136
|
+
type: 'fail',
|
|
137
|
+
checkId: 'ai-literacy-incomplete',
|
|
138
|
+
message: `AI literacy training not yet completed for this system (${ARTICLE_REF})`,
|
|
139
|
+
severity: 'medium',
|
|
140
|
+
obligationId: 'eu-ai-act-OBL-001a',
|
|
141
|
+
articleReference: ARTICLE_REF,
|
|
142
|
+
fix: 'Complete AI literacy training and update passport field compliance.ai_literacy.training_completed',
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
} catch { /* skip unparseable passport */ }
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// No document found at all
|
|
150
|
+
if (!docFound) {
|
|
151
|
+
results.push({
|
|
152
|
+
type: 'fail',
|
|
153
|
+
checkId: CHECK_ID,
|
|
154
|
+
message: `No AI literacy policy or training documentation found (${ARTICLE_REF})`,
|
|
155
|
+
severity: 'medium',
|
|
156
|
+
obligationId: OBLIGATION_ID,
|
|
157
|
+
articleReference: ARTICLE_REF,
|
|
158
|
+
fix: 'Create an AI-LITERACY.md policy document covering staff AI competency requirements',
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
return results;
|
|
163
|
+
};
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { checkBehavioralConstraints } from './behavioral-constraints.js';
|
|
3
|
+
import { createScanFile, createScanCtx } from '../../../test-helpers/factories.js';
|
|
4
|
+
|
|
5
|
+
const makePassport = (overrides: Record<string, unknown> = {}): string =>
|
|
6
|
+
JSON.stringify({
|
|
7
|
+
name: 'test-bot',
|
|
8
|
+
compliance: { eu_ai_act: { risk_class: 'high' } },
|
|
9
|
+
constraints: {
|
|
10
|
+
escalation_rules: [
|
|
11
|
+
{ condition: 'action == "deploy"', action: 'require_approval', description: 'Deploy approval', timeout_minutes: 5 },
|
|
12
|
+
],
|
|
13
|
+
},
|
|
14
|
+
permissions: {
|
|
15
|
+
tools: ['search'],
|
|
16
|
+
data_boundaries: { pii_handling: 'redact' },
|
|
17
|
+
},
|
|
18
|
+
...overrides,
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
describe('checkBehavioralConstraints', () => {
|
|
22
|
+
it('returns empty when no passport files', () => {
|
|
23
|
+
const ctx = createScanCtx([
|
|
24
|
+
createScanFile('package.json', '{"dependencies":{}}'),
|
|
25
|
+
]);
|
|
26
|
+
const results = checkBehavioralConstraints(ctx);
|
|
27
|
+
expect(results).toHaveLength(0);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('passes for high-risk with escalation rules and data boundaries', () => {
|
|
31
|
+
const ctx = createScanCtx([
|
|
32
|
+
createScanFile('.complior/agents/test-bot-manifest.json', makePassport()),
|
|
33
|
+
]);
|
|
34
|
+
const results = checkBehavioralConstraints(ctx);
|
|
35
|
+
const passes = results.filter((r) => r.type === 'pass');
|
|
36
|
+
expect(passes).toHaveLength(2);
|
|
37
|
+
expect(results.every((r) => r.type === 'pass')).toBe(true);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('fails high-risk missing escalation rules', () => {
|
|
41
|
+
const passport = makePassport({
|
|
42
|
+
constraints: { prohibited_actions: [] },
|
|
43
|
+
});
|
|
44
|
+
const ctx = createScanCtx([
|
|
45
|
+
createScanFile('.complior/agents/test-bot-manifest.json', passport),
|
|
46
|
+
]);
|
|
47
|
+
const results = checkBehavioralConstraints(ctx);
|
|
48
|
+
const fails = results.filter((r) => r.type === 'fail');
|
|
49
|
+
expect(fails).toHaveLength(1);
|
|
50
|
+
expect(fails[0].type === 'fail' && fails[0].severity).toBe('high');
|
|
51
|
+
expect(fails[0].message).toContain('escalation rules');
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('fails high-risk missing data boundaries', () => {
|
|
55
|
+
const passport = makePassport({
|
|
56
|
+
permissions: { tools: ['search'] },
|
|
57
|
+
});
|
|
58
|
+
const ctx = createScanCtx([
|
|
59
|
+
createScanFile('.complior/agents/test-bot-manifest.json', passport),
|
|
60
|
+
]);
|
|
61
|
+
const results = checkBehavioralConstraints(ctx);
|
|
62
|
+
const dataBoundaryFail = results.find(
|
|
63
|
+
(r) => r.type === 'fail' && r.message.includes('data boundaries'),
|
|
64
|
+
);
|
|
65
|
+
expect(dataBoundaryFail).toBeDefined();
|
|
66
|
+
expect(dataBoundaryFail!.type === 'fail' && dataBoundaryFail!.severity).toBe('high');
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it('does not require escalation rules for limited-risk', () => {
|
|
70
|
+
const passport = makePassport({
|
|
71
|
+
compliance: { eu_ai_act: { risk_class: 'limited' } },
|
|
72
|
+
constraints: { prohibited_actions: [] },
|
|
73
|
+
});
|
|
74
|
+
const ctx = createScanCtx([
|
|
75
|
+
createScanFile('.complior/agents/test-bot-manifest.json', passport),
|
|
76
|
+
]);
|
|
77
|
+
const results = checkBehavioralConstraints(ctx);
|
|
78
|
+
const escalationFail = results.find(
|
|
79
|
+
(r) => r.type === 'fail' && r.message.includes('escalation'),
|
|
80
|
+
);
|
|
81
|
+
expect(escalationFail).toBeUndefined();
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it('fails limited-risk missing data boundaries with medium severity', () => {
|
|
85
|
+
const passport = makePassport({
|
|
86
|
+
compliance: { eu_ai_act: { risk_class: 'limited' } },
|
|
87
|
+
permissions: { tools: ['search'] },
|
|
88
|
+
});
|
|
89
|
+
const ctx = createScanCtx([
|
|
90
|
+
createScanFile('.complior/agents/test-bot-manifest.json', passport),
|
|
91
|
+
]);
|
|
92
|
+
const results = checkBehavioralConstraints(ctx);
|
|
93
|
+
const dataBoundaryFail = results.find(
|
|
94
|
+
(r) => r.type === 'fail' && r.message.includes('data boundaries'),
|
|
95
|
+
);
|
|
96
|
+
expect(dataBoundaryFail).toBeDefined();
|
|
97
|
+
expect(dataBoundaryFail!.type === 'fail' && dataBoundaryFail!.severity).toBe('medium');
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it('passes limited-risk with pii_handling defined', () => {
|
|
101
|
+
const passport = makePassport({
|
|
102
|
+
compliance: { eu_ai_act: { risk_class: 'limited' } },
|
|
103
|
+
constraints: { prohibited_actions: [] },
|
|
104
|
+
permissions: {
|
|
105
|
+
tools: ['search'],
|
|
106
|
+
data_boundaries: { pii_handling: 'block' },
|
|
107
|
+
},
|
|
108
|
+
});
|
|
109
|
+
const ctx = createScanCtx([
|
|
110
|
+
createScanFile('.complior/agents/test-bot-manifest.json', passport),
|
|
111
|
+
]);
|
|
112
|
+
const results = checkBehavioralConstraints(ctx);
|
|
113
|
+
expect(results.every((r) => r.type === 'pass')).toBe(true);
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it('handles invalid JSON without crashing', () => {
|
|
117
|
+
const ctx = createScanCtx([
|
|
118
|
+
createScanFile('.complior/agents/broken-manifest.json', '{ invalid json'),
|
|
119
|
+
]);
|
|
120
|
+
const results = checkBehavioralConstraints(ctx);
|
|
121
|
+
expect(results).toHaveLength(0);
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
it('treats minimal-risk same as limited — no escalation required, medium data boundary', () => {
|
|
125
|
+
const passport = makePassport({
|
|
126
|
+
compliance: { eu_ai_act: { risk_class: 'minimal' } },
|
|
127
|
+
constraints: { prohibited_actions: [] },
|
|
128
|
+
permissions: { tools: ['search'] },
|
|
129
|
+
});
|
|
130
|
+
const ctx = createScanCtx([
|
|
131
|
+
createScanFile('.complior/agents/test-bot-manifest.json', passport),
|
|
132
|
+
]);
|
|
133
|
+
const results = checkBehavioralConstraints(ctx);
|
|
134
|
+
const escalationFail = results.find(
|
|
135
|
+
(r) => r.type === 'fail' && r.message.includes('escalation'),
|
|
136
|
+
);
|
|
137
|
+
expect(escalationFail).toBeUndefined();
|
|
138
|
+
const dataBoundaryFail = results.find(
|
|
139
|
+
(r) => r.type === 'fail' && r.message.includes('data boundaries'),
|
|
140
|
+
);
|
|
141
|
+
expect(dataBoundaryFail).toBeDefined();
|
|
142
|
+
expect(dataBoundaryFail!.type === 'fail' && dataBoundaryFail!.severity).toBe('medium');
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
it('handles multiple passports independently', () => {
|
|
146
|
+
const highRiskPassport = makePassport({
|
|
147
|
+
name: 'high-bot',
|
|
148
|
+
compliance: { eu_ai_act: { risk_class: 'high' } },
|
|
149
|
+
});
|
|
150
|
+
const limitedPassport = makePassport({
|
|
151
|
+
name: 'limited-bot',
|
|
152
|
+
compliance: { eu_ai_act: { risk_class: 'limited' } },
|
|
153
|
+
constraints: { prohibited_actions: [] },
|
|
154
|
+
});
|
|
155
|
+
const ctx = createScanCtx([
|
|
156
|
+
createScanFile('.complior/agents/high-bot-manifest.json', highRiskPassport),
|
|
157
|
+
createScanFile('.complior/agents/limited-bot-manifest.json', limitedPassport),
|
|
158
|
+
]);
|
|
159
|
+
const results = checkBehavioralConstraints(ctx);
|
|
160
|
+
// high-risk: escalation pass + data boundary pass = 2
|
|
161
|
+
// limited-risk: no escalation check + data boundary pass = 1
|
|
162
|
+
expect(results).toHaveLength(3);
|
|
163
|
+
expect(results.every((r) => r.type === 'pass')).toBe(true);
|
|
164
|
+
expect(results.some((r) => r.message.includes('high-bot'))).toBe(true);
|
|
165
|
+
expect(results.some((r) => r.message.includes('limited-bot'))).toBe(true);
|
|
166
|
+
});
|
|
167
|
+
});
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import type { CheckResult } from '../../../types/common.types.js';
|
|
2
|
+
import type { ScanContext } from '../../../ports/scanner.port.js';
|
|
3
|
+
import { filterPassportManifests, extractRiskClass } from '../../passport/builder/manifest-files.js';
|
|
4
|
+
|
|
5
|
+
const CHECK_ID = 'behavioral-constraints';
|
|
6
|
+
const ARTICLE_REF_ESCALATION = 'Art. 14(4)';
|
|
7
|
+
const ARTICLE_REF_DATA = 'Art. 9(2)';
|
|
8
|
+
const OBLIGATION_ID_ESCALATION = 'eu-ai-act-OBL-014';
|
|
9
|
+
const OBLIGATION_ID_DATA = 'eu-ai-act-OBL-009';
|
|
10
|
+
|
|
11
|
+
const checkEscalation = (
|
|
12
|
+
manifest: Record<string, unknown>,
|
|
13
|
+
isHighRisk: boolean,
|
|
14
|
+
name: string,
|
|
15
|
+
filePath: string,
|
|
16
|
+
): CheckResult | undefined => {
|
|
17
|
+
if (!isHighRisk) return undefined;
|
|
18
|
+
|
|
19
|
+
const constraints = manifest.constraints as Record<string, unknown> | undefined;
|
|
20
|
+
const rules = constraints?.escalation_rules as unknown[] | undefined;
|
|
21
|
+
|
|
22
|
+
if (!rules || rules.length === 0) {
|
|
23
|
+
return {
|
|
24
|
+
type: 'fail',
|
|
25
|
+
checkId: CHECK_ID,
|
|
26
|
+
message: `Missing escalation rules for high-risk agent: ${name} (${ARTICLE_REF_ESCALATION})`,
|
|
27
|
+
severity: 'high',
|
|
28
|
+
obligationId: OBLIGATION_ID_ESCALATION,
|
|
29
|
+
articleReference: ARTICLE_REF_ESCALATION,
|
|
30
|
+
fix: 'Add constraints.escalation_rules to passport with structured human oversight measures',
|
|
31
|
+
file: filePath,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return { type: 'pass', checkId: CHECK_ID, message: `Escalation rules defined for high-risk agent: ${name}` };
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const checkDataBoundary = (
|
|
39
|
+
manifest: Record<string, unknown>,
|
|
40
|
+
isHighRisk: boolean,
|
|
41
|
+
name: string,
|
|
42
|
+
filePath: string,
|
|
43
|
+
): CheckResult => {
|
|
44
|
+
const permissions = manifest.permissions as Record<string, unknown> | undefined;
|
|
45
|
+
const boundaries = permissions?.data_boundaries as Record<string, unknown> | undefined;
|
|
46
|
+
|
|
47
|
+
if (!boundaries || !boundaries.pii_handling) {
|
|
48
|
+
return {
|
|
49
|
+
type: 'fail',
|
|
50
|
+
checkId: CHECK_ID,
|
|
51
|
+
message: `Missing data boundaries for agent: ${name} (${ARTICLE_REF_DATA})`,
|
|
52
|
+
severity: isHighRisk ? 'high' : 'medium',
|
|
53
|
+
obligationId: OBLIGATION_ID_DATA,
|
|
54
|
+
articleReference: ARTICLE_REF_DATA,
|
|
55
|
+
fix: 'Add permissions.data_boundaries with pii_handling mode to passport',
|
|
56
|
+
file: filePath,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return { type: 'pass', checkId: CHECK_ID, message: `Data boundaries defined for agent: ${name}` };
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
export const checkBehavioralConstraints = (ctx: ScanContext): readonly CheckResult[] => {
|
|
64
|
+
const passportFiles = filterPassportManifests(ctx.files);
|
|
65
|
+
if (passportFiles.length === 0) return [];
|
|
66
|
+
|
|
67
|
+
const results: CheckResult[] = [];
|
|
68
|
+
|
|
69
|
+
for (const file of passportFiles) {
|
|
70
|
+
try {
|
|
71
|
+
const manifest = JSON.parse(file.content) as Record<string, unknown>;
|
|
72
|
+
const riskClass = extractRiskClass(manifest);
|
|
73
|
+
const isHighRisk = riskClass === 'high' || riskClass === 'prohibited';
|
|
74
|
+
const name = (manifest.name as string) ?? file.relativePath;
|
|
75
|
+
|
|
76
|
+
const escalationResult = checkEscalation(manifest, isHighRisk, name, file.relativePath);
|
|
77
|
+
if (escalationResult) results.push(escalationResult);
|
|
78
|
+
|
|
79
|
+
results.push(checkDataBoundary(manifest, isHighRisk, name, file.relativePath));
|
|
80
|
+
} catch {
|
|
81
|
+
// Invalid JSON — skip silently, passport-presence handles parse errors
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return results;
|
|
86
|
+
};
|