@apifuse/provider-sdk 2.2.0-beta.58 → 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 +25 -0
- package/CHANGELOG.md +8 -0
- package/README.md +18 -2
- package/bin/apifuse-dev.ts +2 -1
- package/bin/apifuse-pack-types.ts +49 -0
- package/bin/apifuse-perf.ts +41 -4
- package/bin/apifuse-record.ts +9 -2
- package/dist/cli/templates/provider/README.md.tpl +15 -3
- package/dist/config/loader.d.ts +7 -0
- package/dist/config/loader.js +43 -18
- package/dist/declaration-validation.d.ts +2 -1
- package/dist/declaration-validation.js +39 -0
- package/dist/define.d.ts +4 -4
- package/dist/define.js +25 -1
- package/dist/engine.d.ts +16 -0
- package/dist/engine.js +59 -3
- package/dist/error-resolution.js +13 -0
- package/dist/index.d.ts +7 -4
- package/dist/index.js +4 -2
- package/dist/lint.js +38 -17
- package/dist/provider.d.ts +3 -3
- package/dist/provider.js +2 -2
- package/dist/runtime/bounded-expiring-map.d.ts +21 -0
- package/dist/runtime/bounded-expiring-map.js +64 -0
- package/dist/runtime/browser.js +2 -1
- package/dist/runtime/cache.d.ts +2 -0
- package/dist/runtime/cache.js +11 -3
- 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 +65 -0
- package/dist/runtime/egress-lease.js +236 -0
- package/dist/runtime/env.js +2 -1
- package/dist/runtime/http.js +1 -0
- 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/proxy-retry-policy.d.ts +4 -1
- package/dist/runtime/proxy-retry-policy.js +11 -2
- package/dist/runtime/request-options.d.ts +7 -0
- package/dist/runtime/request-options.js +62 -22
- package/dist/runtime/request-telemetry.d.ts +7 -9
- package/dist/runtime/resolver-config.d.ts +7 -0
- package/dist/runtime/resolver-config.js +8 -0
- package/dist/runtime/resolver-public.d.ts +3 -1
- package/dist/runtime/resolver-public.js +1 -1
- package/dist/runtime/resolver-shared.d.ts +9 -0
- package/dist/runtime/resolver-shared.js +57 -0
- package/dist/runtime/resolver-telemetry.d.ts +134 -0
- package/dist/runtime/resolver-telemetry.js +204 -0
- package/dist/runtime/resolver-usage.d.ts +24 -0
- package/dist/runtime/resolver-usage.js +60 -0
- package/dist/runtime/resolver-vendors/bindings.d.ts +5 -0
- package/dist/runtime/resolver-vendors/bindings.js +5 -0
- package/dist/runtime/resolver-vendors/browser.js +0 -1
- package/dist/runtime/resolver-vendors/capsolver.d.ts +2 -2
- package/dist/runtime/resolver-vendors/capsolver.js +21 -9
- package/dist/runtime/resolver-vendors/hypersolutions.d.ts +27 -0
- package/dist/runtime/resolver-vendors/hypersolutions.js +369 -0
- package/dist/runtime/resolver-vendors/twocaptcha.d.ts +2 -2
- package/dist/runtime/resolver-vendors/twocaptcha.js +26 -10
- package/dist/runtime/resolver-vendors/types.d.ts +35 -5
- package/dist/runtime/resolver-vendors/types.js +20 -10
- package/dist/runtime/resolver.d.ts +5 -1
- package/dist/runtime/resolver.js +400 -111
- package/dist/runtime/stealth-akamai-sbsd.d.ts +54 -0
- package/dist/runtime/stealth-akamai-sbsd.js +142 -0
- package/dist/runtime/stealth.js +547 -53
- 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 +20 -0
- package/dist/schema.js +72 -2
- package/dist/server/index.d.ts +2 -0
- package/dist/server/index.js +1 -0
- package/dist/server/self-test-redaction.js +8 -33
- package/dist/server/self-test.d.ts +4 -0
- package/dist/server/self-test.js +10 -1
- package/dist/server/sensitive-values.d.ts +26 -0
- package/dist/server/sensitive-values.js +131 -0
- package/dist/server/serve-implementation.d.ts +7 -1
- package/dist/server/serve-implementation.js +394 -125
- package/dist/server/trace-output.d.ts +2 -1
- package/dist/server/trace-output.js +46 -14
- package/dist/server/types.d.ts +6 -0
- package/dist/server/types.js +14 -0
- package/dist/stealth/profiles.d.ts +7 -1
- package/dist/stealth/profiles.js +9 -0
- package/dist/testing/run.js +6 -0
- package/dist/trace-sanitization.d.ts +4 -3
- package/dist/trace-sanitization.js +45 -22
- package/dist/types.d.ts +106 -10
- package/package.json +2 -1
- package/src/cli/templates/provider/README.md.tpl +15 -3
- package/src/config/loader.ts +47 -18
- package/src/declaration-validation.ts +47 -1
- package/src/define.ts +45 -3
- package/src/engine.ts +78 -3
- package/src/error-resolution.ts +13 -0
- package/src/index.ts +34 -1
- package/src/lint.ts +44 -18
- package/src/provider.ts +10 -0
- package/src/runtime/bounded-expiring-map.ts +67 -0
- package/src/runtime/browser.ts +2 -1
- package/src/runtime/cache.ts +13 -3
- 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 +341 -0
- package/src/runtime/env.ts +2 -1
- package/src/runtime/http.ts +1 -0
- 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/proxy-retry-policy.ts +12 -1
- package/src/runtime/request-options.ts +76 -28
- package/src/runtime/request-telemetry.ts +5 -7
- package/src/runtime/resolver-config.ts +12 -0
- package/src/runtime/resolver-public.ts +21 -0
- package/src/runtime/resolver-shared.ts +75 -0
- package/src/runtime/resolver-telemetry.ts +362 -0
- package/src/runtime/resolver-usage.ts +96 -0
- package/src/runtime/resolver-vendors/bindings.ts +5 -0
- package/src/runtime/resolver-vendors/browser.ts +0 -1
- package/src/runtime/resolver-vendors/capsolver.ts +22 -3
- package/src/runtime/resolver-vendors/hypersolutions.ts +480 -0
- package/src/runtime/resolver-vendors/twocaptcha.ts +26 -12
- package/src/runtime/resolver-vendors/types.ts +60 -12
- package/src/runtime/resolver.ts +468 -126
- package/src/runtime/stealth-akamai-sbsd.ts +207 -0
- package/src/runtime/stealth.ts +727 -95
- package/src/runtime/stt.ts +3 -2
- package/src/runtime/trace.ts +168 -26
- package/src/schema.ts +80 -2
- package/src/server/index.ts +21 -0
- package/src/server/self-test-redaction.ts +10 -40
- package/src/server/self-test.ts +13 -0
- package/src/server/sensitive-values.ts +200 -0
- package/src/server/serve-implementation.ts +561 -136
- package/src/server/trace-output.ts +77 -17
- package/src/server/types.ts +14 -0
- package/src/stealth/profiles.ts +10 -0
- package/src/testing/run.ts +6 -0
- package/src/trace-sanitization.ts +70 -23
- package/src/types.ts +124 -19
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { STATUS_CODES } from "node:http";
|
|
2
|
+
import { readDiagnosticEnv, withDiagnosticEnv } from "../runtime/diagnostic-env.js";
|
|
2
3
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
4
|
+
import { createHash } from "node:crypto";
|
|
3
5
|
import { existsSync } from "node:fs";
|
|
4
6
|
import { createRequire } from "node:module";
|
|
5
7
|
import { join } from "node:path";
|
|
@@ -7,41 +9,45 @@ import { Hono } from "hono";
|
|
|
7
9
|
import { z } from "zod";
|
|
8
10
|
import { AuthAbortError, createAuthFlowHelpers } from "../auth.js";
|
|
9
11
|
import { validateFailClosedDeclaration } from "../declaration-validation.js";
|
|
10
|
-
import { safeProviderErrorObservability } from "../error-observability.js";
|
|
11
12
|
import { createInProcessProviderEngine, ENGINE_OWNED_PROXY_CREDENTIAL_ENV_NAMES, isEngineOwnedEnvName, readEngineProxyCredentials, } from "../engine.js";
|
|
13
|
+
import { safeProviderErrorObservability } from "../error-observability.js";
|
|
12
14
|
import { SDK_OWNED_PROVIDER_ERROR_CODES, SDK_RUNTIME_OWNED_ERROR_CODES, SDK_STATUS_MAPPED_PROVIDER_ERROR_CODES, } from "../error-resolution.js";
|
|
13
|
-
import { AuthError, isProviderError, isSessionExpiredError, isTransportError, isValidationError, ProviderError, } from "../errors.js";
|
|
15
|
+
import { AuthError, isProviderError, isSessionExpiredError, isTransportError, isValidationError, ProviderError, SDKError, } from "../errors.js";
|
|
14
16
|
import { sanitizeDiagnosticText } from "../fixture-sanitization.js";
|
|
15
17
|
import { loadProviderLocaleCatalogs, localizeAuthTurn, } from "../i18n/catalog.js";
|
|
16
|
-
import { categoryForStatus,
|
|
18
|
+
import { categoryForStatus, isRetryableCategory, PROVIDER_OBSERVABILITY_TAXONOMY_VERSION, sourceForCategory, } from "../observability.js";
|
|
17
19
|
import { createScratchpad } from "../runtime/auth-flow.js";
|
|
18
20
|
import { createProviderCache } from "../runtime/cache.js";
|
|
19
21
|
import { createProviderChoiceContext, PROVIDER_RUNTIME_CHOICE_TOKEN_MASTER_SECRET_ENV, } from "../runtime/choice.js";
|
|
20
22
|
import { createCredentialContext } from "../runtime/credential.js";
|
|
23
|
+
import { bindDiagnosticSensitiveRegistry, compileProcessDiagnosticSensitiveValues, createDiagnosticRedactor, REDACTION_FAILED, isDiagnosticRedactionSuppressed, bindCriticalDiagnosticRedactor, redactCriticalDiagnosticText, redactDiagnosticText, registerDiagnosticSensitiveValues, } from "../runtime/diagnostic-redactor.js";
|
|
24
|
+
import { createCeremonyEgressLeaseRuntime, ENGINE_CEREMONY_EGRESS_LEASE, } from "../runtime/egress-lease.js";
|
|
21
25
|
import { createEnvContext } from "../runtime/env.js";
|
|
22
26
|
import { executeOperation } from "../runtime/executor.js";
|
|
23
27
|
import { createHttpClient } from "../runtime/http.js";
|
|
24
28
|
import { wrapWithInstrumentation } from "../runtime/instrumentation.js";
|
|
25
|
-
import { getProviderBaseUrl } from "../runtime/provider.js";
|
|
26
29
|
import { createOcrClientFromEnv } from "../runtime/ocr.js";
|
|
30
|
+
import { getProviderBaseUrl } from "../runtime/provider.js";
|
|
27
31
|
import { PROXY_AUTH_IP_DENIED_CODE, PROXY_EDGE_AUTH_REJECTED_CODE, PROXY_POOL_EXHAUSTED_CODE, } from "../runtime/proxy-errors.js";
|
|
28
32
|
import { PROVIDER_TELEMETRY_HEADER, ProxyTelemetryCollector, } from "../runtime/proxy-telemetry.js";
|
|
29
33
|
import { closedEnum, RequestTelemetry, tenantOpaqueKeys, } from "../runtime/request-telemetry.js";
|
|
30
|
-
import { createUnsupportedResolverClient, } from "../runtime/resolver-shared.js";
|
|
34
|
+
import { bindResolverTelemetry, createUnsupportedResolverClient, inheritResolverTelemetryBinding, } from "../runtime/resolver-shared.js";
|
|
35
|
+
import { ResolverTelemetryCollector, } from "../runtime/resolver-telemetry.js";
|
|
31
36
|
import { assertRequiredSecretsPresent, listMissingRequiredSecrets, MISSING_SECRET_CODE, } from "../runtime/secrets.js";
|
|
32
37
|
import { createProviderRuntimeStateFromEnv, createUnsupportedProviderRuntimeState, } from "../runtime/state.js";
|
|
33
38
|
import { StealthCookieJar } from "../runtime/stealth-cookies.js";
|
|
34
39
|
import { createSttClientFromEnv } from "../runtime/stt.js";
|
|
35
|
-
import { createTraceContext, updateTraceContextExportMetadata, } from "../runtime/trace.js";
|
|
40
|
+
import { createTraceContext, getTraceRecorder, updateTraceContextExportMetadata, } from "../runtime/trace.js";
|
|
36
41
|
import { resolveTraceConfigFromEnv } from "../runtime/trace-config.js";
|
|
37
42
|
import { parseSchema } from "../schema.js";
|
|
38
|
-
import { STATEFUL_NONCE_HEADER as STATEFUL_FORWARDING_NONCE_HEADER, STATEFUL_SIGNATURE_HEADER as STATEFUL_FORWARDING_SIGNATURE_HEADER, STATEFUL_TIMESTAMP_HEADER as STATEFUL_FORWARDING_TIMESTAMP_HEADER, verifyStatefulRequestSignature, } from "../stateful-signing.js";
|
|
39
43
|
import { StatefulRoutingDeadlineError } from "../stateful/errors.js";
|
|
44
|
+
import { STATEFUL_NONCE_HEADER as STATEFUL_FORWARDING_NONCE_HEADER, STATEFUL_SIGNATURE_HEADER as STATEFUL_FORWARDING_SIGNATURE_HEADER, STATEFUL_TIMESTAMP_HEADER as STATEFUL_FORWARDING_TIMESTAMP_HEADER, verifyStatefulRequestSignature, } from "../stateful-signing.js";
|
|
40
45
|
import { getStealthProfile } from "../stealth/profiles.js";
|
|
41
|
-
import { sanitizeTraceAttributes } from "../trace-sanitization.js";
|
|
42
46
|
import { APIFUSE_STREAM_DONE_EVENT, APIFUSE_STREAM_ERROR_EVENT, encodeSseEvent, error as streamError, } from "../stream.js";
|
|
47
|
+
import { sanitizeSpanNameForOutput, sanitizeTraceAttributes } from "../trace-sanitization.js";
|
|
43
48
|
import { VALID_OPERATION_ERROR_STATUSES } from "../types.js";
|
|
44
49
|
import { resolveSelfTestMasterSecrets } from "./self-test-token.js";
|
|
50
|
+
import { collectStaticDiagnosticSensitiveValues, createDiagnosticEnvObserver, } from "./sensitive-values.js";
|
|
45
51
|
import { resolveServerTraceContextOptions } from "./trace-output.js";
|
|
46
52
|
import { AuthFlowRequestSchema, OperationConnectionSchema, OperationRequestSchema, } from "./types.js";
|
|
47
53
|
const DEFAULT_HOST = "0.0.0.0";
|
|
@@ -268,6 +274,9 @@ function createLazyStealthClient(onCleanupError, ...createArgs) {
|
|
|
268
274
|
async fetch(...args) {
|
|
269
275
|
return (await getSession()).fetch(...args);
|
|
270
276
|
},
|
|
277
|
+
async replayChallenged(...args) {
|
|
278
|
+
return (await getSession()).replayChallenged(...args);
|
|
279
|
+
},
|
|
271
280
|
redirects: {
|
|
272
281
|
async run(...args) {
|
|
273
282
|
return (await getSession()).redirects.run(...args);
|
|
@@ -304,13 +313,15 @@ function createLazyStealthClient(onCleanupError, ...createArgs) {
|
|
|
304
313
|
function bindResolverSignalWithoutRuntime(resolver, defaultSignal) {
|
|
305
314
|
if (!defaultSignal)
|
|
306
315
|
return resolver;
|
|
307
|
-
|
|
316
|
+
const boundResolver = {
|
|
308
317
|
solve(challenge, signal = defaultSignal, traceRecorder) {
|
|
309
318
|
// Forward the instrumentation trace recorder so resolver.vendor.* spans
|
|
310
319
|
// survive this wrapper. See ResolverSolveWithRecorder in resolver-shared.ts.
|
|
311
320
|
return resolver.solve(challenge, signal, traceRecorder);
|
|
312
321
|
},
|
|
313
322
|
};
|
|
323
|
+
inheritResolverTelemetryBinding(resolver, boundResolver);
|
|
324
|
+
return boundResolver;
|
|
314
325
|
}
|
|
315
326
|
function getProviderStealthBaseUrl(provider) {
|
|
316
327
|
const baseUrl = getProviderBaseUrl(provider);
|
|
@@ -323,6 +334,73 @@ function getProviderStealthBaseUrl(provider) {
|
|
|
323
334
|
function getProviderStealthProfile(provider) {
|
|
324
335
|
return provider.stealth ? getStealthProfile(provider.stealth) : undefined;
|
|
325
336
|
}
|
|
337
|
+
/** One option set for ctx.resolver and the automatic SBSD solve, so both share identity scope, cache, proxy intent, and telemetry. */
|
|
338
|
+
function createResolverRuntimeOptions(provider, cache, identityScope, proxyPolicy, proxyClientOptions, stealthProfile, telemetry) {
|
|
339
|
+
return {
|
|
340
|
+
allowedHosts: provider.allowedHosts,
|
|
341
|
+
cache,
|
|
342
|
+
telemetry,
|
|
343
|
+
identityScope,
|
|
344
|
+
...(proxyPolicy
|
|
345
|
+
? {
|
|
346
|
+
proxyIntent: {
|
|
347
|
+
mode: proxyPolicy.mode,
|
|
348
|
+
...proxyClientOptions,
|
|
349
|
+
...(stealthProfile ? { userAgent: stealthProfile.userAgent } : {}),
|
|
350
|
+
},
|
|
351
|
+
}
|
|
352
|
+
: {}),
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
/** Shared predicate for the SBSD challenge runtime and the ceremony egress lease. */
|
|
356
|
+
function declaresAkamaiSbsdResolver(provider) {
|
|
357
|
+
return provider.resolver?.kinds.some((kind) => kind === "akamai_sbsd") === true;
|
|
358
|
+
}
|
|
359
|
+
function createStealthChallengeDetection(provider, resolverRuntime, resolverOverride, resolverOptions, trace, signal) {
|
|
360
|
+
const resolverDeclared = declaresAkamaiSbsdResolver(provider);
|
|
361
|
+
const detectOnly = provider.stealth?.challengeDetection?.akamaiSbsd === true;
|
|
362
|
+
if (!resolverDeclared && !detectOnly) {
|
|
363
|
+
return undefined;
|
|
364
|
+
}
|
|
365
|
+
return {
|
|
366
|
+
akamaiSbsd: {
|
|
367
|
+
allowedHosts: provider.allowedHosts ?? [],
|
|
368
|
+
// The profile belongs to the SBSD resolver; a resolver declared for other kinds
|
|
369
|
+
// may name a different browser without affecting detect-only classification.
|
|
370
|
+
...(resolverDeclared && provider.resolver?.clientProfile
|
|
371
|
+
? { clientProfile: provider.resolver.clientProfile }
|
|
372
|
+
: {}),
|
|
373
|
+
...(resolverDeclared && resolverRuntime
|
|
374
|
+
? {
|
|
375
|
+
async solve(challenge, transport, solveSignal) {
|
|
376
|
+
const resolver = resolverRuntime.bindResolverSignal((resolverOverride
|
|
377
|
+
? bindResolverTelemetry(resolverOverride, resolverOptions.telemetry)
|
|
378
|
+
: undefined) ??
|
|
379
|
+
resolverRuntime.createResolverClientFromEnv(provider.resolver, undefined, {
|
|
380
|
+
allowedHosts: resolverOptions.allowedHosts,
|
|
381
|
+
cache: resolverOptions.cache,
|
|
382
|
+
telemetry: resolverOptions.telemetry,
|
|
383
|
+
identityScope: resolverOptions.identityScope,
|
|
384
|
+
// No proxyIntent: the transport is already bound to the initiating
|
|
385
|
+
// request's lease, and a second identity resolution would allocate
|
|
386
|
+
// again (and fail closed without allocator credentials) for nothing
|
|
387
|
+
// the SBSD adapters consume.
|
|
388
|
+
createTransport: () => transport,
|
|
389
|
+
}), signal);
|
|
390
|
+
// Same span and recorder threading as the instrumented ctx.resolver.solve.
|
|
391
|
+
const recorder = getTraceRecorder(trace);
|
|
392
|
+
const solve = () => resolver.solve(challenge, solveSignal, recorder ?? undefined);
|
|
393
|
+
return recorder
|
|
394
|
+
? recorder.runSpan("resolver.solve", solve, {
|
|
395
|
+
attributes: { challenge_kind: challenge.kind },
|
|
396
|
+
})
|
|
397
|
+
: solve();
|
|
398
|
+
},
|
|
399
|
+
}
|
|
400
|
+
: {}),
|
|
401
|
+
},
|
|
402
|
+
};
|
|
403
|
+
}
|
|
326
404
|
function isProductionProviderBrowserMode(provider, env = process.env) {
|
|
327
405
|
if (provider.runtime !== "browser") {
|
|
328
406
|
return false;
|
|
@@ -424,17 +502,29 @@ function createProviderContext(provider, request, operationId, options, state =
|
|
|
424
502
|
engineCredentials: engineProxyCredentials,
|
|
425
503
|
};
|
|
426
504
|
const resolverIdentityScope = resolveProviderResolverIdentityScope(provider, proxyClientOptions.affinityKey, request.requestId);
|
|
505
|
+
const cache = createProviderCache({ providerId: provider.id });
|
|
427
506
|
let wrappedContext;
|
|
507
|
+
const { capabilityModules } = options;
|
|
508
|
+
const resolverOptions = createResolverRuntimeOptions(provider, cache, resolverIdentityScope, proxyPolicy, proxyClientOptions, stealthProfile, scope.resolverTelemetry);
|
|
509
|
+
const challengeRuntime = createStealthChallengeDetection(provider, capabilityModules.resolver, options.resolver, resolverOptions, scope.trace, signal);
|
|
428
510
|
const stealthClientOptions = {
|
|
429
511
|
upstream: proxyClientOptions.upstream,
|
|
430
512
|
affinityKey: proxyClientOptions.affinityKey,
|
|
431
513
|
telemetry: scope.telemetry.proxy,
|
|
432
514
|
engineCredentials: engineProxyCredentials,
|
|
433
515
|
...(signal ? { signal } : {}),
|
|
434
|
-
|
|
516
|
+
// A declared resolver without a `stealth` block still gets the default Chrome client
|
|
517
|
+
// (auth FlowContext is not capability-gated), so the runtime must ride along.
|
|
518
|
+
...(provider.stealth || challengeRuntime
|
|
519
|
+
? {
|
|
520
|
+
stealth: {
|
|
521
|
+
...(provider.stealth ?? {}),
|
|
522
|
+
...(challengeRuntime ? { challengeRuntime } : {}),
|
|
523
|
+
},
|
|
524
|
+
}
|
|
525
|
+
: {}),
|
|
435
526
|
};
|
|
436
|
-
const
|
|
437
|
-
const logStealthCleanupError = (error) => logProviderCleanupError(options.logger, provider, "operation", operationId, request.requestId, "stealth", error);
|
|
527
|
+
const logStealthCleanupError = (error) => logProviderCleanupError(options.logger, provider, "operation", operationId, request.requestId, "stealth", error, scope.redact);
|
|
438
528
|
const env = createEnvContext([
|
|
439
529
|
...providerSecretNames(provider),
|
|
440
530
|
PROVIDER_RUNTIME_CHOICE_TOKEN_MASTER_SECRET_ENV,
|
|
@@ -450,7 +540,6 @@ function createProviderContext(provider, request, operationId, options, state =
|
|
|
450
540
|
headers: request.headers ?? {},
|
|
451
541
|
};
|
|
452
542
|
const requestState = state.forConnection(requestContext.connectionId);
|
|
453
|
-
const cache = createProviderCache({ providerId: provider.id });
|
|
454
543
|
const bindings = {
|
|
455
544
|
env,
|
|
456
545
|
credential,
|
|
@@ -475,7 +564,7 @@ function createProviderContext(provider, request, operationId, options, state =
|
|
|
475
564
|
browser: provider.runtime === "browser"
|
|
476
565
|
? capabilityModules.browser.createBrowserClient({
|
|
477
566
|
allowedHosts: provider.allowedHosts,
|
|
478
|
-
cdpUrl:
|
|
567
|
+
cdpUrl: readDiagnosticEnv("APIFUSE__CDP_POOL__URL"),
|
|
479
568
|
headless: true,
|
|
480
569
|
requireCdpPool: isProductionProviderBrowserMode(provider),
|
|
481
570
|
stealth: true,
|
|
@@ -499,22 +588,13 @@ function createProviderContext(provider, request, operationId, options, state =
|
|
|
499
588
|
ocr: options.ocr ?? createOcrClientFromEnv(provider.ocr),
|
|
500
589
|
stt: options.stt ?? createSttClientFromEnv(provider.stt),
|
|
501
590
|
resolver: capabilityModules.resolver
|
|
502
|
-
? capabilityModules.resolver.bindResolverSignal(options.resolver
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
proxyIntent: {
|
|
510
|
-
mode: proxyPolicy.mode,
|
|
511
|
-
...proxyClientOptions,
|
|
512
|
-
...(stealthProfile ? { userAgent: stealthProfile.userAgent } : {}),
|
|
513
|
-
},
|
|
514
|
-
}
|
|
515
|
-
: {}),
|
|
516
|
-
}), signal)
|
|
517
|
-
: bindResolverSignalWithoutRuntime(options.resolver ??
|
|
591
|
+
? capabilityModules.resolver.bindResolverSignal((options.resolver
|
|
592
|
+
? bindResolverTelemetry(options.resolver, resolverOptions.telemetry)
|
|
593
|
+
: undefined) ??
|
|
594
|
+
capabilityModules.resolver.createResolverClientFromEnv(provider.resolver, undefined, resolverOptions), signal)
|
|
595
|
+
: bindResolverSignalWithoutRuntime((options.resolver
|
|
596
|
+
? bindResolverTelemetry(options.resolver, resolverOptions.telemetry)
|
|
597
|
+
: undefined) ??
|
|
518
598
|
createUnsupportedResolverClient("Provider does not declare resolver capability"), signal),
|
|
519
599
|
choice: createProviderChoiceContext({
|
|
520
600
|
providerId: provider.id,
|
|
@@ -564,7 +644,7 @@ export function resolveAuthFlowProxyAffinityKey(provider, request) {
|
|
|
564
644
|
request.providerId ??
|
|
565
645
|
provider.id);
|
|
566
646
|
}
|
|
567
|
-
function createAuthFlowContext(provider, request, options, state, scope, signal) {
|
|
647
|
+
function createAuthFlowContext(provider, request, route, options, state, scope, signal) {
|
|
568
648
|
const baseUrl = getProviderBaseUrl(provider);
|
|
569
649
|
const stealthBaseUrl = getProviderStealthBaseUrl(provider);
|
|
570
650
|
const stealthProfile = getProviderStealthProfile(provider);
|
|
@@ -578,17 +658,51 @@ function createAuthFlowContext(provider, request, options, state, scope, signal)
|
|
|
578
658
|
telemetry: scope.telemetry.proxy,
|
|
579
659
|
engineCredentials: engineProxyCredentials,
|
|
580
660
|
};
|
|
661
|
+
// The lease pins the proxy egress a challenged ceremony solved on, so only providers
|
|
662
|
+
// that declare the akamai_sbsd resolver kind carry one (same predicate as the challenge
|
|
663
|
+
// runtime). `abort` never evaluates the key or tenant: a user must be able to cancel a
|
|
664
|
+
// stranded flow even when engine provisioning is broken, and an abort is not a
|
|
665
|
+
// continuation of the ceremony the lease pins.
|
|
666
|
+
const ceremonyEgressLease = route !== "abort" &&
|
|
667
|
+
declaresAkamaiSbsdResolver(provider) &&
|
|
668
|
+
proxyPolicy &&
|
|
669
|
+
proxyPolicy.mode !== "disabled"
|
|
670
|
+
? createCeremonyEgressLeaseRuntime({
|
|
671
|
+
tenantId: request.tenantId,
|
|
672
|
+
providerId: provider.id,
|
|
673
|
+
flowId: request.flowId,
|
|
674
|
+
affinityKey: proxyClientOptions.affinityKey,
|
|
675
|
+
onHandle: (handle) => registerDiagnosticSensitiveValues(scope.trace, [handle]),
|
|
676
|
+
...(request.engine?.egressLease ? { handle: request.engine.egressLease } : {}),
|
|
677
|
+
})
|
|
678
|
+
: undefined;
|
|
679
|
+
if (route !== "abort" && request.engine?.egressLease && !ceremonyEgressLease) {
|
|
680
|
+
throw new SDKError("An egress lease was supplied for an auth flow that does not use a ceremony lease", { code: "EGRESS_LEASE_INVALID" });
|
|
681
|
+
}
|
|
581
682
|
const resolverIdentityScope = resolveProviderResolverIdentityScope(provider, proxyClientOptions.affinityKey, request.requestId);
|
|
683
|
+
const cache = createProviderCache({ providerId: provider.id });
|
|
684
|
+
const { capabilityModules } = options;
|
|
685
|
+
const resolverOptions = createResolverRuntimeOptions(provider, cache, resolverIdentityScope, proxyPolicy, proxyClientOptions, stealthProfile, scope.resolverTelemetry);
|
|
686
|
+
const challengeRuntime = createStealthChallengeDetection(provider, capabilityModules.resolver, options.resolver, resolverOptions, scope.trace, signal);
|
|
582
687
|
const stealthClientOptions = {
|
|
583
688
|
upstream: proxyClientOptions.upstream,
|
|
584
689
|
affinityKey: proxyClientOptions.affinityKey,
|
|
585
690
|
telemetry: scope.telemetry.proxy,
|
|
586
691
|
engineCredentials: engineProxyCredentials,
|
|
692
|
+
...(ceremonyEgressLease ? { [ENGINE_CEREMONY_EGRESS_LEASE]: ceremonyEgressLease } : {}),
|
|
587
693
|
...(signal ? { signal } : {}),
|
|
588
|
-
|
|
694
|
+
// A declared resolver without a `stealth` block still gets the default Chrome client
|
|
695
|
+
// (auth FlowContext is not capability-gated), so the runtime must ride along.
|
|
696
|
+
...(provider.stealth || challengeRuntime
|
|
697
|
+
? {
|
|
698
|
+
stealth: {
|
|
699
|
+
...(provider.stealth ?? {}),
|
|
700
|
+
...(challengeRuntime ? { challengeRuntime } : {}),
|
|
701
|
+
},
|
|
702
|
+
}
|
|
703
|
+
: {}),
|
|
589
704
|
};
|
|
590
|
-
const
|
|
591
|
-
const logStealthCleanupError = (error) => logProviderCleanupError(options.logger, provider, "auth", "flow", request.requestId, "stealth", error);
|
|
705
|
+
const logStealthCleanupError = (error) => logProviderCleanupError(options.logger, provider, "auth", "flow", request.requestId, "stealth", error, scope.redact);
|
|
592
706
|
const credential = request.connection
|
|
593
707
|
? createCredentialContext({
|
|
594
708
|
allowedKeys: provider.credential?.keys,
|
|
@@ -597,7 +711,6 @@ function createAuthFlowContext(provider, request, options, state, scope, signal)
|
|
|
597
711
|
values: request.connection.secrets,
|
|
598
712
|
})
|
|
599
713
|
: undefined;
|
|
600
|
-
const cache = createProviderCache({ providerId: provider.id });
|
|
601
714
|
const context = wrapWithInstrumentation({
|
|
602
715
|
flowId: request.flowId,
|
|
603
716
|
connectionId: resolveOperationConnectionId(request),
|
|
@@ -636,28 +749,23 @@ function createAuthFlowContext(provider, request, options, state, scope, signal)
|
|
|
636
749
|
ocr: options.ocr ?? createOcrClientFromEnv(provider.ocr),
|
|
637
750
|
stt: options.stt ?? createSttClientFromEnv(provider.stt),
|
|
638
751
|
resolver: capabilityModules.resolver
|
|
639
|
-
? capabilityModules.resolver.bindResolverSignal(options.resolver
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
proxyIntent: {
|
|
647
|
-
mode: proxyPolicy.mode,
|
|
648
|
-
...proxyClientOptions,
|
|
649
|
-
...(stealthProfile ? { userAgent: stealthProfile.userAgent } : {}),
|
|
650
|
-
},
|
|
651
|
-
}
|
|
652
|
-
: {}),
|
|
653
|
-
}), signal)
|
|
654
|
-
: bindResolverSignalWithoutRuntime(options.resolver ??
|
|
752
|
+
? capabilityModules.resolver.bindResolverSignal((options.resolver
|
|
753
|
+
? bindResolverTelemetry(options.resolver, resolverOptions.telemetry)
|
|
754
|
+
: undefined) ??
|
|
755
|
+
capabilityModules.resolver.createResolverClientFromEnv(provider.resolver, undefined, resolverOptions), signal)
|
|
756
|
+
: bindResolverSignalWithoutRuntime((options.resolver
|
|
757
|
+
? bindResolverTelemetry(options.resolver, resolverOptions.telemetry)
|
|
758
|
+
: undefined) ??
|
|
655
759
|
createUnsupportedResolverClient("Provider does not declare resolver capability"), signal),
|
|
656
760
|
auth: createAuthFlowHelpers({ signal }),
|
|
657
761
|
});
|
|
658
762
|
return {
|
|
659
763
|
context,
|
|
660
764
|
getPatch: flowContextStore.getPatch,
|
|
765
|
+
getEngineState() {
|
|
766
|
+
const egressLease = ceremonyEgressLease?.handle();
|
|
767
|
+
return egressLease ? { egressLease } : undefined;
|
|
768
|
+
},
|
|
661
769
|
};
|
|
662
770
|
}
|
|
663
771
|
const defaultProviderServerLogger = (event) => {
|
|
@@ -683,11 +791,11 @@ function finishRequestCost(input) {
|
|
|
683
791
|
cpuTotalMicros: Math.max(0, cpuDelta.user + cpuDelta.system),
|
|
684
792
|
};
|
|
685
793
|
}
|
|
686
|
-
function zodDetails(error) {
|
|
794
|
+
function zodDetails(error, redact) {
|
|
687
795
|
return error.issues.map((issue) => ({
|
|
688
|
-
path: issue.path.join("."),
|
|
689
|
-
code: issue.code,
|
|
690
|
-
message: issue.message,
|
|
796
|
+
path: sanitizeSpanNameForOutput(issue.path.join("."), redact),
|
|
797
|
+
code: sanitizeSpanNameForOutput(issue.code, redact),
|
|
798
|
+
message: sanitizeSpanNameForOutput(issue.message, redact),
|
|
691
799
|
}));
|
|
692
800
|
}
|
|
693
801
|
// Category-level projection with code-aware honesty overrides: a missing
|
|
@@ -710,7 +818,7 @@ function publicErrorSource(error, category) {
|
|
|
710
818
|
}
|
|
711
819
|
return sourceForCategory(category);
|
|
712
820
|
}
|
|
713
|
-
function toErrorResponse(error, requestId, observabilityDetails) {
|
|
821
|
+
function toErrorResponse(error, requestId, observabilityDetails, redact) {
|
|
714
822
|
const observability = observabilityDetails;
|
|
715
823
|
const source = publicErrorSource(error, observability.category);
|
|
716
824
|
if (error instanceof StatefulRoutingDeadlineError) {
|
|
@@ -746,7 +854,7 @@ function toErrorResponse(error, requestId, observabilityDetails) {
|
|
|
746
854
|
...(requestId ? { requestId } : {}),
|
|
747
855
|
retryable: observability.retryable,
|
|
748
856
|
source,
|
|
749
|
-
details: zodDetails(error),
|
|
857
|
+
details: zodDetails(error, redact),
|
|
750
858
|
},
|
|
751
859
|
};
|
|
752
860
|
}
|
|
@@ -994,13 +1102,13 @@ function extractRequestId(raw) {
|
|
|
994
1102
|
}
|
|
995
1103
|
const MAX_PROVIDER_ERROR_CAUSE_FRAMES = 5;
|
|
996
1104
|
const MAX_PROVIDER_ERROR_CAUSE_MESSAGE_LENGTH = 300;
|
|
997
|
-
function providerErrorCauseMessage(message) {
|
|
998
|
-
const sanitized = sanitizeDiagnosticText(message);
|
|
1105
|
+
function providerErrorCauseMessage(message, redact) {
|
|
1106
|
+
const sanitized = sanitizeDiagnosticText(redactDiagnosticText(message, redact));
|
|
999
1107
|
return sanitized.length > MAX_PROVIDER_ERROR_CAUSE_MESSAGE_LENGTH
|
|
1000
1108
|
? `${sanitized.slice(0, MAX_PROVIDER_ERROR_CAUSE_MESSAGE_LENGTH)}… [truncated]`
|
|
1001
1109
|
: sanitized;
|
|
1002
1110
|
}
|
|
1003
|
-
function providerErrorCauseChain(error) {
|
|
1111
|
+
function providerErrorCauseChain(error, redact) {
|
|
1004
1112
|
if (!(error instanceof Error) && !isProviderError(error))
|
|
1005
1113
|
return undefined;
|
|
1006
1114
|
const seen = new Set([error]);
|
|
@@ -1014,9 +1122,9 @@ function providerErrorCauseChain(error) {
|
|
|
1014
1122
|
const providerObservability = safeProviderErrorObservability(cause);
|
|
1015
1123
|
const causeCode = providerErrorCode(cause);
|
|
1016
1124
|
frames.push({
|
|
1017
|
-
errorClass: cause.name,
|
|
1018
|
-
...(causeCode !== undefined ? { code: causeCode } : {}),
|
|
1019
|
-
message: providerErrorCauseMessage(message),
|
|
1125
|
+
errorClass: redactDiagnosticText(cause.name, redact),
|
|
1126
|
+
...(causeCode !== undefined ? { code: redactDiagnosticText(causeCode, redact) } : {}),
|
|
1127
|
+
message: providerErrorCauseMessage(message, redact),
|
|
1020
1128
|
messageLength: message.length,
|
|
1021
1129
|
messageFingerprint: createHash("sha256").update(message).digest("hex").slice(0, 12),
|
|
1022
1130
|
...(providerObservability ? { providerObservability } : {}),
|
|
@@ -1025,7 +1133,7 @@ function providerErrorCauseChain(error) {
|
|
|
1025
1133
|
}
|
|
1026
1134
|
return frames.length > 0 ? frames : undefined;
|
|
1027
1135
|
}
|
|
1028
|
-
function logProviderError(logger, provider, kind, route, requestId, error, status, cost, declaredErrorCode, telemetry, observabilityDetails, correlation = {}) {
|
|
1136
|
+
function logProviderError(logger, provider, kind, route, requestId, error, status, cost, declaredErrorCode, telemetry, observabilityDetails, correlation = {}, redact) {
|
|
1029
1137
|
const providerCode = isProviderError(error) ? providerErrorCode(error) : undefined;
|
|
1030
1138
|
const code = isProviderError(error)
|
|
1031
1139
|
? (providerCode ?? "provider_error")
|
|
@@ -1036,7 +1144,7 @@ function logProviderError(logger, provider, kind, route, requestId, error, statu
|
|
|
1036
1144
|
: "internal_error";
|
|
1037
1145
|
const errorClass = error instanceof Error ? error.name : typeof error;
|
|
1038
1146
|
const message = error instanceof Error ? error.message : String(error);
|
|
1039
|
-
const causeChain = providerErrorCauseChain(error);
|
|
1147
|
+
const causeChain = providerErrorCauseChain(error, redact);
|
|
1040
1148
|
const details = observabilityDetails;
|
|
1041
1149
|
const isUnregisteredProviderErrorCode = status === 500 &&
|
|
1042
1150
|
isProviderError(error) &&
|
|
@@ -1055,24 +1163,28 @@ function logProviderError(logger, provider, kind, route, requestId, error, statu
|
|
|
1055
1163
|
emit({
|
|
1056
1164
|
level: status >= 500 ? "error" : "warn",
|
|
1057
1165
|
event: "provider_request_failed",
|
|
1058
|
-
providerId: provider.id,
|
|
1166
|
+
providerId: redactDiagnosticText(provider.id, redact),
|
|
1059
1167
|
kind,
|
|
1060
|
-
route,
|
|
1061
|
-
...(requestId ? { requestId } : {}),
|
|
1168
|
+
route: redactCriticalDiagnosticText(route, redact),
|
|
1169
|
+
...(requestId ? { requestId: redactCriticalDiagnosticText(requestId, redact) } : {}),
|
|
1062
1170
|
...(correlation.connectionId !== undefined
|
|
1063
|
-
? { connectionId: correlation.connectionId }
|
|
1171
|
+
? { connectionId: redactDiagnosticText(correlation.connectionId, redact) }
|
|
1172
|
+
: {}),
|
|
1173
|
+
...(correlation.flowId !== undefined
|
|
1174
|
+
? { flowId: redactDiagnosticText(correlation.flowId, redact) }
|
|
1175
|
+
: {}),
|
|
1176
|
+
...(correlation.tenantId !== undefined
|
|
1177
|
+
? { tenantId: redactDiagnosticText(correlation.tenantId, redact) }
|
|
1064
1178
|
: {}),
|
|
1065
|
-
...(correlation.flowId !== undefined ? { flowId: correlation.flowId } : {}),
|
|
1066
|
-
...(correlation.tenantId !== undefined ? { tenantId: correlation.tenantId } : {}),
|
|
1067
1179
|
...(correlation.requestedProviderId !== undefined
|
|
1068
|
-
? { requestedProviderId: correlation.requestedProviderId }
|
|
1180
|
+
? { requestedProviderId: redactDiagnosticText(correlation.requestedProviderId, redact) }
|
|
1069
1181
|
: {}),
|
|
1070
1182
|
status,
|
|
1071
1183
|
...cost,
|
|
1072
1184
|
...(telemetryPayload ?? {}),
|
|
1073
|
-
code,
|
|
1074
|
-
errorClass,
|
|
1075
|
-
message,
|
|
1185
|
+
code: sanitizeSpanNameForOutput(redactCriticalDiagnosticText(code, redact)),
|
|
1186
|
+
errorClass: redactDiagnosticText(errorClass, redact),
|
|
1187
|
+
message: sanitizeDiagnosticText(redactDiagnosticText(message, redact)),
|
|
1076
1188
|
...(causeChain ? { causeChain } : {}),
|
|
1077
1189
|
...(providerObservability ? { providerObservability } : {}),
|
|
1078
1190
|
...(details.upstreamStatus ? { upstreamStatus: details.upstreamStatus } : {}),
|
|
@@ -1085,42 +1197,46 @@ function logProviderError(logger, provider, kind, route, requestId, error, statu
|
|
|
1085
1197
|
signalFix: "Declare this code (with status and retryable) in the operation's errorCodes so it serves its intended status instead of 500.",
|
|
1086
1198
|
}
|
|
1087
1199
|
: {}),
|
|
1088
|
-
...(error instanceof z.ZodError ? { issues: zodDetails(error) } : {}),
|
|
1200
|
+
...(error instanceof z.ZodError ? { issues: zodDetails(error, redact) } : {}),
|
|
1089
1201
|
});
|
|
1090
1202
|
}
|
|
1091
|
-
function logProviderCleanupError(logger, provider, kind, operationId, requestId, resource, error) {
|
|
1203
|
+
function logProviderCleanupError(logger, provider, kind, operationId, requestId, resource, error, redact) {
|
|
1092
1204
|
const emit = typeof logger === "function" ? logger : defaultProviderServerLogger;
|
|
1093
1205
|
const errorClass = error instanceof Error ? error.name : typeof error;
|
|
1094
1206
|
const message = error instanceof Error ? error.message : String(error);
|
|
1095
1207
|
emit({
|
|
1096
1208
|
level: "warn",
|
|
1097
1209
|
event: "provider_cleanup_failed",
|
|
1098
|
-
providerId: provider.id,
|
|
1210
|
+
providerId: redactDiagnosticText(provider.id, redact),
|
|
1099
1211
|
kind,
|
|
1100
|
-
route: operationId,
|
|
1101
|
-
...(requestId ? { requestId } : {}),
|
|
1212
|
+
route: redactCriticalDiagnosticText(operationId, redact),
|
|
1213
|
+
...(requestId ? { requestId: redactCriticalDiagnosticText(requestId, redact) } : {}),
|
|
1102
1214
|
resource,
|
|
1103
|
-
errorClass,
|
|
1104
|
-
message,
|
|
1215
|
+
errorClass: redactDiagnosticText(errorClass, redact),
|
|
1216
|
+
message: sanitizeDiagnosticText(redactDiagnosticText(message, redact)),
|
|
1105
1217
|
});
|
|
1106
1218
|
}
|
|
1107
|
-
function logProviderSuccess(logger, provider, kind, route, requestId, status, cost, telemetry, correlation = {}) {
|
|
1219
|
+
function logProviderSuccess(logger, provider, kind, route, requestId, status, cost, telemetry, correlation = {}, redact) {
|
|
1108
1220
|
const telemetryPayload = telemetry?.toLogPayload();
|
|
1109
1221
|
const emit = typeof logger === "function" ? logger : defaultProviderServerLogger;
|
|
1110
1222
|
emit({
|
|
1111
1223
|
level: "info",
|
|
1112
1224
|
event: "provider_request_completed",
|
|
1113
|
-
providerId: provider.id,
|
|
1225
|
+
providerId: redactDiagnosticText(provider.id, redact),
|
|
1114
1226
|
kind,
|
|
1115
|
-
route,
|
|
1116
|
-
...(requestId ? { requestId } : {}),
|
|
1227
|
+
route: redactCriticalDiagnosticText(route, redact),
|
|
1228
|
+
...(requestId ? { requestId: redactCriticalDiagnosticText(requestId, redact) } : {}),
|
|
1117
1229
|
...(correlation.connectionId !== undefined
|
|
1118
|
-
? { connectionId: correlation.connectionId }
|
|
1230
|
+
? { connectionId: redactDiagnosticText(correlation.connectionId, redact) }
|
|
1231
|
+
: {}),
|
|
1232
|
+
...(correlation.flowId !== undefined
|
|
1233
|
+
? { flowId: redactDiagnosticText(correlation.flowId, redact) }
|
|
1234
|
+
: {}),
|
|
1235
|
+
...(correlation.tenantId !== undefined
|
|
1236
|
+
? { tenantId: redactDiagnosticText(correlation.tenantId, redact) }
|
|
1119
1237
|
: {}),
|
|
1120
|
-
...(correlation.flowId !== undefined ? { flowId: correlation.flowId } : {}),
|
|
1121
|
-
...(correlation.tenantId !== undefined ? { tenantId: correlation.tenantId } : {}),
|
|
1122
1238
|
...(correlation.requestedProviderId !== undefined
|
|
1123
|
-
? { requestedProviderId: correlation.requestedProviderId }
|
|
1239
|
+
? { requestedProviderId: redactDiagnosticText(correlation.requestedProviderId, redact) }
|
|
1124
1240
|
: {}),
|
|
1125
1241
|
status,
|
|
1126
1242
|
...cost,
|
|
@@ -1173,9 +1289,106 @@ function requestTraceId(headers, requestId) {
|
|
|
1173
1289
|
? createHash("sha256").update(requestId, "utf8").digest("hex").slice(0, 32)
|
|
1174
1290
|
: undefined));
|
|
1175
1291
|
}
|
|
1292
|
+
/** Harvest only credential subtrees, iteratively and before schema diagnostics. */
|
|
1293
|
+
function rawRequestCredentials(rawBody, kind) {
|
|
1294
|
+
const record = (value) => value !== null && typeof value === "object" ? value : undefined;
|
|
1295
|
+
const own = (value, key) => {
|
|
1296
|
+
const object = record(value);
|
|
1297
|
+
return object && Object.hasOwn(object, key) ? object[key] : undefined;
|
|
1298
|
+
};
|
|
1299
|
+
const body = kind === "stateful" ? own(rawBody, "operationRequest") : rawBody;
|
|
1300
|
+
const values = [];
|
|
1301
|
+
let remainingBytes = 64 * 1024;
|
|
1302
|
+
let exhausted = false;
|
|
1303
|
+
const add = (value) => {
|
|
1304
|
+
if (typeof value !== "string")
|
|
1305
|
+
return;
|
|
1306
|
+
const bytes = Buffer.byteLength(value, "utf8");
|
|
1307
|
+
if (bytes > remainingBytes) {
|
|
1308
|
+
exhausted = true;
|
|
1309
|
+
return;
|
|
1310
|
+
}
|
|
1311
|
+
remainingBytes -= bytes;
|
|
1312
|
+
if (value)
|
|
1313
|
+
values.push(value);
|
|
1314
|
+
const trimmed = value.trim();
|
|
1315
|
+
if (trimmed && trimmed !== value)
|
|
1316
|
+
values.push(trimmed);
|
|
1317
|
+
};
|
|
1318
|
+
function* entries(object) {
|
|
1319
|
+
for (const key in object) {
|
|
1320
|
+
if (Object.hasOwn(object, key))
|
|
1321
|
+
yield [key, object[key]];
|
|
1322
|
+
}
|
|
1323
|
+
}
|
|
1324
|
+
const visit = (root) => {
|
|
1325
|
+
const object = record(root);
|
|
1326
|
+
if (!object) {
|
|
1327
|
+
add(root);
|
|
1328
|
+
return;
|
|
1329
|
+
}
|
|
1330
|
+
// Connection inventories are usually flat. Walk the root directly and only
|
|
1331
|
+
// allocate iterator frames when a nested credential container is encountered.
|
|
1332
|
+
for (const key in object) {
|
|
1333
|
+
if (!Object.hasOwn(object, key))
|
|
1334
|
+
continue;
|
|
1335
|
+
const value = object[key];
|
|
1336
|
+
add(key);
|
|
1337
|
+
if (exhausted)
|
|
1338
|
+
break;
|
|
1339
|
+
const child = record(value);
|
|
1340
|
+
if (!child) {
|
|
1341
|
+
add(value);
|
|
1342
|
+
if (exhausted)
|
|
1343
|
+
break;
|
|
1344
|
+
continue;
|
|
1345
|
+
}
|
|
1346
|
+
const stack = [{ iterator: entries(child), depth: 1 }];
|
|
1347
|
+
while (stack.length && !exhausted) {
|
|
1348
|
+
const frame = stack[stack.length - 1];
|
|
1349
|
+
const entry = frame.iterator.next();
|
|
1350
|
+
if (entry.done) {
|
|
1351
|
+
stack.pop();
|
|
1352
|
+
continue;
|
|
1353
|
+
}
|
|
1354
|
+
const [key, value] = entry.value;
|
|
1355
|
+
add(key);
|
|
1356
|
+
if (exhausted)
|
|
1357
|
+
break;
|
|
1358
|
+
const child = record(value);
|
|
1359
|
+
if (child) {
|
|
1360
|
+
if (frame.depth >= 8) {
|
|
1361
|
+
exhausted = true;
|
|
1362
|
+
break;
|
|
1363
|
+
}
|
|
1364
|
+
stack.push({ iterator: entries(child), depth: frame.depth + 1 });
|
|
1365
|
+
}
|
|
1366
|
+
else
|
|
1367
|
+
add(value);
|
|
1368
|
+
}
|
|
1369
|
+
if (exhausted)
|
|
1370
|
+
break;
|
|
1371
|
+
}
|
|
1372
|
+
};
|
|
1373
|
+
visit(own(own(body, "connection"), "secrets"));
|
|
1374
|
+
if (kind === "auth" && !exhausted) {
|
|
1375
|
+
add(own(own(body, "engine"), "egressLease"));
|
|
1376
|
+
if (!exhausted)
|
|
1377
|
+
visit(own(body, "input"));
|
|
1378
|
+
}
|
|
1379
|
+
return { values, exhausted };
|
|
1380
|
+
}
|
|
1176
1381
|
function createRequestScope(input) {
|
|
1382
|
+
const sensitiveRegistry = createDiagnosticRedactor([], input.staticSensitiveValues);
|
|
1383
|
+
const redact = (text) => redactDiagnosticText(text, sensitiveRegistry.redact);
|
|
1384
|
+
bindCriticalDiagnosticRedactor(redact, sensitiveRegistry.redactCritical, sensitiveRegistry.redactStructured, () => sensitiveRegistry.suppressed);
|
|
1177
1385
|
const requestCost = startRequestCost();
|
|
1178
|
-
const
|
|
1386
|
+
const observeEnv = {
|
|
1387
|
+
observe: createDiagnosticEnvObserver(input.provider, sensitiveRegistry.add, sensitiveRegistry.has),
|
|
1388
|
+
finished: false,
|
|
1389
|
+
staticValues: input.staticSensitiveValues,
|
|
1390
|
+
};
|
|
1391
|
+
const traceConfig = withDiagnosticEnv(observeEnv, () => resolveTraceConfigFromEnv());
|
|
1179
1392
|
const details = {
|
|
1180
1393
|
route: input.route,
|
|
1181
1394
|
requestId: input.requestId,
|
|
@@ -1194,13 +1407,17 @@ function createRequestScope(input) {
|
|
|
1194
1407
|
const initialTraceId = requestTraceId(input.headers, input.requestId);
|
|
1195
1408
|
const trace = traceConfig
|
|
1196
1409
|
? createTraceContext({
|
|
1197
|
-
...resolveServerTraceContextOptions(traceConfig, traceAttributes),
|
|
1410
|
+
...withDiagnosticEnv(observeEnv, () => resolveServerTraceContextOptions(traceConfig, traceAttributes, undefined, redact)),
|
|
1411
|
+
redact,
|
|
1198
1412
|
...(initialTraceId ? { traceId: initialTraceId } : {}),
|
|
1199
1413
|
})
|
|
1200
|
-
: createTraceContext();
|
|
1414
|
+
: createTraceContext({ redact });
|
|
1415
|
+
bindDiagnosticSensitiveRegistry(trace, sensitiveRegistry);
|
|
1201
1416
|
const proxyCollector = new ProxyTelemetryCollector();
|
|
1417
|
+
const resolverCollector = new ResolverTelemetryCollector({ redact });
|
|
1202
1418
|
const telemetry = new RequestTelemetry(trace);
|
|
1203
1419
|
telemetry.register(proxyCollector);
|
|
1420
|
+
telemetry.register(resolverCollector);
|
|
1204
1421
|
let rootRunner = (fn) => fn();
|
|
1205
1422
|
let resolveRoot;
|
|
1206
1423
|
const rootTerminal = new Promise((resolve) => {
|
|
@@ -1221,13 +1438,13 @@ function createRequestScope(input) {
|
|
|
1221
1438
|
return clientCancelledError();
|
|
1222
1439
|
return undefined;
|
|
1223
1440
|
};
|
|
1224
|
-
const root = trace.span(`request:${input.kind}:${input.route}`, async () => {
|
|
1441
|
+
const root = withDiagnosticEnv(observeEnv, () => trace.span(`request:${input.kind}:${input.route}`, async () => {
|
|
1225
1442
|
rootRunner = AsyncLocalStorage.snapshot();
|
|
1226
1443
|
const outcome = await rootTerminal;
|
|
1227
1444
|
const error = terminalError(outcome);
|
|
1228
1445
|
if (error !== undefined)
|
|
1229
1446
|
throw error;
|
|
1230
|
-
});
|
|
1447
|
+
}));
|
|
1231
1448
|
void root.catch(() => undefined);
|
|
1232
1449
|
const settleRoot = () => {
|
|
1233
1450
|
if (!terminalOutcome || streamingSetupPending || rootSettled)
|
|
@@ -1263,7 +1480,7 @@ function createRequestScope(input) {
|
|
|
1263
1480
|
traceAttributes.flow_id = details.flowId;
|
|
1264
1481
|
traceAttributes.route = details.route;
|
|
1265
1482
|
const traceId = requestTraceId(details.headers, details.requestId);
|
|
1266
|
-
const sanitizedAttributes = Object.fromEntries(Object.entries(sanitizeTraceAttributes(traceAttributes)).map(([key, value]) => [
|
|
1483
|
+
const sanitizedAttributes = Object.fromEntries(Object.entries(sanitizeTraceAttributes(traceAttributes, redact)).map(([key, value]) => [
|
|
1267
1484
|
key,
|
|
1268
1485
|
String(value),
|
|
1269
1486
|
]));
|
|
@@ -1273,8 +1490,17 @@ function createRequestScope(input) {
|
|
|
1273
1490
|
});
|
|
1274
1491
|
};
|
|
1275
1492
|
const scope = {
|
|
1493
|
+
redact,
|
|
1276
1494
|
trace,
|
|
1277
1495
|
telemetry,
|
|
1496
|
+
resolverTelemetry: resolverCollector,
|
|
1497
|
+
seedCredentials(rawBody, kind) {
|
|
1498
|
+
const harvested = rawRequestCredentials(rawBody, kind);
|
|
1499
|
+
sensitiveRegistry.add(harvested.values);
|
|
1500
|
+
// An incomplete inventory cannot certify free text: emit less.
|
|
1501
|
+
if (harvested.exhausted)
|
|
1502
|
+
sensitiveRegistry.suppress();
|
|
1503
|
+
},
|
|
1278
1504
|
enrich(enrichment) {
|
|
1279
1505
|
if (terminalOutcome)
|
|
1280
1506
|
return;
|
|
@@ -1336,21 +1562,21 @@ function createRequestScope(input) {
|
|
|
1336
1562
|
terminalize(outcome) {
|
|
1337
1563
|
if (finishedResult)
|
|
1338
1564
|
return finishedResult;
|
|
1565
|
+
observeEnv.finished = true;
|
|
1339
1566
|
terminalOutcome = outcome;
|
|
1340
1567
|
const error = terminalError(outcome);
|
|
1341
1568
|
finishedResult = {};
|
|
1342
1569
|
try {
|
|
1343
1570
|
const declaredErrorCode = error === undefined ? undefined : input.declaredErrorCode?.(error);
|
|
1344
|
-
const status = outcome.status ??
|
|
1345
|
-
(error === undefined ? 200 : toStatusCode(error, declaredErrorCode));
|
|
1571
|
+
const status = outcome.status ?? (error === undefined ? 200 : toStatusCode(error, declaredErrorCode));
|
|
1346
1572
|
finishedResult = headerSnapshot(error);
|
|
1347
1573
|
const cost = finishRequestCost(requestCost);
|
|
1348
1574
|
try {
|
|
1349
1575
|
if (error === undefined) {
|
|
1350
|
-
logProviderSuccess(input.logger, input.provider, input.kind, details.route, details.requestId, status, cost, telemetry, details.correlation);
|
|
1576
|
+
logProviderSuccess(input.logger, input.provider, input.kind, details.route, details.requestId, status, cost, telemetry, details.correlation, redact);
|
|
1351
1577
|
}
|
|
1352
1578
|
else {
|
|
1353
|
-
logProviderError(input.logger, input.provider, input.kind, details.route, details.requestId, error, status, cost, declaredErrorCode, telemetry, finishedResult.errorObservability, details.correlation);
|
|
1579
|
+
logProviderError(input.logger, input.provider, input.kind, details.route, details.requestId, error, status, cost, declaredErrorCode, telemetry, finishedResult.errorObservability, details.correlation, redact);
|
|
1354
1580
|
}
|
|
1355
1581
|
}
|
|
1356
1582
|
catch {
|
|
@@ -1384,25 +1610,55 @@ function createRequestScope(input) {
|
|
|
1384
1610
|
};
|
|
1385
1611
|
return scope;
|
|
1386
1612
|
}
|
|
1387
|
-
|
|
1613
|
+
// A raw `Response` result skips `toErrorResponse`. Its payload shape stays
|
|
1614
|
+
// provider-owned; known credentials in error payloads still need redaction.
|
|
1615
|
+
// The observability header is SDK-owned: derive it
|
|
1616
|
+
// from the status. Only timeouts, rate limits, and 5xx are retryable;
|
|
1617
|
+
// `isRetryableCategory` would otherwise mark every other 4xx (`upstream_http`)
|
|
1618
|
+
// as retryable.
|
|
1619
|
+
function rawResponseErrorObservability(status) {
|
|
1620
|
+
return {
|
|
1621
|
+
category: categoryForStatus(status),
|
|
1622
|
+
taxonomyVersion: PROVIDER_OBSERVABILITY_TAXONOMY_VERSION,
|
|
1623
|
+
retryable: status === 408 || status === 429 || status >= 500,
|
|
1624
|
+
};
|
|
1625
|
+
}
|
|
1626
|
+
// `rawStatusFallback` is decided by the caller from the outcome, not from a
|
|
1627
|
+
// missing snapshot: a failed outcome whose thrown value is `undefined` also has
|
|
1628
|
+
// no `errorObservability`, and its 500 carries the SDK envelope, not a raw body.
|
|
1629
|
+
async function responseWithRequestScopeHeaders(response, finished, rawStatusFallback, redact) {
|
|
1388
1630
|
const headers = new Headers(response.headers);
|
|
1389
1631
|
headers.delete(PROVIDER_TELEMETRY_HEADER);
|
|
1390
1632
|
if (finished.providerTelemetryHeader) {
|
|
1391
1633
|
headers.set(PROVIDER_TELEMETRY_HEADER, finished.providerTelemetryHeader);
|
|
1392
1634
|
}
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1635
|
+
headers.delete(ERROR_OBSERVABILITY_HEADER);
|
|
1636
|
+
const errorObservability = finished.errorObservability ??
|
|
1637
|
+
(rawStatusFallback && response.status >= 400
|
|
1638
|
+
? rawResponseErrorObservability(response.status)
|
|
1639
|
+
: undefined);
|
|
1640
|
+
if (errorObservability) {
|
|
1641
|
+
headers.set(ERROR_OBSERVABILITY_HEADER, JSON.stringify(errorObservability));
|
|
1642
|
+
}
|
|
1643
|
+
// Raw Response bodies are provider-owned. Preserve their stream and representation.
|
|
1644
|
+
const body = response.body;
|
|
1645
|
+
const statusText = rawStatusFallback && response.status >= 400
|
|
1646
|
+
? redactDiagnosticText(response.statusText, redact)
|
|
1647
|
+
: response.statusText;
|
|
1648
|
+
return new Response(body, {
|
|
1397
1649
|
headers,
|
|
1398
1650
|
status: response.status,
|
|
1399
|
-
|
|
1651
|
+
// When free text cannot be certified, use the SDK's standard HTTP reason
|
|
1652
|
+
// phrase. It preserves the status contract without trusting provider text.
|
|
1653
|
+
statusText: statusText === REDACTION_FAILED && isDiagnosticRedactionSuppressed(redact)
|
|
1654
|
+
? (STATUS_CODES[response.status] ?? "")
|
|
1655
|
+
: statusText,
|
|
1400
1656
|
});
|
|
1401
1657
|
}
|
|
1402
|
-
function finalizeRequestResponse(scope, response, outcome) {
|
|
1658
|
+
async function finalizeRequestResponse(scope, response, outcome) {
|
|
1403
1659
|
try {
|
|
1404
1660
|
const error = outcome.kind === "failed" ? outcome.error : undefined;
|
|
1405
|
-
const finalResponse = responseWithRequestScopeHeaders(response, scope.snapshotHeaders(error));
|
|
1661
|
+
const finalResponse = await responseWithRequestScopeHeaders(response, scope.snapshotHeaders(error), outcome.kind === "completed", scope.redact);
|
|
1406
1662
|
scope.terminalize(outcome);
|
|
1407
1663
|
return finalResponse;
|
|
1408
1664
|
}
|
|
@@ -1580,7 +1836,7 @@ function toSseResponse(operation, result, lifecycle, requestId) {
|
|
|
1580
1836
|
}
|
|
1581
1837
|
catch (error) {
|
|
1582
1838
|
const message = error instanceof Error ? error.message : "Stream failed";
|
|
1583
|
-
controller.enqueue(encoder.encode(encodeSseEvent(streamError("stream_error", message, {
|
|
1839
|
+
controller.enqueue(encoder.encode(encodeSseEvent(streamError("stream_error", sanitizeDiagnosticText(redactDiagnosticText(message, lifecycle.redact)), {
|
|
1584
1840
|
...(requestId ? { requestId } : {}),
|
|
1585
1841
|
}))));
|
|
1586
1842
|
controller.close();
|
|
@@ -1679,7 +1935,7 @@ function getSseTransport(operation) {
|
|
|
1679
1935
|
function getHttpStreamTransport(operation) {
|
|
1680
1936
|
return operation.transport?.kind === "http-stream" ? operation.transport : undefined;
|
|
1681
1937
|
}
|
|
1682
|
-
function toAuthFlowResponse(result, contextPatch) {
|
|
1938
|
+
function toAuthFlowResponse(result, contextPatch, engine) {
|
|
1683
1939
|
if (result instanceof Response) {
|
|
1684
1940
|
return result;
|
|
1685
1941
|
}
|
|
@@ -1689,6 +1945,7 @@ function toAuthFlowResponse(result, contextPatch) {
|
|
|
1689
1945
|
return {
|
|
1690
1946
|
data: result,
|
|
1691
1947
|
...(contextPatch ? { contextPatch } : {}),
|
|
1948
|
+
...(engine ? { engine } : {}),
|
|
1692
1949
|
};
|
|
1693
1950
|
}
|
|
1694
1951
|
function authFlowLocaleFromHeaders(headers) {
|
|
@@ -1759,7 +2016,7 @@ async function handleOperation(provider, request, operationId, options, state =
|
|
|
1759
2016
|
ctx.stealth.close?.();
|
|
1760
2017
|
}
|
|
1761
2018
|
catch (error) {
|
|
1762
|
-
logProviderCleanupError(options.logger, provider, "operation", operationId, request.requestId, "stealth", error);
|
|
2019
|
+
logProviderCleanupError(options.logger, provider, "operation", operationId, request.requestId, "stealth", error, scope.redact);
|
|
1763
2020
|
}
|
|
1764
2021
|
}
|
|
1765
2022
|
if ("browser" in ctx) {
|
|
@@ -1767,11 +2024,12 @@ async function handleOperation(provider, request, operationId, options, state =
|
|
|
1767
2024
|
await ctx.browser.close?.();
|
|
1768
2025
|
}
|
|
1769
2026
|
catch (error) {
|
|
1770
|
-
logProviderCleanupError(options.logger, provider, "operation", operationId, request.requestId, "browser", error);
|
|
2027
|
+
logProviderCleanupError(options.logger, provider, "operation", operationId, request.requestId, "browser", error, scope.redact);
|
|
1771
2028
|
}
|
|
1772
2029
|
}
|
|
1773
2030
|
};
|
|
1774
2031
|
const streamLifecycle = {
|
|
2032
|
+
redact: scope.redact,
|
|
1775
2033
|
runStep(fn) {
|
|
1776
2034
|
return scope.runStreamStep(fn);
|
|
1777
2035
|
},
|
|
@@ -1823,8 +2081,9 @@ async function handleAuthFlow(provider, request, route, options, state, scope, s
|
|
|
1823
2081
|
// depend on declared secrets (client ids/secrets), so fail structured before
|
|
1824
2082
|
// any flow code runs instead of at whatever point the ceremony first reads
|
|
1825
2083
|
// the env. `abort` stays exempt: a user must always be able to cancel a
|
|
1826
|
-
// stranded flow even when provisioning is broken
|
|
1827
|
-
|
|
2084
|
+
// stranded flow even when provisioning is broken (createAuthFlowContext
|
|
2085
|
+
// applies the same exemption to the engine ceremony lease).
|
|
2086
|
+
const { context, getPatch, getEngineState } = createAuthFlowContext(provider, request, route, options, state, scope, signal);
|
|
1828
2087
|
try {
|
|
1829
2088
|
if (route !== "abort") {
|
|
1830
2089
|
assertRequiredSecretsPresent(provider, context.env);
|
|
@@ -1855,11 +2114,11 @@ async function handleAuthFlow(provider, request, route, options, state, scope, s
|
|
|
1855
2114
|
isAuthTurn(result)
|
|
1856
2115
|
? materializeAuthFlowTurn(provider, request, result)
|
|
1857
2116
|
: result;
|
|
1858
|
-
return toAuthFlowResponse(materializedResult, getPatch());
|
|
2117
|
+
return toAuthFlowResponse(materializedResult, getPatch(), getEngineState());
|
|
1859
2118
|
}
|
|
1860
2119
|
catch (error) {
|
|
1861
2120
|
if (error instanceof AuthAbortError) {
|
|
1862
|
-
return toAuthFlowResponse(error.turn, getPatch());
|
|
2121
|
+
return toAuthFlowResponse(error.turn, getPatch(), getEngineState());
|
|
1863
2122
|
}
|
|
1864
2123
|
throw error;
|
|
1865
2124
|
}
|
|
@@ -1868,7 +2127,7 @@ async function handleAuthFlow(provider, request, route, options, state, scope, s
|
|
|
1868
2127
|
context.stealth.close?.();
|
|
1869
2128
|
}
|
|
1870
2129
|
catch (error) {
|
|
1871
|
-
logProviderCleanupError(options.logger, provider, "auth", route, request.requestId, "stealth", error);
|
|
2130
|
+
logProviderCleanupError(options.logger, provider, "auth", route, request.requestId, "stealth", error, scope.redact);
|
|
1872
2131
|
}
|
|
1873
2132
|
}
|
|
1874
2133
|
}
|
|
@@ -1974,13 +2233,13 @@ function operationRequestFromForwardingEnvelope(envelope) {
|
|
|
1974
2233
|
...(envelope.deadlineAt !== undefined ? { deadlineAt: envelope.deadlineAt } : {}),
|
|
1975
2234
|
};
|
|
1976
2235
|
}
|
|
1977
|
-
function parseStatefulForwardingEnvelope(rawBody) {
|
|
2236
|
+
function parseStatefulForwardingEnvelope(rawBody, redact) {
|
|
1978
2237
|
const parsed = ProviderServerStatefulForwardEnvelopeSchema.safeParse(rawBody);
|
|
1979
2238
|
if (parsed.success)
|
|
1980
2239
|
return parsed.data;
|
|
1981
2240
|
throw new ProviderError("Stateful forwarding envelope is invalid.", {
|
|
1982
2241
|
code: "STATEFUL_FORWARDING_ENVELOPE_INVALID",
|
|
1983
|
-
details: zodDetails(parsed.error),
|
|
2242
|
+
details: zodDetails(parsed.error, redact),
|
|
1984
2243
|
});
|
|
1985
2244
|
}
|
|
1986
2245
|
/**
|
|
@@ -2014,6 +2273,10 @@ function createServerAppWithCapabilityModules(provider, serverOptions, capabilit
|
|
|
2014
2273
|
engine: serverOptions.engine ?? createInProcessProviderEngine(),
|
|
2015
2274
|
};
|
|
2016
2275
|
const app = new Hono();
|
|
2276
|
+
// Compile the startup inventory once, composed with the shared outside-context fallback.
|
|
2277
|
+
const staticSensitiveValues = compileProcessDiagnosticSensitiveValues(collectStaticDiagnosticSensitiveValues(provider, {
|
|
2278
|
+
statefulForwardingSecret: options.statefulForwarding?.secret,
|
|
2279
|
+
}));
|
|
2017
2280
|
const logger = options.logger ?? defaultProviderServerLogger;
|
|
2018
2281
|
const operationErrorCodes = buildOperationErrorCodeLookup(provider);
|
|
2019
2282
|
const statefulForwardingReplayCache = new StatefulForwardingReplayCache(options.statefulForwarding?.replayCacheMaxEntries ??
|
|
@@ -2054,6 +2317,7 @@ function createServerAppWithCapabilityModules(provider, serverOptions, capabilit
|
|
|
2054
2317
|
const operation = "stateful-internal";
|
|
2055
2318
|
const requestScope = createRequestScope({
|
|
2056
2319
|
provider,
|
|
2320
|
+
staticSensitiveValues,
|
|
2057
2321
|
kind: "operation",
|
|
2058
2322
|
route: operation,
|
|
2059
2323
|
headers: Object.fromEntries(c.req.raw.headers.entries()),
|
|
@@ -2084,7 +2348,8 @@ function createServerAppWithCapabilityModules(provider, serverOptions, capabilit
|
|
|
2084
2348
|
code: "STATEFUL_FORWARDING_ENVELOPE_INVALID",
|
|
2085
2349
|
});
|
|
2086
2350
|
}
|
|
2087
|
-
|
|
2351
|
+
requestScope.seedCredentials(rawBody, "stateful");
|
|
2352
|
+
const envelope = parseStatefulForwardingEnvelope(rawBody, requestScope.redact);
|
|
2088
2353
|
if (envelope.providerId !== provider.id) {
|
|
2089
2354
|
throw new ProviderError("Stateful forwarding envelope providerId does not match the served provider.", { code: "STATEFUL_FORWARDING_PROVIDER_MISMATCH" });
|
|
2090
2355
|
}
|
|
@@ -2185,7 +2450,7 @@ function createServerAppWithCapabilityModules(provider, serverOptions, capabilit
|
|
|
2185
2450
|
...(requestId ? { requestId } : {}),
|
|
2186
2451
|
});
|
|
2187
2452
|
const observabilityDetails = errorObservabilityDetails(error, declaredErrorCode);
|
|
2188
|
-
const response = c.json(toErrorResponse(error, requestId, observabilityDetails), status);
|
|
2453
|
+
const response = c.json(toErrorResponse(error, requestId, observabilityDetails, requestScope.redact), status);
|
|
2189
2454
|
return finalizeRequestResponse(requestScope, response, {
|
|
2190
2455
|
kind: "failed",
|
|
2191
2456
|
status,
|
|
@@ -2198,6 +2463,7 @@ function createServerAppWithCapabilityModules(provider, serverOptions, capabilit
|
|
|
2198
2463
|
const operation = c.req.param("operation");
|
|
2199
2464
|
const requestScope = createRequestScope({
|
|
2200
2465
|
provider,
|
|
2466
|
+
staticSensitiveValues,
|
|
2201
2467
|
kind: "operation",
|
|
2202
2468
|
route: operation,
|
|
2203
2469
|
operationId: operation,
|
|
@@ -2211,6 +2477,7 @@ function createServerAppWithCapabilityModules(provider, serverOptions, capabilit
|
|
|
2211
2477
|
.clone()
|
|
2212
2478
|
.json()
|
|
2213
2479
|
.catch(() => undefined);
|
|
2480
|
+
requestScope.seedCredentials(rawBody, "operation");
|
|
2214
2481
|
const body = OperationRequestSchema.parse(rawBody);
|
|
2215
2482
|
const requestHeaders = Object.fromEntries(c.req.raw.headers.entries());
|
|
2216
2483
|
body.headers = { ...requestHeaders, ...body.headers };
|
|
@@ -2228,7 +2495,7 @@ function createServerAppWithCapabilityModules(provider, serverOptions, capabilit
|
|
|
2228
2495
|
const handled = await handleOperation(provider, body, operation, options, state, requestScope, c.req.raw.signal);
|
|
2229
2496
|
const response = handled instanceof Response ? handled : c.json(handled);
|
|
2230
2497
|
return streaming
|
|
2231
|
-
? responseWithRequestScopeHeaders(response, requestScope.snapshotHeaders())
|
|
2498
|
+
? responseWithRequestScopeHeaders(response, requestScope.snapshotHeaders(), true, requestScope.redact)
|
|
2232
2499
|
: response;
|
|
2233
2500
|
};
|
|
2234
2501
|
const response = streaming
|
|
@@ -2247,7 +2514,7 @@ function createServerAppWithCapabilityModules(provider, serverOptions, capabilit
|
|
|
2247
2514
|
const requestId = extractRequestId(rawBody);
|
|
2248
2515
|
requestScope.enrich({ ...(requestId ? { requestId } : {}) });
|
|
2249
2516
|
const observabilityDetails = errorObservabilityDetails(error, declaredErrorCode);
|
|
2250
|
-
const response = c.json(toErrorResponse(error, requestId, observabilityDetails), status);
|
|
2517
|
+
const response = c.json(toErrorResponse(error, requestId, observabilityDetails, requestScope.redact), status);
|
|
2251
2518
|
return finalizeRequestResponse(requestScope, response, {
|
|
2252
2519
|
kind: "failed",
|
|
2253
2520
|
status,
|
|
@@ -2267,6 +2534,7 @@ function createServerAppWithCapabilityModules(provider, serverOptions, capabilit
|
|
|
2267
2534
|
let rawBody;
|
|
2268
2535
|
const requestScope = createRequestScope({
|
|
2269
2536
|
provider,
|
|
2537
|
+
staticSensitiveValues,
|
|
2270
2538
|
kind: "auth",
|
|
2271
2539
|
route: logRoute,
|
|
2272
2540
|
headers: Object.fromEntries(c.req.raw.headers.entries()),
|
|
@@ -2278,6 +2546,7 @@ function createServerAppWithCapabilityModules(provider, serverOptions, capabilit
|
|
|
2278
2546
|
.clone()
|
|
2279
2547
|
.json()
|
|
2280
2548
|
.catch(() => undefined);
|
|
2549
|
+
requestScope.seedCredentials(rawBody, "auth");
|
|
2281
2550
|
const body = withAuthRequestHeaders(AuthFlowRequestSchema.parse(rawBody), c.req.raw.headers);
|
|
2282
2551
|
requestScope.enrich({
|
|
2283
2552
|
requestId: body.requestId,
|
|
@@ -2306,7 +2575,7 @@ function createServerAppWithCapabilityModules(provider, serverOptions, capabilit
|
|
|
2306
2575
|
const requestId = extractRequestId(rawBody);
|
|
2307
2576
|
requestScope.enrich({ ...(requestId ? { requestId } : {}) });
|
|
2308
2577
|
const observabilityDetails = errorObservabilityDetails(error);
|
|
2309
|
-
const response = c.json(toErrorResponse(error, requestId, observabilityDetails), status);
|
|
2578
|
+
const response = c.json(toErrorResponse(error, requestId, observabilityDetails, requestScope.redact), status);
|
|
2310
2579
|
return finalizeRequestResponse(requestScope, response, {
|
|
2311
2580
|
kind: "failed",
|
|
2312
2581
|
status,
|