docs/plans/2026-09-04-triage-qrfj-html-parser-advisory.md
Objective: Resolve the private HTML parsing advisory with shipped-state proof; done when fixed, released, published, and read back; plan docs/plans/2026-09-04-triage-qrfj-html-parser-advisory.md.
Flow mode: one-shot execution
Goal plan: docs/plans/2026-09-04-triage-qrfj-html-parser-advisory.md
Task source:
Task state:
Current verdict:
Pre-solution issue challenge:
Timed checkpoint:
Completion threshold:
Verification surface:
rg source/call-site audit excluding generated trees and dependencies.Constraints:
Boundaries:
Output budget strategy:
rg to source/test/docs owners; exclude node_modules, generated registry/template output, build artifacts, and caches.Blocked condition:
Completion rule:
update_goal(status: complete) while any required checklist item
remains unchecked. If an item does not apply, check it and add N/A: <reason>.update_goal(status: complete) until the named verification
evidence is recorded below and
node .agents/skills/autogoal/scripts/check-complete.mjs docs/plans/2026-09-04-triage-qrfj-html-parser-advisory.md passes.Start Gates:
| Gate | Applies | Evidence |
|---|---|---|
| Timed checkpoint parsed | no | N/A: no duration requested |
| Skill analysis before edits | yes | Read security-triage and full autogoal instructions before substantive work |
| Active goal checked or created | yes | get_goal returned null; created the matching active goal |
| Source of truth read before edits | yes | Full private advisory, local owners/call sites, and npm 53.3.10 artifact inspected |
docs/solutions checked for non-trivial existing-code work | yes | Focused rg found no prior solution for this helper or execution boundary |
| TDD decision before behavior change or bug fix | yes | No behavior edit before verdict; if valid, load task and tdd, then prove red before green |
| Browser tool decision for browser surface | yes | GitHub API is state authority; use browser for final UI readback when reachable |
| Output budget strategy recorded | yes | Scoped/capped strategy recorded above |
| Tracker comments and attachments read | yes | Advisory API returned the full report and metadata; no separate comment/attachment API surface was present |
| Video transcript evidence required | no | N/A: advisory has no video evidence |
| Pre-solution issue challenge required | yes | Reporter claim reproduced and wording limitation recorded above |
| Reproduction verdict before implementation | yes | Valid/reproduced in Chromium before source edits |
| Repro escalation ladder selected | yes | Source/artifact -> repo harness N/A -> Browser Use -> visual N/A |
| Suggested fix reviewed against durable boundary | yes | Shared helper is correct boundary; DOMParser is inert parsing, not sanitization |
| Branch decision for code-changing task | yes | Created codex/inert-html-string-parser from current origin/main before code edits |
| Release artifact decision | yes | Published @platejs/core behavior change requires a patch changeset |
| PR expectation decision | yes | Sanitized public PR; user explicitly approved public handling and task workflow requires PR |
| Dedicated task plan selected for exact PR | yes | This plan is dedicated only to PR #5117 |
| Tracker sync expectation decision | yes | Advisory metadata/publication is the tracker sync surface |
| Security advisory pack selected | yes | Applied in place after the valid verdict |
| Advisory source read through correct authority or explicit access blocker | yes | Repository advisory endpoint read successfully |
| Affected package, vulnerable range, and fixed-version target identified | yes | @platejs/core through npm latest 53.3.10; target is the next patch release |
| Disclosure/release order recorded | yes | Merge and publish package before advisory publication |
| Private/draft disclosure safety recorded | yes | Public PR remained sanitized; advisory was published only after npm served the fixed artifact |
| CVE decision recorded | yes | Request CVE after patched version is published and metadata is final |
| Package/API pack selected | yes | Published package runtime behavior is changing |
| Public surface or package boundary identified | yes | Public htmlStringToDOMNode and all string deserialization paths in @platejs/core |
| Release artifact path selected | yes | .changeset for @platejs/core patch |
changeset skill loaded when .changeset is required | yes | Loaded before adding .changeset/calm-bodies-parse.md |
| Barrel/export impact decision recorded | yes | No export or file-layout change; barrel generation N/A |
Work Checklist:
Completion Gates:
| Gate | Applies | Required action | Evidence |
|---|---|---|---|
| Named verification threshold | complete | Run the command, proof, source audit, or artifact check named in this plan | Source/artifact audit, red-green tests, package tests/typecheck, root check, autoreview, CI, npm artifact, release, and public advisory readbacks complete |
| TypeScript or typed config changed | yes | Run relevant typecheck | pnpm turbo typecheck --filter=./packages/core passed |
| Package exports or file layout changed | no | Run pnpm brl before final verification and keep generated barrel updates | N/A: existing exported files and symbols retained |
| Package manifests, lockfile, or install graph changed | no | Run pnpm install and relevant package checks | N/A: no manifest, lockfile, or dependency change |
| Agent rules or skills changed | no | Run pnpm install and verify generated skill sync | N/A: no agent/tooling change |
| Workspace authority proof | yes | Run verification in the owning repo/package/app/route/tool and record cwd; do not count the wrong workspace as proof | Core package tests/typecheck ran in /Users/zbeyens/git/plate; Browser Use ran the bundled source in Chromium |
| Browser surface changed | yes | Capture Browser Use proof | Bundled fixed source had inert ownership and zero parsing side effects in Chromium |
| CI-controlled template output changed | no | Restore generated template output or record why it is intentionally kept | N/A: no template output touched |
| Package behavior or public API changed | yes | Add a changeset or record why no changeset applies | .changeset/calm-bodies-parse.md adds a core patch release note |
| High-risk mini gate | yes | For public API/runtime/package-boundary/browser/agent-action/command-contract changes, record realistic failure mode, proof plan, and why the chosen boundary is right; otherwise N/A | Failure: parsing still touches the active document or changes returned content; proof: red-green owner tests, 856 core tests, typecheck, bundled-source Chromium; boundary: both public string parsers |
| Autoreview for non-trivial implementation changes | yes | Load .agents/skills/autoreview/SKILL.md; use dirty local --mode local, branch/PR --mode branch --base <base>, or committed slice --mode commit --commit <ref> until no accepted/actionable findings, or record N/A for docs-only/planning-only/trivial/no local patch | Local autoreview clean, no accepted/actionable findings, overall 0.84 |
| PR create or update | yes | Run check before PR work | pnpm check passed; sanitized public PR #5117 created |
| Final lint | yes | Run pnpm lint:fix or scoped equivalent | Passed; no fixes applied |
| Output budget discipline | yes | Verify no unbounded high-volume command output was streamed, or record the accidental output and recovery | Two failing DOM assertions emitted oversized object dumps; subsequent failure output was line-sliced and all other reads stayed scoped/capped |
| Timed checkpoint | no | If duration was requested, keep improving until elapsed, then finish the current loop cleanly; otherwise N/A | N/A: no duration requested |
| Goal plan complete | yes | Run node .agents/skills/autogoal/scripts/check-complete.mjs docs/plans/2026-09-04-triage-qrfj-html-parser-advisory.md | Passed after final evidence update |
| Pre-solution issue challenge verdict | yes | Record reporter claim, repro verdict, durable boundary, and decision before implementation | Valid/reproduced; shared parser boundary selected |
| Repro escalation ladder | yes | Record each applicable ladder level or N/A reason | Complete in pre-solution section |
| Bug reproduced before fix | yes | Record failing browser/source proof | Chromium reproduced the active-document side effect; inert parsing produced none |
| Targeted behavior verification | yes | Run focused red-green regression | Both owner tests failed before their fixes and pass together after; 4 tests / 5 assertions |
| Per-PR task ownership | yes | Verify one task-plan body line, plan at exact head, and exact PR ownership | PR #5117 body contained one exact task-plan line; plan existed at head 7c460371522b2de94bc3adef67af6818bfda802c and names PR #5117 |
| Task-style PR body verified | yes | Verify sanitized PR body with gh pr view --json body | Verified sanitized body with task plan, confidence, outcome, caveat, design, and checks |
| Tracker sync-back | yes | Update and publish repository advisory after release | Published repository advisory with final package ranges and remediation |
| Final handoff contract | yes | Record exact PR, confidence, tests, browser, outcome, caveat, design, verification | Recorded in Final handoff below |
| Advisory source read | yes | Read repository advisory endpoint | Full private advisory read via gh api |
| Security repro / regression proof | yes | Record failing-before/passing-after proof | Browser pre/post proof plus two automated red-green tracer cycles complete |
| Private disclosure guard | yes | Keep public artifacts sanitized until patched package exists | User approved public PR; titles/body/tests/changeset must omit exploit details |
| Patched version published | yes | Verify npm publish and GitHub release/tag | npm serves @platejs/[email protected]; published tarball and GitHub release v53.3.11 verified |
| Advisory metadata updated | yes | Set exact package, vulnerable range, and patched version | Stable range < 53.3.11, patched >= 53.3.11; discontinued beta builds 54.0.0-beta.0/1 explicitly listed as affected |
| Advisory published | yes | Publish after npm package is available | Published at 2026-09-04T18:09:10Z |
| CVE request decision | yes | Request CVE and read back status | CVE request accepted; GitHub has not assigned an ID |
| Advisory final readback | yes | Read state, published_at, cve_id, vulnerabilities, and URL | Repository API and logged-out browser both show the published advisory, exact ranges, remediation, CWE-79, and reporter credit |
| Propagation caveat | yes | Record GitHub/Dependabot propagation caveat | Global Advisory Database endpoint returned 404 immediately after publication; GitHub review and Dependabot propagation are asynchronous |
| Public API / package boundary proof | yes | Audit export and affected consumers | Public helper and all string deserialization callers mapped |
| Release artifact classification | yes | Classify published user-visible delta | Patch behavior change in @platejs/core |
| Published package changeset | yes | Load changeset skill and add patch changeset | .changeset/calm-bodies-parse.md, one @platejs/core patch |
| Registry changelog | no | N/A: no registry-only change | N/A: no registry-only change |
| No release artifact | no | N/A: changeset required | N/A: changeset required |
| Package typecheck/build/test | yes | Run owning package checks | 856 core tests passed; source-first core typecheck passed |
| Barrel/export generation | no | N/A: no export or file layout change | N/A: implementation stays behind existing export |
Phase / pass table:
| Phase | Status | Evidence | Next |
|---|---|---|---|
| Intake and source read | complete | Advisory, consumers, shipped artifact, and browser behavior verified | Done |
| Implementation | complete | Two red-green tracer cycles; inert parsing applied to both public string parsers; patch changeset added | Done |
| Verification | complete | Focused tests, 856 core tests, typecheck, lint, root check, fixed-source Chromium, autoreview, and hosted CI passed | Done |
| PR / CI | complete | Sanitized PR #5117 verified and admin-squash-merged after green CI | Done |
| Closeout | complete | npm/GitHub release, published tarball, advisory API/browser readback, and goal checker complete | Done |
Findings:
@platejs/[email protected] still ships the same implementation and public export.@platejs/[email protected]; the published tarball contains inert parsing at both affected entry points.beta tag points to discontinued 54.0.0-beta.1; beta.0 and beta.1 contain the vulnerable code and are explicitly listed as affected without a patched beta.1diot9, and names the exact stable and beta ranges.Decisions and tradeoffs:
main into the structurally divergent next branch -> the supported lane script found broad source moves/deletions -> abort the merge cleanly and record the beta artifacts as affected.Error attempts:
| Error / failed attempt | Count | Next different move | Resolution |
|---|---|---|---|
In-app browser blocked a data: probe URL | 1 | Use a materially safer loopback page | Localhost Browser Use probe succeeded |
| Browser evaluate exposes a read-only page scope | 2 | Run the probe from page-owned script | Localhost page recorded event counts |
| Focused failing tests emitted oversized DOM object dumps | 2 | Slice subsequent failure output and keep passing runs capped | Red state captured; later output remained bounded |
pnpm exec esbuild unavailable | 1 | Use repository-installed Bun bundler | Bundled the real fixed source successfully |
| CI changeset policy rejected single-quoted frontmatter | 1 | Match the workflow's double-quoted package-name grammar | Changeset frontmatter corrected; rerun passed |
Obsolete advisory /publish action endpoint returned 404 | 1 | Read the current official REST contract | Published by PATCHing advisory state to published |
| Main release workflow ended red after package/release success | 1 | Read job-level outcomes and failure log | Package, tag, release, registry push, and npm propagation passed; unrelated template generation opened PR #5119 after lint errors |
Direct main to next beta sync produced broad source conflicts | 1 | Follow the release-lanes hard stop and abort | Merge aborted cleanly; returned to and fast-forwarded main; affected discontinued betas disclosed |
External/browser findings:
Timeline:
pnpm check passed before PR creation: lint, 54-package build/typecheck, fast tests, slow tests, and slowest-test gate.d02afe45d5ec3a9fb95e0745bc5820ff18a3c12b; release PR #5118 auto-merged as c6801761b10a81e54b8f2e91e5ecbd20414e2939.@platejs/[email protected]; GitHub published release v53.3.11; published tarball audited.Verification evidence:
gh api repos/udecode/plate/security-advisories/GHSA-qrfj-mgw8-j9c6 -> triage report and metadata captured.rg source/call-site audit in /Users/zbeyens/git/plate -> public helper and string-entry consumers mapped.npm pack @platejs/[email protected] artifact audit -> vulnerable helper remains shipped in latest.bun test packages/core/src/lib/plugins/html/utils/htmlStringToDOMNode.spec.ts packages/core/src/static/deserialize/htmlStringToEditorDOM.spec.ts -> 4 passed, 0 failed.pnpm --filter @platejs/core test -> 856 passed, 0 failed.pnpm turbo typecheck --filter=./packages/core -> 5 tasks passed.pnpm lint:fix -> 3290 files checked, no fixes..agents/skills/autoreview/scripts/autoreview --mode local -> clean; no accepted/actionable findings; 0.84.pnpm check -> passed; only pre-existing non-error warnings were reported.33902505885 -> CI job completed successfully at head 7c460371522b2de94bc3adef67af6818bfda802c.npm view @platejs/[email protected] and npm pack -> version available; both published parser entry points use inert DOMParser documents.v53.3.11 -> published at 2026-09-04T18:02:43Z from c6801761b10a81e54b8f2e91e5ecbd20414e2939.published at 2026-09-04T18:09:10Z, Moderate/CWE-79, reporter retained, exact stable and discontinued-beta ranges.Final handoff:
@platejs/[email protected]; advisory published.Reboot status:
| Question | Answer |
|---|---|
| Where am I? | Closeout complete |
| Where am I going? | N/A: completion threshold reached |
| What is the goal? | Resolve GHSA-qrfj with shipped-state proof and the correct final advisory state |
| What have I learned? | The report is valid, the shared boundary owns the fix, inert parsing preserves return shapes, and discontinued beta artifacts must be disclosed separately |
| What have I done? | Merged and released the fix, audited the published artifact, published and read back the advisory, and recorded asynchronous CVE/database propagation |
Open risks:
Primary template: docs/plans/templates/goal.md
Applied packs: