mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-18 22:47:26 +00:00
fix(config): redact dynamic catchall secret keys
This commit is contained in:
@@ -656,7 +656,7 @@ describe("redactConfigSnapshot", () => {
|
||||
expectGatewayAuthFieldValue(result, "token", "not-actually-secret-value");
|
||||
});
|
||||
|
||||
it("does not redact paths absent from uiHints (schema is single source of truth)", () => {
|
||||
it("redacts sensitive-looking paths even when absent from uiHints (defense in depth)", () => {
|
||||
const hints: ConfigUiHints = {
|
||||
"some.other.path": { sensitive: true },
|
||||
};
|
||||
@@ -664,7 +664,57 @@ describe("redactConfigSnapshot", () => {
|
||||
gateway: { auth: { password: "not-in-hints-value" } },
|
||||
});
|
||||
const result = redactConfigSnapshot(snapshot, hints);
|
||||
expectGatewayAuthFieldValue(result, "password", "not-in-hints-value");
|
||||
expectGatewayAuthFieldValue(result, "password", REDACTED_SENTINEL);
|
||||
});
|
||||
|
||||
it("redacts and restores dynamic env catchall secrets when uiHints miss the path", () => {
|
||||
const hints: ConfigUiHints = {
|
||||
"some.other.path": { sensitive: true },
|
||||
};
|
||||
const snapshot = makeSnapshot({
|
||||
env: {
|
||||
GROQ_API_KEY: "gsk-secret-123",
|
||||
NODE_ENV: "production",
|
||||
},
|
||||
});
|
||||
const redacted = redactConfigSnapshot(snapshot, hints);
|
||||
const env = redacted.config.env as Record<string, string>;
|
||||
expect(env.GROQ_API_KEY).toBe(REDACTED_SENTINEL);
|
||||
expect(env.NODE_ENV).toBe("production");
|
||||
|
||||
const restored = restoreRedactedValues(redacted.config, snapshot.config, hints);
|
||||
expect(restored.env.GROQ_API_KEY).toBe("gsk-secret-123");
|
||||
expect(restored.env.NODE_ENV).toBe("production");
|
||||
});
|
||||
|
||||
it("redacts and restores skills entry env secrets in dynamic record paths", () => {
|
||||
const hints: ConfigUiHints = {
|
||||
"some.other.path": { sensitive: true },
|
||||
};
|
||||
const snapshot = makeSnapshot({
|
||||
skills: {
|
||||
entries: {
|
||||
web_search: {
|
||||
env: {
|
||||
GEMINI_API_KEY: "gemini-secret-456",
|
||||
BRAVE_REGION: "us",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
const redacted = redactConfigSnapshot(snapshot, hints);
|
||||
const entry = (
|
||||
redacted.config.skills as {
|
||||
entries: Record<string, { env: Record<string, string> }>;
|
||||
}
|
||||
).entries.web_search;
|
||||
expect(entry.env.GEMINI_API_KEY).toBe(REDACTED_SENTINEL);
|
||||
expect(entry.env.BRAVE_REGION).toBe("us");
|
||||
|
||||
const restored = restoreRedactedValues(redacted.config, snapshot.config, hints);
|
||||
expect(restored.skills.entries.web_search.env.GEMINI_API_KEY).toBe("gemini-secret-456");
|
||||
expect(restored.skills.entries.web_search.env.BRAVE_REGION).toBe("us");
|
||||
});
|
||||
|
||||
it("uses wildcard hints for array items", () => {
|
||||
|
||||
@@ -164,7 +164,10 @@ function redactObjectWithLookup(
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!matched && isExtensionPath(path)) {
|
||||
if (!matched) {
|
||||
// Fall back to pattern-based guessing for paths not covered by schema
|
||||
// hints. This catches dynamic keys inside catchall objects (for example
|
||||
// env.GROQ_API_KEY) and extension/plugin config alike.
|
||||
const markedNonSensitive = isExplicitlyNonSensitivePath(hints, [path, wildcardPath]);
|
||||
if (
|
||||
typeof value === "string" &&
|
||||
@@ -542,7 +545,7 @@ function restoreRedactedValuesWithLookup(
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!matched && isExtensionPath(path)) {
|
||||
if (!matched) {
|
||||
const markedNonSensitive = isExplicitlyNonSensitivePath(hints, [path, wildcardPath]);
|
||||
if (!markedNonSensitive && isSensitivePath(path) && value === REDACTED_SENTINEL) {
|
||||
result[key] = restoreOriginalValueOrThrow({ key, path, original: orig });
|
||||
|
||||
Reference in New Issue
Block a user