Skip to content

feat(compiler): SYN008 — warn on WebSocket() calls that bypass the net capability model (?bs 0.7+)#149

Open
marcelofarias wants to merge 8 commits into
mainfrom
botkowski/syn008-websocket-bypass
Open

feat(compiler): SYN008 — warn on WebSocket() calls that bypass the net capability model (?bs 0.7+)#149
marcelofarias wants to merge 8 commits into
mainfrom
botkowski/syn008-websocket-bypass

Conversation

@marcelofarias

Copy link
Copy Markdown
Owner

Summary

  • Adds SYN008: a new warning that fires when a fn body constructs a WebSocket via new WebSocket(url), WebSocket(url), or TypeScript instantiation forms like new WebSocket<T>(url) at ?bs 0.7+
  • WebSocket opens a persistent bidirectional connection at runtime but is invisible to CAP001 (which only checks http.* member calls). A fn that constructs a WebSocket has an undeclared net dependency: no uses { net } in the header, no signal to callers, no capability manifest entry
  • Unlike fetch (SYN007), there is no stdlib WebSocket wrapper — the idiomatic fix is unsafe "reason" { new WebSocket(url) } rather than replacing with an http.* call
  • Same bypass class as fetch (SYN007) and console.* (SYN003): real network effects, invisible to the declared capability surface
  • Suppressed inside unsafe "reason" { } blocks and unsafe "reason" fn bodies
  • Note: should be merged after PR feat(compiler): SYN007 — warn on fetch() calls that bypass the net capability model (?bs 0.7+) #148 (SYN007) lands; SYN008 is the next available code

Changes

File Change
packages/compiler/src/error-codes.ts Add SYN008 entry (rule, idiom, rewrite, example)
packages/compiler/src/passes/syn-check.ts Detection: WebSocket not preceded by ./?., followed by (, ?.(, or <T>( (with correct >>/>>> depth scan); unsafe suppression
packages/compiler/tests/syn008-check.test.ts 11 tests: fires/not-fires cases, severity, unsafe suppression, member-call exclusion, optional-call and generic forms, nested-generic form
packages/compiler/tests/error-codes.test.ts Add SYN008 to exhaustive allowlist
packages/mcp/src/explanations.ts Add SYN008 long-form explanation with fails/passes examples
packages/mcp/tests/server.test.ts Add SYN008 to KNOWN_CODES list
AGENTS.md Add SYN008 row to diagnostic table
README.md Add SYN008 to explain tool code list

Test plan

  • pnpm -r build passes
  • npx vitest run — 11/11 SYN008 tests pass, 1160 total tests pass
  • SYN008 fires on new WebSocket(url), new WebSocket(url, protocols)
  • SYN008 fires on bare WebSocket(url) (without new)
  • SYN008 fires on TypeScript instantiation form new WebSocket<MessageEvent>(url)
  • SYN008 fires on nested generic form new WebSocket<EventSource<MessageEvent>>(url)
  • SYN008 fires on WebSocket?.(url) optional call
  • SYN008 does NOT fire below ?bs 0.7
  • SYN008 does NOT fire inside unsafe {} or unsafe fn
  • SYN008 does NOT fire on obj.WebSocket(...) (member call on a local)
  • SYN008 does NOT fire on bare WebSocket reference (not called)
  • MCP explain tool returns long-form for SYN008

🤖 Generated with Claude Code

…t capability model (?bs 0.7+)

- `new WebSocket(url)`, `WebSocket(url)`, and TypeScript instantiation
  forms like `new WebSocket<T>(url)` open persistent bidirectional
  connections that are invisible to CAP001 (which only checks `http.*`
  member calls). A fn that constructs a WebSocket has an undeclared net
  dependency — no `uses { net }` reflects it.
- Same bypass class as fetch (SYN007) and console.* (SYN003): real
  network effects, invisible to the declared capability surface.
- Unlike SYN007, there is no stdlib WebSocket wrapper, so the idiomatic
  fix is to wrap in `unsafe "reason" { new WebSocket(url) }` rather than
  replacing with an http.* call.
- Suppressed inside `unsafe {}` blocks and `unsafe "reason" fn` bodies.
- Detection: `WebSocket` not preceded by `.`/`?.`, followed by `(`,
  `?.(`, or `<T>(`. Nested generics (e.g. `WebSocket<A<B>>`) handled
  correctly via `>> / >>>` depth scan.

Files:
  packages/compiler/src/error-codes.ts       — SYN008 registry entry
  packages/compiler/src/passes/syn-check.ts  — detection pass
  packages/compiler/tests/syn008-check.test.ts — 11 tests
  packages/compiler/tests/error-codes.test.ts  — add SYN008 to allowlist
  packages/mcp/src/explanations.ts           — long-form MCP explanation
  packages/mcp/tests/server.test.ts          — add SYN008 to KNOWN_CODES
  AGENTS.md / README.md                      — document SYN008

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

Copilot AI 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.

Pull request overview

Adds compiler + MCP support for SYN008, a new ?bs 0.7+ warning that detects new WebSocket(...) / WebSocket(...) (including optional-call and TS generic-instantiation forms) as a capability-model bypass, with suppression inside unsafe {} and unsafe fn bodies.

Changes:

  • Register SYN008 in the compiler diagnostic registry and add token-based detection in syn-check.
  • Add a dedicated SYN008 test suite plus updates to “exhaustive allowlist” tests.
  • Extend MCP/Docs surfaces (explain, long-form explanation, AGENTS/README) to include SYN008.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
README.md Adds SYN008 to the explain tool’s supported diagnostic code list.
AGENTS.md Documents SYN008 in the diagnostics table.
packages/compiler/src/error-codes.ts Adds SYN008 registry entry (rule/idiom/rewrite/example).
packages/compiler/src/passes/syn-check.ts Implements SYN008 detection + suppression behavior.
packages/compiler/tests/syn008-check.test.ts Adds SYN008 behavior tests (fires / not-fires / suppression / forms).
packages/compiler/tests/error-codes.test.ts Adds SYN008 to the exhaustive compiler diagnostic allowlist.
packages/mcp/src/explanations.ts Adds long-form MCP explanation for SYN008 with fails/passes examples.
packages/mcp/tests/server.test.ts Adds SYN008 to the stable KNOWN_CODES expectation list.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread packages/compiler/src/passes/syn-check.ts Outdated
Comment thread packages/compiler/src/passes/syn-check.ts
…fires without http.* calls

Copilot review: the doc comment and warning message both suggested
`uses { net }` as part of the fix, but `uses { net }` without an
underlying `http.*` call triggers CAP002 (over-declared capability).
Fix both to recommend `unsafe { ... }` with an optional
`$require("net")`-checked wrapper instead.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

Copilot AI 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.

Pull request overview

Copilot reviewed 8 out of 8 changed files in this pull request and generated 3 comments.

Comment thread packages/compiler/src/passes/syn-check.ts
Comment thread packages/mcp/src/explanations.ts Outdated
Comment thread packages/mcp/src/explanations.ts Outdated
- Add SYN006 to header comment (was missing between SYN005 and SYN008)
- Remove forward references to SYN007 in explanations.ts — SYN007 is not
  in this branch; reword to describe the fetch bypass class without the code

Copilot AI 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.

Pull request overview

Copilot reviewed 8 out of 8 changed files in this pull request and generated 2 comments.

Comment thread packages/compiler/src/passes/syn-check.ts Outdated
Comment thread packages/compiler/tests/syn008-check.test.ts
…ction for SYN008

The questionDot handler only matched `WebSocket?.(...)` but not `WebSocket?.<T>(...)`.
Add generic-arg scanning (matching the existing `<T>(...)` branch) inside the questionDot
branch, plus a regression test.

Copilot AI 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.

Pull request overview

Copilot reviewed 8 out of 8 changed files in this pull request and generated 1 comment.

Comment thread packages/compiler/src/passes/syn-check.ts
…s are skipped

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@marcelofarias marcelofarias requested a review from Copilot June 11, 2026 14:33

Copilot AI 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.

Pull request overview

Copilot reviewed 8 out of 8 changed files in this pull request and generated 5 comments.

Comment thread packages/compiler/src/passes/syn-check.ts Outdated
Comment thread packages/compiler/src/passes/syn-check.ts Outdated
Comment thread packages/compiler/src/passes/syn-check.ts Outdated
Comment thread packages/compiler/src/passes/syn-check.ts Outdated
Comment thread packages/compiler/tests/syn008-check.test.ts
…hod-shorthand exclusion

- Refactor to track parenIdx8 pointing at the actual `(` token for all forms
  (direct, generic <T>, optional ?.(), optional-generic ?.<T>())
- Fix depth scan: use Math.max(0, depth - len) to prevent negative depth from
  >> or >>> composite tokens causing premature scan exit
- Add method-shorthand exclusion: `{ WebSocket(url) { ... } }` no longer fires
- Shorten the diagnostic message to its actionable core
- Add two regression tests: method-shorthand exclusion and depth-3 generic

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

Copilot AI 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.

Pull request overview

Copilot reviewed 8 out of 8 changed files in this pull request and generated 6 comments.

Comment thread packages/compiler/src/passes/syn-check.ts
Comment thread packages/compiler/src/passes/syn-check.ts Outdated
Comment thread packages/compiler/src/error-codes.ts Outdated
Comment thread packages/mcp/src/explanations.ts Outdated
Comment thread packages/mcp/src/explanations.ts Outdated
Comment thread AGENTS.md Outdated
- syn-check.ts: update inline comment to list all detection forms
  including `WebSocket?.<T>(...)` (optional-call-with-type-args)
- syn-check.ts: change diagnostic message from "constructs" to
  "calls or constructs" to cover bare `WebSocket(url)` (no `new`)
- error-codes.ts: rephrase rule text — replace the misleading
  "no `uses { net }` reflects it" with "no `http.*` call is present
  to justify a `uses { net }` declaration" to clarify the constraint
- explanations.ts: rephrase Fix note so it doesn't imply you can't
  add `uses { net }`; clarify that the compiler cannot verify the
  net usage without an `http.*` call, and CAP002 will fire
- explanations.ts: add `?.<T>(` to the detection description
- AGENTS.md: update SYN008 table row to include
  `WebSocket?.<T>(url)` form and use "calls or constructs"

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

Copilot AI 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.

Pull request overview

Copilot reviewed 8 out of 8 changed files in this pull request and generated no new comments.

- syn-check.ts: expand detection comment to list all five forms including
  `WebSocket?.(...)` and `WebSocket?.<T>(...)` (was missing optional-call forms)
- error-codes.ts: rephrase SYN008 rule text to avoid implying `uses { net }`
  is impossible to declare — say "the capability model cannot see it" instead of
  "no uses { net } reflects it"
- explanations.ts: rephrase line 822 from "`uses { net }` is absent from the
  fn header" to "the capability model cannot require `uses { net }` for it"
- explanations.ts: expand detection description to enumerate all five detected
  surface forms including `WebSocket?.<T>(url)` (optional-call-with-type-args)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

Copilot AI 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.

Pull request overview

Copilot reviewed 8 out of 8 changed files in this pull request and generated 2 comments.

Comment on lines +830 to +831
"This is the same bypass class as bare `fetch(...)` calls and `console.*` (SYN003): real network " +
"effects that sidestep the declared capability surface.\n\n" +
Comment on lines +515 to +517
message:
`fn '${decl.name}' calls or constructs a WebSocket — bypasses the net capability model; ` +
`wrap in unsafe "wraps WebSocket directly" { new WebSocket(url) } or write a $require("net")-checked wrapper`,
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.

2 participants