@volidator/node 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,663 @@
1
+ // src/index.ts
2
+ var VolidatorClient = class _VolidatorClient {
3
+ constructor(config) {
4
+ this.apiKey = config.apiKey;
5
+ this.endpoint = config.endpoint || "https://ingestion.volidator.com";
6
+ this.projectId = config.projectId;
7
+ this.clientSecret = config.clientSecret;
8
+ this.redactKeys = config.redactKeys || [];
9
+ this.referenceKeys = config.referenceKeys || [];
10
+ this.maxMetadataSize = config.maxMetadataSize || 10240;
11
+ if (config.keyring && config.activeEncryptionKeyId) {
12
+ this.keyring = config.keyring;
13
+ this.activeKeyId = config.activeEncryptionKeyId;
14
+ } else if (config.encryptionKey) {
15
+ this.activeKeyId = "v1";
16
+ this.keyring = { "v1": config.encryptionKey };
17
+ } else {
18
+ throw new Error("Either encryptionKey OR (keyring AND activeEncryptionKeyId) must be provided in VolidatorClient constructor.");
19
+ }
20
+ if (Object.keys(this.keyring).length > 5) {
21
+ throw new Error("Keyring size cannot exceed 5 keys.");
22
+ }
23
+ if (!this.keyring[this.activeKeyId]) {
24
+ throw new Error(`Active key ID '${this.activeKeyId}' must exist in the keyring.`);
25
+ }
26
+ this.hashedKeyring = {};
27
+ this.telemetryConfig = _VolidatorClient.resolveTelemetryConfig(config.telemetry || { preset: "standard" });
28
+ this.compliance = new VolidatorCompliance(this);
29
+ this.agent = new VolidatorAgent(this);
30
+ }
31
+ // ---------------------------------------------------------------------------
32
+ // Telemetry Context Parser (Zero-latency header parsing)
33
+ // ---------------------------------------------------------------------------
34
+ static extractContext(req) {
35
+ const getHeader = (name) => {
36
+ if (!req) return "";
37
+ if (typeof req.headers?.get === "function") {
38
+ return req.headers.get(name) || "";
39
+ }
40
+ if (req.headers && typeof req.headers === "object") {
41
+ return req.headers[name] || req.headers[name.toLowerCase()] || "";
42
+ }
43
+ return "";
44
+ };
45
+ const rawIp = getHeader("cf-connecting-ip") || getHeader("x-real-ip") || getHeader("x-forwarded-for");
46
+ const ip = rawIp ? rawIp.split(",")[0].trim() : req?.socket?.remoteAddress || "";
47
+ const userAgent = getHeader("user-agent");
48
+ return {
49
+ ip,
50
+ userAgent,
51
+ location: {
52
+ country: getHeader("cf-ipcountry") || getHeader("x-vercel-ip-country") || "",
53
+ region: getHeader("cf-region-code") || getHeader("x-vercel-ip-country-region") || "",
54
+ city: getHeader("cf-ipcity") || getHeader("x-vercel-ip-city") || ""
55
+ }
56
+ };
57
+ }
58
+ // ---------------------------------------------------------------------------
59
+ // OpenTelemetry W3C traceparent context parser
60
+ // ---------------------------------------------------------------------------
61
+ static extractTraceContext(req) {
62
+ if (!req) return {};
63
+ const getHeader = (name) => {
64
+ if (typeof req.headers?.get === "function") {
65
+ return req.headers.get(name) || "";
66
+ }
67
+ if (req.headers && typeof req.headers === "object") {
68
+ return req.headers[name] || req.headers[name.toLowerCase()] || "";
69
+ }
70
+ return "";
71
+ };
72
+ const traceparent = getHeader("traceparent");
73
+ if (!traceparent) return {};
74
+ const parts = traceparent.split("-");
75
+ if (parts.length !== 4) return {};
76
+ return { traceId: parts[1], spanId: parts[2] };
77
+ }
78
+ static resolveTelemetryConfig(config) {
79
+ const preset = config.preset || "standard";
80
+ let ip = "anonymize";
81
+ let userAgent = "parse";
82
+ let location = true;
83
+ if (preset === "strict") {
84
+ ip = "skip";
85
+ userAgent = "skip";
86
+ location = false;
87
+ } else if (preset === "full") {
88
+ ip = "track";
89
+ userAgent = "track";
90
+ location = true;
91
+ }
92
+ if (config.ip !== void 0) ip = config.ip;
93
+ if (config.userAgent !== void 0) userAgent = config.userAgent;
94
+ if (config.location !== void 0) location = config.location;
95
+ return { ip, userAgent, location };
96
+ }
97
+ parseUserAgent(ua) {
98
+ let browser = "Unknown Browser";
99
+ let os = "Unknown OS";
100
+ let type = "Desktop";
101
+ if (/Volidator-Ingest-Worker/i.test(ua)) {
102
+ return {
103
+ browser: "Volidator Ingest Worker",
104
+ os: "Linux Server",
105
+ type: "Server"
106
+ };
107
+ }
108
+ if (/mobile/i.test(ua)) {
109
+ type = "Mobile";
110
+ } else if (/tablet|ipad/i.test(ua)) {
111
+ type = "Tablet";
112
+ } else if (/server|bot/i.test(ua)) {
113
+ type = "Server";
114
+ }
115
+ if (/chrome|crios/i.test(ua) && !/edge|edg/i.test(ua) && !/opr/i.test(ua)) {
116
+ const match = ua.match(/(?:chrome|crios)\/([0-9\.]+)/i);
117
+ browser = `Chrome ${match ? match[1].split(".")[0] : ""}`.trim();
118
+ } else if (/safari/i.test(ua) && !/chrome|crios/i.test(ua) && !/android/i.test(ua)) {
119
+ browser = /mobile/i.test(ua) ? "Safari Mobile" : "Safari";
120
+ } else if (/firefox|fxios/i.test(ua)) {
121
+ browser = "Firefox";
122
+ } else if (/edge|edg/i.test(ua)) {
123
+ browser = "Edge";
124
+ }
125
+ if (/windows/i.test(ua)) {
126
+ os = "Windows";
127
+ } else if (/macintosh|mac os x/i.test(ua)) {
128
+ os = "macOS";
129
+ } else if (/iphone|ipad|ipod/i.test(ua)) {
130
+ os = "iOS";
131
+ } else if (/android/i.test(ua)) {
132
+ os = "Android";
133
+ } else if (/linux/i.test(ua)) {
134
+ os = "Linux";
135
+ }
136
+ return { browser, os, type };
137
+ }
138
+ // ---------------------------------------------------------------------------
139
+ // Blind Index & Key Hashing (WebCrypto)
140
+ // ---------------------------------------------------------------------------
141
+ async _getHashedKey(id) {
142
+ if (this.hashedKeyring[id]) return this.hashedKeyring[id];
143
+ const rawKey = this.keyring[id];
144
+ const buf = new TextEncoder().encode(rawKey);
145
+ const hash = await globalThis.crypto.subtle.digest("SHA-256", buf);
146
+ this.hashedKeyring[id] = new Uint8Array(hash);
147
+ return this.hashedKeyring[id];
148
+ }
149
+ async generateBlindIndex(value, keyBuffer) {
150
+ const key = await globalThis.crypto.subtle.importKey(
151
+ "raw",
152
+ keyBuffer,
153
+ { name: "HMAC", hash: "SHA-256" },
154
+ false,
155
+ ["sign"]
156
+ );
157
+ const signature = await globalThis.crypto.subtle.sign(
158
+ "HMAC",
159
+ key,
160
+ new TextEncoder().encode(value)
161
+ );
162
+ return Array.from(new Uint8Array(signature)).map((b) => b.toString(16).padStart(2, "0")).join("");
163
+ }
164
+ // ---------------------------------------------------------------------------
165
+ // Payload Encryption — AES-256-GCM with prepended 12-byte IV
166
+ // Layout: [IV (12B)] [Ciphertext] [Auth Tag (16B)]
167
+ // ---------------------------------------------------------------------------
168
+ async encryptPayload(payload) {
169
+ const text = JSON.stringify(payload);
170
+ const iv = globalThis.crypto.getRandomValues(new Uint8Array(12));
171
+ const activeHashedKey = await this._getHashedKey(this.activeKeyId);
172
+ const cryptoKey = await globalThis.crypto.subtle.importKey(
173
+ "raw",
174
+ activeHashedKey,
175
+ { name: "AES-GCM" },
176
+ false,
177
+ ["encrypt"]
178
+ );
179
+ const encryptedBuffer = await globalThis.crypto.subtle.encrypt(
180
+ { name: "AES-GCM", iv },
181
+ cryptoKey,
182
+ new TextEncoder().encode(text)
183
+ );
184
+ const finalBuffer = new Uint8Array(iv.length + encryptedBuffer.byteLength);
185
+ finalBuffer.set(iv, 0);
186
+ finalBuffer.set(new Uint8Array(encryptedBuffer), iv.length);
187
+ let binary = "";
188
+ const len = finalBuffer.byteLength;
189
+ for (let i = 0; i < len; i++) {
190
+ binary += String.fromCharCode(finalBuffer[i]);
191
+ }
192
+ const base64 = btoa(binary);
193
+ return `${this.activeKeyId}:${base64}`;
194
+ }
195
+ // ---------------------------------------------------------------------------
196
+ // prepareLogEntry() — Prepares, redacts, and encrypts a single log entry payload
197
+ // ---------------------------------------------------------------------------
198
+ async prepareLogEntry(payload, maxMetaOverride) {
199
+ const actorRaw = payload.actor || payload.actorId || "unknown";
200
+ const targetRaw = payload.target || payload.targetId || "unknown";
201
+ const tenantRaw = payload.tenant || payload.tenantId || "";
202
+ const truncate = (str, maxLen) => {
203
+ if (str.length > maxLen) {
204
+ return str.slice(0, maxLen) + "...";
205
+ }
206
+ return str;
207
+ };
208
+ const extractPii = (v) => typeof v === "object" ? v.pii : v;
209
+ const extractId = (v) => typeof v === "object" ? v.id : v;
210
+ const actor = truncate(extractPii(actorRaw), 255);
211
+ const target = truncate(extractPii(targetRaw), 255);
212
+ const tenant = tenantRaw ? truncate(extractPii(tenantRaw), 255) : "";
213
+ const action = truncate(payload.action, 255);
214
+ const logTelemetry = payload.telemetry ? _VolidatorClient.resolveTelemetryConfig({ ...this.telemetryConfig, ...payload.telemetry }) : this.telemetryConfig;
215
+ let payloadCtx = payload.context || {};
216
+ if (payload.req) {
217
+ const extracted = _VolidatorClient.extractContext(payload.req);
218
+ payloadCtx = {
219
+ ...extracted,
220
+ ...payloadCtx,
221
+ location: {
222
+ ...extracted.location,
223
+ ...payloadCtx.location
224
+ },
225
+ device: {
226
+ ...extracted.device,
227
+ ...payloadCtx.device
228
+ }
229
+ };
230
+ }
231
+ const context = {};
232
+ const rawIp = payloadCtx.ip || "";
233
+ const rawUa = payloadCtx.userAgent || "";
234
+ if (logTelemetry.location) {
235
+ context.location = {};
236
+ if (payloadCtx.location) {
237
+ context.location.country = payloadCtx.location.country || "";
238
+ context.location.region = payloadCtx.location.region || "";
239
+ const isFull = payload.telemetry?.preset === "full" || !payload.telemetry && this.telemetryConfig.ip === "track";
240
+ if (logTelemetry.ip === "track" || isFull) {
241
+ context.location.city = payloadCtx.location.city || "";
242
+ }
243
+ }
244
+ }
245
+ if (logTelemetry.ip === "anonymize" && rawIp) {
246
+ context.ip = await this.generateBlindIndex(rawIp, await this._getHashedKey(this.activeKeyId));
247
+ } else if (logTelemetry.ip === "track" && rawIp) {
248
+ context.ip = rawIp;
249
+ }
250
+ if (logTelemetry.userAgent !== "skip") {
251
+ if (rawUa) {
252
+ context.device = this.parseUserAgent(rawUa);
253
+ if (logTelemetry.userAgent === "track") {
254
+ context.userAgent = truncate(rawUa, 1e3);
255
+ }
256
+ } else if (payloadCtx.device) {
257
+ context.device = payloadCtx.device;
258
+ }
259
+ }
260
+ const scrub = (value, key) => this.redactKeys.includes(key) ? `[REDACTED:${key}]` : value;
261
+ const applyRef = (rawValue, key) => {
262
+ if (this.referenceKeys.includes(key)) {
263
+ const id = extractId(rawValue);
264
+ return `[REF:${id}]`;
265
+ }
266
+ return scrub(extractPii(rawValue), key);
267
+ };
268
+ const rawMetadata = payload.metadata || {};
269
+ const safeMetadata = {};
270
+ for (const [k, v] of Object.entries(rawMetadata)) {
271
+ const metaKey = `metadata.${k}`;
272
+ if (this.referenceKeys.includes(metaKey) && typeof v === "object" && v !== null && "id" in v) {
273
+ safeMetadata[k] = `[REF:${v.id}]`;
274
+ } else if (this.redactKeys.includes(metaKey) && typeof v === "string") {
275
+ safeMetadata[k] = `[REDACTED:${k}]`;
276
+ } else {
277
+ safeMetadata[k] = v;
278
+ }
279
+ }
280
+ const limitDepth = (obj, currentDepth = 1) => {
281
+ if (currentDepth > 5) {
282
+ return "[Truncated - Depth Exceeded]";
283
+ }
284
+ if (typeof obj !== "object" || obj === null) {
285
+ if (typeof obj === "string") {
286
+ return truncate(obj, 1e3);
287
+ }
288
+ return obj;
289
+ }
290
+ if (Array.isArray(obj)) {
291
+ return obj.map((item) => limitDepth(item, currentDepth + 1));
292
+ }
293
+ const result = {};
294
+ for (const [key, val] of Object.entries(obj)) {
295
+ result[key] = limitDepth(val, currentDepth + 1);
296
+ }
297
+ return result;
298
+ };
299
+ const processedMetadata = limitDepth(safeMetadata);
300
+ const serializedMeta = JSON.stringify(processedMetadata);
301
+ const maxMetaLimit = maxMetaOverride || this.maxMetadataSize;
302
+ if (serializedMeta.length > maxMetaLimit) {
303
+ throw new Error(`Metadata size exceeds maximum allowed limit of ${maxMetaLimit / 1024}KB.`);
304
+ }
305
+ const safeActor = applyRef(actorRaw, "actor");
306
+ const safeTarget = applyRef(targetRaw, "target");
307
+ const safeTenant = tenantRaw ? applyRef(tenantRaw, "tenant") : void 0;
308
+ const enrichedPayload = {
309
+ actor: safeActor,
310
+ action,
311
+ target: safeTarget,
312
+ metadata: processedMetadata
313
+ };
314
+ if (safeTenant) {
315
+ enrichedPayload.tenant = safeTenant;
316
+ }
317
+ if (Object.keys(context).length > 0) {
318
+ enrichedPayload.context = context;
319
+ }
320
+ let traceId = payload.traceId;
321
+ let spanId = payload.spanId;
322
+ let parentSpanId = payload.parentSpanId;
323
+ if (payload.req) {
324
+ const extractedTrace = _VolidatorClient.extractTraceContext(payload.req);
325
+ if (!traceId && extractedTrace.traceId) {
326
+ traceId = extractedTrace.traceId;
327
+ }
328
+ if (!spanId && extractedTrace.spanId) {
329
+ spanId = extractedTrace.spanId;
330
+ }
331
+ }
332
+ if (spanId) {
333
+ enrichedPayload.spanId = spanId;
334
+ }
335
+ if (parentSpanId) {
336
+ enrichedPayload.parentSpanId = parentSpanId;
337
+ }
338
+ const activeKeyBuffer = await this._getHashedKey(this.activeKeyId);
339
+ const actorBlindIndex = await this.generateBlindIndex(actor, activeKeyBuffer);
340
+ const actionBlindIndex = await this.generateBlindIndex(action, activeKeyBuffer);
341
+ const targetBlindIndex = await this.generateBlindIndex(target, activeKeyBuffer);
342
+ const tenantBlindIndex = tenant ? await this.generateBlindIndex(tenant, activeKeyBuffer) : void 0;
343
+ const traceBlindIndex = traceId ? await this.generateBlindIndex(traceId, activeKeyBuffer) : void 0;
344
+ const encryptedPayload = await this.encryptPayload(enrichedPayload);
345
+ return {
346
+ actorBlindIndex,
347
+ actionBlindIndex,
348
+ targetBlindIndex,
349
+ tenantBlindIndex,
350
+ traceBlindIndex,
351
+ encryptedPayload
352
+ };
353
+ }
354
+ // ---------------------------------------------------------------------------
355
+ // log() — Encrypt and ingest a single audit event
356
+ // ---------------------------------------------------------------------------
357
+ async log(payload, maxMetaOverride) {
358
+ const entry = await this.prepareLogEntry(payload, maxMetaOverride);
359
+ try {
360
+ const res = await fetch(`${this.endpoint}/v1/log`, {
361
+ method: "POST",
362
+ headers: {
363
+ Authorization: `Bearer ${this.apiKey}`,
364
+ "Content-Type": "application/json"
365
+ },
366
+ body: JSON.stringify(entry)
367
+ });
368
+ return res.ok;
369
+ } catch (err) {
370
+ console.error(`[Volidator] Failed to send log: ${err.message}`);
371
+ return false;
372
+ }
373
+ }
374
+ // ---------------------------------------------------------------------------
375
+ // logBatch() — Bulk encrypt and ingest a batch of up to 100 audit events
376
+ // ---------------------------------------------------------------------------
377
+ async logBatch(payloads, maxMetaOverride) {
378
+ if (!Array.isArray(payloads) || payloads.length === 0) {
379
+ return { accepted: 0, rejected: 0 };
380
+ }
381
+ const batch = payloads.slice(0, 100);
382
+ const preparedEntries = [];
383
+ let rejected = payloads.length - batch.length;
384
+ const results = await Promise.allSettled(
385
+ batch.map((p) => this.prepareLogEntry(p, maxMetaOverride))
386
+ );
387
+ for (const res of results) {
388
+ if (res.status === "fulfilled") {
389
+ preparedEntries.push(res.value);
390
+ } else {
391
+ rejected++;
392
+ console.error(`[Volidator] Failed to prepare batch entry: ${res.reason?.message}`);
393
+ }
394
+ }
395
+ if (preparedEntries.length === 0) {
396
+ return { accepted: 0, rejected };
397
+ }
398
+ try {
399
+ const res = await fetch(`${this.endpoint}/v1/logs/batch`, {
400
+ method: "POST",
401
+ headers: {
402
+ Authorization: `Bearer ${this.apiKey}`,
403
+ "Content-Type": "application/json"
404
+ },
405
+ body: JSON.stringify({ logs: preparedEntries })
406
+ });
407
+ if (res.ok) {
408
+ return { accepted: preparedEntries.length, rejected };
409
+ } else {
410
+ console.error(`[Volidator] Batch ingestion endpoint returned status: ${res.status}`);
411
+ return { accepted: 0, rejected: rejected + preparedEntries.length };
412
+ }
413
+ } catch (err) {
414
+ console.error(`[Volidator] Failed to send log batch: ${err.message}`);
415
+ return { accepted: 0, rejected: rejected + preparedEntries.length };
416
+ }
417
+ }
418
+ // ---------------------------------------------------------------------------
419
+ // generateEmbedToken() — Sign a HS256 JWT for the embeddable dashboard widget
420
+ // ---------------------------------------------------------------------------
421
+ async generateEmbedToken(config) {
422
+ if (!this.projectId || !this.clientSecret) {
423
+ throw new Error(
424
+ "generateEmbedToken() requires projectId and clientSecret in the VolidatorClient constructor."
425
+ );
426
+ }
427
+ const {
428
+ actorId,
429
+ targetId,
430
+ tenantId,
431
+ scope,
432
+ expiresIn = "2h",
433
+ dashboardUrl = "https://dash.volidator.com",
434
+ hostOrigin,
435
+ view
436
+ } = config;
437
+ const defaultScope = scope || (tenantId ? "tenant" : "actor");
438
+ if (defaultScope !== "auditor" && !actorId && !targetId && !tenantId) {
439
+ throw new Error(
440
+ "At least one of actorId, targetId, or tenantId must be provided to generateEmbedToken()."
441
+ );
442
+ }
443
+ const actorBlindIndexes = actorId ? await Promise.all(Object.keys(this.keyring).map(async (id) => this.generateBlindIndex(actorId, await this._getHashedKey(id)))) : void 0;
444
+ const targetBlindIndexes = targetId ? await Promise.all(Object.keys(this.keyring).map(async (id) => this.generateBlindIndex(targetId, await this._getHashedKey(id)))) : void 0;
445
+ const tenantBlindIndexes = tenantId ? await Promise.all(Object.keys(this.keyring).map(async (id) => this.generateBlindIndex(tenantId, await this._getHashedKey(id)))) : void 0;
446
+ const parsedExpiry = this.parseExpiry(expiresIn);
447
+ const maxExpiry = defaultScope === "auditor" ? 604800 : 3600;
448
+ const expiresInSeconds = Math.min(parsedExpiry, maxExpiry);
449
+ const now = Math.floor(Date.now() / 1e3);
450
+ const payload = {
451
+ pid: this.projectId,
452
+ scope: defaultScope,
453
+ iat: now,
454
+ exp: now + expiresInSeconds
455
+ };
456
+ if (actorBlindIndexes) payload.abi = actorBlindIndexes;
457
+ if (targetBlindIndexes) payload.tgb = targetBlindIndexes;
458
+ if (tenantBlindIndexes) payload.tbi = tenantBlindIndexes;
459
+ if (view) {
460
+ const compressed = {};
461
+ if (Array.isArray(view.columns)) {
462
+ compressed.cols = view.columns.map((col) => {
463
+ if (col === "createdAt") return "cat";
464
+ if (col === "actor") return "act";
465
+ if (col === "action") return "acn";
466
+ if (col === "target") return "tgt";
467
+ if (col.startsWith("metadata.")) {
468
+ return `m.${col.slice(9)}`;
469
+ }
470
+ return col;
471
+ });
472
+ }
473
+ if (view.defaultFilter) {
474
+ compressed.flt = {};
475
+ if (view.defaultFilter.search !== void 0) {
476
+ compressed.flt.q = view.defaultFilter.search;
477
+ }
478
+ if (view.defaultFilter.action !== void 0) {
479
+ compressed.flt.act = view.defaultFilter.action;
480
+ }
481
+ }
482
+ payload.view = compressed;
483
+ }
484
+ const token = await this.signHS256JWT(payload, this.clientSecret);
485
+ const keyringString = Object.entries(this.keyring).map(([id, key]) => `${id}:${key}`).join(",");
486
+ const hostParam = hostOrigin ? `?host=${encodeURIComponent(hostOrigin)}` : "";
487
+ const embedUrl = `${dashboardUrl}/embed/${token}${hostParam}#${keyringString}`;
488
+ return { token, embedUrl };
489
+ }
490
+ async signHS256JWT(payload, secret) {
491
+ const header = { alg: "HS256", typ: "JWT" };
492
+ const encode = (obj) => btoa(JSON.stringify(obj)).replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
493
+ const unsigned = `${encode(header)}.${encode(payload)}`;
494
+ const key = await globalThis.crypto.subtle.importKey(
495
+ "raw",
496
+ new TextEncoder().encode(secret),
497
+ { name: "HMAC", hash: "SHA-256" },
498
+ false,
499
+ ["sign"]
500
+ );
501
+ const signatureBuffer = await globalThis.crypto.subtle.sign(
502
+ "HMAC",
503
+ key,
504
+ new TextEncoder().encode(unsigned)
505
+ );
506
+ let binary = "";
507
+ const arr = new Uint8Array(signatureBuffer);
508
+ for (let i = 0; i < arr.byteLength; i++) {
509
+ binary += String.fromCharCode(arr[i]);
510
+ }
511
+ const signature = btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
512
+ return `${unsigned}.${signature}`;
513
+ }
514
+ parseExpiry(expiry) {
515
+ const match = expiry.match(/^(\d+)(s|m|h|d)$/);
516
+ if (!match) return 7200;
517
+ const [, num, unit] = match;
518
+ const n = parseInt(num, 10);
519
+ switch (unit) {
520
+ case "s":
521
+ return n;
522
+ case "m":
523
+ return n * 60;
524
+ case "h":
525
+ return n * 3600;
526
+ case "d":
527
+ return n * 86400;
528
+ default:
529
+ return 7200;
530
+ }
531
+ }
532
+ };
533
+ var VolidatorCompliance = class {
534
+ constructor(client) {
535
+ this.client = client;
536
+ }
537
+ async logWithControl(action, soc2Control, isoControl, payload) {
538
+ const metadata = {
539
+ ...payload.metadata,
540
+ soc2_control: soc2Control,
541
+ iso27001: isoControl
542
+ };
543
+ return this.client.log({
544
+ ...payload,
545
+ action,
546
+ metadata
547
+ });
548
+ }
549
+ async accessRevoked(payload) {
550
+ return this.logWithControl("access.revoked", "CC6.1", "A.9.2.6", payload);
551
+ }
552
+ async accessGranted(payload) {
553
+ return this.logWithControl("access.granted", "CC6.1", "A.9.2.1", payload);
554
+ }
555
+ async dataExported(payload) {
556
+ return this.logWithControl("data.exported", "CC6.6", "A.12.4.1", payload);
557
+ }
558
+ async systemConfigChanged(payload) {
559
+ return this.logWithControl("system.config_changed", "CC6.2", "A.12.1.2", payload);
560
+ }
561
+ async mfaEnabled(payload) {
562
+ return this.logWithControl("mfa.enabled", "CC6.3", "A.9.4.2", payload);
563
+ }
564
+ };
565
+ var VolidatorAgent = class {
566
+ constructor(client) {
567
+ this.client = client;
568
+ }
569
+ async logAgent(action, euAiAct, nistAiRmf, soc2Control, isoControl, payload) {
570
+ const {
571
+ actor,
572
+ actorId,
573
+ target,
574
+ targetId,
575
+ tenant,
576
+ tenantId,
577
+ metadata,
578
+ context,
579
+ telemetry,
580
+ req,
581
+ traceId,
582
+ spanId,
583
+ parentSpanId,
584
+ ...agentData
585
+ } = payload;
586
+ const enrichedMetadata = {
587
+ ...metadata,
588
+ ...agentData,
589
+ eu_ai_act: euAiAct,
590
+ nist_ai_rmf: nistAiRmf,
591
+ soc2_control: soc2Control,
592
+ iso27001: isoControl
593
+ };
594
+ return this.client.log(
595
+ {
596
+ actor,
597
+ actorId,
598
+ target,
599
+ targetId,
600
+ tenant,
601
+ tenantId,
602
+ action,
603
+ metadata: enrichedMetadata,
604
+ context,
605
+ telemetry,
606
+ req,
607
+ traceId,
608
+ spanId,
609
+ parentSpanId
610
+ },
611
+ 65536
612
+ );
613
+ }
614
+ /**
615
+ * Logs a tool call or external API invocation by an agent.
616
+ * Maps to EU AI Act Article 12, NIST AI RMF MANAGE 2.2, SOC2 CC6.6, ISO 27001 A.12.4.1.
617
+ */
618
+ async toolCall(payload) {
619
+ return this.logAgent("agent.tool_call", "Article 12", "MANAGE 2.2", "CC6.6", "A.12.4.1", payload);
620
+ }
621
+ /**
622
+ * Logs a key decision made autonomously by an AI agent model.
623
+ * Maps to EU AI Act Article 12 & 13, NIST AI RMF GOVERN 1.7, SOC2 CC6.2, ISO 27001 A.18.1.3.
624
+ */
625
+ async decision(payload) {
626
+ return this.logAgent("agent.decision", "Article 12 & 13", "GOVERN 1.7", "CC6.2", "A.18.1.3", payload);
627
+ }
628
+ /**
629
+ * Logs a request for human review or permission escalation.
630
+ * Maps to EU AI Act Article 14, NIST AI RMF GOVERN 5.1, SOC2 CC6.3, ISO 27001 A.6.1.2.
631
+ */
632
+ async escalation(payload) {
633
+ return this.logAgent("agent.escalation", "Article 14", "GOVERN 5.1", "CC6.3", "A.6.1.2", payload);
634
+ }
635
+ /**
636
+ * Logs anomalous environment inputs, potential prompt injections, or policy violations.
637
+ * Maps to EU AI Act Article 9, NIST AI RMF MANAGE 2.4, SOC2 CC7.2, ISO 27001 A.16.1.2.
638
+ */
639
+ async anomaly(payload) {
640
+ return this.logAgent("agent.anomaly", "Article 9", "MANAGE 2.4", "CC7.2", "A.16.1.2", payload);
641
+ }
642
+ /**
643
+ * Logs a model refusal to execute a user prompt or command due to alignment safety.
644
+ * Maps to EU AI Act Article 5, NIST AI RMF GOVERN 1.1, SOC2 CC6.8, ISO 27001 A.18.1.3.
645
+ */
646
+ async refusal(payload) {
647
+ return this.logAgent("agent.refusal", "Article 5", "GOVERN 1.1", "CC6.8", "A.18.1.3", payload);
648
+ }
649
+ /**
650
+ * Logs an execution context handoff from one agent to another.
651
+ * Maps to EU AI Act Article 12, NIST AI RMF MAP 1.6, SOC2 CC6.6, ISO 27001 A.12.4.1.
652
+ */
653
+ async handoff(payload) {
654
+ return this.logAgent("agent.handoff", "Article 12", "MAP 1.6", "CC6.6", "A.12.4.1", payload);
655
+ }
656
+ };
657
+
658
+ export {
659
+ VolidatorClient,
660
+ VolidatorCompliance,
661
+ VolidatorAgent
662
+ };
663
+ //# sourceMappingURL=chunk-YI4DCCHT.js.map