[GH-ISSUE #2553] fix(browser/dom): invalid snapshot entries shift facts between nodes #868

Closed
opened 2026-10-15 18:29:09 +00:00 by bsakaguchi · 1 comment
Owner

Originally created by @coygeek on GitHub (Oct 13, 2026).
Original GitHub issue: https://github.com/morluto/rea/issues/2553

Summary

When a CDP reply contains one invalid entry in a DOM snapshot's aligned numeric arrays, inspection assigns later valid facts to different nodes and reports no DOM omission. The same normalization can turn an attribute name into a URL value. This affects page inspection and the page capture used by bundle analysis. The failure was reproduced directly in the production normalizer with disposable fixtures; production entry-point reachability was checked from source.

Steps to reproduce

At the source baseline stated under Runtime or environment, with locked dependencies installed, save the following test beside the browser capture modules as src/browser/dom-array-alignment.test.ts. The fixture supplies an allowed document URL, three nodes and an unchanged string table. Each malformed case changes one aligned array or one attribute list independently.

import { expect, it } from "vitest";
import { captureDom } from "./CdpCaptureDocuments.js";
import { CdpCaptureCompleteness } from "./CdpCaptureCompleteness.js";
import { inspectWebPageInputSchema } from "../domain/browserObservation.js";

it("shows whether invalid entries preserve DOM fact positions", () => {
  const origin = "https://app.example.test";
  const input = inspectWebPageInputSchema.parse({
    cdp_endpoint: "http://127.0.0.1:9222",
    allowed_origins: [origin], target_id: "page-1",
  });
  for (const kind of ["control", "name", "parent", "attributes"]) {
    const nodeName: unknown[] = [1, 2, 3];
    const parentIndex: unknown[] = [-1, 0, 1];
    const attributes: unknown[][] = [[], [], []];
    if (kind === "name") nodeName[1] = "malformed";
    if (kind === "parent") parentIndex[1] = "malformed";
    if (kind === "attributes") attributes[1] = [5, "malformed", 6, 7];
    const ledger = new CdpCaptureCompleteness();
    const result = captureDom({
      strings: [origin + "/app", "#document", "DIV", "SPAN", "",
        "href", "title", "description"],
      documents: [{ documentURL: 0, nodes: {
        nodeType: [9, 1, 1], nodeName, nodeValue: [4, 4, 4],
        parentIndex, attributes,
      } }],
    }, new Set([origin]), input, ledger);
    const completeness = ledger.snapshot();
    console.log(JSON.stringify({ kind,
      names: result.nodes.map(n => n.node_name),
      parents: result.nodes.map(n => n.parent_index),
      attributes: result.nodes[1]?.attribute_names,
      urls: result.urls.map(u => u.url),
      unavailable: completeness.unavailable_sections,
      excluded: completeness.excluded,
    }));
    if (kind === "control") {
      expect(result.nodes.map(n => n.node_name)).toEqual(["#document", "DIV", "SPAN"]);
      expect(result.nodes.map(n => n.parent_index)).toEqual([-1, 0, 1]);
      expect(completeness.unavailable_sections).toEqual([]);
    }
  }
});

Run the focused test and compare each malformed case's output with the control. This test prints the failure signal and validates the control; its successful exit does not mean malformed handling meets the contract. The fixture does not open a browser or contact the example origin.

npm run test:focused -- src/browser/dom-array-alignment.test.ts

Expected behavior

A valid fact in a later array position must remain associated with that node. The invalid middle entry provides no basis to recover its original name or parent. Inspection must preserve the remaining facts without changing ownership, or explicitly omit affected evidence and report incomplete DOM coverage. Values omitted by validation must appear in completeness reporting. Attribute name/value pairs must retain their original roles, so the string "title" in an attribute-name slot cannot become an href value.

Actual behavior

Independent executed probes produced names ["#document","SPAN",""] for the malformed name case and parents [-1,1,-1] for the malformed parent case. The attribute case returned attribute_names=["href","description"] and a DOM URL of https://app.example.test/title, although the snapshot supplied no valid href value. Every malformed case left unavailable_sections=[] and excluded=[]. The control retained names ["#document","DIV","SPAN"] and parents [-1,0,1].

The numeric-array conversion discards invalid entries separately before consumers index the arrays. This changes the positions of later facts and attribute pairs. The output can satisfy the result schema even when the facts belong to different nodes. The observed omission concerns DOM reporting; it does not establish that the entire inspection has no unrelated coverage limitations.

Evidence

  • Expected source: docs/browser-observation.md:160-162 requires incomplete or truncated values to be reported; :173 promises DOM node fact retention; :189-197 requires reporting validation omissions. AGENTS.md:29 requires preservation of partial facts, meaningful failure reasons and explicit unknowns.
  • Failure source: src/browser/CdpCaptureDocuments.ts:168-196 consumes independently filtered arrays; :377-383 removes invalid entries and changes their indexes. Independent focused probes produced the outputs described above, including unchanged omission ledgers.
  • Evidence provenance: observed
  • Local verification: reproduced
  • Reproduction completeness: complete

One focused file ran four tests successfully, covering the valid control and the three defective outputs. The inline reproduction above retains the same inputs and action in one test. No real browser, CLI or MCP capture was executed. No occurrence on a conforming browser was demonstrated.

Production reachability is established from source. src/browser/CdpPageCapture.ts:267-282 passes a DOMSnapshot result to the normalizer with the capture ledger, and :300 projects that ledger into inspection. src/browser/CdpConnection.ts:238-261 accepts a correlated result object without checking nested node arrays. src/domain/browserObservationSchemas.ts:50-57 validates output field types without checking original fact ownership. Both inspection and bundle analysis share this capture path.

Restoration check

Repeat the malformed name and parent cases. If valid nodes remain, the SPAN name and parent 1 must still describe the original third node; the invalid middle entries must not acquire those facts. Explicit omission of affected evidence is also acceptable when completeness reports the gap. Malformed DOM values must produce a DOM invalid_protocol_value exclusion and mark DOM unavailable. In the attribute case, no URL may be inferred from "title" and "description" must not become an attribute name. The numeric control must continue to retain all three names and parents with no DOM exclusion.

Runtime or environment

Failure was verified at a2d8b94dd5 and is still present at 8b181716ac by source recheck, package rea-agents 6.4.0, Node v26.10.0, npm 11.19.1 and locked Vitest 5.0.3. Verification used a direct normalizer test rather than a browser session.

Triage assessment

  • Impact level: P2
  • Assessment status: supported
  • Impact basis: Malformed nested protocol values produce consequential false DOM structure and URL metadata while concealing the affected coverage gap. The observed defect is bounded to malformed replies; normal-browser frequency and downstream harm are unknown.
  • Workaround status: unverified
  • Workaround basis: A fully numeric aligned snapshot passed the control, so a conforming CDP reply avoids this trigger. No practical recovery route for a user already receiving malformed replies was tested.
Originally created by @coygeek on GitHub (Oct 13, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/2553 ## Summary When a CDP reply contains one invalid entry in a DOM snapshot's aligned numeric arrays, inspection assigns later valid facts to different nodes and reports no DOM omission. The same normalization can turn an attribute name into a URL value. This affects page inspection and the page capture used by bundle analysis. The failure was reproduced directly in the production normalizer with disposable fixtures; production entry-point reachability was checked from source. ## Steps to reproduce At the source baseline stated under Runtime or environment, with locked dependencies installed, save the following test beside the browser capture modules as src/browser/dom-array-alignment.test.ts. The fixture supplies an allowed document URL, three nodes and an unchanged string table. Each malformed case changes one aligned array or one attribute list independently. ```ts import { expect, it } from "vitest"; import { captureDom } from "./CdpCaptureDocuments.js"; import { CdpCaptureCompleteness } from "./CdpCaptureCompleteness.js"; import { inspectWebPageInputSchema } from "../domain/browserObservation.js"; it("shows whether invalid entries preserve DOM fact positions", () => { const origin = "https://app.example.test"; const input = inspectWebPageInputSchema.parse({ cdp_endpoint: "http://127.0.0.1:9222", allowed_origins: [origin], target_id: "page-1", }); for (const kind of ["control", "name", "parent", "attributes"]) { const nodeName: unknown[] = [1, 2, 3]; const parentIndex: unknown[] = [-1, 0, 1]; const attributes: unknown[][] = [[], [], []]; if (kind === "name") nodeName[1] = "malformed"; if (kind === "parent") parentIndex[1] = "malformed"; if (kind === "attributes") attributes[1] = [5, "malformed", 6, 7]; const ledger = new CdpCaptureCompleteness(); const result = captureDom({ strings: [origin + "/app", "#document", "DIV", "SPAN", "", "href", "title", "description"], documents: [{ documentURL: 0, nodes: { nodeType: [9, 1, 1], nodeName, nodeValue: [4, 4, 4], parentIndex, attributes, } }], }, new Set([origin]), input, ledger); const completeness = ledger.snapshot(); console.log(JSON.stringify({ kind, names: result.nodes.map(n => n.node_name), parents: result.nodes.map(n => n.parent_index), attributes: result.nodes[1]?.attribute_names, urls: result.urls.map(u => u.url), unavailable: completeness.unavailable_sections, excluded: completeness.excluded, })); if (kind === "control") { expect(result.nodes.map(n => n.node_name)).toEqual(["#document", "DIV", "SPAN"]); expect(result.nodes.map(n => n.parent_index)).toEqual([-1, 0, 1]); expect(completeness.unavailable_sections).toEqual([]); } } }); ``` Run the focused test and compare each malformed case's output with the control. This test prints the failure signal and validates the control; its successful exit does not mean malformed handling meets the contract. The fixture does not open a browser or contact the example origin. ```sh npm run test:focused -- src/browser/dom-array-alignment.test.ts ``` ## Expected behavior A valid fact in a later array position must remain associated with that node. The invalid middle entry provides no basis to recover its original name or parent. Inspection must preserve the remaining facts without changing ownership, or explicitly omit affected evidence and report incomplete DOM coverage. Values omitted by validation must appear in completeness reporting. Attribute name/value pairs must retain their original roles, so the string "title" in an attribute-name slot cannot become an href value. ## Actual behavior Independent executed probes produced names ["#document","SPAN",""] for the malformed name case and parents [-1,1,-1] for the malformed parent case. The attribute case returned attribute_names=["href","description"] and a DOM URL of https://app.example.test/title, although the snapshot supplied no valid href value. Every malformed case left unavailable_sections=[] and excluded=[]. The control retained names ["#document","DIV","SPAN"] and parents [-1,0,1]. The numeric-array conversion discards invalid entries separately before consumers index the arrays. This changes the positions of later facts and attribute pairs. The output can satisfy the result schema even when the facts belong to different nodes. The observed omission concerns DOM reporting; it does not establish that the entire inspection has no unrelated coverage limitations. ## Evidence - Expected source: docs/browser-observation.md:160-162 requires incomplete or truncated values to be reported; :173 promises DOM node fact retention; :189-197 requires reporting validation omissions. AGENTS.md:29 requires preservation of partial facts, meaningful failure reasons and explicit unknowns. - Failure source: src/browser/CdpCaptureDocuments.ts:168-196 consumes independently filtered arrays; :377-383 removes invalid entries and changes their indexes. Independent focused probes produced the outputs described above, including unchanged omission ledgers. - Evidence provenance: observed - Local verification: reproduced - Reproduction completeness: complete One focused file ran four tests successfully, covering the valid control and the three defective outputs. The inline reproduction above retains the same inputs and action in one test. No real browser, CLI or MCP capture was executed. No occurrence on a conforming browser was demonstrated. Production reachability is established from source. src/browser/CdpPageCapture.ts:267-282 passes a DOMSnapshot result to the normalizer with the capture ledger, and :300 projects that ledger into inspection. src/browser/CdpConnection.ts:238-261 accepts a correlated result object without checking nested node arrays. src/domain/browserObservationSchemas.ts:50-57 validates output field types without checking original fact ownership. Both inspection and bundle analysis share this capture path. ## Restoration check Repeat the malformed name and parent cases. If valid nodes remain, the SPAN name and parent 1 must still describe the original third node; the invalid middle entries must not acquire those facts. Explicit omission of affected evidence is also acceptable when completeness reports the gap. Malformed DOM values must produce a DOM invalid_protocol_value exclusion and mark DOM unavailable. In the attribute case, no URL may be inferred from "title" and "description" must not become an attribute name. The numeric control must continue to retain all three names and parents with no DOM exclusion. ## Runtime or environment Failure was verified at a2d8b94dd50dab8d966239dd63ab84718aa8b4b4 and is still present at 8b181716aca2cb467be499cba226abfb5ae19141 by source recheck, package rea-agents 6.4.0, Node v26.10.0, npm 11.19.1 and locked Vitest 5.0.3. Verification used a direct normalizer test rather than a browser session. ## Triage assessment - Impact level: P2 - Assessment status: supported - Impact basis: Malformed nested protocol values produce consequential false DOM structure and URL metadata while concealing the affected coverage gap. The observed defect is bounded to malformed replies; normal-browser frequency and downstream harm are unknown. - Workaround status: unverified - Workaround basis: A fully numeric aligned snapshot passed the control, so a conforming CDP reply avoids this trigger. No practical recovery route for a user already receiving malformed replies was tested.
bsakaguchi 2026-10-15 18:29:09 +00:00
Author
Owner

@morluto commented on GitHub (Oct 14, 2026):

Addressed on main in bffdd91053fb5d076b762cf1b325e8dcb5ab2d8c, e8d22ec9e.

Malformed parallel DOM arrays and string-table references no longer shift facts or invent empty values. Invalid nodes are omitted with source index gaps and explicit incompleteness; malformed SVG ancestry remains unknown.

Validation: Affected suites passed in the merged 65-file run (1169 passed, 2 explicit skips), with full static/architecture checks and documentation checks passing. Final installed-package verification is recorded separately in the audit.

Verification scope: Controlled source and production-boundary verification on macOS with pinned Node 24.18.0; real external engines and Windows hosts were not independently rerun. Closure covers the described source defect, not broader provider conformance or performance.

<!-- gh-comment-id:6159206202 --> @morluto commented on GitHub (Oct 14, 2026): Addressed on main in [bffdd91053fb5d076b762cf1b325e8dcb5ab2d8c](https://github.com/morluto/rea/commit/bffdd91053fb5d076b762cf1b325e8dcb5ab2d8c), [e8d22ec9e](https://github.com/morluto/rea/commit/e8d22ec9e). Malformed parallel DOM arrays and string-table references no longer shift facts or invent empty values. Invalid nodes are omitted with source index gaps and explicit incompleteness; malformed SVG ancestry remains unknown. Validation: Affected suites passed in the merged 65-file run (1169 passed, 2 explicit skips), with full static/architecture checks and documentation checks passing. Final installed-package verification is recorded separately in the audit. Verification scope: Controlled source and production-boundary verification on macOS with pinned Node 24.18.0; real external engines and Windows hosts were not independently rerun. Closure covers the described source defect, not broader provider conformance or performance.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
morluto/rea#868
No description provided.