Skip to content

D3 finite source guard — bounded replacement - #70

Closed
LogicDuke wants to merge 3 commits into
cockpit/d3-readonly-dashboard-hostfrom
rebuild/d3-finite-source-guard
Closed

D3 finite source guard — bounded replacement#70
LogicDuke wants to merge 3 commits into
cockpit/d3-readonly-dashboard-hostfrom
rebuild/d3-finite-source-guard

Conversation

@LogicDuke

Copy link
Copy Markdown
Owner

D3 finite source guard — bounded replacement

Built fresh from the exact PR #55 HEAD 5ae2b786ad6dc4653286d4c2b50e1fd705daa974. PR #64 and PR #67 are retired / closed / unmerged; none of their analyzer implementations were resumed, copied, cherry-picked, or recreated.

What this is

A finite reviewed-source policy guard only. It asserts bounded facts about the written source of the read-only Cockpit D3 dashboard host — nothing about runtime behavior. Concretely it proves:

  • Pinned executable ESM closure — the runtime dependency closure of src/cockpit-host/, resolved through the TypeScript AST and normal relative-module resolution, equals a pinned set of files. Only runtime edges count; type-only imports/exports are erased.
  • Exact builtin allowlist derived from the real sourcenode:http, node:url only (the builtins the current host actually imports); every other builtin is rejected.
  • Bare runtime require(...) is forbidden (and import x = require(...)); no CommonJS .cts/.cjs execution.
  • Computed / unverifiable dynamic import(...) fails closed; a static-string dynamic import is a supported, resolvable edge.
  • Exactly one authored http.createServer(...) site exists in the authored closure (the real host), and its authored listen(...) uses the loopback bind 127.0.0.1, with the legitimate port constant preserved where statically resolvable.

Scope of the network claim (honest bound)

  • Literal / runtime no-egress is NOT claimed by this source guard. It bounds the imported networking capability surface (no networking module importable beyond the single loopback node:http server), not runtime behavior. Node's node:http client API and global fetch/WebSocket are outside what an import-closure source guard can prove.
  • Stronger no-egress belongs to a future runtime / process / OS isolation boundary, not to this source-policy check.

Changed files (exactly)

  • tests/cockpit-host/purity.test.ts
  • tests/cockpit-host/executable-closure.ts
  • docs/architecture/D3-cockpit-dashboard-host.md

Explicitly NOT present in this replacement

This guard deliberately implements none of the following (and adds no such machinery):

The single source of truth for "what is an import" is the official typescript parser AST — there is no custom JavaScript lexer/parser, no value/fact propagation, no receiver or global tracking, and no runtime code-generation route scanning.

🤖 Generated with Claude Code

https://claude.ai/code/session_016A9gwyNtdnZzRHduGzih11

@coderabbitai

coderabbitai Bot commented Sep 3, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Team

Run ID: c441e5dd-b591-4736-b69c-c019226fefaf

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

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
LogicDuke marked this pull request as ready for review September 3, 2026 21:17
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 3, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-03T21:52:04.784192Z df72c2b Draft marked ready
ℹ️ 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@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: 434204ecf5

ℹ️ 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".

if (arg === undefined) return { kind: 'unresolved' };
if (ts.isStringLiteral(arg)) return { kind: 'string', value: arg.text };
if (ts.isNumericLiteral(arg)) return { kind: 'number', value: Number(arg.text) };
if (ts.isIdentifier(arg)) return resolveConstLiteral(sourceFile, arg.text);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Resolve the listen argument in its lexical scope

If main introduces a valid local shadow such as const HOST = '0.0.0.0' and passes it to server.listen, this lookup ignores that binding and resolves the identifier to the top-level HOST = '127.0.0.1'. The loopback test therefore passes while the server actually binds all IPv4 interfaces; resolve the identifier's lexical declaration, or fail closed when it is not the expected top-level constant.

Useful? React with 👍 / 👎.

Comment on lines +378 to +382
const isMemberSite =
ts.isPropertyAccessExpression(callee) &&
callee.name.text === 'createServer' &&
ts.isIdentifier(callee.expression) &&
defaultOrNamespace.has(callee.expression.text);

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 Count wrapped direct createServer calls

If another closure file calls (http).createServer(...) or http['createServer'](...), the call still directly targets the imported node:http binding but this predicate ignores it because the receiver is not a bare identifier/property-access pair. Adding such a second authored server leaves the reported site count at one, so the new exact-one invariant passes; unwrap transparent expression wrappers and recognize static element access before matching.

Useful? React with 👍 / 👎.

@LogicDuke
LogicDuke marked this pull request as draft September 3, 2026 21:34
…er wrappers

Close the two Codex findings on the finite D3 source guard, keeping both
rules strictly structural (no alias/value-flow, fixpoint, Proxy/Reflect,
RC/HA, taint, or runtime logic):

- Rule D listen host/port now resolves the identifier to the declaration
  actually visible at the use site (innermost lexical scope), so a nearer
  const literal shadows the top-level one and a let/var, parameter,
  destructured, for-loop/catch, or non-literal binding fails closed.
- Rule D createServer recognition normalizes transparent syntax only —
  parenthesized receivers and static element access such as
  http['createServer'] — so a second wrapped authored site is counted.

Adds regressions for both, including sibling-scope isolation and the
adjacent shadow/wrapper forms.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016A9gwyNtdnZzRHduGzih11
@LogicDuke
LogicDuke marked this pull request as ready for review September 3, 2026 21:44

@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: df72c2bfc4

ℹ️ 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 on lines +281 to +285
const target = resolution.target;
edges.push({ from: fromRel, specifier, to: toRepoRelative(target), kind });
if (!closure.has(target)) {
closure.add(target);
queue.push(target);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Reject direct host imports that bypass Cockpit

When a file under src/cockpit-host/ directly imports an already-transitive dependency such as ../domain/review.js, this code merely records the edge and finds the target already present in EXPECTED_CLOSURE, so every new closure assertion remains green. This removes the documented boundary that host code may reach domain truth only through src/cockpit/, allowing future rendering code to bypass the D1/D2 validation and projection layer without CI noticing; validate host-originated edges against the host/Cockpit roots in addition to checking closure membership.

Useful? React with 👍 / 👎.

const file = queue.shift();
if (file === undefined) break;
const fromRel = toRepoRelative(file);
const references = collectRuntimeReferences(file, readFileSync(file, 'utf8'));

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Restore detection of authored environment reads

If authored host code reads process.env—for example, interpolating a token into a response—the new guard reports no reference or violation because it inventories only module edges and calls to require/import. The replacement deleted the previous environment-access check while the D3 architecture still promises no environment or secret access, so such a type-correct change now passes this purity suite and can leak secrets; retain a finite source check for direct environment reads or explicitly remove that security guarantee.

Useful? React with 👍 / 👎.

Comment on lines +431 to +432
if (element.propertyName?.text === 'createServer' || element.name.text === 'createServer') {
named.add(element.name.text);

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 Match the imported name before counting createServer

For an aliased import such as import { request as createServer } from 'node:http', element.name.text is createServer, so a subsequent request call is counted as the required HTTP server creation even though the imported export is request. The exact-one assertion can therefore pass after the real http.createServer site is replaced while still claiming that a direct server-creation site exists; when propertyName is present, only that imported name should determine whether the binding represents createServer.

Useful? React with 👍 / 👎.

@LogicDuke
LogicDuke marked this pull request as draft September 3, 2026 22:08
@LogicDuke LogicDuke closed this Sep 3, 2026
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