@blamejs/core 0.7.107 → 0.8.4

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 (100) hide show
  1. package/CHANGELOG.md +41 -1
  2. package/NOTICE +17 -1
  3. package/README.md +4 -3
  4. package/index.js +15 -0
  5. package/lib/asyncapi-bindings.js +160 -0
  6. package/lib/asyncapi-traits.js +143 -0
  7. package/lib/asyncapi.js +531 -0
  8. package/lib/audit-sign.js +1 -1
  9. package/lib/audit.js +68 -2
  10. package/lib/auth/acr-vocabulary.js +265 -0
  11. package/lib/auth/auth-time-tracker.js +111 -0
  12. package/lib/auth/elevation-grant.js +306 -0
  13. package/lib/auth/jwt.js +13 -0
  14. package/lib/auth/lockout.js +16 -3
  15. package/lib/auth/oauth.js +15 -1
  16. package/lib/auth/password.js +22 -2
  17. package/lib/auth/sd-jwt-vc-issuer.js +2 -2
  18. package/lib/auth/sd-jwt-vc.js +7 -2
  19. package/lib/auth/step-up-policy.js +335 -0
  20. package/lib/auth/step-up.js +445 -0
  21. package/lib/break-glass.js +53 -14
  22. package/lib/cache-redis.js +1 -1
  23. package/lib/cache.js +6 -1
  24. package/lib/cli.js +3 -3
  25. package/lib/cluster.js +24 -1
  26. package/lib/compliance-ai-act-logging.js +190 -0
  27. package/lib/compliance-ai-act-prohibited.js +205 -0
  28. package/lib/compliance-ai-act-risk.js +189 -0
  29. package/lib/compliance-ai-act-transparency.js +200 -0
  30. package/lib/compliance-ai-act.js +558 -0
  31. package/lib/compliance.js +12 -2
  32. package/lib/config-drift.js +2 -2
  33. package/lib/crypto-field.js +21 -1
  34. package/lib/crypto.js +114 -1
  35. package/lib/db.js +35 -4
  36. package/lib/dev.js +30 -3
  37. package/lib/dual-control.js +19 -1
  38. package/lib/external-db.js +10 -0
  39. package/lib/file-upload.js +30 -3
  40. package/lib/flag-cache.js +136 -0
  41. package/lib/flag-evaluation-context.js +135 -0
  42. package/lib/flag-providers.js +279 -0
  43. package/lib/flag-targeting.js +210 -0
  44. package/lib/flag.js +284 -0
  45. package/lib/guard-all.js +33 -16
  46. package/lib/guard-csv.js +16 -2
  47. package/lib/guard-html.js +35 -0
  48. package/lib/guard-svg.js +20 -0
  49. package/lib/http-client.js +57 -11
  50. package/lib/inbox.js +391 -0
  51. package/lib/log-stream-syslog.js +8 -0
  52. package/lib/log-stream.js +1 -1
  53. package/lib/mail-arc-sign.js +372 -0
  54. package/lib/mail-auth.js +2 -0
  55. package/lib/mail.js +40 -0
  56. package/lib/middleware/ai-act-disclosure.js +166 -0
  57. package/lib/middleware/asyncapi-serve.js +136 -0
  58. package/lib/middleware/attach-user.js +25 -2
  59. package/lib/middleware/bearer-auth.js +71 -6
  60. package/lib/middleware/body-parser.js +13 -0
  61. package/lib/middleware/cors.js +10 -0
  62. package/lib/middleware/csrf-protect.js +34 -3
  63. package/lib/middleware/dpop.js +3 -3
  64. package/lib/middleware/flag-context.js +76 -0
  65. package/lib/middleware/host-allowlist.js +1 -1
  66. package/lib/middleware/index.js +15 -0
  67. package/lib/middleware/openapi-serve.js +143 -0
  68. package/lib/middleware/require-aal.js +2 -2
  69. package/lib/middleware/require-step-up.js +186 -0
  70. package/lib/middleware/trace-propagate.js +1 -1
  71. package/lib/mtls-ca.js +23 -29
  72. package/lib/mtls-engine-default.js +21 -1
  73. package/lib/network-tls.js +21 -6
  74. package/lib/object-store/sigv4-bucket-ops.js +41 -0
  75. package/lib/observability-otlp-exporter.js +35 -2
  76. package/lib/openapi-paths-builder.js +248 -0
  77. package/lib/openapi-schema-walk.js +192 -0
  78. package/lib/openapi-security.js +169 -0
  79. package/lib/openapi-yaml.js +154 -0
  80. package/lib/openapi.js +443 -0
  81. package/lib/outbox.js +3 -3
  82. package/lib/permissions.js +10 -1
  83. package/lib/pqc-agent.js +22 -1
  84. package/lib/pqc-software.js +195 -0
  85. package/lib/pubsub.js +8 -4
  86. package/lib/redact.js +26 -1
  87. package/lib/retention.js +26 -0
  88. package/lib/router.js +1 -0
  89. package/lib/scheduler.js +57 -1
  90. package/lib/session.js +3 -3
  91. package/lib/ssrf-guard.js +19 -4
  92. package/lib/static.js +12 -0
  93. package/lib/totp.js +16 -0
  94. package/lib/vault/index.js +3 -0
  95. package/lib/vault-aad.js +259 -0
  96. package/lib/vendor/MANIFEST.json +29 -0
  97. package/lib/vendor/noble-post-quantum.cjs +18 -0
  98. package/lib/ws-client.js +978 -0
  99. package/package.json +1 -1
  100. package/sbom.cyclonedx.json +6 -6
