Skip to content

feat(console): mask access/secret keys in application config forms#5

Merged
kitsunoff merged 4 commits into
mainfrom
feat/mask-sensitive-string-fields
May 13, 2026
Merged

feat(console): mask access/secret keys in application config forms#5
kitsunoff merged 4 commits into
mainfrom
feat/mask-sensitive-string-fields

Conversation

@lexfrei
Copy link
Copy Markdown
Contributor

@lexfrei lexfrei commented May 12, 2026

What

Application config forms (the SchemaForm-driven create/edit screens for postgres and any other chart that exposes credentials in its OpenAPI schema) now render fields like s3AccessKey, s3SecretKey, password, apiKey, privateKey, bearerToken as a masked <input type="password"> with an eye-icon toggle next to it. Click the toggle and the input flips to text; click again and it goes back to bullets.

Detection is purely name-based — Cozystack chart schemas don't carry a format: password hint, so the new helper splits a field name on camelCase / underscore / dash / dot boundaries and matches the resulting word tokens against a small allowlist. The split between "matches anywhere" (accesskey, secretkey, secretaccesskey, password, apikey, privatekey) and "matches only as the trailing word" (token, passwd) keeps neighbours like tokenAudience, csrfTokenName, passwdFile, publicKey out of the set.

Why

Today these fields render with the default RJSF TextWidget, which means anyone with the dashboard on screen during a demo, a paired session, or a screenshare can read the S3 credentials a tenant typed in. Masking by default with an opt-in reveal toggle is the standard pattern for credential entry and matches the existing SecretsTab behaviour on the application detail page.

Scope

  • Edit/create form only. The Overview tab YAML dump and the Monaco YAML editor on ApplicationOrderPage are not changed — both intentionally show the raw spec and changing that is a separate decision.
  • The widget keeps its toggle usable when the surrounding form is disabled or readonly, so a viewer of an existing application config can still inspect the stored value without being able to edit it.
  • autoComplete="new-password" is set on the input to discourage browser password-manager prompts on configuration fields that aren't the user's login credential. Browser behaviour varies and the prompt is not fully suppressible.

Test infrastructure

This PR also wires up vitest + React Testing Library in apps/console (no test infra existed before) and adds a small CI workflow that runs pnpm typecheck && pnpm test on every push to main and every PR. The detector and the widget are covered by 29 unit tests across two test files.

Limitations

  • Schemas that put credentials inside oneOf / anyOf / allOf branches are not walked yet. A FIXME-marked test pins that behaviour so the gap is visible.
  • Detection is heuristic. False negatives stay possible — anything outside the allowlist won't be masked. Adding format: password to the upstream chart schemas would let this widget be triggered explicitly; that work belongs in cozystack/cozystack.

lexfrei added 4 commits May 12, 2026 21:19
Adds vitest with jsdom and React Testing Library to apps/console so that
schema-form widgets and helpers can be unit-tested. No tests yet; the
infra is wired up first to keep follow-up commits focused on behaviour.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
Adds a pure helper that walks an OpenAPI/RJSF schema and tags every
string field whose name matches the conventional credential patterns
(access/secret keys, passwords, tokens, private keys, api keys) with a
`SensitiveStringWidget` binding in the uiSchema.

Cozystack chart schemas do not annotate sensitive fields with a
`format: password` hint, so the detection is name-based and runs in the
same style as the existing storageClass/backupClassName/disks helpers
in SchemaForm. The helper lives in a separate module so it can be
covered with unit tests without rendering RJSF.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
Adds an RJSF widget that renders a credential field as eight bullets
plus an eye-icon toggle: clicking the eye swaps the bullets for a plain
text input that forwards typing through the standard onChange contract
(empty string becomes undefined). When the field is disabled or
readonly the toggle is disabled and editing is blocked.

The visual style mirrors the SecretRow reveal control already used in
the Secrets tab.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
Wires the sensitive-field detector into SchemaForm's uiSchema pipeline
and registers the toggle widget under the customWidgets map. As a
result, fields like postgres' s3AccessKey and s3SecretKey on the
application order/edit screens render as bullets with a click-to-reveal
control instead of plain text. Detection is purely additive and does
not override widgets bound by earlier detectors.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 12, 2026

Warning

Rate limit exceeded

@lexfrei has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 10 seconds before requesting another review.

You’ve run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 0d049e95-c202-4ca4-8117-6cfd23055cd5

📥 Commits

Reviewing files that changed from the base of the PR and between 2ff7cae and 380bd4f.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (12)
  • .github/workflows/test.yaml
  • apps/console/package.json
  • apps/console/src/components/SchemaForm.tsx
  • apps/console/src/components/SensitiveStringWidget.test.tsx
  • apps/console/src/components/SensitiveStringWidget.tsx
  • apps/console/src/components/rjsf-templates.tsx
  • apps/console/src/lib/sensitive-fields.test.ts
  • apps/console/src/lib/sensitive-fields.ts
  • apps/console/test/setup.ts
  • apps/console/tsconfig.json
  • apps/console/vitest.config.ts
  • package.json
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/mask-sensitive-string-fields

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@lexfrei lexfrei self-assigned this May 12, 2026
Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request implements automatic masking for sensitive fields like passwords and API keys in schema-driven forms. It introduces a SensitiveStringWidget with a visibility toggle and a utility to identify sensitive fields by name. Additionally, it sets up a Vitest-based testing environment with comprehensive unit tests for the new components and logic. I have no feedback to provide.

Copy link
Copy Markdown

@kitsunoff kitsunoff left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tested locally against a live cozystack cluster: vitest 29/29, typecheck clean.
Verified masking renders on Postgres backup s3AccessKey/s3SecretKey with the
toggle flipping between password and text. Heuristic allowlist looks tight
(tokenAudience/passwdFile correctly stay unmasked).

LGTM.

Note: the test workflow check is failing because pnpm/action-setup@v4 needs
either a version: in the workflow or packageManager in package.json — a
one-line follow-up, unrelated to the change.

@kitsunoff kitsunoff merged commit fa41573 into main May 13, 2026
2 of 3 checks passed
@kitsunoff kitsunoff deleted the feat/mask-sensitive-string-fields branch May 13, 2026 15:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants