fix(cli): treat cleared (empty-string) credentials as unset in config list
The settings panel persists "" when a credential is cleared and renders that as unset; config list now uses the same semantics instead of masking the empty string as a configured credential.
This commit is contained in:
@@ -287,12 +287,14 @@ async function handleList(flags: { json?: boolean }): Promise<void> {
|
||||
//
|
||||
// Redaction is driven by the value, not by classification alone. Marking an
|
||||
// unset credential as redacted would report every fresh install as having
|
||||
// one configured, which leaks the opposite of what redaction is for.
|
||||
// one configured, which leaks the opposite of what redaction is for. The
|
||||
// settings panel persists "" when a credential is cleared and renders that
|
||||
// as unset; the same semantics apply here (credentials are all strings).
|
||||
const result: Record<string, { value?: unknown; redacted?: true; type: string; description: string }> = {};
|
||||
for (const def of defs) {
|
||||
const value = settings.get(def.path);
|
||||
result[def.path] =
|
||||
isCredential(def.path) && value !== undefined
|
||||
isCredential(def.path) && value
|
||||
? { redacted: true, type: def.type, description: def.description }
|
||||
: { value, type: def.type, description: def.description };
|
||||
}
|
||||
@@ -321,10 +323,11 @@ async function handleList(flags: { json?: boolean }): Promise<void> {
|
||||
for (const def of groups[group]) {
|
||||
// `list` dumps every value without anyone asking for a specific
|
||||
// credential, so redact here. `get <path>` stays an explicit
|
||||
// single-value request and is left alone. An unset credential keeps its
|
||||
// ordinary empty rendering: masking it would imply one is configured.
|
||||
// single-value request and is left alone. An unset or cleared ("")
|
||||
// credential keeps its ordinary rendering: masking it would imply one
|
||||
// is configured.
|
||||
const value = settings.get(def.path);
|
||||
const valueStr = isCredential(def.path) && value !== undefined ? REDACTED : formatValue(value);
|
||||
const valueStr = isCredential(def.path) && value ? REDACTED : formatValue(value);
|
||||
const typeStr = getTypeDisplay(def);
|
||||
console.log(` ${chalk.white(def.path)} = ${valueStr} ${chalk.dim(typeStr)}`);
|
||||
}
|
||||
|
||||
@@ -152,6 +152,17 @@ describe("config list output", () => {
|
||||
expect(parsed["searxng.token"]).not.toHaveProperty("redacted");
|
||||
});
|
||||
|
||||
it("does not report a cleared credential as configured", async () => {
|
||||
// The settings panel persists "" when a credential is cleared and renders
|
||||
// that as unset; `config list` must agree, or a cleared token looks set.
|
||||
await runConfigCommand({ action: "set", key: "searxng.token", value: SECRET, flags: { json: true } });
|
||||
await runConfigCommand({ action: "set", key: "searxng.token", value: "", flags: { json: true } });
|
||||
const output = await humanList();
|
||||
expect(output).not.toContain("searxng.token = ********");
|
||||
const { parsed } = await jsonList();
|
||||
expect(parsed["searxng.token"]).not.toHaveProperty("redacted");
|
||||
});
|
||||
|
||||
it("leaves the Hindsight server URL readable", async () => {
|
||||
// It sits beside the API token under the same display condition, and is an
|
||||
// ordinary endpoint: masking it hides a value users need to inspect.
|
||||
|
||||
Reference in New Issue
Block a user