@@ -0,0 +1,190 @@
1
+ "use strict";
2
+ /**
3
+ * EU AI Act Article 12 — automatic logging requirements for high-risk
4
+ * AI systems.
5
+ *
6
+ * Per Regulation (EU) 2024/1689 Art. 12, providers of high-risk AI
7
+ * systems MUST design the system to automatically record events
8
+ * ("logs") over its lifetime, sufficient to:
9
+ *
10
+ * (a) identify situations that may result in the AI system
11
+ * presenting a risk under Art. 79(1) (post-market monitoring);
12
+ * (b) facilitate post-market monitoring per Art. 72;
13
+ * (c) monitor the operation of the high-risk AI systems referred
14
+ * to in Art. 26(5) (deployer obligations).
15
+ *
16
+ * For Annex III §1(a) (remote biometric identification systems), the
17
+ * minimum required logged fields are explicitly enumerated in
18
+ * Art. 12(3):
19
+ *
20
+ * - period of each use (start time, end time)
21
+ * - reference database against which input data was checked
22
+ * - input data for which the search led to a match
23
+ * - identification of natural persons involved in result verification
24
+ *
25
+ * The framework provides a typed event-builder that produces records
26
+ * conforming to these requirements, plus a serialiser that funnels the
27
+ * records into the framework's audit-chain (b.audit) so they ride the
28
+ * tamper-evident PQC-signed chain.
29
+ *
30
+ * Logs are operator-retained per Art. 19 (provider must keep them at
31
+ * least 6 months unless local law requires longer; for high-risk
32
+ * systems used in financial services + employment + law enforcement
33
+ * the retention floor is 1 year). The retention floor cross-walks
34
+ * into b.retention.complianceFloor.
35
+ */
36
+
37
+ var validateOpts = require("./validate-opts");
38
+ var lazyRequire = require("./lazy-require");
39
+ var C = require("./constants");
40
+ var { ComplianceError } = require("./framework-error");
41
+
42
+ var audit = lazyRequire(function () { return require("./audit"); });
43
+
44
+ // Retention floors per Art. 19. Operator's b.retention.complianceFloor
45
+ // applies the more-stringent of: AI Act floor, sectoral law, internal
46
+ // retention policy.
47
+ var RETENTION_FLOORS = Object.freeze({
48
+ default: C.TIME.days(180),
49
+ "high-risk-financial": C.TIME.days(365),
50
+ "high-risk-employment": C.TIME.days(365),
51
+ "high-risk-law-enforcement": C.TIME.days(365),
52
+ });
53
+
54
+ var MIN_BIOMETRIC_FIELDS = Object.freeze([
55
+ "periodStart", "periodEnd", "referenceDatabase",
56
+ "matchedInputRef", "verifiers",
57
+ ]);
58
+
59
+ function buildEvent(opts) {
60
+ opts = opts || {};
61
+ validateOpts(opts, [
62
+ "systemId", "kind", "actor", "timestamp",
63
+ "periodStart", "periodEnd", "referenceDatabase",
64
+ "matchedInputRef", "verifiers",
65
+ "outcome", "metadata", "annexIII",
66
+ ], "compliance.aiAct.logging.buildEvent");
67
+
68
+ validateOpts.requireNonEmptyString(opts.systemId,
69
+ "buildEvent: systemId", ComplianceError, "compliance-ai-act/bad-event");
70
+ validateOpts.requireNonEmptyString(opts.kind,
71
+ "buildEvent: kind", ComplianceError, "compliance-ai-act/bad-event");
72
+
73
+ var nowMs = (typeof opts.timestamp === "number" && isFinite(opts.timestamp))
74
+ ? opts.timestamp : Date.now();
75
+
76
+ var record = {
77
+ aiActArticle: "Art. 12",
78
+ systemId: opts.systemId,
79
+ kind: opts.kind,
80
+ timestamp: new Date(nowMs).toISOString(),
81
+ actor: opts.actor || null,
82
+ annexIII: opts.annexIII || null,
83
+ outcome: opts.outcome || "ok",
84
+ };
85
+
86
+ // Annex III §1(a) biometric-id systems require specific fields.
87
+ if (opts.annexIII === "biometric-id-categorisation") {
88
+ var missing = [];
89
+ for (var i = 0; i < MIN_BIOMETRIC_FIELDS.length; i += 1) {
90
+ var field = MIN_BIOMETRIC_FIELDS[i];
91
+ if (opts[field] == null) missing.push(field);
92
+ }
93
+ if (missing.length > 0) {
94
+ throw new ComplianceError("compliance-ai-act/missing-biometric-fields",
95
+ "buildEvent: biometric-id event missing required fields per Art. 12(3): " +
96
+ missing.join(", "));
97
+ }
98
+ record.periodStart = _toIsoString(opts.periodStart);
99
+ record.periodEnd = _toIsoString(opts.periodEnd);
100
+ record.referenceDatabase = opts.referenceDatabase;
101
+ record.matchedInputRef = opts.matchedInputRef;
102
+ record.verifiers = Array.isArray(opts.verifiers)
103
+ ? opts.verifiers.slice() : [opts.verifiers];
104
+ }
105
+
106
+ if (opts.metadata && typeof opts.metadata === "object") {
107
+ record.metadata = opts.metadata;
108
+ }
109
+ return record;
110
+ }
111
+
112
+ function _toIsoString(value) {
113
+ if (value == null) return null;
114
+ if (typeof value === "string") return value;
115
+ if (typeof value === "number" && isFinite(value)) {
116
+ return new Date(value).toISOString();
117
+ }
118
+ if (value instanceof Date) return value.toISOString();
119
+ return null;
120
+ }
121
+
122
+ function emit(event) {
123
+ if (!event || typeof event !== "object") {
124
+ throw new ComplianceError("compliance-ai-act/bad-event",
125
+ "compliance.aiAct.logging.emit: event must be an object");
126
+ }
127
+ // Funnel into the framework audit chain so the record rides the
128
+ // tamper-evident PQC-signed chain. The operator-facing kind vocabulary
129
+ // (from RFC-style slug identifiers in the AI-Act-Notice header — e.g.
130
+ // "biometric-id-categorisation") carries hyphens; the audit action
131
+ // namespace uses underscores, so the kind is rewritten before emit.
132
+ try {
133
+ var kindCanonical = String(event.kind || "log").replace(/-/g, "_");
134
+ audit().safeEmit({
135
+ action: "compliance.aiact." + kindCanonical,
136
+ outcome: event.outcome || "success",
137
+ actor: event.actor || null,
138
+ metadata: event,
139
+ });
140
+ } catch (_e) { /* drop-silent */ }
141
+ return event;
142
+ }
143
+
144
+ function logEvent(opts) {
145
+ var record = buildEvent(opts);
146
+ return emit(record);
147
+ }
148
+
149
+ function retentionFloorMs(opts) {
150
+ opts = opts || {};
151
+ validateOpts(opts, ["domain"], "compliance.aiAct.logging.retentionFloorMs");
152
+ var key = opts.domain || "default";
153
+ if (Object.prototype.hasOwnProperty.call(RETENTION_FLOORS, key)) {
154
+ return RETENTION_FLOORS[key];
155
+ }
156
+ return RETENTION_FLOORS.default;
157
+ }
158
+
159
+ // Build a request-attached logger pre-bound to a system context. The
160
+ // returned function accepts a partial event and merges it with the
161
+ // preset (systemId, annexIII, deployer).
162
+ function loggerFor(systemContext) {
163
+ if (!systemContext || typeof systemContext !== "object") {
164
+ throw new ComplianceError("compliance-ai-act/bad-system-context",
165
+ "loggerFor: systemContext must be an object");
166
+ }
167
+ validateOpts.requireNonEmptyString(systemContext.systemId,
168
+ "loggerFor: systemContext.systemId", ComplianceError, "compliance-ai-act/bad-system-context");
169
+ return function (eventPartial) {
170
+ var merged = Object.assign({}, eventPartial || {});
171
+ merged.systemId = systemContext.systemId;
172
+ if (systemContext.annexIII && !merged.annexIII) {
173
+ merged.annexIII = systemContext.annexIII;
174
+ }
175
+ if (systemContext.deployer && !merged.actor) {
176
+ merged.actor = { deployer: systemContext.deployer };
177
+ }
178
+ return logEvent(merged);
179
+ };
180
+ }
181
+
182
+ module.exports = {
183
+ buildEvent: buildEvent,
184
+ emit: emit,
185
+ logEvent: logEvent,
186
+ retentionFloorMs: retentionFloorMs,
187
+ loggerFor: loggerFor,
188
+ RETENTION_FLOORS: RETENTION_FLOORS,
189
+ MIN_BIOMETRIC_FIELDS: MIN_BIOMETRIC_FIELDS,
190
+ };
@@ -0,0 +1,205 @@
1
+ "use strict";
2
+ /**
3
+ * EU AI Act Article 5 — prohibited AI practices.
4
+ *
5
+ * Per Regulation (EU) 2024/1689 Art. 5, certain AI practices are
6
+ * unconditionally prohibited in the EU market. The practices fall
7
+ * into eight categories listed below; each entry carries:
8
+ *
9
+ * - id — short canonical identifier (used by classify())
10
+ * - article — the sub-article of Art. 5 (a-h)
11
+ * - title — operator-readable title
12
+ * - description — paraphrase of the prohibited practice
13
+ * - examples — non-exhaustive list of system shapes that fall in
14
+ *
15
+ * The catalog is operator-readable but NOT operator-extensible — Art. 5
16
+ * is set by EU regulation; operators don't add private "prohibited
17
+ * practices". For private "we don't allow this" rules see
18
+ * b.compliance.aiAct.local-policy (separate primitive).
19
+ *
20
+ * Effective date: 2026-02-02 per Art. 113(a). Operators with EU users
21
+ * MUST classify each AI system against this catalog before deployment.
22
+ */
23
+
24
+ var PROHIBITED_PRACTICES = Object.freeze([
25
+ Object.freeze({
26
+ id: "subliminal-manipulation",
27
+ article: "Art. 5(1)(a)",
28
+ title: "Subliminal techniques beyond a person's consciousness",
29
+ description: "AI systems that deploy subliminal, purposefully manipulative, or deceptive techniques with the objective or effect of materially distorting a person's behaviour by appreciably impairing their ability to make an informed decision, thereby causing or likely to cause significant harm.",
30
+ examples: Object.freeze([
31
+ "Hidden audio cues in marketing audio that bypass conscious perception",
32
+ "UX patterns engineered with eye-tracking to push specific decisions",
33
+ "Generative content that subliminally embeds product imagery",
34
+ ]),
35
+ effectiveDate: "2026-02-02",
36
+ }),
37
+ Object.freeze({
38
+ id: "exploit-vulnerabilities",
39
+ article: "Art. 5(1)(b)",
40
+ title: "Exploiting vulnerabilities of specific groups",
41
+ description: "AI systems that exploit vulnerabilities of a specific group of persons (due to age, disability, or specific social or economic situation) with the objective or effect of materially distorting their behaviour, thereby causing or likely to cause significant harm.",
42
+ examples: Object.freeze([
43
+ "Recommender systems targeting children with addictive content patterns",
44
+ "Predatory lending offers tuned to financial-distress signals",
45
+ "Manipulative chatbots aimed at elderly users with dementia signals",
46
+ ]),
47
+ effectiveDate: "2026-02-02",
48
+ }),
49
+ Object.freeze({
50
+ id: "social-scoring",
51
+ article: "Art. 5(1)(c)",
52
+ title: "Social scoring by public authorities",
53
+ description: "AI systems for the evaluation or classification of natural persons over a certain period of time based on their social behaviour or known, inferred or predicted personal or personality characteristics, leading to detrimental or unfavourable treatment that is unjustified or disproportionate.",
54
+ examples: Object.freeze([
55
+ "General-purpose social-credit ranking by a state agency",
56
+ "Cross-context behavior scoring used to deny unrelated services",
57
+ ]),
58
+ effectiveDate: "2026-02-02",
59
+ }),
60
+ Object.freeze({
61
+ id: "predictive-policing-individual",
62
+ article: "Art. 5(1)(d)",
63
+ title: "Predictive policing solely on profiling",
64
+ description: "AI systems for making risk assessments of natural persons in order to assess or predict the risk of a natural person committing a criminal offence, based solely on the profiling of a natural person or on assessing their personality traits and characteristics.",
65
+ examples: Object.freeze([
66
+ "Recidivism scoring using only demographic + arrest-history features",
67
+ "Heatmap-driven 'pre-crime' targeting of named individuals",
68
+ ]),
69
+ effectiveDate: "2026-02-02",
70
+ }),
71
+ Object.freeze({
72
+ id: "untargeted-facial-scraping",
73
+ article: "Art. 5(1)(e)",
74
+ title: "Untargeted scraping for facial-recognition databases",
75
+ description: "AI systems that create or expand facial-recognition databases through the untargeted scraping of facial images from the internet or CCTV footage.",
76
+ examples: Object.freeze([
77
+ "Bulk-collecting public profile photos to populate a face-search index",
78
+ "CCTV-derived enrolment of unconsenting bystanders",
79
+ ]),
80
+ effectiveDate: "2026-02-02",
81
+ }),
82
+ Object.freeze({
83
+ id: "emotion-inference-workplace-edu",
84
+ article: "Art. 5(1)(f)",
85
+ title: "Emotion inference in workplace and education",
86
+ description: "AI systems to infer emotions of a natural person in the areas of workplace and education institutions, except where the use of the AI system is intended to be put in place or into the market for medical or safety reasons.",
87
+ examples: Object.freeze([
88
+ "Camera-based 'engagement' scoring of students during remote class",
89
+ "Sentiment analysis of employee video calls for performance review",
90
+ ]),
91
+ effectiveDate: "2026-02-02",
92
+ }),
93
+ Object.freeze({
94
+ id: "biometric-categorisation-sensitive",
95
+ article: "Art. 5(1)(g)",
96
+ title: "Biometric categorisation by sensitive attributes",
97
+ description: "AI systems of biometric categorisation that categorise individually natural persons based on their biometric data to deduce or infer their race, political opinions, trade union membership, religious or philosophical beliefs, sex life or sexual orientation.",
98
+ examples: Object.freeze([
99
+ "Face-derived political-opinion or religion classifiers",
100
+ "Voice-derived sexual-orientation inference",
101
+ ]),
102
+ effectiveDate: "2026-02-02",
103
+ }),
104
+ Object.freeze({
105
+ id: "real-time-remote-biometric-id",
106
+ article: "Art. 5(1)(h)",
107
+ title: "Real-time remote biometric identification in public",
108
+ description: "AI systems for real-time remote biometric identification in publicly accessible spaces for law-enforcement purposes — except for narrowly-defined exceptions (search for missing persons, prevention of imminent threat, suspect of serious crime listed in Annex II).",
109
+ examples: Object.freeze([
110
+ "Mass facial-recognition gates at festivals or stations",
111
+ "Real-time crowd-scanning ID systems without imminent-threat trigger",
112
+ ]),
113
+ effectiveDate: "2026-02-02",
114
+ }),
115
+ ]);
116
+
117
+ function listPractices() {
118
+ return PROHIBITED_PRACTICES.slice();
119
+ }
120
+
121
+ function getPractice(id) {
122
+ for (var i = 0; i < PROHIBITED_PRACTICES.length; i += 1) {
123
+ if (PROHIBITED_PRACTICES[i].id === id) return PROHIBITED_PRACTICES[i];
124
+ }
125
+ return null;
126
+ }
127
+
128
+ function listIds() {
129
+ return PROHIBITED_PRACTICES.map(function (p) { return p.id; });
130
+ }
131
+
132
+ // Classify a system description against the catalog. Returns the array
133
+ // of practice IDs it appears to fall under, based on operator-supplied
134
+ // signals. The classifier is intentionally conservative — it errs on
135
+ // the side of flagging a system as potentially-prohibited; legal
136
+ // review is required before deployment regardless of the result.
137
+
138
+ function classify(systemDescription) {
139
+ if (!systemDescription || typeof systemDescription !== "object") {
140
+ return [];
141
+ }
142
+ var hits = [];
143
+ // (a) subliminal manipulation
144
+ if (systemDescription.usesSubliminalCues === true ||
145
+ systemDescription.intent === "subliminal-influence") {
146
+ hits.push("subliminal-manipulation");
147
+ }
148
+ // (b) exploit vulnerabilities
149
+ if (systemDescription.targetsVulnerableGroup === true) {
150
+ hits.push("exploit-vulnerabilities");
151
+ }
152
+ // (c) social scoring
153
+ if (systemDescription.purpose === "social-scoring" &&
154
+ systemDescription.deployerType === "public-authority") {
155
+ hits.push("social-scoring");
156
+ }
157
+ // (d) predictive policing on profiling alone
158
+ if (systemDescription.purpose === "predictive-policing" &&
159
+ systemDescription.usesProfileOnly === true) {
160
+ hits.push("predictive-policing-individual");
161
+ }
162
+ // (e) untargeted facial-recognition database build
163
+ if (systemDescription.builds === "facial-recognition-db" &&
164
+ systemDescription.scrapesUntargeted === true) {
165
+ hits.push("untargeted-facial-scraping");
166
+ }
167
+ // (f) emotion inference in workplace / education
168
+ if (systemDescription.infersEmotion === true &&
169
+ (systemDescription.deployContext === "workplace" ||
170
+ systemDescription.deployContext === "education")) {
171
+ if (systemDescription.purpose !== "medical" &&
172
+ systemDescription.purpose !== "safety") {
173
+ hits.push("emotion-inference-workplace-edu");
174
+ }
175
+ }
176
+ // (g) biometric categorisation of sensitive attributes
177
+ if (systemDescription.biometricCategorisation === true &&
178
+ Array.isArray(systemDescription.inferredAttributes)) {
179
+ var sensitive = ["race", "political-opinion", "trade-union",
180
+ "religion", "philosophy", "sex-life", "sexual-orientation"];
181
+ for (var i = 0; i < systemDescription.inferredAttributes.length; i += 1) {
182
+ if (sensitive.indexOf(systemDescription.inferredAttributes[i]) !== -1) {
183
+ hits.push("biometric-categorisation-sensitive");
184
+ break;
185
+ }
186
+ }
187
+ }
188
+ // (h) real-time remote biometric ID for law enforcement
189
+ if (systemDescription.remoteBiometricId === "real-time" &&
190
+ systemDescription.deployContext === "law-enforcement-public-space" &&
191
+ systemDescription.exemption !== "missing-person" &&
192
+ systemDescription.exemption !== "imminent-threat" &&
193
+ systemDescription.exemption !== "annex-ii-suspect") {
194
+ hits.push("real-time-remote-biometric-id");
195
+ }
196
+ return hits;
197
+ }
198
+
199
+ module.exports = {
200
+ PROHIBITED_PRACTICES: PROHIBITED_PRACTICES,
201
+ listPractices: listPractices,
202
+ listIds: listIds,
203
+ getPractice: getPractice,
204
+ classify: classify,
205
+ };
@@ -0,0 +1,189 @@
1
+ "use strict";
2
+ /**
3
+ * EU AI Act Article 6 + Annex III — high-risk AI system classification.
4
+ *
5
+ * Per Regulation (EU) 2024/1689 Art. 6, an AI system is "high-risk"
6
+ * when:
7
+ *
8
+ * (a) it is intended to be used as a safety component of a product
9
+ * covered by the Union harmonisation legislation listed in Annex I,
10
+ * OR the AI system is itself such a product, AND the product is
11
+ * required to undergo third-party conformity assessment; OR
12
+ *
13
+ * (b) it falls into one of the eight Annex III use-case categories.
14
+ *
15
+ * The classifier returns the tier and the matched Annex-III row(s) so
16
+ * operators can produce the Annex IV technical documentation required
17
+ * by Art. 11 + 18.
18
+ *
19
+ * Tiers:
20
+ * "prohibited" → falls under Article 5
21
+ * "high-risk" → falls under Article 6 / Annex III
22
+ * "limited-risk" → falls under Article 50 (transparency obligations only)
23
+ * "minimal-risk" → no specific obligations beyond general principles
24
+ * "general-purpose" → general-purpose AI model (GPAI) — Article 53
25
+ */
26
+
27
+ var ANNEX_III_USE_CASES = Object.freeze([
28
+ Object.freeze({
29
+ id: "biometric-id-categorisation",
30
+ annexRow: "Annex III §1",
31
+ title: "Biometric identification and categorisation",
32
+ description: "Remote biometric identification systems (excluding the prohibited real-time-public ones), biometric categorisation systems, emotion-recognition systems.",
33
+ obligations: Object.freeze(["risk-management-art-9", "data-governance-art-10",
34
+ "technical-documentation-art-11", "logging-art-12",
35
+ "transparency-art-13", "human-oversight-art-14",
36
+ "accuracy-robustness-art-15"]),
37
+ }),
38
+ Object.freeze({
39
+ id: "critical-infrastructure",
40
+ annexRow: "Annex III §2",
41
+ title: "Critical infrastructure",
42
+ description: "AI systems intended to be used as safety components in the management and operation of critical digital infrastructure, road traffic, or in the supply of water, gas, heating or electricity.",
43
+ obligations: Object.freeze(["risk-management-art-9", "data-governance-art-10",
44
+ "technical-documentation-art-11", "logging-art-12",
45
+ "transparency-art-13", "human-oversight-art-14",
46
+ "accuracy-robustness-art-15"]),
47
+ }),
48
+ Object.freeze({
49
+ id: "education-vocational",
50
+ annexRow: "Annex III §3",
51
+ title: "Education and vocational training",
52
+ description: "AI systems for determining access, admission or assignment to educational and vocational training institutions; for evaluating learning outcomes; for assessing the appropriate level of education; for monitoring and detecting prohibited behaviour during tests.",
53
+ obligations: Object.freeze(["risk-management-art-9", "data-governance-art-10",
54
+ "technical-documentation-art-11", "logging-art-12",
55
+ "transparency-art-13", "human-oversight-art-14",
56
+ "accuracy-robustness-art-15"]),
57
+ }),
58
+ Object.freeze({
59
+ id: "employment-workers-mgmt",
60
+ annexRow: "Annex III §4",
61
+ title: "Employment, workers management, and access to self-employment",
62
+ description: "AI systems for recruitment / selection, advertising vacancies, screening or filtering applications, evaluating candidates; for promotion / termination decisions, task allocation based on individual behaviour or personal traits, for monitoring and evaluating performance and behaviour.",
63
+ obligations: Object.freeze(["risk-management-art-9", "data-governance-art-10",
64
+ "technical-documentation-art-11", "logging-art-12",
65
+ "transparency-art-13", "human-oversight-art-14",
66
+ "accuracy-robustness-art-15"]),
67
+ }),
68
+ Object.freeze({
69
+ id: "essential-services",
70
+ annexRow: "Annex III §5",
71
+ title: "Access to essential private and public services",
72
+ description: "AI systems used by public authorities to evaluate eligibility for essential public assistance benefits and services; for credit-worthiness scoring of natural persons; for risk assessment and pricing of life and health insurance; for emergency-response triage.",
73
+ obligations: Object.freeze(["risk-management-art-9", "data-governance-art-10",
74
+ "technical-documentation-art-11", "logging-art-12",
75
+ "transparency-art-13", "human-oversight-art-14",
76
+ "accuracy-robustness-art-15"]),
77
+ }),
78
+ Object.freeze({
79
+ id: "law-enforcement",
80
+ annexRow: "Annex III §6",
81
+ title: "Law enforcement",
82
+ description: "AI systems used by law enforcement authorities for risk assessment of natural persons (excluding the prohibited profiling-only ones), polygraphs / similar tools, evaluating reliability of evidence, profiling for detection / investigation of criminal offences.",
83
+ obligations: Object.freeze(["risk-management-art-9", "data-governance-art-10",
84
+ "technical-documentation-art-11", "logging-art-12",
85
+ "transparency-art-13", "human-oversight-art-14",
86
+ "accuracy-robustness-art-15", "fundamental-rights-impact-assessment-art-27"]),
87
+ }),
88
+ Object.freeze({
89
+ id: "migration-asylum-border",
90
+ annexRow: "Annex III §7",
91
+ title: "Migration, asylum and border control management",
92
+ description: "AI systems used by competent public authorities for assessing risks (security, irregular migration, health) posed by a natural person intending to enter / having entered the EU; for examining applications for asylum / visa / residence permits; for detecting / recognising / identifying natural persons in the context of migration.",
93
+ obligations: Object.freeze(["risk-management-art-9", "data-governance-art-10",
94
+ "technical-documentation-art-11", "logging-art-12",
95
+ "transparency-art-13", "human-oversight-art-14",
96
+ "accuracy-robustness-art-15", "fundamental-rights-impact-assessment-art-27"]),
97
+ }),
98
+ Object.freeze({
99
+ id: "judicial-democratic-process",
100
+ annexRow: "Annex III §8",
101
+ title: "Administration of justice and democratic processes",
102
+ description: "AI systems intended to assist judicial authorities in researching / interpreting facts and the law and applying the law; AI systems used to influence the outcome of an election or referendum or voting behaviour.",
103
+ obligations: Object.freeze(["risk-management-art-9", "data-governance-art-10",
104
+ "technical-documentation-art-11", "logging-art-12",
105
+ "transparency-art-13", "human-oversight-art-14",
106
+ "accuracy-robustness-art-15"]),
107
+ }),
108
+ ]);
109
+
110
+ function listAnnexIII() {
111
+ return ANNEX_III_USE_CASES.slice();
112
+ }
113
+
114
+ function getAnnexIII(id) {
115
+ for (var i = 0; i < ANNEX_III_USE_CASES.length; i += 1) {
116
+ if (ANNEX_III_USE_CASES[i].id === id) return ANNEX_III_USE_CASES[i];
117
+ }
118
+ return null;
119
+ }
120
+
121
+ // Operator-side helper to determine the matching Annex III row from a
122
+ // purpose vocabulary the framework recognises.
123
+ function classifyAnnexIII(purpose) {
124
+ if (typeof purpose !== "string") return [];
125
+ var hits = [];
126
+ // Per-row heuristics — operators with a private vocabulary use
127
+ // .isHighRisk and pass annexId directly.
128
+ if (purpose === "biometric-id" || purpose === "biometric-category" ||
129
+ purpose === "emotion-recognition") {
130
+ hits.push("biometric-id-categorisation");
131
+ }
132
+ if (purpose === "critical-infrastructure-control" ||
133
+ purpose === "traffic-control" || purpose === "energy-grid") {
134
+ hits.push("critical-infrastructure");
135
+ }
136
+ if (purpose === "school-admissions" || purpose === "exam-grading" ||
137
+ purpose === "exam-proctoring") {
138
+ hits.push("education-vocational");
139
+ }
140
+ if (purpose === "candidate-screening" || purpose === "performance-evaluation" ||
141
+ purpose === "promotion-decision" || purpose === "termination-decision") {
142
+ hits.push("employment-workers-mgmt");
143
+ }
144
+ if (purpose === "credit-scoring" || purpose === "insurance-pricing" ||
145
+ purpose === "benefits-eligibility" || purpose === "emergency-triage") {
146
+ hits.push("essential-services");
147
+ }
148
+ if (purpose === "evidence-evaluation" || purpose === "law-enforcement-risk-assessment" ||
149
+ purpose === "polygraph") {
150
+ hits.push("law-enforcement");
151
+ }
152
+ if (purpose === "asylum-application-screening" || purpose === "visa-screening" ||
153
+ purpose === "border-biometric-id") {
154
+ hits.push("migration-asylum-border");
155
+ }
156
+ if (purpose === "judicial-research-assistant" || purpose === "election-influence") {
157
+ hits.push("judicial-democratic-process");
158
+ }
159
+ return hits;
160
+ }
161
+
162
+ function isHighRisk(opts) {
163
+ if (!opts || typeof opts !== "object") return false;
164
+ if (opts.tier === "high-risk") return true;
165
+ if (opts.purpose) {
166
+ var matches = classifyAnnexIII(opts.purpose);
167
+ if (matches.length > 0) return true;
168
+ }
169
+ if (opts.safetyComponentForRegulatedProduct === true &&
170
+ opts.requiresThirdPartyConformity === true) {
171
+ return true;
172
+ }
173
+ return false;
174
+ }
175
+
176
+ function obligationsFor(annexId) {
177
+ var row = getAnnexIII(annexId);
178
+ if (!row) return [];
179
+ return row.obligations.slice();
180
+ }
181
+
182
+ module.exports = {
183
+ ANNEX_III_USE_CASES: ANNEX_III_USE_CASES,
184
+ listAnnexIII: listAnnexIII,
185
+ getAnnexIII: getAnnexIII,
186
+ classifyAnnexIII: classifyAnnexIII,
187
+ isHighRisk: isHighRisk,
188
+ obligationsFor: obligationsFor,
189
+ };