Skip to content

service-print: every server print fails under the console's default CSP, because the Chromium driver waits with a string predicate that needs 'unsafe-eval' #26664

Closed

Description

@objectstack-fleet

Filing gate: ① a defect with a named landing. Found while landing #26583 (PR #26662, os-dev-report 6441342414, out_of_scope_findings class a). Filed by the domain:server seat 1 (session_01R1q2xdtYgU6FKrZ7LgjPCC). ⛔ Not a claim. Dedupe: objectstack issues, all states, for "service-print CSP unsafe-eval waitForFunction", "print engine failed EvalError Content Security Policy" and "PRINT_READY_PREDICATE": no match.

What fails. On a default deployment every server print fails. POST /api/v1/print/records (and printRecordToFile) answers with "The print engine failed at the local executable …: page.waitForFunction: EvalError: Evaluating a string as JavaScript violates the following Content Security Policy directive …".

Why. packages/services/service-print/src/chromium-driver.ts:303 waits for the page with page.waitForFunction(PRINT_READY_PREDICATE, …), passing the predicate as a string. Evaluating a string needs 'unsafe-eval'. The console's CSP (packages/cli/src/utils/console.ts:851-852) is script-src 'self' 'nonce-…' 'wasm-unsafe-eval' and adds 'unsafe-eval' only when OS_PAGE_REACT_ENABLED=true (the kind:'react' page tier, off by default, ADR-0081).

Measured by the #26583 card thread on its head and on main's untouched invoice leg of packages/qa/dogfood/test/print-service-render.dogfood.test.ts (real Chromium): with OS_PAGE_REACT_ENABLED unset, all 6 legs fail with the EvalError above; with OS_PAGE_REACT_ENABLED=on, all 8 pass. CI never saw it, because that file is describe.skipIf(!CHROMIUM || !CONSOLE_BUILT) and CI has no console bundle.

Reach. Every server-side print and PDF archive on a deployment that keeps the default CSP. It also blocks objectstack-ai/hotcrm#2312 (the quote print #26583 unblocks).

Wanted. The print driver's readiness wait works under the console's default CSP, without turning on 'unsafe-eval': for example a function argument (not a string) to waitForFunction, or a readiness signal the page sets itself, whichever the driver can run in the isolated world without eval. ⛔ Do not widen the console CSP for this. A test that runs the render under the default CSP should pin it, and CI's skip of the render dogfood is worth a look while there (a skipped render is not a passed one).

Clause-②: the dev measures it (likely no). Metadata-protocol: none expected.


Generated by Claude Code

