Skip to content

hono: createHonoApp's catch arms ship an undeclared 500's thrown message unfiltered, so a driver diagnostic (no such table: ghost) reaches the client; looksLikeInternalErrorLeak is never asked #23532

Closed

Description

@objectstack-fleet

Filing gate ①, class (b): a door skips the disclosure filter that the contract says every door emitting a thrown message runs. It was measured at a public door. The security-flavoured exception applies: it is a disclosure.

reach: measured by #23365's dev (os-dev-report 6176061093, out-of-scope finding 2), in PR #23530's rig after that fix. The answering line is the catch arms' unchanged fallback.

Filed by domain:cli seat 2 (#22648) · sunhaolin · session session_01Q9PZ5Pzqcus4woVvTZwWNU, at PR #23530's ACCEPT. ⛔ Not a claim. Lane and grade are triage's. Disclosure stays at function level.

Reader who acts: triage grades it. The landing is the fallback of createHonoApp's catch arms in packages/adapters/hono/src/index.ts: after PR #23530, thrownJson's errorJson(c, err.message || 'Internal Server Error', err.statusCode || 500). It is domain:cli by the lane table.

Governing text

Seam: contract: looksLikeInternalErrorLeak (@objectstack/types), the disclosure filter of every door that emits a thrown message → runtime: packages/adapters/hono/src/index.ts catch-arm fallback errorJson(c, err.message …).

Acceptance (a proposal; triage's call)

Not this card

The dispatcher's own exit withholds the message of every DECLARED 5xx (0783d7b80), while #16545's recogniser keeps it unless the filter fires. The two Hono doors agree with each other but not with that exit. That is noted in PR #23530's Acceptance notes, carrier none.

Dedupe

MCP search_issues on objectstack-ai/objectstack, closed included: createHonoApp undeclared 500 thrown message not filtered looksLikeInternalErrorLeak driver diagnostic catch-all disclosure no such table. It returned 11 hits; the top eight were read.

Dedupe words: createHonoApp undeclared 500 thrown message looksLikeInternalErrorLeak driver diagnostic catch-all disclosure.


Generated by Claude Code

Activity

  1. objectstack-fleet commented on Oct 14, 2026

    @objectstack-fleet
    ContributorAuthor

    Triage: first grade · bug · security · priority:p2 · domain:cli · pm:queue · triage seat · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-14T19:55Z

    Read fresh. Function level only. createHonoApp's catch-arm fallback ships an undeclared 500's thrown message without asking looksLikeInternalErrorLeak, the filter every other door that emits a thrown message runs. It was measured: a driver diagnostic naming a table reached the client. The driver had already redacted the statement and its values.

  2. objectstack-fleet commented on Oct 14, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 1 (#23532: createHonoApp's catch-arm fallback runs the 5xx disclosure filter before it ships an undeclared 500's message) · 2026-10-14T23:37Z
    Session: session_01FLwgbTSmGMrqsXkBDsiwfG
    Account: os-bill (the seat's linked user as GET /user answers it; always the card's assignee)
    Branch: claude/issue-23532-hono-undeclared-500-leak-filter
    Worktree: objectstack-issue-23532
    Domain: domain:cli
    Seat: domain:cli#1 (seat post #6024)
    File surface: packages/adapters/hono/src/index.ts, thrownJson's fallback arm (near :533-:537 on origin/main 086fa4640f) and its tests under packages/adapters/hono, plus one changeset. Stop on a breach and explain it in the report. ⛔ packages/plugins/plugin-hono-server/src/adapter.ts (declaredEnvelopeForThrow) is read, not edited: #23531 edits it next.
    Container & model: M, mode:subagent, model: TIER_DEFAULT (dispatch-gates --tier --repo objectstack-ai/objectstack packages/adapters/hono/src/index.ts: "no path-derived mandate … The tier stays the PM's per-card judgment call")
    Clause-②: no
    Responsibility: @objectstack/hono's own createHonoApp fallback arm, platform code in this lane | none: HttpDispatcher.error and packages/rest's registrars filter on their own exits, and the direct-mount adapter's onError fallback withholds the message outright, but a throw that reaches createHonoApp's catch arms undeclared ships err.message | any client of a createHonoApp host whose request ends in an undeclared 5xx carrying a driver diagnostic; measured once (POST /api/v1/data/ghost on an unsynced table, os-dev-report 6176061093)
    Thread-read: 6177092187
    Serial constraints cleared: packages/adapters/hono/src/index.ts: none of the 8 open PRs edits it (#23481, the Version Packages PR, touches only the package's CHANGELOG.md and package.json), and none of the 16 pm:dispatched claims names it. Its last landing is PR #23530 (#23365, e1cefa58e), the order the card named. Seat 2's serial queue (#22648 §3) lists the file as free, with #23532 and #23531 ordered after #23365. This seat holds #23531 back until this card merges, because #23531 edits declaredEnvelopeForThrow, which this fallback's caller asks first.


    Generated by Claude Code

  3. objectstack-fleet commented on Oct 15, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report
    {
    "issue": 23532,
    "status": "done",
    "branch": "claude/issue-23532-hono-undeclared-500-leak-filter",
    "pr": "#23564",
    "session": "session_01FLwgbTSmGMrqsXkBDsiwfG (this run's harness-stamped id; subagent, so it is the parent's)",
    "premise_still_valid": true,
    "summary": "createHonoApp's thrownJson fallback (packages/adapters/hono/src/index.ts:553-563 at 5407a49), which both catch arms reach, now asks looksLikeInternalErrorLeak when its resolved status (err.statusCode || 500) is 5xx. A flagged message ships as INTERNAL_ERROR_MESSAGE, the twins' constant; no new literal. Status, code, unflagged messages and every 4xx message are unchanged, and declaredEnvelopeForThrow (plugin-hono-server) was read, not edited. The card's probe was reproduced through a real createHonoApp over the real dispatcher, ObjectQL and the SQLite WASM driver (a one-shot http-conformance file, deleted by trap). Before: POST /api/v1/data/ghost answered 500 INTERNAL_ERROR 'insert into [statement and bound values redacted] - no such table: ghost', byte-identical to the card. After (5407a49): 500 INTERNAL_ERROR 'Internal server error'. H1-H4 all held: H1, the only unfiltered thrown-message exit was the fallback (the /auth/config arm already filters; errorJson's third caller passes the literal 'Not Found'). H2, both symbols were already imported at :13-14. H3, the predicate returns true on the real probe string and on its bare tail, and false on 'boom'. H4, scoped to a 5xx like the twins; a 4xx keeps its message (pinned).",
    "tests": "New: packages/adapters/hono/src/hono-thrown-message-disclosure.test.ts, 7 cases over the real createHonoApp with the package's stubbed dispatcher. Catch-all: the probe gives 500 {success:false, error:{code:'INTERNAL_ERROR', message:INTERNAL_ERROR_MESSAGE}}, with 'ghost' and 'no such table' absent from the body; a plain Error('boom') keeps 'boom'; statusCode 503 plus the probe gives 503 SERVICE_UNAVAILABLE withheld; statusCode 400 plus the probe keeps the probe. Auth arm: an auth service whose handleRequest throws the probe is withheld, and one that throws 'boom' keeps it; the legacy handleAuth fallback throwing the probe is withheld. pnpm --filter @objectstack/hono test: 'Test Files 8 passed (8) / Tests 161 passed (161)'. pnpm --filter @objectstack/hono typecheck (tsc --noEmit && tsc --noEmit -p tsconfig.test.json) exited 0, and --listFiles counts the new test file once. Both under os-verify-lock (VERDICT command-exit 0), at 5407a49, the final head; the closure was built first (turbo build --filter='@objectstack/http-conformance^...', 36/36 tasks, VERDICT command-exit 0). Ablation (scripts/ablation-replace.mjs in wrap mode plus an outer trap restore; the subject is imported as ./index from source, so no dist preflight applies). Leg A, filter removed (anchor x1 to x0, replacement x0 to x1, blob 446655b1e9b5 changed): 'Tests 4 failed | 3 passed (7)'; the 4 withheld pins went red, and the received message was the probe. Leg B, 5xx bound removed: 'Tests 1 failed | 6 passed (7)'; only the 4xx pin went red ('Internal server error' received). Restore proven per leg: blob == HEAD 446655b1e9b5, git diff HEAD empty. The first attempt was a no-op: the replacement 'message,' already occurred in the anchor, the tool refused ('rise of 0, not the declared 1') and restored before the child ran. It was re-run with marker-bearing replacements. Real-rig before/after: see summary.",
    "gates": "Local, at 5407a49. node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derived 63 commands. Each exit code was captured to a file before any pipe; all 63 ended at exit 0. Three first exited 3 (PREREQUISITE NOT MET) and were re-run once the prerequisite was met. check-plugin-teardown-shape --self-test needed the pinned control 621a487 in this shallow clone; after git fetch --depth=1 of that commit: exit 0, 48 cases. check:type-check-debt timed out in the lock queue behind a sibling's 17-minute hold; re-run: exit 0, '1 ledger entr(ies) re-measured … none above its recorded number'. check:dual-build-cjs-loads needs the whole tree's dist/; that re-measure built ./packages/, and the re-run gave exit 0, '110 published require entry point(s) across 68 package(s) load'. --ran ran.list: 'Run reconciliation — 63 derived, 63 run, 0 NOT-MEASURED, 0 UNRUN' (exit 0). Gates included check:route-envelope, check:dispatcher-error-vocabulary, check:cross-package-test-inputs, check:test-source-alias, check:published-files, check:nul-bytes and check:doc-authoring, each exit 0. Lint (the lane's blind spot): eslint --no-inline-config over the diff's 2 .ts files gave '2 files, 0 errors, 0 warnings' (exit 0); over packages/adapters/hono, exit 0. Positive control: a stdin file importing a root @objectstack/spec namespace exits 1 (no-restricted-imports). The hono dist built at this head loads as CJS and as ESM, and carries the new expression. CI (PR check-runs, read once at 2026-10-15T00:37Z, not re-run locally): Check PR Size success; 'No other open PR may claim the same issue' success; 'No other open PR may claim the same single-writer path' success; filter success. In progress or queued: Lint · ESLint, Lint · repo & tooling gates, Lint · CI, release & engine gates, Lint · docs & code guards, Lint · PM dispatch-gates, Lint · scoped (3), Type Check · workspace, source gates, consumer gates and debt ledger, Build Core, Test Core (1-6/6), Check Changeset, Governed Surface Queue Guard, 'The card this PR closes must claim this branch', 'Part-of PR must not also close its card', Dogfood Regression Gate, Auto Label, Check Documentation Links. Skipped by selector: Build Docs, Console Pin Gate, Dogfood Verify CLI, Temporal Conformance, Packed-tarball smoke.",
    "line_budget": "n/a",
    "mcp_calls": "0 — none",
    "api_writes": "2 relay strokes through scripts/pm/with-fleet.sh --via dispatch, each a POST /repos/objectstack-ai/objectstack/dispatches as the session. Stroke 1, pr_create (run 38660970927, success), executed as objectstack-fleet[bot]: POST /repos/objectstack-ai/objectstack/pulls (draft, #23564) and POST /repos//issues/23564/assignees (os-bill); the read-back reported 8925 bytes sent, 8925 stored. Stroke 2, comment: POST /repos//issues/23532/comments (this os-dev-report). Plus 2 git pushes (not REST). Zero label writes: the diff publishes @objectstack/hono, so skip-changeset fails its criterion, and the dispatch named no label.",
    "open_questions": [],
    "out_of_scope_findings": [
    "carrier: none · noted, not filed — in the one-shot rig the unsynced-table insert left the engine as an undeclared bare Error, which is why it reached this fallback rather than the declared limb. Whether the insert path should declare its fault (code plus a 5xx status, the predicate header's preferred remedy) is the producer's question, not this door's. It is a read-only inference, so it sits in the PR's Acceptance notes only."
    ],
    "deviations": [
    "origin/main gained b572289 and 7ffc578 after BASE 086fa46. They touch packages/spec tests and packages/metadata-protocol, disjoint from this diff, so the branch was not merged before opening (AGENTS.md section 10 says pull main first). CI runs on the merge ref, and the queue rebuilds on main.",
    "A shared-object-store write: git fetch --depth=1 origin 621a487607881c66b2899b7e3477115229a156b4 brought the pinned control commit of check-plugin-teardown-shape --self-test into the common .git (one object chain and a shallow boundary; no ref moved).",
    "One-shot measurement outside the claimed file surface: packages/qa/http-conformance/src/tmp-23532-ghost-probe.conformance.test.ts was written, run (before and after) and deleted by the script's trap. It was never staged or committed, and git status was clean afterwards.",
    "check:type-check-debt's own re-measure built ./packages/
    (turbo, under the lock) in this worktree. That is the gate's side effect, not a step of this card.",
    "The PR body ends with AGENTS.md's session-URL footer form, not the harness reminder's 'Generated with' form; the harness reminder yields to the repo's instructions."
    ],
    "files_changed": [
    "packages/adapters/hono/src/index.ts",
    "packages/adapters/hono/src/hono-thrown-message-disclosure.test.ts",
    ".changeset/23532-hono-undeclared-500-leak-filter.md"
    ]
    }


    Generated by Claude Code

  4. objectstack-fleet commented on Oct 15, 2026

    @objectstack-fleet
    ContributorAuthor

    ACCEPT → PR #23564 comment 6181691006 (seat record ## 席位核对, head 5407a49e22). It lands through the queue once every check on the head is green. domain:cli seat 1 · session session_01FLwgbTSmGMrqsXkBDsiwfG · 2026-10-15T00:41Z.


    Generated by Claude Code

  5. objectstack-fleet commented on Oct 15, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed: PR #23564 → fa6a6085c0 (#23532). The card is closed completed, and pm:dispatched is stripped

    domain:cli seat 1 (#6024) · os-bill · session session_01FLwgbTSmGMrqsXkBDsiwfG · 2026-10-15T01:20Z · holder of claim 6180882480, released by this landing.

    reading value
    queue added_to_merge_queue 00:57:49Z; merge-group runs on gh-readonly-queue/main/pr-23564-cdc42eb495…
    origin/main fa6a6085c0, one parent (a queue squash), an ancestor of origin/main
    card closed completed by the PR's Fixes #23532 at 01:19:17Z; pm:dispatched removed by targeted delete; bug, priority:p2, security, domain:cli stay

    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions