-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathaudit.ts
More file actions
58 lines (50 loc) · 1.52 KB
/
audit.ts
File metadata and controls
58 lines (50 loc) · 1.52 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
import { appendFileSync, readFileSync, existsSync } from 'node:fs'
import type { AuditEvent } from './types.js'
export interface AuditLogger {
log(event: AuditEvent): void
query(filters?: AuditFilters): AuditEvent[]
export(): AuditEvent[]
}
export interface AuditFilters {
action?: AuditEvent['action']
agent?: string
since?: Date
}
/**
* Creates an append-only JSONL audit logger.
*/
export function createAuditLogger(filePath: string = './leash-audit.jsonl'): AuditLogger {
return {
log(event: AuditEvent): void {
const line = JSON.stringify(event) + '\n'
appendFileSync(filePath, line)
},
query(filters?: AuditFilters): AuditEvent[] {
const events = readEvents(filePath)
if (!filters) return events
return events.filter((e) => {
if (filters.action && e.action !== filters.action) return false
if (filters.agent && e.agent !== filters.agent) return false
if (filters.since && new Date(e.timestamp) < filters.since) return false
return true
})
},
export(): AuditEvent[] {
return readEvents(filePath)
},
}
}
function readEvents(filePath: string): AuditEvent[] {
if (!existsSync(filePath)) return []
const content = readFileSync(filePath, 'utf-8')
const events: AuditEvent[] = []
for (const line of content.split('\n')) {
if (!line.trim()) continue
try {
events.push(JSON.parse(line) as AuditEvent)
} catch {
// Skip corrupt lines — append-only log may have partial writes
}
}
return events
}