@attestly/compliance-core 0.1.0 → 0.1.1
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/README.md +1 -1
- package/dist/index.d.mts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +61 -2
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +61 -2
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -8,7 +8,7 @@ Built specifically to shift **EU AI Act Compliance**, **Model Version Locking**,
|
|
|
8
8
|
|
|
9
9
|
- **Zero-Dependency Core**: The SDK is incredibly lightweight and natively relies on Web Standard `Request` and `Response` objects. No heavy AST parsers or backend-bloat.
|
|
10
10
|
- **Zero-Latency (TTFB Protected)**: Validation runs entirely synchronously in milliseconds. In production, the wrapper acts as a near-zero-cost pass-through unless explicitly configured to intercept.
|
|
11
|
-
- **EU AI Act Shift-Left**: Catches Prohibited Practices (Article 5), auto-escalates High-Risk domains (like Law Enforcement
|
|
11
|
+
- **EU AI Act Shift-Left**: Catches Prohibited Practices (Article 5), auto-escalates Annex III High-Risk domains (like Law Enforcement, Biometrics, Healthcare, and Critical Infrastructure), and strictly enforces required metadata traceability according to EU regulations.
|
|
12
12
|
- **Vercel AI SDK First**: Out-of-the-box support for streaming UIs. It inspects the prompt, scrubs PII, and returns the native `DataStreamResponse` totally untouched so your Lighthouse scores remain pinned at 100.
|
|
13
13
|
|
|
14
14
|
## 📦 Installation
|
package/dist/index.d.mts
CHANGED
|
@@ -22,7 +22,7 @@ interface AiManifest {
|
|
|
22
22
|
/**
|
|
23
23
|
* Use case context domain under the EU AI Act.
|
|
24
24
|
*/
|
|
25
|
-
systemDomain: 'general' | 'law-enforcement' | 'education' | 'employment' | 'critical-infrastructure' | 'biometrics';
|
|
25
|
+
systemDomain: 'general' | 'law-enforcement' | 'education' | 'employment' | 'critical-infrastructure' | 'biometrics' | 'essential-services' | 'migration' | 'justice';
|
|
26
26
|
/**
|
|
27
27
|
* Flag for explicit prohibited practices under Article 5 of the EU AI Act.
|
|
28
28
|
*/
|
package/dist/index.d.ts
CHANGED
|
@@ -22,7 +22,7 @@ interface AiManifest {
|
|
|
22
22
|
/**
|
|
23
23
|
* Use case context domain under the EU AI Act.
|
|
24
24
|
*/
|
|
25
|
-
systemDomain: 'general' | 'law-enforcement' | 'education' | 'employment' | 'critical-infrastructure' | 'biometrics';
|
|
25
|
+
systemDomain: 'general' | 'law-enforcement' | 'education' | 'employment' | 'critical-infrastructure' | 'biometrics' | 'essential-services' | 'migration' | 'justice';
|
|
26
26
|
/**
|
|
27
27
|
* Flag for explicit prohibited practices under Article 5 of the EU AI Act.
|
|
28
28
|
*/
|
package/dist/index.js
CHANGED
|
@@ -59,7 +59,7 @@ function validateManifest(payload, headers, manifest) {
|
|
|
59
59
|
}
|
|
60
60
|
}
|
|
61
61
|
}
|
|
62
|
-
if (manifest.systemDomain === "law-enforcement" || manifest.systemDomain === "biometrics") {
|
|
62
|
+
if (manifest.systemDomain === "law-enforcement" || manifest.systemDomain === "biometrics" || manifest.systemDomain === "education" || manifest.systemDomain === "employment" || manifest.systemDomain === "critical-infrastructure" || manifest.systemDomain === "essential-services" || manifest.systemDomain === "migration" || manifest.systemDomain === "justice") {
|
|
63
63
|
if (manifest.euRiskCategory === "minimal" || manifest.euRiskCategory === "limited") {
|
|
64
64
|
errors.push({
|
|
65
65
|
code: "EU_RISK_MISCLASSIFICATION",
|
|
@@ -140,6 +140,19 @@ function scrubObject(obj, config) {
|
|
|
140
140
|
return obj;
|
|
141
141
|
}
|
|
142
142
|
|
|
143
|
+
// src/utils/telemetry.ts
|
|
144
|
+
function pingStudio(data) {
|
|
145
|
+
if (process.env.NODE_ENV !== "development") {
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
fetch("http://localhost:5050/ingest", {
|
|
149
|
+
method: "POST",
|
|
150
|
+
headers: { "Content-Type": "application/json" },
|
|
151
|
+
body: JSON.stringify(data)
|
|
152
|
+
}).catch(() => {
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
|
|
143
156
|
// src/middleware/withCompliance.ts
|
|
144
157
|
function withAttestlyCompliance(handler, manifest) {
|
|
145
158
|
return async (req, ...args) => {
|
|
@@ -154,20 +167,43 @@ function withAttestlyCompliance(handler, manifest) {
|
|
|
154
167
|
if (process.env.NODE_ENV === "development") {
|
|
155
168
|
const { valid, errors } = validateManifest(payload, req.headers, manifest);
|
|
156
169
|
if (!valid) {
|
|
170
|
+
pingStudio({
|
|
171
|
+
id: Math.random().toString(36).substring(7),
|
|
172
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
173
|
+
model: payload?.model,
|
|
174
|
+
status: "blocked",
|
|
175
|
+
prompt: payload ? JSON.stringify(payload, null, 2) : "No JSON body",
|
|
176
|
+
errors
|
|
177
|
+
});
|
|
157
178
|
return createComplianceError(errors);
|
|
158
179
|
}
|
|
159
180
|
}
|
|
160
181
|
if (payload && manifest.piiScrubbing.level !== "off") {
|
|
161
182
|
const scrubbedPayload = scrubObject(payload, manifest.piiScrubbing);
|
|
183
|
+
pingStudio({
|
|
184
|
+
id: Math.random().toString(36).substring(7),
|
|
185
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
186
|
+
model: payload?.model,
|
|
187
|
+
status: "passed",
|
|
188
|
+
prompt: JSON.stringify(payload, null, 2),
|
|
189
|
+
scrubbedPrompt: JSON.stringify(scrubbedPayload, null, 2)
|
|
190
|
+
});
|
|
162
191
|
const newReq = new Request(req.url, {
|
|
163
192
|
method: req.method,
|
|
164
193
|
headers: req.headers,
|
|
165
194
|
body: JSON.stringify(scrubbedPayload),
|
|
166
|
-
// @ts-ignore
|
|
195
|
+
// @ts-ignore
|
|
167
196
|
duplex: "half"
|
|
168
197
|
});
|
|
169
198
|
return handler(newReq, ...args);
|
|
170
199
|
}
|
|
200
|
+
pingStudio({
|
|
201
|
+
id: Math.random().toString(36).substring(7),
|
|
202
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
203
|
+
model: payload?.model,
|
|
204
|
+
status: "passed",
|
|
205
|
+
prompt: payload ? JSON.stringify(payload, null, 2) : "No JSON body"
|
|
206
|
+
});
|
|
171
207
|
return handler(req, ...args);
|
|
172
208
|
};
|
|
173
209
|
}
|
|
@@ -186,6 +222,14 @@ function withAttestlyStream(handler, manifest) {
|
|
|
186
222
|
if (process.env.NODE_ENV === "development") {
|
|
187
223
|
const { valid, errors } = validateManifest(payload, req.headers, manifest);
|
|
188
224
|
if (!valid) {
|
|
225
|
+
pingStudio({
|
|
226
|
+
id: Math.random().toString(36).substring(7),
|
|
227
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
228
|
+
model: payload?.model,
|
|
229
|
+
status: "blocked",
|
|
230
|
+
prompt: JSON.stringify(payload, null, 2),
|
|
231
|
+
errors
|
|
232
|
+
});
|
|
189
233
|
const errorMessages = errors.map((e) => `[${e.code}] ${e.message}`).join("\n");
|
|
190
234
|
throw new Error(`Attestly Compliance Stream Check Failed:
|
|
191
235
|
${errorMessages}`);
|
|
@@ -193,6 +237,14 @@ ${errorMessages}`);
|
|
|
193
237
|
}
|
|
194
238
|
if (payload && manifest.piiScrubbing.level !== "off") {
|
|
195
239
|
const scrubbedPayload = scrubObject(payload, manifest.piiScrubbing);
|
|
240
|
+
pingStudio({
|
|
241
|
+
id: Math.random().toString(36).substring(7),
|
|
242
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
243
|
+
model: payload?.model,
|
|
244
|
+
status: "passed",
|
|
245
|
+
prompt: JSON.stringify(payload, null, 2),
|
|
246
|
+
scrubbedPrompt: JSON.stringify(scrubbedPayload, null, 2)
|
|
247
|
+
});
|
|
196
248
|
const newReq = new Request(req.url, {
|
|
197
249
|
method: req.method,
|
|
198
250
|
headers: req.headers,
|
|
@@ -202,6 +254,13 @@ ${errorMessages}`);
|
|
|
202
254
|
});
|
|
203
255
|
return handler(newReq, ...args);
|
|
204
256
|
}
|
|
257
|
+
pingStudio({
|
|
258
|
+
id: Math.random().toString(36).substring(7),
|
|
259
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
260
|
+
model: payload?.model,
|
|
261
|
+
status: "passed",
|
|
262
|
+
prompt: payload ? JSON.stringify(payload, null, 2) : "No JSON body"
|
|
263
|
+
});
|
|
205
264
|
return handler(req, ...args);
|
|
206
265
|
};
|
|
207
266
|
}
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/utils/errors.ts","../src/utils/validator.ts","../src/utils/scrubber.ts","../src/middleware/withCompliance.ts","../src/middleware/withStream.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import type { ValidationError } from './validator';\r\n\r\nexport function createComplianceError(errors: ValidationError[]): Response {\r\n // Use the code from the first error as the primary telemetry code, \r\n // or default to BLOCK_ENFORCED if none exists.\r\n const primaryCode = errors.length > 0 ? errors[0].code : 'BLOCK_ENFORCED';\r\n \r\n return new Response(\r\n JSON.stringify({\r\n error: 'AttestlyComplianceViolation',\r\n code: primaryCode,\r\n details: errors\r\n }),\r\n {\r\n status: 400,\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n 'X-Attestly-Blocked': 'true'\r\n }\r\n }\r\n );\r\n}\r\n","import { AiManifest } from '../types';\r\n\r\nexport interface ValidationError {\r\n code: string;\r\n message: string;\r\n}\r\n\r\nexport function validateManifest(\r\n payload: any,\r\n headers: Headers,\r\n manifest: AiManifest\r\n): { valid: boolean; errors: ValidationError[] } {\r\n const errors: ValidationError[] = [];\r\n\r\n // ---------------------------------------------------------\r\n // Existing Rules\r\n // ---------------------------------------------------------\r\n\r\n // Rule 1: Strict Model Locking\r\n if (payload && payload.model) {\r\n const model = String(payload.model);\r\n \r\n // Check against allowedModels\r\n if (!manifest.allowedModels.includes(model)) {\r\n errors.push({\r\n code: 'UNAUTHORIZED_MODEL',\r\n message: `Model '${model}' is not in the allowedModels list.`\r\n });\r\n }\r\n\r\n // Enforce version/date suffixes regex\r\n const versionRegex = /-[0-9]+(-[0-9]+)*$/;\r\n if (!versionRegex.test(model)) {\r\n errors.push({\r\n code: 'INVALID_MODEL_VERSION',\r\n message: `Model '${model}' must contain a strict version or date suffix (e.g., 'gpt-4-0613').`\r\n });\r\n }\r\n }\r\n\r\n // Rule 2: Traceability\r\n for (const requiredKey of manifest.requiredMetadata) {\r\n const hasInPayload = payload && payload[requiredKey] !== undefined;\r\n const hasInHeader = headers.has(requiredKey) || headers.has(`x-${requiredKey}`);\r\n\r\n if (!hasInPayload && !hasInHeader) {\r\n errors.push({\r\n code: 'MISSING_REQUIRED_METADATA',\r\n message: `Missing required metadata: '${requiredKey}'.`\r\n });\r\n }\r\n }\r\n\r\n // ---------------------------------------------------------\r\n // EU AI Act Rules\r\n // ---------------------------------------------------------\r\n\r\n // Rule A (The Kill Switch): Prohibited Practices\r\n if (manifest.prohibitedPractices) {\r\n const practices = Object.entries(manifest.prohibitedPractices);\r\n for (const [practice, isEnabled] of practices) {\r\n if (isEnabled === true) {\r\n errors.push({\r\n code: 'EU_PROHIBITED_PRACTICE',\r\n message: `Under Article 5 of the EU AI Act, the practice '${practice}' is prohibited. Execution blocked.`\r\n });\r\n }\r\n }\r\n }\r\n\r\n // Rule B (Law Enforcement Escalation): Auto-classify high-risk domains\r\n if (manifest.systemDomain === 'law-enforcement' || manifest.systemDomain === 'biometrics') {\r\n if (manifest.euRiskCategory === 'minimal' || manifest.euRiskCategory === 'limited') {\r\n errors.push({\r\n code: 'EU_RISK_MISCLASSIFICATION',\r\n message: `Under Annex III of the EU AI Act, ${manifest.systemDomain} systems are automatically high-risk. Cannot be classified as ${manifest.euRiskCategory}.`\r\n });\r\n }\r\n }\r\n\r\n // Rule C (Conditional Traceability): High-Risk requires strict logging\r\n if (manifest.euRiskCategory === 'high') {\r\n const hasSession = (payload && payload.sessionId !== undefined) || headers.has('sessionId') || headers.has('x-sessionId');\r\n const hasUserId = (payload && payload.userId !== undefined) || headers.has('userId') || headers.has('x-userId');\r\n const hasPurpose = (payload && payload.purpose !== undefined) || headers.has('purpose') || headers.has('x-purpose');\r\n\r\n // We can check if these are in requiredMetadata or explicitly check payload/headers\r\n // Assuming 'sessionId' and 'userId' or 'purpose' are representative traces.\r\n // The instructions say: \"If missing, push an error explaining that High-Risk systems require strict logging\"\r\n // Let's enforce that at least some core traces exist or check requiredMetadata.\r\n // Given the prompt: \"strictly verify that the payload or headers contain traceability metadata (e.g., sessionId, userId). If missing...\"\r\n if (!hasSession && !hasUserId && !hasPurpose) {\r\n errors.push({\r\n code: 'MISSING_HIGH_RISK_TRACEABILITY',\r\n message: 'Under the EU AI Act, High-Risk systems require strict logging traceability. Missing metadata like sessionId, userId, or purpose.'\r\n });\r\n }\r\n }\r\n\r\n return {\r\n valid: errors.length === 0,\r\n errors\r\n };\r\n}\r\n","import { AiManifest } from '../types';\r\n\r\nconst STANDARD_PII_REGEX = [\r\n // Emails\r\n /[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}/g,\r\n // US SSN\r\n /\\b\\d{3}-\\d{2}-\\d{4}\\b/g,\r\n // 16-digit Credit Cards (simple)\r\n /\\b(?:\\d[ -]*?){13,16}\\b/g,\r\n // Generic API Keys (e.g., sk-..., AKIA...)\r\n /\\b(sk-[a-zA-Z0-9]{20,}|AKIA[0-9A-Z]{16})\\b/g\r\n];\r\n\r\nexport function scrubPayload(\r\n text: string,\r\n config: AiManifest['piiScrubbing']\r\n): string {\r\n if (config.level === 'off') {\r\n return text;\r\n }\r\n\r\n if (config.level === 'strict') {\r\n throw new Error('NotImplemented: Strict PII scrubbing is reserved for the Attestly backend ML engine.');\r\n }\r\n\r\n let scrubbed = text;\r\n const redactString = config.redactString ?? '[REDACTED]';\r\n\r\n if (config.level === 'standard') {\r\n for (const regex of STANDARD_PII_REGEX) {\r\n scrubbed = scrubbed.replace(regex, redactString);\r\n }\r\n }\r\n\r\n if (config.customRegex && config.customRegex.length > 0) {\r\n for (const pattern of config.customRegex) {\r\n try {\r\n const regex = new RegExp(pattern, 'g');\r\n scrubbed = scrubbed.replace(regex, redactString);\r\n } catch (e) {\r\n // Ignore invalid regex to prevent crashing the edge function\r\n console.warn('Invalid custom regex pattern in PII scrubber:', pattern);\r\n }\r\n }\r\n }\r\n\r\n return scrubbed;\r\n}\r\n\r\nexport function scrubObject(\r\n obj: any,\r\n config: AiManifest['piiScrubbing']\r\n): any {\r\n if (config.level === 'off') return obj;\r\n if (!obj) return obj;\r\n\r\n if (typeof obj === 'string') {\r\n return scrubPayload(obj, config);\r\n }\r\n\r\n if (Array.isArray(obj)) {\r\n return obj.map(item => scrubObject(item, config));\r\n }\r\n\r\n if (typeof obj === 'object') {\r\n const scrubbedObj: any = {};\r\n for (const [key, value] of Object.entries(obj)) {\r\n scrubbedObj[key] = scrubObject(value, config);\r\n }\r\n return scrubbedObj;\r\n }\r\n\r\n return obj;\r\n}\r\n","import { AiManifest } from '../types';\r\nimport { createComplianceError } from '../utils/errors';\r\nimport { validateManifest } from '../utils/validator';\r\nimport { scrubObject } from '../utils/scrubber';\r\n\r\nexport type RequestHandler = (req: Request, ...args: any[]) => Promise<Response> | Response;\r\n\r\n/**\r\n * A higher-order function that wraps a standard Web API Request handler\r\n * to enforce AI compliance according to the provided manifest.\r\n */\r\nexport function withAttestlyCompliance(\r\n handler: RequestHandler,\r\n manifest: AiManifest\r\n): RequestHandler {\r\n return async (req: Request, ...args: any[]) => {\r\n // Clone the request since we might need to read the body and pass the request down\r\n // But Request bodies can only be read once.\r\n // To be perfectly framework agnostic and non-intrusive, we only check JSON payloads.\r\n let clonedReq = req.clone();\r\n let payload = null;\r\n\r\n try {\r\n if (clonedReq.headers.get('content-type')?.includes('application/json')) {\r\n payload = await clonedReq.json();\r\n }\r\n } catch (e) {\r\n // Ignore if body is not readable/json\r\n }\r\n\r\n if (process.env.NODE_ENV === 'development') {\r\n const { valid, errors } = validateManifest(payload, req.headers, manifest);\r\n\r\n if (!valid) {\r\n return createComplianceError(errors);\r\n }\r\n }\r\n\r\n // Scrub payload if needed before passing it down.\r\n // To do this completely transparently, we'd have to construct a new Request.\r\n // However, the instructions say \"run scrubPayload on the prompt/messages array to ensure data is clean before it hits the underlying AI client.\"\r\n // In a middleware, modifying the `Request` object requires instantiating a new Request.\r\n if (payload && manifest.piiScrubbing.level !== 'off') {\r\n const scrubbedPayload = scrubObject(payload, manifest.piiScrubbing);\r\n const newReq = new Request(req.url, {\r\n method: req.method,\r\n headers: req.headers,\r\n body: JSON.stringify(scrubbedPayload),\r\n // @ts-ignore - some environments require duplex for streaming requests\r\n duplex: 'half'\r\n });\r\n return handler(newReq, ...args);\r\n }\r\n\r\n // Pass through to the original handler if no scrubbing was needed\r\n return handler(req, ...args);\r\n };\r\n}\r\n","import { AiManifest } from '../types';\r\nimport { validateManifest } from '../utils/validator';\r\nimport { scrubObject } from '../utils/scrubber';\r\n\r\nexport type StreamHandler = (req: Request, ...args: any[]) => Promise<Response> | Response;\r\n\r\n/**\r\n * A specialized wrapper for the Vercel AI SDK streams.\r\n * Performs a synchronous compliance check upfront and returns the native stream untouched.\r\n */\r\nexport function withAttestlyStream(\r\n handler: StreamHandler,\r\n manifest: AiManifest\r\n): StreamHandler {\r\n return async (req: Request, ...args: any[]) => {\r\n let clonedReq = req.clone();\r\n let payload = null;\r\n\r\n try {\r\n if (clonedReq.headers.get('content-type')?.includes('application/json')) {\r\n payload = await clonedReq.json();\r\n }\r\n } catch (e) {\r\n // Ignore if body is not readable/json\r\n }\r\n\r\n if (process.env.NODE_ENV === 'development') {\r\n const { valid, errors } = validateManifest(payload, req.headers, manifest);\r\n\r\n if (!valid) {\r\n const errorMessages = errors.map(e => `[${e.code}] ${e.message}`).join('\\n');\r\n throw new Error(`Attestly Compliance Stream Check Failed: \\n${errorMessages}`);\r\n }\r\n }\r\n\r\n if (payload && manifest.piiScrubbing.level !== 'off') {\r\n const scrubbedPayload = scrubObject(payload, manifest.piiScrubbing);\r\n const newReq = new Request(req.url, {\r\n method: req.method,\r\n headers: req.headers,\r\n body: JSON.stringify(scrubbedPayload),\r\n // @ts-ignore\r\n duplex: 'half'\r\n });\r\n return handler(newReq, ...args);\r\n }\r\n\r\n // Pass through the DataStreamResponse untouched\r\n return handler(req, ...args);\r\n };\r\n}\r\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/utils/errors.ts","../src/utils/validator.ts","../src/utils/scrubber.ts","../src/utils/telemetry.ts","../src/middleware/withCompliance.ts","../src/middleware/withStream.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import type { ValidationError } from './validator';\r\n\r\nexport function createComplianceError(errors: ValidationError[]): Response {\r\n // Use the code from the first error as the primary telemetry code, \r\n // or default to BLOCK_ENFORCED if none exists.\r\n const primaryCode = errors.length > 0 ? errors[0].code : 'BLOCK_ENFORCED';\r\n \r\n return new Response(\r\n JSON.stringify({\r\n error: 'AttestlyComplianceViolation',\r\n code: primaryCode,\r\n details: errors\r\n }),\r\n {\r\n status: 400,\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n 'X-Attestly-Blocked': 'true'\r\n }\r\n }\r\n );\r\n}\r\n","import { AiManifest } from '../types';\r\n\r\nexport interface ValidationError {\r\n code: string;\r\n message: string;\r\n}\r\n\r\nexport function validateManifest(\r\n payload: any,\r\n headers: Headers,\r\n manifest: AiManifest\r\n): { valid: boolean; errors: ValidationError[] } {\r\n const errors: ValidationError[] = [];\r\n\r\n // ---------------------------------------------------------\r\n // Existing Rules\r\n // ---------------------------------------------------------\r\n\r\n // Rule 1: Strict Model Locking\r\n if (payload && payload.model) {\r\n const model = String(payload.model);\r\n \r\n // Check against allowedModels\r\n if (!manifest.allowedModels.includes(model)) {\r\n errors.push({\r\n code: 'UNAUTHORIZED_MODEL',\r\n message: `Model '${model}' is not in the allowedModels list.`\r\n });\r\n }\r\n\r\n // Enforce version/date suffixes regex\r\n const versionRegex = /-[0-9]+(-[0-9]+)*$/;\r\n if (!versionRegex.test(model)) {\r\n errors.push({\r\n code: 'INVALID_MODEL_VERSION',\r\n message: `Model '${model}' must contain a strict version or date suffix (e.g., 'gpt-4-0613').`\r\n });\r\n }\r\n }\r\n\r\n // Rule 2: Traceability\r\n for (const requiredKey of manifest.requiredMetadata) {\r\n const hasInPayload = payload && payload[requiredKey] !== undefined;\r\n const hasInHeader = headers.has(requiredKey) || headers.has(`x-${requiredKey}`);\r\n\r\n if (!hasInPayload && !hasInHeader) {\r\n errors.push({\r\n code: 'MISSING_REQUIRED_METADATA',\r\n message: `Missing required metadata: '${requiredKey}'.`\r\n });\r\n }\r\n }\r\n\r\n // ---------------------------------------------------------\r\n // EU AI Act Rules\r\n // ---------------------------------------------------------\r\n\r\n // Rule A (The Kill Switch): Prohibited Practices\r\n if (manifest.prohibitedPractices) {\r\n const practices = Object.entries(manifest.prohibitedPractices);\r\n for (const [practice, isEnabled] of practices) {\r\n if (isEnabled === true) {\r\n errors.push({\r\n code: 'EU_PROHIBITED_PRACTICE',\r\n message: `Under Article 5 of the EU AI Act, the practice '${practice}' is prohibited. Execution blocked.`\r\n });\r\n }\r\n }\r\n }\r\n\r\n // Rule B (Law Enforcement Escalation): Auto-classify high-risk domains\r\n if (\r\n manifest.systemDomain === 'law-enforcement' ||\r\n manifest.systemDomain === 'biometrics' ||\r\n manifest.systemDomain === 'education' ||\r\n manifest.systemDomain === 'employment' ||\r\n manifest.systemDomain === 'critical-infrastructure' ||\r\n manifest.systemDomain === 'essential-services' ||\r\n manifest.systemDomain === 'migration' ||\r\n manifest.systemDomain === 'justice'\r\n ) {\r\n if (manifest.euRiskCategory === 'minimal' || manifest.euRiskCategory === 'limited') {\r\n errors.push({\r\n code: 'EU_RISK_MISCLASSIFICATION',\r\n message: `Under Annex III of the EU AI Act, ${manifest.systemDomain} systems are automatically high-risk. Cannot be classified as ${manifest.euRiskCategory}.`\r\n });\r\n }\r\n }\r\n\r\n // Rule C (Conditional Traceability): High-Risk requires strict logging\r\n if (manifest.euRiskCategory === 'high') {\r\n const hasSession = (payload && payload.sessionId !== undefined) || headers.has('sessionId') || headers.has('x-sessionId');\r\n const hasUserId = (payload && payload.userId !== undefined) || headers.has('userId') || headers.has('x-userId');\r\n const hasPurpose = (payload && payload.purpose !== undefined) || headers.has('purpose') || headers.has('x-purpose');\r\n\r\n // We can check if these are in requiredMetadata or explicitly check payload/headers\r\n // Assuming 'sessionId' and 'userId' or 'purpose' are representative traces.\r\n // The instructions say: \"If missing, push an error explaining that High-Risk systems require strict logging\"\r\n // Let's enforce that at least some core traces exist or check requiredMetadata.\r\n // Given the prompt: \"strictly verify that the payload or headers contain traceability metadata (e.g., sessionId, userId). If missing...\"\r\n if (!hasSession && !hasUserId && !hasPurpose) {\r\n errors.push({\r\n code: 'MISSING_HIGH_RISK_TRACEABILITY',\r\n message: 'Under the EU AI Act, High-Risk systems require strict logging traceability. Missing metadata like sessionId, userId, or purpose.'\r\n });\r\n }\r\n }\r\n\r\n return {\r\n valid: errors.length === 0,\r\n errors\r\n };\r\n}\r\n","import { AiManifest } from '../types';\r\n\r\nconst STANDARD_PII_REGEX = [\r\n // Emails\r\n /[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}/g,\r\n // US SSN\r\n /\\b\\d{3}-\\d{2}-\\d{4}\\b/g,\r\n // 16-digit Credit Cards (simple)\r\n /\\b(?:\\d[ -]*?){13,16}\\b/g,\r\n // Generic API Keys (e.g., sk-..., AKIA...)\r\n /\\b(sk-[a-zA-Z0-9]{20,}|AKIA[0-9A-Z]{16})\\b/g\r\n];\r\n\r\nexport function scrubPayload(\r\n text: string,\r\n config: AiManifest['piiScrubbing']\r\n): string {\r\n if (config.level === 'off') {\r\n return text;\r\n }\r\n\r\n if (config.level === 'strict') {\r\n throw new Error('NotImplemented: Strict PII scrubbing is reserved for the Attestly backend ML engine.');\r\n }\r\n\r\n let scrubbed = text;\r\n const redactString = config.redactString ?? '[REDACTED]';\r\n\r\n if (config.level === 'standard') {\r\n for (const regex of STANDARD_PII_REGEX) {\r\n scrubbed = scrubbed.replace(regex, redactString);\r\n }\r\n }\r\n\r\n if (config.customRegex && config.customRegex.length > 0) {\r\n for (const pattern of config.customRegex) {\r\n try {\r\n const regex = new RegExp(pattern, 'g');\r\n scrubbed = scrubbed.replace(regex, redactString);\r\n } catch (e) {\r\n // Ignore invalid regex to prevent crashing the edge function\r\n console.warn('Invalid custom regex pattern in PII scrubber:', pattern);\r\n }\r\n }\r\n }\r\n\r\n return scrubbed;\r\n}\r\n\r\nexport function scrubObject(\r\n obj: any,\r\n config: AiManifest['piiScrubbing']\r\n): any {\r\n if (config.level === 'off') return obj;\r\n if (!obj) return obj;\r\n\r\n if (typeof obj === 'string') {\r\n return scrubPayload(obj, config);\r\n }\r\n\r\n if (Array.isArray(obj)) {\r\n return obj.map(item => scrubObject(item, config));\r\n }\r\n\r\n if (typeof obj === 'object') {\r\n const scrubbedObj: any = {};\r\n for (const [key, value] of Object.entries(obj)) {\r\n scrubbedObj[key] = scrubObject(value, config);\r\n }\r\n return scrubbedObj;\r\n }\r\n\r\n return obj;\r\n}\r\n","export interface TelemetryPayload {\r\n id: string;\r\n timestamp: string;\r\n model?: string;\r\n status: 'passed' | 'blocked';\r\n prompt?: string;\r\n scrubbedPrompt?: string;\r\n errors?: { code: string; message: string }[];\r\n}\r\n\r\n/**\r\n * Non-blocking ping to the local Attestly Studio.\r\n * Fails silently if the studio is not running.\r\n */\r\nexport function pingStudio(data: TelemetryPayload) {\r\n // Only attempt to ping if in development environment\r\n if (process.env.NODE_ENV !== 'development') {\r\n return;\r\n }\r\n\r\n // Fire and forget, no await to prevent TTFB degradation\r\n fetch('http://localhost:5050/ingest', {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: JSON.stringify(data),\r\n }).catch(() => {\r\n // Silently ignore if studio isn't running\r\n });\r\n}\r\n","import { AiManifest } from '../types';\r\nimport { createComplianceError } from '../utils/errors';\r\nimport { validateManifest } from '../utils/validator';\r\nimport { scrubObject } from '../utils/scrubber';\r\nimport { pingStudio } from '../utils/telemetry';\r\n\r\nexport type RequestHandler = (req: Request, ...args: any[]) => Promise<Response> | Response;\r\n\r\n/**\r\n * A higher-order function that wraps a standard Web API Request handler\r\n * to enforce AI compliance according to the provided manifest.\r\n */\r\nexport function withAttestlyCompliance(\r\n handler: RequestHandler,\r\n manifest: AiManifest\r\n): RequestHandler {\r\n return async (req: Request, ...args: any[]) => {\r\n let clonedReq = req.clone();\r\n let payload = null;\r\n\r\n try {\r\n if (clonedReq.headers.get('content-type')?.includes('application/json')) {\r\n payload = await clonedReq.json();\r\n }\r\n } catch (e) {\r\n // Ignore if body is not readable/json\r\n }\r\n\r\n if (process.env.NODE_ENV === 'development') {\r\n const { valid, errors } = validateManifest(payload, req.headers, manifest);\r\n\r\n if (!valid) {\r\n pingStudio({\r\n id: Math.random().toString(36).substring(7),\r\n timestamp: new Date().toISOString(),\r\n model: payload?.model,\r\n status: 'blocked',\r\n prompt: payload ? JSON.stringify(payload, null, 2) : 'No JSON body',\r\n errors\r\n });\r\n return createComplianceError(errors);\r\n }\r\n }\r\n\r\n if (payload && manifest.piiScrubbing.level !== 'off') {\r\n const scrubbedPayload = scrubObject(payload, manifest.piiScrubbing);\r\n \r\n pingStudio({\r\n id: Math.random().toString(36).substring(7),\r\n timestamp: new Date().toISOString(),\r\n model: payload?.model,\r\n status: 'passed',\r\n prompt: JSON.stringify(payload, null, 2),\r\n scrubbedPrompt: JSON.stringify(scrubbedPayload, null, 2)\r\n });\r\n\r\n const newReq = new Request(req.url, {\r\n method: req.method,\r\n headers: req.headers,\r\n body: JSON.stringify(scrubbedPayload),\r\n // @ts-ignore\r\n duplex: 'half'\r\n });\r\n return handler(newReq, ...args);\r\n }\r\n\r\n pingStudio({\r\n id: Math.random().toString(36).substring(7),\r\n timestamp: new Date().toISOString(),\r\n model: payload?.model,\r\n status: 'passed',\r\n prompt: payload ? JSON.stringify(payload, null, 2) : 'No JSON body'\r\n });\r\n\r\n return handler(req, ...args);\r\n };\r\n}\r\n","import { AiManifest } from '../types';\r\nimport { validateManifest } from '../utils/validator';\r\nimport { scrubObject } from '../utils/scrubber';\r\nimport { pingStudio } from '../utils/telemetry';\r\n\r\nexport type StreamHandler = (req: Request, ...args: any[]) => Promise<Response> | Response;\r\n\r\n/**\r\n * A specialized wrapper for the Vercel AI SDK streams.\r\n * Performs a synchronous compliance check upfront and returns the native stream untouched.\r\n */\r\nexport function withAttestlyStream(\r\n handler: StreamHandler,\r\n manifest: AiManifest\r\n): StreamHandler {\r\n return async (req: Request, ...args: any[]) => {\r\n let clonedReq = req.clone();\r\n let payload = null;\r\n\r\n try {\r\n if (clonedReq.headers.get('content-type')?.includes('application/json')) {\r\n payload = await clonedReq.json();\r\n }\r\n } catch (e) {\r\n // Ignore if body is not readable/json\r\n }\r\n\r\n if (process.env.NODE_ENV === 'development') {\r\n const { valid, errors } = validateManifest(payload, req.headers, manifest);\r\n\r\n if (!valid) {\r\n pingStudio({\r\n id: Math.random().toString(36).substring(7),\r\n timestamp: new Date().toISOString(),\r\n model: payload?.model,\r\n status: 'blocked',\r\n prompt: JSON.stringify(payload, null, 2),\r\n errors\r\n });\r\n const errorMessages = errors.map(e => `[${e.code}] ${e.message}`).join('\\n');\r\n throw new Error(`Attestly Compliance Stream Check Failed: \\n${errorMessages}`);\r\n }\r\n }\r\n\r\n if (payload && manifest.piiScrubbing.level !== 'off') {\r\n const scrubbedPayload = scrubObject(payload, manifest.piiScrubbing);\r\n \r\n pingStudio({\r\n id: Math.random().toString(36).substring(7),\r\n timestamp: new Date().toISOString(),\r\n model: payload?.model,\r\n status: 'passed',\r\n prompt: JSON.stringify(payload, null, 2),\r\n scrubbedPrompt: JSON.stringify(scrubbedPayload, null, 2)\r\n });\r\n\r\n const newReq = new Request(req.url, {\r\n method: req.method,\r\n headers: req.headers,\r\n body: JSON.stringify(scrubbedPayload),\r\n // @ts-ignore\r\n duplex: 'half'\r\n });\r\n return handler(newReq, ...args);\r\n }\r\n\r\n pingStudio({\r\n id: Math.random().toString(36).substring(7),\r\n timestamp: new Date().toISOString(),\r\n model: payload?.model,\r\n status: 'passed',\r\n prompt: payload ? JSON.stringify(payload, null, 2) : 'No JSON body'\r\n });\r\n\r\n // Pass through the DataStreamResponse untouched\r\n return handler(req, ...args);\r\n };\r\n}\r\n"]}
|
package/dist/index.mjs
CHANGED
|
@@ -57,7 +57,7 @@ function validateManifest(payload, headers, manifest) {
|
|
|
57
57
|
}
|
|
58
58
|
}
|
|
59
59
|
}
|
|
60
|
-
if (manifest.systemDomain === "law-enforcement" || manifest.systemDomain === "biometrics") {
|
|
60
|
+
if (manifest.systemDomain === "law-enforcement" || manifest.systemDomain === "biometrics" || manifest.systemDomain === "education" || manifest.systemDomain === "employment" || manifest.systemDomain === "critical-infrastructure" || manifest.systemDomain === "essential-services" || manifest.systemDomain === "migration" || manifest.systemDomain === "justice") {
|
|
61
61
|
if (manifest.euRiskCategory === "minimal" || manifest.euRiskCategory === "limited") {
|
|
62
62
|
errors.push({
|
|
63
63
|
code: "EU_RISK_MISCLASSIFICATION",
|
|
@@ -138,6 +138,19 @@ function scrubObject(obj, config) {
|
|
|
138
138
|
return obj;
|
|
139
139
|
}
|
|
140
140
|
|
|
141
|
+
// src/utils/telemetry.ts
|
|
142
|
+
function pingStudio(data) {
|
|
143
|
+
if (process.env.NODE_ENV !== "development") {
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
fetch("http://localhost:5050/ingest", {
|
|
147
|
+
method: "POST",
|
|
148
|
+
headers: { "Content-Type": "application/json" },
|
|
149
|
+
body: JSON.stringify(data)
|
|
150
|
+
}).catch(() => {
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
|
|
141
154
|
// src/middleware/withCompliance.ts
|
|
142
155
|
function withAttestlyCompliance(handler, manifest) {
|
|
143
156
|
return async (req, ...args) => {
|
|
@@ -152,20 +165,43 @@ function withAttestlyCompliance(handler, manifest) {
|
|
|
152
165
|
if (process.env.NODE_ENV === "development") {
|
|
153
166
|
const { valid, errors } = validateManifest(payload, req.headers, manifest);
|
|
154
167
|
if (!valid) {
|
|
168
|
+
pingStudio({
|
|
169
|
+
id: Math.random().toString(36).substring(7),
|
|
170
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
171
|
+
model: payload?.model,
|
|
172
|
+
status: "blocked",
|
|
173
|
+
prompt: payload ? JSON.stringify(payload, null, 2) : "No JSON body",
|
|
174
|
+
errors
|
|
175
|
+
});
|
|
155
176
|
return createComplianceError(errors);
|
|
156
177
|
}
|
|
157
178
|
}
|
|
158
179
|
if (payload && manifest.piiScrubbing.level !== "off") {
|
|
159
180
|
const scrubbedPayload = scrubObject(payload, manifest.piiScrubbing);
|
|
181
|
+
pingStudio({
|
|
182
|
+
id: Math.random().toString(36).substring(7),
|
|
183
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
184
|
+
model: payload?.model,
|
|
185
|
+
status: "passed",
|
|
186
|
+
prompt: JSON.stringify(payload, null, 2),
|
|
187
|
+
scrubbedPrompt: JSON.stringify(scrubbedPayload, null, 2)
|
|
188
|
+
});
|
|
160
189
|
const newReq = new Request(req.url, {
|
|
161
190
|
method: req.method,
|
|
162
191
|
headers: req.headers,
|
|
163
192
|
body: JSON.stringify(scrubbedPayload),
|
|
164
|
-
// @ts-ignore
|
|
193
|
+
// @ts-ignore
|
|
165
194
|
duplex: "half"
|
|
166
195
|
});
|
|
167
196
|
return handler(newReq, ...args);
|
|
168
197
|
}
|
|
198
|
+
pingStudio({
|
|
199
|
+
id: Math.random().toString(36).substring(7),
|
|
200
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
201
|
+
model: payload?.model,
|
|
202
|
+
status: "passed",
|
|
203
|
+
prompt: payload ? JSON.stringify(payload, null, 2) : "No JSON body"
|
|
204
|
+
});
|
|
169
205
|
return handler(req, ...args);
|
|
170
206
|
};
|
|
171
207
|
}
|
|
@@ -184,6 +220,14 @@ function withAttestlyStream(handler, manifest) {
|
|
|
184
220
|
if (process.env.NODE_ENV === "development") {
|
|
185
221
|
const { valid, errors } = validateManifest(payload, req.headers, manifest);
|
|
186
222
|
if (!valid) {
|
|
223
|
+
pingStudio({
|
|
224
|
+
id: Math.random().toString(36).substring(7),
|
|
225
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
226
|
+
model: payload?.model,
|
|
227
|
+
status: "blocked",
|
|
228
|
+
prompt: JSON.stringify(payload, null, 2),
|
|
229
|
+
errors
|
|
230
|
+
});
|
|
187
231
|
const errorMessages = errors.map((e) => `[${e.code}] ${e.message}`).join("\n");
|
|
188
232
|
throw new Error(`Attestly Compliance Stream Check Failed:
|
|
189
233
|
${errorMessages}`);
|
|
@@ -191,6 +235,14 @@ ${errorMessages}`);
|
|
|
191
235
|
}
|
|
192
236
|
if (payload && manifest.piiScrubbing.level !== "off") {
|
|
193
237
|
const scrubbedPayload = scrubObject(payload, manifest.piiScrubbing);
|
|
238
|
+
pingStudio({
|
|
239
|
+
id: Math.random().toString(36).substring(7),
|
|
240
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
241
|
+
model: payload?.model,
|
|
242
|
+
status: "passed",
|
|
243
|
+
prompt: JSON.stringify(payload, null, 2),
|
|
244
|
+
scrubbedPrompt: JSON.stringify(scrubbedPayload, null, 2)
|
|
245
|
+
});
|
|
194
246
|
const newReq = new Request(req.url, {
|
|
195
247
|
method: req.method,
|
|
196
248
|
headers: req.headers,
|
|
@@ -200,6 +252,13 @@ ${errorMessages}`);
|
|
|
200
252
|
});
|
|
201
253
|
return handler(newReq, ...args);
|
|
202
254
|
}
|
|
255
|
+
pingStudio({
|
|
256
|
+
id: Math.random().toString(36).substring(7),
|
|
257
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
258
|
+
model: payload?.model,
|
|
259
|
+
status: "passed",
|
|
260
|
+
prompt: payload ? JSON.stringify(payload, null, 2) : "No JSON body"
|
|
261
|
+
});
|
|
203
262
|
return handler(req, ...args);
|
|
204
263
|
};
|
|
205
264
|
}
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/utils/errors.ts","../src/utils/validator.ts","../src/utils/scrubber.ts","../src/middleware/withCompliance.ts","../src/middleware/withStream.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["import type { ValidationError } from './validator';\r\n\r\nexport function createComplianceError(errors: ValidationError[]): Response {\r\n // Use the code from the first error as the primary telemetry code, \r\n // or default to BLOCK_ENFORCED if none exists.\r\n const primaryCode = errors.length > 0 ? errors[0].code : 'BLOCK_ENFORCED';\r\n \r\n return new Response(\r\n JSON.stringify({\r\n error: 'AttestlyComplianceViolation',\r\n code: primaryCode,\r\n details: errors\r\n }),\r\n {\r\n status: 400,\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n 'X-Attestly-Blocked': 'true'\r\n }\r\n }\r\n );\r\n}\r\n","import { AiManifest } from '../types';\r\n\r\nexport interface ValidationError {\r\n code: string;\r\n message: string;\r\n}\r\n\r\nexport function validateManifest(\r\n payload: any,\r\n headers: Headers,\r\n manifest: AiManifest\r\n): { valid: boolean; errors: ValidationError[] } {\r\n const errors: ValidationError[] = [];\r\n\r\n // ---------------------------------------------------------\r\n // Existing Rules\r\n // ---------------------------------------------------------\r\n\r\n // Rule 1: Strict Model Locking\r\n if (payload && payload.model) {\r\n const model = String(payload.model);\r\n \r\n // Check against allowedModels\r\n if (!manifest.allowedModels.includes(model)) {\r\n errors.push({\r\n code: 'UNAUTHORIZED_MODEL',\r\n message: `Model '${model}' is not in the allowedModels list.`\r\n });\r\n }\r\n\r\n // Enforce version/date suffixes regex\r\n const versionRegex = /-[0-9]+(-[0-9]+)*$/;\r\n if (!versionRegex.test(model)) {\r\n errors.push({\r\n code: 'INVALID_MODEL_VERSION',\r\n message: `Model '${model}' must contain a strict version or date suffix (e.g., 'gpt-4-0613').`\r\n });\r\n }\r\n }\r\n\r\n // Rule 2: Traceability\r\n for (const requiredKey of manifest.requiredMetadata) {\r\n const hasInPayload = payload && payload[requiredKey] !== undefined;\r\n const hasInHeader = headers.has(requiredKey) || headers.has(`x-${requiredKey}`);\r\n\r\n if (!hasInPayload && !hasInHeader) {\r\n errors.push({\r\n code: 'MISSING_REQUIRED_METADATA',\r\n message: `Missing required metadata: '${requiredKey}'.`\r\n });\r\n }\r\n }\r\n\r\n // ---------------------------------------------------------\r\n // EU AI Act Rules\r\n // ---------------------------------------------------------\r\n\r\n // Rule A (The Kill Switch): Prohibited Practices\r\n if (manifest.prohibitedPractices) {\r\n const practices = Object.entries(manifest.prohibitedPractices);\r\n for (const [practice, isEnabled] of practices) {\r\n if (isEnabled === true) {\r\n errors.push({\r\n code: 'EU_PROHIBITED_PRACTICE',\r\n message: `Under Article 5 of the EU AI Act, the practice '${practice}' is prohibited. Execution blocked.`\r\n });\r\n }\r\n }\r\n }\r\n\r\n // Rule B (Law Enforcement Escalation): Auto-classify high-risk domains\r\n if (manifest.systemDomain === 'law-enforcement' || manifest.systemDomain === 'biometrics') {\r\n if (manifest.euRiskCategory === 'minimal' || manifest.euRiskCategory === 'limited') {\r\n errors.push({\r\n code: 'EU_RISK_MISCLASSIFICATION',\r\n message: `Under Annex III of the EU AI Act, ${manifest.systemDomain} systems are automatically high-risk. Cannot be classified as ${manifest.euRiskCategory}.`\r\n });\r\n }\r\n }\r\n\r\n // Rule C (Conditional Traceability): High-Risk requires strict logging\r\n if (manifest.euRiskCategory === 'high') {\r\n const hasSession = (payload && payload.sessionId !== undefined) || headers.has('sessionId') || headers.has('x-sessionId');\r\n const hasUserId = (payload && payload.userId !== undefined) || headers.has('userId') || headers.has('x-userId');\r\n const hasPurpose = (payload && payload.purpose !== undefined) || headers.has('purpose') || headers.has('x-purpose');\r\n\r\n // We can check if these are in requiredMetadata or explicitly check payload/headers\r\n // Assuming 'sessionId' and 'userId' or 'purpose' are representative traces.\r\n // The instructions say: \"If missing, push an error explaining that High-Risk systems require strict logging\"\r\n // Let's enforce that at least some core traces exist or check requiredMetadata.\r\n // Given the prompt: \"strictly verify that the payload or headers contain traceability metadata (e.g., sessionId, userId). If missing...\"\r\n if (!hasSession && !hasUserId && !hasPurpose) {\r\n errors.push({\r\n code: 'MISSING_HIGH_RISK_TRACEABILITY',\r\n message: 'Under the EU AI Act, High-Risk systems require strict logging traceability. Missing metadata like sessionId, userId, or purpose.'\r\n });\r\n }\r\n }\r\n\r\n return {\r\n valid: errors.length === 0,\r\n errors\r\n };\r\n}\r\n","import { AiManifest } from '../types';\r\n\r\nconst STANDARD_PII_REGEX = [\r\n // Emails\r\n /[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}/g,\r\n // US SSN\r\n /\\b\\d{3}-\\d{2}-\\d{4}\\b/g,\r\n // 16-digit Credit Cards (simple)\r\n /\\b(?:\\d[ -]*?){13,16}\\b/g,\r\n // Generic API Keys (e.g., sk-..., AKIA...)\r\n /\\b(sk-[a-zA-Z0-9]{20,}|AKIA[0-9A-Z]{16})\\b/g\r\n];\r\n\r\nexport function scrubPayload(\r\n text: string,\r\n config: AiManifest['piiScrubbing']\r\n): string {\r\n if (config.level === 'off') {\r\n return text;\r\n }\r\n\r\n if (config.level === 'strict') {\r\n throw new Error('NotImplemented: Strict PII scrubbing is reserved for the Attestly backend ML engine.');\r\n }\r\n\r\n let scrubbed = text;\r\n const redactString = config.redactString ?? '[REDACTED]';\r\n\r\n if (config.level === 'standard') {\r\n for (const regex of STANDARD_PII_REGEX) {\r\n scrubbed = scrubbed.replace(regex, redactString);\r\n }\r\n }\r\n\r\n if (config.customRegex && config.customRegex.length > 0) {\r\n for (const pattern of config.customRegex) {\r\n try {\r\n const regex = new RegExp(pattern, 'g');\r\n scrubbed = scrubbed.replace(regex, redactString);\r\n } catch (e) {\r\n // Ignore invalid regex to prevent crashing the edge function\r\n console.warn('Invalid custom regex pattern in PII scrubber:', pattern);\r\n }\r\n }\r\n }\r\n\r\n return scrubbed;\r\n}\r\n\r\nexport function scrubObject(\r\n obj: any,\r\n config: AiManifest['piiScrubbing']\r\n): any {\r\n if (config.level === 'off') return obj;\r\n if (!obj) return obj;\r\n\r\n if (typeof obj === 'string') {\r\n return scrubPayload(obj, config);\r\n }\r\n\r\n if (Array.isArray(obj)) {\r\n return obj.map(item => scrubObject(item, config));\r\n }\r\n\r\n if (typeof obj === 'object') {\r\n const scrubbedObj: any = {};\r\n for (const [key, value] of Object.entries(obj)) {\r\n scrubbedObj[key] = scrubObject(value, config);\r\n }\r\n return scrubbedObj;\r\n }\r\n\r\n return obj;\r\n}\r\n","import { AiManifest } from '../types';\r\nimport { createComplianceError } from '../utils/errors';\r\nimport { validateManifest } from '../utils/validator';\r\nimport { scrubObject } from '../utils/scrubber';\r\n\r\nexport type RequestHandler = (req: Request, ...args: any[]) => Promise<Response> | Response;\r\n\r\n/**\r\n * A higher-order function that wraps a standard Web API Request handler\r\n * to enforce AI compliance according to the provided manifest.\r\n */\r\nexport function withAttestlyCompliance(\r\n handler: RequestHandler,\r\n manifest: AiManifest\r\n): RequestHandler {\r\n return async (req: Request, ...args: any[]) => {\r\n // Clone the request since we might need to read the body and pass the request down\r\n // But Request bodies can only be read once.\r\n // To be perfectly framework agnostic and non-intrusive, we only check JSON payloads.\r\n let clonedReq = req.clone();\r\n let payload = null;\r\n\r\n try {\r\n if (clonedReq.headers.get('content-type')?.includes('application/json')) {\r\n payload = await clonedReq.json();\r\n }\r\n } catch (e) {\r\n // Ignore if body is not readable/json\r\n }\r\n\r\n if (process.env.NODE_ENV === 'development') {\r\n const { valid, errors } = validateManifest(payload, req.headers, manifest);\r\n\r\n if (!valid) {\r\n return createComplianceError(errors);\r\n }\r\n }\r\n\r\n // Scrub payload if needed before passing it down.\r\n // To do this completely transparently, we'd have to construct a new Request.\r\n // However, the instructions say \"run scrubPayload on the prompt/messages array to ensure data is clean before it hits the underlying AI client.\"\r\n // In a middleware, modifying the `Request` object requires instantiating a new Request.\r\n if (payload && manifest.piiScrubbing.level !== 'off') {\r\n const scrubbedPayload = scrubObject(payload, manifest.piiScrubbing);\r\n const newReq = new Request(req.url, {\r\n method: req.method,\r\n headers: req.headers,\r\n body: JSON.stringify(scrubbedPayload),\r\n // @ts-ignore - some environments require duplex for streaming requests\r\n duplex: 'half'\r\n });\r\n return handler(newReq, ...args);\r\n }\r\n\r\n // Pass through to the original handler if no scrubbing was needed\r\n return handler(req, ...args);\r\n };\r\n}\r\n","import { AiManifest } from '../types';\r\nimport { validateManifest } from '../utils/validator';\r\nimport { scrubObject } from '../utils/scrubber';\r\n\r\nexport type StreamHandler = (req: Request, ...args: any[]) => Promise<Response> | Response;\r\n\r\n/**\r\n * A specialized wrapper for the Vercel AI SDK streams.\r\n * Performs a synchronous compliance check upfront and returns the native stream untouched.\r\n */\r\nexport function withAttestlyStream(\r\n handler: StreamHandler,\r\n manifest: AiManifest\r\n): StreamHandler {\r\n return async (req: Request, ...args: any[]) => {\r\n let clonedReq = req.clone();\r\n let payload = null;\r\n\r\n try {\r\n if (clonedReq.headers.get('content-type')?.includes('application/json')) {\r\n payload = await clonedReq.json();\r\n }\r\n } catch (e) {\r\n // Ignore if body is not readable/json\r\n }\r\n\r\n if (process.env.NODE_ENV === 'development') {\r\n const { valid, errors } = validateManifest(payload, req.headers, manifest);\r\n\r\n if (!valid) {\r\n const errorMessages = errors.map(e => `[${e.code}] ${e.message}`).join('\\n');\r\n throw new Error(`Attestly Compliance Stream Check Failed: \\n${errorMessages}`);\r\n }\r\n }\r\n\r\n if (payload && manifest.piiScrubbing.level !== 'off') {\r\n const scrubbedPayload = scrubObject(payload, manifest.piiScrubbing);\r\n const newReq = new Request(req.url, {\r\n method: req.method,\r\n headers: req.headers,\r\n body: JSON.stringify(scrubbedPayload),\r\n // @ts-ignore\r\n duplex: 'half'\r\n });\r\n return handler(newReq, ...args);\r\n }\r\n\r\n // Pass through the DataStreamResponse untouched\r\n return handler(req, ...args);\r\n };\r\n}\r\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/utils/errors.ts","../src/utils/validator.ts","../src/utils/scrubber.ts","../src/utils/telemetry.ts","../src/middleware/withCompliance.ts","../src/middleware/withStream.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["import type { ValidationError } from './validator';\r\n\r\nexport function createComplianceError(errors: ValidationError[]): Response {\r\n // Use the code from the first error as the primary telemetry code, \r\n // or default to BLOCK_ENFORCED if none exists.\r\n const primaryCode = errors.length > 0 ? errors[0].code : 'BLOCK_ENFORCED';\r\n \r\n return new Response(\r\n JSON.stringify({\r\n error: 'AttestlyComplianceViolation',\r\n code: primaryCode,\r\n details: errors\r\n }),\r\n {\r\n status: 400,\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n 'X-Attestly-Blocked': 'true'\r\n }\r\n }\r\n );\r\n}\r\n","import { AiManifest } from '../types';\r\n\r\nexport interface ValidationError {\r\n code: string;\r\n message: string;\r\n}\r\n\r\nexport function validateManifest(\r\n payload: any,\r\n headers: Headers,\r\n manifest: AiManifest\r\n): { valid: boolean; errors: ValidationError[] } {\r\n const errors: ValidationError[] = [];\r\n\r\n // ---------------------------------------------------------\r\n // Existing Rules\r\n // ---------------------------------------------------------\r\n\r\n // Rule 1: Strict Model Locking\r\n if (payload && payload.model) {\r\n const model = String(payload.model);\r\n \r\n // Check against allowedModels\r\n if (!manifest.allowedModels.includes(model)) {\r\n errors.push({\r\n code: 'UNAUTHORIZED_MODEL',\r\n message: `Model '${model}' is not in the allowedModels list.`\r\n });\r\n }\r\n\r\n // Enforce version/date suffixes regex\r\n const versionRegex = /-[0-9]+(-[0-9]+)*$/;\r\n if (!versionRegex.test(model)) {\r\n errors.push({\r\n code: 'INVALID_MODEL_VERSION',\r\n message: `Model '${model}' must contain a strict version or date suffix (e.g., 'gpt-4-0613').`\r\n });\r\n }\r\n }\r\n\r\n // Rule 2: Traceability\r\n for (const requiredKey of manifest.requiredMetadata) {\r\n const hasInPayload = payload && payload[requiredKey] !== undefined;\r\n const hasInHeader = headers.has(requiredKey) || headers.has(`x-${requiredKey}`);\r\n\r\n if (!hasInPayload && !hasInHeader) {\r\n errors.push({\r\n code: 'MISSING_REQUIRED_METADATA',\r\n message: `Missing required metadata: '${requiredKey}'.`\r\n });\r\n }\r\n }\r\n\r\n // ---------------------------------------------------------\r\n // EU AI Act Rules\r\n // ---------------------------------------------------------\r\n\r\n // Rule A (The Kill Switch): Prohibited Practices\r\n if (manifest.prohibitedPractices) {\r\n const practices = Object.entries(manifest.prohibitedPractices);\r\n for (const [practice, isEnabled] of practices) {\r\n if (isEnabled === true) {\r\n errors.push({\r\n code: 'EU_PROHIBITED_PRACTICE',\r\n message: `Under Article 5 of the EU AI Act, the practice '${practice}' is prohibited. Execution blocked.`\r\n });\r\n }\r\n }\r\n }\r\n\r\n // Rule B (Law Enforcement Escalation): Auto-classify high-risk domains\r\n if (\r\n manifest.systemDomain === 'law-enforcement' ||\r\n manifest.systemDomain === 'biometrics' ||\r\n manifest.systemDomain === 'education' ||\r\n manifest.systemDomain === 'employment' ||\r\n manifest.systemDomain === 'critical-infrastructure' ||\r\n manifest.systemDomain === 'essential-services' ||\r\n manifest.systemDomain === 'migration' ||\r\n manifest.systemDomain === 'justice'\r\n ) {\r\n if (manifest.euRiskCategory === 'minimal' || manifest.euRiskCategory === 'limited') {\r\n errors.push({\r\n code: 'EU_RISK_MISCLASSIFICATION',\r\n message: `Under Annex III of the EU AI Act, ${manifest.systemDomain} systems are automatically high-risk. Cannot be classified as ${manifest.euRiskCategory}.`\r\n });\r\n }\r\n }\r\n\r\n // Rule C (Conditional Traceability): High-Risk requires strict logging\r\n if (manifest.euRiskCategory === 'high') {\r\n const hasSession = (payload && payload.sessionId !== undefined) || headers.has('sessionId') || headers.has('x-sessionId');\r\n const hasUserId = (payload && payload.userId !== undefined) || headers.has('userId') || headers.has('x-userId');\r\n const hasPurpose = (payload && payload.purpose !== undefined) || headers.has('purpose') || headers.has('x-purpose');\r\n\r\n // We can check if these are in requiredMetadata or explicitly check payload/headers\r\n // Assuming 'sessionId' and 'userId' or 'purpose' are representative traces.\r\n // The instructions say: \"If missing, push an error explaining that High-Risk systems require strict logging\"\r\n // Let's enforce that at least some core traces exist or check requiredMetadata.\r\n // Given the prompt: \"strictly verify that the payload or headers contain traceability metadata (e.g., sessionId, userId). If missing...\"\r\n if (!hasSession && !hasUserId && !hasPurpose) {\r\n errors.push({\r\n code: 'MISSING_HIGH_RISK_TRACEABILITY',\r\n message: 'Under the EU AI Act, High-Risk systems require strict logging traceability. Missing metadata like sessionId, userId, or purpose.'\r\n });\r\n }\r\n }\r\n\r\n return {\r\n valid: errors.length === 0,\r\n errors\r\n };\r\n}\r\n","import { AiManifest } from '../types';\r\n\r\nconst STANDARD_PII_REGEX = [\r\n // Emails\r\n /[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}/g,\r\n // US SSN\r\n /\\b\\d{3}-\\d{2}-\\d{4}\\b/g,\r\n // 16-digit Credit Cards (simple)\r\n /\\b(?:\\d[ -]*?){13,16}\\b/g,\r\n // Generic API Keys (e.g., sk-..., AKIA...)\r\n /\\b(sk-[a-zA-Z0-9]{20,}|AKIA[0-9A-Z]{16})\\b/g\r\n];\r\n\r\nexport function scrubPayload(\r\n text: string,\r\n config: AiManifest['piiScrubbing']\r\n): string {\r\n if (config.level === 'off') {\r\n return text;\r\n }\r\n\r\n if (config.level === 'strict') {\r\n throw new Error('NotImplemented: Strict PII scrubbing is reserved for the Attestly backend ML engine.');\r\n }\r\n\r\n let scrubbed = text;\r\n const redactString = config.redactString ?? '[REDACTED]';\r\n\r\n if (config.level === 'standard') {\r\n for (const regex of STANDARD_PII_REGEX) {\r\n scrubbed = scrubbed.replace(regex, redactString);\r\n }\r\n }\r\n\r\n if (config.customRegex && config.customRegex.length > 0) {\r\n for (const pattern of config.customRegex) {\r\n try {\r\n const regex = new RegExp(pattern, 'g');\r\n scrubbed = scrubbed.replace(regex, redactString);\r\n } catch (e) {\r\n // Ignore invalid regex to prevent crashing the edge function\r\n console.warn('Invalid custom regex pattern in PII scrubber:', pattern);\r\n }\r\n }\r\n }\r\n\r\n return scrubbed;\r\n}\r\n\r\nexport function scrubObject(\r\n obj: any,\r\n config: AiManifest['piiScrubbing']\r\n): any {\r\n if (config.level === 'off') return obj;\r\n if (!obj) return obj;\r\n\r\n if (typeof obj === 'string') {\r\n return scrubPayload(obj, config);\r\n }\r\n\r\n if (Array.isArray(obj)) {\r\n return obj.map(item => scrubObject(item, config));\r\n }\r\n\r\n if (typeof obj === 'object') {\r\n const scrubbedObj: any = {};\r\n for (const [key, value] of Object.entries(obj)) {\r\n scrubbedObj[key] = scrubObject(value, config);\r\n }\r\n return scrubbedObj;\r\n }\r\n\r\n return obj;\r\n}\r\n","export interface TelemetryPayload {\r\n id: string;\r\n timestamp: string;\r\n model?: string;\r\n status: 'passed' | 'blocked';\r\n prompt?: string;\r\n scrubbedPrompt?: string;\r\n errors?: { code: string; message: string }[];\r\n}\r\n\r\n/**\r\n * Non-blocking ping to the local Attestly Studio.\r\n * Fails silently if the studio is not running.\r\n */\r\nexport function pingStudio(data: TelemetryPayload) {\r\n // Only attempt to ping if in development environment\r\n if (process.env.NODE_ENV !== 'development') {\r\n return;\r\n }\r\n\r\n // Fire and forget, no await to prevent TTFB degradation\r\n fetch('http://localhost:5050/ingest', {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: JSON.stringify(data),\r\n }).catch(() => {\r\n // Silently ignore if studio isn't running\r\n });\r\n}\r\n","import { AiManifest } from '../types';\r\nimport { createComplianceError } from '../utils/errors';\r\nimport { validateManifest } from '../utils/validator';\r\nimport { scrubObject } from '../utils/scrubber';\r\nimport { pingStudio } from '../utils/telemetry';\r\n\r\nexport type RequestHandler = (req: Request, ...args: any[]) => Promise<Response> | Response;\r\n\r\n/**\r\n * A higher-order function that wraps a standard Web API Request handler\r\n * to enforce AI compliance according to the provided manifest.\r\n */\r\nexport function withAttestlyCompliance(\r\n handler: RequestHandler,\r\n manifest: AiManifest\r\n): RequestHandler {\r\n return async (req: Request, ...args: any[]) => {\r\n let clonedReq = req.clone();\r\n let payload = null;\r\n\r\n try {\r\n if (clonedReq.headers.get('content-type')?.includes('application/json')) {\r\n payload = await clonedReq.json();\r\n }\r\n } catch (e) {\r\n // Ignore if body is not readable/json\r\n }\r\n\r\n if (process.env.NODE_ENV === 'development') {\r\n const { valid, errors } = validateManifest(payload, req.headers, manifest);\r\n\r\n if (!valid) {\r\n pingStudio({\r\n id: Math.random().toString(36).substring(7),\r\n timestamp: new Date().toISOString(),\r\n model: payload?.model,\r\n status: 'blocked',\r\n prompt: payload ? JSON.stringify(payload, null, 2) : 'No JSON body',\r\n errors\r\n });\r\n return createComplianceError(errors);\r\n }\r\n }\r\n\r\n if (payload && manifest.piiScrubbing.level !== 'off') {\r\n const scrubbedPayload = scrubObject(payload, manifest.piiScrubbing);\r\n \r\n pingStudio({\r\n id: Math.random().toString(36).substring(7),\r\n timestamp: new Date().toISOString(),\r\n model: payload?.model,\r\n status: 'passed',\r\n prompt: JSON.stringify(payload, null, 2),\r\n scrubbedPrompt: JSON.stringify(scrubbedPayload, null, 2)\r\n });\r\n\r\n const newReq = new Request(req.url, {\r\n method: req.method,\r\n headers: req.headers,\r\n body: JSON.stringify(scrubbedPayload),\r\n // @ts-ignore\r\n duplex: 'half'\r\n });\r\n return handler(newReq, ...args);\r\n }\r\n\r\n pingStudio({\r\n id: Math.random().toString(36).substring(7),\r\n timestamp: new Date().toISOString(),\r\n model: payload?.model,\r\n status: 'passed',\r\n prompt: payload ? JSON.stringify(payload, null, 2) : 'No JSON body'\r\n });\r\n\r\n return handler(req, ...args);\r\n };\r\n}\r\n","import { AiManifest } from '../types';\r\nimport { validateManifest } from '../utils/validator';\r\nimport { scrubObject } from '../utils/scrubber';\r\nimport { pingStudio } from '../utils/telemetry';\r\n\r\nexport type StreamHandler = (req: Request, ...args: any[]) => Promise<Response> | Response;\r\n\r\n/**\r\n * A specialized wrapper for the Vercel AI SDK streams.\r\n * Performs a synchronous compliance check upfront and returns the native stream untouched.\r\n */\r\nexport function withAttestlyStream(\r\n handler: StreamHandler,\r\n manifest: AiManifest\r\n): StreamHandler {\r\n return async (req: Request, ...args: any[]) => {\r\n let clonedReq = req.clone();\r\n let payload = null;\r\n\r\n try {\r\n if (clonedReq.headers.get('content-type')?.includes('application/json')) {\r\n payload = await clonedReq.json();\r\n }\r\n } catch (e) {\r\n // Ignore if body is not readable/json\r\n }\r\n\r\n if (process.env.NODE_ENV === 'development') {\r\n const { valid, errors } = validateManifest(payload, req.headers, manifest);\r\n\r\n if (!valid) {\r\n pingStudio({\r\n id: Math.random().toString(36).substring(7),\r\n timestamp: new Date().toISOString(),\r\n model: payload?.model,\r\n status: 'blocked',\r\n prompt: JSON.stringify(payload, null, 2),\r\n errors\r\n });\r\n const errorMessages = errors.map(e => `[${e.code}] ${e.message}`).join('\\n');\r\n throw new Error(`Attestly Compliance Stream Check Failed: \\n${errorMessages}`);\r\n }\r\n }\r\n\r\n if (payload && manifest.piiScrubbing.level !== 'off') {\r\n const scrubbedPayload = scrubObject(payload, manifest.piiScrubbing);\r\n \r\n pingStudio({\r\n id: Math.random().toString(36).substring(7),\r\n timestamp: new Date().toISOString(),\r\n model: payload?.model,\r\n status: 'passed',\r\n prompt: JSON.stringify(payload, null, 2),\r\n scrubbedPrompt: JSON.stringify(scrubbedPayload, null, 2)\r\n });\r\n\r\n const newReq = new Request(req.url, {\r\n method: req.method,\r\n headers: req.headers,\r\n body: JSON.stringify(scrubbedPayload),\r\n // @ts-ignore\r\n duplex: 'half'\r\n });\r\n return handler(newReq, ...args);\r\n }\r\n\r\n pingStudio({\r\n id: Math.random().toString(36).substring(7),\r\n timestamp: new Date().toISOString(),\r\n model: payload?.model,\r\n status: 'passed',\r\n prompt: payload ? JSON.stringify(payload, null, 2) : 'No JSON body'\r\n });\r\n\r\n // Pass through the DataStreamResponse untouched\r\n return handler(req, ...args);\r\n };\r\n}\r\n"]}
|