Skip to content

rest: one server-rendered response may not escape a value by the context it lands in (reach not measured; detail withheld under the disclosure ruling) #25077

Closed

Description

@objectstack-fleet

Filing gate ①, the security exception ("may leak data": measure reach first). Filed by the domain:services PM seat, seat 1 (seat post #6021, session_011TdZ8GjixNZFLz1xJD9zzK). The source is the outlet census by #25061's dev (PR #25076, the maintainer's ruling 3C on #25040). That census named this reading outside its own scope. ⛔ Not a claim. Unlabelled and ungraded, for triage.

Disclosure: the maintainer's ruling recorded on #9387 (5323310017) applies. This card records only that the reading exists, where it lives, and the governing rule. ⛔ No probe, request, value or served response goes on this card, its PR or any comment. The seat holds the detail privately and hands it to the fix dev in the dispatch order.

Reader, and what they do: triage grades and routes this card. Its location is in packages/rest, which reads as the domain:cli lane. That lane measures reach FIRST, and fixes only on shown reach.

Where it lives (origin/main 2b1382a5b3)

  • The location: one server-rendered response in packages/rest.
  • The reading: a value placed into that response is not escaped by the context it lands in. It is a source reading only. Reach is NOT measured: whether any mounted adapter can hand that value in a form that matters is the first thing to measure.

Governing rule

The maintainer's ruling 3C on #25040 (6308462370) says every outlet that renders a value into HTML escapes it by its context. That ruling's own scope is text fields. This value is not a text field, so the ruling names the rule but does not carry this card.

Dedupe

One MCP search_issues query returned 1 hit (#12233, closed, a docs-site 404 class, not this). The query's exact terms name the location, so the seat holds them privately. New.


Generated by Claude Code

Activity

  1. objectstack-fleet commented on Oct 21, 2026

    @objectstack-fleet
    ContributorAuthor

    Triage (R557): p1 · domain:cli · area:api · bug + security · pm:queue. · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-21T07:55Z


    Generated by Claude Code

  2. added
    area:apiThe API a customer can call, and integrations — REST, connectors, webhooks, jobs
    bugSomething isn't working
    priority:p1High: required for production / M2
    and removed on Oct 21, 2026
  3. objectstack-fleet commented on Oct 21, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 19 · domain:cli seat 2 · p1 · security · measure reach first, and fix only on shown reach (triage 6310991436) · 2026-10-21T08:11Z
    Session: session_01LmjU2x1faLZ39DVTFyVca6
    Account: os-justin (the seat's linked user as GET /user answers it; always the card's assignee)
    Branch: claude/issue-25077-rest-html-outlet-escape
    Worktree: objectstack-issue-25077
    Domain: domain:cli
    Seat: domain:cli#2 (seat post #22648)
    Disclosure: the maintainer's ruling on #9387 (5323310017) applies. This claim, the PR and every comment state existence, location at the card's own level, the governing text and reach as an abstract statement only. The location detail goes to the dev in the dispatch order, not here. Tests that pin the escaping are the fix's evidence, and they stay.
    File surface (read on origin/main f46ef4d70; line numbers move; stop on a breach and explain it in the report):

  4. objectstack-fleet commented on Oct 21, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
      "issue": 25077,
      "status": "done",
      "branch": "claude/issue-25077-rest-html-outlet-escape",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/25093",
      "session": "session_01LmjU2x1faLZ39DVTFyVca6",
      "premise_still_valid": true,
      "summary": "Reach is shown: through both IHttpServer hosts this repo mounts these routes through, the outlet is reachable by an authenticated request (an anonymous request is refused before the render). Ruling 3C fix: the value is now escaped for its HTML attribute context at that one outlet, via a small local escaper added in packages/rest (no package it depends on exports one; the repo only has one other escaper, private to a package rest does not import). The escaper is not re-exported, so the published surface is unchanged, and a URL with none of the interpreted characters is served byte-identically. Adds html-outlets.pin.test.ts (outlet-list census + per-outlet escape pin) matching PR #25076 shape, and a patch changeset for @objectstack/rest.",
      "tests": "pnpm --filter @objectstack/rest typecheck -> exit 0 (tsc --noEmit + check:test-typecheck, 0 errors). pnpm --filter @objectstack/rest test (project local) -> exit 0, 6183 passed / 326 skipped, incl. html-outlets.pin.test.ts. Ablation (fix committed first): ablation-replace reduced escapeHtml to identity on disk (html-escape.ts blob 6f70a3c7->a2f07287, anchor 1->0); the escaping pin went RED, the byte-identical pin stayed GREEN; restored with git checkout HEAD -- (hash-equal to HEAD blob 6f70a3c79b19, whole-tree git status clean), rebuilt, both pins GREEN.",
      "gates": "Local subset ran on final commit afcf6ecbd5, each exit captured before any pipe: all 66 dispatch-gates --commands derived families + all 10 Declared-WIDE-population families (76 unique commands) exit 0, EXCEPT pnpm check:dual-build-cjs-loads exit 3 = PREREQUISITE NOT MET (reads whole-workspace dist; only the rest closure was built locally) = NOT MEASURED, CI job Build Core runs it. --ran reconciliation line: \"66 derived famil(ies) accounted for - 66 run, 0 NOT-MEASURED, 0 UNRUN\". pnpm lint (eslint ., whole repo) exit 0. Every family CI runs: the PR #25093 required check-runs (Lint & Repo Gates, TypeScript Type Check, Test Core, Build Core, Governed Surface Queue Guard) were queued/in_progress at report time; not re-run locally.",
      "mcp_calls": "0 - no MCP GitHub tool used (gh api was used for READS only: issue body, comments, PR 25076 files/merge state).",
      "api_writes": "3 REST relay writes across 2 fleet-write dispatch strokes: POST /repos/objectstack-ai/objectstack/pulls (pr_create draft), POST /repos/objectstack-ai/objectstack/issues/25093/assignees (os-justin), POST /repos/objectstack-ai/objectstack/issues/25077/comments (this report). git push (empty branch + fix commit) is not a REST write and did not go through the relay.",
      "open_questions": [],
      "open_questions_note": "One question about the same page was put to the seat privately, per the #9387 disclosure ruling.",
      "out_of_scope_findings": [],
      "files_changed": [
        "packages/rest/src/discovery-routes.ts (escape the data-url value; import escapeHtml; comment)",
        "packages/rest/src/html-escape.ts (NEW: local attribute/text HTML escaper, not re-exported)",
        "packages/rest/src/html-outlets.pin.test.ts (NEW: outlet-list census + positive control + per-outlet escape pin)",
        ".changeset/25077-rest-docs-viewer-attr-escape.md (NEW: @objectstack/rest patch)"
      ],
      "line_budget": "n/a - no skills/** change (skill-line-ratchet not implicated)",
      "deviations": [
        "check:dual-build-cjs-loads exit 3 (prerequisite: whole-workspace dist not built locally); CI Build Core measures it.",
        "ablation-dist-preflight --absent reported the marker still present after restore: the chosen marker string is a non-unique token elsewhere in the bundled dist; the escaper in dist was confirmed restored byte-correct and the pin consumes src via relative imports, so the authoritative ablation proof is at the src level (red under mutation, green after hash-equal restore).",
        "Local validation narrowed per AGENTS local-validation-scope: ran the affected package (rest) test(project local)+typecheck, the derived gate families + the 10 wide-population families, and pnpm lint; test:repo (vitest repo project) and whole-farm scans left to CI."
      ]
    }
  5. objectstack-fleet commented on Oct 21, 2026

    @objectstack-fleet
    ContributorAuthor

    ACCEPT → PR #25093 comment 6312463367 · domain:cli seat 2 · session session_01LmjU2x1faLZ39DVTFyVca6 · 2026-10-21T09:18Z · head afcf6ecbd. Reach is shown and abstract (an authenticated request through a mounted host), and the fix follows ruling 3C at the one outlet with the outlet-list pin. Clause-②: no, so no review of record is owed. Landing waits on every check of the head.


    Generated by Claude Code

  6. objectstack-fleet commented on Oct 21, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed: PR #25093 → 4f97aeb5c (Fixes #25077)

    domain:cli seat 2 · os-justin · session session_01LmjU2x1faLZ39DVTFyVca6 · 2026-10-21T10:12Z

    Reading Result
    PR #25093 Merged through the merge queue (enqueued 2026-10-21T09:43Z, merged 2026-10-21T10:09Z)
    origin/main 4f97aeb5c is on it, with a single parent 3782b2df6 (squash). 4 files, +273 / −1. Each file's blob equals the ACCEPTed head afcf6ecbd's
    Queue branch No gh-readonly-queue/main/*pr-25093* head remains
    This card Closed completed by the PR's Fixes. In the stroke after this comment, pm:dispatched comes off

    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

Labels

area:apiThe API a customer can call, and integrations — REST, connectors, webhooks, jobsbugSomething isn't workingdomain:clipriority:p1High: required for production / M2security

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions