mirror of
https://github.com/openclaw/openclaw.git
synced 2026-07-16 17:01:45 +00:00
fix(models): redact synthetic auth credentials from models status --json (#104734)
The provider auth overview passed the caller's synthetic-auth object through to the JSON payload. Status hands it the full runtime shape, which also carries the raw credential, so structural typing let live API keys/tokens ship in a diagnostic surface (#104713). Re-project to the declared value/source fields at the overview boundary. Fixes #104713
This commit is contained in:
@@ -99,6 +99,31 @@ describe("resolveProviderAuthOverview", () => {
|
||||
vi.mocked(resolveEnvApiKey).mockClear();
|
||||
});
|
||||
|
||||
it("projects synthetic auth to value/source and drops runtime credential fields", () => {
|
||||
// #104713: status callers pass their richer runtime object (credential,
|
||||
// mode, expiresAt); the overview must not let those reach JSON output.
|
||||
const runtimeSyntheticAuth = {
|
||||
value: "plugin-owned",
|
||||
source: "xAI plugin config",
|
||||
credential: "xai-raw-credential-material",
|
||||
mode: "api-key",
|
||||
expiresAt: Date.now() + 60_000,
|
||||
};
|
||||
const overview = resolveProviderAuthOverview({
|
||||
provider: "xai",
|
||||
cfg: {},
|
||||
store: { version: 1, profiles: {} } as never,
|
||||
modelsPath: "/tmp/models.json",
|
||||
syntheticAuth: runtimeSyntheticAuth,
|
||||
});
|
||||
|
||||
expect(overview.syntheticAuth).toStrictEqual({
|
||||
value: "plugin-owned",
|
||||
source: "xAI plugin config",
|
||||
});
|
||||
expect(JSON.stringify(overview)).not.toContain("xai-raw-credential-material");
|
||||
});
|
||||
|
||||
it("labels token profiles that only have tokenRef", () => {
|
||||
const overview = resolveProviderAuthOverview({
|
||||
provider: "github-copilot",
|
||||
|
||||
@@ -218,6 +218,16 @@ export function resolveProviderAuthOverview(params: {
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
...(params.syntheticAuth ? { syntheticAuth: params.syntheticAuth } : {}),
|
||||
// Re-project instead of passing the caller's object through: status callers
|
||||
// hand richer runtime shapes that also carry the raw synthetic credential,
|
||||
// and structural typing would let it leak into `--json` output verbatim.
|
||||
...(params.syntheticAuth
|
||||
? {
|
||||
syntheticAuth: {
|
||||
value: params.syntheticAuth.value,
|
||||
source: params.syntheticAuth.source,
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1221,6 +1221,13 @@ describe("modelsStatusCommand auth overview", () => {
|
||||
value: "plugin-owned",
|
||||
source: "codex-app-server",
|
||||
});
|
||||
// #104713: the summary must ship only the projected fields; the runtime
|
||||
// synthetic-auth object also carries the raw credential and must never
|
||||
// reach the JSON payload.
|
||||
expect(
|
||||
Object.keys(requireRecord(codexProvider.syntheticAuth, "codex synthetic auth")),
|
||||
).toStrictEqual(["value", "source"]);
|
||||
expect(JSON.stringify(payload)).not.toContain("codex-runtime-token");
|
||||
expectRecordFields(requireRecord(codexProvider.effective, "codex effective auth"), {
|
||||
kind: "synthetic",
|
||||
detail: "codex-app-server",
|
||||
|
||||
Reference in New Issue
Block a user