AntFleet

Anatomy · 57e5c9ae-4

ALLOWED_ORIGIN defaults to '*' but Access-Control-Allow-Credentials is always 'true'

mediumsecurityclosed in 3c10efc
repo bf0d040b·PR #3·reviewed 1 month ago·closed 1 month ago

The vulnerable code

agent/src/api.ts:47-60

47function send(res: http.ServerResponse, status: number, body: unknown) {
48 const json = JSON.stringify(body);
49 res.writeHead(status, {
50 'content-type': 'application/json',
51 'access-control-allow-origin': ALLOWED_ORIGIN,
52 'access-control-allow-methods': 'GET,POST,OPTIONS',
53 'access-control-allow-headers': 'content-type, authorization',
54 'access-control-allow-credentials': 'true',
55 'content-length': Buffer.byteLength(json),
56 });
57 res.end(json);
58}
59
60async function readBody(req: http.IncomingMessage): Promise<any> {

The reasoning

Opus

ALLOWED_ORIGIN defaults to '*' but Access-Control-Allow-Credentials is always 'true'

mediumsecurityhigh
  • agent/src/api.ts:47-60
When DASHBOARD_ORIGIN is not set, ALLOWED_ORIGIN === '*' but the response also sets `access-control-allow-credentials: true`. Browsers reject this combination (cookies/auth headers won't be sent), and more importantly, hosting providers/load balancers that allow this header will accept cross-origin credentialed requests from anywhere, expanding attack surface for SIWE replay or CSRF if cookies are added later. The agent admin endpoints (POST /api/mode, /api/proposals/.../reject) could be hit cross-origin.

Recommendation

Reflect the request Origin only when it matches an allowlist, or refuse to emit Allow-Credentials when Allow-Origin is '*'. At minimum, default ALLOWED_ORIGIN to a safe value rather than '*'.

GPT-5

Admin auth defaults to allow any wallet when ADMIN_WALLETS is unset

mediumsecurityhigh
  • agent/src/api.ts:54-61
If ADMIN_WALLETS is missing in production, any SIWE-authenticated wallet can mutate mode and proposal state. This footgun undermines the intended admin boundary.

Recommendation

Default to deny: return false when ADMIN_WALLETS is empty, or gate it behind an explicit DEV_MODE flag. Refuse to start the API if ADMIN_WALLETS is empty and NODE_ENV !== 'development'.

The agreement

Both frontier models flagged this within the same line range. AntFleet's unanimous gate fired — the finding posted on the PR. Closed in 3c10efc.

The fix

47function send(res: http.ServerResponse, status: number, body: unknown) {
48 const json = JSON.stringify(body);
49 res.writeHead(status, {
50 'content-type': 'application/json',
51 'access-control-allow-origin': ALLOWED_ORIGIN,
52 'access-control-allow-methods': 'GET,POST,OPTIONS',
53 'access-control-allow-headers': 'content-type, authorization',
54 'access-control-allow-credentials': 'true',
55 'content-length': Buffer.byteLength(json),
56 });
57 res.end(json);
58}
59
60async function readBody(req: http.IncomingMessage): Promise<any> {

Closure

Closed 1 month ago

SHA: 3c10efc6038bc5ab182e8b192224745b99bcf729

View closure receipt on GitHub →

Tweet thread template

tweet 1 of 8171 / 280

Two frontier models reviewed PR #3 on bf0d040b. Both found this bug: medium security: ALLOWED_ORIGIN defaults to '*' but Access-Control-Allow-Credentials is always 'true'

tweet 2 of 8108 / 280

The vulnerable code (agent/src/api.ts:47-60): (full snippet at https://www.antfleet.dev/anatomy/57e5c9ae-4)

tweet 3 of 8280 / 280

What Opus saw: "When DASHBOARD_ORIGIN is not set, ALLOWED_ORIGIN === '*' but the response also sets `access-control-allow-credentials: true`. Browsers reject this combination (cookies/auth headers won't be sent), and more importantly, hosting providers/load balancers that allow…

tweet 4 of 8180 / 280

What GPT-5 saw: "If ADMIN_WALLETS is missing in production, any SIWE-authenticated wallet can mutate mode and proposal state. This footgun undermines the intended admin boundary."

tweet 5 of 897 / 280

Both flagged the same line range. AntFleet's unanimous gate fired — the finding posted on the PR.

tweet 6 of 893 / 280

The fix landed in commit 3c10efc: (view diff at https://www.antfleet.dev/anatomy/57e5c9ae-4)

tweet 7 of 881 / 280

AntFleet reviews every PR with two frontier models. Only unanimous findings post.

tweet 8 of 877 / 280

Full anatomy + reasoning + diffs: https://www.antfleet.dev/anatomy/57e5c9ae-4

Paste into X composer one tweet at a time. X has no multi-tweet intent API.