autotel-audit 0.4.15 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -95,34 +95,35 @@ function lazyCounter(name, description) {
95
95
  * Drives both standard-field emission and the reserved-key check for the
96
96
  * custom-attribute loop — adding a field here is the whole change.
97
97
  */
98
- const FIELD_ATTRIBUTES = {
99
- name: autotel_security_schema.SECURITY_ATTR.event,
100
- category: autotel_security_schema.SECURITY_ATTR.category,
101
- outcome: autotel_security_schema.SECURITY_ATTR.outcome,
102
- severity: autotel_security_schema.SECURITY_ATTR.severity,
103
- actorId: autotel_security_schema.SECURITY_ATTR.actorId,
104
- targetType: autotel_security_schema.SECURITY_ATTR.targetType,
105
- targetId: autotel_security_schema.SECURITY_ATTR.targetId,
106
- tenantId: autotel_security_schema.SECURITY_ATTR.tenantId,
107
- reason: autotel_security_schema.SECURITY_ATTR.reason
108
- };
98
+ const FIELD_ATTRIBUTES = /* @__PURE__ */ new Map([
99
+ ["name", autotel_security_schema.SECURITY_ATTR.event],
100
+ ["category", autotel_security_schema.SECURITY_ATTR.category],
101
+ ["outcome", autotel_security_schema.SECURITY_ATTR.outcome],
102
+ ["severity", autotel_security_schema.SECURITY_ATTR.severity],
103
+ ["actorId", autotel_security_schema.SECURITY_ATTR.actorId],
104
+ ["targetType", autotel_security_schema.SECURITY_ATTR.targetType],
105
+ ["targetId", autotel_security_schema.SECURITY_ATTR.targetId],
106
+ ["tenantId", autotel_security_schema.SECURITY_ATTR.tenantId],
107
+ ["reason", autotel_security_schema.SECURITY_ATTR.reason]
108
+ ]);
109
109
  function flattenSecurityAttributes(metadata) {
110
- const attributes = {
111
- [autotel_security_schema.SECURITY_ATTR.marker]: true,
112
- [autotel_security_schema.SECURITY_ATTR.severity]: metadata.severity ?? "info"
113
- };
110
+ const custom = [];
114
111
  const droppedKeys = [];
115
112
  for (const [key, value] of Object.entries(metadata)) {
116
- const standardAttribute = FIELD_ATTRIBUTES[key];
113
+ const standardAttribute = FIELD_ATTRIBUTES.get(key);
117
114
  if (standardAttribute === void 0 && autotel.REDACTOR_PATTERNS.sensitiveKey.test(key)) {
118
115
  droppedKeys.push(key);
119
116
  continue;
120
117
  }
121
118
  const attr = toAttributeValue(value);
122
- if (attr !== void 0) attributes[standardAttribute ?? `security.${key}`] = attr;
119
+ if (attr !== void 0) custom.push([standardAttribute ?? `security.${key}`, attr]);
123
120
  }
124
- if (droppedKeys.length > 0) attributes[autotel_security_schema.SECURITY_ATTR.droppedKeys] = droppedKeys;
125
- return attributes;
121
+ if (droppedKeys.length > 0) custom.push([autotel_security_schema.SECURITY_ATTR.droppedKeys, droppedKeys]);
122
+ return Object.fromEntries([
123
+ [autotel_security_schema.SECURITY_ATTR.marker, true],
124
+ [autotel_security_schema.SECURITY_ATTR.severity, metadata.severity ?? "info"],
125
+ ...custom
126
+ ]);
126
127
  }
127
128
  const eventsCounter = lazyCounter(autotel_security_schema.SECURITY_METRICS.events, "Security events by name, category, outcome, and severity");
128
129
  function countSecurityEvent(metadata) {
@@ -570,12 +571,12 @@ function createMcpSecurityEventBridge(options = {}) {
570
571
  //#endregion
571
572
  //#region src/index.ts
572
573
  function flattenAuditAttributes(metadata) {
573
- const attributes = { "autotel.audit": true };
574
+ const custom = [];
574
575
  for (const [key, value] of Object.entries(metadata)) {
575
576
  const attr = toAttributeValue(value);
576
- if (attr !== void 0) attributes[`audit.${key}`] = attr;
577
+ if (attr !== void 0) custom.push([`audit.${key}`, attr]);
577
578
  }
578
- return attributes;
579
+ return Object.fromEntries([["autotel.audit", true], ...custom]);
579
580
  }
580
581
  function forceKeepAuditEvent(ctx) {
581
582
  const traceCtx = resolveContextSafe(ctx);
package/dist/index.d.cts CHANGED
@@ -32,6 +32,8 @@ type SecurityOutcome = 'success' | 'failure' | 'denied' | 'blocked' | 'error';
32
32
  * this union exists for autocomplete and consistency across services.
33
33
  */
34
34
  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' | 'llm.output.budget_exceeded' | 'llm.guard.triggered' | 'llm.action_chain.suspicious' | 'llm.manifest.suspicious' | 'llm.plan.risk.elevated';
35
+ /** An attribute value, as OpenTelemetry allows one on a span. */
36
+ type SecurityAttributeValue = string | number | boolean | string[] | number[] | boolean[];
35
37
  interface SecurityEventMetadata {
36
38
  /** Stable, dot-separated event name, e.g. `auth.login.failed`. */
37
39
  name: SuggestedSecurityEventName | (string & {});
@@ -46,7 +48,8 @@ interface SecurityEventMetadata {
46
48
  tenantId?: string;
47
49
  /** Short machine-readable reason, e.g. `invalid_password`. */
48
50
  reason?: string;
49
- [key: string]: unknown;
51
+ /** Extra fields ride along as span attributes; see AuditMetadataValue. */
52
+ [key: string]: AuditMetadataValue;
50
53
  }
51
54
  interface SecurityEventOptions {
52
55
  ctx?: AuditContext;
@@ -76,7 +79,7 @@ interface SecurityEventOptions {
76
79
  }
77
80
  type WithSecurityOptions = SecurityEventOptions;
78
81
  interface SecurityAttributeSink {
79
- setAttribute(key: string, value: string | number | boolean | string[] | number[] | boolean[]): unknown;
82
+ setAttribute(key: string, value: SecurityAttributeValue): void;
80
83
  }
81
84
  declare function applySecurityEventAttributes(sink: SecurityAttributeSink, metadata: SecurityEventMetadata, options?: Pick<SecurityEventOptions, 'forceKeep' | 'metrics'>): void;
82
85
  /**
@@ -153,12 +156,18 @@ declare function hashIdentifier(value: string, options?: HashIdentifierOptions):
153
156
  * exist, survive sampling, and stay queryable under a stable schema.
154
157
  */
155
158
  type AttributeValue = string | number | boolean | Array<null | undefined | string> | Array<null | undefined | number> | Array<null | undefined | boolean>;
159
+ /** The parent context OTel hands a processor. This package never reads it. */
160
+ interface SpanParentContext {
161
+ getValue?: (key: symbol) => ContextValue;
162
+ }
163
+ /** Whatever OTel stored under a context key. This package never reads one. */
164
+ type ContextValue = object | string | number | boolean | undefined;
156
165
  interface MutableSpanLike {
157
166
  attributes: Record<string, AttributeValue | undefined>;
158
167
  spanContext?: {
159
168
  traceId: string;
160
169
  };
161
- setAttribute(key: string, value: AttributeValue): unknown;
170
+ setAttribute(key: string, value: AttributeValue): void;
162
171
  }
163
172
  interface ReadableSpanLike {
164
173
  attributes: Record<string, AttributeValue | undefined>;
@@ -167,7 +176,7 @@ interface ReadableSpanLike {
167
176
  };
168
177
  }
169
178
  interface SecuritySignalProcessor {
170
- onStart(span: MutableSpanLike, parentContext?: unknown): void;
179
+ onStart(span: MutableSpanLike, parentContext?: SpanParentContext): void;
171
180
  onEnd(span: ReadableSpanLike): void;
172
181
  shutdown(): Promise<void>;
173
182
  forceFlush(): Promise<void>;
@@ -289,7 +298,13 @@ interface SecuritySignalProcessorOptions {
289
298
  * Conservative request-target patterns. Tuned for scanner/probe traffic —
290
299
  * high signal, low false-positive — not as a WAF. Extend via `extraPatterns`.
291
300
  */
292
- declare const SUSPICIOUS_REQUEST_PATTERNS: Record<string, RegExp>;
301
+ declare const SUSPICIOUS_REQUEST_PATTERNS: {
302
+ path_traversal: RegExp;
303
+ sensitive_file_probe: RegExp;
304
+ sqli_probe: RegExp;
305
+ xss_probe: RegExp;
306
+ null_byte: RegExp;
307
+ };
293
308
  declare function createSecuritySignalProcessor(options?: SecuritySignalProcessorOptions): SecuritySignalProcessor;
294
309
  //#endregion
295
310
  //#region src/security-heartbeat.d.ts
@@ -338,7 +353,8 @@ interface McpBridgedSecurityEvent {
338
353
  toolName?: string;
339
354
  verdict?: string;
340
355
  source?: string;
341
- [key: string]: unknown;
356
+ /** Extra fields ride along as span attributes; see AuditMetadataValue. */
357
+ [key: string]: AuditMetadataValue;
342
358
  }
343
359
  interface McpSecurityEventBridgeOptions extends SecurityEventOptions {
344
360
  /** Optional fixed audit context for bridged events. */
@@ -368,8 +384,17 @@ interface AuditMetadata {
368
384
  actorId?: string;
369
385
  category?: string;
370
386
  outcome?: 'success' | 'failure' | (string & {});
371
- [key: string]: unknown;
387
+ /** Extra fields ride along as span attributes, so they must be attribute-shaped. */
388
+ [key: string]: AuditMetadataValue;
372
389
  }
390
+ /**
391
+ * What a metadata field can hold: anything `toAttributeValue` can render onto a
392
+ * span. Scalars and scalar arrays pass through; a Date becomes an ISO string;
393
+ * anything else nested is JSON-serialized.
394
+ */
395
+ type AuditMetadataValue = string | number | boolean | null | undefined | Date | Array<AuditMetadataValue> | {
396
+ [key: string]: AuditMetadataValue;
397
+ };
373
398
  interface WithAuditOptions {
374
399
  ctx?: AuditContext;
375
400
  emitNow?: boolean;
@@ -385,4 +410,4 @@ declare function forceKeepAuditEvent(ctx?: AuditContext): void;
385
410
  declare function setAuditAttributes(metadata: AuditMetadata, ctx?: AuditContext): void;
386
411
  declare function withAudit<T>(metadata: AuditMetadata, fn: (ctx: AuditContext, logger: RequestLogger) => T | Promise<T>, options?: WithAuditOptions): Promise<T>;
387
412
  //#endregion
388
- export { type AuditContext, AuditMetadata, AuthFailureBurstSignal, BurstOptions, HashIdentifierOptions, LlmActionChainSuspiciousSignal, LlmExcessiveTokensSignal, LlmSignalOptions, LlmTokenBudgetSignal, McpBridgedSecurityEvent, McpSecurityEventBridgeOptions, type OnMissingContext, SUSPICIOUS_REQUEST_PATTERNS, SecurityEventCategory, SecurityEventMetadata, SecurityEventOptions, SecurityHeartbeat, SecurityHeartbeatOptions, SecurityOutcome, type SecuritySeverity, SecuritySignal, SecuritySignalProcessor, SecuritySignalProcessorOptions, SuggestedSecurityEventName, SuspiciousRequestSignal, WithAuditOptions, WithSecurityOptions, applySecurityEventAttributes, createMcpSecurityEventBridge, createSecuritySignalProcessor, forceKeepAuditEvent, hashIdentifier, securityEvent, setAuditAttributes, startSecurityHeartbeat, withAudit, withSecurity };
413
+ export { AttributeValue, type AuditContext, AuditMetadata, AuditMetadataValue, AuthFailureBurstSignal, BurstOptions, ContextValue, HashIdentifierOptions, LlmActionChainSuspiciousSignal, LlmExcessiveTokensSignal, LlmSignalOptions, LlmTokenBudgetSignal, McpBridgedSecurityEvent, McpSecurityEventBridgeOptions, type OnMissingContext, SUSPICIOUS_REQUEST_PATTERNS, SecurityAttributeValue, SecurityEventCategory, SecurityEventMetadata, SecurityEventOptions, SecurityHeartbeat, SecurityHeartbeatOptions, SecurityOutcome, type SecuritySeverity, SecuritySignal, SecuritySignalProcessor, SecuritySignalProcessorOptions, SpanParentContext, SuggestedSecurityEventName, SuspiciousRequestSignal, WithAuditOptions, WithSecurityOptions, applySecurityEventAttributes, createMcpSecurityEventBridge, createSecuritySignalProcessor, forceKeepAuditEvent, hashIdentifier, securityEvent, setAuditAttributes, startSecurityHeartbeat, withAudit, withSecurity };
package/dist/index.d.ts CHANGED
@@ -32,6 +32,8 @@ type SecurityOutcome = 'success' | 'failure' | 'denied' | 'blocked' | 'error';
32
32
  * this union exists for autocomplete and consistency across services.
33
33
  */
34
34
  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' | 'llm.output.budget_exceeded' | 'llm.guard.triggered' | 'llm.action_chain.suspicious' | 'llm.manifest.suspicious' | 'llm.plan.risk.elevated';
35
+ /** An attribute value, as OpenTelemetry allows one on a span. */
36
+ type SecurityAttributeValue = string | number | boolean | string[] | number[] | boolean[];
35
37
  interface SecurityEventMetadata {
36
38
  /** Stable, dot-separated event name, e.g. `auth.login.failed`. */
37
39
  name: SuggestedSecurityEventName | (string & {});
@@ -46,7 +48,8 @@ interface SecurityEventMetadata {
46
48
  tenantId?: string;
47
49
  /** Short machine-readable reason, e.g. `invalid_password`. */
48
50
  reason?: string;
49
- [key: string]: unknown;
51
+ /** Extra fields ride along as span attributes; see AuditMetadataValue. */
52
+ [key: string]: AuditMetadataValue;
50
53
  }
51
54
  interface SecurityEventOptions {
52
55
  ctx?: AuditContext;
@@ -76,7 +79,7 @@ interface SecurityEventOptions {
76
79
  }
77
80
  type WithSecurityOptions = SecurityEventOptions;
78
81
  interface SecurityAttributeSink {
79
- setAttribute(key: string, value: string | number | boolean | string[] | number[] | boolean[]): unknown;
82
+ setAttribute(key: string, value: SecurityAttributeValue): void;
80
83
  }
81
84
  declare function applySecurityEventAttributes(sink: SecurityAttributeSink, metadata: SecurityEventMetadata, options?: Pick<SecurityEventOptions, 'forceKeep' | 'metrics'>): void;
82
85
  /**
@@ -153,12 +156,18 @@ declare function hashIdentifier(value: string, options?: HashIdentifierOptions):
153
156
  * exist, survive sampling, and stay queryable under a stable schema.
154
157
  */
155
158
  type AttributeValue = string | number | boolean | Array<null | undefined | string> | Array<null | undefined | number> | Array<null | undefined | boolean>;
159
+ /** The parent context OTel hands a processor. This package never reads it. */
160
+ interface SpanParentContext {
161
+ getValue?: (key: symbol) => ContextValue;
162
+ }
163
+ /** Whatever OTel stored under a context key. This package never reads one. */
164
+ type ContextValue = object | string | number | boolean | undefined;
156
165
  interface MutableSpanLike {
157
166
  attributes: Record<string, AttributeValue | undefined>;
158
167
  spanContext?: {
159
168
  traceId: string;
160
169
  };
161
- setAttribute(key: string, value: AttributeValue): unknown;
170
+ setAttribute(key: string, value: AttributeValue): void;
162
171
  }
163
172
  interface ReadableSpanLike {
164
173
  attributes: Record<string, AttributeValue | undefined>;
@@ -167,7 +176,7 @@ interface ReadableSpanLike {
167
176
  };
168
177
  }
169
178
  interface SecuritySignalProcessor {
170
- onStart(span: MutableSpanLike, parentContext?: unknown): void;
179
+ onStart(span: MutableSpanLike, parentContext?: SpanParentContext): void;
171
180
  onEnd(span: ReadableSpanLike): void;
172
181
  shutdown(): Promise<void>;
173
182
  forceFlush(): Promise<void>;
@@ -289,7 +298,13 @@ interface SecuritySignalProcessorOptions {
289
298
  * Conservative request-target patterns. Tuned for scanner/probe traffic —
290
299
  * high signal, low false-positive — not as a WAF. Extend via `extraPatterns`.
291
300
  */
292
- declare const SUSPICIOUS_REQUEST_PATTERNS: Record<string, RegExp>;
301
+ declare const SUSPICIOUS_REQUEST_PATTERNS: {
302
+ path_traversal: RegExp;
303
+ sensitive_file_probe: RegExp;
304
+ sqli_probe: RegExp;
305
+ xss_probe: RegExp;
306
+ null_byte: RegExp;
307
+ };
293
308
  declare function createSecuritySignalProcessor(options?: SecuritySignalProcessorOptions): SecuritySignalProcessor;
294
309
  //#endregion
295
310
  //#region src/security-heartbeat.d.ts
@@ -338,7 +353,8 @@ interface McpBridgedSecurityEvent {
338
353
  toolName?: string;
339
354
  verdict?: string;
340
355
  source?: string;
341
- [key: string]: unknown;
356
+ /** Extra fields ride along as span attributes; see AuditMetadataValue. */
357
+ [key: string]: AuditMetadataValue;
342
358
  }
343
359
  interface McpSecurityEventBridgeOptions extends SecurityEventOptions {
344
360
  /** Optional fixed audit context for bridged events. */
@@ -368,8 +384,17 @@ interface AuditMetadata {
368
384
  actorId?: string;
369
385
  category?: string;
370
386
  outcome?: 'success' | 'failure' | (string & {});
371
- [key: string]: unknown;
387
+ /** Extra fields ride along as span attributes, so they must be attribute-shaped. */
388
+ [key: string]: AuditMetadataValue;
372
389
  }
390
+ /**
391
+ * What a metadata field can hold: anything `toAttributeValue` can render onto a
392
+ * span. Scalars and scalar arrays pass through; a Date becomes an ISO string;
393
+ * anything else nested is JSON-serialized.
394
+ */
395
+ type AuditMetadataValue = string | number | boolean | null | undefined | Date | Array<AuditMetadataValue> | {
396
+ [key: string]: AuditMetadataValue;
397
+ };
373
398
  interface WithAuditOptions {
374
399
  ctx?: AuditContext;
375
400
  emitNow?: boolean;
@@ -385,4 +410,4 @@ declare function forceKeepAuditEvent(ctx?: AuditContext): void;
385
410
  declare function setAuditAttributes(metadata: AuditMetadata, ctx?: AuditContext): void;
386
411
  declare function withAudit<T>(metadata: AuditMetadata, fn: (ctx: AuditContext, logger: RequestLogger) => T | Promise<T>, options?: WithAuditOptions): Promise<T>;
387
412
  //#endregion
388
- export { type AuditContext, AuditMetadata, AuthFailureBurstSignal, BurstOptions, HashIdentifierOptions, LlmActionChainSuspiciousSignal, LlmExcessiveTokensSignal, LlmSignalOptions, LlmTokenBudgetSignal, McpBridgedSecurityEvent, McpSecurityEventBridgeOptions, type OnMissingContext, SUSPICIOUS_REQUEST_PATTERNS, SecurityEventCategory, SecurityEventMetadata, SecurityEventOptions, SecurityHeartbeat, SecurityHeartbeatOptions, SecurityOutcome, type SecuritySeverity, SecuritySignal, SecuritySignalProcessor, SecuritySignalProcessorOptions, SuggestedSecurityEventName, SuspiciousRequestSignal, WithAuditOptions, WithSecurityOptions, applySecurityEventAttributes, createMcpSecurityEventBridge, createSecuritySignalProcessor, forceKeepAuditEvent, hashIdentifier, securityEvent, setAuditAttributes, startSecurityHeartbeat, withAudit, withSecurity };
413
+ export { AttributeValue, type AuditContext, AuditMetadata, AuditMetadataValue, AuthFailureBurstSignal, BurstOptions, ContextValue, HashIdentifierOptions, LlmActionChainSuspiciousSignal, LlmExcessiveTokensSignal, LlmSignalOptions, LlmTokenBudgetSignal, McpBridgedSecurityEvent, McpSecurityEventBridgeOptions, type OnMissingContext, SUSPICIOUS_REQUEST_PATTERNS, SecurityAttributeValue, SecurityEventCategory, SecurityEventMetadata, SecurityEventOptions, SecurityHeartbeat, SecurityHeartbeatOptions, SecurityOutcome, type SecuritySeverity, SecuritySignal, SecuritySignalProcessor, SecuritySignalProcessorOptions, SpanParentContext, SuggestedSecurityEventName, SuspiciousRequestSignal, WithAuditOptions, WithSecurityOptions, applySecurityEventAttributes, createMcpSecurityEventBridge, createSecuritySignalProcessor, forceKeepAuditEvent, hashIdentifier, securityEvent, setAuditAttributes, startSecurityHeartbeat, withAudit, withSecurity };
package/dist/index.js CHANGED
@@ -94,34 +94,35 @@ function lazyCounter(name, description) {
94
94
  * Drives both standard-field emission and the reserved-key check for the
95
95
  * custom-attribute loop — adding a field here is the whole change.
96
96
  */
97
- const FIELD_ATTRIBUTES = {
98
- name: SECURITY_ATTR.event,
99
- category: SECURITY_ATTR.category,
100
- outcome: SECURITY_ATTR.outcome,
101
- severity: SECURITY_ATTR.severity,
102
- actorId: SECURITY_ATTR.actorId,
103
- targetType: SECURITY_ATTR.targetType,
104
- targetId: SECURITY_ATTR.targetId,
105
- tenantId: SECURITY_ATTR.tenantId,
106
- reason: SECURITY_ATTR.reason
107
- };
97
+ const FIELD_ATTRIBUTES = /* @__PURE__ */ new Map([
98
+ ["name", SECURITY_ATTR.event],
99
+ ["category", SECURITY_ATTR.category],
100
+ ["outcome", SECURITY_ATTR.outcome],
101
+ ["severity", SECURITY_ATTR.severity],
102
+ ["actorId", SECURITY_ATTR.actorId],
103
+ ["targetType", SECURITY_ATTR.targetType],
104
+ ["targetId", SECURITY_ATTR.targetId],
105
+ ["tenantId", SECURITY_ATTR.tenantId],
106
+ ["reason", SECURITY_ATTR.reason]
107
+ ]);
108
108
  function flattenSecurityAttributes(metadata) {
109
- const attributes = {
110
- [SECURITY_ATTR.marker]: true,
111
- [SECURITY_ATTR.severity]: metadata.severity ?? "info"
112
- };
109
+ const custom = [];
113
110
  const droppedKeys = [];
114
111
  for (const [key, value] of Object.entries(metadata)) {
115
- const standardAttribute = FIELD_ATTRIBUTES[key];
112
+ const standardAttribute = FIELD_ATTRIBUTES.get(key);
116
113
  if (standardAttribute === void 0 && REDACTOR_PATTERNS.sensitiveKey.test(key)) {
117
114
  droppedKeys.push(key);
118
115
  continue;
119
116
  }
120
117
  const attr = toAttributeValue(value);
121
- if (attr !== void 0) attributes[standardAttribute ?? `security.${key}`] = attr;
118
+ if (attr !== void 0) custom.push([standardAttribute ?? `security.${key}`, attr]);
122
119
  }
123
- if (droppedKeys.length > 0) attributes[SECURITY_ATTR.droppedKeys] = droppedKeys;
124
- return attributes;
120
+ if (droppedKeys.length > 0) custom.push([SECURITY_ATTR.droppedKeys, droppedKeys]);
121
+ return Object.fromEntries([
122
+ [SECURITY_ATTR.marker, true],
123
+ [SECURITY_ATTR.severity, metadata.severity ?? "info"],
124
+ ...custom
125
+ ]);
125
126
  }
126
127
  const eventsCounter = lazyCounter(SECURITY_METRICS.events, "Security events by name, category, outcome, and severity");
127
128
  function countSecurityEvent(metadata) {
@@ -569,12 +570,12 @@ function createMcpSecurityEventBridge(options = {}) {
569
570
  //#endregion
570
571
  //#region src/index.ts
571
572
  function flattenAuditAttributes(metadata) {
572
- const attributes = { "autotel.audit": true };
573
+ const custom = [];
573
574
  for (const [key, value] of Object.entries(metadata)) {
574
575
  const attr = toAttributeValue(value);
575
- if (attr !== void 0) attributes[`audit.${key}`] = attr;
576
+ if (attr !== void 0) custom.push([`audit.${key}`, attr]);
576
577
  }
577
- return attributes;
578
+ return Object.fromEntries([["autotel.audit", true], ...custom]);
578
579
  }
579
580
  function forceKeepAuditEvent(ctx) {
580
581
  const traceCtx = resolveContextSafe(ctx);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "autotel-audit",
3
- "version": "0.4.15",
3
+ "version": "1.0.0",
4
4
  "description": "Audit-focused helpers for Autotel (force-keep + structured audit instrumentation)",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -27,7 +27,7 @@
27
27
  "author": "Jag Reehal <jag@jagreehal.com> (https://jagreehal.com)",
28
28
  "license": "Apache-2.0",
29
29
  "dependencies": {
30
- "autotel": "6.5.0"
30
+ "autotel": "7.0.0"
31
31
  },
32
32
  "devDependencies": {
33
33
  "@types/node": "^26.1.2",