@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.
- package/CHANGELOG.md +41 -1
- package/NOTICE +17 -1
- package/README.md +4 -3
- package/index.js +15 -0
- package/lib/asyncapi-bindings.js +160 -0
- package/lib/asyncapi-traits.js +143 -0
- package/lib/asyncapi.js +531 -0
- package/lib/audit-sign.js +1 -1
- package/lib/audit.js +68 -2
- package/lib/auth/acr-vocabulary.js +265 -0
- package/lib/auth/auth-time-tracker.js +111 -0
- package/lib/auth/elevation-grant.js +306 -0
- package/lib/auth/jwt.js +13 -0
- package/lib/auth/lockout.js +16 -3
- package/lib/auth/oauth.js +15 -1
- package/lib/auth/password.js +22 -2
- package/lib/auth/sd-jwt-vc-issuer.js +2 -2
- package/lib/auth/sd-jwt-vc.js +7 -2
- package/lib/auth/step-up-policy.js +335 -0
- package/lib/auth/step-up.js +445 -0
- package/lib/break-glass.js +53 -14
- package/lib/cache-redis.js +1 -1
- package/lib/cache.js +6 -1
- package/lib/cli.js +3 -3
- package/lib/cluster.js +24 -1
- package/lib/compliance-ai-act-logging.js +190 -0
- package/lib/compliance-ai-act-prohibited.js +205 -0
- package/lib/compliance-ai-act-risk.js +189 -0
- package/lib/compliance-ai-act-transparency.js +200 -0
- package/lib/compliance-ai-act.js +558 -0
- package/lib/compliance.js +12 -2
- package/lib/config-drift.js +2 -2
- package/lib/crypto-field.js +21 -1
- package/lib/crypto.js +114 -1
- package/lib/db.js +35 -4
- package/lib/dev.js +30 -3
- package/lib/dual-control.js +19 -1
- package/lib/external-db.js +10 -0
- package/lib/file-upload.js +30 -3
- package/lib/flag-cache.js +136 -0
- package/lib/flag-evaluation-context.js +135 -0
- package/lib/flag-providers.js +279 -0
- package/lib/flag-targeting.js +210 -0
- package/lib/flag.js +284 -0
- package/lib/guard-all.js +33 -16
- package/lib/guard-csv.js +16 -2
- package/lib/guard-html.js +35 -0
- package/lib/guard-svg.js +20 -0
- package/lib/http-client.js +57 -11
- package/lib/inbox.js +391 -0
- package/lib/log-stream-syslog.js +8 -0
- package/lib/log-stream.js +1 -1
- package/lib/mail-arc-sign.js +372 -0
- package/lib/mail-auth.js +2 -0
- package/lib/mail.js +40 -0
- package/lib/middleware/ai-act-disclosure.js +166 -0
- package/lib/middleware/asyncapi-serve.js +136 -0
- package/lib/middleware/attach-user.js +25 -2
- package/lib/middleware/bearer-auth.js +71 -6
- package/lib/middleware/body-parser.js +13 -0
- package/lib/middleware/cors.js +10 -0
- package/lib/middleware/csrf-protect.js +34 -3
- package/lib/middleware/dpop.js +3 -3
- package/lib/middleware/flag-context.js +76 -0
- package/lib/middleware/host-allowlist.js +1 -1
- package/lib/middleware/index.js +15 -0
- package/lib/middleware/openapi-serve.js +143 -0
- package/lib/middleware/require-aal.js +2 -2
- package/lib/middleware/require-step-up.js +186 -0
- package/lib/middleware/trace-propagate.js +1 -1
- package/lib/mtls-ca.js +23 -29
- package/lib/mtls-engine-default.js +21 -1
- package/lib/network-tls.js +21 -6
- package/lib/object-store/sigv4-bucket-ops.js +41 -0
- package/lib/observability-otlp-exporter.js +35 -2
- package/lib/openapi-paths-builder.js +248 -0
- package/lib/openapi-schema-walk.js +192 -0
- package/lib/openapi-security.js +169 -0
- package/lib/openapi-yaml.js +154 -0
- package/lib/openapi.js +443 -0
- package/lib/outbox.js +3 -3
- package/lib/permissions.js +10 -1
- package/lib/pqc-agent.js +22 -1
- package/lib/pqc-software.js +195 -0
- package/lib/pubsub.js +8 -4
- package/lib/redact.js +26 -1
- package/lib/retention.js +26 -0
- package/lib/router.js +1 -0
- package/lib/scheduler.js +57 -1
- package/lib/session.js +3 -3
- package/lib/ssrf-guard.js +19 -4
- package/lib/static.js +12 -0
- package/lib/totp.js +16 -0
- package/lib/vault/index.js +3 -0
- package/lib/vault-aad.js +259 -0
- package/lib/vendor/MANIFEST.json +29 -0
- package/lib/vendor/noble-post-quantum.cjs +18 -0
- package/lib/ws-client.js +978 -0
- package/package.json +1 -1
- 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
|
+
};
|