mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-26 04:15:48 -06:00
fix(ui): conceal sensitive collection defaults
This commit is contained in:
@@ -132,6 +132,65 @@ describe("config form collection defaults", () => {
|
||||
expect(onPatch).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("conceals sensitive collection defaults and disables restore until revealed", () => {
|
||||
const container = document.createElement("div");
|
||||
const onPatch = vi.fn();
|
||||
const hints = {
|
||||
"settings.profile": { sensitive: true },
|
||||
"settings.tokens": { sensitive: true },
|
||||
};
|
||||
|
||||
render(
|
||||
renderObject(
|
||||
{
|
||||
schema: {
|
||||
type: "object",
|
||||
title: "Profile",
|
||||
default: { apiKey: "default-secret" },
|
||||
properties: { apiKey: { type: "string" } },
|
||||
},
|
||||
value: { apiKey: "authored-secret" },
|
||||
path: ["settings", "profile"],
|
||||
hints,
|
||||
unsupported: new Set(),
|
||||
disabled: false,
|
||||
revealSensitive: false,
|
||||
onPatch,
|
||||
},
|
||||
renderNode,
|
||||
),
|
||||
container,
|
||||
);
|
||||
|
||||
expect(container.textContent).not.toContain("default-secret");
|
||||
expect(resetButton(container).disabled).toBe(true);
|
||||
|
||||
render(
|
||||
renderArray(
|
||||
{
|
||||
schema: {
|
||||
type: "array",
|
||||
title: "Tokens",
|
||||
items: { type: "string" },
|
||||
default: ["default-token"],
|
||||
},
|
||||
value: ["authored-token"],
|
||||
path: ["settings", "tokens"],
|
||||
hints,
|
||||
unsupported: new Set(),
|
||||
disabled: false,
|
||||
revealSensitive: false,
|
||||
onPatch,
|
||||
},
|
||||
renderNode,
|
||||
),
|
||||
container,
|
||||
);
|
||||
|
||||
expect(container.textContent).not.toContain("default-token");
|
||||
expect(resetButton(container).disabled).toBe(true);
|
||||
});
|
||||
|
||||
it("removes an optional object as one value and keeps inherited children inherited", () => {
|
||||
const container = document.createElement("div");
|
||||
const onPatch = vi.fn();
|
||||
|
||||
@@ -53,16 +53,30 @@ import { configFieldId, hintForPath, type JsonSchema } from "./config-form.share
|
||||
import { renderSettingsEmpty } from "./settings-ui.ts";
|
||||
|
||||
const UNSET_ARRAY_SOURCE_IDENTITY = Symbol("unset-array-source");
|
||||
const UNSET_MAP_SOURCE_IDENTITY = Symbol("unset-map-source");
|
||||
|
||||
function collectionDefaultPresentation(params: ConfigNodeRenderParams, effectiveValue: unknown) {
|
||||
const redacted = getSensitiveRenderState({
|
||||
path: params.path,
|
||||
value: effectiveValue,
|
||||
hints: params.hints,
|
||||
revealSensitive: params.revealSensitive ?? false,
|
||||
isSensitivePathRevealed: params.isSensitivePathRevealed,
|
||||
}).isRedacted;
|
||||
return {
|
||||
description: redacted ? nothing : renderSchemaDefaultDescription(params.schema, params.value),
|
||||
action: renderRestoreDefaultButton({
|
||||
...params,
|
||||
disabled: params.disabled || redacted,
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
function openCollectionDraft(event: Event, draftId: string): void {
|
||||
const block = (event.currentTarget as HTMLElement).closest(".cfg-block");
|
||||
const draft = Array.from(block?.children ?? []).find((child) => child.id === draftId);
|
||||
const openDraft = (draft as Partial<ConfigFormCollectionDraft> | undefined)?.openDraft;
|
||||
if (typeof openDraft === "function") {
|
||||
openDraft.call(draft);
|
||||
}
|
||||
(draft as Partial<ConfigFormCollectionDraft> | undefined)?.openDraft?.call(draft);
|
||||
}
|
||||
const UNSET_MAP_SOURCE_IDENTITY = Symbol("unset-map-source");
|
||||
|
||||
export function renderObject(
|
||||
params: ConfigNodeRenderParams,
|
||||
@@ -97,6 +111,7 @@ export function renderObject(
|
||||
fallback && typeof fallback === "object" && !Array.isArray(fallback)
|
||||
? (fallback as Record<string, unknown>)
|
||||
: {};
|
||||
const defaultPresentation = collectionDefaultPresentation(params, fallback);
|
||||
const entries = objectPropertyKeys(schema)
|
||||
.map((key) => [key, objectPropertySchema(schema, key)] as const)
|
||||
.filter((entry): entry is readonly [string, ConfigNodeRenderParams["schema"]] =>
|
||||
@@ -206,14 +221,12 @@ export function renderObject(
|
||||
<span class="settings-row__title">${label}</span>
|
||||
${help ? html`<span class="settings-row__desc">${help}</span>` : nothing}
|
||||
${schema.default !== undefined
|
||||
? html`<span class="settings-row__desc"
|
||||
>${renderSchemaDefaultDescription(schema, value)}</span
|
||||
>`
|
||||
? html`<span class="settings-row__desc">${defaultPresentation.description}</span>`
|
||||
: nothing}
|
||||
${renderTags(tags)}
|
||||
</div>
|
||||
<div class="settings-row__control">
|
||||
${renderRestoreDefaultButton(params)}
|
||||
${defaultPresentation.action}
|
||||
<span class="settings-row__chevron cfg-object__chevron">${icons.chevronDown}</span>
|
||||
</div>
|
||||
</summary>
|
||||
@@ -271,6 +284,7 @@ export function renderArray(
|
||||
: Array.isArray(schema.default)
|
||||
? schema.default
|
||||
: UNSET_ARRAY_SOURCE_IDENTITY;
|
||||
const defaultPresentation = collectionDefaultPresentation(params, arrayValue);
|
||||
const rowIdentities = rowIdentitiesForArray(arrayValue);
|
||||
const {
|
||||
minItems: minimumItems,
|
||||
@@ -352,9 +366,7 @@ export function renderArray(
|
||||
${showLabel ? html`<span class="settings-row__title">${label}</span>` : nothing}
|
||||
${showLabel && help ? html`<span class="settings-row__desc">${help}</span>` : nothing}
|
||||
${showLabel && schema.default !== undefined
|
||||
? html`<span class="settings-row__desc"
|
||||
>${renderSchemaDefaultDescription(schema, value)}</span
|
||||
>`
|
||||
? html`<span class="settings-row__desc">${defaultPresentation.description}</span>`
|
||||
: nothing}
|
||||
${renderTags(tags)}
|
||||
</div>
|
||||
@@ -364,7 +376,7 @@ export function renderArray(
|
||||
count: String(arrayValue.length),
|
||||
})}</span
|
||||
>
|
||||
${renderRestoreDefaultButton(params)}
|
||||
${defaultPresentation.action}
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn--sm"
|
||||
|
||||
Reference in New Issue
Block a user