@apifuse/provider-sdk 2.2.0-beta.59 → 2.2.0-beta.60
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/AUTHORING.md +16 -0
- package/CHANGELOG.md +4 -0
- package/bin/apifuse-dev.ts +2 -1
- package/bin/apifuse-perf.ts +41 -4
- package/bin/apifuse-record.ts +3 -2
- package/dist/config/loader.js +13 -12
- package/dist/engine.js +3 -2
- package/dist/index.d.ts +2 -1
- package/dist/index.js +1 -1
- package/dist/lint.js +36 -15
- package/dist/provider.d.ts +1 -1
- package/dist/provider.js +1 -1
- package/dist/runtime/browser.js +2 -1
- package/dist/runtime/cache.js +3 -2
- package/dist/runtime/diagnostic-env-values.d.ts +5 -0
- package/dist/runtime/diagnostic-env-values.js +57 -0
- package/dist/runtime/diagnostic-env.d.ts +11 -0
- package/dist/runtime/diagnostic-env.js +35 -0
- package/dist/runtime/diagnostic-redactor.d.ts +49 -0
- package/dist/runtime/diagnostic-redactor.js +569 -0
- package/dist/runtime/egress-lease.d.ts +2 -0
- package/dist/runtime/egress-lease.js +4 -1
- package/dist/runtime/env.js +2 -1
- package/dist/runtime/instrumentation.js +3 -0
- package/dist/runtime/ocr.js +2 -1
- package/dist/runtime/otlp.js +5 -4
- package/dist/runtime/proxy-nodemaven.js +3 -2
- package/dist/runtime/request-options.d.ts +7 -0
- package/dist/runtime/request-options.js +62 -22
- package/dist/runtime/resolver-vendors/browser.js +0 -1
- package/dist/runtime/resolver.js +3 -2
- package/dist/runtime/stt.js +3 -2
- package/dist/runtime/trace.d.ts +5 -0
- package/dist/runtime/trace.js +115 -20
- package/dist/schema.d.ts +11 -0
- package/dist/schema.js +56 -2
- package/dist/server/self-test-redaction.js +8 -33
- package/dist/server/sensitive-values.d.ts +26 -0
- package/dist/server/sensitive-values.js +131 -0
- package/dist/server/serve-implementation.js +219 -78
- package/dist/server/trace-output.d.ts +2 -1
- package/dist/server/trace-output.js +46 -14
- package/dist/trace-sanitization.d.ts +4 -3
- package/dist/trace-sanitization.js +45 -22
- package/package.json +2 -1
- package/src/config/loader.ts +13 -12
- package/src/engine.ts +3 -2
- package/src/index.ts +2 -0
- package/src/lint.ts +42 -16
- package/src/provider.ts +1 -0
- package/src/runtime/browser.ts +2 -1
- package/src/runtime/cache.ts +3 -2
- package/src/runtime/diagnostic-env-values.ts +56 -0
- package/src/runtime/diagnostic-env.ts +55 -0
- package/src/runtime/diagnostic-redactor.ts +662 -0
- package/src/runtime/egress-lease.ts +6 -1
- package/src/runtime/env.ts +2 -1
- package/src/runtime/instrumentation.ts +3 -0
- package/src/runtime/ocr.ts +2 -1
- package/src/runtime/otlp.ts +5 -4
- package/src/runtime/proxy-nodemaven.ts +3 -2
- package/src/runtime/request-options.ts +76 -28
- package/src/runtime/resolver-vendors/browser.ts +0 -1
- package/src/runtime/resolver.ts +3 -2
- package/src/runtime/stt.ts +3 -2
- package/src/runtime/trace.ts +168 -26
- package/src/schema.ts +63 -2
- package/src/server/self-test-redaction.ts +10 -40
- package/src/server/sensitive-values.ts +200 -0
- package/src/server/serve-implementation.ts +301 -96
- package/src/server/trace-output.ts +77 -17
- package/src/trace-sanitization.ts +70 -23
|
@@ -1,4 +1,11 @@
|
|
|
1
1
|
import { isSensitiveFixtureKey, REDACTED_FIXTURE_VALUE } from "../fixture-sanitization.js";
|
|
2
|
+
import {
|
|
3
|
+
type DiagnosticRedactor,
|
|
4
|
+
redactedKeyAllocator,
|
|
5
|
+
isRedactedKey,
|
|
6
|
+
REDACTION_FAILED,
|
|
7
|
+
redactDiagnosticText,
|
|
8
|
+
} from "../runtime/diagnostic-redactor.js";
|
|
2
9
|
import {
|
|
3
10
|
OTEL_EXPORTER_OTLP_ENDPOINT,
|
|
4
11
|
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT,
|
|
@@ -22,6 +29,16 @@ import type { TraceConfig } from "../types.js";
|
|
|
22
29
|
|
|
23
30
|
type EnvLike = Record<string, string | undefined>;
|
|
24
31
|
|
|
32
|
+
const TRACE_RESOURCE_ATTRIBUTE_SANITIZER = Symbol.for(
|
|
33
|
+
"@apifuse/provider-sdk/runtime/trace-resource-attribute-sanitizer",
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
type ResourceAttributesWithSanitizer = Record<string, string> & {
|
|
37
|
+
[TRACE_RESOURCE_ATTRIBUTE_SANITIZER]?: (
|
|
38
|
+
attributes: Record<string, string>,
|
|
39
|
+
) => Record<string, string>;
|
|
40
|
+
};
|
|
41
|
+
|
|
25
42
|
// Warn once per environment object: process.env in production, each injected env in tests.
|
|
26
43
|
const warnedEnvironments = new WeakSet<EnvLike>();
|
|
27
44
|
const warnedResourceEnvironments = new WeakSet<EnvLike>();
|
|
@@ -73,22 +90,52 @@ function resolveServerOTLPExportOptions(
|
|
|
73
90
|
function resolveExportResourceAttributes(
|
|
74
91
|
requestAttributes: Record<string, string>,
|
|
75
92
|
env: EnvLike,
|
|
76
|
-
|
|
93
|
+
redact?: DiagnosticRedactor,
|
|
94
|
+
): {
|
|
95
|
+
attributes: Record<string, string>;
|
|
96
|
+
sanitize: (attributes: Record<string, string>) => Record<string, string>;
|
|
97
|
+
} {
|
|
77
98
|
const resolution = resolveOTLPResourceAttributes({}, env);
|
|
78
99
|
warnDiscardedResourceAttributes(env, resolution);
|
|
79
|
-
const operatorAttributes =
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
100
|
+
const operatorAttributes = resolution.attributes;
|
|
101
|
+
const requestKeys = new Set(Object.keys(requestAttributes));
|
|
102
|
+
return {
|
|
103
|
+
// Keep the raw snapshot private until export. A credential registered after
|
|
104
|
+
// trace creation must be matched before any key or value is bounded.
|
|
105
|
+
attributes: { ...operatorAttributes, ...requestAttributes },
|
|
106
|
+
sanitize(attributes) {
|
|
107
|
+
const nextKey = redactedKeyAllocator(Object.keys(attributes));
|
|
108
|
+
const sanitizedOperatorAttributes: Record<string, string> = {};
|
|
109
|
+
const rawRequestAttributes: Record<string, string> = {};
|
|
110
|
+
for (const [key, value] of Object.entries(attributes)) {
|
|
111
|
+
const unchangedOperatorValue = !requestKeys.has(key) && operatorAttributes[key] === value;
|
|
112
|
+
if (unchangedOperatorValue) {
|
|
113
|
+
const redactedKey = isRedactedKey(key) ? key : redactDiagnosticText(key, redact);
|
|
114
|
+
const keyChanged = redactedKey !== key;
|
|
115
|
+
const sanitizedKey = keyChanged ? nextKey() : sanitizeSpanNameForOutput(key);
|
|
116
|
+
sanitizedOperatorAttributes[sanitizedKey] = keyChanged
|
|
117
|
+
? redactedKey === REDACTION_FAILED
|
|
118
|
+
? REDACTION_FAILED
|
|
119
|
+
: REDACTED_FIXTURE_VALUE
|
|
120
|
+
: isSensitiveFixtureKey(key)
|
|
121
|
+
? REDACTED_FIXTURE_VALUE
|
|
122
|
+
: sanitizeSpanNameForOutput(value, redact);
|
|
123
|
+
} else {
|
|
124
|
+
rawRequestAttributes[key] = value;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
const sanitizedRequestAttributes = Object.fromEntries(
|
|
128
|
+
Object.entries(
|
|
129
|
+
sanitizeTraceAttributes(
|
|
130
|
+
rawRequestAttributes,
|
|
131
|
+
redact,
|
|
132
|
+
Object.keys(sanitizedOperatorAttributes),
|
|
133
|
+
),
|
|
134
|
+
).map(([key, value]) => [key, String(value)]),
|
|
135
|
+
);
|
|
136
|
+
return { ...sanitizedOperatorAttributes, ...sanitizedRequestAttributes };
|
|
137
|
+
},
|
|
138
|
+
};
|
|
92
139
|
}
|
|
93
140
|
|
|
94
141
|
/** Server-only trace output policy. Shared programmatic trace callers stay in-memory. */
|
|
@@ -96,12 +143,14 @@ export function resolveServerTraceContextOptions(
|
|
|
96
143
|
config: TraceConfig,
|
|
97
144
|
resourceAttributes: Record<string, string>,
|
|
98
145
|
env: EnvLike = process.env,
|
|
146
|
+
redact?: DiagnosticRedactor,
|
|
99
147
|
): CreateTraceContextOptions {
|
|
100
148
|
const resolved = resolveTraceContextOptions(config);
|
|
101
149
|
const outputEnabled = config.enabled !== false && config.exporter !== "none";
|
|
102
150
|
const consoleHook =
|
|
103
151
|
outputEnabled && (config.exporter === "console" || config.exporter === "json")
|
|
104
|
-
? (span: Span) =>
|
|
152
|
+
? (span: Span) =>
|
|
153
|
+
console.log(JSON.stringify(sanitizeSpanForOutput(span, resourceAttributes, redact)))
|
|
105
154
|
: undefined;
|
|
106
155
|
const onSpan =
|
|
107
156
|
consoleHook && resolved.onSpan
|
|
@@ -114,15 +163,26 @@ export function resolveServerTraceContextOptions(
|
|
|
114
163
|
outputEnabled && config.exporter === "otlp"
|
|
115
164
|
? resolveServerOTLPExportOptions(config, env)
|
|
116
165
|
: undefined;
|
|
166
|
+
const exportResources = exportOptions
|
|
167
|
+
? resolveExportResourceAttributes(resourceAttributes, env, redact)
|
|
168
|
+
: undefined;
|
|
169
|
+
if (exportResources) {
|
|
170
|
+
Object.defineProperty(
|
|
171
|
+
exportResources.attributes as ResourceAttributesWithSanitizer,
|
|
172
|
+
TRACE_RESOURCE_ATTRIBUTE_SANITIZER,
|
|
173
|
+
{ value: exportResources.sanitize },
|
|
174
|
+
);
|
|
175
|
+
}
|
|
117
176
|
|
|
118
177
|
return {
|
|
119
178
|
maxSpans: resolved.maxSpans,
|
|
120
179
|
onSpan,
|
|
180
|
+
...(redact ? { redact } : {}),
|
|
121
181
|
...(exportOptions
|
|
122
182
|
? {
|
|
123
183
|
exportOptions,
|
|
124
|
-
resourceAttributes:
|
|
125
|
-
sanitizeSpanForExport: (span: Span) => sanitizeSpanForOutput(span),
|
|
184
|
+
resourceAttributes: exportResources?.attributes,
|
|
185
|
+
sanitizeSpanForExport: (span: Span) => sanitizeSpanForOutput(span, undefined, redact),
|
|
126
186
|
}
|
|
127
187
|
: {}),
|
|
128
188
|
};
|
|
@@ -4,20 +4,33 @@ import {
|
|
|
4
4
|
REDACTED_FIXTURE_VALUE,
|
|
5
5
|
sanitizeDiagnosticText,
|
|
6
6
|
} from "./fixture-sanitization.js";
|
|
7
|
+
import {
|
|
8
|
+
type DiagnosticRedactor,
|
|
9
|
+
diagnosticStructuredRedactor,
|
|
10
|
+
redactDiagnosticAttributeKey,
|
|
11
|
+
copyDiagnosticAttributeTypes,
|
|
12
|
+
isDiagnosticBigintAttribute,
|
|
13
|
+
redactedKeyAllocator,
|
|
14
|
+
isRedactedKey,
|
|
15
|
+
redactCriticalDiagnosticText,
|
|
16
|
+
diagnosticAttributeRedactor,
|
|
17
|
+
REDACTION_FAILED,
|
|
18
|
+
redactDiagnosticText,
|
|
19
|
+
} from "./runtime/diagnostic-redactor.js";
|
|
7
20
|
import type { TraceSpan } from "./types.js";
|
|
8
21
|
|
|
9
22
|
const MAX_TRACE_TEXT_LENGTH = 300;
|
|
10
23
|
|
|
11
|
-
function sanitizeTraceText(value: string): string {
|
|
12
|
-
const encoded = sanitizeDiagnosticText(value);
|
|
24
|
+
function sanitizeTraceText(value: string, redact?: DiagnosticRedactor): string {
|
|
25
|
+
const encoded = sanitizeDiagnosticText(redactDiagnosticText(value, redact));
|
|
13
26
|
return encoded.length > MAX_TRACE_TEXT_LENGTH
|
|
14
27
|
? `${encoded.slice(0, MAX_TRACE_TEXT_LENGTH)}… [truncated]`
|
|
15
28
|
: encoded;
|
|
16
29
|
}
|
|
17
30
|
|
|
18
31
|
/** Span names are SDK-authored identifiers; retain them while preventing log injection. */
|
|
19
|
-
export function sanitizeSpanNameForOutput(value: string): string {
|
|
20
|
-
const encoded = encodeDiagnosticControls(value);
|
|
32
|
+
export function sanitizeSpanNameForOutput(value: string, redact?: DiagnosticRedactor): string {
|
|
33
|
+
const encoded = encodeDiagnosticControls(redactDiagnosticText(value, redact));
|
|
21
34
|
return encoded.length > MAX_TRACE_TEXT_LENGTH
|
|
22
35
|
? `${encoded.slice(0, MAX_TRACE_TEXT_LENGTH)}… [truncated]`
|
|
23
36
|
: encoded;
|
|
@@ -25,39 +38,73 @@ export function sanitizeSpanNameForOutput(value: string): string {
|
|
|
25
38
|
|
|
26
39
|
export function sanitizeTraceAttributes(
|
|
27
40
|
attributes: Record<string, unknown>,
|
|
41
|
+
redact?: DiagnosticRedactor,
|
|
42
|
+
reservedKeys: Iterable<string> = [],
|
|
28
43
|
): Record<string, string | number | boolean> {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
44
|
+
const nextKey = redactedKeyAllocator([...Object.keys(attributes), ...reservedKeys]);
|
|
45
|
+
return copyDiagnosticAttributeTypes(
|
|
46
|
+
attributes,
|
|
47
|
+
Object.fromEntries(
|
|
48
|
+
Object.entries(attributes).map(([key, value]) => {
|
|
49
|
+
const redactedKey = isRedactedKey(key) ? key : redactDiagnosticAttributeKey(key, redact);
|
|
50
|
+
const keyChanged = redactedKey !== key;
|
|
51
|
+
const sanitizedKey = keyChanged ? nextKey() : sanitizeTraceText(key);
|
|
52
|
+
const valueRedactor =
|
|
53
|
+
typeof value === "bigint" || isDiagnosticBigintAttribute(attributes, key)
|
|
54
|
+
? diagnosticStructuredRedactor(redact)
|
|
55
|
+
: diagnosticAttributeRedactor(key, redact);
|
|
56
|
+
const sanitizedValue = keyChanged
|
|
57
|
+
? redactedKey === REDACTION_FAILED
|
|
58
|
+
? REDACTION_FAILED
|
|
59
|
+
: REDACTED_FIXTURE_VALUE
|
|
60
|
+
: isSensitiveFixtureKey(key)
|
|
61
|
+
? REDACTED_FIXTURE_VALUE
|
|
62
|
+
: typeof value === "bigint" || isDiagnosticBigintAttribute(attributes, key)
|
|
63
|
+
? String(value).length < 8
|
|
64
|
+
? String(value)
|
|
65
|
+
: sanitizeTraceText(String(value), valueRedactor)
|
|
66
|
+
: typeof value === "string"
|
|
67
|
+
? sanitizeTraceText(value, valueRedactor)
|
|
68
|
+
: typeof value === "number" || typeof value === "boolean"
|
|
69
|
+
? (() => {
|
|
70
|
+
if (String(value).length < 8) return value;
|
|
71
|
+
const redacted = redactDiagnosticText(
|
|
72
|
+
String(value),
|
|
73
|
+
diagnosticStructuredRedactor(valueRedactor),
|
|
74
|
+
);
|
|
75
|
+
return redacted === String(value) ? value : sanitizeTraceText(redacted);
|
|
76
|
+
})()
|
|
77
|
+
: sanitizeTraceText(String(value), valueRedactor);
|
|
78
|
+
return [sanitizedKey, sanitizedValue];
|
|
79
|
+
}),
|
|
80
|
+
),
|
|
40
81
|
);
|
|
41
82
|
}
|
|
42
83
|
|
|
43
84
|
export function sanitizeSpanForOutput(
|
|
44
85
|
span: TraceSpan,
|
|
45
86
|
additionalAttributes?: Record<string, string>,
|
|
87
|
+
redact?: DiagnosticRedactor,
|
|
46
88
|
): TraceSpan {
|
|
47
89
|
// Keep this schema explicit so future fields are not silently added to a
|
|
48
90
|
// process output path before their trust boundary has been reviewed.
|
|
49
91
|
return {
|
|
50
|
-
id: span.id,
|
|
51
|
-
name: sanitizeSpanNameForOutput(span.name),
|
|
92
|
+
id: redactCriticalDiagnosticText(span.id, redact),
|
|
93
|
+
name: sanitizeSpanNameForOutput(redactCriticalDiagnosticText(span.name, redact)),
|
|
52
94
|
startedAt: span.startedAt,
|
|
53
95
|
endedAt: span.endedAt,
|
|
54
96
|
duration_ms: span.duration_ms,
|
|
55
97
|
status: span.status,
|
|
56
|
-
attributes: sanitizeTraceAttributes(
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
98
|
+
attributes: sanitizeTraceAttributes(
|
|
99
|
+
copyDiagnosticAttributeTypes(span.attributes, {
|
|
100
|
+
...span.attributes,
|
|
101
|
+
...(additionalAttributes ?? {}),
|
|
102
|
+
}),
|
|
103
|
+
redact,
|
|
104
|
+
),
|
|
105
|
+
...(span.error !== undefined ? { error: sanitizeTraceText(span.error, redact) } : {}),
|
|
106
|
+
...(span.parentId !== undefined
|
|
107
|
+
? { parentId: redactCriticalDiagnosticText(span.parentId, redact) }
|
|
108
|
+
: {}),
|
|
62
109
|
};
|
|
63
110
|
}
|