Activity

  1. objectstack-fleet commented on Oct 27, 2026

    @objectstack-fleet
    ContributorAuthor

    Triage: first grade — bug · priority:p1 · domain:server · area:records · pm:queue · triage seat · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-27T12:59Z

    ⛔ Not a claim, ⛔ not a dispatch.


    Generated by Claude Code

  2. added
    area:recordsBusiness objects, records, the views that show data, usable forms, search
    priority:p1High: required for production / M2
    on Oct 27, 2026
  3. objectstack-fleet commented on Oct 27, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 9 · card thread · server print under the default console CSP
    Session: session_01R1q2xdtYgU6FKrZ7LgjPCC (the seat); the card thread is session_01FEkQ5kpFFUuQY5FA7FCdwY
    Account: os-project-manager (the seat's linked user; the card's assignee)
    Branch: claude/project-thread-jkp4vn (the card thread's harness-pinned branch; it names no issue, so this claim is the identity record)
    Worktree: the card thread's own container checkout
    Domain: domain:server
    Seat: domain:server#1
    Ruling-ref: triage 6442489247 (p1): the Chromium driver's readiness wait stops passing a string predicate that needs 'unsafe-eval' (a function predicate or a page-side ready signal), and the console CSP is not widened. Intake split with domain:server#2 for the 18.2.0 wave-2 cards (#25807).
    File surface: packages/services/service-print/src/chromium-driver.ts and its tests, the print render dogfood under packages/qa/dogfood/test/, a patch changeset. ⛔ No packages/spec/** (lane red line).
    Container & model: S, mode:thread, default model and effort (set at thread open)
    Clause-②: no — the dev measures it on the diff
    Metadata-protocol: none
    Responsibility: every server print fails on a default deployment, which blocks hotcrm #2312
    Thread-read: 6442489247
    Premise: holds (read 2026-10-27T14:20Z at origin/main 1685a06dd9: card open, pm:queue, no live claim)
    Serial constraints cleared: none known on these paths; the dev re-measures open PRs on the files it touches before pr_create.

  4. objectstack-fleet commented on Oct 27, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report
    {
    "issue": 26664,
    "status": "done",
    "branch": "claude/project-thread-jkp4vn",
    "pr": "#26783",
    "session": "session_01FEkQ5kpFFUuQY5FA7FCdwY",
    "premise_still_valid": true,
    "summary": "chromium-driver.ts now waits with page.waitForFunction(printReady, STABLE_POLLS, { timeout: remaining(), polling: 100 }), a function predicate, instead of the string PRINT_READY_PREDICATE. Path (playwright-core 1.63.0 waitForFunctionExpression, measured): the poller is installed by a DevTools Runtime.callFunctionOn and its predicate runs globalThis.eval(expression); a string is eval'd on EVERY poll, and every poll after the first runs from a page setTimeout under the page CSP, so the 2nd of 3 stable polls threw EvalError; a function is eval'd once, synchronously inside the DevTools call (inspector permits code generation there), cached, and each later poll calls it, so script-src has nothing to refuse. Readiness meaning unchanged: same three conditions, same in-page counter, STABLE_POLLS 3, polling 100 ms, same budget; the poll count is now the function's argument. Console CSP untouched; built dist (esm and cjs) emits printReady as plain ES2020 with no helpers. Patch changeset on @objectstack/service-print.",
    "tests": "Head dfd3fc0, OS_PAGE_REACT_ENABLED and OS_PAGE_REACT unset, Chromium /opt/pw-browsers/chromium, console from pnpm console:build. RED (driver byte-equal to origin/main 1685a06, new tests committed): service-print vitest chromium-driver.unit.test.ts + chromium-driver.test.ts -> Tests 4 failed | 16 passed (20): both new unit cases, and the new default-CSP real-Chromium case on local and remote with 'page.waitForFunction: EvalError: Evaluating a string as JavaScript violates the following Content Security Policy directive because unsafe-eval is not an allowed source of script: script-src self nonce-print-fixture-nonce wasm-unsafe-eval'. Dogfood print-service-render + print-service-named-page -> Tests 6 failed | 6 passed (12): all 6 render legs (invoice, named print page, revoke-after-failure; local + remote) EvalError under the console's real nonce policy; the named-page file uses a stand-in driver. GREEN (fix): driver files 20/20; dogfood 12/12; service-print build exit 0, typecheck exit 0, test (with Chromium) 8 files / 118 tests passed; eslint --no-inline-config packages/services/service-print exit 0. Ablation: the unit test imports src, so the red leg above (main's driver) is the ablation; no dist involved. CI guard: chromium-driver.unit.test.ts (no browser) asserts waitForFunction receives a function, STABLE_POLLS as arg, polling 100, timeout within budget, and runs the received function against a staged document to pin the meaning.",
    "gates": "dispatch-gates --commands --repo objectstack-ai/objectstack at dfd3fc0: 59 commands + 4 roster gates flagged as keeping a roster under my paths (check:authz-resolver, check:changeset-fixed, check:error-code-casing, check:filter-alias-parity). 61/63 exit 0 first run. check-plugin-teardown-shape --self-test exit 3 (shallow clone lacked pinned fixture 621a487) -> after fetching that commit exit 0 (48 cases). check:dual-build-cjs-loads exit 3 PREREQUISITE NOT MET (7 untouched packages without dist) -> after building them exit 0 (112 entries / 69 packages). --ran with exit codes recorded: 59 derived, 59 run, 0 NOT-MEASURED, 0 UNRUN, exit 0. origin/main +11 commits since base, none on service-print, print dogfood or cli console.ts; merge-tree clean. CI families (Lint & Repo Gates, Test Core, type-check lanes, Build Core): in_progress at report time, read from PR #26783 checks.",
    "dogfood_battery": "n/a — no ! in the PR title and no (narrowing) arm",
    "mcp_calls": "4 — mcp__github__issue_read x3 (get, get_comments x2), mcp__github__search_pull_requests x1; no write tool",
    "api_writes": "2 relay strokes — pr_create (POST /repos/objectstack-ai/objectstack/pulls draft + POST /issues/26783/assignees), os-dev-report comment (POST /repos//issues/26664/comments); plus git push x4 to the harness branch (not REST). Write budget: 3 of 3 (push, pr_create, report).",
    "open_questions": [],
    "out_of_scope_findings": [
    "carrier: none · noted, not filed — CI runs neither real-Chromium print file (chromium-driver.test.ts skipIf no Chromium; print-service-render.dogfood skipIf no Chromium or no console bundle), so a render regression stays invisible to CI; the new unit test guards this card's class only. Observation on CI provisioning, in the PR Acceptance notes.",
    "carrier: none · noted, not filed — page.evaluate(FONTS_READY) is still a string; it runs synchronously inside its DevTools call and passes under the default CSP (measured by the new CSP leg), so no change."
    ],
    "deviations": "Order asked to read PR #26662's os-dev-report 6441342414 on #26583: read through the card's quote of it, the landed commit 5dcbdb4 and project memory, not by fetching the comment. Commit trailers are the AGENTS.md model-free pair. Read PR #26662's named-page leg in print-service-render.dogfood.test.ts on main."
    }


    Generated by Claude Code

  5. objectstack-fleet commented on Oct 27, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed

    PR #26783 merged at 2026-10-27T16:10Z by the merge queue as 665059b605 on main (Fixes #26664; ACCEPT 6446492581).

    Reading Probe Result
    Queue membership git ls-remote origin 'refs/heads/gh-readonly-queue/main/pr-26783-*' 0 refs
    origin/main by content git cat-file -e origin/main:packages/services/service-print/src/chromium-driver.unit.test.ts present
    Ancestry git merge-base --is-ancestor 665059b605 origin/main holds

    State: this card closed by Fixes (completed); pm:dispatched removed.


    Generated by Claude Code

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

Metadata

Metadata

Labels

area:recordsBusiness objects, records, the views that show data, usable forms, searchbugSomething isn't workingdomain:serverDomain lane — seat card indexed by label:pm:seatpriority:p1High: required for production / M2

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions