@revealui/security 0.2.3 → 0.2.5
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 +96 -0
- package/dist/audit-UF7PIYBU.js +21 -0
- package/dist/audit-UF7PIYBU.js.map +1 -0
- package/dist/chunk-Q5KAPSST.js +429 -0
- package/dist/chunk-Q5KAPSST.js.map +1 -0
- package/dist/index.d.ts +149 -1
- package/dist/index.js +199 -370
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/README.md
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# @revealui/security
|
|
2
|
+
|
|
3
|
+
Security infrastructure for RevealUI. Provides HTTP security headers, CORS management, RBAC/ABAC authorization, field-level encryption, audit logging, and GDPR compliance tooling.
|
|
4
|
+
|
|
5
|
+
## When to Use This
|
|
6
|
+
|
|
7
|
+
- You need security headers (CSP, HSTS, CORS) on HTTP responses
|
|
8
|
+
- You're implementing role-based or attribute-based access control
|
|
9
|
+
- You need audit logging for compliance (SOC2, HIPAA)
|
|
10
|
+
- You need GDPR tooling: consent management, data export, breach reporting, anonymization
|
|
11
|
+
- You need field-level encryption or key rotation
|
|
12
|
+
|
|
13
|
+
If you only need session auth (login/logout/password reset), use `@revealui/auth` instead.
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
pnpm add @revealui/security
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Dependencies: `@revealui/contracts`, `@revealui/utils`
|
|
22
|
+
|
|
23
|
+
## API Reference
|
|
24
|
+
|
|
25
|
+
### Security Headers & CORS
|
|
26
|
+
|
|
27
|
+
| Export | Type | Purpose |
|
|
28
|
+
|--------|------|---------|
|
|
29
|
+
| `SecurityHeaders` | Class | Generate CSP, HSTS, Permissions-Policy, X-Frame-Options headers |
|
|
30
|
+
| `SecurityPresets` | Object | Pre-built header configs (strict, moderate, development) |
|
|
31
|
+
| `CORSManager` | Class | CORS origin/method/header management |
|
|
32
|
+
| `CORSPresets` | Object | Pre-built CORS configs (restrictive, public API, development) |
|
|
33
|
+
| `createSecurityMiddleware` | Function | Hono middleware applying all security headers |
|
|
34
|
+
| `setRateLimitHeaders` | Function | Add X-RateLimit-* headers to responses |
|
|
35
|
+
|
|
36
|
+
### Authorization (RBAC + ABAC)
|
|
37
|
+
|
|
38
|
+
| Export | Type | Purpose |
|
|
39
|
+
|--------|------|---------|
|
|
40
|
+
| `AuthorizationSystem` | Class | Combined RBAC + ABAC policy engine |
|
|
41
|
+
| `CommonRoles` | Object | Pre-defined roles (admin, editor, viewer, superAdmin) |
|
|
42
|
+
| `PolicyBuilder` | Class | Fluent API for building ABAC policies |
|
|
43
|
+
| `PermissionBuilder` | Class | Fluent API for building RBAC permissions |
|
|
44
|
+
| `PermissionCache` | Class | LRU cache for permission lookups |
|
|
45
|
+
| `canAccessResource` | Function | Check if user can perform action on resource |
|
|
46
|
+
| `checkAttributeAccess` | Function | Evaluate ABAC policy conditions |
|
|
47
|
+
| `createAuthorizationMiddleware` | Function | Hono middleware for route-level authorization |
|
|
48
|
+
| `RequirePermission` | Decorator | Enforce permission on class methods |
|
|
49
|
+
| `RequireRole` | Decorator | Enforce role on class methods |
|
|
50
|
+
|
|
51
|
+
### Encryption
|
|
52
|
+
|
|
53
|
+
| Export | Type | Purpose |
|
|
54
|
+
|--------|------|---------|
|
|
55
|
+
| `EncryptionSystem` | Class | AES-256 encryption with key management |
|
|
56
|
+
| `EnvelopeEncryption` | Class | Envelope encryption (data key + master key) |
|
|
57
|
+
| `FieldEncryption` | Class | Encrypt/decrypt individual database fields |
|
|
58
|
+
| `KeyRotationManager` | Class | Scheduled key rotation with re-encryption |
|
|
59
|
+
| `DataMasking` | Class | Mask sensitive data for display (email, phone, SSN) |
|
|
60
|
+
| `TokenGenerator` | Class | Secure random token generation |
|
|
61
|
+
|
|
62
|
+
### Audit Logging
|
|
63
|
+
|
|
64
|
+
| Export | Type | Purpose |
|
|
65
|
+
|--------|------|---------|
|
|
66
|
+
| `AuditSystem` | Class | Structured audit event recording |
|
|
67
|
+
| `AuditTrail` | Class | Query and filter audit history |
|
|
68
|
+
| `AuditReportGenerator` | Class | Generate compliance reports from audit data |
|
|
69
|
+
| `createAuditMiddleware` | Function | Hono middleware for automatic request auditing |
|
|
70
|
+
| `InMemoryAuditStorage` | Class | In-memory storage for testing |
|
|
71
|
+
|
|
72
|
+
### GDPR Compliance
|
|
73
|
+
|
|
74
|
+
| Export | Type | Purpose |
|
|
75
|
+
|--------|------|---------|
|
|
76
|
+
| `ConsentManager` | Class | Record and query user consent |
|
|
77
|
+
| `CookieConsentManager` | Class | Browser cookie consent banner state |
|
|
78
|
+
| `DataDeletionSystem` | Class | Right-to-erasure request processing |
|
|
79
|
+
| `DataExportSystem` | Class | Right-to-portability data export |
|
|
80
|
+
| `DataAnonymization` | Class | Anonymize user data while preserving analytics |
|
|
81
|
+
| `DataBreachManager` | Class | Breach detection, notification, and reporting |
|
|
82
|
+
| `PrivacyPolicyManager` | Class | Version and publish privacy policies |
|
|
83
|
+
| `InMemoryGDPRStorage` | Class | In-memory GDPR storage for testing |
|
|
84
|
+
| `InMemoryBreachStorage` | Class | In-memory breach storage for testing |
|
|
85
|
+
|
|
86
|
+
## JOSHUA Alignment
|
|
87
|
+
|
|
88
|
+
- **Hermetic**: Security boundaries are sealed — auth checks happen at middleware, never inside business logic
|
|
89
|
+
- **Sovereign**: All security infrastructure runs in your deployment, no external auth service required
|
|
90
|
+
- **Justifiable**: Every security header and policy has a documented reason (CSP prevents XSS, HSTS forces HTTPS, etc.)
|
|
91
|
+
|
|
92
|
+
## Related Packages
|
|
93
|
+
|
|
94
|
+
- `@revealui/auth` — Session-based authentication (login, password reset, OAuth)
|
|
95
|
+
- `@revealui/core` — Applies security middleware to CMS routes
|
|
96
|
+
- `@revealui/contracts` — Shared types for roles, permissions, consent records
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AuditReportGenerator,
|
|
3
|
+
AuditSystem,
|
|
4
|
+
AuditTrail,
|
|
5
|
+
InMemoryAuditStorage,
|
|
6
|
+
audit,
|
|
7
|
+
createAuditMiddleware,
|
|
8
|
+
signAuditEntry,
|
|
9
|
+
verifyAuditEntry
|
|
10
|
+
} from "./chunk-Q5KAPSST.js";
|
|
11
|
+
export {
|
|
12
|
+
AuditReportGenerator,
|
|
13
|
+
AuditSystem,
|
|
14
|
+
AuditTrail,
|
|
15
|
+
InMemoryAuditStorage,
|
|
16
|
+
audit,
|
|
17
|
+
createAuditMiddleware,
|
|
18
|
+
signAuditEntry,
|
|
19
|
+
verifyAuditEntry
|
|
20
|
+
};
|
|
21
|
+
//# sourceMappingURL=audit-UF7PIYBU.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -0,0 +1,429 @@
|
|
|
1
|
+
// src/audit.ts
|
|
2
|
+
var AuditSystem = class {
|
|
3
|
+
storage;
|
|
4
|
+
filters = [];
|
|
5
|
+
constructor(storage) {
|
|
6
|
+
this.storage = storage;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Replace the backing storage (e.g. swap InMemory for Postgres at startup).
|
|
10
|
+
* Events already written to the old storage are NOT migrated.
|
|
11
|
+
*/
|
|
12
|
+
setStorage(storage) {
|
|
13
|
+
this.storage = storage;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Log audit event
|
|
17
|
+
*/
|
|
18
|
+
async log(event) {
|
|
19
|
+
const fullEvent = {
|
|
20
|
+
...event,
|
|
21
|
+
id: crypto.randomUUID(),
|
|
22
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
23
|
+
};
|
|
24
|
+
const shouldLog = this.filters.every((filter) => filter(fullEvent));
|
|
25
|
+
if (!shouldLog) {
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
await this.storage.write(fullEvent);
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Log authentication event
|
|
32
|
+
*/
|
|
33
|
+
async logAuth(type, actorId, result, metadata) {
|
|
34
|
+
await this.log({
|
|
35
|
+
type,
|
|
36
|
+
severity: result === "failure" ? "medium" : "low",
|
|
37
|
+
actor: {
|
|
38
|
+
id: actorId,
|
|
39
|
+
type: "user"
|
|
40
|
+
},
|
|
41
|
+
action: type.replace("auth.", ""),
|
|
42
|
+
result,
|
|
43
|
+
metadata
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Log data access event
|
|
48
|
+
*/
|
|
49
|
+
async logDataAccess(action, actorId, resourceType, resourceId, result, changes) {
|
|
50
|
+
await this.log({
|
|
51
|
+
type: `data.${action}`,
|
|
52
|
+
severity: action === "delete" ? "high" : "medium",
|
|
53
|
+
actor: {
|
|
54
|
+
id: actorId,
|
|
55
|
+
type: "user"
|
|
56
|
+
},
|
|
57
|
+
resource: {
|
|
58
|
+
type: resourceType,
|
|
59
|
+
id: resourceId
|
|
60
|
+
},
|
|
61
|
+
action,
|
|
62
|
+
result,
|
|
63
|
+
changes
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Log permission change
|
|
68
|
+
*/
|
|
69
|
+
async logPermissionChange(action, actorId, targetUserId, permission, result) {
|
|
70
|
+
await this.log({
|
|
71
|
+
type: `permission.${action}`,
|
|
72
|
+
severity: "high",
|
|
73
|
+
actor: {
|
|
74
|
+
id: actorId,
|
|
75
|
+
type: "user"
|
|
76
|
+
},
|
|
77
|
+
resource: {
|
|
78
|
+
type: "user",
|
|
79
|
+
id: targetUserId
|
|
80
|
+
},
|
|
81
|
+
action,
|
|
82
|
+
result,
|
|
83
|
+
metadata: {
|
|
84
|
+
permission
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Log security event
|
|
90
|
+
*/
|
|
91
|
+
async logSecurityEvent(type, severity, actorId, message, metadata) {
|
|
92
|
+
await this.log({
|
|
93
|
+
type: `security.${type}`,
|
|
94
|
+
severity,
|
|
95
|
+
actor: {
|
|
96
|
+
id: actorId,
|
|
97
|
+
type: "user"
|
|
98
|
+
},
|
|
99
|
+
action: type,
|
|
100
|
+
result: "failure",
|
|
101
|
+
message,
|
|
102
|
+
metadata
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Log GDPR event
|
|
107
|
+
*/
|
|
108
|
+
async logGDPREvent(type, actorId, result, metadata) {
|
|
109
|
+
await this.log({
|
|
110
|
+
type: `gdpr.${type}`,
|
|
111
|
+
severity: "high",
|
|
112
|
+
actor: {
|
|
113
|
+
id: actorId,
|
|
114
|
+
type: "user"
|
|
115
|
+
},
|
|
116
|
+
action: type,
|
|
117
|
+
result,
|
|
118
|
+
metadata
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Query audit logs
|
|
123
|
+
*/
|
|
124
|
+
async query(query) {
|
|
125
|
+
return this.storage.query(query);
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Count audit logs
|
|
129
|
+
*/
|
|
130
|
+
async count(query) {
|
|
131
|
+
return this.storage.count(query);
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Add filter
|
|
135
|
+
*/
|
|
136
|
+
addFilter(filter) {
|
|
137
|
+
this.filters.push(filter);
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Remove filter
|
|
141
|
+
*/
|
|
142
|
+
removeFilter(filter) {
|
|
143
|
+
const index = this.filters.indexOf(filter);
|
|
144
|
+
if (index > -1) {
|
|
145
|
+
this.filters.splice(index, 1);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
var InMemoryAuditStorage = class {
|
|
150
|
+
events = [];
|
|
151
|
+
maxEvents;
|
|
152
|
+
constructor(maxEvents = 1e4) {
|
|
153
|
+
this.maxEvents = maxEvents;
|
|
154
|
+
}
|
|
155
|
+
async write(event) {
|
|
156
|
+
this.events.push(event);
|
|
157
|
+
if (this.events.length > this.maxEvents) {
|
|
158
|
+
this.events.shift();
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
async query(query) {
|
|
162
|
+
let results = [...this.events];
|
|
163
|
+
if (query.types && query.types.length > 0) {
|
|
164
|
+
results = results.filter((e) => query.types?.includes(e.type));
|
|
165
|
+
}
|
|
166
|
+
if (query.actorId) {
|
|
167
|
+
results = results.filter((e) => e.actor.id === query.actorId);
|
|
168
|
+
}
|
|
169
|
+
if (query.resourceType) {
|
|
170
|
+
results = results.filter((e) => e.resource?.type === query.resourceType);
|
|
171
|
+
}
|
|
172
|
+
if (query.resourceId) {
|
|
173
|
+
results = results.filter((e) => e.resource?.id === query.resourceId);
|
|
174
|
+
}
|
|
175
|
+
if (query.startDate) {
|
|
176
|
+
const startDate = query.startDate;
|
|
177
|
+
results = results.filter((e) => new Date(e.timestamp) >= startDate);
|
|
178
|
+
}
|
|
179
|
+
if (query.endDate) {
|
|
180
|
+
const endDate = query.endDate;
|
|
181
|
+
results = results.filter((e) => new Date(e.timestamp) <= endDate);
|
|
182
|
+
}
|
|
183
|
+
if (query.severity && query.severity.length > 0) {
|
|
184
|
+
results = results.filter((e) => query.severity?.includes(e.severity));
|
|
185
|
+
}
|
|
186
|
+
if (query.result && query.result.length > 0) {
|
|
187
|
+
results = results.filter((e) => query.result?.includes(e.result));
|
|
188
|
+
}
|
|
189
|
+
results.sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime());
|
|
190
|
+
const offset = query.offset || 0;
|
|
191
|
+
const limit = query.limit || 100;
|
|
192
|
+
return results.slice(offset, offset + limit);
|
|
193
|
+
}
|
|
194
|
+
async count(query) {
|
|
195
|
+
const results = await this.query({ ...query, limit: void 0, offset: void 0 });
|
|
196
|
+
return results.length;
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Clear all events
|
|
200
|
+
*/
|
|
201
|
+
clear() {
|
|
202
|
+
this.events = [];
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Get all events
|
|
206
|
+
*/
|
|
207
|
+
getAll() {
|
|
208
|
+
return [...this.events];
|
|
209
|
+
}
|
|
210
|
+
};
|
|
211
|
+
function AuditTrail(type, action, options) {
|
|
212
|
+
return (_target, _propertyKey, descriptor) => {
|
|
213
|
+
const originalMethod = descriptor.value;
|
|
214
|
+
descriptor.value = async function(...args) {
|
|
215
|
+
const actorId = this.user?.id || "system";
|
|
216
|
+
const before = options?.captureChanges ? args[0] : void 0;
|
|
217
|
+
let result = "success";
|
|
218
|
+
let error;
|
|
219
|
+
try {
|
|
220
|
+
const returnValue = await originalMethod.apply(this, args);
|
|
221
|
+
if (this.audit) {
|
|
222
|
+
await this.audit.log({
|
|
223
|
+
type,
|
|
224
|
+
severity: options?.severity || "medium",
|
|
225
|
+
actor: {
|
|
226
|
+
id: actorId,
|
|
227
|
+
type: "user"
|
|
228
|
+
},
|
|
229
|
+
resource: options?.resourceType ? {
|
|
230
|
+
type: options.resourceType,
|
|
231
|
+
id: args[0]?.id || "unknown"
|
|
232
|
+
} : void 0,
|
|
233
|
+
action,
|
|
234
|
+
result,
|
|
235
|
+
changes: options?.captureChanges ? {
|
|
236
|
+
before,
|
|
237
|
+
after: returnValue
|
|
238
|
+
} : void 0
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
return returnValue;
|
|
242
|
+
} catch (err) {
|
|
243
|
+
result = "failure";
|
|
244
|
+
error = err;
|
|
245
|
+
if (this.audit) {
|
|
246
|
+
await this.audit.log({
|
|
247
|
+
type,
|
|
248
|
+
severity: "high",
|
|
249
|
+
actor: {
|
|
250
|
+
id: actorId,
|
|
251
|
+
type: "user"
|
|
252
|
+
},
|
|
253
|
+
resource: options?.resourceType ? {
|
|
254
|
+
type: options.resourceType,
|
|
255
|
+
id: args[0]?.id || "unknown"
|
|
256
|
+
} : void 0,
|
|
257
|
+
action,
|
|
258
|
+
result,
|
|
259
|
+
message: error.message
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
throw error;
|
|
263
|
+
}
|
|
264
|
+
};
|
|
265
|
+
return descriptor;
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
function createAuditMiddleware(audit2, getUser) {
|
|
269
|
+
return async (request, next) => {
|
|
270
|
+
const user = getUser(request);
|
|
271
|
+
const startTime = Date.now();
|
|
272
|
+
try {
|
|
273
|
+
const response = await next();
|
|
274
|
+
await audit2.log({
|
|
275
|
+
type: "data.read",
|
|
276
|
+
severity: "low",
|
|
277
|
+
actor: {
|
|
278
|
+
id: user.id,
|
|
279
|
+
type: "user",
|
|
280
|
+
ip: user.ip,
|
|
281
|
+
userAgent: user.userAgent
|
|
282
|
+
},
|
|
283
|
+
action: request.method,
|
|
284
|
+
result: "success",
|
|
285
|
+
metadata: {
|
|
286
|
+
path: request.url,
|
|
287
|
+
duration: Date.now() - startTime,
|
|
288
|
+
status: response.status
|
|
289
|
+
}
|
|
290
|
+
});
|
|
291
|
+
return response;
|
|
292
|
+
} catch (error) {
|
|
293
|
+
await audit2.log({
|
|
294
|
+
type: "data.read",
|
|
295
|
+
severity: "medium",
|
|
296
|
+
actor: {
|
|
297
|
+
id: user.id,
|
|
298
|
+
type: "user",
|
|
299
|
+
ip: user.ip,
|
|
300
|
+
userAgent: user.userAgent
|
|
301
|
+
},
|
|
302
|
+
action: request.method,
|
|
303
|
+
result: "failure",
|
|
304
|
+
message: error instanceof Error ? error.message : "Unknown error",
|
|
305
|
+
metadata: {
|
|
306
|
+
path: request.url,
|
|
307
|
+
duration: Date.now() - startTime
|
|
308
|
+
}
|
|
309
|
+
});
|
|
310
|
+
throw error;
|
|
311
|
+
}
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
var AuditReportGenerator = class {
|
|
315
|
+
constructor(audit2) {
|
|
316
|
+
this.audit = audit2;
|
|
317
|
+
}
|
|
318
|
+
/**
|
|
319
|
+
* Generate security report
|
|
320
|
+
*/
|
|
321
|
+
async generateSecurityReport(startDate, endDate) {
|
|
322
|
+
const allEvents = await this.audit.query({
|
|
323
|
+
startDate,
|
|
324
|
+
endDate
|
|
325
|
+
});
|
|
326
|
+
const securityViolations = allEvents.filter((e) => e.type.startsWith("security.")).length;
|
|
327
|
+
const failedLogins = allEvents.filter((e) => e.type === "auth.failed_login").length;
|
|
328
|
+
const permissionChanges = allEvents.filter((e) => e.type.startsWith("permission.")).length;
|
|
329
|
+
const dataExports = allEvents.filter((e) => e.type === "data.export").length;
|
|
330
|
+
const criticalEvents = allEvents.filter((e) => e.severity === "critical");
|
|
331
|
+
return {
|
|
332
|
+
totalEvents: allEvents.length,
|
|
333
|
+
securityViolations,
|
|
334
|
+
failedLogins,
|
|
335
|
+
permissionChanges,
|
|
336
|
+
dataExports,
|
|
337
|
+
criticalEvents
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
/**
|
|
341
|
+
* Generate user activity report
|
|
342
|
+
*/
|
|
343
|
+
async generateUserActivityReport(userId, startDate, endDate) {
|
|
344
|
+
const events = await this.audit.query({
|
|
345
|
+
actorId: userId,
|
|
346
|
+
startDate,
|
|
347
|
+
endDate
|
|
348
|
+
});
|
|
349
|
+
const actionsByType = events.reduce(
|
|
350
|
+
(acc, event) => {
|
|
351
|
+
acc[event.type] = (acc[event.type] || 0) + 1;
|
|
352
|
+
return acc;
|
|
353
|
+
},
|
|
354
|
+
{}
|
|
355
|
+
);
|
|
356
|
+
const failedActions = events.filter((e) => e.result === "failure").length;
|
|
357
|
+
return {
|
|
358
|
+
totalActions: events.length,
|
|
359
|
+
actionsByType,
|
|
360
|
+
failedActions,
|
|
361
|
+
recentActions: events.slice(0, 10)
|
|
362
|
+
};
|
|
363
|
+
}
|
|
364
|
+
/**
|
|
365
|
+
* Generate compliance report
|
|
366
|
+
*/
|
|
367
|
+
async generateComplianceReport(startDate, endDate) {
|
|
368
|
+
const events = await this.audit.query({
|
|
369
|
+
startDate,
|
|
370
|
+
endDate
|
|
371
|
+
});
|
|
372
|
+
const dataAccesses = events.filter((e) => e.type === "data.read").length;
|
|
373
|
+
const dataModifications = events.filter(
|
|
374
|
+
(e) => e.type === "data.update" || e.type === "data.create"
|
|
375
|
+
).length;
|
|
376
|
+
const dataDeletions = events.filter((e) => e.type === "data.delete").length;
|
|
377
|
+
const gdprRequests = events.filter((e) => e.type.startsWith("gdpr.")).length;
|
|
378
|
+
const auditTrailComplete = this.checkAuditTrailContinuity(events);
|
|
379
|
+
return {
|
|
380
|
+
dataAccesses,
|
|
381
|
+
dataModifications,
|
|
382
|
+
dataDeletions,
|
|
383
|
+
gdprRequests,
|
|
384
|
+
auditTrailComplete
|
|
385
|
+
};
|
|
386
|
+
}
|
|
387
|
+
/**
|
|
388
|
+
* Check audit trail continuity
|
|
389
|
+
*/
|
|
390
|
+
checkAuditTrailContinuity(events) {
|
|
391
|
+
if (events.length === 0) return true;
|
|
392
|
+
const sorted = events.sort(
|
|
393
|
+
(a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime()
|
|
394
|
+
);
|
|
395
|
+
return sorted.length > 0;
|
|
396
|
+
}
|
|
397
|
+
};
|
|
398
|
+
async function signAuditEntry(entry, secret) {
|
|
399
|
+
const { createHmac } = await import("crypto");
|
|
400
|
+
const canonical = JSON.stringify({
|
|
401
|
+
timestamp: entry.timestamp,
|
|
402
|
+
eventType: entry.eventType,
|
|
403
|
+
severity: entry.severity,
|
|
404
|
+
agentId: entry.agentId,
|
|
405
|
+
payload: entry.payload
|
|
406
|
+
});
|
|
407
|
+
return createHmac("sha256", secret).update(canonical).digest("hex");
|
|
408
|
+
}
|
|
409
|
+
async function verifyAuditEntry(entry, signature, secret) {
|
|
410
|
+
const { timingSafeEqual } = await import("crypto");
|
|
411
|
+
const expected = await signAuditEntry(entry, secret);
|
|
412
|
+
if (expected.length !== signature.length) {
|
|
413
|
+
return false;
|
|
414
|
+
}
|
|
415
|
+
return timingSafeEqual(Buffer.from(expected), Buffer.from(signature));
|
|
416
|
+
}
|
|
417
|
+
var audit = new AuditSystem(new InMemoryAuditStorage());
|
|
418
|
+
|
|
419
|
+
export {
|
|
420
|
+
AuditSystem,
|
|
421
|
+
InMemoryAuditStorage,
|
|
422
|
+
AuditTrail,
|
|
423
|
+
createAuditMiddleware,
|
|
424
|
+
AuditReportGenerator,
|
|
425
|
+
signAuditEntry,
|
|
426
|
+
verifyAuditEntry,
|
|
427
|
+
audit
|
|
428
|
+
};
|
|
429
|
+
//# sourceMappingURL=chunk-Q5KAPSST.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/audit.ts"],"sourcesContent":["/**\n * Audit Logging System\n *\n * Track security-relevant events and user actions for compliance\n */\n\nexport type AuditEventType =\n | 'auth.login'\n | 'auth.logout'\n | 'auth.failed_login'\n | 'auth.password_change'\n | 'auth.password_reset'\n | 'auth.mfa_enabled'\n | 'auth.mfa_disabled'\n | 'user.create'\n | 'user.update'\n | 'user.delete'\n | 'user.view'\n | 'data.create'\n | 'data.read'\n | 'data.update'\n | 'data.delete'\n | 'data.export'\n | 'permission.grant'\n | 'permission.revoke'\n | 'role.assign'\n | 'role.remove'\n | 'config.change'\n | 'security.violation'\n | 'security.alert'\n | 'gdpr.consent'\n | 'gdpr.data_request'\n | 'gdpr.data_deletion'\n | `data.${string}`\n | `permission.${string}`\n | `security.${string}`\n | `gdpr.${string}`;\n\nexport type AuditSeverity = 'low' | 'medium' | 'high' | 'critical';\n\nexport interface AuditEvent {\n id: string;\n timestamp: string;\n type: AuditEventType;\n severity: AuditSeverity;\n actor: {\n id: string;\n type: 'user' | 'system' | 'api';\n ip?: string;\n userAgent?: string;\n };\n resource?: {\n type: string;\n id: string;\n name?: string;\n };\n action: string;\n result: 'success' | 'failure' | 'partial';\n changes?: {\n before?: Record<string, unknown>;\n after?: Record<string, unknown>;\n };\n metadata?: Record<string, unknown>;\n message?: string;\n}\n\nexport interface AuditQuery {\n types?: AuditEventType[];\n actorId?: string;\n resourceType?: string;\n resourceId?: string;\n startDate?: Date;\n endDate?: Date;\n severity?: AuditSeverity[];\n result?: ('success' | 'failure' | 'partial')[];\n limit?: number;\n offset?: number;\n}\n\nexport interface AuditStorage {\n write(event: AuditEvent): Promise<void>;\n query(query: AuditQuery): Promise<AuditEvent[]>;\n count(query: AuditQuery): Promise<number>;\n}\n\n/**\n * Audit logging system\n */\nexport class AuditSystem {\n private storage: AuditStorage;\n private filters: Array<(event: AuditEvent) => boolean> = [];\n\n constructor(storage: AuditStorage) {\n this.storage = storage;\n }\n\n /**\n * Replace the backing storage (e.g. swap InMemory for Postgres at startup).\n * Events already written to the old storage are NOT migrated.\n */\n setStorage(storage: AuditStorage): void {\n this.storage = storage;\n }\n\n /**\n * Log audit event\n */\n async log(event: Omit<AuditEvent, 'id' | 'timestamp'>): Promise<void> {\n const fullEvent: AuditEvent = {\n ...event,\n id: crypto.randomUUID(),\n timestamp: new Date().toISOString(),\n };\n\n // Apply filters\n const shouldLog = this.filters.every((filter) => filter(fullEvent));\n\n if (!shouldLog) {\n return;\n }\n\n await this.storage.write(fullEvent);\n }\n\n /**\n * Log authentication event\n */\n async logAuth(\n type: Extract<\n AuditEventType,\n 'auth.login' | 'auth.logout' | 'auth.failed_login' | 'auth.password_change'\n >,\n actorId: string,\n result: 'success' | 'failure',\n metadata?: Record<string, unknown>,\n ): Promise<void> {\n await this.log({\n type,\n severity: result === 'failure' ? 'medium' : 'low',\n actor: {\n id: actorId,\n type: 'user',\n },\n action: (type as string).replace('auth.', ''),\n result,\n metadata,\n });\n }\n\n /**\n * Log data access event\n */\n async logDataAccess(\n action: 'create' | 'read' | 'update' | 'delete',\n actorId: string,\n resourceType: string,\n resourceId: string,\n result: 'success' | 'failure',\n changes?: { before?: Record<string, unknown>; after?: Record<string, unknown> },\n ): Promise<void> {\n await this.log({\n type: `data.${action}` as AuditEventType,\n severity: action === 'delete' ? 'high' : 'medium',\n actor: {\n id: actorId,\n type: 'user',\n },\n resource: {\n type: resourceType,\n id: resourceId,\n },\n action,\n result,\n changes,\n });\n }\n\n /**\n * Log permission change\n */\n async logPermissionChange(\n action: 'grant' | 'revoke',\n actorId: string,\n targetUserId: string,\n permission: string,\n result: 'success' | 'failure',\n ): Promise<void> {\n await this.log({\n type: `permission.${action}` as AuditEventType,\n severity: 'high',\n actor: {\n id: actorId,\n type: 'user',\n },\n resource: {\n type: 'user',\n id: targetUserId,\n },\n action,\n result,\n metadata: {\n permission,\n },\n });\n }\n\n /**\n * Log security event\n */\n async logSecurityEvent(\n type: 'violation' | 'alert',\n severity: AuditSeverity,\n actorId: string,\n message: string,\n metadata?: Record<string, unknown>,\n ): Promise<void> {\n await this.log({\n type: `security.${type}` as AuditEventType,\n severity,\n actor: {\n id: actorId,\n type: 'user',\n },\n action: type,\n result: 'failure',\n message,\n metadata,\n });\n }\n\n /**\n * Log GDPR event\n */\n async logGDPREvent(\n type: 'consent' | 'data_request' | 'data_deletion',\n actorId: string,\n result: 'success' | 'failure',\n metadata?: Record<string, unknown>,\n ): Promise<void> {\n await this.log({\n type: `gdpr.${type}` as AuditEventType,\n severity: 'high',\n actor: {\n id: actorId,\n type: 'user',\n },\n action: type,\n result,\n metadata,\n });\n }\n\n /**\n * Query audit logs\n */\n async query(query: AuditQuery): Promise<AuditEvent[]> {\n return this.storage.query(query);\n }\n\n /**\n * Count audit logs\n */\n async count(query: AuditQuery): Promise<number> {\n return this.storage.count(query);\n }\n\n /**\n * Add filter\n */\n addFilter(filter: (event: AuditEvent) => boolean): void {\n this.filters.push(filter);\n }\n\n /**\n * Remove filter\n */\n removeFilter(filter: (event: AuditEvent) => boolean): void {\n const index = this.filters.indexOf(filter);\n if (index > -1) {\n this.filters.splice(index, 1);\n }\n }\n}\n\n/**\n * In-memory audit storage (for development)\n */\nexport class InMemoryAuditStorage implements AuditStorage {\n private events: AuditEvent[] = [];\n private maxEvents: number;\n\n constructor(maxEvents: number = 10000) {\n this.maxEvents = maxEvents;\n }\n\n async write(event: AuditEvent): Promise<void> {\n this.events.push(event);\n\n // Trim old events\n if (this.events.length > this.maxEvents) {\n this.events.shift();\n }\n }\n\n async query(query: AuditQuery): Promise<AuditEvent[]> {\n let results = [...this.events];\n\n // Filter by type\n if (query.types && query.types.length > 0) {\n results = results.filter((e) => query.types?.includes(e.type));\n }\n\n // Filter by actor\n if (query.actorId) {\n results = results.filter((e) => e.actor.id === query.actorId);\n }\n\n // Filter by resource\n if (query.resourceType) {\n results = results.filter((e) => e.resource?.type === query.resourceType);\n }\n\n if (query.resourceId) {\n results = results.filter((e) => e.resource?.id === query.resourceId);\n }\n\n // Filter by date range\n if (query.startDate) {\n const startDate = query.startDate;\n results = results.filter((e) => new Date(e.timestamp) >= startDate);\n }\n\n if (query.endDate) {\n const endDate = query.endDate;\n results = results.filter((e) => new Date(e.timestamp) <= endDate);\n }\n\n // Filter by severity\n if (query.severity && query.severity.length > 0) {\n results = results.filter((e) => query.severity?.includes(e.severity));\n }\n\n // Filter by result\n if (query.result && query.result.length > 0) {\n results = results.filter((e) => query.result?.includes(e.result));\n }\n\n // Sort by timestamp (newest first)\n results.sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime());\n\n // Apply pagination\n const offset = query.offset || 0;\n const limit = query.limit || 100;\n\n return results.slice(offset, offset + limit);\n }\n\n async count(query: AuditQuery): Promise<number> {\n const results = await this.query({ ...query, limit: undefined, offset: undefined });\n return results.length;\n }\n\n /**\n * Clear all events\n */\n clear(): void {\n this.events = [];\n }\n\n /**\n * Get all events\n */\n getAll(): AuditEvent[] {\n return [...this.events];\n }\n}\n\n/**\n * Audit trail decorator\n */\nexport function AuditTrail(\n type: AuditEventType,\n action: string,\n options?: {\n severity?: AuditSeverity;\n captureChanges?: boolean;\n resourceType?: string;\n },\n) {\n return (_target: object, _propertyKey: string, descriptor: PropertyDescriptor) => {\n const originalMethod = descriptor.value;\n\n descriptor.value = async function (\n this: { user?: { id?: string }; audit?: AuditSystem },\n ...args: unknown[]\n ) {\n const actorId = this.user?.id || 'system';\n const before = options?.captureChanges ? args[0] : undefined;\n\n let result: 'success' | 'failure' | 'partial' = 'success';\n let error: Error | undefined;\n\n try {\n const returnValue = await originalMethod.apply(this, args);\n\n // Log audit event\n if (this.audit) {\n await this.audit.log({\n type,\n severity: options?.severity || 'medium',\n actor: {\n id: actorId,\n type: 'user',\n },\n resource: options?.resourceType\n ? {\n type: options.resourceType,\n id: (args[0] as { id?: string })?.id || 'unknown',\n }\n : undefined,\n action,\n result,\n changes: options?.captureChanges\n ? {\n before: before as Record<string, unknown> | undefined,\n after: returnValue as Record<string, unknown> | undefined,\n }\n : undefined,\n });\n }\n\n return returnValue;\n } catch (err) {\n result = 'failure';\n error = err as Error;\n\n // Log failure\n if (this.audit) {\n await this.audit.log({\n type,\n severity: 'high',\n actor: {\n id: actorId,\n type: 'user',\n },\n resource: options?.resourceType\n ? {\n type: options.resourceType,\n id: (args[0] as { id?: string })?.id || 'unknown',\n }\n : undefined,\n action,\n result,\n message: error.message,\n });\n }\n\n throw error;\n }\n };\n\n return descriptor;\n };\n}\n\n/**\n * Audit middleware\n */\nexport function createAuditMiddleware<TRequest = unknown, TResponse = unknown>(\n audit: AuditSystem,\n getUser: (request: TRequest) => { id: string; ip?: string; userAgent?: string },\n) {\n return async (\n request: TRequest & { method: string; url: string },\n next: () => Promise<TResponse & { status?: number }>,\n ) => {\n const user = getUser(request);\n const startTime = Date.now();\n\n try {\n const response = await next();\n\n // Log successful request\n await audit.log({\n type: 'data.read',\n severity: 'low',\n actor: {\n id: user.id,\n type: 'user',\n ip: user.ip,\n userAgent: user.userAgent,\n },\n action: request.method,\n result: 'success',\n metadata: {\n path: request.url,\n duration: Date.now() - startTime,\n status: response.status,\n },\n });\n\n return response;\n } catch (error) {\n // Log failed request\n await audit.log({\n type: 'data.read',\n severity: 'medium',\n actor: {\n id: user.id,\n type: 'user',\n ip: user.ip,\n userAgent: user.userAgent,\n },\n action: request.method,\n result: 'failure',\n message: error instanceof Error ? error.message : 'Unknown error',\n metadata: {\n path: request.url,\n duration: Date.now() - startTime,\n },\n });\n\n throw error;\n }\n };\n}\n\n/**\n * Audit report generator\n */\nexport class AuditReportGenerator {\n constructor(private audit: AuditSystem) {}\n\n /**\n * Generate security report\n */\n async generateSecurityReport(\n startDate: Date,\n endDate: Date,\n ): Promise<{\n totalEvents: number;\n securityViolations: number;\n failedLogins: number;\n permissionChanges: number;\n dataExports: number;\n criticalEvents: AuditEvent[];\n }> {\n const allEvents = await this.audit.query({\n startDate,\n endDate,\n });\n\n const securityViolations = allEvents.filter((e) => e.type.startsWith('security.')).length;\n\n const failedLogins = allEvents.filter((e) => e.type === 'auth.failed_login').length;\n\n const permissionChanges = allEvents.filter((e) => e.type.startsWith('permission.')).length;\n\n const dataExports = allEvents.filter((e) => e.type === 'data.export').length;\n\n const criticalEvents = allEvents.filter((e) => e.severity === 'critical');\n\n return {\n totalEvents: allEvents.length,\n securityViolations,\n failedLogins,\n permissionChanges,\n dataExports,\n criticalEvents,\n };\n }\n\n /**\n * Generate user activity report\n */\n async generateUserActivityReport(\n userId: string,\n startDate: Date,\n endDate: Date,\n ): Promise<{\n totalActions: number;\n actionsByType: Record<string, number>;\n failedActions: number;\n recentActions: AuditEvent[];\n }> {\n const events = await this.audit.query({\n actorId: userId,\n startDate,\n endDate,\n });\n\n const actionsByType = events.reduce(\n (acc, event) => {\n acc[event.type] = (acc[event.type] || 0) + 1;\n return acc;\n },\n {} as Record<string, number>,\n );\n\n const failedActions = events.filter((e) => e.result === 'failure').length;\n\n return {\n totalActions: events.length,\n actionsByType,\n failedActions,\n recentActions: events.slice(0, 10),\n };\n }\n\n /**\n * Generate compliance report\n */\n async generateComplianceReport(\n startDate: Date,\n endDate: Date,\n ): Promise<{\n dataAccesses: number;\n dataModifications: number;\n dataDeletions: number;\n gdprRequests: number;\n auditTrailComplete: boolean;\n }> {\n const events = await this.audit.query({\n startDate,\n endDate,\n });\n\n const dataAccesses = events.filter((e) => e.type === 'data.read').length;\n\n const dataModifications = events.filter(\n (e) => e.type === 'data.update' || e.type === 'data.create',\n ).length;\n\n const dataDeletions = events.filter((e) => e.type === 'data.delete').length;\n\n const gdprRequests = events.filter((e) => e.type.startsWith('gdpr.')).length;\n\n // Check if audit trail is complete (no gaps)\n const auditTrailComplete = this.checkAuditTrailContinuity(events);\n\n return {\n dataAccesses,\n dataModifications,\n dataDeletions,\n gdprRequests,\n auditTrailComplete,\n };\n }\n\n /**\n * Check audit trail continuity\n */\n private checkAuditTrailContinuity(events: AuditEvent[]): boolean {\n if (events.length === 0) return true;\n\n // Sort by timestamp\n const sorted = events.sort(\n (a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime(),\n );\n\n // Check for gaps (simplified - just check if we have events)\n return sorted.length > 0;\n }\n}\n\n// =============================================================================\n// Audit Log Integrity — HMAC-SHA256 Signing\n// =============================================================================\n\n/** Fields included in the HMAC signature for tamper detection. */\ninterface SignableFields {\n timestamp: string;\n eventType: string;\n severity: string;\n agentId: string;\n payload: unknown;\n}\n\n/**\n * Compute an HMAC-SHA256 signature over the canonical fields of an audit entry.\n *\n * The signature covers `timestamp`, `eventType`, `severity`, `agentId`, and\n * `payload` — the immutable core of every audit record. Changing any of\n * these fields after signing will cause verification to fail.\n *\n * @param entry - The audit entry fields to sign\n * @param secret - The HMAC secret key\n * @returns Hex-encoded HMAC-SHA256 signature\n */\nexport async function signAuditEntry(entry: SignableFields, secret: string): Promise<string> {\n const { createHmac } = await import('node:crypto');\n const canonical = JSON.stringify({\n timestamp: entry.timestamp,\n eventType: entry.eventType,\n severity: entry.severity,\n agentId: entry.agentId,\n payload: entry.payload,\n });\n return createHmac('sha256', secret).update(canonical).digest('hex');\n}\n\n/**\n * Verify an HMAC-SHA256 signature against the canonical fields of an audit entry.\n *\n * Uses timing-safe comparison to prevent timing attacks.\n *\n * @param entry - The audit entry fields to verify\n * @param signature - The hex-encoded HMAC-SHA256 signature to verify\n * @param secret - The HMAC secret key\n * @returns True if the signature is valid\n */\nexport async function verifyAuditEntry(\n entry: SignableFields,\n signature: string,\n secret: string,\n): Promise<boolean> {\n const { timingSafeEqual } = await import('node:crypto');\n const expected = await signAuditEntry(entry, secret);\n\n // Lengths must match for timingSafeEqual\n if (expected.length !== signature.length) {\n return false;\n }\n\n return timingSafeEqual(Buffer.from(expected), Buffer.from(signature));\n}\n\n/**\n * Global audit system\n */\nexport const audit = new AuditSystem(new InMemoryAuditStorage());\n"],"mappings":"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","names":["audit"]}
|