autotel-audit 0.2.1 → 0.3.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.cjs +486 -433
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +175 -150
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.ts +175 -150
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +485 -431
- package/dist/index.js.map +1 -1
- package/package.json +5 -5
- package/src/context.ts +81 -13
- package/src/index.test.ts +50 -0
- package/src/index.ts +47 -8
- package/src/security.test.ts +3 -0
- package/src/security.ts +43 -13
package/dist/index.d.ts
CHANGED
|
@@ -1,15 +1,27 @@
|
|
|
1
|
-
import { RequestLogger } from
|
|
2
|
-
import { SecuritySeverity } from
|
|
3
|
-
export { SecuritySeverity } from 'autotel/security-schema';
|
|
1
|
+
import { RequestLogger } from "autotel";
|
|
2
|
+
import { SecuritySeverity } from "autotel/security-schema";
|
|
4
3
|
|
|
4
|
+
//#region src/context.d.ts
|
|
5
5
|
interface AuditContext {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
6
|
+
traceId: string;
|
|
7
|
+
spanId: string;
|
|
8
|
+
correlationId: string;
|
|
9
|
+
setAttribute(key: string, value: string | number | boolean): void;
|
|
10
|
+
setAttributes(attrs: Record<string, string | number | boolean | string[] | number[] | boolean[]>): void;
|
|
11
11
|
}
|
|
12
|
-
|
|
12
|
+
/**
|
|
13
|
+
* How instrumentation should behave when no trace context is available.
|
|
14
|
+
*
|
|
15
|
+
* - `throw` — fail fast (original behaviour). Use when telemetry is mandatory.
|
|
16
|
+
* - `warn` — run the wrapped handler un-audited and log one warning per action (default).
|
|
17
|
+
* - `skip` — run the wrapped handler un-audited, silently.
|
|
18
|
+
*
|
|
19
|
+
* Telemetry is observability: a missing context should never crash the business
|
|
20
|
+
* logic it wraps, so the default is best-effort (`warn`).
|
|
21
|
+
*/
|
|
22
|
+
type OnMissingContext = 'throw' | 'warn' | 'skip';
|
|
23
|
+
//#endregion
|
|
24
|
+
//#region src/security.d.ts
|
|
13
25
|
/**
|
|
14
26
|
* Security event categories, aligned with OWASP A09:2025
|
|
15
27
|
* (Security Logging & Alerting Failures) and ASVS V7.
|
|
@@ -22,40 +34,46 @@ type SecurityOutcome = 'success' | 'failure' | 'denied' | 'blocked' | 'error';
|
|
|
22
34
|
*/
|
|
23
35
|
type SuggestedSecurityEventName = 'auth.login.success' | 'auth.login.failed' | 'auth.mfa.failed' | 'auth.session.revoked' | 'auth.password.reset' | 'auth.account.locked' | 'access.denied' | 'access.role.changed' | 'access.permission.changed' | 'access.tenant.violation' | 'admin.action' | 'config.changed' | 'secret.accessed' | 'secret.rotation.failed' | 'api_key.created' | 'api_key.revoked' | 'rate_limit.exceeded' | 'validation.failed' | 'webhook.signature.failed' | 'dependency.scan.failed' | 'llm.prompt_injection.detected' | 'llm.tool_call.denied' | 'llm.output.blocked';
|
|
24
36
|
interface SecurityEventMetadata {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
37
|
+
/** Stable, dot-separated event name, e.g. `auth.login.failed`. */
|
|
38
|
+
name: SuggestedSecurityEventName | (string & {});
|
|
39
|
+
category: SecurityEventCategory;
|
|
40
|
+
outcome: SecurityOutcome;
|
|
41
|
+
/** Defaults to `info`. */
|
|
42
|
+
severity?: SecuritySeverity;
|
|
43
|
+
/** Stable identifier of the actor — an id or a `hashIdentifier()` digest, never raw PII. */
|
|
44
|
+
actorId?: string;
|
|
45
|
+
targetType?: string;
|
|
46
|
+
targetId?: string;
|
|
47
|
+
tenantId?: string;
|
|
48
|
+
/** Short machine-readable reason, e.g. `invalid_password`. */
|
|
49
|
+
reason?: string;
|
|
50
|
+
[key: string]: unknown;
|
|
39
51
|
}
|
|
40
52
|
interface SecurityEventOptions {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
53
|
+
ctx?: AuditContext;
|
|
54
|
+
/**
|
|
55
|
+
* Security events are exempt from tail sampling by default —
|
|
56
|
+
* an attack you sampled away is an attack you cannot investigate.
|
|
57
|
+
* Pass `false` to opt out (e.g. very high-volume info events).
|
|
58
|
+
*/
|
|
59
|
+
forceKeep?: boolean;
|
|
60
|
+
emitNow?: boolean;
|
|
61
|
+
logger?: RequestLogger;
|
|
62
|
+
/**
|
|
63
|
+
* Also increment the `autotel.security.events` counter
|
|
64
|
+
* (attributes: event, category, outcome, severity) so security teams
|
|
65
|
+
* can alert on rates without log-based alerting. Default true.
|
|
66
|
+
*
|
|
67
|
+
* Cardinality note: the event name is a counter attribute — keep names
|
|
68
|
+
* to a stable catalogue, never interpolate user input into them.
|
|
69
|
+
*/
|
|
70
|
+
metrics?: boolean;
|
|
71
|
+
/**
|
|
72
|
+
* Behaviour when no trace context can be resolved. Defaults to `warn`
|
|
73
|
+
* (best-effort: record nothing, warn once). A dropped security event is still
|
|
74
|
+
* better than a crashed request — but the warning makes the gap visible.
|
|
75
|
+
*/
|
|
76
|
+
onMissingContext?: OnMissingContext;
|
|
59
77
|
}
|
|
60
78
|
type WithSecurityOptions = SecurityEventOptions;
|
|
61
79
|
/**
|
|
@@ -93,10 +111,10 @@ declare function securityEvent(metadata: SecurityEventMetadata, options?: Securi
|
|
|
93
111
|
*/
|
|
94
112
|
declare function withSecurity<T>(metadata: SecurityEventMetadata, fn: (ctx: AuditContext, logger: RequestLogger) => T | Promise<T>, options?: WithSecurityOptions): Promise<T>;
|
|
95
113
|
interface HashIdentifierOptions {
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
114
|
+
/** Optional salt; use one stable per-deployment salt to defeat rainbow lookups. */
|
|
115
|
+
salt?: string;
|
|
116
|
+
/** Digest length in hex chars (default 16). */
|
|
117
|
+
length?: number;
|
|
100
118
|
}
|
|
101
119
|
/**
|
|
102
120
|
* Stable one-way digest for correlating PII-bearing identifiers
|
|
@@ -105,7 +123,8 @@ interface HashIdentifierOptions {
|
|
|
105
123
|
* NOT for secrets — never log secrets in any form, hashed or not.
|
|
106
124
|
*/
|
|
107
125
|
declare function hashIdentifier(value: string, options?: HashIdentifierOptions): string;
|
|
108
|
-
|
|
126
|
+
//#endregion
|
|
127
|
+
//#region src/security-signals.d.ts
|
|
109
128
|
/**
|
|
110
129
|
* Zero-code security signal derivation from spans you already have.
|
|
111
130
|
*
|
|
@@ -132,112 +151,110 @@ declare function hashIdentifier(value: string, options?: HashIdentifierOptions):
|
|
|
132
151
|
*/
|
|
133
152
|
type AttributeValue = string | number | boolean | Array<null | undefined | string> | Array<null | undefined | number> | Array<null | undefined | boolean>;
|
|
134
153
|
interface MutableSpanLike {
|
|
135
|
-
|
|
136
|
-
|
|
154
|
+
attributes: Record<string, AttributeValue | undefined>;
|
|
155
|
+
setAttribute(key: string, value: AttributeValue): unknown;
|
|
137
156
|
}
|
|
138
157
|
interface ReadableSpanLike {
|
|
139
|
-
|
|
158
|
+
attributes: Record<string, AttributeValue | undefined>;
|
|
140
159
|
}
|
|
141
160
|
interface SecuritySignalProcessor {
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
161
|
+
onStart(span: MutableSpanLike, parentContext?: unknown): void;
|
|
162
|
+
onEnd(span: ReadableSpanLike): void;
|
|
163
|
+
shutdown(): Promise<void>;
|
|
164
|
+
forceFlush(): Promise<void>;
|
|
146
165
|
}
|
|
147
166
|
interface SuspiciousRequestSignal {
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
167
|
+
signal: 'suspicious_request';
|
|
168
|
+
/** Which pattern matched, e.g. `path_traversal`. */
|
|
169
|
+
pattern: string;
|
|
170
|
+
/** The matched request path/URL (as found on the span). */
|
|
171
|
+
target: string;
|
|
153
172
|
}
|
|
154
173
|
interface AuthFailureBurstSignal {
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
174
|
+
signal: 'auth_failure_burst';
|
|
175
|
+
/** Value of the configured key attribute (e.g. client address). */
|
|
176
|
+
key: string;
|
|
177
|
+
/** Denied responses observed inside the window. */
|
|
178
|
+
count: number;
|
|
179
|
+
windowMs: number;
|
|
180
|
+
status: number;
|
|
162
181
|
}
|
|
163
182
|
interface LlmExcessiveTokensSignal {
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
183
|
+
signal: 'llm_excessive_tokens';
|
|
184
|
+
/** Total tokens consumed by the single LLM call. */
|
|
185
|
+
tokens: number;
|
|
186
|
+
maxTokens: number;
|
|
187
|
+
model?: string;
|
|
169
188
|
}
|
|
170
189
|
interface LlmTokenBudgetSignal {
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
190
|
+
signal: 'llm_token_budget_exceeded';
|
|
191
|
+
/** Value of the configured key attribute (e.g. end-user id). */
|
|
192
|
+
key: string;
|
|
193
|
+
/** Tokens consumed inside the window. */
|
|
194
|
+
tokens: number;
|
|
195
|
+
budget: number;
|
|
196
|
+
windowMs: number;
|
|
178
197
|
}
|
|
179
198
|
type SecuritySignal = SuspiciousRequestSignal | AuthFailureBurstSignal | LlmExcessiveTokensSignal | LlmTokenBudgetSignal;
|
|
180
199
|
interface BurstOptions {
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
200
|
+
/** HTTP statuses counted toward a burst. Default `[401, 403]`. */
|
|
201
|
+
statuses?: number[];
|
|
202
|
+
/** Denied responses within the window that trigger a signal. Default 10. */
|
|
203
|
+
threshold?: number;
|
|
204
|
+
/** Sliding window size in milliseconds. Default 60_000. */
|
|
205
|
+
windowMs?: number;
|
|
206
|
+
/**
|
|
207
|
+
* Span attribute identifying the client. Default `client.address`
|
|
208
|
+
* (falls back to `http.client_ip`).
|
|
209
|
+
*/
|
|
210
|
+
keyAttribute?: string;
|
|
211
|
+
/** Max distinct clients tracked (oldest evicted). Default 10_000. */
|
|
212
|
+
maxKeys?: number;
|
|
194
213
|
}
|
|
195
214
|
interface LlmSignalOptions {
|
|
215
|
+
/**
|
|
216
|
+
* Single-call token ceiling (`gen_ai.usage.total_tokens`, or input+output).
|
|
217
|
+
* Default 100_000. Pass `false` to disable the per-call check.
|
|
218
|
+
*/
|
|
219
|
+
maxTokensPerCall?: number | false;
|
|
220
|
+
/**
|
|
221
|
+
* Sliding-window token budget per key — catches slow-drip abuse that
|
|
222
|
+
* stays under the per-call ceiling (OWASP LLM10: Unbounded Consumption).
|
|
223
|
+
* Off unless configured.
|
|
224
|
+
*/
|
|
225
|
+
tokenBudget?: {
|
|
226
|
+
budget: number; /** Window size in milliseconds. Default 300_000 (5 min). */
|
|
227
|
+
windowMs?: number;
|
|
196
228
|
/**
|
|
197
|
-
*
|
|
198
|
-
*
|
|
199
|
-
*/
|
|
200
|
-
maxTokensPerCall?: number | false;
|
|
201
|
-
/**
|
|
202
|
-
* Sliding-window token budget per key — catches slow-drip abuse that
|
|
203
|
-
* stays under the per-call ceiling (OWASP LLM10: Unbounded Consumption).
|
|
204
|
-
* Off unless configured.
|
|
229
|
+
* Span attribute identifying the consumer. Default `enduser.id`
|
|
230
|
+
* (falls back to `client.address`).
|
|
205
231
|
*/
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
windowMs?: number;
|
|
210
|
-
/**
|
|
211
|
-
* Span attribute identifying the consumer. Default `enduser.id`
|
|
212
|
-
* (falls back to `client.address`).
|
|
213
|
-
*/
|
|
214
|
-
keyAttribute?: string;
|
|
215
|
-
/** Max distinct keys tracked (oldest evicted). Default 10_000. */
|
|
216
|
-
maxKeys?: number;
|
|
217
|
-
};
|
|
232
|
+
keyAttribute?: string; /** Max distinct keys tracked (oldest evicted). Default 10_000. */
|
|
233
|
+
maxKeys?: number;
|
|
234
|
+
};
|
|
218
235
|
}
|
|
219
236
|
interface SecuritySignalProcessorOptions {
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
237
|
+
/** Flag suspicious request paths on span start. Default true. */
|
|
238
|
+
detectSuspiciousRequests?: boolean;
|
|
239
|
+
/** Additional name → pattern pairs checked against the request target. */
|
|
240
|
+
extraPatterns?: Record<string, RegExp>;
|
|
241
|
+
/** Force-keep flagged spans through tail sampling. Default true. */
|
|
242
|
+
forceKeepSuspicious?: boolean;
|
|
243
|
+
/** HTTP statuses counted as denied. Default `[401, 403, 429]`. */
|
|
244
|
+
deniedStatuses?: number[];
|
|
245
|
+
/** Burst detection over denied responses. Pass `false` to disable. */
|
|
246
|
+
burst?: BurstOptions | false;
|
|
247
|
+
/**
|
|
248
|
+
* LLM consumption signals from `gen_ai.*` spans (OWASP LLM10).
|
|
249
|
+
* Enabled with the per-call ceiling by default; pass `false` to disable.
|
|
250
|
+
*/
|
|
251
|
+
llm?: LlmSignalOptions | false;
|
|
252
|
+
/** Emit `autotel.security.*` metrics. Default true. */
|
|
253
|
+
metrics?: boolean;
|
|
254
|
+
/** Called whenever a signal fires. Keep it fast and non-throwing. */
|
|
255
|
+
onSignal?: (signal: SecuritySignal) => void;
|
|
256
|
+
/** Clock override for tests. */
|
|
257
|
+
now?: () => number;
|
|
241
258
|
}
|
|
242
259
|
/**
|
|
243
260
|
* Conservative request-target patterns. Tuned for scanner/probe traffic —
|
|
@@ -245,7 +262,8 @@ interface SecuritySignalProcessorOptions {
|
|
|
245
262
|
*/
|
|
246
263
|
declare const SUSPICIOUS_REQUEST_PATTERNS: Record<string, RegExp>;
|
|
247
264
|
declare function createSecuritySignalProcessor(options?: SecuritySignalProcessorOptions): SecuritySignalProcessor;
|
|
248
|
-
|
|
265
|
+
//#endregion
|
|
266
|
+
//#region src/security-heartbeat.d.ts
|
|
249
267
|
/**
|
|
250
268
|
* Security-telemetry heartbeat.
|
|
251
269
|
*
|
|
@@ -266,32 +284,39 @@ declare function createSecuritySignalProcessor(options?: SecuritySignalProcessor
|
|
|
266
284
|
* ```
|
|
267
285
|
*/
|
|
268
286
|
interface SecurityHeartbeatOptions {
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
287
|
+
/** Beat interval in milliseconds. Default 60_000. */
|
|
288
|
+
intervalMs?: number;
|
|
289
|
+
/** Extra counter attributes (keep cardinality low — labels, not data). */
|
|
290
|
+
attributes?: Record<string, string | number | boolean>;
|
|
273
291
|
}
|
|
274
292
|
interface SecurityHeartbeat {
|
|
275
|
-
|
|
293
|
+
stop(): void;
|
|
276
294
|
}
|
|
277
295
|
declare function startSecurityHeartbeat(options?: SecurityHeartbeatOptions): SecurityHeartbeat;
|
|
278
|
-
|
|
296
|
+
//#endregion
|
|
297
|
+
//#region src/index.d.ts
|
|
279
298
|
interface AuditMetadata {
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
299
|
+
action: string;
|
|
300
|
+
resource?: string;
|
|
301
|
+
actorId?: string;
|
|
302
|
+
category?: string;
|
|
303
|
+
outcome?: 'success' | 'failure' | (string & {});
|
|
304
|
+
[key: string]: unknown;
|
|
286
305
|
}
|
|
287
306
|
interface WithAuditOptions {
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
307
|
+
ctx?: AuditContext;
|
|
308
|
+
emitNow?: boolean;
|
|
309
|
+
forceKeep?: boolean;
|
|
310
|
+
logger?: RequestLogger;
|
|
311
|
+
/**
|
|
312
|
+
* Behaviour when no trace context can be resolved. Defaults to `warn`
|
|
313
|
+
* (best-effort: run un-audited, warn once). See {@link OnMissingContext}.
|
|
314
|
+
*/
|
|
315
|
+
onMissingContext?: OnMissingContext;
|
|
292
316
|
}
|
|
293
317
|
declare function forceKeepAuditEvent(ctx?: AuditContext): void;
|
|
294
318
|
declare function setAuditAttributes(metadata: AuditMetadata, ctx?: AuditContext): void;
|
|
295
319
|
declare function withAudit<T>(metadata: AuditMetadata, fn: (ctx: AuditContext, logger: RequestLogger) => T | Promise<T>, options?: WithAuditOptions): Promise<T>;
|
|
296
|
-
|
|
297
|
-
export { type AuditContext,
|
|
320
|
+
//#endregion
|
|
321
|
+
export { type AuditContext, AuditMetadata, AuthFailureBurstSignal, BurstOptions, HashIdentifierOptions, LlmExcessiveTokensSignal, LlmSignalOptions, LlmTokenBudgetSignal, type OnMissingContext, SUSPICIOUS_REQUEST_PATTERNS, SecurityEventCategory, SecurityEventMetadata, SecurityEventOptions, SecurityHeartbeat, SecurityHeartbeatOptions, SecurityOutcome, type SecuritySeverity, SecuritySignal, SecuritySignalProcessor, SecuritySignalProcessorOptions, SuggestedSecurityEventName, SuspiciousRequestSignal, WithAuditOptions, WithSecurityOptions, createSecuritySignalProcessor, forceKeepAuditEvent, hashIdentifier, securityEvent, setAuditAttributes, startSecurityHeartbeat, withAudit, withSecurity };
|
|
322
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/context.ts","../src/security.ts","../src/security-signals.ts","../src/security-heartbeat.ts","../src/index.ts"],"mappings":"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"}
|