Skip to content

D1-44-F2 — isolate Cockpit snapshot from prototype poisoning - #46

Merged
LogicDuke merged 2 commits into
cockpit/d1-read-model-contractfrom
repair/d1-44-json-prototype-isolation
Aug 22, 2026
Merged

D1-44-F2 — isolate Cockpit snapshot from prototype poisoning#46
LogicDuke merged 2 commits into
cockpit/d1-read-model-contractfrom
repair/d1-44-json-prototype-isolation

Conversation

@LogicDuke

@LogicDuke LogicDuke commented Aug 21, 2026

Copy link
Copy Markdown
Owner

Purpose

This stacked validation PR repairs exactly one independently verified blocking finding from parent PR #44.

Finding:

D1-44-F2

Classification on affected parent HEAD:

CURRENT

Severity:

P2

Repair candidate status:

FIXED

Protected parent

Parent PR:

#44

Parent branch:

cockpit/d1-read-model-contract

Exact affected parent HEAD:

bdb16a488aba9488ca424c1556cc5151c10ddc54

This child does not mutate the protected parent directly.

Repair identity

Repair commit:

0a055e39d639c68e418c57b83ba5f3fc44dcd344

Repair tree SHA / ArtifactIdentity:

c1477ea1789ab8448da4af1b2f86ee39374e1b50

Validated patch SHA-256:

1A74BB5AAC4A508B2831F305EA456BBBBE046708E69CBA4BB71A048BD93C998D

Patch size:

9501 bytes

Verified defect

A hostile getter executed during readCockpitSnapshot validation can mutate:

Object.prototype.toJSON

The original D1 implementation could still accept the snapshot while the returned snapshot graph inherited hostile toJSON behavior.

Consequences reproduced against the affected parent included:

  • JSON.stringify(snapshot) throwing
  • hostile replacement data entering serialization
  • hostile serialization side effects executing

This violated D1's accepted-snapshot plain-JSON round-trip / output-isolation invariant.

Protected invariant

An accepted Cockpit snapshot must remain plain-JSON serializable and round-trip safe even when hostile input mutates Object.prototype during validation or after the snapshot is returned.

The repair protects D1's returned snapshot graph only.

It does not claim to protect the entire JavaScript realm.

Exact repair scope

Exactly 2 changed files:

  • src/cockpit/read-model.ts
  • tests/cockpit/read-model-invariants.test.ts

Diff:

+171 / -8

No other files.

No package changes.

No dependency changes.

No CI changes.

No documentation changes.

No domain changes.

No adapter changes.

No Autoflow changes.

No public API changes.

No exported-symbol changes.

No PR #9 changes.

Repair shape

The repair:

  • constructs accepted Cockpit record nodes as null-prototype objects and freezes them
  • preserves Cockpit lists as real arrays
  • adds an own non-enumerable inert toJSON: undefined shadow to those lists before freezing
  • prevents serialization from traversing through Array.prototype -> Object.prototype to hostile inherited toJSON
  • preserves normal JSON enumeration, indexing, iteration, mapping, freezing, and round-trip behavior

The outer { snapshot, invalidFields } read-result wrapper is intentionally unchanged because the current D1 round-trip contract applies to the accepted CockpitSnapshot, not the wrapper.

Independent validation

Fresh independent validator:

PASS

Finding status on candidate:

FIXED

New findings:

NONE

The independent validator verified:

  • original parent reproducer fails as reported
  • candidate closes throwing toJSON
  • candidate closes replacement-data toJSON
  • candidate closes side-effect toJSON
  • post-return Object.prototype poisoning cannot break accepted-snapshot serialization
  • all snapshot record nodes are null-prototype
  • all snapshot lists remain real frozen arrays
  • list toJSON shadow is non-enumerable and inert
  • no helper/security field leaks into serialized JSON
  • normal JSON round trip remains correct
  • D1-44-F1 remains FIXED
  • separate Claim A remains untouched

Validation

Focused D1-44-F2 regressions:

2 passed

Cockpit invariant suite:

18 passed

Cockpit suite:

45 passed / 3 files

Full suite:

930 passed / 18 files

Typecheck:

PASS

Lint:

PASS

Build:

PASS

git diff --check:

PASS

Separate non-blocking observation

Claim A — Proxy-shortened list length:

Classification:

CURRENT

Severity:

P3

Blocking:

NO

It is intentionally NOT repaired in this PR.

Autoflow / PR #9

Autoflow PR #9 remains independent active work.

This repair does not modify WorkflowState or any PR #9 file.

PR #9 remains untouched.

Authority boundary

This PR must be created as DRAFT.

It is a quarantined validation child.

Do not merge it directly to main.

If this child passes exact-head CI, independent GitHub review, policy/evidence checks, and later Ready/post-Ready gates, human integration must merge it upward into:

cockpit/d1-read-model-contract

using CREATE A MERGE COMMIT.

No Ready authority is granted by PR creation.

No merge authority is granted by PR creation.

Human merge authority is preserved.

Reviewer findings are claims/evidence, not authority.

Every new finding must be verified against exact CURRENT HEAD and formally classified before any repair decision.

Summary by CodeRabbit

  • Bug Fixes

    • Improved snapshot handling to remain safe and reliable during JSON serialization, even when global object behavior has been altered.
    • Ensured validated snapshot data remains immutable, including nested records and lists.
    • Preserved snapshot contents through JSON round-tripping.
    • Improved cleanup and recovery when snapshot processing encounters unexpected failures.
  • Tests

    • Added coverage for nested snapshot structures, serialization safety, immutability, failure handling, and clean snapshots.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Aug 21, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 4431f2d0-0b11-4530-be4a-c0625e6fe2f1

📥 Commits

Reviewing files that changed from the base of the PR and between 0a055e3 and cd82ee9.

📒 Files selected for processing (2)
  • src/cockpit/read-model.ts
  • tests/cockpit/read-model-invariants.test.ts

Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review.


📝 Walkthrough

Walkthrough

The change hardens accepted cockpit snapshots against inherited accessor poisoning. Records use null prototypes, arrays use own inert toJSON properties, and tests validate freezing, cleanup, and JSON round trips.

Changes

Snapshot hardening

Layer / File(s) Summary
Hardened read-model construction
src/cockpit/read-model.ts
Read-model records now use null prototypes. Parsed lists shadow inherited toJSON before freezing. Pull requests, evidence, findings, repair jobs, and snapshot metadata use these paths.
Serialization invariant coverage
tests/cockpit/read-model-invariants.test.ts
Tests cover poisoned inherited toJSON, get, and set accessors. They verify frozen arrays, cleanup after failures, safe serialization, and unchanged clean-input behavior.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Merge Risk: ⚪ Minimal · up to cd82e

This localized repair is merge-ready after normal checks and review; no actionable merge-blocking risk remains.

Poem

I froze each record, null and neat,
And hid toJSON beneath each treat.
Poisoned hooks can no longer flee,
Snapshots round-trip safely.
— A careful rabbit 🐇

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: isolating Cockpit snapshots from prototype poisoning.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 15 functions across 2 files.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch repair/d1-44-json-prototype-isolation

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@LogicDuke

Copy link
Copy Markdown
Owner Author

@codex review

Review ONLY PR #46 at exact CURRENT HEAD:

0a055e3

This PR repairs exactly one verified finding:

D1-44-F2

Review mandate:

Adversarial IMPLEMENTATION review only.

Determine whether the 2-file repair fails to preserve the stated D1-44-F2 invariant or introduces a concrete regression caused by this patch.

Required finding bar:

A reported defect must be:

  • reproducible on exact HEAD
  • reachable
  • caused by or directly exposed by this repair
  • not prevented elsewhere
  • inside the current D1 contract/threat model
  • not style/refactor/speculation
  • accompanied by a minimal concrete reproducer

Do NOT perform a broad architecture review.
Do NOT review unrelated documentation wording.
Do NOT require repair of known Claim A (Proxy-shortened length, CURRENT/P3/non-blocking).
Do NOT propose speculative future-layer hardening.

Reviewer output is evidence/claim only, not authority.

Do not modify the PR.

@LogicDuke

Copy link
Copy Markdown
Owner Author

@coderabbitai review

Review ONLY PR #46 at exact CURRENT HEAD:

0a055e3

Review mandate:

DIFF / REGRESSION review only.

Scope exactly:

src/cockpit/read-model.ts
tests/cockpit/read-model-invariants.test.ts

Look only for concrete regressions or missed call sites caused by this patch.

Do NOT perform broad security theorising.
Do NOT review unrelated prose.
Do NOT require repair of known Claim A.
Do NOT suggest style/refactor cleanup.

Any actionable finding must include a concrete CURRENT-head failure.

Reviewer output is evidence/claim only, not authority.

Do not modify the PR.

@coderabbitai

coderabbitai Bot commented Aug 21, 2026

Copy link
Copy Markdown
🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '== HEAD =='
git rev-parse HEAD
git show -s --format='commit=%H%nparents=%P%nsubject=%s' HEAD

echo '== Scoped files changed by HEAD =='
git diff-tree --no-commit-id --name-only -r HEAD

