@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 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 or Biometrics), and strictly enforces required metadata traceability according to EU regulations.
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 - some environments require duplex for streaming requests
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 - some environments require duplex for streaming requests
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
  }
@@ -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"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@attestly/compliance-core",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Zero-latency, Web Standard-based SDK that enforces AI compliance",
5
5
  "license": "MIT",
6
6
  "main": "./dist/index.js",