Repository navigation
Auth credential hygiene: Basic/Digest password redaction, replay guard over every credential header #71
Description
Part of the audit remediation umbrella #67. Milestone 2. Severity: MEDIUM (security). Requirements: AUTH-8, AUTH-28, XCUT-16.
Current SDK behavior
Passwords in plain fields. BasicCredential and DigestCredential
(packages/core/src/auth/auth-step.ts:36-41,48-58) are structural interfaces with a public
password: string. ApiKeyCredential, NameKeyCredential and BearerToken in
credential.ts keep the secret in a #private field and override toString and
nodejs.util.inspect.custom. util.inspect(credentials) on an AuthCredentialSet prints
password: 'hunter2' beside ApiKeyCredential{key=***}. JSON.stringify serializes both
passwords. docs/sdk-documentation/auth.md:40-46 shows the raw-password shape directly above
the claim that a credential cannot leak into a log line by accident.
Replay guard checks two header names. After a 401, the auth step calls
guardReplayScheme (auth-step.ts:549-567) on the replacement request. The guard tests only
Authorization and Proxy-Authorization (:554-556). ApiKeyCredentialConfig.headerName
(:81) lets the step stamp any header. A challengeHook that returns a replacement on
http:// with X-Api-Key: SECRET is dispatched in clear text. No PlaintextCredentialError
is thrown.
Expected behavior
The SDK redacts every credential type in every string or diagnostic representation. The
Basic and Digest credentials do not expose the password as a plain property. The SDK refuses
to send a replay over plain HTTP when the replacement request carries any header the step
treats as a credential carrier.
Notes and leads
- Passwords: add
BasicCredential/DigestCredentialclasses (orcreateBasicCredential()
factories) with#password,toString, and the inspect override, matchingcredential.ts.
Accept them inAuthCredentialSet. Updateauth.md. This is a public-shape change; it is
free before the first version bump (docs/first-release.md). Minor changeset for
@dexpace/core. - Replay guard: simplest correct rule is "if the original request was guarded, run
requireHttpson the replacement, regardless of headers". Alternative: build the set of
credential-carrying names from the step's configuration and check each. - AUTH-8 names bearer, API-key and name-key types only. Record the wider reading in the Phase
5c ledger section. - Tests:
util.inspectandJSON.stringifyon a fullAuthCredentialSetcontain no
password; a replay with an API-key header onhttp://throwsPlaintextCredentialError.
Add both topackages/core/src/auth/and the API-key case to
tests/conformance/xcut/security-by-default.conformance.test.ts.
Activity
docs: ledger — record the #71 outcome
Wahbeh-Mohammad commentedon Sep 5, 2026
Addressed in PR #86, merged into
audit/remediation-67; reachesmvpthrough PR #98. Decision record:docs/audit-67-decisions.md.