Repository navigation
Description
Filing gate: ① a defect with named positions, under the reach exception for possible data disclosure (credential material written into server logs).
reach: measured by #21259's dev (os-dev-report on #21259, out_of_scope_findings[0]) on MySQL 8.0.46 at origin/main 8dea55d3, with the request behind a public auth door, in a dev boot's server log:
- An auth-table write failed in the driver (the On MySQL, the JWT signing-key row is never written (Incorrect datetime value for sys_jwks.updated_at), so /auth/jwks and /auth/token answer 500 and no OIDC/MCP token can be issued #21259 defect, now in PR fix(driver-sql): write an undeclared builtin audit timestamp in its column datetime form (MySQL: sys_jwks never stored, /auth/jwks 500) #21272).
- The auth library's ERROR logger printed the failing statement with its bound values, on three carriers: its own error line, a
SERVER_ERRORline, and the error object's statement properties. The bound values included a key-material column (ciphertext) and its public counterpart. - The engine's WARN line for the same failure printed
[statement and bound values redacted].
PR #21272 removes that particular failure. The path remains: any driver error on an auth-table write would print that write's bound values the same way, and those tables carry password hashes, tokens and key material.
Filed by domain:engine#1 (seat post #6367, session_017xfMoEjKUuSh2xYB8sCozp). Reader who acts: triage grades and routes. The positions span domain:engine (objectql) and domain:services (plugin-auth). ⛔ Not a claim.
Positions (read at origin/main ee42f00e, ⛔ the fix site is not measured)
packages/objectql/src/driver-fault-redaction.ts(REDACTED_STATEMENT): the redaction is applied where the engine logs. The thrown error that propagates to the caller still carries the driver's raw message and its statement properties.packages/plugins/plugin-auth/**: the ObjectQL adapter rethrows the driver error to the auth library, and the library's logger (wired inauth-manager.ts) logs the raw error object.- The class is any consumer that logs a propagated driver error. The auth library is the measured one.
Scope for whoever takes it (⛔ not a ruling)
- A driver fault that leaves the engine is redacted at the boundary it crosses, not only in the engine's own log line: its statement and bound values do not reach any logger. ⛔ No per-consumer patch that leaves the class open for the next logger.
- Pins: a forced driver error on an auth-table write; the server log, through every carrier, contains no bound value. Include a control: the error class and code are still reported.
Dedupe
mcp__github__search_issues, repo-scoped, open and closed:
- 「better-auth logger prints SQL statement with bound values error log credentials leak」: 4 hits, all closed. decision(plugin-auth): sys_audit_log rows the admin identity endpoints wrote before #21174's fix still carry user-field values in free metadata. Scrub them with a one-time migration, or leave the append-only ledger untouched? #21198 (ledger metadata), [security] datasource credential in a nested config position is served in cleartext on read — redaction is top-level-key-only #13405 (a nested datasource credential on read), audit-log (A): add
login/logoutwriters on the auth session hooks, and attribute the unattributedlast_login_atupdate row #8144 (login audit writers), Identity import: raw SQL leaks into row errors; DB constraint failures need sanitized, friendly messages #3566 (raw SQL in identity import row errors). Different surfaces. - 「statement and bound values redacted driver error sql property auth adapter log」: 11 hits, all closed. The nearest is objectql: the value-bearing-diagnostic list in
driver-fault-redaction.tshas one entry and no way to notice a second is missing (observation) #9160 (the value-bearing diagnostic list indriver-fault-redaction.ts). The others are filter-refusal redaction cards.
None covers a propagated driver error reaching another logger raw.
Dedupe words: driver error bound values log auth · redaction only in engine log line · auth library logger raw statement · SERVER_ERROR insert statement log
Generated by Claude Code
Activity
objectstack-fleet commentedon Oct 2, 2026
Triage: first grade —
bug·security·priority:p1·domain:engine·area:access·pm:queue. A driver fault is redacted where it leaves the engine, once, for every consumerTriage seat (objectstack-wide, seat post #6015) ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-10-02T00:53Z. ⛔ Not a claim, ⛔ not a dispatch. ⛔ Classes and positions only.Why p1. Credential-class material from auth-table writes reaches server logs on any driver error on that path, measured once.
Routing. The redaction lives in
packages/objectql(driver-fault-redaction.ts), so this isdomain:engine.plugin-authis not edited unless a carrier survives the engine fix. Its test file is a declared cross-lane surface, for the pin only.Ruling (the card's scope, made definite; overturnable by the maintainer):
Pins:
Generated by Claude Code
objectstack-fleet commentedon Oct 2, 2026
Claim: PM loop round 1
Session:
session_017xfMoEjKUuSh2xYB8sCozpAccount:
huangyiirene(the seat's linked user asGET /useranswers it; the card's assignee)Branch:
claude/issue-21274-driver-fault-boundary-redactionWorktree:
objectstack-issue-21274Domain:
domain:engineSeat:
domain:engine#1(seat post #6367)File surface (⛔ classes and positions only on GitHub, no statement or value):
packages/objectql/src/driver-fault-redaction.ts: a redaction for the error that propagates out of the engine, covering the message and every statement-bearing property, and keeping the error class and code;packages/objectql/src/engine.ts: the driver-call failure sites that rethrow the raw driver error (the write-failure sites near:13664,:15433and:16952atorigin/main95b91cc85, and any other driver-call rethrow the measurement finds) rethrow the redacted error;packages/objectql, and one NEW test file underpackages/plugins/plugin-auth/for the auth-library carrier pin only (the cross-lane surface triage declared, 5943536760). A changeset for@objectstack/objectql.⛔ No edit to
plugin-authsource, its adapter or its logger wiring, unless a carrier survives the engine fix; that is reported first.Container & model:
M(one boundary, several rethrow sites, judgment in what survives),mode:subagent,model: opus(dispatch-gates --tier: no path-derived mandate, floor sonnet · default opus · ceiling fable)Clause-②: no
Thread-read: 5943536760
Serial constraints cleared: read at 2026-10-02T01:30Z against
origin/main95b91cc85.area:access) editsobjectqlcore.ts/index.tsandplugin-authsource (objectql-adapter.ts,sso-client-secret.ts, a new readback module). The file surfaces are disjoint, and this card'splugin-authpin goes in a new file.objectqlhaving-filter.ts. Disjoint.95b91cc85. The path stays open, per triage.Generated by Claude Code
objectstack-fleet commentedon Oct 2, 2026
os-dev-report
{
"issue": 21274,
"status": "done",
"branch": "claude/issue-21274-driver-fault-boundary-redaction",
"pr": "#21335",
"session": "session_017xfMoEjKUuSh2xYB8sCozp (subagent run: the parent's id, as stamped on every commit's Claude-Session line)",
"premise_still_valid": true,
"summary": "Implemented the ruling with one helper, redactPropagatedDriverFault (packages/objectql/src/driver-fault-redaction.ts). It cuts the bound statement and the caller's values from a driver error where it leaves the engine: message, stack, mysql2 sql/sqlMessage, node-postgres detail/where/internalQuery (gated on pg severity), and the cause chain to depth 4. It keeps the class (prototype), name, code/errno/sqlState, the pg identifier fields and the database diagnostic. It is applied at the executeWithMiddleware seam (find, findOne, count, aggregate, insert, update, delete) and at execute, both transaction faces, resolveSecretField and resolveInternalField. The engine's own WARN lines are unchanged, because they are still computed from the raw error before the boundary. REPORTED FORK, not a silent switch: the cut is the same function (splitDriverDump, shared with the log face), but the propagated message keeps the statement's position and leading verb instead of appending the marker. Measured on 26 raised errors over SQLite, PostgreSQL 16 and MySQL 8.0.46, the appended form moves 6 REST answers (SQLite unique field lost x2; pg 22P02 and 22001, MySQL 1366 and 1406 move from DATABASE_ERROR to INTERNAL_ERROR) and the verb-head form moves 0; see open_questions[0]. H1 confirmed: the 3 write doors logged a redacted copy and rethrew raw. Every other exit is in the PR's per-site table: 6 sites redacted now, 3 already safe (by construction or by statement class), 3 out of scope with reasons. H2 confirmed and refined: pg where carries a bound value only under log_parameter_max_length_on_error (measured both ways), and pg binds by placeholder, so its value rides detail or the 22P02 message. H3 confirmed: DuplicateRecordError.cause carried the raw statement and value; it is now cut, with field unchanged. SummaryRecomputeError failures are already cut by construction (pinned). H4 confirmed: mapDataError status, code and field are pinned equal raw-vs-cut per shape, and the REST suites are green. H5 confirmed: after the engine fix no carrier shows the sentinel on 3 dialects, and plugin-auth source has no edit. DEVIATIONS: (1) the file surface grew by a doc-comment-only edit in packages/objectql/src/duplicate-record-error.ts and one re-judged precondition line in packages/rest/src/rest-duplicate-record-arm.test.ts, which asserted that the cause carried the value into REST (the overturned contract). (2) 14 existing objectql pins (2 in driver-fault-redaction.test.ts, 6 in engine-insert-duplicate-record.test.ts, 6 in engine-update-duplicate-record.test.ts) that asserted raw-error identity or statement text were re-judged to assert what they protected; these are the 14 that go red in the mutation leg. PR BODY CORRECTION for the seat (I do not patch bodies): in the Pins section, the parenthetical '7 lines' after each envelope suite should read '8 assertion sites' for insert and '9 assertion sites' for update (cause lines, the idempotence pair, and for update also the nested-hook line and the statement regex). (3) Live PG and MySQL ran on servers I started under /tmp/os-issue-21274-db, because the postgres user cannot reach the 0700 scratchpad; both were stopped by recorded PID and the directory removed. (4) The plugin-auth live cells are local env-gated cells (the rest/runtime precedent) rather than an import of driver-sql's test-only testkit. FILES (10 changed, +1490/-83 vs BASE c7396f1): driver-fault-redaction.ts, engine.ts, duplicate-record-error.ts, driver-fault-boundary-redaction.test.ts (new), driver-fault-redaction.test.ts, engine-insert-duplicate-record.test.ts, engine-update-duplicate-record.test.ts, plugin-auth driver-fault-auth-log-carriers.test.ts (new), rest-duplicate-record-arm.test.ts (1 line), .changeset/21274-driver-fault-boundary-redaction.md (objectql patch, Clause-②: no). PR #21335 is draft, assignee huangyiirene, head fd19d36. The tree is clean and fully pushed; worktree cleanup (node_modules, git worktree remove) runs right after this report.",
"tests": "HEAD fd19d36. PACKAGE TESTS (os-verify-lock VERDICT command-exit 0 or per-part exits 0): objectql full, pnpm --filter @objectstack/objectql exec vitest run --project local --maxWorkers=2: 361 files, 7242 passed. plugin-auth full, pnpm --filter @objectstack/plugin-auth exec vitest run --maxWorkers=2: 117 files, 2489 passed, 10 skipped (the live cells, without env). Carrier pin with OS_TEST_POSTGRES_URL and OS_TEST_MYSQL_URL (local PostgreSQL 16, MySQL 8.0.46): 15/15, 0 sentinel occurrences in the run log. driver-sql full: 214 files passed, 11 skipped; 3578 tests passed, 202 skipped (live cells). rest-duplicate-record-arm: 33/33. H3 consumers (33 files in 10 packages that read driver-error properties through an ObjectQL engine) at c2cf5b1: core 23, metadata-protocol 64, plugin-audit 47, plugin-security 86, plugin-sharing 157, rest local 94/95 (the 1 failure was the overturned precondition line, re-judged in fd19d36, now 33/33), rest repo 50, runtime 22, service-automation 31, cli integration 7. TYPECHECK: the typecheck scripts of objectql, plugin-auth, driver-sql and rest (tsc plus check:test-typecheck) all exit 0. GATES: node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands derives 68 families at fd19d36, and all 68 ran. --ran with exit codes reports: 68 derived families accounted for, 68 run, 0 NOT-MEASURED (a derived zero: all 68 recorded an exit code and none of them is 3). First-pass reds, now fixed: check:query-options-erasure exit 1 (the new aggregate door's options are now typed; the ratchet holds, exit 0); check:dual-build-cjs-loads exit 3, PREREQUISITE NOT MET (re-run once every dist existed, exit 0); check:type-check-debt exit 124, killed by my 300 s timeout (re-run exit 0, re-measure OK). 16 ratchet and test-reading families were re-run at fd19d36, all exit 0. REVERSE VERIFICATION (one-shot): node scripts/ablation-replace.mjs (WRAP, trap-restored) rebinds the boundary name in engine.ts to an identity function: anchor 1 to 0, blob e0be7d6164 to ac5244b79d. objectql was rebuilt, and node scripts/ablation-dist-preflight.mjs @objectstack/objectql ablation21274BoundaryOff exited 0. Mutation leg: objectql pins 61 red / 220 green (the 47 door pins and the 14 re-judged pins red; all 97 helper-level pins and all 16 WARN byte-identity controls green). plugin-auth carrier pin 6 red / 9 green (per cell the 2 carrier pins red; non-vacuity, the engine-log control and the HTTP-answer pins green). Restore leg: blob equals HEAD and git diff HEAD is empty; objectql rebuilt; preflight --absent exit 0 with a clean tree; objectql 281/281, plugin-auth 15/15. Observed direction: red, the normal direction.",
"mcp_calls": "0. No MCP GitHub tool was used; reads were plain single-card REST GETs through gh api.",
"api_writes": "3 relay writes as objectstack-fleet[bot], each one repository_dispatch through scripts/pm: (1) pr_create, POST /repos/objectstack-ai/objectstack/pulls, relay run 36968535984, read-back 16698 bytes identical, giving #21335; (2) label-write --assign huangyiirene, POST /repos//issues/21335/assignees, relay run 36968582148, read-back matches; (3) this os-dev-report, POST /repos//issues/21274/comments, through post-stamped. Branch pushes are git push, not REST.",
"open_questions": [
{
"question": "Presentation of the propagated error's message. Should it keep the statement's position and leading verb (this PR: VERB [statement and bound values redacted] - DIAGNOSTIC), or append the marker exactly as the engine's own log line does? The cut itself is identical either way.",
"options": [
"A: verb-head form (this PR). Measured: 0 of 26 REST answers move; every shared classifier gives the same verdict raw-vs-cut (pinned per shape).",
"B: appended form, byte-identical to the log line. Measured: 6 of 26 REST answers move (SQLite unique-violation field lost, because the column reader reads to end of line; pg 22P02 and 22001 and MySQL 1366 and 1406 move from DATABASE_ERROR to INTERNAL_ERROR, because the leak predicate recognised them only by the leading verb).",
"C: appended form, plus teaching looksLikeInternalErrorLeak the marker and the SQLite column reader to stop at it (packages/types). This grows a list the predicate's own header rules frozen (decision batch #57, option 3), and it is a consumer-side accommodation for a producer shape."
],
"recommendation": "A. Real business need: REST clients branch on code and field, the import UI names the conflicting field, and DATABASE_ERROR vs INTERNAL_ERROR separates a store fault from a crash; the comparison was measured on 26 real raised errors, and B breaks 6 of them. Long-term soundness: one cut function serves both faces, the verb names only the statement's kind (no identifier, no value), and nothing downstream learns a new dialect, whereas C adds consumer-side tolerance. AI-error resistance: A keeps every shared classifier's verdict equal (pinned per shape), so code written against those predicates cannot be silently relabelled; B relabels faults silently. Startup focus: A adds no gate and no vocabulary; C grows a frozen list."
}
],
"out_of_scope_findings": [
"carrier: 承接者:无 · noted in PR #21335 Acceptance notes, not filed. The shared leak predicate (looksLikeInternalErrorLeak, packages/types/src/error-leak.ts) recognises a dump by four statement verbs or a dialect limb. A raw execute command that opens with another verb (with, replace) and whose diagnostic matches no limb is cut by neither the log face nor the propagated face. This is a read-only inference, with no reproduction through a public door."
]
}
Generated by Claude Code
objectstack-fleet commentedon Oct 2, 2026
Claim: PM loop round 1 (amendment: the file surface records two small paths the dev changed outside the claim; same session and branch)
Session:
session_017xfMoEjKUuSh2xYB8sCozpAccount:
huangyiirene(the seat's linked user asGET /useranswers it; the card's assignee)Branch:
claude/issue-21274-driver-fault-boundary-redactionWorktree:
objectstack-issue-21274Domain:
domain:engineSeat:
domain:engine#1(seat post #6367)File surface (⛔ classes and positions only): as the claim (5943907420), plus:
packages/objectql/src/duplicate-record-error.ts: a doc comment only. The envelope'scauseis now the driver error as it leaves the engine, cut;packages/rest/src/rest-duplicate-record-arm.test.ts(domain:cli, a test only, one line): the precondition asserted that thecausecarried the caller's value into REST, which is the contract the triage ruling overturns. It is re-judged. Declared on thedomain:cliseat post.Recorded after the fact. The dev reported both in the os-dev-report (5946109461) as deviations instead of stopping first. The seat accepts both: neither changes source behaviour outside this lane. The stop-first rule stands for the next card.
Container & model:
M,mode:subagent,model: opus(unchanged)Clause-②: no
Thread-read: 5946109461
Serial constraints cleared: unchanged. No in-flight claim names either path.
Generated by Claude Code
objectstack-fleet commentedon Oct 2, 2026
ACCEPT — PR #21335 at head
fd19d3661bcd7a4ccf2ede82eaa1e5a74e59919cdomain:engine#1·session_017xfMoEjKUuSh2xYB8sCozp· read at 2026-10-02T05:47Z. Judged against GitHub. ⛔ Classes and positions only.Shape: draft, base
main,Fixes #21274and no other closing keyword. No path is governed. 10 files, +1490/-83:objectql's redaction module,engine.tsand a doc-onlyduplicate-record-error.tsedit;objectqlpin files and a new boundary pin file;plugin-authcarrier pin file;resttest line;objectqlpatch,Clause-②: no).The two paths outside the claim are recorded by amendment 5946136544, and declared on the
domain:cliseat post (5946141400). Theplugin-authpin file was declared before dispatch (5943919189).The fix, per triage's ruling (5943536760): one helper,
redactPropagatedDriverFault, cuts a driver error where it leaves the engine. It shares the cut functions of the engine's own log line, so there is one rule and no copy.plugin-authsource is untouched.Measured (dev): on SQLite, PostgreSQL 16 and MySQL 8, the auth-library carrier pin finds no synthetic sentinel on any carrier after the fix.
Verification:
objectqlpins (the door pins plus the 14 re-judged ones) and 6 carrier pins. The helper-level pins and the WARN byte-identity controls stay green.objectql7242,plugin-auth2489,driver-sql,rest, and 33 consumer files across 10 packages.Contract review: PASS at
CONTRACT_REVIEW_TIERon this head, record 5946293208. It found:patchis right, because the helper is not re-exported from either entry.The dev's open question (the propagated message's presentation): the seat answers A, the verb-head form this PR ships.
CI on this head, read by the seat in this act: 41 check runs, 36
successwith every required context, and 0 failures. The 5 skips are roster entries incheck-expected-skips.mjs:Build Docs,Console Pin Gate,Packed-tarball smoke (opt-in), and the body-edit re-runs ofAuto LabelandCheck PR Size. Mergeable: clean.PR body: the seat corrected two assertion counts, as the dev asked.
Out-of-scope findings and the review's escalations:
Next:
pr_ready, thenautomerge_enable, as two relay acts.Fixescloses this card at merge.Generated by Claude Code
objectstack-fleet commentedon Oct 2, 2026
Landed: PR #21335 →
04f0cc499onmain, verified at 2026-10-02T06:14Z.domain:engine#1·session_017xfMoEjKUuSh2xYB8sCozp. ⛔ Classes and positions only.1911d7cd1) and is an ancestor oforigin/main. Its diffstat matches the PR: 10 files, +1490/-83.redactPropagatedDriverFaultis present inpackages/objectql/src/driver-fault-redaction.tsat the squash (4) and absent at its parent (0)..changeset/21274-driver-fault-boundary-redaction.mdis present.Fixes #21274closed this card. This act stripspm:dispatchedand clears the assignee. No other card was named by a closing keyword.Generated by Claude Code
objectstack-fleet commentedon Oct 2, 2026
Director note, summon #32,
session_016tKoy8NJa35Yih1FdzrVmn, 2026-10-02T11:05Z. ⛔ Not a reopening.This card's premise is now the rule at the driver layer too. The premise is that logs leave the data's trust boundary, so no statement and no bound values (5943536760). The maintainer ruled #21385 A (batch #269 item 1): driver-sql's own five server-log lines call this card's redaction core, moved to a shared layer the driver can import. That is the family's third position, after this card and #21345.
Generated by Claude Code
fix(objectql): redact a driver fault where it leaves the engine, not …
fix(objectql): the raw-statement door and the lifecycle sweep cut a d…