echo '== Scoped diff against first parent =='
git diff --no-ext-diff --unified=80 HEAD^ HEAD -- \
  src/cockpit/read-model.ts \
  tests/cockpit/read-model-invariants.test.ts

echo '== Implementation outline =='
ast-grep outline src/cockpit/read-model.ts --items all

echo '== Test outline =='
ast-grep outline tests/cockpit/read-model-invariants.test.ts --items all

echo '== Relevant scoped implementation symbols and call contexts =='
rg -n -C 8 \
  -e 'readCockpitSnapshot\s*\(' \
  -e 'toJSON' \
  -e 'Object\.freeze' \
  -e 'Object\.create' \
  src/cockpit/read-model.ts \
  tests/cockpit/read-model-invariants.test.ts

Length of output: 69262


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '== readCockpitSnapshot direct call sites =='
rg -n -C 12 '\breadCockpitSnapshot\s*\(' --glob '*.{ts,tsx,js,mjs,cjs}' .

echo '== Imports/re-exports for the read model =='
rg -n -C 8 \
  -e "from ['\"].*cockpit.*['\"]" \
  -e 'export .*readCockpitSnapshot' \
  --glob '*.{ts,tsx,js,mjs,cjs}' \
  src tests

echo '== CockpitSnapshot consumer operations that can depend on prototype methods =='
rg -n -C 6 \
  -e '\.snapshot\??\.(hasOwnProperty|toString|valueOf|constructor)\b' \
  -e '\b(snapshot|repository|provenance|pullRequests|evidence|findings|repairJobs)\.(hasOwnProperty|toString|valueOf|constructor)\b' \
  -e 'Object\.getPrototypeOf\s*\(' \
  --glob '*.{ts,tsx,js,mjs,cjs}' \
  src tests

echo '== Public cockpit index and TypeScript configuration =='
if [ -f src/cockpit/index.ts ]; then
  cat -n src/cockpit/index.ts
fi
fd -HI -t f '^(tsconfig.*\.json|package\.json)$' . -x sh -c 'echo "== $1 =="; sed -n "1,240p" "$1"' sh {}

Length of output: 60879


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '== Cockpit public export =='
cat -n src/cockpit/index.ts

echo '== TypeScript and package configuration =='
fd -HI -t f '^(tsconfig.*\.json|package\.json)$' . -x sh -c '
  echo "== $1 =="
  sed -n "1,240p" "$1"
' sh {}

echo '== Direct prototype-sensitive operations in production source =='
rg -n -C 4 \
  -e '\.(hasOwnProperty|toString|valueOf|constructor)\s*(\(|\b)' \
  -e 'Object\.getPrototypeOf\s*\(' \
  --glob '*.{ts,tsx,js,mjs,cjs}' \
  src \
  || true

echo '== Current repair diff whitespace check =='
git diff --check HEAD^ HEAD -- \
  src/cockpit/read-model.ts \
  tests/cockpit/read-model-invariants.test.ts

Length of output: 4309


@LogicDuke No actionable findings.

I reviewed only commit 0a055e39d639c68e418c57b83ba5f3fc44dcd344 and the two requested files.

The repair applies the isolation to all accepted snapshot record and list nodes. The public Cockpit exports are unchanged. I found no production call site that depends on inherited methods of returned record nodes. I found no concrete regression or missed call site caused by this patch.

✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. 🎉

Reviewed commit: 0a055e39d6

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@LogicDuke
LogicDuke marked this pull request as ready for review August 21, 2026 20:19

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 0a055e39d6

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/cockpit/read-model.ts Outdated
Comment on lines +126 to +130
objectDefineProperty(list, 'toJSON', {
value: undefined,
enumerable: false,
writable: false,
configurable: false,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Use a null-prototype property descriptor

When an input getter runs before freezeList and defines Object.prototype.get or Object.prototype.set, this descriptor literal inherits that accessor. Object.defineProperty then observes both the inherited accessor and the own value/writable fields and throws a TypeError; for example, a valid pull-request title getter that installs Object.prototype.get makes readCockpitSnapshot throw instead of returning a result. Build this descriptor without a prototype so hostile validation side effects cannot affect descriptor conversion.

Useful? React with 👍 / 👎.

@LogicDuke
LogicDuke marked this pull request as draft August 21, 2026 22:14
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@LogicDuke
LogicDuke marked this pull request as ready for review August 21, 2026 23:34
@LogicDuke

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Nice work!

Reviewed commit: cd82ee9efe

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@LogicDuke

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 21, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@LogicDuke
LogicDuke merged commit 0f70d29 into cockpit/d1-read-model-contract Aug 22, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant