@euconform/core 1.0.1 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +12 -0
- package/dist/evidence/bundle.d.ts +23 -0
- package/dist/evidence/bundle.d.ts.map +1 -0
- package/dist/evidence/bundle.js +44 -0
- package/dist/evidence/compliance-evaluation.d.ts +12 -0
- package/dist/evidence/compliance-evaluation.d.ts.map +1 -0
- package/dist/evidence/compliance-evaluation.js +78 -0
- package/dist/evidence/hash.d.ts +2 -0
- package/dist/evidence/hash.d.ts.map +1 -0
- package/dist/evidence/hash.js +4 -0
- package/dist/evidence/index.d.ts +13 -0
- package/dist/evidence/index.d.ts.map +1 -0
- package/dist/evidence/index.js +11 -0
- package/dist/evidence/markdown.d.ts +9 -0
- package/dist/evidence/markdown.d.ts.map +1 -0
- package/dist/evidence/markdown.js +169 -0
- package/dist/evidence/output.d.ts +13 -0
- package/dist/evidence/output.d.ts.map +1 -0
- package/dist/evidence/output.js +547 -0
- package/dist/evidence/types.d.ts +230 -0
- package/dist/evidence/types.d.ts.map +1 -0
- package/dist/evidence/types.js +7 -0
- package/dist/evidence/validate.d.ts +8 -0
- package/dist/evidence/validate.d.ts.map +1 -0
- package/dist/evidence/validate.js +175 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/inference/ollama-client.d.ts +3 -0
- package/dist/inference/ollama-client.d.ts.map +1 -1
- package/dist/inference/ollama-client.js +69 -71
- package/dist/legal-checks/annex-iv-report.d.ts +3 -0
- package/dist/legal-checks/annex-iv-report.d.ts.map +1 -1
- package/dist/legal-checks/annex-iv-report.js +1 -0
- package/dist/legal-checks/deadline-timeline.d.ts +57 -0
- package/dist/legal-checks/deadline-timeline.d.ts.map +1 -0
- package/dist/legal-checks/deadline-timeline.js +265 -0
- package/dist/legal-checks/gap-analysis.d.ts +48 -0
- package/dist/legal-checks/gap-analysis.d.ts.map +1 -0
- package/dist/legal-checks/gap-analysis.js +397 -0
- package/dist/legal-checks/gpai-classifier.d.ts +49 -0
- package/dist/legal-checks/gpai-classifier.d.ts.map +1 -0
- package/dist/legal-checks/gpai-classifier.js +197 -0
- package/dist/legal-checks/index.d.ts +6 -0
- package/dist/legal-checks/index.d.ts.map +1 -1
- package/dist/legal-checks/index.js +3 -0
- package/dist/scanner/aggregator.d.ts +7 -0
- package/dist/scanner/aggregator.d.ts.map +1 -0
- package/dist/scanner/aggregator.js +200 -0
- package/dist/scanner/detectors/ai-framework-detector.d.ts +3 -0
- package/dist/scanner/detectors/ai-framework-detector.d.ts.map +1 -0
- package/dist/scanner/detectors/ai-framework-detector.js +74 -0
- package/dist/scanner/detectors/ai-provider-detector.d.ts +3 -0
- package/dist/scanner/detectors/ai-provider-detector.d.ts.map +1 -0
- package/dist/scanner/detectors/ai-provider-detector.js +100 -0
- package/dist/scanner/detectors/compliance-detector.d.ts +3 -0
- package/dist/scanner/detectors/compliance-detector.d.ts.map +1 -0
- package/dist/scanner/detectors/compliance-detector.js +201 -0
- package/dist/scanner/detectors/config-detector.d.ts +3 -0
- package/dist/scanner/detectors/config-detector.d.ts.map +1 -0
- package/dist/scanner/detectors/config-detector.js +248 -0
- package/dist/scanner/detectors/framework-detector.d.ts +3 -0
- package/dist/scanner/detectors/framework-detector.d.ts.map +1 -0
- package/dist/scanner/detectors/framework-detector.js +141 -0
- package/dist/scanner/detectors/index.d.ts +8 -0
- package/dist/scanner/detectors/index.d.ts.map +1 -0
- package/dist/scanner/detectors/index.js +37 -0
- package/dist/scanner/detectors/local-inference-detector.d.ts +3 -0
- package/dist/scanner/detectors/local-inference-detector.d.ts.map +1 -0
- package/dist/scanner/detectors/local-inference-detector.js +93 -0
- package/dist/scanner/detectors/markdown-detector.d.ts +3 -0
- package/dist/scanner/detectors/markdown-detector.d.ts.map +1 -0
- package/dist/scanner/detectors/markdown-detector.js +139 -0
- package/dist/scanner/detectors/package-json-detector.d.ts +3 -0
- package/dist/scanner/detectors/package-json-detector.d.ts.map +1 -0
- package/dist/scanner/detectors/package-json-detector.js +166 -0
- package/dist/scanner/detectors/rag-detector.d.ts +3 -0
- package/dist/scanner/detectors/rag-detector.d.ts.map +1 -0
- package/dist/scanner/detectors/rag-detector.js +132 -0
- package/dist/scanner/detectors/shared.d.ts +18 -0
- package/dist/scanner/detectors/shared.d.ts.map +1 -0
- package/dist/scanner/detectors/shared.js +36 -0
- package/dist/scanner/discovery.d.ts +14 -0
- package/dist/scanner/discovery.d.ts.map +1 -0
- package/dist/scanner/discovery.js +235 -0
- package/dist/scanner/index.d.ts +9 -0
- package/dist/scanner/index.d.ts.map +1 -0
- package/dist/scanner/index.js +8 -0
- package/package.json +6 -3
- package/src/evidence/bundle.ts +65 -0
- package/src/evidence/compliance-evaluation.ts +114 -0
- package/src/evidence/hash.ts +5 -0
- package/src/evidence/index.ts +51 -0
- package/src/evidence/markdown.ts +226 -0
- package/src/evidence/output.ts +717 -0
- package/src/evidence/types.ts +329 -0
- package/src/evidence/validate.ts +270 -0
- package/src/index.ts +25 -0
- package/src/inference/ollama-client.ts +93 -86
- package/src/legal-checks/annex-iv-report.ts +4 -0
- package/src/legal-checks/deadline-timeline.ts +313 -0
- package/src/legal-checks/gap-analysis.ts +454 -0
- package/src/legal-checks/gpai-classifier.ts +281 -0
- package/src/legal-checks/index.ts +33 -0
- package/src/scanner/aggregator.ts +258 -0
- package/src/scanner/detectors/ai-framework-detector.ts +94 -0
- package/src/scanner/detectors/ai-provider-detector.ts +135 -0
- package/src/scanner/detectors/compliance-detector.ts +232 -0
- package/src/scanner/detectors/config-detector.ts +316 -0
- package/src/scanner/detectors/framework-detector.ts +163 -0
- package/src/scanner/detectors/index.ts +40 -0
- package/src/scanner/detectors/local-inference-detector.ts +124 -0
- package/src/scanner/detectors/markdown-detector.ts +176 -0
- package/src/scanner/detectors/package-json-detector.ts +202 -0
- package/src/scanner/detectors/rag-detector.ts +162 -0
- package/src/scanner/detectors/shared.ts +45 -0
- package/src/scanner/discovery.ts +290 -0
- package/src/scanner/index.ts +9 -0
- package/tests/evidence/bundle.test.ts +128 -0
- package/tests/evidence/ecef-schemas.test.ts +316 -0
- package/tests/evidence/hash.test.ts +25 -0
- package/tests/evidence/markdown.test.ts +92 -0
- package/tests/evidence/output.test.ts +203 -0
- package/tests/fixtures/compliance-good/README.md +14 -0
- package/tests/fixtures/compliance-good/lib/ai-service.ts +17 -0
- package/tests/fixtures/compliance-good/lib/review-flow.ts +11 -0
- package/tests/fixtures/compliance-good/package.json +10 -0
- package/tests/fixtures/compliance-good/tests/bias-check.test.ts +13 -0
- package/tests/fixtures/nextjs-openai/README.md +5 -0
- package/tests/fixtures/nextjs-openai/app/page.tsx +11 -0
- package/tests/fixtures/nextjs-openai/package.json +9 -0
- package/tests/fixtures/ollama-rag/.env.example +2 -0
- package/tests/fixtures/ollama-rag/docker-compose.yml +9 -0
- package/tests/fixtures/ollama-rag/package.json +9 -0
- package/tests/fixtures/ollama-rag/server.ts +21 -0
- package/tests/fixtures/plain-webapp/README.md +3 -0
- package/tests/fixtures/plain-webapp/index.js +5 -0
- package/tests/fixtures/plain-webapp/package.json +8 -0
- package/tests/gpai-classifier.test.ts +125 -0
- package/tests/gpai-timeline.test.ts +55 -0
- package/tests/scanner/aggregator.test.ts +122 -0
- package/tests/scanner/detectors.test.ts +274 -0
- package/tests/scanner/discovery.test.ts +87 -0
- package/vitest.config.ts +1 -0
- package/.turbo/turbo-check-types.log +0 -4
- package/.turbo/turbo-test.log +0 -364
- package/coverage/base.css +0 -224
- package/coverage/block-navigation.js +0 -87
- package/coverage/clover.xml +0 -3282
- package/coverage/coverage-final.json +0 -20
- package/coverage/favicon.png +0 -0
- package/coverage/index.html +0 -161
- package/coverage/prettify.css +0 -1
- package/coverage/prettify.js +0 -2
- package/coverage/sort-arrow-sprite.png +0 -0
- package/coverage/sorter.js +0 -210
- package/coverage/src/fairness-metrics.ts.html +0 -1579
- package/coverage/src/index.html +0 -161
- package/coverage/src/index.ts.html +0 -535
- package/coverage/src/inference/browser-inference.ts.html +0 -859
- package/coverage/src/inference/capability-detection.ts.html +0 -2227
- package/coverage/src/inference/index.html +0 -176
- package/coverage/src/inference/index.ts.html +0 -553
- package/coverage/src/inference/inference-factory.ts.html +0 -751
- package/coverage/src/inference/ollama-client.ts.html +0 -1627
- package/coverage/src/legal-checks/annex-iv-report.ts.html +0 -556
- package/coverage/src/legal-checks/bias-metrics.ts.html +0 -1855
- package/coverage/src/legal-checks/data-governance.ts.html +0 -529
- package/coverage/src/legal-checks/human-oversight-logging.ts.html +0 -490
- package/coverage/src/legal-checks/index.html +0 -206
- package/coverage/src/legal-checks/index.ts.html +0 -250
- package/coverage/src/legal-checks/risk-classifier.ts.html +0 -940
- package/coverage/src/legal-checks/robustness-transparency.ts.html +0 -571
- package/coverage/src/metrics/crows-pairs-bias.ts.html +0 -820
- package/coverage/src/metrics/index.html +0 -146
- package/coverage/src/metrics/index.ts.html +0 -151
- package/coverage/src/metrics/stereotype-bias.ts.html +0 -886
- package/coverage/src/risk-engine.ts.html +0 -1660
- package/coverage/src/types.ts.html +0 -547
- package/dist/legal-checks/disclaimer.d.ts +0 -2
- package/dist/legal-checks/disclaimer.d.ts.map +0 -1
- package/dist/legal-checks/disclaimer.js +0 -1
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* EuConform Evidence Engine — Type Definitions
|
|
3
|
+
*
|
|
4
|
+
* The scanner produces evidence, hints, and open questions — not legal verdicts.
|
|
5
|
+
* Classification is done by the wizard from human input, not by the scanner.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
// ---------------------------------------------------------------------------
|
|
9
|
+
// Signal types
|
|
10
|
+
// ---------------------------------------------------------------------------
|
|
11
|
+
|
|
12
|
+
export type SignalCategory =
|
|
13
|
+
| "framework"
|
|
14
|
+
| "runtime"
|
|
15
|
+
| "ai-provider"
|
|
16
|
+
| "ai-framework"
|
|
17
|
+
| "ai-model"
|
|
18
|
+
| "local-inference"
|
|
19
|
+
| "rag"
|
|
20
|
+
| "training"
|
|
21
|
+
| "compliance-disclosure"
|
|
22
|
+
| "compliance-logging"
|
|
23
|
+
| "compliance-oversight"
|
|
24
|
+
| "compliance-bias"
|
|
25
|
+
| "compliance-data"
|
|
26
|
+
| "compliance-incident"
|
|
27
|
+
| "compliance-reporting";
|
|
28
|
+
|
|
29
|
+
export type ConfidenceLevel = "high" | "medium" | "low";
|
|
30
|
+
|
|
31
|
+
export type ScanScope = "production" | "all";
|
|
32
|
+
|
|
33
|
+
export type FileProvenance =
|
|
34
|
+
| "runtime"
|
|
35
|
+
| "config"
|
|
36
|
+
| "root-docs"
|
|
37
|
+
| "docs"
|
|
38
|
+
| "test"
|
|
39
|
+
| "fixture"
|
|
40
|
+
| "example"
|
|
41
|
+
| "generated"
|
|
42
|
+
| "tooling";
|
|
43
|
+
|
|
44
|
+
export interface SignalEvidence {
|
|
45
|
+
file: string;
|
|
46
|
+
line?: number;
|
|
47
|
+
snippet: string;
|
|
48
|
+
provenance?: FileProvenance;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export interface DetectedSignal {
|
|
52
|
+
id: string;
|
|
53
|
+
name: string;
|
|
54
|
+
category: SignalCategory;
|
|
55
|
+
confidence: ConfidenceLevel;
|
|
56
|
+
evidence: SignalEvidence[];
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// ---------------------------------------------------------------------------
|
|
60
|
+
// Open questions
|
|
61
|
+
// ---------------------------------------------------------------------------
|
|
62
|
+
|
|
63
|
+
export interface OpenQuestion {
|
|
64
|
+
id: string;
|
|
65
|
+
question: string;
|
|
66
|
+
context: string;
|
|
67
|
+
relatedSignalIds: string[];
|
|
68
|
+
suggestedAction: string;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// ---------------------------------------------------------------------------
|
|
72
|
+
// Scanner result (raw output of scanRepository)
|
|
73
|
+
// ---------------------------------------------------------------------------
|
|
74
|
+
|
|
75
|
+
export interface ScanMeta {
|
|
76
|
+
scannedAt: string;
|
|
77
|
+
scanDurationMs: number;
|
|
78
|
+
toolVersion: string;
|
|
79
|
+
targetPath: string;
|
|
80
|
+
scanScope: ScanScope;
|
|
81
|
+
filesScanned: number;
|
|
82
|
+
filesSkipped: number;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export interface RepoProfile {
|
|
86
|
+
name: string;
|
|
87
|
+
version?: string;
|
|
88
|
+
description?: string;
|
|
89
|
+
license?: string;
|
|
90
|
+
isMonorepo: boolean;
|
|
91
|
+
packageManager?: "npm" | "pnpm" | "yarn" | "bun";
|
|
92
|
+
languages: string[];
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export type AppType =
|
|
96
|
+
| "web-app"
|
|
97
|
+
| "api-server"
|
|
98
|
+
| "cli-tool"
|
|
99
|
+
| "library"
|
|
100
|
+
| "chatbot"
|
|
101
|
+
| "ml-pipeline"
|
|
102
|
+
| "unknown";
|
|
103
|
+
|
|
104
|
+
export interface ScanResult {
|
|
105
|
+
meta: ScanMeta;
|
|
106
|
+
repo: RepoProfile;
|
|
107
|
+
appTypes: AppType[];
|
|
108
|
+
signals: DetectedSignal[];
|
|
109
|
+
openQuestions: OpenQuestion[];
|
|
110
|
+
signalSummary: {
|
|
111
|
+
total: number;
|
|
112
|
+
byCategory: Partial<Record<SignalCategory, number>>;
|
|
113
|
+
byConfidence: Record<ConfidenceLevel, number>;
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// ---------------------------------------------------------------------------
|
|
118
|
+
// File discovery
|
|
119
|
+
// ---------------------------------------------------------------------------
|
|
120
|
+
|
|
121
|
+
export interface ScanOptions {
|
|
122
|
+
targetPath: string;
|
|
123
|
+
scope?: ScanScope;
|
|
124
|
+
extensions?: string[];
|
|
125
|
+
excludeGlobs?: string[];
|
|
126
|
+
maxFileSizeBytes?: number;
|
|
127
|
+
maxFiles?: number;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export interface ScanFile {
|
|
131
|
+
relativePath: string;
|
|
132
|
+
absolutePath: string;
|
|
133
|
+
extension: string;
|
|
134
|
+
content: string;
|
|
135
|
+
sizeBytes: number;
|
|
136
|
+
provenance: FileProvenance;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// ---------------------------------------------------------------------------
|
|
140
|
+
// Detector interface
|
|
141
|
+
// ---------------------------------------------------------------------------
|
|
142
|
+
|
|
143
|
+
export interface DetectorContext {
|
|
144
|
+
file: ScanFile;
|
|
145
|
+
/** Pre-split lines of file.content — avoids redundant .split("\n") in each detector */
|
|
146
|
+
lines: string[];
|
|
147
|
+
allFiles: ScanFile[];
|
|
148
|
+
/** Pre-parsed root package.json, if available */
|
|
149
|
+
rootPackageJson?: Record<string, unknown>;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export type SignalDetector = (ctx: DetectorContext) => DetectedSignal[];
|
|
153
|
+
|
|
154
|
+
// ---------------------------------------------------------------------------
|
|
155
|
+
// Scan report (euconform.report.v1)
|
|
156
|
+
// ---------------------------------------------------------------------------
|
|
157
|
+
|
|
158
|
+
export interface ComplianceSignalGroup {
|
|
159
|
+
status: "present" | "partial" | "absent" | "unknown";
|
|
160
|
+
confidence: ConfidenceLevel;
|
|
161
|
+
evidence: SignalEvidence[];
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
export interface AssessmentHint {
|
|
165
|
+
hint: string;
|
|
166
|
+
articleRef?: string;
|
|
167
|
+
confidence: ConfidenceLevel;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export interface ScanGap {
|
|
171
|
+
id: string;
|
|
172
|
+
title: string;
|
|
173
|
+
description: string;
|
|
174
|
+
priority: "critical" | "high" | "medium" | "low";
|
|
175
|
+
status: "missing" | "partial";
|
|
176
|
+
basis: "scanner-rule";
|
|
177
|
+
evidence: SignalEvidence[];
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
export interface ScanReport {
|
|
181
|
+
schemaVersion: "euconform.report.v1";
|
|
182
|
+
generatedAt: string;
|
|
183
|
+
tool: { name: string; version: string };
|
|
184
|
+
target: {
|
|
185
|
+
rootPath: string;
|
|
186
|
+
name: string;
|
|
187
|
+
repoType: string;
|
|
188
|
+
detectedStack: string[];
|
|
189
|
+
};
|
|
190
|
+
aiFootprint: {
|
|
191
|
+
usesAI: boolean;
|
|
192
|
+
inferenceModes: string[];
|
|
193
|
+
providerHints: string[];
|
|
194
|
+
ragHints: string[];
|
|
195
|
+
};
|
|
196
|
+
complianceSignals: {
|
|
197
|
+
disclosure: ComplianceSignalGroup;
|
|
198
|
+
biasTesting: ComplianceSignalGroup;
|
|
199
|
+
reportingExports: ComplianceSignalGroup;
|
|
200
|
+
loggingMonitoring: ComplianceSignalGroup;
|
|
201
|
+
humanOversight: ComplianceSignalGroup;
|
|
202
|
+
dataGovernance: ComplianceSignalGroup;
|
|
203
|
+
incidentReporting: ComplianceSignalGroup;
|
|
204
|
+
};
|
|
205
|
+
assessmentHints: {
|
|
206
|
+
possibleModes: string[];
|
|
207
|
+
riskIndicators: AssessmentHint[];
|
|
208
|
+
gpaiIndicators: AssessmentHint[];
|
|
209
|
+
openQuestions: string[];
|
|
210
|
+
};
|
|
211
|
+
gaps: ScanGap[];
|
|
212
|
+
recommendationSummary: string[];
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// ---------------------------------------------------------------------------
|
|
216
|
+
// AI Bill of Materials (euconform.aibom.v1)
|
|
217
|
+
// ---------------------------------------------------------------------------
|
|
218
|
+
|
|
219
|
+
export type BomComponentKind =
|
|
220
|
+
| "framework"
|
|
221
|
+
| "runtime"
|
|
222
|
+
| "inference-provider"
|
|
223
|
+
| "ai-framework"
|
|
224
|
+
| "model"
|
|
225
|
+
| "vector-store"
|
|
226
|
+
| "embedding"
|
|
227
|
+
| "dataset"
|
|
228
|
+
| "tool";
|
|
229
|
+
|
|
230
|
+
export interface BomComponent {
|
|
231
|
+
id: string;
|
|
232
|
+
kind: BomComponentKind;
|
|
233
|
+
name: string;
|
|
234
|
+
version?: string;
|
|
235
|
+
source: "package.json" | "lock-file" | "code" | "config" | "docs";
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
export interface AiBillOfMaterials {
|
|
239
|
+
schemaVersion: "euconform.aibom.v1";
|
|
240
|
+
generatedAt: string;
|
|
241
|
+
project: {
|
|
242
|
+
name: string;
|
|
243
|
+
rootPath: string;
|
|
244
|
+
};
|
|
245
|
+
components: BomComponent[];
|
|
246
|
+
complianceCapabilities: {
|
|
247
|
+
biasEvaluation: boolean;
|
|
248
|
+
jsonExport: boolean;
|
|
249
|
+
pdfExport: boolean;
|
|
250
|
+
loggingInfrastructure: boolean;
|
|
251
|
+
humanReviewFlow: boolean;
|
|
252
|
+
incidentHandling: boolean;
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// ---------------------------------------------------------------------------
|
|
257
|
+
// CI report (euconform.ci.v1)
|
|
258
|
+
// ---------------------------------------------------------------------------
|
|
259
|
+
|
|
260
|
+
export type FailOnLevel = "none" | "critical" | "high" | "medium" | "low";
|
|
261
|
+
|
|
262
|
+
export interface GapCounts {
|
|
263
|
+
critical: number;
|
|
264
|
+
high: number;
|
|
265
|
+
medium: number;
|
|
266
|
+
low: number;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
export interface CiReport {
|
|
270
|
+
schemaVersion: "euconform.ci.v1";
|
|
271
|
+
generatedAt: string;
|
|
272
|
+
target: {
|
|
273
|
+
name: string;
|
|
274
|
+
rootPath: string;
|
|
275
|
+
};
|
|
276
|
+
status: {
|
|
277
|
+
failOn: FailOnLevel;
|
|
278
|
+
failing: boolean;
|
|
279
|
+
gapCounts: GapCounts;
|
|
280
|
+
openQuestions: number;
|
|
281
|
+
};
|
|
282
|
+
aiDetected: boolean;
|
|
283
|
+
scanScope: ScanScope;
|
|
284
|
+
artifacts: string[];
|
|
285
|
+
complianceOverview: Array<{
|
|
286
|
+
area: string;
|
|
287
|
+
status: ComplianceSignalGroup["status"];
|
|
288
|
+
confidence: ComplianceSignalGroup["confidence"];
|
|
289
|
+
}>;
|
|
290
|
+
topGaps: Array<{
|
|
291
|
+
id: string;
|
|
292
|
+
title: string;
|
|
293
|
+
priority: ScanGap["priority"];
|
|
294
|
+
status: ScanGap["status"];
|
|
295
|
+
}>;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// ---------------------------------------------------------------------------
|
|
299
|
+
// Combined scan output
|
|
300
|
+
// ---------------------------------------------------------------------------
|
|
301
|
+
|
|
302
|
+
export interface ScanOutput {
|
|
303
|
+
report: ScanReport;
|
|
304
|
+
aibom: AiBillOfMaterials;
|
|
305
|
+
summaryMarkdown: string;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// ---------------------------------------------------------------------------
|
|
309
|
+
// Bundle manifest (euconform.bundle.v1)
|
|
310
|
+
// ---------------------------------------------------------------------------
|
|
311
|
+
|
|
312
|
+
export type BundleArtifactRole = "report" | "aibom" | "ci" | "summary";
|
|
313
|
+
|
|
314
|
+
export interface BundleArtifactRef {
|
|
315
|
+
role: BundleArtifactRole;
|
|
316
|
+
fileName: string;
|
|
317
|
+
sha256: string;
|
|
318
|
+
schemaVersion?: string;
|
|
319
|
+
mimeType?: string;
|
|
320
|
+
required: boolean;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
export interface ScanBundle {
|
|
324
|
+
schemaVersion: "euconform.bundle.v1";
|
|
325
|
+
generatedAt: string;
|
|
326
|
+
tool: { name: string; version: string };
|
|
327
|
+
target: { name: string; rootPath: string };
|
|
328
|
+
artifacts: BundleArtifactRef[];
|
|
329
|
+
}
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
import type { AiBillOfMaterials, CiReport, ScanBundle, ScanReport } from "./types";
|
|
2
|
+
|
|
3
|
+
function assertObject(data: unknown, label: string): Record<string, unknown> {
|
|
4
|
+
if (!data || typeof data !== "object" || Array.isArray(data)) {
|
|
5
|
+
throw new Error(`Invalid ${label}: expected a JSON object`);
|
|
6
|
+
}
|
|
7
|
+
return data as Record<string, unknown>;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function requireSchemaVersion(obj: Record<string, unknown>, expected: string, label: string): void {
|
|
11
|
+
if (obj.schemaVersion !== expected) {
|
|
12
|
+
throw new Error(
|
|
13
|
+
`Invalid ${label} schema version: expected "${expected}", got "${String(obj.schemaVersion)}"`
|
|
14
|
+
);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function requireField(
|
|
19
|
+
obj: Record<string, unknown>,
|
|
20
|
+
key: string,
|
|
21
|
+
type: "string" | "boolean" | "object" | "array" | "number",
|
|
22
|
+
label: string,
|
|
23
|
+
fieldName = key
|
|
24
|
+
): unknown {
|
|
25
|
+
if (!(key in obj)) {
|
|
26
|
+
throw new Error(`Invalid ${label}: missing '${fieldName}'`);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const value = obj[key];
|
|
30
|
+
const isValid =
|
|
31
|
+
(type === "array" && Array.isArray(value)) ||
|
|
32
|
+
(type === "object" && value && typeof value === "object" && !Array.isArray(value)) ||
|
|
33
|
+
(type === "string" && typeof value === "string") ||
|
|
34
|
+
(type === "boolean" && typeof value === "boolean") ||
|
|
35
|
+
(type === "number" && typeof value === "number");
|
|
36
|
+
|
|
37
|
+
if (!isValid) {
|
|
38
|
+
throw new Error(`Invalid ${label}: '${fieldName}' must be a ${type}`);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return value;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function requireStringArray(
|
|
45
|
+
obj: Record<string, unknown>,
|
|
46
|
+
key: string,
|
|
47
|
+
label: string,
|
|
48
|
+
fieldName = key
|
|
49
|
+
): string[] {
|
|
50
|
+
const value = requireField(obj, key, "array", label, fieldName) as unknown[];
|
|
51
|
+
if (!value.every((item) => typeof item === "string")) {
|
|
52
|
+
throw new Error(`Invalid ${label}: '${fieldName}' must contain strings`);
|
|
53
|
+
}
|
|
54
|
+
return value as string[];
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function majorVersion(schemaVersion: string): string | null {
|
|
58
|
+
const match = /\.v(\d+)$/.exec(schemaVersion);
|
|
59
|
+
return match?.[1] ?? null;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function getSchemaMajorVersion(schemaVersion: string): string | null {
|
|
63
|
+
return majorVersion(schemaVersion);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export function validateScanReport(data: unknown): ScanReport {
|
|
67
|
+
const obj = assertObject(data, "report");
|
|
68
|
+
requireSchemaVersion(obj, "euconform.report.v1", "report");
|
|
69
|
+
requireField(obj, "generatedAt", "string", "report");
|
|
70
|
+
|
|
71
|
+
const tool = requireField(obj, "tool", "object", "report") as Record<string, unknown>;
|
|
72
|
+
requireField(tool, "name", "string", "report", "tool.name");
|
|
73
|
+
requireField(tool, "version", "string", "report", "tool.version");
|
|
74
|
+
|
|
75
|
+
const target = requireField(obj, "target", "object", "report") as Record<string, unknown>;
|
|
76
|
+
requireField(target, "rootPath", "string", "report", "target.rootPath");
|
|
77
|
+
requireField(target, "name", "string", "report", "target.name");
|
|
78
|
+
requireField(target, "repoType", "string", "report", "target.repoType");
|
|
79
|
+
requireField(target, "detectedStack", "array", "report", "target.detectedStack");
|
|
80
|
+
|
|
81
|
+
const aiFootprint = requireField(obj, "aiFootprint", "object", "report") as Record<
|
|
82
|
+
string,
|
|
83
|
+
unknown
|
|
84
|
+
>;
|
|
85
|
+
requireField(aiFootprint, "usesAI", "boolean", "report", "aiFootprint.usesAI");
|
|
86
|
+
requireStringArray(aiFootprint, "inferenceModes", "report", "aiFootprint.inferenceModes");
|
|
87
|
+
requireStringArray(aiFootprint, "providerHints", "report", "aiFootprint.providerHints");
|
|
88
|
+
requireStringArray(aiFootprint, "ragHints", "report", "aiFootprint.ragHints");
|
|
89
|
+
|
|
90
|
+
requireField(obj, "complianceSignals", "object", "report");
|
|
91
|
+
const hints = requireField(obj, "assessmentHints", "object", "report") as Record<string, unknown>;
|
|
92
|
+
requireStringArray(hints, "possibleModes", "report", "assessmentHints.possibleModes");
|
|
93
|
+
requireField(hints, "riskIndicators", "array", "report", "assessmentHints.riskIndicators");
|
|
94
|
+
requireField(hints, "gpaiIndicators", "array", "report", "assessmentHints.gpaiIndicators");
|
|
95
|
+
requireStringArray(hints, "openQuestions", "report", "assessmentHints.openQuestions");
|
|
96
|
+
|
|
97
|
+
requireField(obj, "gaps", "array", "report");
|
|
98
|
+
requireStringArray(obj, "recommendationSummary", "report");
|
|
99
|
+
|
|
100
|
+
return data as ScanReport;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export function validateAiBillOfMaterials(data: unknown): AiBillOfMaterials {
|
|
104
|
+
const obj = assertObject(data, "AIBOM");
|
|
105
|
+
requireSchemaVersion(obj, "euconform.aibom.v1", "AIBOM");
|
|
106
|
+
requireField(obj, "generatedAt", "string", "AIBOM");
|
|
107
|
+
|
|
108
|
+
const project = requireField(obj, "project", "object", "AIBOM") as Record<string, unknown>;
|
|
109
|
+
requireField(project, "name", "string", "AIBOM", "project.name");
|
|
110
|
+
requireField(project, "rootPath", "string", "AIBOM", "project.rootPath");
|
|
111
|
+
|
|
112
|
+
requireField(obj, "components", "array", "AIBOM");
|
|
113
|
+
requireField(obj, "complianceCapabilities", "object", "AIBOM");
|
|
114
|
+
|
|
115
|
+
return data as AiBillOfMaterials;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export function validateCiReport(data: unknown): CiReport {
|
|
119
|
+
const obj = assertObject(data, "CI report");
|
|
120
|
+
requireSchemaVersion(obj, "euconform.ci.v1", "CI report");
|
|
121
|
+
requireField(obj, "generatedAt", "string", "CI report");
|
|
122
|
+
|
|
123
|
+
const target = requireField(obj, "target", "object", "CI report") as Record<string, unknown>;
|
|
124
|
+
requireField(target, "name", "string", "CI report", "target.name");
|
|
125
|
+
requireField(target, "rootPath", "string", "CI report", "target.rootPath");
|
|
126
|
+
|
|
127
|
+
const status = requireField(obj, "status", "object", "CI report") as Record<string, unknown>;
|
|
128
|
+
requireField(status, "failOn", "string", "CI report", "status.failOn");
|
|
129
|
+
requireField(status, "failing", "boolean", "CI report", "status.failing");
|
|
130
|
+
requireField(status, "openQuestions", "number", "CI report", "status.openQuestions");
|
|
131
|
+
|
|
132
|
+
const gapCounts = requireField(
|
|
133
|
+
status,
|
|
134
|
+
"gapCounts",
|
|
135
|
+
"object",
|
|
136
|
+
"CI report",
|
|
137
|
+
"status.gapCounts"
|
|
138
|
+
) as Record<string, unknown>;
|
|
139
|
+
for (const key of ["critical", "high", "medium", "low"] as const) {
|
|
140
|
+
requireField(gapCounts, key, "number", "CI report", `status.gapCounts.${key}`);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
requireField(obj, "aiDetected", "boolean", "CI report");
|
|
144
|
+
requireField(obj, "scanScope", "string", "CI report");
|
|
145
|
+
requireField(obj, "artifacts", "array", "CI report");
|
|
146
|
+
requireField(obj, "complianceOverview", "array", "CI report");
|
|
147
|
+
requireField(obj, "topGaps", "array", "CI report");
|
|
148
|
+
|
|
149
|
+
return data as CiReport;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const VALID_BUNDLE_ROLES = new Set(["report", "aibom", "ci", "summary"]);
|
|
153
|
+
const SHA256_HEX = /^[a-f0-9]{64}$/;
|
|
154
|
+
|
|
155
|
+
export function validateScanBundle(data: unknown): ScanBundle {
|
|
156
|
+
const obj = assertObject(data, "bundle");
|
|
157
|
+
requireSchemaVersion(obj, "euconform.bundle.v1", "bundle");
|
|
158
|
+
requireField(obj, "generatedAt", "string", "bundle");
|
|
159
|
+
|
|
160
|
+
const tool = requireField(obj, "tool", "object", "bundle") as Record<string, unknown>;
|
|
161
|
+
requireField(tool, "name", "string", "bundle", "tool.name");
|
|
162
|
+
requireField(tool, "version", "string", "bundle", "tool.version");
|
|
163
|
+
|
|
164
|
+
const target = requireField(obj, "target", "object", "bundle") as Record<string, unknown>;
|
|
165
|
+
requireField(target, "name", "string", "bundle", "target.name");
|
|
166
|
+
requireField(target, "rootPath", "string", "bundle", "target.rootPath");
|
|
167
|
+
|
|
168
|
+
const artifacts = requireField(obj, "artifacts", "array", "bundle") as unknown[];
|
|
169
|
+
if (artifacts.length === 0) {
|
|
170
|
+
throw new Error("Invalid bundle: 'artifacts' must contain at least one artifact");
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const seenRoles = new Set<string>();
|
|
174
|
+
const seenFiles = new Set<string>();
|
|
175
|
+
const bundleMajor = majorVersion("euconform.bundle.v1");
|
|
176
|
+
let hasRequiredReport = false;
|
|
177
|
+
|
|
178
|
+
for (let index = 0; index < artifacts.length; index++) {
|
|
179
|
+
const artifact = assertObject(artifacts[index], "bundle artifact");
|
|
180
|
+
const role = requireField(
|
|
181
|
+
artifact,
|
|
182
|
+
"role",
|
|
183
|
+
"string",
|
|
184
|
+
"bundle artifact",
|
|
185
|
+
`artifacts[${index}].role`
|
|
186
|
+
) as string;
|
|
187
|
+
const fileName = requireField(
|
|
188
|
+
artifact,
|
|
189
|
+
"fileName",
|
|
190
|
+
"string",
|
|
191
|
+
"bundle artifact",
|
|
192
|
+
`artifacts[${index}].fileName`
|
|
193
|
+
) as string;
|
|
194
|
+
const sha256 = requireField(
|
|
195
|
+
artifact,
|
|
196
|
+
"sha256",
|
|
197
|
+
"string",
|
|
198
|
+
"bundle artifact",
|
|
199
|
+
`artifacts[${index}].sha256`
|
|
200
|
+
) as string;
|
|
201
|
+
const required = requireField(
|
|
202
|
+
artifact,
|
|
203
|
+
"required",
|
|
204
|
+
"boolean",
|
|
205
|
+
"bundle artifact",
|
|
206
|
+
`artifacts[${index}].required`
|
|
207
|
+
) as boolean;
|
|
208
|
+
|
|
209
|
+
if (!VALID_BUNDLE_ROLES.has(role)) {
|
|
210
|
+
throw new Error(`Invalid bundle artifact: unsupported role '${role}'`);
|
|
211
|
+
}
|
|
212
|
+
if (!SHA256_HEX.test(sha256)) {
|
|
213
|
+
throw new Error(`Invalid bundle artifact: '${fileName}' has invalid sha256`);
|
|
214
|
+
}
|
|
215
|
+
if (seenRoles.has(role)) {
|
|
216
|
+
throw new Error(`Invalid bundle artifact: duplicate role '${role}'`);
|
|
217
|
+
}
|
|
218
|
+
if (seenFiles.has(fileName)) {
|
|
219
|
+
throw new Error(`Invalid bundle artifact: duplicate fileName '${fileName}'`);
|
|
220
|
+
}
|
|
221
|
+
seenRoles.add(role);
|
|
222
|
+
seenFiles.add(fileName);
|
|
223
|
+
|
|
224
|
+
const declaredSchemaVersion = artifact.schemaVersion;
|
|
225
|
+
if (declaredSchemaVersion !== undefined && typeof declaredSchemaVersion !== "string") {
|
|
226
|
+
throw new Error(`Invalid bundle artifact: '${fileName}' has non-string schemaVersion`);
|
|
227
|
+
}
|
|
228
|
+
if (artifact.mimeType !== undefined && typeof artifact.mimeType !== "string") {
|
|
229
|
+
throw new Error(`Invalid bundle artifact: '${fileName}' has non-string mimeType`);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
if (
|
|
233
|
+
declaredSchemaVersion &&
|
|
234
|
+
bundleMajor &&
|
|
235
|
+
majorVersion(declaredSchemaVersion) !== bundleMajor
|
|
236
|
+
) {
|
|
237
|
+
throw new Error(
|
|
238
|
+
`Invalid bundle artifact: '${fileName}' uses schemaVersion '${declaredSchemaVersion}' with a different major than the bundle`
|
|
239
|
+
);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
if (role === "report") {
|
|
243
|
+
hasRequiredReport = required;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
if (!hasRequiredReport) {
|
|
248
|
+
throw new Error("Invalid bundle: a required 'report' artifact is mandatory");
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
return data as ScanBundle;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
export function validateEcefJsonDocument(
|
|
255
|
+
schemaVersion: string,
|
|
256
|
+
data: unknown
|
|
257
|
+
): ScanReport | AiBillOfMaterials | CiReport | ScanBundle {
|
|
258
|
+
switch (schemaVersion) {
|
|
259
|
+
case "euconform.report.v1":
|
|
260
|
+
return validateScanReport(data);
|
|
261
|
+
case "euconform.aibom.v1":
|
|
262
|
+
return validateAiBillOfMaterials(data);
|
|
263
|
+
case "euconform.ci.v1":
|
|
264
|
+
return validateCiReport(data);
|
|
265
|
+
case "euconform.bundle.v1":
|
|
266
|
+
return validateScanBundle(data);
|
|
267
|
+
default:
|
|
268
|
+
throw new Error(`Unsupported ECEF schemaVersion '${schemaVersion}'`);
|
|
269
|
+
}
|
|
270
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -31,6 +31,12 @@ export {
|
|
|
31
31
|
classifyAnnexIIIRisk,
|
|
32
32
|
getAnnexIIIQuestions,
|
|
33
33
|
getProhibitedAIScreeningQuestions,
|
|
34
|
+
GPAI_QUESTIONS,
|
|
35
|
+
classifyGPAICompliance,
|
|
36
|
+
getGPAIQuestions,
|
|
37
|
+
isSystemicRiskModel,
|
|
38
|
+
generateAnnexIIIGapAnalysis,
|
|
39
|
+
generateGPAIGapAnalysis,
|
|
34
40
|
AI_ACT_SOURCES,
|
|
35
41
|
METRIC_SOURCES,
|
|
36
42
|
createPerspectiveApiToxicityScorer,
|
|
@@ -48,6 +54,14 @@ export {
|
|
|
48
54
|
runCrowsPairsLogProbTest,
|
|
49
55
|
runRegardTest,
|
|
50
56
|
runToxicityTest,
|
|
57
|
+
EU_AI_ACT_TIMELINE,
|
|
58
|
+
DIGITAL_OMNIBUS_PROPOSALS,
|
|
59
|
+
getDeadlineStatus,
|
|
60
|
+
getDaysUntilDeadline,
|
|
61
|
+
getAudiencesForRiskLevel,
|
|
62
|
+
getNextDeadline,
|
|
63
|
+
getTimelineForRiskLevel,
|
|
64
|
+
getTimelineForAudiences,
|
|
51
65
|
} from "./legal-checks";
|
|
52
66
|
export type {
|
|
53
67
|
AnnexIIIAnswer,
|
|
@@ -71,6 +85,17 @@ export type {
|
|
|
71
85
|
SensitiveAttribute,
|
|
72
86
|
ToxicityResult,
|
|
73
87
|
ToxicityScorer,
|
|
88
|
+
DeadlineStatus,
|
|
89
|
+
AffectedAudience,
|
|
90
|
+
DeadlineObligation,
|
|
91
|
+
DeadlinePhase,
|
|
92
|
+
GPAIObligationType,
|
|
93
|
+
GPAIComplianceLevel,
|
|
94
|
+
GPAIComplianceFlag,
|
|
95
|
+
GPAIComplianceResult,
|
|
96
|
+
GapPriority,
|
|
97
|
+
GapAction,
|
|
98
|
+
GapAnalysisResult,
|
|
74
99
|
} from "./legal-checks";
|
|
75
100
|
|
|
76
101
|
// Risk Engine
|