Skip to content

feat(spec)!: refuse URL-embedded credentials (user:password@host) in driver config.url at publish (#8082) - #8341

Merged
hotlong merged 4 commits into
mainfrom
claude/issue-8082-url-userinfo-publish-refusal
Aug 13, 2026
Merged

feat(spec)!: refuse URL-embedded credentials (user:password@host) in driver config.url at publish (#8082)#8341
hotlong merged 4 commits into
mainfrom
claude/issue-8082-url-userinfo-publish-refusal

Conversation

@hotlong

Copy link
Copy Markdown
Contributor

Fixes#8082

Implements the maintainer-ruled Option A (on-card comment, 2026-08-12): driver config.url carrying userinfo credentials (user:password@host) is refused at publish, via one value-level parse shared by the URL-bearing driver schemas.

What changed

Test coverage

Per URL key (5 surfaces × the family suite): refusal of user:password@, ${…}-placeholder password, percent-encoded password, malformed double-@ password (caught WHOLE at the RFC 3986 boundary, mirroring the read-path redactor), userinfo before an IPv6 host; acceptance of bare-user, credential-free, and IPv6-without-userinfo forms byte-identically. Detector unit tests cover multi-host DSNs, scheme-relative URLs, path/query/fragment colons, file:/:memory:/bare strings, and the empty-password boundary.

Reverse verification (fix committed first): restoring the pre-ruling url lines flips the new rejection pins red while the acceptance pins stay green — direction as predicted; readings in the dev report on #8082.

Carve-out, verified on the tree

OS_DATABASE_URL / CLI / standalone-stack DSNs are translated into driver configs and handed to the driver factory directly (packages/runtime/src/standalone-stack.ts, packages/cli/src/utils/storage-driver.ts); they never meet the authoring schema — the same construction #8078 already relies on for the turso authToken env path. The refusal docs state this rather than implying it, per the ruling.

Out of scope, tracked separately (both remain open, neither is addressed here): the ${…} placeholder escape, tracked as #8336; the turso ?authToken= query-parameter door, tracked as #8337. #8081's stored-cleartext remediation is likewise untouched (#8126 already narrowed read exposure; the ruling calls for no sweep here).


Generated by Claude Code

…publish (#8082)
The #7990 closure refused the inline credential keys; #8078 measured and
pinned that config.url still accepted the identical secret one syntax over.
Maintainer-ruled Option A (2026-08-12): one value-level parse
(urlUserinfoPassword / credentialFreeUrl, data/driver/common.zod.ts) shared
by the four URL-bearing driver schemas — postgres/mysql/mongo url, turso
url + syncUrl — refuses a URL whose userinfo carries a non-empty password.
- Bare-user userinfo (user@host) stays accepted, matching #7990's posture
(username is a writable key; only the secret is refused) — and it is the
exact shape the #8126 read path serves for legacy rows, so an untouched
Save keeps working.
- The refusal message names the working mechanisms (secret binder /
external.credentialsRef), states the runtime-DSN carve-out explicitly
(OS_DATABASE_URL never passes the publish door), and warns that
${...} placeholders resolve to nothing (#8078, measured) instead of
steering authors into that broken escape.
- The #8078 acceptance pin (driver-credential-refusal.test.ts) is INVERTED
to a rejection pin, not deleted; the #8126 write-door acceptance pin in
service-datasource flips the same way (its own comment said it waited on
exactly this ruling).
- ADR-0087: D3 semantic entry datasource-config-url-userinfo-refused (no D2
conversion — a credential cannot be mechanically encrypted into
sys_secret); registry, spec-changes, upgrade guide, api-surface,
export-origins, reference docs regenerated.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Euoy6wyfzgiWtgCg4s6JK2
…ixture triage)
mongo.test.ts / postgres.test.ts 'accept config with connection URI' merely
used the userinfo spelling to demonstrate URI acceptance — re-spelled to the
bare-user form the rule still accepts (disposition: re-spell; the family
rejection pins live in driver-credential-refusal.test.ts).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Euoy6wyfzgiWtgCg4s6JK2
…egen step 4)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Euoy6wyfzgiWtgCg4s6JK2
@vercel

vercelBot commented Aug 13, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
ProjectDeploymentActionsUpdated (UTC)
objectstackIgnoredIgnoredAug 13, 2026 6:27am

Request Review

@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 2 package(s): @objectstack/service-datasource, @objectstack/spec.

106 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:

  • content/docs/ai/agents.mdx(via @objectstack/spec)
  • content/docs/ai/skills-reference.mdx(via @objectstack/spec)
  • content/docs/ai/skills.mdx(via @objectstack/spec)
  • content/docs/api/client-sdk.mdx(via @objectstack/spec)
  • content/docs/api/environment-routing.mdx(via @objectstack/spec)
  • content/docs/api/error-catalog.mdx(via @objectstack/spec)
  • content/docs/api/error-handling-client.mdx(via @objectstack/spec)
  • content/docs/api/error-handling-server.mdx(via @objectstack/spec)
  • content/docs/api/index.mdx(via @objectstack/spec)
  • content/docs/automation/approvals.mdx(via @objectstack/spec)
  • content/docs/automation/connectors.mdx(via @objectstack/spec)
  • content/docs/automation/flows.mdx(via @objectstack/spec)
  • content/docs/automation/hook-bodies.mdx(via packages/spec)
  • content/docs/automation/hooks.mdx(via @objectstack/spec)
  • content/docs/automation/index.mdx(via @objectstack/spec)
  • content/docs/automation/webhooks.mdx(via @objectstack/spec)
  • content/docs/automation/workflows.mdx(via @objectstack/spec)
  • content/docs/concepts/architecture.mdx(via @objectstack/spec)
  • content/docs/concepts/design-principles.mdx(via packages/spec)
  • content/docs/concepts/index.mdx(via @objectstack/spec)
  • content/docs/concepts/metadata-driven.mdx(via @objectstack/spec)
  • content/docs/concepts/metadata-lifecycle.mdx(via packages/spec)
  • content/docs/concepts/north-star.mdx(via @objectstack/spec)
  • content/docs/data-modeling/analytics.mdx(via @objectstack/spec)
  • content/docs/data-modeling/drivers.mdx(via @objectstack/spec)
  • content/docs/data-modeling/external-datasources.mdx(via @objectstack/spec)
  • content/docs/data-modeling/field-types.mdx(via @objectstack/spec)
  • content/docs/data-modeling/fields.mdx(via @objectstack/spec)
  • content/docs/data-modeling/formulas.mdx(via @objectstack/spec)
  • content/docs/data-modeling/index.mdx(via @objectstack/spec)
  • content/docs/data-modeling/objects.mdx(via @objectstack/spec)
  • content/docs/data-modeling/queries.mdx(via @objectstack/spec)
  • content/docs/data-modeling/schema-design.mdx(via @objectstack/spec)
  • content/docs/data-modeling/seed-data.mdx(via @objectstack/spec)
  • content/docs/data-modeling/validation-rules.mdx(via @objectstack/spec)
  • content/docs/data-modeling/validation.mdx(via @objectstack/spec)
  • content/docs/deployment/cli.mdx(via @objectstack/spec)
  • content/docs/deployment/tenancy-modes.mdx(via @objectstack/spec)
  • content/docs/deployment/troubleshooting.mdx(via @objectstack/spec)
  • content/docs/deployment/validating-metadata.mdx(via @objectstack/spec)
  • content/docs/getting-started/build-with-claude-code.mdx(via @objectstack/spec)
  • content/docs/getting-started/common-patterns.mdx(via @objectstack/spec)
  • content/docs/getting-started/examples.mdx(via @objectstack/spec)
  • content/docs/getting-started/quick-reference.mdx(via @objectstack/spec)
  • content/docs/getting-started/quick-start.mdx(via @objectstack/spec)
  • content/docs/getting-started/your-first-project.mdx(via @objectstack/spec)
  • content/docs/kernel/cluster.mdx(via @objectstack/spec)
  • content/docs/kernel/contracts/auth-service.mdx(via packages/spec)
  • content/docs/kernel/contracts/cache-service.mdx(via packages/spec)
  • content/docs/kernel/contracts/data-engine.mdx(via @objectstack/spec)
  • content/docs/kernel/contracts/index.mdx(via @objectstack/spec)
  • content/docs/kernel/contracts/metadata-service.mdx(via packages/spec)
  • content/docs/kernel/contracts/storage-service.mdx(via @objectstack/spec)
  • content/docs/kernel/index.mdx(via packages/spec)
  • content/docs/kernel/runtime-services/data-service.mdx(via @objectstack/spec)
  • content/docs/kernel/runtime-services/email-service.mdx(via packages/spec)
  • content/docs/kernel/runtime-services/examples.mdx(via @objectstack/spec)
  • content/docs/kernel/runtime-services/index.mdx(via packages/spec)
  • content/docs/kernel/runtime-services/queue-service.mdx(via packages/spec)
  • content/docs/kernel/runtime-services/sharing-service.mdx(via @objectstack/spec)
  • content/docs/kernel/runtime-services/sms-service.mdx(via packages/spec)
  • content/docs/kernel/runtime-services/storage-service.mdx(via @objectstack/spec)
  • content/docs/kernel/services-checklist.mdx(via @objectstack/spec)
  • content/docs/kernel/services.mdx(via @objectstack/spec)
  • content/docs/permissions/authorization.mdx(via @objectstack/spec)
  • content/docs/permissions/permission-sets.mdx(via @objectstack/spec)
  • content/docs/permissions/permissions-matrix.mdx(via @objectstack/spec)
  • content/docs/permissions/positions.mdx(via @objectstack/spec)
  • content/docs/permissions/rls.mdx(via @objectstack/spec)
  • content/docs/permissions/sharing-rules.mdx(via @objectstack/spec)
  • content/docs/permissions/system-context.mdx(via packages/spec)
  • content/docs/plugins/adding-a-metadata-type.mdx(via @objectstack/spec)
  • content/docs/plugins/development.mdx(via @objectstack/spec)
  • content/docs/plugins/index.mdx(via @objectstack/spec)
  • content/docs/plugins/packages.mdx(via @objectstack/spec)
  • content/docs/protocol/backward-compatibility.mdx(via @objectstack/spec)
  • content/docs/protocol/diagram.mdx(via packages/spec)
  • content/docs/protocol/kernel/config-resolution.mdx(via @objectstack/spec)
  • content/docs/protocol/kernel/http-protocol.mdx(via @objectstack/spec)
  • content/docs/protocol/kernel/i18n-standard.mdx(via @objectstack/spec)
  • content/docs/protocol/kernel/index.mdx(via @objectstack/spec)
  • content/docs/protocol/kernel/lifecycle.mdx(via @objectstack/spec)
  • content/docs/protocol/kernel/plugin-spec.mdx(via @objectstack/spec)
  • content/docs/protocol/knowledge.mdx(via @objectstack/spec)
  • content/docs/protocol/objectql/index.mdx(via @objectstack/spec)
  • content/docs/protocol/objectql/query-syntax.mdx(via @objectstack/spec)
  • content/docs/protocol/objectql/schema.mdx(via @objectstack/spec)
  • content/docs/protocol/objectql/security.mdx(via packages/spec)
  • content/docs/protocol/objectql/state-machine.mdx(via @objectstack/spec)
  • content/docs/protocol/objectui/actions.mdx(via @objectstack/spec)
  • content/docs/protocol/objectui/concept.mdx(via @objectstack/spec)
  • content/docs/protocol/objectui/index.mdx(via @objectstack/spec)
  • content/docs/protocol/objectui/layout-dsl.mdx(via @objectstack/spec)
  • content/docs/protocol/objectui/record-alert.mdx(via @objectstack/spec)
  • content/docs/protocol/objectui/widget-contract.mdx(via @objectstack/spec)
  • content/docs/ui/actions.mdx(via @objectstack/spec)
  • content/docs/ui/apps.mdx(via @objectstack/spec)
  • content/docs/ui/create-vs-edit-form.mdx(via @objectstack/spec)
  • content/docs/ui/dashboards.mdx(via @objectstack/spec)
  • content/docs/ui/field-grouping-and-order.mdx(via @objectstack/spec)
  • content/docs/ui/forms.mdx(via @objectstack/spec)
  • content/docs/ui/index.mdx(via @objectstack/spec)
  • content/docs/ui/public-data-collection.mdx(via @objectstack/spec)
  • content/docs/ui/setup-app.mdx(via @objectstack/spec)
  • content/docs/ui/translations.mdx(via @objectstack/spec)
  • content/docs/ui/views.mdx(via @objectstack/spec)

7 release-owned page(s) also reference the affected code. These are read-only:

  • content/docs/releases/implementation-status.mdx(via @objectstack/spec)
  • content/docs/releases/index.mdx(via @objectstack/spec)
  • content/docs/releases/v12.mdx(via @objectstack/spec)
  • content/docs/releases/v13.mdx(via @objectstack/spec)
  • content/docs/releases/v16.mdx(via @objectstack/spec)
  • content/docs/releases/v17.mdx(via @objectstack/spec)
  • content/docs/releases/v9.mdx(via @objectstack/spec)

content/docs/releases/ is RELEASE-OWNED (AGENTS.md "Documentation Guardrails"): release
notes are written centrally at release time, and a code PR that edits them is the exact PR
that guardrail exists to stop. They are still audited — read-only. If one of them is actually
wrong, file an issue or open a dedicated docs-only PR; do not edit it here.

Advisory only. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs origin/main → pass the list as args.docs.

@github-actionsgithub-actionsBot added size/l documentation Improvements or additions to documentation protocol:data tests tooling labels Aug 13, 2026
@hotlong
hotlong marked this pull request as ready for review August 13, 2026 08:52
@hotlong
hotlong added this pull request to the merge queueAug 13, 2026
Merged via the queue into main with commit d88f3e9Aug 13, 2026
27 checks passed
@hotlong
hotlong deleted the claude/issue-8082-url-userinfo-publish-refusal branch August 13, 2026 09:09
hotlong pushed a commit that referenced this pull request Aug 13, 2026
…tree (#8116 x #8341/#8335)
Discharges the os-regen deferral from the preceding merge commit: the
data.json shards regenerate with both sides present — this PR's 10 new
provenance exports and main's landed entries (zodIssuesToFields intact
in the api.json shards).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Euoy6wyfzgiWtgCg4s6JK2
os-zhuang pushed a commit that referenced this pull request Aug 13, 2026
…gain (#8315)
Third lap of merge-then-regenerate; main moved 25 commits while CI was green and
the arm was held. The conflict-free merge again left the two driver-managed
prose projections stale, this time missing `datasource-config-url-userinfo-refused`
(#8082 / PR #8341): present in origin/main's copies, absent after the merge,
restored by regeneration.
Round-2's prediction held exactly. `registry.ts` is byte-identical to the plain
merge (79 semantic entries) because it is NOT_DRIVER_MANAGED and #8341 added a
NEW entry file — the #7297 per-entry-file sharding removes the conflict at the
source. The two `merge=os-regen` projections are the ones that needed the
generator: spec-changes.json +14 lines, protocol-upgrade-guide.md +3.
Regeneration only — no code change, no changeset reclassification, no ratchet.
Merge committed first, then:
pnpm --filter @objectstack/spec gen:migration-registry
pnpm --filter @objectstack/spec gen:spec-changes
pnpm --filter @objectstack/spec gen:upgrade-guide
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PEVB6w7D7uCszR9Mw1BL73
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationprotocol:datasize/lteststooling

Projects

None yet

2 participants

@hotlong@claude