@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,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { collectDiagnosticSensitiveValues, SELF_TEST_SENSITIVE_SOURCE_KINDS, } from "./sensitive-values.js";
|
|
2
2
|
export const SELF_TEST_REDACTED_PLACEHOLDER = "[REDACTED]";
|
|
3
3
|
/** Maximum length of any string echoed in a self-test response. */
|
|
4
4
|
export const SELF_TEST_MAX_TEXT_LENGTH = 300;
|
|
@@ -16,37 +16,12 @@ const HEADER_SHAPED_SECRETS = [
|
|
|
16
16
|
* credential inputs.
|
|
17
17
|
*/
|
|
18
18
|
export function collectSelfTestSensitiveValues(provider, options = {}) {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
envNames.add(secret.name);
|
|
26
|
-
}
|
|
27
|
-
const healthProbe = provider.healthProbe ?? provider.healthMonitor;
|
|
28
|
-
for (const name of healthProbe?.requiredSecrets ?? []) {
|
|
29
|
-
envNames.add(name);
|
|
30
|
-
}
|
|
31
|
-
for (const name of Object.values(healthProbe?.credentialInputs ?? {})) {
|
|
32
|
-
envNames.add(name);
|
|
33
|
-
}
|
|
34
|
-
const values = new Set();
|
|
35
|
-
for (const name of envNames) {
|
|
36
|
-
const value = env[name];
|
|
37
|
-
if (typeof value === "string" && value.length >= MIN_SENSITIVE_VALUE_LENGTH) {
|
|
38
|
-
values.add(value);
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
for (const value of Object.values(options.credentialInputs ?? {})) {
|
|
42
|
-
if (value.length >= MIN_SENSITIVE_VALUE_LENGTH) {
|
|
43
|
-
values.add(value);
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
return [...values];
|
|
47
|
-
}
|
|
48
|
-
function escapeRegExp(value) {
|
|
49
|
-
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
19
|
+
return collectDiagnosticSensitiveValues(provider, {
|
|
20
|
+
...options,
|
|
21
|
+
sourceKinds: SELF_TEST_SENSITIVE_SOURCE_KINDS,
|
|
22
|
+
minimumLength: MIN_SENSITIVE_VALUE_LENGTH,
|
|
23
|
+
includeResolvedEnvVariants: false,
|
|
24
|
+
});
|
|
50
25
|
}
|
|
51
26
|
/**
|
|
52
27
|
* Scrubs known secret values and header-shaped credentials from a string and
|
|
@@ -58,7 +33,7 @@ export function redactSelfTestText(text, sensitiveValues, maxLength = SELF_TEST_
|
|
|
58
33
|
for (const value of sensitiveValues) {
|
|
59
34
|
if (value.length < MIN_SENSITIVE_VALUE_LENGTH)
|
|
60
35
|
continue;
|
|
61
|
-
redacted = redacted.
|
|
36
|
+
redacted = redacted.replaceAll(value, SELF_TEST_REDACTED_PLACEHOLDER);
|
|
62
37
|
}
|
|
63
38
|
for (const pattern of HEADER_SHAPED_SECRETS) {
|
|
64
39
|
redacted = redacted.replace(pattern, (_match, prefix) => `${prefix}${SELF_TEST_REDACTED_PLACEHOLDER}`);
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { ProviderDefinition } from "../types.js";
|
|
2
|
+
export declare const SELF_TEST_SENSITIVE_SOURCE_KINDS: readonly ["selfTestMasterSecrets", "providerSecrets", "healthProbeSecrets", "requestCredentials"];
|
|
3
|
+
/** Explicit source allowlist: no environment-name or payload-shape secret sweep. */
|
|
4
|
+
export declare const DIAGNOSTIC_SENSITIVE_SOURCE_KINDS: readonly ["selfTestMasterSecrets", "providerSecrets", "healthProbeSecrets", "requestCredentials", "engineProxyCredentials", "engineSolverKeys", "engineCeremonyLeaseCredentials", "choiceMasterSecret", "ocrCredentials", "sttCredentials", "cdpCredentials", "statefulForwardingSecret", "redisCredentials", "otlpHeaders", "cacheKeyPepper", "sensitiveParams"];
|
|
5
|
+
export type DiagnosticSensitiveSourceKind = (typeof DIAGNOSTIC_SENSITIVE_SOURCE_KINDS)[number];
|
|
6
|
+
/**
|
|
7
|
+
* Startup inventory; request-scoped readers additionally register live values on access.
|
|
8
|
+
*/
|
|
9
|
+
export declare function collectStaticDiagnosticSensitiveValues(provider: ProviderDefinition, options?: {
|
|
10
|
+
env?: Readonly<Record<string, string | undefined>>;
|
|
11
|
+
statefulForwardingSecret?: string;
|
|
12
|
+
}): string[];
|
|
13
|
+
/** Generalised self-test collector, also used to seed and enrich ordinary request scopes. */
|
|
14
|
+
export declare function collectDiagnosticSensitiveValues(provider: ProviderDefinition, options?: {
|
|
15
|
+
env?: Readonly<Record<string, string | undefined>>;
|
|
16
|
+
credentialInputs?: Readonly<Record<string, unknown>>;
|
|
17
|
+
connectionSecrets?: Readonly<Record<string, string | undefined>>;
|
|
18
|
+
sensitiveParams?: readonly string[];
|
|
19
|
+
statefulForwardingSecret?: string;
|
|
20
|
+
sourceKinds?: readonly DiagnosticSensitiveSourceKind[];
|
|
21
|
+
minimumLength?: number;
|
|
22
|
+
/** Preserve the legacy self-test response collector when false. */
|
|
23
|
+
includeResolvedEnvVariants?: boolean;
|
|
24
|
+
}): string[];
|
|
25
|
+
/** Derive the live-read allowlist from the same collector as the startup inventory. */
|
|
26
|
+
export declare function createDiagnosticEnvObserver(provider: ProviderDefinition, register: (values: readonly string[]) => void, has?: (value: string) => boolean): (name: string, value: string) => void;
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { resolvedStringVariants, urlCredentialComponents, otlpHeaderCredentialValues, } from "../runtime/diagnostic-env-values.js";
|
|
2
|
+
import { PROVIDER_CACHE_REDIS_URL_ENV, PROVIDER_STATE_REDIS_URL_ENV, REDIS_URL_ENV, } from "../config/loader.js";
|
|
3
|
+
import { ENGINE_OWNED_PROXY_CREDENTIAL_ENV_NAMES, ENGINE_OWNED_RESOLVER_CREDENTIAL_ENV_NAMES, } from "../engine.js";
|
|
4
|
+
import { APIFUSE__CACHE__KEY_PEPPER_ENV } from "../runtime/cache.js";
|
|
5
|
+
import { PROVIDER_RUNTIME_CHOICE_TOKEN_MASTER_SECRET_ENV } from "../runtime/choice.js";
|
|
6
|
+
import { APIFUSE__ENGINE__CEREMONY_LEASE_KEY } from "../runtime/egress-lease.js";
|
|
7
|
+
import { APIFUSE__OCR__API_KEY_ENV, APIFUSE__OCR__CLOUDFLARE_API_TOKEN_ENV, } from "../runtime/ocr.js";
|
|
8
|
+
import { OTEL_EXPORTER_OTLP_HEADERS, OTEL_EXPORTER_OTLP_TRACES_HEADERS, OTEL_EXPORTER_OTLP_ENDPOINT, OTEL_EXPORTER_OTLP_TRACES_ENDPOINT, } from "../runtime/otlp.js";
|
|
9
|
+
import { APIFUSE__CDP_POOL__URL } from "../runtime/resolver-config.js";
|
|
10
|
+
import { APIFUSE__STT__CLOUDFLARE_API_TOKEN_ENV } from "../runtime/stt.js";
|
|
11
|
+
import { PROVIDER_RUNTIME_SELF_TEST_MASTER_SECRET_ENV, PROVIDER_RUNTIME_SELF_TEST_MASTER_SECRET_PREVIOUS_ENV, } from "./self-test-token.js";
|
|
12
|
+
export const SELF_TEST_SENSITIVE_SOURCE_KINDS = [
|
|
13
|
+
"selfTestMasterSecrets",
|
|
14
|
+
"providerSecrets",
|
|
15
|
+
"healthProbeSecrets",
|
|
16
|
+
"requestCredentials",
|
|
17
|
+
];
|
|
18
|
+
/** Explicit source allowlist: no environment-name or payload-shape secret sweep. */
|
|
19
|
+
export const DIAGNOSTIC_SENSITIVE_SOURCE_KINDS = [
|
|
20
|
+
...SELF_TEST_SENSITIVE_SOURCE_KINDS,
|
|
21
|
+
"engineProxyCredentials",
|
|
22
|
+
"engineSolverKeys",
|
|
23
|
+
"engineCeremonyLeaseCredentials",
|
|
24
|
+
"choiceMasterSecret",
|
|
25
|
+
"ocrCredentials",
|
|
26
|
+
"sttCredentials",
|
|
27
|
+
"cdpCredentials",
|
|
28
|
+
"statefulForwardingSecret",
|
|
29
|
+
"redisCredentials",
|
|
30
|
+
"otlpHeaders",
|
|
31
|
+
"cacheKeyPepper",
|
|
32
|
+
"sensitiveParams",
|
|
33
|
+
];
|
|
34
|
+
/**
|
|
35
|
+
* Startup inventory; request-scoped readers additionally register live values on access.
|
|
36
|
+
*/
|
|
37
|
+
export function collectStaticDiagnosticSensitiveValues(provider, options = {}) {
|
|
38
|
+
return collectDiagnosticSensitiveValues(provider, {
|
|
39
|
+
...options,
|
|
40
|
+
sourceKinds: DIAGNOSTIC_SENSITIVE_SOURCE_KINDS.filter((kind) => kind !== "requestCredentials" && kind !== "sensitiveParams"),
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
/** Generalised self-test collector, also used to seed and enrich ordinary request scopes. */
|
|
44
|
+
export function collectDiagnosticSensitiveValues(provider, options = {}) {
|
|
45
|
+
const env = options.env ?? process.env;
|
|
46
|
+
const envValues = (names) => names.map((name) => env[name]);
|
|
47
|
+
const resolvedEnvValues = (names) => envValues(names).flatMap((value) => value === undefined
|
|
48
|
+
? []
|
|
49
|
+
: options.includeResolvedEnvVariants === false
|
|
50
|
+
? [value]
|
|
51
|
+
: [value, value.trim()]);
|
|
52
|
+
const healthProbe = provider.healthProbe ?? provider.healthMonitor;
|
|
53
|
+
const sources = {
|
|
54
|
+
selfTestMasterSecrets: () => resolvedEnvValues([
|
|
55
|
+
PROVIDER_RUNTIME_SELF_TEST_MASTER_SECRET_ENV,
|
|
56
|
+
PROVIDER_RUNTIME_SELF_TEST_MASTER_SECRET_PREVIOUS_ENV,
|
|
57
|
+
]),
|
|
58
|
+
providerSecrets: () => resolvedEnvValues((provider.secrets ?? []).map((secret) => secret.name)),
|
|
59
|
+
healthProbeSecrets: () => resolvedEnvValues([
|
|
60
|
+
...(healthProbe?.requiredSecrets ?? []),
|
|
61
|
+
...Object.values(healthProbe?.credentialInputs ?? {}),
|
|
62
|
+
]),
|
|
63
|
+
requestCredentials: () => [
|
|
64
|
+
...Object.values(options.credentialInputs ?? {}),
|
|
65
|
+
...Object.values(options.connectionSecrets ?? {}),
|
|
66
|
+
],
|
|
67
|
+
engineProxyCredentials: () => resolvedEnvValues(ENGINE_OWNED_PROXY_CREDENTIAL_ENV_NAMES),
|
|
68
|
+
// These are the resolver-config API keys after #251's engine credential projection.
|
|
69
|
+
engineSolverKeys: () => resolvedEnvValues(ENGINE_OWNED_RESOLVER_CREDENTIAL_ENV_NAMES),
|
|
70
|
+
engineCeremonyLeaseCredentials: () => resolvedEnvValues([APIFUSE__ENGINE__CEREMONY_LEASE_KEY]),
|
|
71
|
+
choiceMasterSecret: () => resolvedEnvValues([PROVIDER_RUNTIME_CHOICE_TOKEN_MASTER_SECRET_ENV]),
|
|
72
|
+
ocrCredentials: () => resolvedEnvValues([APIFUSE__OCR__CLOUDFLARE_API_TOKEN_ENV, APIFUSE__OCR__API_KEY_ENV]),
|
|
73
|
+
sttCredentials: () => resolvedEnvValues([APIFUSE__STT__CLOUDFLARE_API_TOKEN_ENV]),
|
|
74
|
+
cdpCredentials: () => resolvedStringVariants(env[APIFUSE__CDP_POOL__URL]).flatMap((url) => [
|
|
75
|
+
url,
|
|
76
|
+
...urlCredentialComponents(url, { includePathAndQuery: true }),
|
|
77
|
+
]),
|
|
78
|
+
statefulForwardingSecret: () => resolvedStringVariants(options.statefulForwardingSecret),
|
|
79
|
+
redisCredentials: () => [PROVIDER_CACHE_REDIS_URL_ENV, PROVIDER_STATE_REDIS_URL_ENV, REDIS_URL_ENV]
|
|
80
|
+
.flatMap((name) => resolvedStringVariants(env[name]))
|
|
81
|
+
.flatMap((url) => urlCredentialComponents(url, { includePathAndQuery: false })),
|
|
82
|
+
otlpHeaders: () => [
|
|
83
|
+
...[OTEL_EXPORTER_OTLP_TRACES_HEADERS, OTEL_EXPORTER_OTLP_HEADERS].flatMap((name) => env[name] === undefined ? [] : otlpHeaderCredentialValues(env[name])),
|
|
84
|
+
...[OTEL_EXPORTER_OTLP_ENDPOINT, OTEL_EXPORTER_OTLP_TRACES_ENDPOINT].flatMap((name) => resolvedStringVariants(env[name]).flatMap((url) => urlCredentialComponents(url, { includePathAndQuery: false }))),
|
|
85
|
+
],
|
|
86
|
+
cacheKeyPepper: () => resolvedEnvValues([APIFUSE__CACHE__KEY_PEPPER_ENV]),
|
|
87
|
+
sensitiveParams: () => options.sensitiveParams ?? [],
|
|
88
|
+
};
|
|
89
|
+
const values = new Set();
|
|
90
|
+
for (const kind of options.sourceKinds ?? DIAGNOSTIC_SENSITIVE_SOURCE_KINDS) {
|
|
91
|
+
for (const value of sources[kind]()) {
|
|
92
|
+
if (typeof value === "string" && value.length >= (options.minimumLength ?? 1))
|
|
93
|
+
values.add(value);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return [...values];
|
|
97
|
+
}
|
|
98
|
+
const diagnosticEnvNames = new WeakMap();
|
|
99
|
+
/** Derive the live-read allowlist from the same collector as the startup inventory. */
|
|
100
|
+
export function createDiagnosticEnvObserver(provider, register, has) {
|
|
101
|
+
let names = diagnosticEnvNames.get(provider);
|
|
102
|
+
if (!names) {
|
|
103
|
+
names = new Set();
|
|
104
|
+
const captured = names;
|
|
105
|
+
collectStaticDiagnosticSensitiveValues(provider, {
|
|
106
|
+
env: new Proxy({}, {
|
|
107
|
+
get: (_target, key) => {
|
|
108
|
+
if (typeof key === "string")
|
|
109
|
+
captured.add(key);
|
|
110
|
+
return undefined;
|
|
111
|
+
},
|
|
112
|
+
}),
|
|
113
|
+
});
|
|
114
|
+
diagnosticEnvNames.set(provider, names);
|
|
115
|
+
}
|
|
116
|
+
const seen = new Map();
|
|
117
|
+
return (name, value) => {
|
|
118
|
+
if (!names.has(name) || seen.get(name) === value)
|
|
119
|
+
return;
|
|
120
|
+
if (has?.(value) && has(value.trim()))
|
|
121
|
+
return;
|
|
122
|
+
// Includes URL userinfo/CDP components and OTLP auth values, using the same rules.
|
|
123
|
+
register([
|
|
124
|
+
...resolvedStringVariants(value),
|
|
125
|
+
...collectStaticDiagnosticSensitiveValues(provider, {
|
|
126
|
+
env: { [name]: value },
|
|
127
|
+
}),
|
|
128
|
+
]);
|
|
129
|
+
seen.set(name, value);
|
|
130
|
+
};
|
|
131
|
+
}
|