Skip to content

Add integration anchors for sealed re-declaration and local-principal isolation - #3808

Merged
kriszyp merged 5 commits into
mainfrom
test/promote-sealed-update-and-local-principal-anchors
Oct 30, 2026
Merged

kriszyp merged 5 commits into
mainfrom
test/promote-sealed-update-and-local-principal-anchors

Conversation

@kriszyp

@kriszyp kriszyp commented Oct 30, 2026 •

Copy link
Copy Markdown
Member
A workshop guard returns a parcel with an undeclared teddy bear to a sealed vault door while another refuses a wooden mannequin wearing a local name tag

⊙ Problem

Two security regressions fixed on main — #3594 (sealed tables with tracked updates across a table re-declaration) and #3755 (the local principal) — had only single-process or domain-socket coverage. Nothing checked them across two HTTP workers, or from a real non-loopback source address.

🤔 Is a test-only PR the right fix?

As specified, yes. Both fixes are on main; what is missing is a test that fails when they regress.

💡 Solution

Two integration suites pass on RocksDB and LMDB and fail on the pre-fix base.

  • database/sealed-redeclare-tracked-update: a sealed table rejects an undeclared own property assigned on a Table.update() instance next to a declared edit, with 2 HTTP workers. Rejected arms assert detail[0].code === 'unknown_property' and an unchanged updatedAt.
  • security/local-principal-isolation: nothing from a non-loopback address becomes the local principal (headers, body hdb_user, pre-auth header, replayed cookies, MQTT, minted tokens, scoped token, reserved name).

🤔 Which non-loopback address does the security suite use?

HARPER_TEST_NON_LOOPBACK_ADDRESS, else the first external IPv4 interface of the host. With neither, the suite skips with that reason. A CI runner with no external IPv4 would skip silently; the other choice is a dedicated network namespace.

🔧 Changes

✅ Verification

Docs: none; test-only.

🤖 Generated by Anthropic Claude (Sonnet 5.5, dispatch dev agent); posted via @kriszyp.

Release target: v5.4 — test-only; the fixes under test are not on v5.3
Related PRs: #3594 overlaps, #3653 independent, #3803 independent, #3610 independent
Complexity: medium

Late Grok findings

12 surviving (highest minor) — Grok reviewed 5b7ab01
  • minor — integrationTests/database/sealed-redeclare-tracked-update/resources.js:58 — a held handle can be aborted by the open-transaction limit inside the time budgets the test allows itself. Arms then fail with an "open too long" 422 instead of unknown_property, and the setup test still passes
  • minor — integrationTests/database/sealed-redeclare-tracked-update.test.ts:163 — request() has no timeout, and onThread/waitFor check their deadlines only between awaits. A stalled exchange hangs the before hook with no attribution instead of failing it
  • minor — integrationTests/security/local-principal-isolation.test.ts:614 — note() (console.log, line 51) writes credential values to CI stdout
  • minor — integrationTests/database/sealed-redeclare-tracked-update.test.ts:378 — the \b7\b|\b99\b scan runs over successful response bodies, which contain threadId. A worker whose threadId is 7 or 99 fails A4, A4p or B4 although no attempted value was stored
  • minor — integrationTests/security/local-principal-isolation.test.ts:752 — arm 6 can pass vacuously, and its look-alike users are created as super users
  • nit — integrationTests/security/local-principal-isolation.test.ts:71 — freePorts closes the probe servers without awaiting the close, and openMqtt (lines 228-253) has no connect or upgrade timeout
  • nit — integrationTests/security/local-principal-isolation.test.ts:546 — after a refused CONNACK, mqttAttempt returns before publishing (line 289). The itemStatus(remote.itemId) 404 check (also at line 853) therefore cannot fail once line 544 passes
  • nit — integrationTests/database/sealed-redeclare-tracked-update.test.ts:419 — body.threadId ?? threadA falls back to threadA on 4xx bodies, so the assertion is always true on rejected arms
  • nit — integrationTests/database/sealed-redeclare-tracked-update.test.ts:331 — a convergence failure is caught, the gate still opens, and the arms run against an unconverged schema
  • nit — integrationTests/database/sealed-redeclare-tracked-update.test.ts:261 — added comments narrate the next line or act as section banners
  • nit — integrationTests/security/local-principal-isolation/resources.js:3 — the fixture uses the loader-injected Resource and databases globals, while the sibling fixture in this PR imports from harper
  • nit — integrationTests/database/sealed-redeclare-tracked-update.test.ts:17 — the suite title includes [engine=…], but the run note gives no HARPER_STORAGE_ENGINE=lmdb invocation

Not standing: 3 merged. Grok ran 19m (19m from launch to report); the fold adjudication (cursor-grok@5b7ab01b1c30-97417a8d, claude) took 2m.

Review-Coverage: authored=claude; ran=cursor-composer,gemini,codex,cursor-grok; late=cursor-grok:adjudicated; adjudicated=domain,domain(codex); declined=muse; rounds=4; full=2 @ 052b543

Review-Attention: read ~4m (decisions: do-less-alternative, platform-scope, retired-field-semantics) @ 052b543

kriszyp and others added 4 commits October 30, 2026 09:13
… isolation

Promotes two security-lane regression specs.

sealed-redeclare-tracked-update (#3594): a sealed table refuses an undeclared own
property assigned on a tracked Table.update() instance, across a two-worker
re-declaration. Rejected arms now assert detail[0].code === 'unknown_property' and
an unchanged updatedAt.

local-principal-isolation (#3755): nothing from a non-loopback source becomes the
`local` principal. The listener address is HARPER_TEST_NON_LOOPBACK_ADDRESS or the
first external IPv4 interface (suite skipped when there is none), ports are
allocated per run, the scoped-token mint must succeed, the dead `name === 'local'`
clause is gone, and a new check covers a principal named `local` without the
_localPrincipal marker.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Dispatch-Task: harper-promote-sealed-update-and-local-principal-anchors
Bound network waits, fail the sealed setup when a worker does not converge, keep
tokens and cookies out of the logs, use a plain role for the look-alike users so a
local-principal substitution cannot hide behind super_user, and drop the dead
MQTT poll.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Dispatch-Task: harper-promote-sealed-update-and-local-principal-anchors
The gate budget is 60 s; the HTTP idle timeout and the 30 s default
maxTransactionOpenTime sat inside it.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Dispatch-Task: harper-promote-sealed-update-and-local-principal-anchors
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Dispatch-Task: harper-promote-sealed-update-and-local-principal-anchors

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request introduces comprehensive integration tests covering sealed table re-declarations across multiple workers and local principal isolation security mechanisms. The review feedback identifies two potential sources of test flakiness: a regex assertion in the sealed table tests that could falsely fail if a worker's thread ID matches an excluded value, and asynchronous session writes in the security tests that should be polled with a timeout rather than asserted immediately.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Dispatch-Task: harper-promote-sealed-update-and-local-principal-anchors
@kriszyp
kriszyp marked this pull request as ready for review October 30, 2026 16:47
@kriszyp
kriszyp merged commit 87ff4bf into main Oct 30, 2026
69 of 70 checks passed
@kriszyp
kriszyp deleted the test/promote-sealed-update-and-local-principal-anchors branch October 30, 2026 16:47
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