Skip to content

fix(cli): a connection label printed by the CLI removes a password or a credential query pair wherever the URL rules read one - #30171

Merged
objectstack-fleet[bot] merged 5 commits into
mainfrom
claude/sec-hardening-y
Nov 4, 2026
Merged

objectstack-fleet[bot] merged 5 commits into
mainfrom
claude/sec-hardening-y

Conversation

@objectstack-fleet

@objectstack-fleet objectstack-fleet Bot commented Nov 4, 2026 •

Copy link
Copy Markdown
Contributor

Clause-②: no
Metadata-protocol: none

维护者速读

  • 改了什么:CLI 打印的数据库连接标签(os dev / os start / os serve 启动横幅与迁移确认)改为通过 spec 的共享 URL 凭据函数 redactUrlCredentials 去掉密码与凭据查询参数,不再自带一套规则或参数名单。
  • 为什么改:凡是平台 URL 规则读出凭据(userinfo 密码或凭据查询参数)的值,终端标签都必须去掉它;标签与数据源读门共用同一套规则,规则扩展时标签自动跟随。
  • 风险与回滚:仅影响打印文本;共享规则读不出凭据的值输出逐字节不变(已用对照用例钉住)。回滚即还原单个函数。
  • 先看:packages/cli/src/utils/connection-display.ts 的 redactConnectionUrl 前两行;connection-display.test.ts 新增的两个 describe 块。
  • 两分钟验证:pnpm --filter @objectstack/cli exec vitest run --maxWorkers=2 src/utils/connection-display.test.ts,预期 33 个用例全绿。
  • 需要你做的:无,席位落地。

What changes

  • redactConnectionUrl (the database label the CLI prints) first removes credentials through redactUrlCredentials from @objectstack/spec/data, the composite the datasource read path applies. That covers the userinfo password and every credential query pair judged by the shared query-pair judge and its one list of names. Rule: a connection label printed by the CLI removes a password or a credential query pair wherever the URL rules read one, on every path the label takes.
  • The CLI keeps no second grammar and no second list of names. The existing display shaping runs unchanged after that step: the URL parser drops the username and query string, and when the parser rejects the value the userinfo segment is dropped by hand.
  • @objectstack/cli already depends on @objectstack/spec (dependencies, workspace:*), so no dependency is added.
  • Changeset: @objectstack/cli patch.

Tests

Two new top-level describes in the existing harness file packages/cli/src/utils/connection-display.test.ts, named for the rule.

Password rule:

  • the password is removed wherever the shared grammar reads one, including a special scheme's authority;
  • the whole password is removed up to the boundary the shared grammar draws;
  • property pin: across a corpus of URL shapes, the label never contains a password that urlUserinfoPassword reads, and every shape in the corpus is judged;
  • controls, byte for byte against today's output.

Query rule:

  • a credential query pair is removed on each of the three paths: a value with no ://, the URL parser path, and the hand fallback;
  • property pin: the label never contains a value that urlCredentialQueryParams judges a credential;
  • controls, byte for byte, for values with no credential pair: a non-credential query on the fallback path and on a value with no ://, and an empty-valued pair.

Expected outputs for the controls were computed from the pre-change function before they were written into the pins.

Acceptance notes

  • One-shot ablations (not kept as files), each restored to the HEAD blob with git diff HEAD empty:
    • the password step replaced by the identity: the three password-rule pins go red and every control stays green;
    • the query half removed (the import aliased to the password-only function): 3 failed, 30 passed. The no-:// and hand-fallback pins and the query property pin go red. The URL-parser path pin stays green as expected, because that path already drops the whole query string.
  • The label follows any future widening of the shared grammar with no edit here; the property pins cover new shapes through their oracles.

Verification

  • connection-display.test.ts: 33/33 passed.
  • Query-token coverage: a credential query pair is pinned removed on the no-:// path, the hand-fallback path and the URL-parser path.
  • pnpm --filter @objectstack/cli typecheck: exit 0.
  • Full @objectstack/cli suite, both tiers, under the verify lock at 1e01266: Test Files 577 passed, 1 skipped (578); Tests 7074 passed, 9 skipped (7083); VERDICT command-exit 0.
  • Derived gate families (dispatch-gates.mjs --commands): 59 commands run locally, 56 exited 0. Three answered PREREQUISITE NOT MET (exit 3) because they need a whole-repo build: check:dual-build-cjs-loads, check:i18n-coverage, check:type-check-debt. Those three are NOT MEASURED locally and are left to Lint & Repo Gates in CI.

Generated by Claude Code

@github-actions github-actions Bot added size/m documentation Improvements or additions to documentation tests tooling labels Nov 4, 2026
@github-actions

github-actions Bot commented Nov 4, 2026 •

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

