@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.
Files changed (594) hide show
  1. package/.well-known/ai-compliance.json +16 -0
  2. package/COMPLIANCE.md +64 -0
  3. package/data/data-integrity.test.ts +75 -0
  4. package/data/eval/eval-mappings.json +33 -0
  5. package/data/llm/model-pricing.json +15 -0
  6. package/data/llm/model-routing.json +36 -0
  7. package/data/onboarding/risk-profile.json +17 -0
  8. package/data/regulations/eu-ai-act/README.md +245 -0
  9. package/data/regulations/eu-ai-act/applicability-tree.json +160 -0
  10. package/data/regulations/eu-ai-act/cross-mapping.json +175 -0
  11. package/data/regulations/eu-ai-act/localization.json +186 -0
  12. package/data/regulations/eu-ai-act/obligations.json +3981 -0
  13. package/data/regulations/eu-ai-act/regulation-meta.json +482 -0
  14. package/data/regulations/eu-ai-act/scoring.json +342 -0
  15. package/data/regulations/eu-ai-act/technical-requirements.json +2590 -0
  16. package/data/regulations/eu-ai-act/timeline.json +160 -0
  17. package/data/regulations/jurisdictions/at.json +15 -0
  18. package/data/regulations/jurisdictions/be.json +15 -0
  19. package/data/regulations/jurisdictions/bg.json +15 -0
  20. package/data/regulations/jurisdictions/cy.json +15 -0
  21. package/data/regulations/jurisdictions/cz.json +15 -0
  22. package/data/regulations/jurisdictions/de.json +15 -0
  23. package/data/regulations/jurisdictions/dk.json +15 -0
  24. package/data/regulations/jurisdictions/ee.json +15 -0
  25. package/data/regulations/jurisdictions/es.json +15 -0
  26. package/data/regulations/jurisdictions/fi.json +15 -0
  27. package/data/regulations/jurisdictions/fr.json +15 -0
  28. package/data/regulations/jurisdictions/gr.json +15 -0
  29. package/data/regulations/jurisdictions/hr.json +15 -0
  30. package/data/regulations/jurisdictions/hu.json +15 -0
  31. package/data/regulations/jurisdictions/ie.json +15 -0
  32. package/data/regulations/jurisdictions/is.json +15 -0
  33. package/data/regulations/jurisdictions/it.json +15 -0
  34. package/data/regulations/jurisdictions/li.json +15 -0
  35. package/data/regulations/jurisdictions/lt.json +15 -0
  36. package/data/regulations/jurisdictions/lu.json +15 -0
  37. package/data/regulations/jurisdictions/lv.json +15 -0
  38. package/data/regulations/jurisdictions/mt.json +15 -0
  39. package/data/regulations/jurisdictions/nl.json +15 -0
  40. package/data/regulations/jurisdictions/no.json +15 -0
  41. package/data/regulations/jurisdictions/pl.json +15 -0
  42. package/data/regulations/jurisdictions/pt.json +15 -0
  43. package/data/regulations/jurisdictions/ro.json +15 -0
  44. package/data/regulations/jurisdictions/se.json +15 -0
  45. package/data/regulations/jurisdictions/si.json +15 -0
  46. package/data/regulations/jurisdictions/sk.json +15 -0
  47. package/data/scanner/check-id-categories.json +81 -0
  48. package/data/scanner/confidence-params.json +16 -0
  49. package/data/scanner/limits.json +4 -0
  50. package/data/schemas/http-contract-sample.json +79 -0
  51. package/data/schemas/http-contract.json +144 -0
  52. package/data/semgrep-rules/bare-call.yaml +37 -0
  53. package/data/semgrep-rules/injection.yaml +73 -0
  54. package/data/semgrep-rules/missing-error-handling.yaml +58 -0
  55. package/data/semgrep-rules/unsafe-deser.yaml +65 -0
  56. package/data/templates/eu-ai-act/ai-literacy.md +184 -0
  57. package/data/templates/eu-ai-act/art5-screening.md +131 -0
  58. package/data/templates/eu-ai-act/data-governance.md +145 -0
  59. package/data/templates/eu-ai-act/declaration-of-conformity.md +161 -0
  60. package/data/templates/eu-ai-act/fria.md +127 -0
  61. package/data/templates/eu-ai-act/gpai-systemic-risk.md +150 -0
  62. package/data/templates/eu-ai-act/gpai-transparency.md +166 -0
  63. package/data/templates/eu-ai-act/incident-report.md +188 -0
  64. package/data/templates/eu-ai-act/instructions-for-use.md +202 -0
  65. package/data/templates/eu-ai-act/monitoring-policy.md +110 -0
  66. package/data/templates/eu-ai-act/qms.md +180 -0
  67. package/data/templates/eu-ai-act/risk-management-system.md +123 -0
  68. package/data/templates/eu-ai-act/technical-documentation.md +287 -0
  69. package/data/templates/eu-ai-act/worker-notification.md +143 -0
  70. package/data/templates/policies/biometrics-ai-policy.md +214 -0
  71. package/data/templates/policies/critical-infra-ai-policy.md +228 -0
  72. package/data/templates/policies/education-ai-policy.md +184 -0
  73. package/data/templates/policies/finance-ai-policy.md +191 -0
  74. package/data/templates/policies/healthcare-ai-policy.md +197 -0
  75. package/data/templates/policies/hr-ai-policy.md +178 -0
  76. package/data/templates/policies/legal-ai-policy.md +189 -0
  77. package/data/templates/policies/migration-ai-policy.md +239 -0
  78. package/engine.log +7 -0
  79. package/package.json +74 -0
  80. package/src/composition-root.ts +791 -0
  81. package/src/data/eval/conformity-tests.test.ts +122 -0
  82. package/src/data/eval/ct-1-transparency.ts +106 -0
  83. package/src/data/eval/ct-10-gpai.ts +25 -0
  84. package/src/data/eval/ct-11-industry.ts +42 -0
  85. package/src/data/eval/ct-2-oversight.ts +41 -0
  86. package/src/data/eval/ct-3-explanation.ts +14 -0
  87. package/src/data/eval/ct-4-bias.ts +83 -0
  88. package/src/data/eval/ct-5-accuracy.ts +41 -0
  89. package/src/data/eval/ct-6-robustness.ts +81 -0
  90. package/src/data/eval/ct-7-prohibited.ts +52 -0
  91. package/src/data/eval/ct-8-logging.ts +68 -0
  92. package/src/data/eval/ct-9-risk-awareness.ts +33 -0
  93. package/src/data/eval/deterministic-evaluator.ts +120 -0
  94. package/src/data/eval/index.ts +55 -0
  95. package/src/data/eval/judge-prompts.ts +146 -0
  96. package/src/data/eval/llm-judged-tests.ts +279 -0
  97. package/src/data/eval/llm-tests.test.ts +83 -0
  98. package/src/data/eval/remediation/ct-1-transparency.ts +91 -0
  99. package/src/data/eval/remediation/ct-10-gpai.ts +94 -0
  100. package/src/data/eval/remediation/ct-11-industry.ts +94 -0
  101. package/src/data/eval/remediation/ct-2-oversight.ts +71 -0
  102. package/src/data/eval/remediation/ct-3-explanation.ts +70 -0
  103. package/src/data/eval/remediation/ct-4-bias.ts +70 -0
  104. package/src/data/eval/remediation/ct-5-accuracy.ts +70 -0
  105. package/src/data/eval/remediation/ct-6-robustness.ts +70 -0
  106. package/src/data/eval/remediation/ct-7-prohibited.ts +94 -0
  107. package/src/data/eval/remediation/ct-8-logging.ts +94 -0
  108. package/src/data/eval/remediation/ct-9-risk-awareness.ts +94 -0
  109. package/src/data/eval/remediation/index.ts +89 -0
  110. package/src/data/eval/remediation/owasp-art5.ts +15 -0
  111. package/src/data/eval/remediation/owasp-llm01.ts +72 -0
  112. package/src/data/eval/remediation/owasp-llm02.ts +72 -0
  113. package/src/data/eval/remediation/owasp-llm03.ts +15 -0
  114. package/src/data/eval/remediation/owasp-llm04.ts +15 -0
  115. package/src/data/eval/remediation/owasp-llm05.ts +15 -0
  116. package/src/data/eval/remediation/owasp-llm06.ts +15 -0
  117. package/src/data/eval/remediation/owasp-llm07.ts +15 -0
  118. package/src/data/eval/remediation/owasp-llm08.ts +15 -0
  119. package/src/data/eval/remediation/owasp-llm09.ts +15 -0
  120. package/src/data/eval/remediation/owasp-llm10.ts +15 -0
  121. package/src/data/eval/remediation/remediation.test.ts +229 -0
  122. package/src/data/eval/remediation/test-mapping.ts +290 -0
  123. package/src/data/eval/security-rubrics.ts +381 -0
  124. package/src/data/finding-explanations.json +453 -0
  125. package/src/data/industry-patterns.ts +161 -0
  126. package/src/data/registry-cards.ts +368 -0
  127. package/src/data/regulation/index.ts +5 -0
  128. package/src/data/regulation/jurisdiction-data.test.ts +73 -0
  129. package/src/data/regulation/jurisdiction-data.ts +65 -0
  130. package/src/data/regulation/regulation-data.ts +19 -0
  131. package/src/data/regulation/regulation-loader.test.ts +107 -0
  132. package/src/data/regulation/regulation-loader.ts +56 -0
  133. package/src/data/scanner-constants.ts +46 -0
  134. package/src/data/schemas/schemas-core.ts +140 -0
  135. package/src/data/schemas/schemas-supplementary.ts +211 -0
  136. package/src/data/schemas/schemas.ts +28 -0
  137. package/src/data/security/attack-probes.test.ts +62 -0
  138. package/src/data/security/attack-probes.ts +496 -0
  139. package/src/data/security/eu-ai-act-security.ts +40 -0
  140. package/src/data/security/index.ts +19 -0
  141. package/src/data/security/mitre-atlas.test.ts +43 -0
  142. package/src/data/security/mitre-atlas.ts +93 -0
  143. package/src/data/security/nist-ai-rmf.ts +43 -0
  144. package/src/data/security/owasp-llm-top10.test.ts +60 -0
  145. package/src/data/security/owasp-llm-top10.ts +138 -0
  146. package/src/data/template-registry.ts +53 -0
  147. package/src/data/tool-versions.json +22 -0
  148. package/src/domain/audit/audit-package.test.ts +152 -0
  149. package/src/domain/audit/audit-package.ts +166 -0
  150. package/src/domain/audit/audit-trail.test.ts +121 -0
  151. package/src/domain/audit/audit-trail.ts +174 -0
  152. package/src/domain/audit/index.ts +8 -0
  153. package/src/domain/audit/permissions-matrix.test.ts +136 -0
  154. package/src/domain/audit/permissions-matrix.ts +121 -0
  155. package/src/domain/certification/adversarial/bias-tests.ts +95 -0
  156. package/src/domain/certification/adversarial/evaluators.ts +304 -0
  157. package/src/domain/certification/adversarial/index.ts +11 -0
  158. package/src/domain/certification/adversarial/prompt-injection.ts +103 -0
  159. package/src/domain/certification/adversarial/safety-boundary.ts +132 -0
  160. package/src/domain/certification/aiuc1-readiness.test.ts +236 -0
  161. package/src/domain/certification/aiuc1-readiness.ts +298 -0
  162. package/src/domain/certification/aiuc1-requirements.ts +235 -0
  163. package/src/domain/certification/index.ts +10 -0
  164. package/src/domain/certification/redteam-runner.test.ts +97 -0
  165. package/src/domain/certification/redteam-runner.ts +205 -0
  166. package/src/domain/certification/test-runner.test.ts +232 -0
  167. package/src/domain/certification/test-runner.ts +289 -0
  168. package/src/domain/cost/cost-estimator.test.ts +187 -0
  169. package/src/domain/cost/cost-estimator.ts +133 -0
  170. package/src/domain/disclaimer.test.ts +52 -0
  171. package/src/domain/disclaimer.ts +39 -0
  172. package/src/domain/documents/ai-enricher.test.ts +120 -0
  173. package/src/domain/documents/ai-enricher.ts +159 -0
  174. package/src/domain/documents/document-generator.test.ts +318 -0
  175. package/src/domain/documents/document-generator.ts +239 -0
  176. package/src/domain/documents/index.ts +9 -0
  177. package/src/domain/documents/passport-helpers.ts +25 -0
  178. package/src/domain/documents/policy-generator.test.ts +252 -0
  179. package/src/domain/documents/policy-generator.ts +94 -0
  180. package/src/domain/documents/worker-notification-generator.test.ts +162 -0
  181. package/src/domain/documents/worker-notification-generator.ts +141 -0
  182. package/src/domain/eval/adapters/adapter-port.ts +94 -0
  183. package/src/domain/eval/adapters/adapters.test.ts +303 -0
  184. package/src/domain/eval/adapters/anthropic-adapter.ts +57 -0
  185. package/src/domain/eval/adapters/auto-detect.ts +104 -0
  186. package/src/domain/eval/adapters/create-chat-adapter.ts +106 -0
  187. package/src/domain/eval/adapters/custom-adapter.ts +74 -0
  188. package/src/domain/eval/adapters/http-adapter.ts +66 -0
  189. package/src/domain/eval/adapters/index.ts +7 -0
  190. package/src/domain/eval/adapters/ollama-adapter.ts +48 -0
  191. package/src/domain/eval/adapters/openai-adapter.ts +58 -0
  192. package/src/domain/eval/adapters/with-timeout.ts +25 -0
  193. package/src/domain/eval/conformity-score.test.ts +161 -0
  194. package/src/domain/eval/conformity-score.ts +135 -0
  195. package/src/domain/eval/eval-constants.ts +55 -0
  196. package/src/domain/eval/eval-evidence.test.ts +85 -0
  197. package/src/domain/eval/eval-evidence.ts +103 -0
  198. package/src/domain/eval/eval-fix-generator.test.ts +421 -0
  199. package/src/domain/eval/eval-fix-generator.ts +205 -0
  200. package/src/domain/eval/eval-passport.test.ts +82 -0
  201. package/src/domain/eval/eval-passport.ts +89 -0
  202. package/src/domain/eval/eval-remediation-report.test.ts +682 -0
  203. package/src/domain/eval/eval-remediation-report.ts +170 -0
  204. package/src/domain/eval/eval-report.ts +108 -0
  205. package/src/domain/eval/eval-runner.test.ts +609 -0
  206. package/src/domain/eval/eval-runner.ts +593 -0
  207. package/src/domain/eval/eval-to-findings.test.ts +293 -0
  208. package/src/domain/eval/eval-to-findings.ts +83 -0
  209. package/src/domain/eval/index.ts +31 -0
  210. package/src/domain/eval/llm-judge.test.ts +139 -0
  211. package/src/domain/eval/llm-judge.ts +168 -0
  212. package/src/domain/eval/remediation-types.ts +90 -0
  213. package/src/domain/eval/security-integration.test.ts +196 -0
  214. package/src/domain/eval/security-integration.ts +136 -0
  215. package/src/domain/eval/types.test.ts +173 -0
  216. package/src/domain/eval/types.ts +244 -0
  217. package/src/domain/eval/verdict-utils.ts +45 -0
  218. package/src/domain/fixer/create-fixer.ts +101 -0
  219. package/src/domain/fixer/diff.ts +70 -0
  220. package/src/domain/fixer/fix-history.ts +23 -0
  221. package/src/domain/fixer/fixer.test.ts +306 -0
  222. package/src/domain/fixer/index.ts +9 -0
  223. package/src/domain/fixer/strategies/bandit-fix.ts +61 -0
  224. package/src/domain/fixer/strategies/bias-testing.ts +49 -0
  225. package/src/domain/fixer/strategies/ci-compliance.ts +57 -0
  226. package/src/domain/fixer/strategies/content-marking.ts +45 -0
  227. package/src/domain/fixer/strategies/cve-upgrade.ts +66 -0
  228. package/src/domain/fixer/strategies/data-governance.ts +65 -0
  229. package/src/domain/fixer/strategies/disclosure.ts +69 -0
  230. package/src/domain/fixer/strategies/doc-code-sync.ts +53 -0
  231. package/src/domain/fixer/strategies/documentation.ts +59 -0
  232. package/src/domain/fixer/strategies/error-handler.ts +63 -0
  233. package/src/domain/fixer/strategies/hitl-gate.ts +67 -0
  234. package/src/domain/fixer/strategies/index.ts +61 -0
  235. package/src/domain/fixer/strategies/kill-switch-test.ts +85 -0
  236. package/src/domain/fixer/strategies/kill-switch.ts +53 -0
  237. package/src/domain/fixer/strategies/license-fix.ts +57 -0
  238. package/src/domain/fixer/strategies/log-retention.ts +40 -0
  239. package/src/domain/fixer/strategies/logging.ts +59 -0
  240. package/src/domain/fixer/strategies/metadata.ts +45 -0
  241. package/src/domain/fixer/strategies/permission-guard.ts +84 -0
  242. package/src/domain/fixer/strategies/record-keeping.ts +69 -0
  243. package/src/domain/fixer/strategies/secret-rotation.ts +52 -0
  244. package/src/domain/fixer/strategies.test.ts +341 -0
  245. package/src/domain/fixer/template-engine.test.ts +64 -0
  246. package/src/domain/fixer/template-engine.ts +38 -0
  247. package/src/domain/fixer/types.ts +88 -0
  248. package/src/domain/frameworks/aiuc1-framework.test.ts +159 -0
  249. package/src/domain/frameworks/aiuc1-framework.ts +126 -0
  250. package/src/domain/frameworks/collect-foundation-metrics.test.ts +96 -0
  251. package/src/domain/frameworks/collect-foundation-metrics.ts +34 -0
  252. package/src/domain/frameworks/eu-ai-act-framework.test.ts +117 -0
  253. package/src/domain/frameworks/eu-ai-act-framework.ts +100 -0
  254. package/src/domain/frameworks/framework-registry.test.ts +91 -0
  255. package/src/domain/frameworks/framework-registry.ts +38 -0
  256. package/src/domain/frameworks/index.ts +8 -0
  257. package/src/domain/frameworks/mitre-atlas-framework.test.ts +53 -0
  258. package/src/domain/frameworks/mitre-atlas-framework.ts +53 -0
  259. package/src/domain/frameworks/owasp-llm-framework.test.ts +77 -0
  260. package/src/domain/frameworks/owasp-llm-framework.ts +54 -0
  261. package/src/domain/frameworks/score-plugin-framework.ts +117 -0
  262. package/src/domain/fria/fria-generator.test.ts +273 -0
  263. package/src/domain/fria/fria-generator.ts +366 -0
  264. package/src/domain/import/promptfoo-importer.test.ts +103 -0
  265. package/src/domain/import/promptfoo-importer.ts +151 -0
  266. package/src/domain/onboarding/guided-onboarding.test.ts +144 -0
  267. package/src/domain/onboarding/guided-onboarding.ts +135 -0
  268. package/src/domain/passport/builder/domain-mapper.ts +9 -0
  269. package/src/domain/passport/builder/manifest-builder.test.ts +546 -0
  270. package/src/domain/passport/builder/manifest-builder.ts +535 -0
  271. package/src/domain/passport/builder/manifest-diff.test.ts +105 -0
  272. package/src/domain/passport/builder/manifest-diff.ts +89 -0
  273. package/src/domain/passport/builder/manifest-files.ts +17 -0
  274. package/src/domain/passport/crypto-signer.test.ts +93 -0
  275. package/src/domain/passport/crypto-signer.ts +157 -0
  276. package/src/domain/passport/discovery/agent-discovery.test.ts +296 -0
  277. package/src/domain/passport/discovery/agent-discovery.ts +325 -0
  278. package/src/domain/passport/discovery/autonomy-analyzer.test.ts +141 -0
  279. package/src/domain/passport/discovery/autonomy-analyzer.ts +113 -0
  280. package/src/domain/passport/discovery/permission-scanner.test.ts +191 -0
  281. package/src/domain/passport/discovery/permission-scanner.ts +414 -0
  282. package/src/domain/passport/export/a2a-mapper.ts +75 -0
  283. package/src/domain/passport/export/aiuc1-mapper.ts +126 -0
  284. package/src/domain/passport/export/export.test.ts +207 -0
  285. package/src/domain/passport/export/index.ts +41 -0
  286. package/src/domain/passport/export/nist-mapper.ts +227 -0
  287. package/src/domain/passport/import/a2a-importer.test.ts +133 -0
  288. package/src/domain/passport/import/a2a-importer.ts +156 -0
  289. package/src/domain/passport/import/index.ts +2 -0
  290. package/src/domain/passport/index.ts +32 -0
  291. package/src/domain/passport/obligation-field-map.test.ts +113 -0
  292. package/src/domain/passport/obligation-field-map.ts +117 -0
  293. package/src/domain/passport/passport-validator.test.ts +156 -0
  294. package/src/domain/passport/passport-validator.ts +126 -0
  295. package/src/domain/passport/scan-to-compliance.test.ts +336 -0
  296. package/src/domain/passport/scan-to-compliance.ts +166 -0
  297. package/src/domain/passport/test-generator.test.ts +93 -0
  298. package/src/domain/passport/test-generator.ts +136 -0
  299. package/src/domain/proxy/index.ts +11 -0
  300. package/src/domain/proxy/json-rpc.test.ts +72 -0
  301. package/src/domain/proxy/json-rpc.ts +53 -0
  302. package/src/domain/proxy/policy-engine.test.ts +259 -0
  303. package/src/domain/proxy/policy-engine.ts +137 -0
  304. package/src/domain/proxy/proxy-bridge.ts +125 -0
  305. package/src/domain/proxy/proxy-interceptor.test.ts +184 -0
  306. package/src/domain/proxy/proxy-interceptor.ts +120 -0
  307. package/src/domain/proxy/proxy-types.ts +35 -0
  308. package/src/domain/registry/compute-agent-score.test.ts +279 -0
  309. package/src/domain/registry/compute-agent-score.ts +162 -0
  310. package/src/domain/reporter/audit-report.test.ts +87 -0
  311. package/src/domain/reporter/audit-report.ts +116 -0
  312. package/src/domain/reporter/badge-generator.test.ts +54 -0
  313. package/src/domain/reporter/badge-generator.ts +40 -0
  314. package/src/domain/reporter/compliance-md.ts +45 -0
  315. package/src/domain/reporter/index.ts +7 -0
  316. package/src/domain/reporter/pdf-renderer.ts +282 -0
  317. package/src/domain/reporter/share.test.ts +92 -0
  318. package/src/domain/reporter/share.ts +80 -0
  319. package/src/domain/scanner/ast/swc-analyzer.test.ts +49 -0
  320. package/src/domain/scanner/ast/swc-analyzer.ts +124 -0
  321. package/src/domain/scanner/attestations.ts +97 -0
  322. package/src/domain/scanner/checks/ai-disclosure.test.ts +90 -0
  323. package/src/domain/scanner/checks/ai-disclosure.ts +54 -0
  324. package/src/domain/scanner/checks/ai-literacy.ts +163 -0
  325. package/src/domain/scanner/checks/behavioral-constraints.test.ts +167 -0
  326. package/src/domain/scanner/checks/behavioral-constraints.ts +86 -0
  327. package/src/domain/scanner/checks/compliance-metadata.ts +63 -0
  328. package/src/domain/scanner/checks/content-marking.ts +74 -0
  329. package/src/domain/scanner/checks/dep-deep-scan.test.ts +318 -0
  330. package/src/domain/scanner/checks/dep-deep-scan.ts +137 -0
  331. package/src/domain/scanner/checks/documentation.test.ts +88 -0
  332. package/src/domain/scanner/checks/documentation.ts +79 -0
  333. package/src/domain/scanner/checks/git-history.test.ts +120 -0
  334. package/src/domain/scanner/checks/git-history.ts +163 -0
  335. package/src/domain/scanner/checks/gpai-systemic-risk.test.ts +84 -0
  336. package/src/domain/scanner/checks/gpai-systemic-risk.ts +98 -0
  337. package/src/domain/scanner/checks/gpai-transparency.ts +94 -0
  338. package/src/domain/scanner/checks/index.ts +28 -0
  339. package/src/domain/scanner/checks/industry/index.ts +40 -0
  340. package/src/domain/scanner/checks/industry/industry.test.ts +287 -0
  341. package/src/domain/scanner/checks/interaction-logging.test.ts +113 -0
  342. package/src/domain/scanner/checks/interaction-logging.ts +142 -0
  343. package/src/domain/scanner/checks/nhi-scanner.test.ts +158 -0
  344. package/src/domain/scanner/checks/nhi-scanner.ts +78 -0
  345. package/src/domain/scanner/checks/passport-completeness.test.ts +127 -0
  346. package/src/domain/scanner/checks/passport-completeness.ts +82 -0
  347. package/src/domain/scanner/checks/passport-presence.test.ts +56 -0
  348. package/src/domain/scanner/checks/passport-presence.ts +78 -0
  349. package/src/domain/scanner/checks/pattern-check-factory.ts +70 -0
  350. package/src/domain/scanner/checks/permission-scanner.test.ts +279 -0
  351. package/src/domain/scanner/checks/permission-scanner.ts +90 -0
  352. package/src/domain/scanner/checks/presence-check-factory.test.ts +124 -0
  353. package/src/domain/scanner/checks/presence-check-factory.ts +275 -0
  354. package/src/domain/scanner/compliance-diff.test.ts +165 -0
  355. package/src/domain/scanner/compliance-diff.ts +138 -0
  356. package/src/domain/scanner/confidence.test.ts +235 -0
  357. package/src/domain/scanner/confidence.ts +156 -0
  358. package/src/domain/scanner/constants.ts +13 -0
  359. package/src/domain/scanner/create-scanner.ts +573 -0
  360. package/src/domain/scanner/cross-layer.test.ts +372 -0
  361. package/src/domain/scanner/cross-layer.ts +232 -0
  362. package/src/domain/scanner/data/ai-packages.ts +82 -0
  363. package/src/domain/scanner/debt-calculator.test.ts +89 -0
  364. package/src/domain/scanner/debt-calculator.ts +111 -0
  365. package/src/domain/scanner/drift.test.ts +191 -0
  366. package/src/domain/scanner/drift.ts +73 -0
  367. package/src/domain/scanner/evidence-store.test.ts +207 -0
  368. package/src/domain/scanner/evidence-store.ts +195 -0
  369. package/src/domain/scanner/evidence.test.ts +104 -0
  370. package/src/domain/scanner/evidence.ts +71 -0
  371. package/src/domain/scanner/external/bandit-runner.test.ts +45 -0
  372. package/src/domain/scanner/external/bandit-runner.ts +90 -0
  373. package/src/domain/scanner/external/checks.ts +321 -0
  374. package/src/domain/scanner/external/dedup.test.ts +79 -0
  375. package/src/domain/scanner/external/dedup.ts +94 -0
  376. package/src/domain/scanner/external/detect-secrets-runner.test.ts +58 -0
  377. package/src/domain/scanner/external/detect-secrets-runner.ts +81 -0
  378. package/src/domain/scanner/external/external-scanner.test.ts +221 -0
  379. package/src/domain/scanner/external/external-scanner.ts +36 -0
  380. package/src/domain/scanner/external/finding-mapper.test.ts +95 -0
  381. package/src/domain/scanner/external/finding-mapper.ts +138 -0
  382. package/src/domain/scanner/external/index.ts +15 -0
  383. package/src/domain/scanner/external/mappings.ts +93 -0
  384. package/src/domain/scanner/external/modelscan-runner.test.ts +35 -0
  385. package/src/domain/scanner/external/modelscan-runner.ts +101 -0
  386. package/src/domain/scanner/external/path-utils.ts +8 -0
  387. package/src/domain/scanner/external/runner-port.ts +45 -0
  388. package/src/domain/scanner/external/semgrep-runner.test.ts +52 -0
  389. package/src/domain/scanner/external/semgrep-runner.ts +94 -0
  390. package/src/domain/scanner/external/types.ts +32 -0
  391. package/src/domain/scanner/finding-attribution.test.ts +444 -0
  392. package/src/domain/scanner/finding-attribution.ts +195 -0
  393. package/src/domain/scanner/finding-explainer.test.ts +157 -0
  394. package/src/domain/scanner/finding-explainer.ts +73 -0
  395. package/src/domain/scanner/fix-diff-builder.test.ts +272 -0
  396. package/src/domain/scanner/fix-diff-builder.ts +477 -0
  397. package/src/domain/scanner/import-graph.test.ts +162 -0
  398. package/src/domain/scanner/import-graph.ts +198 -0
  399. package/src/domain/scanner/languages/adapter.test.ts +105 -0
  400. package/src/domain/scanner/languages/adapter.ts +239 -0
  401. package/src/domain/scanner/layers/index.ts +24 -0
  402. package/src/domain/scanner/layers/layer1-files.ts +54 -0
  403. package/src/domain/scanner/layers/layer2-docs.test.ts +1207 -0
  404. package/src/domain/scanner/layers/layer2-docs.ts +297 -0
  405. package/src/domain/scanner/layers/layer2-parsing.ts +217 -0
  406. package/src/domain/scanner/layers/layer3-config.test.ts +187 -0
  407. package/src/domain/scanner/layers/layer3-config.ts +279 -0
  408. package/src/domain/scanner/layers/layer3-parsers.ts +73 -0
  409. package/src/domain/scanner/layers/layer4-patterns.test.ts +397 -0
  410. package/src/domain/scanner/layers/layer4-patterns.ts +216 -0
  411. package/src/domain/scanner/layers/layer5-docs.test.ts +99 -0
  412. package/src/domain/scanner/layers/layer5-docs.ts +250 -0
  413. package/src/domain/scanner/layers/layer5-llm.test.ts +146 -0
  414. package/src/domain/scanner/layers/layer5-llm.ts +262 -0
  415. package/src/domain/scanner/layers/layer5-targeted.test.ts +93 -0
  416. package/src/domain/scanner/layers/layer5-targeted.ts +233 -0
  417. package/src/domain/scanner/layers/lockfile-parsers.test.ts +320 -0
  418. package/src/domain/scanner/layers/lockfile-parsers.ts +184 -0
  419. package/src/domain/scanner/regulation-version.test.ts +54 -0
  420. package/src/domain/scanner/regulation-version.ts +23 -0
  421. package/src/domain/scanner/role-filter.test.ts +116 -0
  422. package/src/domain/scanner/role-filter.ts +51 -0
  423. package/src/domain/scanner/rules/banned-packages-data.ts +553 -0
  424. package/src/domain/scanner/rules/banned-packages-sdk.ts +65 -0
  425. package/src/domain/scanner/rules/banned-packages.test.ts +249 -0
  426. package/src/domain/scanner/rules/banned-packages.ts +55 -0
  427. package/src/domain/scanner/rules/comment-filter.test.ts +115 -0
  428. package/src/domain/scanner/rules/comment-filter.ts +297 -0
  429. package/src/domain/scanner/rules/index.ts +9 -0
  430. package/src/domain/scanner/rules/nhi-patterns.test.ts +128 -0
  431. package/src/domain/scanner/rules/nhi-patterns.ts +60 -0
  432. package/src/domain/scanner/rules/pattern-rules.ts +1152 -0
  433. package/src/domain/scanner/sbom.test.ts +136 -0
  434. package/src/domain/scanner/sbom.ts +103 -0
  435. package/src/domain/scanner/scan-cache.test.ts +136 -0
  436. package/src/domain/scanner/scan-cache.ts +115 -0
  437. package/src/domain/scanner/scanner.test.ts +125 -0
  438. package/src/domain/scanner/score-calculator.test.ts +363 -0
  439. package/src/domain/scanner/score-calculator.ts +189 -0
  440. package/src/domain/scanner/security-score.test.ts +107 -0
  441. package/src/domain/scanner/security-score.ts +116 -0
  442. package/src/domain/scanner/source-filter.ts +24 -0
  443. package/src/domain/scanner/validators.ts +223 -0
  444. package/src/domain/shared/compliance-constants.ts +48 -0
  445. package/src/domain/shared/disclosure-patterns.ts +16 -0
  446. package/src/domain/shared/index.ts +6 -0
  447. package/src/domain/shared/parse-dependencies.ts +21 -0
  448. package/src/domain/supply-chain/dependency-analyzer.ts +138 -0
  449. package/src/domain/supply-chain/index.ts +3 -0
  450. package/src/domain/supply-chain/supply-chain.test.ts +211 -0
  451. package/src/domain/supply-chain/types.ts +32 -0
  452. package/src/domain/whatif/config-fixer.ts +187 -0
  453. package/src/domain/whatif/index.ts +6 -0
  454. package/src/domain/whatif/scenario-engine.ts +121 -0
  455. package/src/domain/whatif/simulate-actions.test.ts +161 -0
  456. package/src/domain/whatif/simulate-actions.ts +114 -0
  457. package/src/domain/whatif/whatif.test.ts +135 -0
  458. package/src/e2e/gaps-e2e.test.ts +259 -0
  459. package/src/e2e/smoke.test.ts +101 -0
  460. package/src/hooks/hooks-export.test.ts +81 -0
  461. package/src/hooks/installer.ts +113 -0
  462. package/src/http/cors.test.ts +38 -0
  463. package/src/http/create-router.ts +259 -0
  464. package/src/http/routes/agent.route.ts +380 -0
  465. package/src/http/routes/audit.route.ts +66 -0
  466. package/src/http/routes/badge.route.ts +23 -0
  467. package/src/http/routes/cert.route.ts +66 -0
  468. package/src/http/routes/chat.route.ts +228 -0
  469. package/src/http/routes/cost.route.ts +33 -0
  470. package/src/http/routes/debt.route.ts +29 -0
  471. package/src/http/routes/disclaimer.route.ts +64 -0
  472. package/src/http/routes/eval.route.ts +161 -0
  473. package/src/http/routes/events.route.test.ts +108 -0
  474. package/src/http/routes/events.route.ts +71 -0
  475. package/src/http/routes/external-scan.route.ts +24 -0
  476. package/src/http/routes/file.route.ts +54 -0
  477. package/src/http/routes/fix.route.ts +219 -0
  478. package/src/http/routes/frameworks.route.test.ts +66 -0
  479. package/src/http/routes/frameworks.route.ts +36 -0
  480. package/src/http/routes/git.route.ts +27 -0
  481. package/src/http/routes/guided-onboarding.route.ts +65 -0
  482. package/src/http/routes/import.route.ts +64 -0
  483. package/src/http/routes/jurisdiction.route.ts +22 -0
  484. package/src/http/routes/obligations.route.test.ts +122 -0
  485. package/src/http/routes/obligations.route.ts +110 -0
  486. package/src/http/routes/onboarding.route.ts +53 -0
  487. package/src/http/routes/provider.route.ts +42 -0
  488. package/src/http/routes/proxy.route.ts +40 -0
  489. package/src/http/routes/redteam.route.ts +84 -0
  490. package/src/http/routes/report.route.ts +29 -0
  491. package/src/http/routes/scan.route.ts +104 -0
  492. package/src/http/routes/share.route.ts +44 -0
  493. package/src/http/routes/shell.route.ts +27 -0
  494. package/src/http/routes/status.route.ts +66 -0
  495. package/src/http/routes/supply-chain.route.ts +121 -0
  496. package/src/http/routes/sync.route.ts +328 -0
  497. package/src/http/routes/tools.route.ts +29 -0
  498. package/src/http/routes/whatif.route.ts +96 -0
  499. package/src/http/utils/validation.ts +31 -0
  500. package/src/index.ts +1 -0
  501. package/src/infra/bundle-fetcher.ts +77 -0
  502. package/src/infra/cache-storage.ts +34 -0
  503. package/src/infra/event-bus.ts +31 -0
  504. package/src/infra/file-collector.ts +61 -0
  505. package/src/infra/file-ops-adapter.ts +95 -0
  506. package/src/infra/file-watcher.test.ts +90 -0
  507. package/src/infra/file-watcher.ts +106 -0
  508. package/src/infra/git-adapter.ts +93 -0
  509. package/src/infra/git-history-adapter.ts +41 -0
  510. package/src/infra/headless-browser.ts +178 -0
  511. package/src/infra/llm-adapter.test.ts +83 -0
  512. package/src/infra/llm-adapter.ts +86 -0
  513. package/src/infra/logger.ts +27 -0
  514. package/src/infra/project-config.test.ts +74 -0
  515. package/src/infra/project-config.ts +35 -0
  516. package/src/infra/rate-limiter.test.ts +36 -0
  517. package/src/infra/rate-limiter.ts +34 -0
  518. package/src/infra/retry.ts +46 -0
  519. package/src/infra/saas-client.ts +123 -0
  520. package/src/infra/search-adapter.ts +113 -0
  521. package/src/infra/shell-adapter.ts +68 -0
  522. package/src/infra/tool-manager.test.ts +99 -0
  523. package/src/infra/tool-manager.ts +197 -0
  524. package/src/llm/agents/agent-modes.test.ts +44 -0
  525. package/src/llm/agents/modes.ts +68 -0
  526. package/src/llm/routing/cost-routing.test.ts +37 -0
  527. package/src/llm/routing/cost-tracker.ts +74 -0
  528. package/src/llm/routing/model-routing.test.ts +79 -0
  529. package/src/llm/routing/model-routing.ts +38 -0
  530. package/src/llm/routing/pricing.ts +19 -0
  531. package/src/llm/sse-protocol.ts +77 -0
  532. package/src/llm/tool-definitions.ts +83 -0
  533. package/src/llm/tool-executors.ts +80 -0
  534. package/src/llm/tools/types.ts +13 -0
  535. package/src/mcp/create-mcp-stack.ts +82 -0
  536. package/src/mcp/handlers.ts +245 -0
  537. package/src/mcp/index.ts +28 -0
  538. package/src/mcp/mcp-server.test.ts +80 -0
  539. package/src/mcp/server.ts +79 -0
  540. package/src/mcp/tools.ts +48 -0
  541. package/src/onboarding/auto-detect.ts +164 -0
  542. package/src/onboarding/onboarding.test.ts +89 -0
  543. package/src/onboarding/profile.ts +169 -0
  544. package/src/onboarding/questions.ts +112 -0
  545. package/src/onboarding/wizard.ts +66 -0
  546. package/src/output/github-issue.ts +32 -0
  547. package/src/output/json-output.ts +67 -0
  548. package/src/ports/browser.port.ts +23 -0
  549. package/src/ports/events.port.ts +28 -0
  550. package/src/ports/llm.port.ts +23 -0
  551. package/src/ports/logger.port.ts +6 -0
  552. package/src/ports/process.port.ts +6 -0
  553. package/src/ports/scanner.port.ts +15 -0
  554. package/src/server.ts +134 -0
  555. package/src/services/badge-service.ts +67 -0
  556. package/src/services/chat-service.test.ts +162 -0
  557. package/src/services/chat-service.ts +152 -0
  558. package/src/services/cost-service.ts +52 -0
  559. package/src/services/debt-service.ts +65 -0
  560. package/src/services/eval-integration.test.ts +132 -0
  561. package/src/services/eval-service.test.ts +373 -0
  562. package/src/services/eval-service.ts +463 -0
  563. package/src/services/external-scan-service.ts +60 -0
  564. package/src/services/file-service.ts +37 -0
  565. package/src/services/fix-service.test.ts +470 -0
  566. package/src/services/fix-service.ts +648 -0
  567. package/src/services/framework-service.test.ts +159 -0
  568. package/src/services/framework-service.ts +67 -0
  569. package/src/services/onboarding-service.ts +165 -0
  570. package/src/services/passport-audit.ts +244 -0
  571. package/src/services/passport-documents.ts +258 -0
  572. package/src/services/passport-service-utils.ts +72 -0
  573. package/src/services/passport-service.test.ts +251 -0
  574. package/src/services/passport-service.ts +339 -0
  575. package/src/services/proxy-service.ts +81 -0
  576. package/src/services/report-service.ts +72 -0
  577. package/src/services/scan-service.test.ts +470 -0
  578. package/src/services/scan-service.ts +335 -0
  579. package/src/services/share-service.ts +108 -0
  580. package/src/services/shared/backup.ts +23 -0
  581. package/src/services/status-service.ts +38 -0
  582. package/src/services/undo-service.test.ts +190 -0
  583. package/src/services/undo-service.ts +144 -0
  584. package/src/test-helpers/factories.ts +116 -0
  585. package/src/types/common.schemas.ts +147 -0
  586. package/src/types/common.types.ts +292 -0
  587. package/src/types/contract.test.ts +217 -0
  588. package/src/types/errors.ts +52 -0
  589. package/src/types/framework.types.ts +87 -0
  590. package/src/types/passport-schemas.ts +241 -0
  591. package/src/types/passport.types.ts +296 -0
  592. package/src/version.ts +1 -0
  593. package/tsconfig.json +20 -0
  594. package/vitest.config.ts +9 -0
