Search: node settings are indexed, with credentials masked on the server

/search gains a `setting` category, `<node>.<setting>` under its flow with the
node type and the value in a new `detail` field, so Ctrl-K finds which node
uses a broker or a timeout and opens the node. `setting_text` is the one place
that decides what may be said: a reference to a stored secret shows its name
(to superusers only, as the secrets list does), a plain value in an `x-secret`
field or under a key like password, token or api_key is masked and never
indexed, nested credentials in headers or query are masked, long values are
clipped. The global search ranks what contains the query as typed ahead of
loose spellings before it caps a group, which long setting values would
otherwise have crowded out.
This commit is contained in:
2026-10-28 11:07:27 +01:00
parent 2e0ba32124
commit 29f4d93c2a
7 changed files with 274 additions and 20 deletions
+31 -1
View File
@@ -21,7 +21,14 @@ test.beforeAll(async ({ browser }) => {
data: {
name: flowName,
title: "Searchable flow",
nodes: [{ id: "findme", type: "python", title: "Find me" }],
nodes: [
{
id: "findme",
type: "python",
title: "Find me",
params: { tuning_gain: 7, api_token: "s3cr3t-token" },
},
],
inputs: [{ spec: { name: "findme_input", dtype: "float" } }],
},
})
@@ -102,3 +109,26 @@ test("opens on the keyboard from anywhere", async ({ page }) => {
await expect(page.getByTestId("global-search-input")).toBeFocused()
})
test("finds a setting with its value, and never a credential", async ({
page,
}) => {
await page.goto("/")
await page.getByRole("button", { name: "Search", exact: true }).click()
const input = page.getByTestId("global-search-input")
await input.fill("tuning_gain")
const gain = page.getByRole("option", { name: /findme\.tuning_gain/ })
await expect(gain).toContainText(`${flowName} · python · 7`)
// The token is masked on the server, so it never reaches the page.
await input.fill("api_token")
await expect(
page.getByRole("option", { name: /findme\.api_token/ }),
).toContainText("••••")
await expect(page.getByRole("dialog")).not.toContainText("s3cr3t-token")
await input.fill("tuning_gain")
await gain.click()
await expect(page).toHaveURL(new RegExp(`/flows/${flowName}\\?node=findme$`))
})