fix(backend): compute AgentBridge diagnose DNS check per-agent instead of hard-coded Antigravity (#8466) (#8502)

getMitmStatus() hard-wired dnsConfigured to a single Antigravity hostname
regex regardless of which agent was being diagnosed, and the diagnose route
never accepted an agentId to check against. Add checkDNSEntryForAgent()
reusing resolveHostsForAgent()'s existing per-target host resolution, thread
an optional agentId through getMitmStatus(), and have the diagnose route
parse ?agentId= and pass it through. Callers that omit agentId keep the
legacy Antigravity-only behavior unchanged.

Co-authored-by: ikelvingo <im.kelvinwong@gmail.com>
This commit is contained in:
Diego Rodrigues de Sa e Souza
2026-07-25 04:57:07 -03:00
committed by GitHub
parent 13cc128dae
commit 61277813b2
5 changed files with 147 additions and 8 deletions

View File

@@ -0,0 +1 @@
- fix(backend): compute AgentBridge diagnose `dnsConfigured` per-agent instead of hard-coded to Antigravity hosts (#8466)

View File

@@ -37,9 +37,10 @@ function probeTcp(port: number, host = "127.0.0.1", timeoutMs = 1500): Promise<b
});
}
export async function GET(): Promise<Response> {
export async function GET(request: Request): Promise<Response> {
try {
const status = await getMitmStatus();
const agentId = new URL(request.url).searchParams.get("agentId") ?? undefined;
const status = await getMitmStatus(agentId);
const certPath = path.join(resolveMitmDataDir(), "mitm", "server.crt");
const certExists = fs.existsSync(certPath);
const certTrusted = certExists ? await checkCertInstalled(certPath) : false;

View File

@@ -256,6 +256,19 @@ export function checkDNSEntry(): boolean {
return ANTIGRAVITY_HOSTS.every((h) => hasHostEntry(hostsContent, h));
}
/**
* Check whether ALL hosts for the given agent are present in /etc/hosts.
* Falls back to the Antigravity legacy hosts when `agentId` is omitted or
* unknown, via `resolveHostsForAgent()` — so callers get the same host set
* that `addDNSEntry`/`removeDNSEntry` already use for that agent. Used by
* `getMitmStatus()` to answer "are THIS agent's hosts spoofed?" instead of
* always checking the Antigravity-only set (#8466).
*/
export function checkDNSEntryForAgent(agentId?: string): boolean {
const hostsContent = readHostsFile();
return resolveHostsForAgent(agentId).every((h) => hasHostEntry(hostsContent, h));
}
/**
* Add DNS entries for the Antigravity default hosts, or for a specific agent
* when `agentId` is provided.

View File

@@ -2,7 +2,7 @@ import { spawn, type ChildProcess } from "child_process";
import path from "path";
import fs from "fs";
import { resolveMitmDataDir } from "./dataDir.ts";
import { removeDNSEntry, removeDNSEntries } from "./dns/dnsConfig.ts";
import { removeDNSEntry, removeDNSEntries, checkDNSEntryForAgent } from "./dns/dnsConfig.ts";
import { provisionDnsEntries } from "./dns/provision.ts";
import { generateCert } from "./cert/generate.ts";
import { installCertResult, installCaCert } from "./cert/install.ts";
@@ -353,9 +353,16 @@ export async function handleExitCleanup(
}
/**
* Get MITM status
* Get MITM status.
*
* @param agentId - Optional agent whose hosts should be checked in DNS. When
* omitted, preserves the legacy Antigravity-only check (unchanged behavior
* for the existing no-agentId call sites: state/route.ts, server/route.ts,
* settings/mitm/route.ts, cli-tools/antigravity-mitm/route.ts). When
* provided (e.g. by the diagnose route), checks that agent's own hosts
* instead of always checking the Antigravity host set (#8466).
*/
export async function getMitmStatus(): Promise<{
export async function getMitmStatus(agentId?: string): Promise<{
running: boolean;
pid: number | null;
dnsConfigured: boolean;
@@ -387,11 +394,17 @@ export async function getMitmStatus(): Promise<{
}
}
// Check DNS configuration
// Check DNS configuration. When an agentId is provided, check THAT agent's
// own hosts (#8466) instead of always checking the Antigravity host set —
// callers that don't pass agentId keep the legacy Antigravity-only check.
let dnsConfigured = false;
try {
const hostsContent = fs.readFileSync("/etc/hosts", "utf-8");
dnsConfigured = /\bdaily-cloudcode-pa\.googleapis\.com\b/.test(hostsContent);
if (agentId) {
dnsConfigured = checkDNSEntryForAgent(agentId);
} else {
const hostsContent = fs.readFileSync("/etc/hosts", "utf-8");
dnsConfigured = /\bdaily-cloudcode-pa\.googleapis\.com\b/.test(hostsContent);
}
} catch {
// Ignore
}

View File

@@ -0,0 +1,111 @@
/**
* Regression test for issue #8466: AgentBridge diagnostics `dnsConfigured`
* was hard-wired to the Antigravity host regex (src/mitm/manager.ts) instead
* of being computed per-agent via `resolveHostsForAgent(agentId)`
* (src/mitm/dns/dnsConfig.ts).
*
* We mock fs.readFileSync so the real /etc/hosts is never touched, then
* exercise getMitmStatus() -- the exact function named in the issue -- with
* hosts-file contents representing each failure direction, plus the
* diagnose route to prove the agentId query param is threaded through.
*/
import { test, mock, afterEach } from "node:test";
import assert from "node:assert/strict";
import fs from "node:fs";
afterEach(() => {
mock.restoreAll();
});
test("FALSE NEGATIVE: Claude Code host correctly spoofed, no Antigravity host present -> dnsConfigured should be true when checked for claude-code", async () => {
const realReadFileSync = fs.readFileSync.bind(fs);
mock.method(fs, "readFileSync", (p: string, enc?: BufferEncoding) => {
if (p === "/etc/hosts") {
// Claude Code target hosts = ["api.anthropic.com"] (src/mitm/targets/claudeCode.ts).
// The user correctly spoofed it. No Antigravity host present at all.
return "127.0.0.1 localhost\n127.0.0.1 api.anthropic.com\n::1 api.anthropic.com\n";
}
return realReadFileSync(p, enc);
});
const { getMitmStatus } = await import("../../src/mitm/manager.ts?probe=8466-negative");
const status = await getMitmStatus("claude-code");
assert.equal(
status.dnsConfigured,
true,
"dnsConfigured must be true when the agent being diagnosed (Claude Code) has its own host spoofed"
);
});
test("FALSE POSITIVE: only a leftover Antigravity host is present, Claude Code host is missing -> dnsConfigured should be false when checked for claude-code", async () => {
const realReadFileSync = fs.readFileSync.bind(fs);
mock.method(fs, "readFileSync", (p: string, enc?: BufferEncoding) => {
if (p === "/etc/hosts") {
// Leftover Antigravity entry from a previous setup. Claude Code's host
// (api.anthropic.com) is NOT present.
return "127.0.0.1 localhost\n127.0.0.1 daily-cloudcode-pa.googleapis.com\n";
}
return realReadFileSync(p, enc);
});
const { getMitmStatus } = await import("../../src/mitm/manager.ts?probe=8466-positive");
const status = await getMitmStatus("claude-code");
assert.equal(
status.dnsConfigured,
false,
"dnsConfigured must be false when the agent being diagnosed (Claude Code) has no host spoofed, even if a leftover Antigravity host is present"
);
});
test("no-agentId call sites keep legacy Antigravity-only behavior unchanged", async () => {
const realReadFileSync = fs.readFileSync.bind(fs);
mock.method(fs, "readFileSync", (p: string, enc?: BufferEncoding) => {
if (p === "/etc/hosts") {
// Claude Code host spoofed, but NO Antigravity host present. A caller
// that omits agentId (state/route.ts, server/route.ts, settings/mitm,
// cli-tools/antigravity-mitm) must still evaluate the legacy
// Antigravity-only regex, so this should remain false.
return "127.0.0.1 localhost\n127.0.0.1 api.anthropic.com\n::1 api.anthropic.com\n";
}
return realReadFileSync(p, enc);
});
const { getMitmStatus } = await import("../../src/mitm/manager.ts?probe=8466-legacy");
const status = await getMitmStatus();
assert.equal(
status.dnsConfigured,
false,
"callers that omit agentId must keep the legacy Antigravity-only check"
);
});
test("diagnose route: threads ?agentId= query param through to getMitmStatus", async () => {
const realReadFileSync = fs.readFileSync.bind(fs);
mock.method(fs, "readFileSync", (p: string, enc?: BufferEncoding) => {
if (p === "/etc/hosts") {
return "127.0.0.1 localhost\n127.0.0.1 api.anthropic.com\n::1 api.anthropic.com\n";
}
return realReadFileSync(p, enc);
});
const { GET } = await import(
"../../src/app/api/tools/agent-bridge/diagnose/route.ts?probe=8466-route"
);
const res = await GET(
new Request("http://localhost/api/tools/agent-bridge/diagnose?agentId=claude-code")
);
const body = (await res.json()) as {
checks: Array<{ name: string; ok: boolean }>;
};
const dnsCheck = body.checks.find((c) => c.name === "dns-configured");
assert.ok(dnsCheck, "expected a dns-configured check in the diagnostics report");
assert.equal(
dnsCheck?.ok,
true,
"diagnose route must pass agentId through to getMitmStatus so dns-configured reflects the diagnosed agent's hosts"
);
});