Skip to content

fix(fleet): bound the token an enrolling server can make us write - #124

Merged
jonathansantilli merged 1 commit into
mainfrom
fix/bound-enrolment-token
Aug 26, 2026
Merged

fix(fleet): bound the token an enrolling server can make us write#124
jonathansantilli merged 1 commit into
mainfrom
fix/bound-enrolment-token

Conversation

@jonathansantilli

Copy link
Copy Markdown
Owner

Came out of investigating the four CodeQL "network data written to file" alerts. Three are false positives; this one found something real.

What was wrong

enrolMachine writes the server's token straight into ~/.codegate/fleet.json with no bound on its length. A real token is cgm_ plus 32 base64url bytes — under fifty characters. A hostile or simply broken server can answer with a gigabyte of string and this persists all of it.

Capped at 1024, which leaves room for a longer token format without leaving room for that. The content feed already bounds its downloads the same way (8MB bundle, 4KB signature); this path had been missed.

The other three alerts — analysed, not changed

content-store.ts:120,121 — writes the downloaded bundle and its signature. Every leg of the taint path is already guarded:

  • the Ed25519 signature is verified against the pinned publisher key before anything is written (content-updater.ts:114, throws on failure)
  • the download is capped at 8MB / 4KB
  • content_version cannot express a path — constrained at the parse boundary and again where it becomes a path, with ten traversal shapes under test

Controlling these bytes requires the publisher's private key, at which point signature verification is meaningless by definition.

enrol-client.ts:101 (the write itself) — the destination is fleetConfigPath(), a fixed path, not derived from any response. Content is JSON.stringify'd, so no structural injection. Written 0600 inside a 0700 directory. The only party who influences it is the server the operator explicitly chose to enrol with.

report-client.ts:48 — reads the local inventory and POSTs it to the configured server. This is the product: reporting what AI tooling is on the machine to the operator's console. config.server comes from the file written at enrolment by the user's own action, and is validated to be http/https.

Recommend dismissing those three as "won't fix" with that reasoning rather than contorting the code around them.

880 tests pass; typecheck, lint and build clean.

Investigating CodeQL's "network data written to file" alerts turned up one
thing worth changing. The enrolment response's token is written straight into
the user's home directory with no bound on its size, so a hostile or simply
broken server can answer with a gigabyte of string and this persists all of
it. A real token is `cgm_` plus 32 base64url bytes — under fifty characters.

Capped at 1024, which leaves room for a longer token format without leaving
room for that. The content feed already bounds its downloads the same way
(8MB bundle, 4KB signature); this path had been missed.

The other three alerts are examined in the pull request rather than changed.
@jonathansantilli
jonathansantilli merged commit 9e91067 into main Aug 26, 2026
16 checks passed
@jonathansantilli
jonathansantilli deleted the fix/bound-enrolment-token branch August 26, 2026 09:32
github-actions Bot pushed a commit that referenced this pull request Aug 26, 2026
## [1.2.3](v1.2.2...v1.2.3) (2026-08-26)

### Bug Fixes

* **fleet:** bound the token an enrolling server can make us write ([#124](#124)) ([9e91067](9e91067))
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