1 anchor(s) derived from 1 changed package(s); no hand-written page names any of them, so this run has nothing to list — not a clean bill of health. This check sees only pages that NAME a derived anchor: one that documents this change in prose, or enumerates it in an authoring dialect, names none and stays invisible to it on every run.

What this run could not see
  • the SDK route bridge reached 61 of 219 client-bound route-ledger rows — the other 158 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 158: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 55 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 103 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.
  • a key NAME is not a key, so the hand re-read the line above prescribes can land on the wrong schema. The same spelling is authorable on one governed type and a [REMOVED] tombstone on another for each of active, aria, joins, objects, template, tools and version (censused on [finding] tools is a key on BOTH AgentSchema (tombstoned, dead) and SkillSchema (live, cloud-attested), so a name-based search attributes skill examples to the agent key — it produced a false stop-the-line alarm on PR #19059 #19093 over the liveness ledger's governed types, top-level keys); nothing in a search result distinguishes the two, so a grep hit on a LIVE example reads as evidence about the DEAD key. Measured on fix(spec): the agent.tools liveness row says dead — it claimed live on a key the schema tombstoned #19059: content/docs/ai/agents.mdx was reported as contradicting the agent.tools tombstone over its tools: example at :161, which is inside the defineSkill({ block opened at :155 — the page was already correct. Settle ownership by PARSING the value against both schemas, never by the name: that literal PASSES SkillSchema, and as an AgentSchema it FAILS at tools with the tombstone prescription. ⛔ These names are not the whole class — a key retired through a .strict() guidance map leaves no tombstone in the walked shape and none of them here (tool.category, live as AIToolDefinition.category).

Coarse fallback — 35 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 9a53fc9feac04d44f40fffc62f93d04433de1227 → packageMentionDocs.

Which tree this was computed on

This run read content/docs from 8b8deb3f3f3d0a7c3a688c2c91660cb44cbaff43 — the merge of head 1e012660900c3768b722dde27d5343c959bb498f into base 9a53fc9feac04d44f40fffc62f93d04433de1227, 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 8b8deb3f3f3d0a7c3a688c2c91660cb44cbaff43 && git checkout 8b8deb3f3f3d0a7c3a688c2c91660cb44cbaff43
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 9a53fc9feac04d44f40fffc62f93d04433de1227 1e012660900c3768b722dde27d5343c959bb498f && git checkout -B drift-repro 9a53fc9feac04d44f40fffc62f93d04433de1227 && git merge --no-ff 1e012660900c3768b722dde27d5343c959bb498f

node scripts/docs-audit/affected-docs.mjs --json 9a53fc9feac04d44f40fffc62f93d04433de1227

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

@objectstack-fleet objectstack-fleet Bot changed the title fix(cli): a connection label printed by the CLI removes a password wherever the URL rules read one fix(cli): a connection label printed by the CLI removes a password or a credential query pair wherever the URL rules read one Nov 4, 2026
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

security#1 · os-justin · session_01QSeKYn5CmmdShCJifm6WYj

席位核对

ACCEPT · head 1e012660900c3768b722dde27d5343c959bb498f · 2026-11-04T20:52Z

  • Shape: 3 files, not governed, under 3000. Patch for @objectstack/cli, Clause-②: no (print text only, no accept set moves), Metadata-protocol: none.
  • Review: no contract face is touched, so the seat reviewed it in its own seat at TIER_DEFAULT and no second agent was owed.
  • Rule held (ruling D, feat(spec)!: a URL is judged as a URL parser cleans it — surrounding whitespace trimmed, tab and newline characters removed — with one userinfo grammar at both doors; a credential query parameter is refused in any string of a connector entry #29875, and the special-scheme ruling of 2026-11-04): the CLI connection label first runs every value through the platform's one URL credential composite (redactUrlCredentials from @objectstack/spec/data), which the datasource read door already applies. Only then does the label apply its display shaping, which is unchanged. The label therefore cannot print a password or a credential query pair that the doors would refuse or redact. No second grammar or name list is added.
  • Changeset prose, read against the diff: the rule sentence matches the one call. "Unchanged where no credential is read" holds: the composite returns its input unchanged in that case, and the controls pin it.
  • Check-runs at this head (latest per name): 15 skipped, 33 success. mergeable_state: clean.
  • Gates (dev):
    • dispatch-gates --ran: 59 derived, 56 run, 3 NOT MEASURED. All three are green in CI's Lint & Repo Gates.
    • Full cli suite: 577 files and 7074 tests passed.
    • Ablation: red as expected, then restored.
  • Seat edit: the title and body now name the query-pair half, so they match the changeset.
  • Landing: ready, then auto-merge.

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review November 4, 2026 20:53
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Nov 4, 2026
Merged via the queue into main with commit fec1f58 Nov 4, 2026
53 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/sec-hardening-y branch November 4, 2026 21:16
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/m tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant