autotel 3.4.3 → 3.5.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 +5 -5
- package/dist/index.js +1 -1
- package/dist/security-schema.cjs +69 -0
- package/dist/security-schema.cjs.map +1 -0
- package/dist/security-schema.d.cts +67 -0
- package/dist/security-schema.d.ts +67 -0
- package/dist/security-schema.js +59 -0
- package/dist/security-schema.js.map +1 -0
- package/package.json +13 -8
- package/src/security-schema.test.ts +45 -0
- package/src/security-schema.ts +107 -0
- package/binding.gyp +0 -9
- package/index.js +0 -1
package/dist/index.cjs
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
var chunkT4B5LB6E_cjs = require('./chunk-T4B5LB6E.cjs');
|
|
3
4
|
var chunk7EQ4G4SI_cjs = require('./chunk-7EQ4G4SI.cjs');
|
|
4
5
|
var chunkAC5GNZKB_cjs = require('./chunk-AC5GNZKB.cjs');
|
|
5
6
|
var chunkM3LFHHTN_cjs = require('./chunk-M3LFHHTN.cjs');
|
|
6
7
|
var chunk2ZKEORFN_cjs = require('./chunk-2ZKEORFN.cjs');
|
|
7
8
|
var chunkESMHTKLJ_cjs = require('./chunk-ESMHTKLJ.cjs');
|
|
8
|
-
var chunkT4B5LB6E_cjs = require('./chunk-T4B5LB6E.cjs');
|
|
9
9
|
var chunkZ6HRSM2Y_cjs = require('./chunk-Z6HRSM2Y.cjs');
|
|
10
10
|
var chunk4P6ZOARG_cjs = require('./chunk-4P6ZOARG.cjs');
|
|
11
11
|
var chunkINJD3G4K_cjs = require('./chunk-INJD3G4K.cjs');
|
|
@@ -778,6 +778,10 @@ function recordLLMCost(ctx2, model, usage, options) {
|
|
|
778
778
|
return cost;
|
|
779
779
|
}
|
|
780
780
|
|
|
781
|
+
Object.defineProperty(exports, "parseError", {
|
|
782
|
+
enumerable: true,
|
|
783
|
+
get: function () { return chunkT4B5LB6E_cjs.parseError; }
|
|
784
|
+
});
|
|
781
785
|
Object.defineProperty(exports, "createDrainPipeline", {
|
|
782
786
|
enumerable: true,
|
|
783
787
|
get: function () { return chunk7EQ4G4SI_cjs.createDrainPipeline; }
|
|
@@ -882,10 +886,6 @@ Object.defineProperty(exports, "URLAttributes", {
|
|
|
882
886
|
enumerable: true,
|
|
883
887
|
get: function () { return chunkESMHTKLJ_cjs.URLAttributes; }
|
|
884
888
|
});
|
|
885
|
-
Object.defineProperty(exports, "parseError", {
|
|
886
|
-
enumerable: true,
|
|
887
|
-
get: function () { return chunkT4B5LB6E_cjs.parseError; }
|
|
888
|
-
});
|
|
889
889
|
Object.defineProperty(exports, "traceConsumer", {
|
|
890
890
|
enumerable: true,
|
|
891
891
|
get: function () { return chunkZ6HRSM2Y_cjs.traceConsumer; }
|
package/dist/index.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
+
export { parseError } from './chunk-J7VGRIAJ.js';
|
|
1
2
|
export { createDrainPipeline } from './chunk-KFOHQK7X.js';
|
|
2
3
|
export { getCurrentWorkflowContext, isInWorkflow, traceStep, traceWorkflow } from './chunk-URHPSJW2.js';
|
|
3
4
|
export { attrs, autoRedactPII, dbClient, httpClient, httpServer, identify, mergeAttrs, mergeServiceResource, request, safeSetAttributes, setDevice, setError, setException, setSession, setUser, validateAttribute } from './chunk-454CH4OV.js';
|
|
4
5
|
export { httpRequestHeaderAttribute, httpResponseHeaderAttribute } from './chunk-7552UTQW.js';
|
|
5
6
|
export { HTTPAttributes, ServiceAttributes, URLAttributes } from './chunk-4A53YIAX.js';
|
|
6
|
-
export { parseError } from './chunk-J7VGRIAJ.js';
|
|
7
7
|
export { traceConsumer, traceProducer } from './chunk-DQEHQNQE.js';
|
|
8
8
|
import { emitCorrelatedEvent } from './chunk-KIL5CUN6.js';
|
|
9
9
|
export { BusinessBaggage, createSafeBaggageSchema } from './chunk-4IFSYQVX.js';
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// src/security-schema.ts
|
|
4
|
+
var SECURITY_SEVERITIES = [
|
|
5
|
+
"info",
|
|
6
|
+
"warning",
|
|
7
|
+
"error",
|
|
8
|
+
"critical"
|
|
9
|
+
];
|
|
10
|
+
var SECURITY_SEVERITY_RANK = {
|
|
11
|
+
info: 0,
|
|
12
|
+
warning: 1,
|
|
13
|
+
error: 2,
|
|
14
|
+
critical: 3
|
|
15
|
+
};
|
|
16
|
+
function parseSecuritySeverity(value, fallback = "info") {
|
|
17
|
+
return typeof value === "string" && value in SECURITY_SEVERITY_RANK ? value : fallback;
|
|
18
|
+
}
|
|
19
|
+
function securitySeverityAtLeast(severity, min) {
|
|
20
|
+
return SECURITY_SEVERITY_RANK[severity] >= SECURITY_SEVERITY_RANK[min];
|
|
21
|
+
}
|
|
22
|
+
function escalateSecuritySeverity(severity, floor) {
|
|
23
|
+
return SECURITY_SEVERITY_RANK[severity] >= SECURITY_SEVERITY_RANK[floor] ? severity : floor;
|
|
24
|
+
}
|
|
25
|
+
var SECURITY_ATTR = {
|
|
26
|
+
/** Marker set on every span carrying a security event. */
|
|
27
|
+
marker: "autotel.security",
|
|
28
|
+
/** Set when the event was force-kept through tail sampling. */
|
|
29
|
+
forceKeep: "autotel.security.force_keep",
|
|
30
|
+
event: "security.event",
|
|
31
|
+
category: "security.category",
|
|
32
|
+
outcome: "security.outcome",
|
|
33
|
+
severity: "security.severity",
|
|
34
|
+
actorId: "security.actor_id",
|
|
35
|
+
targetType: "security.target_type",
|
|
36
|
+
targetId: "security.target_id",
|
|
37
|
+
tenantId: "security.tenant_id",
|
|
38
|
+
reason: "security.reason",
|
|
39
|
+
/** Custom metadata keys dropped because they looked credential-shaped. */
|
|
40
|
+
droppedKeys: "security.dropped_keys",
|
|
41
|
+
/** Set by the signal processor on suspicious request paths. */
|
|
42
|
+
suspiciousRequest: "security.suspicious_request",
|
|
43
|
+
/** Pattern name that flagged a suspicious request, e.g. `path_traversal`. */
|
|
44
|
+
signal: "security.signal"
|
|
45
|
+
};
|
|
46
|
+
var SECURITY_METRICS = {
|
|
47
|
+
events: "autotel.security.events",
|
|
48
|
+
httpSuspicious: "autotel.security.http.suspicious",
|
|
49
|
+
httpDenied: "autotel.security.http.denied",
|
|
50
|
+
anomaly: "autotel.security.anomaly",
|
|
51
|
+
heartbeat: "autotel.security.heartbeat"
|
|
52
|
+
};
|
|
53
|
+
var SECURITY_DENIED_STATUSES = [401, 403, 429];
|
|
54
|
+
var HTTP_STATUS_ATTRIBUTES = [
|
|
55
|
+
"http.response.status_code",
|
|
56
|
+
"http.status_code"
|
|
57
|
+
];
|
|
58
|
+
|
|
59
|
+
exports.HTTP_STATUS_ATTRIBUTES = HTTP_STATUS_ATTRIBUTES;
|
|
60
|
+
exports.SECURITY_ATTR = SECURITY_ATTR;
|
|
61
|
+
exports.SECURITY_DENIED_STATUSES = SECURITY_DENIED_STATUSES;
|
|
62
|
+
exports.SECURITY_METRICS = SECURITY_METRICS;
|
|
63
|
+
exports.SECURITY_SEVERITIES = SECURITY_SEVERITIES;
|
|
64
|
+
exports.SECURITY_SEVERITY_RANK = SECURITY_SEVERITY_RANK;
|
|
65
|
+
exports.escalateSecuritySeverity = escalateSecuritySeverity;
|
|
66
|
+
exports.parseSecuritySeverity = parseSecuritySeverity;
|
|
67
|
+
exports.securitySeverityAtLeast = securitySeverityAtLeast;
|
|
68
|
+
//# sourceMappingURL=security-schema.cjs.map
|
|
69
|
+
//# sourceMappingURL=security-schema.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/security-schema.ts"],"names":[],"mappings":";;;AAeO,IAAM,mBAAA,GAAmD;AAAA,EAC9D,MAAA;AAAA,EACA,SAAA;AAAA,EACA,OAAA;AAAA,EACA;AACF;AAGO,IAAM,sBAAA,GAA2D;AAAA,EACtE,IAAA,EAAM,CAAA;AAAA,EACN,OAAA,EAAS,CAAA;AAAA,EACT,KAAA,EAAO,CAAA;AAAA,EACP,QAAA,EAAU;AACZ;AAMO,SAAS,qBAAA,CACd,KAAA,EACA,QAAA,GAA6B,MAAA,EACX;AAClB,EAAA,OAAO,OAAO,KAAA,KAAU,QAAA,IAAY,KAAA,IAAS,yBACxC,KAAA,GACD,QAAA;AACN;AAGO,SAAS,uBAAA,CACd,UACA,GAAA,EACS;AACT,EAAA,OAAO,sBAAA,CAAuB,QAAQ,CAAA,IAAK,sBAAA,CAAuB,GAAG,CAAA;AACvE;AAGO,SAAS,wBAAA,CACd,UACA,KAAA,EACkB;AAClB,EAAA,OAAO,uBAAuB,QAAQ,CAAA,IAAK,sBAAA,CAAuB,KAAK,IACnE,QAAA,GACA,KAAA;AACN;AAMO,IAAM,aAAA,GAAgB;AAAA;AAAA,EAE3B,MAAA,EAAQ,kBAAA;AAAA;AAAA,EAER,SAAA,EAAW,6BAAA;AAAA,EACX,KAAA,EAAO,gBAAA;AAAA,EACP,QAAA,EAAU,mBAAA;AAAA,EACV,OAAA,EAAS,kBAAA;AAAA,EACT,QAAA,EAAU,mBAAA;AAAA,EACV,OAAA,EAAS,mBAAA;AAAA,EACT,UAAA,EAAY,sBAAA;AAAA,EACZ,QAAA,EAAU,oBAAA;AAAA,EACV,QAAA,EAAU,oBAAA;AAAA,EACV,MAAA,EAAQ,iBAAA;AAAA;AAAA,EAER,WAAA,EAAa,uBAAA;AAAA;AAAA,EAEb,iBAAA,EAAmB,6BAAA;AAAA;AAAA,EAEnB,MAAA,EAAQ;AACV;AAGO,IAAM,gBAAA,GAAmB;AAAA,EAC9B,MAAA,EAAQ,yBAAA;AAAA,EACR,cAAA,EAAgB,kCAAA;AAAA,EAChB,UAAA,EAAY,8BAAA;AAAA,EACZ,OAAA,EAAS,0BAAA;AAAA,EACT,SAAA,EAAW;AACb;AAGO,IAAM,wBAAA,GAA8C,CAAC,GAAA,EAAK,GAAA,EAAK,GAAG;AAMlE,IAAM,sBAAA,GAA4C;AAAA,EACvD,2BAAA;AAAA,EACA;AACF","file":"security-schema.cjs","sourcesContent":["/**\n * Security telemetry wire schema — the single source of truth for the\n * `security.*` span-attribute contract emitted by `autotel-audit`\n * (`securityEvent()`, `withSecurity()`, `createSecuritySignalProcessor()`)\n * and consumed by `autotel-subscribers`, `autotel-devtools`, and the\n * `autotel security` CLI commands.\n *\n * Dependency-free and side-effect-free by design: safe to import from\n * browser bundles (devtools widget) and anything else that only needs\n * the constants, without pulling in the OpenTelemetry SDK.\n */\n\nexport type SecuritySeverity = 'info' | 'warning' | 'error' | 'critical';\n\n/** All severities, lowest first. */\nexport const SECURITY_SEVERITIES: readonly SecuritySeverity[] = [\n 'info',\n 'warning',\n 'error',\n 'critical',\n];\n\n/** Numeric rank per severity for threshold comparisons. */\nexport const SECURITY_SEVERITY_RANK: Record<SecuritySeverity, number> = {\n info: 0,\n warning: 1,\n error: 2,\n critical: 3,\n};\n\n/**\n * Parse an untrusted value (span attribute, event payload field) into a\n * severity, falling back when it is missing or malformed.\n */\nexport function parseSecuritySeverity(\n value: unknown,\n fallback: SecuritySeverity = 'info',\n): SecuritySeverity {\n return typeof value === 'string' && value in SECURITY_SEVERITY_RANK\n ? (value as SecuritySeverity)\n : fallback;\n}\n\n/** `true` when `severity` meets or exceeds `min`. */\nexport function securitySeverityAtLeast(\n severity: SecuritySeverity,\n min: SecuritySeverity,\n): boolean {\n return SECURITY_SEVERITY_RANK[severity] >= SECURITY_SEVERITY_RANK[min];\n}\n\n/** The higher-ranked of two severities (e.g. escalate failures to ≥ error). */\nexport function escalateSecuritySeverity(\n severity: SecuritySeverity,\n floor: SecuritySeverity,\n): SecuritySeverity {\n return SECURITY_SEVERITY_RANK[severity] >= SECURITY_SEVERITY_RANK[floor]\n ? severity\n : floor;\n}\n\n/**\n * Span attribute keys of the security schema. Emitters and consumers must\n * reference these instead of re-typing the strings.\n */\nexport const SECURITY_ATTR = {\n /** Marker set on every span carrying a security event. */\n marker: 'autotel.security',\n /** Set when the event was force-kept through tail sampling. */\n forceKeep: 'autotel.security.force_keep',\n event: 'security.event',\n category: 'security.category',\n outcome: 'security.outcome',\n severity: 'security.severity',\n actorId: 'security.actor_id',\n targetType: 'security.target_type',\n targetId: 'security.target_id',\n tenantId: 'security.tenant_id',\n reason: 'security.reason',\n /** Custom metadata keys dropped because they looked credential-shaped. */\n droppedKeys: 'security.dropped_keys',\n /** Set by the signal processor on suspicious request paths. */\n suspiciousRequest: 'security.suspicious_request',\n /** Pattern name that flagged a suspicious request, e.g. `path_traversal`. */\n signal: 'security.signal',\n} as const;\n\n/** Metric names emitted by the security instrumentation. */\nexport const SECURITY_METRICS = {\n events: 'autotel.security.events',\n httpSuspicious: 'autotel.security.http.suspicious',\n httpDenied: 'autotel.security.http.denied',\n anomaly: 'autotel.security.anomaly',\n heartbeat: 'autotel.security.heartbeat',\n} as const;\n\n/** HTTP statuses counted as denied responses by default. */\nexport const SECURITY_DENIED_STATUSES: readonly number[] = [401, 403, 429];\n\n/**\n * Span attributes carrying the HTTP response status, current semconv\n * first, legacy fallback second.\n */\nexport const HTTP_STATUS_ATTRIBUTES: readonly string[] = [\n 'http.response.status_code',\n 'http.status_code',\n];\n"]}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Security telemetry wire schema — the single source of truth for the
|
|
3
|
+
* `security.*` span-attribute contract emitted by `autotel-audit`
|
|
4
|
+
* (`securityEvent()`, `withSecurity()`, `createSecuritySignalProcessor()`)
|
|
5
|
+
* and consumed by `autotel-subscribers`, `autotel-devtools`, and the
|
|
6
|
+
* `autotel security` CLI commands.
|
|
7
|
+
*
|
|
8
|
+
* Dependency-free and side-effect-free by design: safe to import from
|
|
9
|
+
* browser bundles (devtools widget) and anything else that only needs
|
|
10
|
+
* the constants, without pulling in the OpenTelemetry SDK.
|
|
11
|
+
*/
|
|
12
|
+
type SecuritySeverity = 'info' | 'warning' | 'error' | 'critical';
|
|
13
|
+
/** All severities, lowest first. */
|
|
14
|
+
declare const SECURITY_SEVERITIES: readonly SecuritySeverity[];
|
|
15
|
+
/** Numeric rank per severity for threshold comparisons. */
|
|
16
|
+
declare const SECURITY_SEVERITY_RANK: Record<SecuritySeverity, number>;
|
|
17
|
+
/**
|
|
18
|
+
* Parse an untrusted value (span attribute, event payload field) into a
|
|
19
|
+
* severity, falling back when it is missing or malformed.
|
|
20
|
+
*/
|
|
21
|
+
declare function parseSecuritySeverity(value: unknown, fallback?: SecuritySeverity): SecuritySeverity;
|
|
22
|
+
/** `true` when `severity` meets or exceeds `min`. */
|
|
23
|
+
declare function securitySeverityAtLeast(severity: SecuritySeverity, min: SecuritySeverity): boolean;
|
|
24
|
+
/** The higher-ranked of two severities (e.g. escalate failures to ≥ error). */
|
|
25
|
+
declare function escalateSecuritySeverity(severity: SecuritySeverity, floor: SecuritySeverity): SecuritySeverity;
|
|
26
|
+
/**
|
|
27
|
+
* Span attribute keys of the security schema. Emitters and consumers must
|
|
28
|
+
* reference these instead of re-typing the strings.
|
|
29
|
+
*/
|
|
30
|
+
declare const SECURITY_ATTR: {
|
|
31
|
+
/** Marker set on every span carrying a security event. */
|
|
32
|
+
readonly marker: "autotel.security";
|
|
33
|
+
/** Set when the event was force-kept through tail sampling. */
|
|
34
|
+
readonly forceKeep: "autotel.security.force_keep";
|
|
35
|
+
readonly event: "security.event";
|
|
36
|
+
readonly category: "security.category";
|
|
37
|
+
readonly outcome: "security.outcome";
|
|
38
|
+
readonly severity: "security.severity";
|
|
39
|
+
readonly actorId: "security.actor_id";
|
|
40
|
+
readonly targetType: "security.target_type";
|
|
41
|
+
readonly targetId: "security.target_id";
|
|
42
|
+
readonly tenantId: "security.tenant_id";
|
|
43
|
+
readonly reason: "security.reason";
|
|
44
|
+
/** Custom metadata keys dropped because they looked credential-shaped. */
|
|
45
|
+
readonly droppedKeys: "security.dropped_keys";
|
|
46
|
+
/** Set by the signal processor on suspicious request paths. */
|
|
47
|
+
readonly suspiciousRequest: "security.suspicious_request";
|
|
48
|
+
/** Pattern name that flagged a suspicious request, e.g. `path_traversal`. */
|
|
49
|
+
readonly signal: "security.signal";
|
|
50
|
+
};
|
|
51
|
+
/** Metric names emitted by the security instrumentation. */
|
|
52
|
+
declare const SECURITY_METRICS: {
|
|
53
|
+
readonly events: "autotel.security.events";
|
|
54
|
+
readonly httpSuspicious: "autotel.security.http.suspicious";
|
|
55
|
+
readonly httpDenied: "autotel.security.http.denied";
|
|
56
|
+
readonly anomaly: "autotel.security.anomaly";
|
|
57
|
+
readonly heartbeat: "autotel.security.heartbeat";
|
|
58
|
+
};
|
|
59
|
+
/** HTTP statuses counted as denied responses by default. */
|
|
60
|
+
declare const SECURITY_DENIED_STATUSES: readonly number[];
|
|
61
|
+
/**
|
|
62
|
+
* Span attributes carrying the HTTP response status, current semconv
|
|
63
|
+
* first, legacy fallback second.
|
|
64
|
+
*/
|
|
65
|
+
declare const HTTP_STATUS_ATTRIBUTES: readonly string[];
|
|
66
|
+
|
|
67
|
+
export { HTTP_STATUS_ATTRIBUTES, SECURITY_ATTR, SECURITY_DENIED_STATUSES, SECURITY_METRICS, SECURITY_SEVERITIES, SECURITY_SEVERITY_RANK, type SecuritySeverity, escalateSecuritySeverity, parseSecuritySeverity, securitySeverityAtLeast };
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Security telemetry wire schema — the single source of truth for the
|
|
3
|
+
* `security.*` span-attribute contract emitted by `autotel-audit`
|
|
4
|
+
* (`securityEvent()`, `withSecurity()`, `createSecuritySignalProcessor()`)
|
|
5
|
+
* and consumed by `autotel-subscribers`, `autotel-devtools`, and the
|
|
6
|
+
* `autotel security` CLI commands.
|
|
7
|
+
*
|
|
8
|
+
* Dependency-free and side-effect-free by design: safe to import from
|
|
9
|
+
* browser bundles (devtools widget) and anything else that only needs
|
|
10
|
+
* the constants, without pulling in the OpenTelemetry SDK.
|
|
11
|
+
*/
|
|
12
|
+
type SecuritySeverity = 'info' | 'warning' | 'error' | 'critical';
|
|
13
|
+
/** All severities, lowest first. */
|
|
14
|
+
declare const SECURITY_SEVERITIES: readonly SecuritySeverity[];
|
|
15
|
+
/** Numeric rank per severity for threshold comparisons. */
|
|
16
|
+
declare const SECURITY_SEVERITY_RANK: Record<SecuritySeverity, number>;
|
|
17
|
+
/**
|
|
18
|
+
* Parse an untrusted value (span attribute, event payload field) into a
|
|
19
|
+
* severity, falling back when it is missing or malformed.
|
|
20
|
+
*/
|
|
21
|
+
declare function parseSecuritySeverity(value: unknown, fallback?: SecuritySeverity): SecuritySeverity;
|
|
22
|
+
/** `true` when `severity` meets or exceeds `min`. */
|
|
23
|
+
declare function securitySeverityAtLeast(severity: SecuritySeverity, min: SecuritySeverity): boolean;
|
|
24
|
+
/** The higher-ranked of two severities (e.g. escalate failures to ≥ error). */
|
|
25
|
+
declare function escalateSecuritySeverity(severity: SecuritySeverity, floor: SecuritySeverity): SecuritySeverity;
|
|
26
|
+
/**
|
|
27
|
+
* Span attribute keys of the security schema. Emitters and consumers must
|
|
28
|
+
* reference these instead of re-typing the strings.
|
|
29
|
+
*/
|
|
30
|
+
declare const SECURITY_ATTR: {
|
|
31
|
+
/** Marker set on every span carrying a security event. */
|
|
32
|
+
readonly marker: "autotel.security";
|
|
33
|
+
/** Set when the event was force-kept through tail sampling. */
|
|
34
|
+
readonly forceKeep: "autotel.security.force_keep";
|
|
35
|
+
readonly event: "security.event";
|
|
36
|
+
readonly category: "security.category";
|
|
37
|
+
readonly outcome: "security.outcome";
|
|
38
|
+
readonly severity: "security.severity";
|
|
39
|
+
readonly actorId: "security.actor_id";
|
|
40
|
+
readonly targetType: "security.target_type";
|
|
41
|
+
readonly targetId: "security.target_id";
|
|
42
|
+
readonly tenantId: "security.tenant_id";
|
|
43
|
+
readonly reason: "security.reason";
|
|
44
|
+
/** Custom metadata keys dropped because they looked credential-shaped. */
|
|
45
|
+
readonly droppedKeys: "security.dropped_keys";
|
|
46
|
+
/** Set by the signal processor on suspicious request paths. */
|
|
47
|
+
readonly suspiciousRequest: "security.suspicious_request";
|
|
48
|
+
/** Pattern name that flagged a suspicious request, e.g. `path_traversal`. */
|
|
49
|
+
readonly signal: "security.signal";
|
|
50
|
+
};
|
|
51
|
+
/** Metric names emitted by the security instrumentation. */
|
|
52
|
+
declare const SECURITY_METRICS: {
|
|
53
|
+
readonly events: "autotel.security.events";
|
|
54
|
+
readonly httpSuspicious: "autotel.security.http.suspicious";
|
|
55
|
+
readonly httpDenied: "autotel.security.http.denied";
|
|
56
|
+
readonly anomaly: "autotel.security.anomaly";
|
|
57
|
+
readonly heartbeat: "autotel.security.heartbeat";
|
|
58
|
+
};
|
|
59
|
+
/** HTTP statuses counted as denied responses by default. */
|
|
60
|
+
declare const SECURITY_DENIED_STATUSES: readonly number[];
|
|
61
|
+
/**
|
|
62
|
+
* Span attributes carrying the HTTP response status, current semconv
|
|
63
|
+
* first, legacy fallback second.
|
|
64
|
+
*/
|
|
65
|
+
declare const HTTP_STATUS_ATTRIBUTES: readonly string[];
|
|
66
|
+
|
|
67
|
+
export { HTTP_STATUS_ATTRIBUTES, SECURITY_ATTR, SECURITY_DENIED_STATUSES, SECURITY_METRICS, SECURITY_SEVERITIES, SECURITY_SEVERITY_RANK, type SecuritySeverity, escalateSecuritySeverity, parseSecuritySeverity, securitySeverityAtLeast };
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
// src/security-schema.ts
|
|
2
|
+
var SECURITY_SEVERITIES = [
|
|
3
|
+
"info",
|
|
4
|
+
"warning",
|
|
5
|
+
"error",
|
|
6
|
+
"critical"
|
|
7
|
+
];
|
|
8
|
+
var SECURITY_SEVERITY_RANK = {
|
|
9
|
+
info: 0,
|
|
10
|
+
warning: 1,
|
|
11
|
+
error: 2,
|
|
12
|
+
critical: 3
|
|
13
|
+
};
|
|
14
|
+
function parseSecuritySeverity(value, fallback = "info") {
|
|
15
|
+
return typeof value === "string" && value in SECURITY_SEVERITY_RANK ? value : fallback;
|
|
16
|
+
}
|
|
17
|
+
function securitySeverityAtLeast(severity, min) {
|
|
18
|
+
return SECURITY_SEVERITY_RANK[severity] >= SECURITY_SEVERITY_RANK[min];
|
|
19
|
+
}
|
|
20
|
+
function escalateSecuritySeverity(severity, floor) {
|
|
21
|
+
return SECURITY_SEVERITY_RANK[severity] >= SECURITY_SEVERITY_RANK[floor] ? severity : floor;
|
|
22
|
+
}
|
|
23
|
+
var SECURITY_ATTR = {
|
|
24
|
+
/** Marker set on every span carrying a security event. */
|
|
25
|
+
marker: "autotel.security",
|
|
26
|
+
/** Set when the event was force-kept through tail sampling. */
|
|
27
|
+
forceKeep: "autotel.security.force_keep",
|
|
28
|
+
event: "security.event",
|
|
29
|
+
category: "security.category",
|
|
30
|
+
outcome: "security.outcome",
|
|
31
|
+
severity: "security.severity",
|
|
32
|
+
actorId: "security.actor_id",
|
|
33
|
+
targetType: "security.target_type",
|
|
34
|
+
targetId: "security.target_id",
|
|
35
|
+
tenantId: "security.tenant_id",
|
|
36
|
+
reason: "security.reason",
|
|
37
|
+
/** Custom metadata keys dropped because they looked credential-shaped. */
|
|
38
|
+
droppedKeys: "security.dropped_keys",
|
|
39
|
+
/** Set by the signal processor on suspicious request paths. */
|
|
40
|
+
suspiciousRequest: "security.suspicious_request",
|
|
41
|
+
/** Pattern name that flagged a suspicious request, e.g. `path_traversal`. */
|
|
42
|
+
signal: "security.signal"
|
|
43
|
+
};
|
|
44
|
+
var SECURITY_METRICS = {
|
|
45
|
+
events: "autotel.security.events",
|
|
46
|
+
httpSuspicious: "autotel.security.http.suspicious",
|
|
47
|
+
httpDenied: "autotel.security.http.denied",
|
|
48
|
+
anomaly: "autotel.security.anomaly",
|
|
49
|
+
heartbeat: "autotel.security.heartbeat"
|
|
50
|
+
};
|
|
51
|
+
var SECURITY_DENIED_STATUSES = [401, 403, 429];
|
|
52
|
+
var HTTP_STATUS_ATTRIBUTES = [
|
|
53
|
+
"http.response.status_code",
|
|
54
|
+
"http.status_code"
|
|
55
|
+
];
|
|
56
|
+
|
|
57
|
+
export { HTTP_STATUS_ATTRIBUTES, SECURITY_ATTR, SECURITY_DENIED_STATUSES, SECURITY_METRICS, SECURITY_SEVERITIES, SECURITY_SEVERITY_RANK, escalateSecuritySeverity, parseSecuritySeverity, securitySeverityAtLeast };
|
|
58
|
+
//# sourceMappingURL=security-schema.js.map
|
|
59
|
+
//# sourceMappingURL=security-schema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/security-schema.ts"],"names":[],"mappings":";AAeO,IAAM,mBAAA,GAAmD;AAAA,EAC9D,MAAA;AAAA,EACA,SAAA;AAAA,EACA,OAAA;AAAA,EACA;AACF;AAGO,IAAM,sBAAA,GAA2D;AAAA,EACtE,IAAA,EAAM,CAAA;AAAA,EACN,OAAA,EAAS,CAAA;AAAA,EACT,KAAA,EAAO,CAAA;AAAA,EACP,QAAA,EAAU;AACZ;AAMO,SAAS,qBAAA,CACd,KAAA,EACA,QAAA,GAA6B,MAAA,EACX;AAClB,EAAA,OAAO,OAAO,KAAA,KAAU,QAAA,IAAY,KAAA,IAAS,yBACxC,KAAA,GACD,QAAA;AACN;AAGO,SAAS,uBAAA,CACd,UACA,GAAA,EACS;AACT,EAAA,OAAO,sBAAA,CAAuB,QAAQ,CAAA,IAAK,sBAAA,CAAuB,GAAG,CAAA;AACvE;AAGO,SAAS,wBAAA,CACd,UACA,KAAA,EACkB;AAClB,EAAA,OAAO,uBAAuB,QAAQ,CAAA,IAAK,sBAAA,CAAuB,KAAK,IACnE,QAAA,GACA,KAAA;AACN;AAMO,IAAM,aAAA,GAAgB;AAAA;AAAA,EAE3B,MAAA,EAAQ,kBAAA;AAAA;AAAA,EAER,SAAA,EAAW,6BAAA;AAAA,EACX,KAAA,EAAO,gBAAA;AAAA,EACP,QAAA,EAAU,mBAAA;AAAA,EACV,OAAA,EAAS,kBAAA;AAAA,EACT,QAAA,EAAU,mBAAA;AAAA,EACV,OAAA,EAAS,mBAAA;AAAA,EACT,UAAA,EAAY,sBAAA;AAAA,EACZ,QAAA,EAAU,oBAAA;AAAA,EACV,QAAA,EAAU,oBAAA;AAAA,EACV,MAAA,EAAQ,iBAAA;AAAA;AAAA,EAER,WAAA,EAAa,uBAAA;AAAA;AAAA,EAEb,iBAAA,EAAmB,6BAAA;AAAA;AAAA,EAEnB,MAAA,EAAQ;AACV;AAGO,IAAM,gBAAA,GAAmB;AAAA,EAC9B,MAAA,EAAQ,yBAAA;AAAA,EACR,cAAA,EAAgB,kCAAA;AAAA,EAChB,UAAA,EAAY,8BAAA;AAAA,EACZ,OAAA,EAAS,0BAAA;AAAA,EACT,SAAA,EAAW;AACb;AAGO,IAAM,wBAAA,GAA8C,CAAC,GAAA,EAAK,GAAA,EAAK,GAAG;AAMlE,IAAM,sBAAA,GAA4C;AAAA,EACvD,2BAAA;AAAA,EACA;AACF","file":"security-schema.js","sourcesContent":["/**\n * Security telemetry wire schema — the single source of truth for the\n * `security.*` span-attribute contract emitted by `autotel-audit`\n * (`securityEvent()`, `withSecurity()`, `createSecuritySignalProcessor()`)\n * and consumed by `autotel-subscribers`, `autotel-devtools`, and the\n * `autotel security` CLI commands.\n *\n * Dependency-free and side-effect-free by design: safe to import from\n * browser bundles (devtools widget) and anything else that only needs\n * the constants, without pulling in the OpenTelemetry SDK.\n */\n\nexport type SecuritySeverity = 'info' | 'warning' | 'error' | 'critical';\n\n/** All severities, lowest first. */\nexport const SECURITY_SEVERITIES: readonly SecuritySeverity[] = [\n 'info',\n 'warning',\n 'error',\n 'critical',\n];\n\n/** Numeric rank per severity for threshold comparisons. */\nexport const SECURITY_SEVERITY_RANK: Record<SecuritySeverity, number> = {\n info: 0,\n warning: 1,\n error: 2,\n critical: 3,\n};\n\n/**\n * Parse an untrusted value (span attribute, event payload field) into a\n * severity, falling back when it is missing or malformed.\n */\nexport function parseSecuritySeverity(\n value: unknown,\n fallback: SecuritySeverity = 'info',\n): SecuritySeverity {\n return typeof value === 'string' && value in SECURITY_SEVERITY_RANK\n ? (value as SecuritySeverity)\n : fallback;\n}\n\n/** `true` when `severity` meets or exceeds `min`. */\nexport function securitySeverityAtLeast(\n severity: SecuritySeverity,\n min: SecuritySeverity,\n): boolean {\n return SECURITY_SEVERITY_RANK[severity] >= SECURITY_SEVERITY_RANK[min];\n}\n\n/** The higher-ranked of two severities (e.g. escalate failures to ≥ error). */\nexport function escalateSecuritySeverity(\n severity: SecuritySeverity,\n floor: SecuritySeverity,\n): SecuritySeverity {\n return SECURITY_SEVERITY_RANK[severity] >= SECURITY_SEVERITY_RANK[floor]\n ? severity\n : floor;\n}\n\n/**\n * Span attribute keys of the security schema. Emitters and consumers must\n * reference these instead of re-typing the strings.\n */\nexport const SECURITY_ATTR = {\n /** Marker set on every span carrying a security event. */\n marker: 'autotel.security',\n /** Set when the event was force-kept through tail sampling. */\n forceKeep: 'autotel.security.force_keep',\n event: 'security.event',\n category: 'security.category',\n outcome: 'security.outcome',\n severity: 'security.severity',\n actorId: 'security.actor_id',\n targetType: 'security.target_type',\n targetId: 'security.target_id',\n tenantId: 'security.tenant_id',\n reason: 'security.reason',\n /** Custom metadata keys dropped because they looked credential-shaped. */\n droppedKeys: 'security.dropped_keys',\n /** Set by the signal processor on suspicious request paths. */\n suspiciousRequest: 'security.suspicious_request',\n /** Pattern name that flagged a suspicious request, e.g. `path_traversal`. */\n signal: 'security.signal',\n} as const;\n\n/** Metric names emitted by the security instrumentation. */\nexport const SECURITY_METRICS = {\n events: 'autotel.security.events',\n httpSuspicious: 'autotel.security.http.suspicious',\n httpDenied: 'autotel.security.http.denied',\n anomaly: 'autotel.security.anomaly',\n heartbeat: 'autotel.security.heartbeat',\n} as const;\n\n/** HTTP statuses counted as denied responses by default. */\nexport const SECURITY_DENIED_STATUSES: readonly number[] = [401, 403, 429];\n\n/**\n * Span attributes carrying the HTTP response status, current semconv\n * first, legacy fallback second.\n */\nexport const HTTP_STATUS_ATTRIBUTES: readonly string[] = [\n 'http.response.status_code',\n 'http.status_code',\n];\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "autotel",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.5.0",
|
|
4
4
|
"description": "Write Once, Observe Anywhere",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -203,6 +203,11 @@
|
|
|
203
203
|
"import": "./dist/correlation-id.js",
|
|
204
204
|
"require": "./dist/correlation-id.cjs"
|
|
205
205
|
},
|
|
206
|
+
"./security-schema": {
|
|
207
|
+
"types": "./dist/security-schema.d.ts",
|
|
208
|
+
"import": "./dist/security-schema.js",
|
|
209
|
+
"require": "./dist/security-schema.cjs"
|
|
210
|
+
},
|
|
206
211
|
"./test-span-collector": {
|
|
207
212
|
"types": "./dist/test-span-collector.d.ts",
|
|
208
213
|
"import": "./dist/test-span-collector.js",
|
|
@@ -273,7 +278,7 @@
|
|
|
273
278
|
"@opentelemetry/resource-detector-container": "^0.8.9",
|
|
274
279
|
"@opentelemetry/resource-detector-gcp": "^0.53.0",
|
|
275
280
|
"@opentelemetry/sdk-trace-node": "^2.7.1",
|
|
276
|
-
"@traceloop/node-server-sdk": "^0.
|
|
281
|
+
"@traceloop/node-server-sdk": "^0.27.0",
|
|
277
282
|
"pino": "^10.3.1",
|
|
278
283
|
"pino-pretty": "^13.1.3",
|
|
279
284
|
"yaml": "^2.9.0"
|
|
@@ -332,21 +337,21 @@
|
|
|
332
337
|
"@total-typescript/ts-reset": "^0.6.1",
|
|
333
338
|
"@total-typescript/tsconfig": "^1.0.4",
|
|
334
339
|
"@types/eslint-config-prettier": "^6.11.3",
|
|
335
|
-
"@types/node": "^25.9.
|
|
336
|
-
"@typescript-eslint/eslint-plugin": "^8.60.
|
|
337
|
-
"@typescript-eslint/parser": "^8.60.
|
|
340
|
+
"@types/node": "^25.9.2",
|
|
341
|
+
"@typescript-eslint/eslint-plugin": "^8.60.1",
|
|
342
|
+
"@typescript-eslint/parser": "^8.60.1",
|
|
338
343
|
"eslint-config-prettier": "^10.1.8",
|
|
339
344
|
"eslint-plugin-unicorn": "^64.0.0",
|
|
340
345
|
"pino": "^10.3.1",
|
|
341
346
|
"prettier": "^3.8.3",
|
|
342
347
|
"rimraf": "^6.1.3",
|
|
343
348
|
"tsup": "^8.5.1",
|
|
344
|
-
"tsx": "^4.22.
|
|
349
|
+
"tsx": "^4.22.4",
|
|
345
350
|
"typescript": "^6.0.3",
|
|
346
|
-
"typescript-eslint": "^8.60.
|
|
351
|
+
"typescript-eslint": "^8.60.1",
|
|
347
352
|
"unplugin-swc": "^1.5.9",
|
|
348
353
|
"vite-tsconfig-paths": "^6.1.1",
|
|
349
|
-
"vitest": "^4.1.
|
|
354
|
+
"vitest": "^4.1.8",
|
|
350
355
|
"vitest-mock-extended": "^4.0.0",
|
|
351
356
|
"winston": "^3.19.0",
|
|
352
357
|
"yaml": "^2.9.0"
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import {
|
|
3
|
+
SECURITY_ATTR,
|
|
4
|
+
SECURITY_DENIED_STATUSES,
|
|
5
|
+
SECURITY_SEVERITIES,
|
|
6
|
+
SECURITY_SEVERITY_RANK,
|
|
7
|
+
escalateSecuritySeverity,
|
|
8
|
+
parseSecuritySeverity,
|
|
9
|
+
securitySeverityAtLeast,
|
|
10
|
+
} from './security-schema';
|
|
11
|
+
|
|
12
|
+
describe('security-schema', () => {
|
|
13
|
+
it('ranks severities in declaration order', () => {
|
|
14
|
+
const ranks = SECURITY_SEVERITIES.map((s) => SECURITY_SEVERITY_RANK[s]);
|
|
15
|
+
expect(ranks).toEqual([0, 1, 2, 3]);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it('parses valid severities and falls back on garbage', () => {
|
|
19
|
+
expect(parseSecuritySeverity('critical')).toBe('critical');
|
|
20
|
+
expect(parseSecuritySeverity('warning')).toBe('warning');
|
|
21
|
+
expect(parseSecuritySeverity('CRITICAL')).toBe('info');
|
|
22
|
+
expect(parseSecuritySeverity(42)).toBe('info');
|
|
23
|
+
expect(parseSecuritySeverity(undefined)).toBe('info');
|
|
24
|
+
expect(parseSecuritySeverity(undefined, 'warning')).toBe('warning');
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('compares severities against a threshold', () => {
|
|
28
|
+
expect(securitySeverityAtLeast('error', 'warning')).toBe(true);
|
|
29
|
+
expect(securitySeverityAtLeast('warning', 'warning')).toBe(true);
|
|
30
|
+
expect(securitySeverityAtLeast('info', 'warning')).toBe(false);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('escalates to the floor but never downgrades', () => {
|
|
34
|
+
expect(escalateSecuritySeverity('info', 'error')).toBe('error');
|
|
35
|
+
expect(escalateSecuritySeverity('error', 'error')).toBe('error');
|
|
36
|
+
expect(escalateSecuritySeverity('critical', 'error')).toBe('critical');
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('keeps the attribute contract stable', () => {
|
|
40
|
+
expect(SECURITY_ATTR.event).toBe('security.event');
|
|
41
|
+
expect(SECURITY_ATTR.severity).toBe('security.severity');
|
|
42
|
+
expect(SECURITY_ATTR.suspiciousRequest).toBe('security.suspicious_request');
|
|
43
|
+
expect(SECURITY_DENIED_STATUSES).toEqual([401, 403, 429]);
|
|
44
|
+
});
|
|
45
|
+
});
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Security telemetry wire schema — the single source of truth for the
|
|
3
|
+
* `security.*` span-attribute contract emitted by `autotel-audit`
|
|
4
|
+
* (`securityEvent()`, `withSecurity()`, `createSecuritySignalProcessor()`)
|
|
5
|
+
* and consumed by `autotel-subscribers`, `autotel-devtools`, and the
|
|
6
|
+
* `autotel security` CLI commands.
|
|
7
|
+
*
|
|
8
|
+
* Dependency-free and side-effect-free by design: safe to import from
|
|
9
|
+
* browser bundles (devtools widget) and anything else that only needs
|
|
10
|
+
* the constants, without pulling in the OpenTelemetry SDK.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
export type SecuritySeverity = 'info' | 'warning' | 'error' | 'critical';
|
|
14
|
+
|
|
15
|
+
/** All severities, lowest first. */
|
|
16
|
+
export const SECURITY_SEVERITIES: readonly SecuritySeverity[] = [
|
|
17
|
+
'info',
|
|
18
|
+
'warning',
|
|
19
|
+
'error',
|
|
20
|
+
'critical',
|
|
21
|
+
];
|
|
22
|
+
|
|
23
|
+
/** Numeric rank per severity for threshold comparisons. */
|
|
24
|
+
export const SECURITY_SEVERITY_RANK: Record<SecuritySeverity, number> = {
|
|
25
|
+
info: 0,
|
|
26
|
+
warning: 1,
|
|
27
|
+
error: 2,
|
|
28
|
+
critical: 3,
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Parse an untrusted value (span attribute, event payload field) into a
|
|
33
|
+
* severity, falling back when it is missing or malformed.
|
|
34
|
+
*/
|
|
35
|
+
export function parseSecuritySeverity(
|
|
36
|
+
value: unknown,
|
|
37
|
+
fallback: SecuritySeverity = 'info',
|
|
38
|
+
): SecuritySeverity {
|
|
39
|
+
return typeof value === 'string' && value in SECURITY_SEVERITY_RANK
|
|
40
|
+
? (value as SecuritySeverity)
|
|
41
|
+
: fallback;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** `true` when `severity` meets or exceeds `min`. */
|
|
45
|
+
export function securitySeverityAtLeast(
|
|
46
|
+
severity: SecuritySeverity,
|
|
47
|
+
min: SecuritySeverity,
|
|
48
|
+
): boolean {
|
|
49
|
+
return SECURITY_SEVERITY_RANK[severity] >= SECURITY_SEVERITY_RANK[min];
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** The higher-ranked of two severities (e.g. escalate failures to ≥ error). */
|
|
53
|
+
export function escalateSecuritySeverity(
|
|
54
|
+
severity: SecuritySeverity,
|
|
55
|
+
floor: SecuritySeverity,
|
|
56
|
+
): SecuritySeverity {
|
|
57
|
+
return SECURITY_SEVERITY_RANK[severity] >= SECURITY_SEVERITY_RANK[floor]
|
|
58
|
+
? severity
|
|
59
|
+
: floor;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Span attribute keys of the security schema. Emitters and consumers must
|
|
64
|
+
* reference these instead of re-typing the strings.
|
|
65
|
+
*/
|
|
66
|
+
export const SECURITY_ATTR = {
|
|
67
|
+
/** Marker set on every span carrying a security event. */
|
|
68
|
+
marker: 'autotel.security',
|
|
69
|
+
/** Set when the event was force-kept through tail sampling. */
|
|
70
|
+
forceKeep: 'autotel.security.force_keep',
|
|
71
|
+
event: 'security.event',
|
|
72
|
+
category: 'security.category',
|
|
73
|
+
outcome: 'security.outcome',
|
|
74
|
+
severity: 'security.severity',
|
|
75
|
+
actorId: 'security.actor_id',
|
|
76
|
+
targetType: 'security.target_type',
|
|
77
|
+
targetId: 'security.target_id',
|
|
78
|
+
tenantId: 'security.tenant_id',
|
|
79
|
+
reason: 'security.reason',
|
|
80
|
+
/** Custom metadata keys dropped because they looked credential-shaped. */
|
|
81
|
+
droppedKeys: 'security.dropped_keys',
|
|
82
|
+
/** Set by the signal processor on suspicious request paths. */
|
|
83
|
+
suspiciousRequest: 'security.suspicious_request',
|
|
84
|
+
/** Pattern name that flagged a suspicious request, e.g. `path_traversal`. */
|
|
85
|
+
signal: 'security.signal',
|
|
86
|
+
} as const;
|
|
87
|
+
|
|
88
|
+
/** Metric names emitted by the security instrumentation. */
|
|
89
|
+
export const SECURITY_METRICS = {
|
|
90
|
+
events: 'autotel.security.events',
|
|
91
|
+
httpSuspicious: 'autotel.security.http.suspicious',
|
|
92
|
+
httpDenied: 'autotel.security.http.denied',
|
|
93
|
+
anomaly: 'autotel.security.anomaly',
|
|
94
|
+
heartbeat: 'autotel.security.heartbeat',
|
|
95
|
+
} as const;
|
|
96
|
+
|
|
97
|
+
/** HTTP statuses counted as denied responses by default. */
|
|
98
|
+
export const SECURITY_DENIED_STATUSES: readonly number[] = [401, 403, 429];
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Span attributes carrying the HTTP response status, current semconv
|
|
102
|
+
* first, legacy fallback second.
|
|
103
|
+
*/
|
|
104
|
+
export const HTTP_STATUS_ATTRIBUTES: readonly string[] = [
|
|
105
|
+
'http.response.status_code',
|
|
106
|
+
'http.status_code',
|
|
107
|
+
];
|