@parmanasystems/audit-db 1.4.0 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +3 -4
- package/dist/index.js +254 -45
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.d.ts
CHANGED
|
@@ -103,19 +103,18 @@ declare class AuditDb {
|
|
|
103
103
|
ping(): Promise<void>;
|
|
104
104
|
disconnect(): Promise<void>;
|
|
105
105
|
migrate(): Promise<void>;
|
|
106
|
-
/** Fire-and-forget — never throws, never delays the caller. */
|
|
107
106
|
recordDecision(attestation: ExecutionAttestation): void;
|
|
108
|
-
/** Fire-and-forget — never throws, never delays the caller. */
|
|
109
107
|
recordVerification(executionId: string, result: VerificationResult): void;
|
|
110
|
-
/** Fire-and-forget — never throws, never delays the caller. */
|
|
111
108
|
recordSecurityEvent(event: SecurityEventInput): void;
|
|
112
|
-
/** Fire-and-forget — never throws, never delays the caller. */
|
|
113
109
|
recordApiAccess(access: ApiAccessInput): void;
|
|
114
110
|
getDecisionTimeline(limit?: number, filter?: DecisionFilter): Promise<DecisionTimelineRow[]>;
|
|
115
111
|
getStats(): Promise<AuditStats>;
|
|
116
112
|
getDecisionById(executionId: string): Promise<AuditDecision | null>;
|
|
117
113
|
getVerificationsByExecution(executionId: string): Promise<AuditVerification[]>;
|
|
118
114
|
getSecurityDashboard(): Promise<SecurityDashboardRow[]>;
|
|
115
|
+
hasExecution(executionId: string): Promise<boolean>;
|
|
116
|
+
storePendingExecution(attestation: ExecutionAttestation): Promise<void>;
|
|
117
|
+
markExecuted(executionId: string): Promise<void>;
|
|
119
118
|
close(): Promise<void>;
|
|
120
119
|
}
|
|
121
120
|
|
package/dist/index.js
CHANGED
|
@@ -124,10 +124,14 @@ async function runMigrations(client) {
|
|
|
124
124
|
var AuditDb = class {
|
|
125
125
|
pool;
|
|
126
126
|
constructor(connectionString) {
|
|
127
|
-
this.pool = new Pool({
|
|
127
|
+
this.pool = new Pool({
|
|
128
|
+
connectionString
|
|
129
|
+
});
|
|
128
130
|
}
|
|
129
131
|
async ping() {
|
|
130
|
-
await this.pool.query(
|
|
132
|
+
await this.pool.query(
|
|
133
|
+
"SELECT 1"
|
|
134
|
+
);
|
|
131
135
|
}
|
|
132
136
|
async disconnect() {
|
|
133
137
|
await this.pool.end();
|
|
@@ -135,19 +139,34 @@ var AuditDb = class {
|
|
|
135
139
|
async migrate() {
|
|
136
140
|
const client = await this.pool.connect();
|
|
137
141
|
try {
|
|
138
|
-
await runMigrations(
|
|
142
|
+
await runMigrations(
|
|
143
|
+
client
|
|
144
|
+
);
|
|
139
145
|
} finally {
|
|
140
146
|
client.release();
|
|
141
147
|
}
|
|
142
148
|
}
|
|
143
|
-
|
|
149
|
+
// ----------------------------------
|
|
150
|
+
// Decision recording
|
|
151
|
+
// ----------------------------------
|
|
144
152
|
recordDecision(attestation) {
|
|
145
153
|
this.pool.query(
|
|
146
|
-
`
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
154
|
+
`
|
|
155
|
+
INSERT INTO audit_decisions
|
|
156
|
+
(
|
|
157
|
+
execution_id,
|
|
158
|
+
decision,
|
|
159
|
+
execution_state,
|
|
160
|
+
runtime_hash,
|
|
161
|
+
signature,
|
|
162
|
+
attestation,
|
|
163
|
+
executed_at
|
|
164
|
+
)
|
|
165
|
+
VALUES ($1,$2,$3,$4,$5,$6,$7)
|
|
166
|
+
|
|
167
|
+
ON CONFLICT (execution_id)
|
|
168
|
+
DO NOTHING
|
|
169
|
+
`,
|
|
151
170
|
[
|
|
152
171
|
attestation.execution_id,
|
|
153
172
|
attestation.decision,
|
|
@@ -159,12 +178,22 @@ var AuditDb = class {
|
|
|
159
178
|
]
|
|
160
179
|
).catch(() => void 0);
|
|
161
180
|
}
|
|
162
|
-
|
|
181
|
+
// ----------------------------------
|
|
182
|
+
// Verification recording
|
|
183
|
+
// ----------------------------------
|
|
163
184
|
recordVerification(executionId, result) {
|
|
164
185
|
this.pool.query(
|
|
165
|
-
`
|
|
166
|
-
|
|
167
|
-
|
|
186
|
+
`
|
|
187
|
+
INSERT INTO audit_verifications
|
|
188
|
+
(
|
|
189
|
+
execution_id,
|
|
190
|
+
valid,
|
|
191
|
+
signature_verified,
|
|
192
|
+
runtime_verified,
|
|
193
|
+
schema_compatible
|
|
194
|
+
)
|
|
195
|
+
VALUES ($1,$2,$3,$4,$5)
|
|
196
|
+
`,
|
|
168
197
|
[
|
|
169
198
|
executionId,
|
|
170
199
|
result.valid,
|
|
@@ -174,12 +203,24 @@ var AuditDb = class {
|
|
|
174
203
|
]
|
|
175
204
|
).catch(() => void 0);
|
|
176
205
|
}
|
|
177
|
-
|
|
206
|
+
// ----------------------------------
|
|
207
|
+
// Security event recording
|
|
208
|
+
// ----------------------------------
|
|
178
209
|
recordSecurityEvent(event) {
|
|
179
210
|
this.pool.query(
|
|
180
|
-
`
|
|
181
|
-
|
|
182
|
-
|
|
211
|
+
`
|
|
212
|
+
INSERT INTO audit_security_events
|
|
213
|
+
(
|
|
214
|
+
event_type,
|
|
215
|
+
severity,
|
|
216
|
+
ip_address,
|
|
217
|
+
path,
|
|
218
|
+
method,
|
|
219
|
+
user_agent,
|
|
220
|
+
details
|
|
221
|
+
)
|
|
222
|
+
VALUES ($1,$2,$3,$4,$5,$6,$7)
|
|
223
|
+
`,
|
|
183
224
|
[
|
|
184
225
|
event.event_type,
|
|
185
226
|
event.severity,
|
|
@@ -191,12 +232,24 @@ var AuditDb = class {
|
|
|
191
232
|
]
|
|
192
233
|
).catch(() => void 0);
|
|
193
234
|
}
|
|
194
|
-
|
|
235
|
+
// ----------------------------------
|
|
236
|
+
// API access recording
|
|
237
|
+
// ----------------------------------
|
|
195
238
|
recordApiAccess(access) {
|
|
196
239
|
this.pool.query(
|
|
197
|
-
`
|
|
198
|
-
|
|
199
|
-
|
|
240
|
+
`
|
|
241
|
+
INSERT INTO audit_api_access
|
|
242
|
+
(
|
|
243
|
+
method,
|
|
244
|
+
path,
|
|
245
|
+
status_code,
|
|
246
|
+
response_time_ms,
|
|
247
|
+
ip_address,
|
|
248
|
+
user_agent,
|
|
249
|
+
execution_id
|
|
250
|
+
)
|
|
251
|
+
VALUES ($1,$2,$3,$4,$5,$6,$7)
|
|
252
|
+
`,
|
|
200
253
|
[
|
|
201
254
|
access.method,
|
|
202
255
|
access.path,
|
|
@@ -208,69 +261,225 @@ var AuditDb = class {
|
|
|
208
261
|
]
|
|
209
262
|
).catch(() => void 0);
|
|
210
263
|
}
|
|
264
|
+
// ----------------------------------
|
|
265
|
+
// Decision timeline
|
|
266
|
+
// ----------------------------------
|
|
211
267
|
async getDecisionTimeline(limit = 100, filter) {
|
|
212
268
|
const conditions = [];
|
|
213
269
|
const values = [];
|
|
214
270
|
if (filter?.policy_id) {
|
|
215
|
-
values.push(
|
|
216
|
-
|
|
271
|
+
values.push(
|
|
272
|
+
filter.policy_id
|
|
273
|
+
);
|
|
274
|
+
conditions.push(
|
|
275
|
+
`policy_id = $${values.length}`
|
|
276
|
+
);
|
|
217
277
|
}
|
|
218
278
|
if (filter?.decision) {
|
|
219
|
-
values.push(
|
|
220
|
-
|
|
279
|
+
values.push(
|
|
280
|
+
filter.decision
|
|
281
|
+
);
|
|
282
|
+
conditions.push(
|
|
283
|
+
`decision = $${values.length}`
|
|
284
|
+
);
|
|
221
285
|
}
|
|
222
286
|
if (filter?.from_date) {
|
|
223
|
-
values.push(
|
|
224
|
-
|
|
287
|
+
values.push(
|
|
288
|
+
filter.from_date
|
|
289
|
+
);
|
|
290
|
+
conditions.push(
|
|
291
|
+
`executed_at >= $${values.length}`
|
|
292
|
+
);
|
|
225
293
|
}
|
|
226
294
|
if (filter?.to_date) {
|
|
227
|
-
values.push(
|
|
228
|
-
|
|
295
|
+
values.push(
|
|
296
|
+
filter.to_date
|
|
297
|
+
);
|
|
298
|
+
conditions.push(
|
|
299
|
+
`executed_at <= $${values.length}`
|
|
300
|
+
);
|
|
229
301
|
}
|
|
230
302
|
values.push(limit);
|
|
231
303
|
const limitParam = `$${values.length}`;
|
|
232
304
|
const where = conditions.length ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
233
305
|
const { rows } = await this.pool.query(
|
|
234
|
-
`
|
|
306
|
+
`
|
|
307
|
+
SELECT *
|
|
308
|
+
FROM view_decision_timeline
|
|
309
|
+
|
|
310
|
+
${where}
|
|
311
|
+
|
|
312
|
+
ORDER BY executed_at DESC
|
|
313
|
+
|
|
314
|
+
LIMIT ${limitParam}
|
|
315
|
+
`,
|
|
235
316
|
values
|
|
236
317
|
);
|
|
237
318
|
return rows;
|
|
238
319
|
}
|
|
320
|
+
// ----------------------------------
|
|
321
|
+
// Stats
|
|
322
|
+
// ----------------------------------
|
|
239
323
|
async getStats() {
|
|
240
|
-
const { rows } = await this.pool.query(
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
324
|
+
const { rows } = await this.pool.query(
|
|
325
|
+
`
|
|
326
|
+
SELECT
|
|
327
|
+
|
|
328
|
+
(
|
|
329
|
+
SELECT COUNT(*)
|
|
330
|
+
FROM audit_decisions
|
|
331
|
+
)::text AS total_decisions,
|
|
332
|
+
|
|
333
|
+
(
|
|
334
|
+
SELECT COUNT(*)
|
|
335
|
+
FROM audit_decisions
|
|
336
|
+
WHERE executed_at >= CURRENT_DATE
|
|
337
|
+
)::text AS decisions_today,
|
|
338
|
+
|
|
339
|
+
(
|
|
340
|
+
SELECT COUNT(*)
|
|
341
|
+
FROM audit_verifications
|
|
342
|
+
)::text AS total_verifications,
|
|
343
|
+
|
|
344
|
+
(
|
|
345
|
+
SELECT COUNT(*)
|
|
346
|
+
FROM audit_verifications
|
|
347
|
+
WHERE valid = true
|
|
348
|
+
)::text AS valid_verifications,
|
|
349
|
+
|
|
350
|
+
(
|
|
351
|
+
SELECT COUNT(*)
|
|
352
|
+
FROM audit_verifications
|
|
353
|
+
WHERE valid = false
|
|
354
|
+
)::text AS invalid_verifications,
|
|
355
|
+
|
|
356
|
+
(
|
|
357
|
+
SELECT COUNT(*)
|
|
358
|
+
FROM audit_security_events
|
|
359
|
+
)::text AS total_security_events,
|
|
360
|
+
|
|
361
|
+
(
|
|
362
|
+
SELECT COUNT(*)
|
|
363
|
+
FROM audit_api_access
|
|
364
|
+
)::text AS total_api_calls
|
|
365
|
+
`
|
|
366
|
+
);
|
|
250
367
|
return rows[0];
|
|
251
368
|
}
|
|
369
|
+
// ----------------------------------
|
|
370
|
+
// Decision lookup
|
|
371
|
+
// ----------------------------------
|
|
252
372
|
async getDecisionById(executionId) {
|
|
253
373
|
const { rows } = await this.pool.query(
|
|
254
|
-
`
|
|
374
|
+
`
|
|
375
|
+
SELECT *
|
|
376
|
+
FROM audit_decisions
|
|
377
|
+
WHERE execution_id = $1
|
|
378
|
+
`,
|
|
255
379
|
[executionId]
|
|
256
380
|
);
|
|
257
381
|
return rows[0] ?? null;
|
|
258
382
|
}
|
|
383
|
+
// ----------------------------------
|
|
384
|
+
// Verification lookup
|
|
385
|
+
// ----------------------------------
|
|
259
386
|
async getVerificationsByExecution(executionId) {
|
|
260
387
|
const { rows } = await this.pool.query(
|
|
261
|
-
`
|
|
262
|
-
|
|
263
|
-
|
|
388
|
+
`
|
|
389
|
+
SELECT *
|
|
390
|
+
FROM audit_verifications
|
|
391
|
+
|
|
392
|
+
WHERE execution_id = $1
|
|
393
|
+
|
|
394
|
+
ORDER BY verified_at DESC
|
|
395
|
+
`,
|
|
264
396
|
[executionId]
|
|
265
397
|
);
|
|
266
398
|
return rows;
|
|
267
399
|
}
|
|
400
|
+
// ----------------------------------
|
|
401
|
+
// Security dashboard
|
|
402
|
+
// ----------------------------------
|
|
268
403
|
async getSecurityDashboard() {
|
|
269
404
|
const { rows } = await this.pool.query(
|
|
270
|
-
`
|
|
405
|
+
`
|
|
406
|
+
SELECT *
|
|
407
|
+
FROM view_security_dashboard
|
|
408
|
+
|
|
409
|
+
ORDER BY event_count DESC
|
|
410
|
+
`
|
|
271
411
|
);
|
|
272
412
|
return rows;
|
|
273
413
|
}
|
|
414
|
+
// ----------------------------------
|
|
415
|
+
// Replay protection
|
|
416
|
+
// ----------------------------------
|
|
417
|
+
async hasExecution(executionId) {
|
|
418
|
+
const { rows } = await this.pool.query(
|
|
419
|
+
`
|
|
420
|
+
SELECT execution_id
|
|
421
|
+
|
|
422
|
+
FROM audit_decisions
|
|
423
|
+
|
|
424
|
+
WHERE execution_id = $1
|
|
425
|
+
|
|
426
|
+
LIMIT 1
|
|
427
|
+
`,
|
|
428
|
+
[executionId]
|
|
429
|
+
);
|
|
430
|
+
return rows.length > 0;
|
|
431
|
+
}
|
|
432
|
+
// ----------------------------------
|
|
433
|
+
// Pending override storage
|
|
434
|
+
// ----------------------------------
|
|
435
|
+
async storePendingExecution(attestation) {
|
|
436
|
+
await this.pool.query(
|
|
437
|
+
`
|
|
438
|
+
INSERT INTO audit_decisions
|
|
439
|
+
(
|
|
440
|
+
execution_id,
|
|
441
|
+
decision,
|
|
442
|
+
execution_state,
|
|
443
|
+
runtime_hash,
|
|
444
|
+
signature,
|
|
445
|
+
attestation,
|
|
446
|
+
executed_at
|
|
447
|
+
)
|
|
448
|
+
|
|
449
|
+
VALUES ($1,$2,$3,$4,$5,$6,$7)
|
|
450
|
+
|
|
451
|
+
ON CONFLICT (execution_id)
|
|
452
|
+
DO NOTHING
|
|
453
|
+
`,
|
|
454
|
+
[
|
|
455
|
+
attestation.execution_id,
|
|
456
|
+
attestation.decision,
|
|
457
|
+
"pending_override",
|
|
458
|
+
attestation.runtime_hash,
|
|
459
|
+
attestation.signature,
|
|
460
|
+
JSON.stringify(attestation),
|
|
461
|
+
(/* @__PURE__ */ new Date()).toISOString()
|
|
462
|
+
]
|
|
463
|
+
);
|
|
464
|
+
}
|
|
465
|
+
// ----------------------------------
|
|
466
|
+
// Completed execution marking
|
|
467
|
+
// ----------------------------------
|
|
468
|
+
async markExecuted(executionId) {
|
|
469
|
+
await this.pool.query(
|
|
470
|
+
`
|
|
471
|
+
UPDATE audit_decisions
|
|
472
|
+
|
|
473
|
+
SET execution_state = 'completed'
|
|
474
|
+
|
|
475
|
+
WHERE execution_id = $1
|
|
476
|
+
`,
|
|
477
|
+
[executionId]
|
|
478
|
+
);
|
|
479
|
+
}
|
|
480
|
+
// ----------------------------------
|
|
481
|
+
// Cleanup
|
|
482
|
+
// ----------------------------------
|
|
274
483
|
async close() {
|
|
275
484
|
await this.pool.end();
|
|
276
485
|
}
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/client.ts","../src/migrations.ts"],"sourcesContent":["import { Pool } from \"pg\";\r\nimport type { PoolClient } from \"pg\";\r\n\r\nimport type { ExecutionAttestation } from \"@parmanasystems/execution\";\r\nimport type { VerificationResult } from \"@parmanasystems/verifier\";\r\n\r\nimport type {\r\n AuditDecision,\r\n AuditVerification,\r\n SecurityEventInput,\r\n ApiAccessInput,\r\n DecisionTimelineRow,\r\n DecisionFilter,\r\n SecurityDashboardRow,\r\n AuditStats,\r\n} from \"./types.js\";\r\nimport { runMigrations } from \"./migrations.js\";\r\n\r\nexport class AuditDb {\r\n private readonly pool: InstanceType<typeof Pool>;\r\n\r\n constructor(connectionString: string) {\r\n this.pool = new Pool({ connectionString });\r\n }\r\n\r\n async ping(): Promise<void> {\r\n await this.pool.query(\"SELECT 1\");\r\n }\r\n\r\n async disconnect(): Promise<void> {\r\n await this.pool.end();\r\n }\r\n\r\n async migrate(): Promise<void> {\r\n const client: PoolClient = await this.pool.connect();\r\n try {\r\n await runMigrations(client);\r\n } finally {\r\n client.release();\r\n }\r\n }\r\n\r\n /** Fire-and-forget — never throws, never delays the caller. */\r\n recordDecision(attestation: ExecutionAttestation): void {\r\n this.pool\r\n .query(\r\n `INSERT INTO audit_decisions\r\n (execution_id, decision, execution_state,\r\n runtime_hash, signature, attestation, executed_at)\r\n VALUES ($1,$2,$3,$4,$5,$6,$7)\r\n ON CONFLICT (execution_id) DO NOTHING`,\r\n [\r\n attestation.execution_id,\r\n attestation.decision,\r\n attestation.execution_state,\r\n attestation.runtime_hash,\r\n attestation.signature,\r\n JSON.stringify(attestation),\r\n new Date().toISOString(),\r\n ],\r\n )\r\n .catch(() => undefined);\r\n }\r\n\r\n /** Fire-and-forget — never throws, never delays the caller. */\r\n recordVerification(executionId: string, result: VerificationResult): void {\r\n this.pool\r\n .query(\r\n `INSERT INTO audit_verifications\r\n (execution_id, valid, signature_verified, runtime_verified, schema_compatible)\r\n VALUES ($1,$2,$3,$4,$5)`,\r\n [\r\n executionId,\r\n result.valid,\r\n result.checks.signature_verified,\r\n result.checks.runtime_verified,\r\n result.checks.schema_compatible,\r\n ],\r\n )\r\n .catch(() => undefined);\r\n }\r\n\r\n /** Fire-and-forget — never throws, never delays the caller. */\r\n recordSecurityEvent(event: SecurityEventInput): void {\r\n this.pool\r\n .query(\r\n `INSERT INTO audit_security_events\r\n (event_type, severity, ip_address, path, method, user_agent, details)\r\n VALUES ($1,$2,$3,$4,$5,$6,$7)`,\r\n [\r\n event.event_type,\r\n event.severity,\r\n event.ip_address ?? null,\r\n event.path ?? null,\r\n event.method ?? null,\r\n event.user_agent ?? null,\r\n event.details != null ? JSON.stringify(event.details) : null,\r\n ],\r\n )\r\n .catch(() => undefined);\r\n }\r\n\r\n /** Fire-and-forget — never throws, never delays the caller. */\r\n recordApiAccess(access: ApiAccessInput): void {\r\n this.pool\r\n .query(\r\n `INSERT INTO audit_api_access\r\n (method, path, status_code, response_time_ms, ip_address, user_agent, execution_id)\r\n VALUES ($1,$2,$3,$4,$5,$6,$7)`,\r\n [\r\n access.method,\r\n access.path,\r\n access.status_code,\r\n access.response_time_ms ?? null,\r\n access.ip_address ?? null,\r\n access.user_agent ?? null,\r\n access.execution_id ?? null,\r\n ],\r\n )\r\n .catch(() => undefined);\r\n }\r\n\r\n async getDecisionTimeline(limit = 100, filter?: DecisionFilter): Promise<DecisionTimelineRow[]> {\r\n const conditions: string[] = [];\r\n const values: unknown[] = [];\r\n\r\n if (filter?.policy_id) {\r\n values.push(filter.policy_id);\r\n conditions.push(`policy_id = $${values.length}`);\r\n }\r\n if (filter?.decision) {\r\n values.push(filter.decision);\r\n conditions.push(`decision = $${values.length}`);\r\n }\r\n if (filter?.from_date) {\r\n values.push(filter.from_date);\r\n conditions.push(`executed_at >= $${values.length}`);\r\n }\r\n if (filter?.to_date) {\r\n values.push(filter.to_date);\r\n conditions.push(`executed_at <= $${values.length}`);\r\n }\r\n\r\n values.push(limit);\r\n const limitParam = `$${values.length}`;\r\n const where = conditions.length ? `WHERE ${conditions.join(\" AND \")}` : \"\";\r\n\r\n const { rows } = await this.pool.query<DecisionTimelineRow>(\r\n `SELECT * FROM view_decision_timeline ${where} ORDER BY executed_at DESC LIMIT ${limitParam}`,\r\n values,\r\n );\r\n return rows;\r\n }\r\n\r\n async getStats(): Promise<AuditStats> {\r\n const { rows } = await this.pool.query<AuditStats>(`\r\n SELECT\r\n (SELECT COUNT(*) FROM audit_decisions)::text AS total_decisions,\r\n (SELECT COUNT(*) FROM audit_decisions WHERE executed_at >= CURRENT_DATE)::text AS decisions_today,\r\n (SELECT COUNT(*) FROM audit_verifications)::text AS total_verifications,\r\n (SELECT COUNT(*) FROM audit_verifications WHERE valid = true)::text AS valid_verifications,\r\n (SELECT COUNT(*) FROM audit_verifications WHERE valid = false)::text AS invalid_verifications,\r\n (SELECT COUNT(*) FROM audit_security_events)::text AS total_security_events,\r\n (SELECT COUNT(*) FROM audit_api_access)::text AS total_api_calls\r\n `);\r\n return rows[0]!;\r\n }\r\n\r\n async getDecisionById(executionId: string): Promise<AuditDecision | null> {\r\n const { rows } = await this.pool.query<AuditDecision>(\r\n `SELECT * FROM audit_decisions WHERE execution_id = $1`,\r\n [executionId],\r\n );\r\n return rows[0] ?? null;\r\n }\r\n\r\n async getVerificationsByExecution(executionId: string): Promise<AuditVerification[]> {\r\n const { rows } = await this.pool.query<AuditVerification>(\r\n `SELECT * FROM audit_verifications\r\n WHERE execution_id = $1\r\n ORDER BY verified_at DESC`,\r\n [executionId],\r\n );\r\n return rows;\r\n }\r\n\r\n async getSecurityDashboard(): Promise<SecurityDashboardRow[]> {\r\n const { rows } = await this.pool.query<SecurityDashboardRow>(\r\n `SELECT * FROM view_security_dashboard ORDER BY event_count DESC`,\r\n );\r\n return rows;\r\n }\r\n\r\n async close(): Promise<void> {\r\n await this.pool.end();\r\n }\r\n}\r\n","import type { PoolClient } from \"pg\";\r\n\r\n// Inlined so the bundle has no runtime fs dependency.\r\n// The canonical reference lives in schema.sql alongside this file.\r\nconst SCHEMA_SQL = `\r\nCREATE TABLE IF NOT EXISTS audit_decisions (\r\n id BIGSERIAL PRIMARY KEY,\r\n execution_id UUID NOT NULL UNIQUE,\r\n policy_id TEXT NOT NULL,\r\n policy_version TEXT NOT NULL,\r\n schema_version TEXT NOT NULL,\r\n runtime_version TEXT NOT NULL,\r\n runtime_hash TEXT NOT NULL,\r\n decision TEXT NOT NULL,\r\n signals_hash TEXT NOT NULL,\r\n signature TEXT NOT NULL,\r\n attestation JSONB NOT NULL,\r\n executed_at TIMESTAMPTZ NOT NULL,\r\n recorded_at TIMESTAMPTZ NOT NULL DEFAULT NOW()\r\n);\r\n\r\nCREATE INDEX IF NOT EXISTS idx_audit_decisions_policy_id\r\n ON audit_decisions (policy_id);\r\nCREATE INDEX IF NOT EXISTS idx_audit_decisions_executed_at\r\n ON audit_decisions (executed_at DESC);\r\nCREATE INDEX IF NOT EXISTS idx_audit_decisions_decision\r\n ON audit_decisions (decision);\r\n\r\nCREATE TABLE IF NOT EXISTS audit_verifications (\r\n id BIGSERIAL PRIMARY KEY,\r\n execution_id UUID NOT NULL,\r\n valid BOOLEAN NOT NULL,\r\n signature_verified BOOLEAN NOT NULL,\r\n runtime_verified BOOLEAN NOT NULL,\r\n schema_compatible BOOLEAN NOT NULL,\r\n verified_at TIMESTAMPTZ NOT NULL DEFAULT NOW()\r\n);\r\n\r\nCREATE INDEX IF NOT EXISTS idx_audit_verifications_execution_id\r\n ON audit_verifications (execution_id);\r\nCREATE INDEX IF NOT EXISTS idx_audit_verifications_valid\r\n ON audit_verifications (valid);\r\nCREATE INDEX IF NOT EXISTS idx_audit_verifications_verified_at\r\n ON audit_verifications (verified_at DESC);\r\n\r\nCREATE TABLE IF NOT EXISTS audit_security_events (\r\n id BIGSERIAL PRIMARY KEY,\r\n event_type TEXT NOT NULL,\r\n severity TEXT NOT NULL CHECK (severity IN ('low','medium','high','critical')),\r\n ip_address TEXT,\r\n path TEXT,\r\n method TEXT,\r\n user_agent TEXT,\r\n details JSONB,\r\n occurred_at TIMESTAMPTZ NOT NULL DEFAULT NOW()\r\n);\r\n\r\nCREATE INDEX IF NOT EXISTS idx_audit_security_events_event_type\r\n ON audit_security_events (event_type);\r\nCREATE INDEX IF NOT EXISTS idx_audit_security_events_severity\r\n ON audit_security_events (severity);\r\nCREATE INDEX IF NOT EXISTS idx_audit_security_events_occurred_at\r\n ON audit_security_events (occurred_at DESC);\r\n\r\nCREATE TABLE IF NOT EXISTS audit_api_access (\r\n id BIGSERIAL PRIMARY KEY,\r\n method TEXT NOT NULL,\r\n path TEXT NOT NULL,\r\n status_code INTEGER NOT NULL,\r\n response_time_ms INTEGER,\r\n ip_address TEXT,\r\n user_agent TEXT,\r\n execution_id UUID,\r\n accessed_at TIMESTAMPTZ NOT NULL DEFAULT NOW()\r\n);\r\n\r\nCREATE INDEX IF NOT EXISTS idx_audit_api_access_path\r\n ON audit_api_access (path);\r\nCREATE INDEX IF NOT EXISTS idx_audit_api_access_status_code\r\n ON audit_api_access (status_code);\r\nCREATE INDEX IF NOT EXISTS idx_audit_api_access_accessed_at\r\n ON audit_api_access (accessed_at DESC);\r\n\r\nCREATE OR REPLACE VIEW view_decision_timeline AS\r\nSELECT\r\n d.execution_id,\r\n d.policy_id,\r\n d.policy_version,\r\n d.decision,\r\n d.runtime_version,\r\n d.runtime_hash,\r\n d.executed_at,\r\n d.recorded_at,\r\n v.valid AS verification_valid,\r\n v.signature_verified,\r\n v.runtime_verified,\r\n v.schema_compatible,\r\n v.verified_at\r\nFROM audit_decisions d\r\nLEFT JOIN audit_verifications v ON d.execution_id = v.execution_id;\r\n\r\nCREATE OR REPLACE VIEW view_security_dashboard AS\r\nSELECT\r\n event_type,\r\n severity,\r\n COUNT(*) AS event_count,\r\n MAX(occurred_at) AS last_occurrence,\r\n MIN(occurred_at) AS first_occurrence\r\nFROM audit_security_events\r\nGROUP BY event_type, severity;\r\n`;\r\n\r\nexport async function runMigrations(client: PoolClient): Promise<void> {\r\n await client.query(\"BEGIN\");\r\n try {\r\n await client.query(SCHEMA_SQL);\r\n await client.query(\"COMMIT\");\r\n } catch (err) {\r\n await client.query(\"ROLLBACK\");\r\n throw err;\r\n }\r\n}\r\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/client.ts","../src/migrations.ts"],"sourcesContent":["import { Pool } from \"pg\";\r\nimport type { PoolClient } from \"pg\";\r\n\r\nimport type { ExecutionAttestation } from \"@parmanasystems/execution\";\r\nimport type { VerificationResult } from \"@parmanasystems/verifier\";\r\n\r\nimport type {\r\n AuditDecision,\r\n AuditVerification,\r\n SecurityEventInput,\r\n ApiAccessInput,\r\n DecisionTimelineRow,\r\n DecisionFilter,\r\n SecurityDashboardRow,\r\n AuditStats,\r\n} from \"./types.js\";\r\n\r\nimport { runMigrations } from \"./migrations.js\";\r\n\r\nexport class AuditDb {\r\n\r\n private readonly pool:\r\n InstanceType<typeof Pool>;\r\n\r\n constructor(\r\n connectionString: string\r\n ) {\r\n\r\n this.pool =\r\n new Pool({\r\n connectionString\r\n });\r\n }\r\n\r\n async ping(): Promise<void> {\r\n\r\n await this.pool.query(\r\n \"SELECT 1\"\r\n );\r\n }\r\n\r\n async disconnect(): Promise<void> {\r\n\r\n await this.pool.end();\r\n }\r\n\r\n async migrate(): Promise<void> {\r\n\r\n const client:\r\n PoolClient =\r\n await this.pool.connect();\r\n\r\n try {\r\n\r\n await runMigrations(\r\n client\r\n );\r\n\r\n } finally {\r\n\r\n client.release();\r\n }\r\n }\r\n\r\n // ----------------------------------\r\n // Decision recording\r\n // ----------------------------------\r\n\r\n recordDecision(\r\n attestation: ExecutionAttestation\r\n ): void {\r\n\r\n this.pool\r\n .query(\r\n `\r\n INSERT INTO audit_decisions\r\n (\r\n execution_id,\r\n decision,\r\n execution_state,\r\n runtime_hash,\r\n signature,\r\n attestation,\r\n executed_at\r\n )\r\n VALUES ($1,$2,$3,$4,$5,$6,$7)\r\n\r\n ON CONFLICT (execution_id)\r\n DO NOTHING\r\n `,\r\n [\r\n attestation.execution_id,\r\n attestation.decision,\r\n attestation.execution_state,\r\n attestation.runtime_hash,\r\n attestation.signature,\r\n JSON.stringify(attestation),\r\n new Date().toISOString()\r\n ]\r\n )\r\n .catch(() => undefined);\r\n }\r\n\r\n // ----------------------------------\r\n // Verification recording\r\n // ----------------------------------\r\n\r\n recordVerification(\r\n executionId: string,\r\n result: VerificationResult\r\n ): void {\r\n\r\n this.pool\r\n .query(\r\n `\r\n INSERT INTO audit_verifications\r\n (\r\n execution_id,\r\n valid,\r\n signature_verified,\r\n runtime_verified,\r\n schema_compatible\r\n )\r\n VALUES ($1,$2,$3,$4,$5)\r\n `,\r\n [\r\n executionId,\r\n result.valid,\r\n result.checks.signature_verified,\r\n result.checks.runtime_verified,\r\n result.checks.schema_compatible\r\n ]\r\n )\r\n .catch(() => undefined);\r\n }\r\n\r\n // ----------------------------------\r\n // Security event recording\r\n // ----------------------------------\r\n\r\n recordSecurityEvent(\r\n event: SecurityEventInput\r\n ): void {\r\n\r\n this.pool\r\n .query(\r\n `\r\n INSERT INTO audit_security_events\r\n (\r\n event_type,\r\n severity,\r\n ip_address,\r\n path,\r\n method,\r\n user_agent,\r\n details\r\n )\r\n VALUES ($1,$2,$3,$4,$5,$6,$7)\r\n `,\r\n [\r\n event.event_type,\r\n event.severity,\r\n event.ip_address ?? null,\r\n event.path ?? null,\r\n event.method ?? null,\r\n event.user_agent ?? null,\r\n\r\n event.details != null\r\n ? JSON.stringify(event.details)\r\n : null\r\n ]\r\n )\r\n .catch(() => undefined);\r\n }\r\n\r\n // ----------------------------------\r\n // API access recording\r\n // ----------------------------------\r\n\r\n recordApiAccess(\r\n access: ApiAccessInput\r\n ): void {\r\n\r\n this.pool\r\n .query(\r\n `\r\n INSERT INTO audit_api_access\r\n (\r\n method,\r\n path,\r\n status_code,\r\n response_time_ms,\r\n ip_address,\r\n user_agent,\r\n execution_id\r\n )\r\n VALUES ($1,$2,$3,$4,$5,$6,$7)\r\n `,\r\n [\r\n access.method,\r\n access.path,\r\n access.status_code,\r\n access.response_time_ms ?? null,\r\n access.ip_address ?? null,\r\n access.user_agent ?? null,\r\n access.execution_id ?? null\r\n ]\r\n )\r\n .catch(() => undefined);\r\n }\r\n\r\n // ----------------------------------\r\n // Decision timeline\r\n // ----------------------------------\r\n\r\n async getDecisionTimeline(\r\n limit = 100,\r\n filter?: DecisionFilter\r\n ): Promise<DecisionTimelineRow[]> {\r\n\r\n const conditions: string[] = [];\r\n\r\n const values: unknown[] = [];\r\n\r\n if (filter?.policy_id) {\r\n\r\n values.push(\r\n filter.policy_id\r\n );\r\n\r\n conditions.push(\r\n `policy_id = $${values.length}`\r\n );\r\n }\r\n\r\n if (filter?.decision) {\r\n\r\n values.push(\r\n filter.decision\r\n );\r\n\r\n conditions.push(\r\n `decision = $${values.length}`\r\n );\r\n }\r\n\r\n if (filter?.from_date) {\r\n\r\n values.push(\r\n filter.from_date\r\n );\r\n\r\n conditions.push(\r\n `executed_at >= $${values.length}`\r\n );\r\n }\r\n\r\n if (filter?.to_date) {\r\n\r\n values.push(\r\n filter.to_date\r\n );\r\n\r\n conditions.push(\r\n `executed_at <= $${values.length}`\r\n );\r\n }\r\n\r\n values.push(limit);\r\n\r\n const limitParam =\r\n `$${values.length}`;\r\n\r\n const where =\r\n conditions.length\r\n ? `WHERE ${conditions.join(\" AND \")}`\r\n : \"\";\r\n\r\n const { rows } =\r\n await this.pool.query<DecisionTimelineRow>(\r\n `\r\n SELECT *\r\n FROM view_decision_timeline\r\n\r\n ${where}\r\n\r\n ORDER BY executed_at DESC\r\n\r\n LIMIT ${limitParam}\r\n `,\r\n values\r\n );\r\n\r\n return rows;\r\n }\r\n\r\n // ----------------------------------\r\n // Stats\r\n // ----------------------------------\r\n\r\n async getStats(): Promise<AuditStats> {\r\n\r\n const { rows } =\r\n await this.pool.query<AuditStats>(\r\n `\r\n SELECT\r\n\r\n (\r\n SELECT COUNT(*)\r\n FROM audit_decisions\r\n )::text AS total_decisions,\r\n\r\n (\r\n SELECT COUNT(*)\r\n FROM audit_decisions\r\n WHERE executed_at >= CURRENT_DATE\r\n )::text AS decisions_today,\r\n\r\n (\r\n SELECT COUNT(*)\r\n FROM audit_verifications\r\n )::text AS total_verifications,\r\n\r\n (\r\n SELECT COUNT(*)\r\n FROM audit_verifications\r\n WHERE valid = true\r\n )::text AS valid_verifications,\r\n\r\n (\r\n SELECT COUNT(*)\r\n FROM audit_verifications\r\n WHERE valid = false\r\n )::text AS invalid_verifications,\r\n\r\n (\r\n SELECT COUNT(*)\r\n FROM audit_security_events\r\n )::text AS total_security_events,\r\n\r\n (\r\n SELECT COUNT(*)\r\n FROM audit_api_access\r\n )::text AS total_api_calls\r\n `\r\n );\r\n\r\n return rows[0]!;\r\n }\r\n\r\n // ----------------------------------\r\n // Decision lookup\r\n // ----------------------------------\r\n\r\n async getDecisionById(\r\n executionId: string\r\n ): Promise<AuditDecision | null> {\r\n\r\n const { rows } =\r\n await this.pool.query<AuditDecision>(\r\n `\r\n SELECT *\r\n FROM audit_decisions\r\n WHERE execution_id = $1\r\n `,\r\n [executionId]\r\n );\r\n\r\n return rows[0] ?? null;\r\n }\r\n\r\n // ----------------------------------\r\n // Verification lookup\r\n // ----------------------------------\r\n\r\n async getVerificationsByExecution(\r\n executionId: string\r\n ): Promise<AuditVerification[]> {\r\n\r\n const { rows } =\r\n await this.pool.query<AuditVerification>(\r\n `\r\n SELECT *\r\n FROM audit_verifications\r\n\r\n WHERE execution_id = $1\r\n\r\n ORDER BY verified_at DESC\r\n `,\r\n [executionId]\r\n );\r\n\r\n return rows;\r\n }\r\n\r\n // ----------------------------------\r\n // Security dashboard\r\n // ----------------------------------\r\n\r\n async getSecurityDashboard():\r\n Promise<SecurityDashboardRow[]> {\r\n\r\n const { rows } =\r\n await this.pool.query<SecurityDashboardRow>(\r\n `\r\n SELECT *\r\n FROM view_security_dashboard\r\n\r\n ORDER BY event_count DESC\r\n `\r\n );\r\n\r\n return rows;\r\n }\r\n\r\n // ----------------------------------\r\n // Replay protection\r\n // ----------------------------------\r\n\r\n async hasExecution(\r\n executionId: string\r\n ): Promise<boolean> {\r\n\r\n const { rows } =\r\n await this.pool.query(\r\n `\r\n SELECT execution_id\r\n\r\n FROM audit_decisions\r\n\r\n WHERE execution_id = $1\r\n\r\n LIMIT 1\r\n `,\r\n [executionId]\r\n );\r\n\r\n return rows.length > 0;\r\n }\r\n\r\n // ----------------------------------\r\n // Pending override storage\r\n // ----------------------------------\r\n\r\n async storePendingExecution(\r\n attestation: ExecutionAttestation\r\n ): Promise<void> {\r\n\r\n await this.pool.query(\r\n `\r\n INSERT INTO audit_decisions\r\n (\r\n execution_id,\r\n decision,\r\n execution_state,\r\n runtime_hash,\r\n signature,\r\n attestation,\r\n executed_at\r\n )\r\n\r\n VALUES ($1,$2,$3,$4,$5,$6,$7)\r\n\r\n ON CONFLICT (execution_id)\r\n DO NOTHING\r\n `,\r\n [\r\n attestation.execution_id,\r\n attestation.decision,\r\n \"pending_override\",\r\n attestation.runtime_hash,\r\n attestation.signature,\r\n JSON.stringify(attestation),\r\n new Date().toISOString()\r\n ]\r\n );\r\n }\r\n\r\n // ----------------------------------\r\n // Completed execution marking\r\n // ----------------------------------\r\n\r\n async markExecuted(\r\n executionId: string\r\n ): Promise<void> {\r\n\r\n await this.pool.query(\r\n `\r\n UPDATE audit_decisions\r\n\r\n SET execution_state = 'completed'\r\n\r\n WHERE execution_id = $1\r\n `,\r\n [executionId]\r\n );\r\n }\r\n\r\n // ----------------------------------\r\n // Cleanup\r\n // ----------------------------------\r\n\r\n async close(): Promise<void> {\r\n\r\n await this.pool.end();\r\n }\r\n}","import type { PoolClient } from \"pg\";\r\n\r\n// Inlined so the bundle has no runtime fs dependency.\r\n// The canonical reference lives in schema.sql alongside this file.\r\nconst SCHEMA_SQL = `\r\nCREATE TABLE IF NOT EXISTS audit_decisions (\r\n id BIGSERIAL PRIMARY KEY,\r\n execution_id UUID NOT NULL UNIQUE,\r\n policy_id TEXT NOT NULL,\r\n policy_version TEXT NOT NULL,\r\n schema_version TEXT NOT NULL,\r\n runtime_version TEXT NOT NULL,\r\n runtime_hash TEXT NOT NULL,\r\n decision TEXT NOT NULL,\r\n signals_hash TEXT NOT NULL,\r\n signature TEXT NOT NULL,\r\n attestation JSONB NOT NULL,\r\n executed_at TIMESTAMPTZ NOT NULL,\r\n recorded_at TIMESTAMPTZ NOT NULL DEFAULT NOW()\r\n);\r\n\r\nCREATE INDEX IF NOT EXISTS idx_audit_decisions_policy_id\r\n ON audit_decisions (policy_id);\r\nCREATE INDEX IF NOT EXISTS idx_audit_decisions_executed_at\r\n ON audit_decisions (executed_at DESC);\r\nCREATE INDEX IF NOT EXISTS idx_audit_decisions_decision\r\n ON audit_decisions (decision);\r\n\r\nCREATE TABLE IF NOT EXISTS audit_verifications (\r\n id BIGSERIAL PRIMARY KEY,\r\n execution_id UUID NOT NULL,\r\n valid BOOLEAN NOT NULL,\r\n signature_verified BOOLEAN NOT NULL,\r\n runtime_verified BOOLEAN NOT NULL,\r\n schema_compatible BOOLEAN NOT NULL,\r\n verified_at TIMESTAMPTZ NOT NULL DEFAULT NOW()\r\n);\r\n\r\nCREATE INDEX IF NOT EXISTS idx_audit_verifications_execution_id\r\n ON audit_verifications (execution_id);\r\nCREATE INDEX IF NOT EXISTS idx_audit_verifications_valid\r\n ON audit_verifications (valid);\r\nCREATE INDEX IF NOT EXISTS idx_audit_verifications_verified_at\r\n ON audit_verifications (verified_at DESC);\r\n\r\nCREATE TABLE IF NOT EXISTS audit_security_events (\r\n id BIGSERIAL PRIMARY KEY,\r\n event_type TEXT NOT NULL,\r\n severity TEXT NOT NULL CHECK (severity IN ('low','medium','high','critical')),\r\n ip_address TEXT,\r\n path TEXT,\r\n method TEXT,\r\n user_agent TEXT,\r\n details JSONB,\r\n occurred_at TIMESTAMPTZ NOT NULL DEFAULT NOW()\r\n);\r\n\r\nCREATE INDEX IF NOT EXISTS idx_audit_security_events_event_type\r\n ON audit_security_events (event_type);\r\nCREATE INDEX IF NOT EXISTS idx_audit_security_events_severity\r\n ON audit_security_events (severity);\r\nCREATE INDEX IF NOT EXISTS idx_audit_security_events_occurred_at\r\n ON audit_security_events (occurred_at DESC);\r\n\r\nCREATE TABLE IF NOT EXISTS audit_api_access (\r\n id BIGSERIAL PRIMARY KEY,\r\n method TEXT NOT NULL,\r\n path TEXT NOT NULL,\r\n status_code INTEGER NOT NULL,\r\n response_time_ms INTEGER,\r\n ip_address TEXT,\r\n user_agent TEXT,\r\n execution_id UUID,\r\n accessed_at TIMESTAMPTZ NOT NULL DEFAULT NOW()\r\n);\r\n\r\nCREATE INDEX IF NOT EXISTS idx_audit_api_access_path\r\n ON audit_api_access (path);\r\nCREATE INDEX IF NOT EXISTS idx_audit_api_access_status_code\r\n ON audit_api_access (status_code);\r\nCREATE INDEX IF NOT EXISTS idx_audit_api_access_accessed_at\r\n ON audit_api_access (accessed_at DESC);\r\n\r\nCREATE OR REPLACE VIEW view_decision_timeline AS\r\nSELECT\r\n d.execution_id,\r\n d.policy_id,\r\n d.policy_version,\r\n d.decision,\r\n d.runtime_version,\r\n d.runtime_hash,\r\n d.executed_at,\r\n d.recorded_at,\r\n v.valid AS verification_valid,\r\n v.signature_verified,\r\n v.runtime_verified,\r\n v.schema_compatible,\r\n v.verified_at\r\nFROM audit_decisions d\r\nLEFT JOIN audit_verifications v ON d.execution_id = v.execution_id;\r\n\r\nCREATE OR REPLACE VIEW view_security_dashboard AS\r\nSELECT\r\n event_type,\r\n severity,\r\n COUNT(*) AS event_count,\r\n MAX(occurred_at) AS last_occurrence,\r\n MIN(occurred_at) AS first_occurrence\r\nFROM audit_security_events\r\nGROUP BY event_type, severity;\r\n`;\r\n\r\nexport async function runMigrations(client: PoolClient): Promise<void> {\r\n await client.query(\"BEGIN\");\r\n try {\r\n await client.query(SCHEMA_SQL);\r\n await client.query(\"COMMIT\");\r\n } catch (err) {\r\n await client.query(\"ROLLBACK\");\r\n throw err;\r\n }\r\n}\r\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@parmanasystems/audit-db",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.8.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "PostgreSQL audit database client for parmanasystems governance decisions, verifications, and security events.",
|
|
@@ -19,8 +19,8 @@
|
|
|
19
19
|
],
|
|
20
20
|
"sideEffects": false,
|
|
21
21
|
"dependencies": {
|
|
22
|
-
"@parmanasystems/execution": "^1.
|
|
23
|
-
"@parmanasystems/verifier": "^1.
|
|
22
|
+
"@parmanasystems/execution": "^1.8.0",
|
|
23
|
+
"@parmanasystems/verifier": "^1.8.0",
|
|
24
24
|
"pg": "^8.13.3"
|
|
25
25
|
},
|
|
26
26
|
"devDependencies": {
|