@@ -0,0 +1,273 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { generateFria, generateFriaStructured } from './fria-generator.js';
3
+ import type { AgentPassport } from '../../types/passport.types.js';
4
+ import { createMockPassport } from '../../test-helpers/factories.js';
5
+
6
+ const TEMPLATE = `# Template 3: Fundamental Rights Impact Assessment (FRIA)
7
+
8
+ ### 1. Assessment Header
9
+
10
+ | Field | Value |
11
+ |-------|-------|
12
+ | Document Title | Fundamental Rights Impact Assessment — [AI System Name] |
13
+ | Assessment ID | FRIA-[YYYY]-[NNN] |
14
+ | Date | [Date] |
15
+ | Assessor | [Name, Title] |
16
+ | DPO Consulted | [Name, Date] |
17
+
18
+ ### 2. AI System Description
19
+
20
+ - System name: [Name]
21
+ - Provider: [Name]
22
+ - Version: [Number]
23
+ - Intended purpose: [Description]
24
+ - Deployment context: [Where and how the system is used]
25
+
26
+ ### 3. Deployer Information
27
+
28
+ - Organisation: [Name]
29
+
30
+ ### 4. Fundamental Rights Risk Assessment
31
+
32
+ | Fundamental Right | Risk Level | Description |
33
+ |---|---|---|
34
+ | Non-discrimination | [H/M/L/N] | [Description] |
35
+ | Privacy | [H/M/L/N] | [Description] |
36
+
37
+ ### 5. Mitigation Measures and Human Oversight
38
+ - Override mechanism: [Description of how human can intervene/stop the system]
39
+ `;
40
+
41
+ const createManifest = createMockPassport;
42
+
43
+ describe('generateFria', () => {
44
+ it('replaces AI System Name from manifest', () => {
45
+ const result = generateFria({ manifest: createManifest(), template: TEMPLATE });
46
+ expect(result.markdown).toContain('Fundamental Rights Impact Assessment — Test Agent');
47
+ expect(result.prefilledFields).toContain('AI System Name');
48
+ });
49
+
50
+ it('replaces Provider from manifest.model.provider', () => {
51
+ const result = generateFria({ manifest: createManifest(), template: TEMPLATE });
52
+ expect(result.markdown).toContain('Provider: OpenAI');
53
+ expect(result.prefilledFields).toContain('Provider');
54
+ });
55
+
56
+ it('replaces Version from manifest.version', () => {
57
+ const result = generateFria({ manifest: createManifest(), template: TEMPLATE });
58
+ expect(result.markdown).toContain('Version: 1.0.0');
59
+ expect(result.prefilledFields).toContain('Version');
60
+ });
61
+
62
+ it('replaces Intended purpose from manifest.description', () => {
63
+ const result = generateFria({ manifest: createManifest(), template: TEMPLATE });
64
+ expect(result.markdown).toContain('Intended purpose: An AI agent for testing compliance');
65
+ expect(result.prefilledFields).toContain('Intended purpose');
66
+ });
67
+
68
+ it('generates FRIA ID with current year', () => {
69
+ const result = generateFria({ manifest: createManifest(), template: TEMPLATE });
70
+ const year = new Date().getFullYear();
71
+ expect(result.markdown).toMatch(new RegExp(`FRIA-${year}-\\d{3}`));
72
+ expect(result.prefilledFields).toContain('Assessment ID');
73
+ });
74
+
75
+ it('fills date with current date', () => {
76
+ const result = generateFria({ manifest: createManifest(), template: TEMPLATE });
77
+ const today = new Date().toISOString().slice(0, 10);
78
+ expect(result.markdown).toContain(today);
79
+ });
80
+
81
+ it('uses organization param over manifest.owner.team', () => {
82
+ const result = generateFria({
83
+ manifest: createManifest(),
84
+ template: TEMPLATE,
85
+ organization: 'Custom Org',
86
+ });
87
+ expect(result.markdown).toContain('Organisation: Custom Org');
88
+ });
89
+
90
+ it('falls back to owner.team when no organization param', () => {
91
+ const result = generateFria({ manifest: createManifest(), template: TEMPLATE });
92
+ expect(result.markdown).toContain('Organisation: Acme Corp');
93
+ });
94
+
95
+ it('fills assessor when provided', () => {
96
+ const result = generateFria({
97
+ manifest: createManifest(),
98
+ template: TEMPLATE,
99
+ assessor: 'Jane Doe, CTO',
100
+ });
101
+ expect(result.markdown).toContain('Jane Doe, CTO');
102
+ expect(result.prefilledFields).toContain('Assessor');
103
+ });
104
+
105
+ it('lists assessor as manual field when not provided', () => {
106
+ const result = generateFria({ manifest: createManifest(), template: TEMPLATE });
107
+ expect(result.manualFields).toContain('Assessor (Name, Title)');
108
+ });
109
+
110
+ it('pre-fills risk level from high-risk passport', () => {
111
+ const result = generateFria({ manifest: createManifest(), template: TEMPLATE });
112
+ // First row should get H (high risk)
113
+ expect(result.markdown).toContain('| H |');
114
+ });
115
+
116
+ it('generates human oversight description from autonomy level', () => {
117
+ const result = generateFria({ manifest: createManifest(), template: TEMPLATE });
118
+ expect(result.markdown).toContain('direct human supervision');
119
+ expect(result.prefilledFields).toContain('Human oversight mechanism');
120
+ });
121
+
122
+ it('includes human_approval_required in oversight', () => {
123
+ const result = generateFria({ manifest: createManifest(), template: TEMPLATE });
124
+ expect(result.markdown).toContain('deploy, delete');
125
+ });
126
+
127
+ it('lists manual fields that cannot be auto-filled', () => {
128
+ const result = generateFria({ manifest: createManifest(), template: TEMPLATE });
129
+ expect(result.manualFields).toContain('DPO Consulted (Name, Date)');
130
+ expect(result.manualFields).toContain('Deployment context');
131
+ expect(result.manualFields).toContain('Overall risk assessment decision');
132
+ expect(result.manualFields).toContain('Assessor sign-off');
133
+ expect(result.manualFields.length).toBeGreaterThan(10);
134
+ });
135
+
136
+ it('returns frozen result', () => {
137
+ const result = generateFria({ manifest: createManifest(), template: TEMPLATE });
138
+ expect(Object.isFrozen(result)).toBe(true);
139
+ expect(Object.isFrozen(result.prefilledFields)).toBe(true);
140
+ expect(Object.isFrozen(result.manualFields)).toBe(true);
141
+ });
142
+
143
+ it('pre-fills impact when provided', () => {
144
+ const result = generateFria({
145
+ manifest: createManifest(),
146
+ template: TEMPLATE + '\n[e.g., AI may produce biased outcomes against certain ethnic groups in credit decisions]',
147
+ impact: 'Credit scoring bias against minorities',
148
+ });
149
+ expect(result.markdown).toContain('Credit scoring bias against minorities');
150
+ expect(result.prefilledFields).toContain('Impact description');
151
+ expect(result.manualFields).not.toContain('Fundamental Rights risk descriptions');
152
+ });
153
+
154
+ it('pre-fills mitigation when provided', () => {
155
+ const result = generateFria({
156
+ manifest: createManifest(),
157
+ template: TEMPLATE + '\n[e.g., Regular bias audits, human review of rejections, fairness metrics monitoring]',
158
+ mitigation: 'Quarterly bias audits and model retraining',
159
+ });
160
+ expect(result.markdown).toContain('Quarterly bias audits and model retraining');
161
+ expect(result.prefilledFields).toContain('Mitigation measures');
162
+ expect(result.manualFields).not.toContain('Mitigation measures');
163
+ });
164
+
165
+ it('pre-fills approval when provided', () => {
166
+ const result = generateFria({
167
+ manifest: createManifest(),
168
+ template: TEMPLATE + '\nDecision-maker: _________________ Date: _________',
169
+ approval: 'Jane Doe, CTO',
170
+ });
171
+ expect(result.markdown).toContain('Decision-maker: Jane Doe, CTO');
172
+ expect(result.prefilledFields).toContain('Decision-maker sign-off');
173
+ expect(result.manualFields).not.toContain('Decision-maker sign-off');
174
+ });
175
+
176
+ it('leaves manual fields when flags not provided', () => {
177
+ const result = generateFria({
178
+ manifest: createManifest(),
179
+ template: TEMPLATE + '\n[e.g., AI may produce biased outcomes against certain ethnic groups in credit decisions]\n[e.g., Regular bias audits, human review of rejections, fairness metrics monitoring]\nDecision-maker: _________________ Date: _________',
180
+ });
181
+ expect(result.manualFields).toContain('Fundamental Rights risk descriptions');
182
+ expect(result.manualFields).toContain('Mitigation measures');
183
+ expect(result.manualFields).toContain('Decision-maker sign-off');
184
+ });
185
+
186
+ it('includes structured payload in result', () => {
187
+ const result = generateFria({ manifest: createManifest(), template: TEMPLATE });
188
+ expect(result.structured).toBeDefined();
189
+ expect(result.structured.toolSlug).toBe('agent-test-001');
190
+ expect(result.structured.sections.general_info.toolName).toBe('Test Agent');
191
+ });
192
+ });
193
+
194
+ describe('generateFriaStructured', () => {
195
+ it('maps general_info from manifest', () => {
196
+ const s = generateFriaStructured({ manifest: createManifest(), template: '' });
197
+ expect(s.toolSlug).toBe('agent-test-001');
198
+ expect(s.sections.general_info.toolName).toBe('Test Agent');
199
+ expect(s.sections.general_info.vendor).toBe('Acme Corp');
200
+ expect(s.sections.general_info.purpose).toBe('An AI agent for testing compliance');
201
+ expect(s.sections.general_info.riskLevel).toBe('high');
202
+ expect(s.sections.general_info.version).toBe('1.0.0');
203
+ expect(s.sections.general_info.provider).toBe('OpenAI');
204
+ });
205
+
206
+ it('uses organization option over manifest.owner.team', () => {
207
+ const s = generateFriaStructured({ manifest: createManifest(), template: '', organization: 'Custom Org' });
208
+ expect(s.sections.general_info.organisation).toBe('Custom Org');
209
+ });
210
+
211
+ it('falls back to owner.team for organisation', () => {
212
+ const s = generateFriaStructured({ manifest: createManifest(), template: '' });
213
+ expect(s.sections.general_info.organisation).toBe('Acme Corp');
214
+ });
215
+
216
+ it('fills assessorName when assessor provided', () => {
217
+ const s = generateFriaStructured({ manifest: createManifest(), template: '', assessor: 'Jane Doe' });
218
+ expect(s.sections.general_info.assessorName).toBe('Jane Doe');
219
+ });
220
+
221
+ it('generates 8 Charter rights in specific_risks', () => {
222
+ const s = generateFriaStructured({ manifest: createManifest(), template: '' });
223
+ expect(s.sections.specific_risks.risks).toHaveLength(8);
224
+ expect(s.sections.specific_risks.risks[0]!.right).toBe('Non-discrimination');
225
+ expect(s.sections.specific_risks.risks[0]!.article).toBe('Art. 21');
226
+ });
227
+
228
+ it('pre-fills first risk severity from high-risk passport', () => {
229
+ const s = generateFriaStructured({ manifest: createManifest(), template: '' });
230
+ expect(s.sections.specific_risks.risks[0]!.severity).toBe('H');
231
+ expect(s.sections.specific_risks.risks[1]!.severity).toBe('');
232
+ });
233
+
234
+ it('sets severity L for minimal risk', () => {
235
+ const manifest = createManifest({
236
+ compliance: { ...createManifest().compliance, eu_ai_act: { ...createManifest().compliance.eu_ai_act, risk_class: 'minimal' } },
237
+ } as Partial<AgentPassport>);
238
+ const s = generateFriaStructured({ manifest, template: '' });
239
+ expect(s.sections.specific_risks.risks[0]!.severity).toBe('L');
240
+ });
241
+
242
+ it('derives human_oversight from autonomy level L2', () => {
243
+ const s = generateFriaStructured({ manifest: createManifest(), template: '' });
244
+ expect(s.sections.human_oversight.hasHumanOversight).toBe(true);
245
+ expect(s.sections.human_oversight.oversightType).toBe('pre_decision');
246
+ expect(s.sections.human_oversight.mechanism).toContain('3 human approval gate(s)');
247
+ });
248
+
249
+ it('derives human_oversight from autonomy level L4', () => {
250
+ const manifest = createManifest({ autonomy_level: 'L4' } as Partial<AgentPassport>);
251
+ const s = generateFriaStructured({ manifest, template: '' });
252
+ expect(s.sections.human_oversight.hasHumanOversight).toBe(false);
253
+ expect(s.sections.human_oversight.oversightType).toBe('post_hoc');
254
+ });
255
+
256
+ it('leaves manual fields as empty strings/arrays', () => {
257
+ const s = generateFriaStructured({ manifest: createManifest(), template: '' });
258
+ expect(s.sections.general_info.deploymentContext).toBe('');
259
+ expect(s.sections.general_info.geographicScope).toBe('');
260
+ expect(s.sections.affected_persons.categories).toEqual([]);
261
+ expect(s.sections.affected_persons.description).toBe('');
262
+ expect(s.sections.mitigation_measures.measures).toEqual([]);
263
+ expect(s.sections.monitoring_plan.metrics).toEqual([]);
264
+ expect(s.sections.monitoring_plan.frequency).toBe('');
265
+ });
266
+
267
+ it('generates valid assessmentId and date', () => {
268
+ const s = generateFriaStructured({ manifest: createManifest(), template: '' });
269
+ const year = new Date().getFullYear();
270
+ expect(s.assessmentId).toMatch(new RegExp(`^FRIA-${year}-\\d{3}$`));
271
+ expect(s.date).toMatch(/^\d{4}-\d{2}-\d{2}$/);
272
+ });
273
+ });
@@ -0,0 +1,366 @@
1
+ import type { AgentPassport } from '../../types/passport.types.js';
2
+ import { mapDomain } from '../passport/builder/domain-mapper.js';
3
+ import { deriveOversightDescription } from '../documents/passport-helpers.js';
4
+
5
+ // --- Types ---
6
+
7
+ export interface FriaGeneratorInput {
8
+ readonly manifest: AgentPassport;
9
+ readonly template: string;
10
+ readonly organization?: string;
11
+ readonly assessor?: string;
12
+ readonly impact?: string;
13
+ readonly mitigation?: string;
14
+ readonly approval?: string;
15
+ }
16
+
17
+ export interface FriaResult {
18
+ readonly markdown: string;
19
+ readonly structured: FriaStructuredPayload;
20
+ readonly prefilledFields: readonly string[];
21
+ readonly manualFields: readonly string[];
22
+ }
23
+
24
+ /** Structured FRIA payload — matches SaaS FRIASection schema */
25
+ export interface FriaStructuredPayload {
26
+ readonly toolSlug: string;
27
+ readonly assessmentId: string;
28
+ readonly date: string;
29
+ readonly sections: {
30
+ readonly general_info: {
31
+ readonly toolName: string;
32
+ readonly vendor: string;
33
+ readonly purpose: string;
34
+ readonly domain: string;
35
+ readonly riskLevel: string;
36
+ readonly version: string;
37
+ readonly provider: string;
38
+ readonly deploymentContext: string;
39
+ readonly assessorName: string;
40
+ readonly assessorTitle: string;
41
+ readonly geographicScope: string;
42
+ readonly organisation: string;
43
+ readonly organisationType: string;
44
+ };
45
+ readonly affected_persons: {
46
+ readonly categories: readonly string[];
47
+ readonly vulnerableGroups: boolean;
48
+ readonly estimatedCount: string;
49
+ readonly description: string;
50
+ };
51
+ readonly specific_risks: {
52
+ readonly risks: ReadonlyArray<{
53
+ readonly right: string;
54
+ readonly article: string;
55
+ readonly severity: string;
56
+ readonly likelihood: string;
57
+ readonly description: string;
58
+ readonly affectedGroups: string;
59
+ readonly mitigation: string;
60
+ }>;
61
+ };
62
+ readonly human_oversight: {
63
+ readonly hasHumanOversight: boolean;
64
+ readonly oversightType: string;
65
+ readonly mechanism: string;
66
+ readonly responsibleRole: string;
67
+ readonly escalationProcess: string;
68
+ readonly reviewFrequency: string;
69
+ };
70
+ readonly mitigation_measures: {
71
+ readonly measures: ReadonlyArray<{
72
+ readonly risk: string;
73
+ readonly measure: string;
74
+ readonly responsible: string;
75
+ readonly deadline: string;
76
+ }>;
77
+ readonly incidentResponse: string;
78
+ readonly communicationPlan: string;
79
+ readonly suspensionCriteria: string;
80
+ readonly remediationProcess: string;
81
+ readonly internalComplaint: string;
82
+ readonly externalComplaint: string;
83
+ };
84
+ readonly monitoring_plan: {
85
+ readonly frequency: string;
86
+ readonly metrics: readonly string[];
87
+ readonly responsibleTeam: string;
88
+ readonly reviewProcess: string;
89
+ readonly nextReviewDate: string;
90
+ readonly dpiaReference: string;
91
+ readonly legalBasis: string;
92
+ readonly overallRiskDecision: string;
93
+ readonly conditionsForDeployment: string;
94
+ };
95
+ };
96
+ }
97
+
98
+ // --- Helpers ---
99
+
100
+ const generateFriaId = (): string => {
101
+ const year = new Date().getFullYear();
102
+ const seq = String(Math.floor(Math.random() * 999) + 1).padStart(3, '0');
103
+ return `FRIA-${year}-${seq}`;
104
+ };
105
+
106
+ const deriveRiskLevel = (riskClass: string): string => {
107
+ switch (riskClass) {
108
+ case 'prohibited': return 'H';
109
+ case 'high': return 'H';
110
+ case 'limited': return 'M';
111
+ case 'minimal': return 'L';
112
+ default: return '[H/M/L/N]';
113
+ }
114
+ };
115
+
116
+ // --- Charter Rights for FRIA ---
117
+
118
+ const CHARTER_RIGHTS = [
119
+ { right: 'Non-discrimination', article: 'Art. 21' },
120
+ { right: 'Privacy and data protection', article: 'Arts. 7-8' },
121
+ { right: 'Freedom of expression', article: 'Art. 11' },
122
+ { right: 'Human dignity', article: 'Art. 1' },
123
+ { right: 'Right to effective remedy', article: 'Art. 47' },
124
+ { right: 'Rights of the child', article: 'Art. 24' },
125
+ { right: 'Workers\' rights', article: 'Art. 31' },
126
+ { right: 'Right to good administration', article: 'Art. 41' },
127
+ ] as const;
128
+
129
+ const mapOversightType = (level: string): string => {
130
+ if (level === 'L1' || level === 'L2') return 'pre_decision';
131
+ if (level === 'L3') return 'concurrent';
132
+ return 'post_hoc';
133
+ };
134
+
135
+ /** Generate structured FRIA JSON from manifest data */
136
+ export const generateFriaStructured = (input: FriaGeneratorInput): FriaStructuredPayload => {
137
+ const { manifest, organization, assessor } = input;
138
+ const today = new Date().toISOString().slice(0, 10);
139
+ const riskClass = manifest.compliance?.eu_ai_act?.risk_class ?? '';
140
+ const firstSeverity = deriveRiskLevel(riskClass);
141
+
142
+ return {
143
+ toolSlug: manifest.agent_id,
144
+ assessmentId: generateFriaId(),
145
+ date: today,
146
+ sections: {
147
+ general_info: {
148
+ toolName: manifest.display_name,
149
+ vendor: manifest.owner?.team ?? '',
150
+ purpose: manifest.description,
151
+ domain: mapDomain(manifest),
152
+ riskLevel: riskClass,
153
+ version: manifest.version,
154
+ provider: manifest.model?.provider ?? '',
155
+ deploymentContext: '',
156
+ assessorName: assessor ?? '',
157
+ assessorTitle: '',
158
+ geographicScope: '',
159
+ organisation: organization ?? manifest.owner?.team ?? '',
160
+ organisationType: '',
161
+ },
162
+ affected_persons: {
163
+ categories: [],
164
+ vulnerableGroups: false,
165
+ estimatedCount: '',
166
+ description: '',
167
+ },
168
+ specific_risks: {
169
+ risks: CHARTER_RIGHTS.map((cr, i) => ({
170
+ right: cr.right,
171
+ article: cr.article,
172
+ severity: i === 0 && firstSeverity !== '[H/M/L/N]' ? firstSeverity : '',
173
+ likelihood: '',
174
+ description: '',
175
+ affectedGroups: '',
176
+ mitigation: '',
177
+ })),
178
+ },
179
+ human_oversight: {
180
+ hasHumanOversight: ['L1', 'L2', 'L3'].includes(manifest.autonomy_level),
181
+ oversightType: mapOversightType(manifest.autonomy_level),
182
+ mechanism: manifest.autonomy_evidence?.human_approval_gates > 0
183
+ ? `${manifest.autonomy_evidence.human_approval_gates} human approval gate(s) detected`
184
+ : '',
185
+ responsibleRole: '',
186
+ escalationProcess: '',
187
+ reviewFrequency: '',
188
+ },
189
+ mitigation_measures: {
190
+ measures: [],
191
+ incidentResponse: '',
192
+ communicationPlan: '',
193
+ suspensionCriteria: '',
194
+ remediationProcess: '',
195
+ internalComplaint: '',
196
+ externalComplaint: '',
197
+ },
198
+ monitoring_plan: {
199
+ frequency: '',
200
+ metrics: [],
201
+ responsibleTeam: '',
202
+ reviewProcess: '',
203
+ nextReviewDate: '',
204
+ dpiaReference: '',
205
+ legalBasis: '',
206
+ overallRiskDecision: '',
207
+ conditionsForDeployment: '',
208
+ },
209
+ },
210
+ };
211
+ };
212
+
213
+ // --- Markdown Generator ---
214
+
215
+ export const generateFria = (input: FriaGeneratorInput): FriaResult => {
216
+ const { manifest, template, organization, assessor, impact, mitigation, approval } = input;
217
+ const prefilledFields: string[] = [];
218
+ const manualFields: string[] = [];
219
+
220
+ let markdown = template;
221
+
222
+ // 1. Assessment Header
223
+ // [AI System Name]
224
+ markdown = markdown.replaceAll('[AI System Name]', manifest.display_name);
225
+ prefilledFields.push('AI System Name');
226
+
227
+ // FRIA-[YYYY]-[NNN]
228
+ const friaId = generateFriaId();
229
+ markdown = markdown.replace('FRIA-[YYYY]-[NNN]', friaId);
230
+ prefilledFields.push('Assessment ID');
231
+
232
+ // [Date] in header
233
+ const today = new Date().toISOString().slice(0, 10);
234
+ markdown = markdown.replaceAll('[Date]', today);
235
+ prefilledFields.push('Date');
236
+
237
+ // Assessor
238
+ if (assessor) {
239
+ markdown = markdown.replace('[Name, Title]', assessor);
240
+ prefilledFields.push('Assessor');
241
+ } else {
242
+ manualFields.push('Assessor (Name, Title)');
243
+ }
244
+
245
+ // DPO
246
+ manualFields.push('DPO Consulted (Name, Date)');
247
+
248
+ // 2. AI System Description
249
+ // System name in section 2
250
+ markdown = markdown.replace('System name: [Name]', `System name: ${manifest.display_name}`);
251
+
252
+ // Provider
253
+ markdown = markdown.replace('Provider: [Name]', `Provider: ${manifest.model.provider}`);
254
+ prefilledFields.push('Provider');
255
+
256
+ // Version
257
+ markdown = markdown.replace('Version: [Number]', `Version: ${manifest.version}`);
258
+ prefilledFields.push('Version');
259
+
260
+ // Intended purpose
261
+ markdown = markdown.replace('Intended purpose: [Description]', `Intended purpose: ${manifest.description}`);
262
+ prefilledFields.push('Intended purpose');
263
+
264
+ // Deployment context, categories, geographic scope — manual
265
+ manualFields.push('Deployment context');
266
+ manualFields.push('Categories of persons affected');
267
+ manualFields.push('Geographic scope');
268
+
269
+ // 3. Deployer Information
270
+ const orgName = organization ?? manifest.owner.team;
271
+ if (orgName) {
272
+ markdown = markdown.replace('Organisation: [Name]', `Organisation: ${orgName}`);
273
+ prefilledFields.push('Organisation');
274
+ } else {
275
+ manualFields.push('Organisation');
276
+ }
277
+ manualFields.push('Organisation type');
278
+ manualFields.push('Article 27 trigger');
279
+
280
+ // 4. Risk Assessment table — set default risk level from passport
281
+ const riskLevel = deriveRiskLevel(manifest.compliance.eu_ai_act.risk_class);
282
+
283
+ // Impact description — replace placeholder or leave as manual
284
+ if (impact) {
285
+ markdown = markdown.replace('[e.g., AI may produce biased outcomes against certain ethnic groups in credit decisions]', impact);
286
+ prefilledFields.push('Impact description');
287
+ } else {
288
+ manualFields.push('Fundamental Rights risk descriptions');
289
+ }
290
+
291
+ manualFields.push('Affected groups');
292
+
293
+ // Mitigation measures — replace placeholder or leave as manual
294
+ if (mitigation) {
295
+ markdown = markdown.replace('[e.g., Regular bias audits, human review of rejections, fairness metrics monitoring]', mitigation);
296
+ prefilledFields.push('Mitigation measures');
297
+ } else {
298
+ manualFields.push('Mitigation measures');
299
+ }
300
+
301
+ // Pre-fill first risk row level if we know the risk class
302
+ if (riskLevel !== '[H/M/L/N]') {
303
+ // Replace only in the table rows (after the header)
304
+ const tableRows = markdown.split('\n');
305
+ let replaced = false;
306
+ for (let i = 0; i < tableRows.length; i++) {
307
+ if (tableRows[i]!.includes('| [H/M/L/N]') && !replaced) {
308
+ tableRows[i] = tableRows[i]!.replace('[H/M/L/N]', riskLevel);
309
+ replaced = true;
310
+ // Only pre-fill first row to hint, rest remain manual
311
+ }
312
+ }
313
+ markdown = tableRows.join('\n');
314
+ }
315
+
316
+ // 5. Mitigation Measures and Human Oversight
317
+ const oversightDesc = deriveOversightDescription(manifest);
318
+ markdown = markdown.replace(
319
+ '[Description of how human can intervene/stop the system]',
320
+ oversightDesc,
321
+ );
322
+ prefilledFields.push('Human oversight mechanism');
323
+ manualFields.push('Assigned oversight person');
324
+ manualFields.push('Escalation process');
325
+ manualFields.push('Review frequency');
326
+
327
+ // 6-8: Mostly manual
328
+ manualFields.push('Incident response plan');
329
+ manualFields.push('Communication to affected persons');
330
+ manualFields.push('System suspension criteria');
331
+ manualFields.push('Remediation process');
332
+ manualFields.push('Internal complaint mechanism');
333
+ manualFields.push('External complaint options');
334
+ manualFields.push('DPIA reference');
335
+ manualFields.push('Legal basis for data processing');
336
+
337
+ // 9. Conclusion — manual
338
+ manualFields.push('Overall risk assessment decision');
339
+ manualFields.push('Conditions for deployment');
340
+ manualFields.push('Next review date');
341
+
342
+ // 10. Sign-off
343
+ manualFields.push('Assessor sign-off');
344
+ manualFields.push('DPO sign-off');
345
+
346
+ if (approval) {
347
+ markdown = markdown.replace(
348
+ 'Decision-maker: _________________ Date: _________',
349
+ `Decision-maker: ${approval} Date: ${new Date().toISOString().split('T')[0]}`,
350
+ );
351
+ prefilledFields.push('Decision-maker sign-off');
352
+ } else {
353
+ manualFields.push('Decision-maker sign-off');
354
+ }
355
+
356
+ manualFields.push('Market surveillance notification');
357
+
358
+ const structured = generateFriaStructured(input);
359
+
360
+ return Object.freeze({
361
+ markdown,
362
+ structured,
363
+ prefilledFields: Object.freeze([...prefilledFields]),
364
+ manualFields: Object.freeze([...manualFields]),
365
+ });
366
+ };