Skip to content

fix(platform-objects): credential action params declare type password, so the dialog masks what is typed - #24867

Merged
objectstack-fleet[bot] merged 4 commits into
mainfrom
claude/issue-24758-password-param-type
Oct 20, 2026
Merged

objectstack-fleet[bot] merged 4 commits into
mainfrom
claude/issue-24758-password-param-type

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Fixes #24758

Clause-②: no (an input's presentation: the param still accepts the same string, and no accept set or served shape moves)

What changed

Every credential param on the identity objects' actions now declares type: 'password' instead of type: 'text', so the console's action dialog draws a masked input and the typed value is no longer readable on screen or in screenshots. Labels, names, required and the posted body are unchanged.

Object Action Param
sys_user create_user password (the card)
sys_user set_user_password newPassword (the card)
sys_user change_my_password currentPassword, newPassword
sys_user delete_my_account, enable_two_factor, disable_two_factor, generate_backup_codes password
sys_two_factor enable_two_factor, disable_two_factor, regenerate_backup_codes password
sys_sso_provider register_sso_provider clientSecret

Files: packages/platform-objects/src/identity/sys-user.object.ts (the claim's surface), sys-two-factor.object.ts and sys-sso-provider.object.ts (outside the claim's listed surface, see below), the pins in packages/platform-objects/src/platform-objects.test.ts, and a patch changeset for @objectstack/platform-objects.

Read before the edit

  • Console render (objectui at the pinned .objectui-sha 616df6598b42). ActionParamDialog builds every param through paramToField(), whose widget key is resolveParamWidgetType(param.type) → resolveFormWidgetType. password is a key of fieldWidgetLoaderByKey in @object-ui/fields, loading widgets/PasswordField.tsx, which renders its input with type password (with a show/hide toggle) and posts the string through onChange exactly as the text widget does. So a password param is masked in the console today; no objectui change is needed.
  • Server. No server path reads an action param's type: resolvePassword in plugin-auth's admin-user-endpoints.ts reads body.password / body.newPassword as strings; MASKED_ON_READ_FIELD_TYPES / isMaskedOnReadFieldType and the generic-object plaintext warning in object.zod.ts judge object FIELDS only, never action params. ActionParamSchema.type is FieldType, which admits password. What the server accepts is unchanged.
  • Every other password-like param (git grep -n "assword" -- 'packages/platform-objects/src/**/*.object.ts'): the eight sys_user params and three sys_two_factor params in the table. A second sweep for credential-named params (password, secret, token) declared type: 'text' across packages/** and examples/** found one more, sys_sso_provider.register_sso_provider#clientSecret.

Scope notes

  • sys-two-factor.object.ts is outside the claim's File surface:; the dispatch names "any other password-like param on the platform objects declared type: 'text'" as in scope, so its three params land here.
  • sys_sso_provider's clientSecret is landed as a bounded in-place fix: the same defect class (a credential typed into an action dialog shown in clear), the same one-token mechanical change pinned by the same test, no open PR or claim on that file, and no new gate family.
  • No translation bundle moves: labels are unchanged, and pnpm check:i18n is green after the build.

Pins

  • credential action params declare 'password', so the dialog masks what is typed in platform-objects.test.ts: a roster row per param above asserting type === 'password', plus a sweep over every object exported from ./identity/index.js that fails on any credential-named param (/password|secret/i, boolean toggles such as mustChangePassword excepted) not declared password; its positive control asserts the sweep reaches exactly the rostered params.
  • Pin sweep: the declarations and their strings ("leave empty to generate", "Current Password", the param names) were searched across every package, qa/, docs/qa/platform-checklist/ and content/docs/. No existing pin asserts these params' type, so no pin moves.

Verification record

Base abd5acdaf, merged origin/main 9a06f0be0 before opening; head d46ae39da.

  • pnpm --filter @objectstack/platform-objects typecheck: exit 0 (before and after the merge).
  • pnpm --filter @objectstack/platform-objects test (full suite): exit 0, 79 files / 1212 tests passed (after the merge).
  • pnpm --filter @objectstack/core test:repo: exit 0, 6 files / 82 tests.
  • pnpm --filter @objectstack/plugin-auth test (the endpoint consumer): exit 0, 170 files, 3260 passed / 10 skipped.
  • Build: turbo run build over the workspace after the merge, 76/76 tasks.
  • Ablation via scripts/ablation-replace.mjs (fix committed first; the test imports src, no dist): reverting create_user#password to text → 2 failed (roster row + sweep), restored to HEAD blob 7abf5fe3a3fb; reverting set_user_password#newPassword → 2 failed (SysUser.set_user_password#newPassword declares 'password' + sweep), restored, git diff HEAD empty.
  • node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack: 66 commands, each run with its exit code captured before any pipe; all exit 0. pnpm check:i18n, pnpm check:dual-build-cjs-loads and pnpm check:lean-entry-closure first exited 3 (PREREQUISITE NOT MET, no build) and were re-run green after the build. --ran reconciliation: 66 derived, 66 run, 0 NOT-MEASURED, 0 UNRUN (a derived zero: all 66 carry an exit code). After the merge, the changeset, ADR-0087, keyed-text-bounds, tenancy-census, nul-bytes and type-check coverage/debt families were re-run on d46ae39da: all exit 0.

Acceptance notes

  • @objectstack/setup and @objectstack/account declare no test script; their filtered runs ran nothing.

Generated by Claude Code

@github-actions github-actions Bot added size/s documentation Improvements or additions to documentation tests tooling labels Oct 20, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/platform-objects, touching 3 documentable anchor(s).

3 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:

  • content/docs/permissions/authentication.mdx (via clientSecret (literal, a string literal in actions), currentPassword (literal, a string literal in actions), newPassword (literal, a string literal in actions))
  • content/docs/permissions/sso.mdx (via clientSecret (literal, a string literal in actions))
  • content/docs/plugins/index.mdx (via clientSecret (literal, a string literal in actions))
What this run could not see

Coarse fallback — 3 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json 81120caac889335467b6ecc6f17fcab57c46e6f5 → packageMentionDocs.

Which tree this was computed on

This run read content/docs from 62a4fc353cdee0a46055907dd68941ee551c374a — the merge of head d46ae39da0dc1392814088cfc412986ac453d528 into base 81120caac889335467b6ecc6f17fcab57c46e6f5, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 62a4fc353cdee0a46055907dd68941ee551c374a && git checkout 62a4fc353cdee0a46055907dd68941ee551c374a
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 81120caac889335467b6ecc6f17fcab57c46e6f5 d46ae39da0dc1392814088cfc412986ac453d528 && git checkout -B drift-repro 81120caac889335467b6ecc6f17fcab57c46e6f5 && git merge --no-ff d46ae39da0dc1392814088cfc412986ac453d528

node scripts/docs-audit/affected-docs.mjs --json 81120caac889335467b6ecc6f17fcab57c46e6f5

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

Advisory only, and a precision-first one (#9192): a page is listed because it names a
symbol, wire route or SDK method this diff touched — not because it mentions a changed
package. Each row says which anchor put it there, so a wrong row is reportable rather than
merely annoying. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs 81120caac889335467b6ecc6f17fcab57c46e6f5 → pass the list as
args.docs, on the commit named under Which tree this was computed on.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: TIER_DEFAULT
Head-sha: d46ae39da0dc1392814088cfc412986ac453d528
Local-runs: none
Scope: the net diff against the merge base 9a06f0be0f (5 files, +86/−12): .changeset/24758-password-param-type.md, packages/platform-objects/src/identity/sys-user.object.ts, sys-two-factor.object.ts, sys-sso-provider.object.ts, and packages/platform-objects/src/platform-objects.test.ts. Also the card #24758 with the claim 6289438399 and the dispatch 6289499030, the dev's os-dev-report 6290388041 (read as claims), the PR body, and the 41 check-runs on the head.

① Derived judgments

  • Twelve credential params move from type: 'text' to type: 'password': right. The card names two of them: create_user#password and set_user_password#newPassword. The other ten are the same defect in the same dialog:

    • change_my_password#currentPassword and #newPassword;
    • #password on delete_my_account, enable_two_factor, disable_two_factor and generate_backup_codes (sys_user);
    • sys_two_factor's three #password params;
    • register_sso_provider#clientSecret.

    The dispatch put every password-like text param in scope. Each hunk is the one token. Name, label, required, helpText and every neighbouring param are byte-identical.

  • Nothing the server accepts moves. ActionParamSchema.type is a FieldType, and password is one. By the dev's read, no server path reads an action param's type: the endpoints take the posted string, and MASKED_ON_READ_FIELD_TYPES and the plaintext warning judge fields only. The diff touches no endpoint, no body mapping and no bodyExtra, so the value is posted as typed.

  • The console masks it. By the dev's read of objectui at the pinned .objectui-sha, paramToField → resolveParamWidgetType → the password widget, a masked input with a show toggle. Not re-read here; nothing in this repo contradicts it.

  • clientSecret as password, not secret: right for a param. The type picks the widget, and secret's encrypt-at-rest meaning belongs to fields. better-auth still stores the secret encrypted, as its helpText says.

  • The pins have teeth.

    • A roster row per param asserts password.
    • A sweep over every ./identity/index.js export fails on a credential-named param (/password|secret/i, booleans excepted) that is not declared password. Its positive control asserts that the sweep reaches exactly the roster.
    • The two-leg ablation the dev reports (2 red each) matches these assertions as read.
    • The sweep is a pin in the package's own suite, not a new gate family.
  • No translation bundle moves: labels are unchanged, and check:i18n is green.

② Semver level

  • patch for @objectstack/platform-objects: right for a presentation change that moves no accept set.
  • Clause-②: no (…) is at a line start in the changeset (line 7) and the PR body (line 3). Check Changeset is green on this head.
  • Nothing widens: no export, no new key, no new field type.

③ Boundary flags

  • Deviation 1 (the surface widened to sys-two-factor.object.ts and sys-sso-provider.object.ts): accepted. Same defect class, one token each, pinned by the same sweep, no open PR on either file. All three files are this lane's.
  • Deviation 2 (dependent suites narrowed to plugin-auth, the endpoints' consumer; the rest left to CI's Test Core): accepted. No consumer pins a param's type (the pin sweep had zero hits), and CI's Test Core is green on this head.
  • Deviations 3 and 4: no bundle regeneration and no checklist edit. Confirmed by the file list.
  • Open questions: both taken as the dev settled them. A: all twelve land here. A: password for clientSecret.
  • Merge state: git merge-tree --write-tree against today's origin/main is clean, and so is the pair with sibling PR fix(platform-objects): Rotate Client Secret result dialog states the immediate cut-off, not a grace period #24870. The PR reads mergeable: clean.
  • Check-runs on d46ae39da0dc1392814088cfc412986ac453d528: ci-failure GREEN, all 41 completed, none failed.

Implemented-by: claude/issue-24758-password-param-type
Reviewed-by: session_01JtSQQ3EWsbNPzyYMug1tXy

VERDICT: PASS


Generated by Claude Code

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

席位核对 · domain:engine seat 1 · session_01JtSQQ3EWsbNPzyYMug1tXy · 2026-10-20T12:44Z · ACCEPT

Head: d46ae39da0. It carries the fix, the pins, the changeset and a merge of main.

Card #24758 (p2, bug, area:identity; from objectstack-ai/hotcrm#2188's Setup pass, F-10): claim 6289438399, dispatch 6289499030. Dev report: 6290388041.

Item Reading
Shape Draft PR → main. The body opens Fixes #24758. 5 files, +86/−12. Not governed: check-governed-merges matched 0 of 5 paths.
Scope Twelve credential action params on the identity objects declare type: 'password' instead of text, so the console's action dialog masks what is typed: the card's create_user#password and set_user_password#newPassword, the self-service password and two-factor params on sys_user and sys_two_factor, and register_sso_provider#clientSecret. Labels, names, required and the posted body are unchanged.
Seat's own read Each hunk is the one type token. The posted value and every endpoint are untouched. The sweep pin's positive control holds the roster exact. git merge-tree is clean against main and against sibling PR #24870.
Review TIER_DEFAULT, in-seat. Record 6291117267 is PASS on this head.
Declaration One changeset, @objectstack/platform-objects patch, Clause-②: no (…) at a line start, as on the claim and the PR body. No export, key or field type is added.
Cross-lane None: all three files are packages/platform-objects, this lane's. No checklist or bundle file. No objection on the card or the seat posts.
Gates Dev at d46ae39da: 66 derived, 66 run, all exit 0. platform-objects 1212 passed, plugin-auth 3260 passed, core test:repo 82 passed. Ablation: two legs, each red, restored to HEAD.
CI At this read on d46ae39da0: ci-failure GREEN, all 41 check-runs completed, none failed.

Disposition:

  • open_questions: both taken as the dev settled them (record ③): all twelve params land here; password for clientSecret.
  • deviations: all accepted (record ③). The surface widened to two sibling identity files of the same defect class. The dependent suites were narrowed to the endpoints' consumer.
  • out_of_scope_findings: none.

Landing: ordinary queue (pr_ready + automerge_enable through the relay). Fixes #24758 then closes the card.


Generated by Claude Code

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 20, 2026 12:46
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 20, 2026 12:46
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 20, 2026
Merged via the queue into main with commit 7035682 Oct 20, 2026
42 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-24758-password-param-type branch October 20, 2026 13:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/s tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Setup: the Create User and Set Password params are declared type text, so the admin's typed password shows in clear text

2 participants