@openape/proxy 0.4.3 → 0.4.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -0
- package/dist/index.cjs +560 -138
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +538 -138
- package/dist/index.js.map +1 -1
- package/package.json +6 -4
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/config.ts","../src/proxy.ts","../src/matcher.ts","../src/auth.ts","../src/grants-client.ts","../src/audit.ts","../src/ssrf.ts","../src/connect.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { createServer } from 'node:http'\nimport { parseArgs } from 'node:util'\nimport { loadMultiAgentConfig } from './config.js'\nimport { createNodeHandler } from './proxy.js'\n\nconst { values } = parseArgs({\n options: {\n config: { type: 'string', short: 'c', default: 'config.toml' },\n 'dry-run': { type: 'boolean', default: false },\n 'mandatory-auth': { type: 'boolean', default: false },\n },\n})\n\nconst configPath = values.config!\n\nconsole.log(`[openape-proxy] Loading config from ${configPath}`)\nconst config = loadMultiAgentConfig(configPath, {\n mandatoryAuth: values['mandatory-auth'] || undefined,\n})\n\nif (values['dry-run']) {\n console.log('[openape-proxy] DRY RUN mode — logging only, not blocking')\n console.log('[openape-proxy] Config loaded:')\n console.log(` Listen: ${config.proxy.listen}`)\n console.log(` Default action: ${config.proxy.default_action}`)\n console.log(` Mandatory auth: ${config.proxy.mandatory_auth ?? false}`)\n console.log(` Agents: ${config.agents.length}`)\n for (const agent of config.agents) {\n const allowCount = agent.allow?.length ?? 0\n const denyCount = agent.deny?.length ?? 0\n const grantCount = agent.grant_required?.length ?? 0\n console.log(` ${agent.email} (${agent.idp_url}) — ${allowCount} allow, ${denyCount} deny, ${grantCount} grant`)\n }\n process.exit(0)\n}\n\nconst handler = createNodeHandler(config)\n\nconst port = Number.parseInt(config.proxy.listen.split(':')[1] || '9090')\nconst hostname = config.proxy.listen.split(':')[0] || '127.0.0.1'\n\nconst server = createServer(handler.handleRequest)\nserver.on('connect', handler.handleConnect)\n\nserver.listen(port, hostname, () => {\n // When configured with port 0, the OS assigns a free port — use the actual\n // bound port for the log line so external orchestrators (apes proxy --) can\n // grep this line to discover where to connect.\n const addr = server.address()\n const actualPort = typeof addr === 'object' && addr ? addr.port : port\n console.log(`[openape-proxy] Listening on http://${hostname}:${actualPort}`)\n console.log(`[openape-proxy] CONNECT tunneling enabled`)\n console.log(`[openape-proxy] Mandatory auth: ${config.proxy.mandatory_auth ?? false}`)\n console.log(`[openape-proxy] Agents: ${config.agents.map(a => a.email).join(', ')}`)\n console.log(`[openape-proxy] Default action: ${config.proxy.default_action}`)\n})\n\n// Graceful shutdown\nprocess.on('SIGINT', () => {\n console.log('\\n[openape-proxy] Shutting down...')\n server.close()\n process.exit(0)\n})\n\nprocess.on('SIGTERM', () => {\n console.log('[openape-proxy] Shutting down...')\n server.close()\n process.exit(0)\n})\n","import { readFileSync } from 'node:fs'\nimport { parse as parseTOML } from 'smol-toml'\nimport type { AgentConfig, MultiAgentProxyConfig, ProxyConfig } from './types.js'\n\nexport function loadConfig(path: string): ProxyConfig {\n const raw = readFileSync(path, 'utf-8')\n\n let parsed: Record<string, unknown>\n if (path.endsWith('.json')) {\n parsed = JSON.parse(raw)\n }\n else {\n parsed = parseTOML(raw) as Record<string, unknown>\n }\n\n const proxy = parsed.proxy as ProxyConfig['proxy']\n if (!proxy?.listen || !proxy?.idp_url || !proxy?.agent_email) {\n throw new Error('Config must have [proxy] with listen, idp_url, and agent_email')\n }\n\n proxy.default_action ??= 'block'\n\n return {\n proxy,\n allow: (parsed.allow ?? []) as ProxyConfig['allow'],\n deny: (parsed.deny ?? []) as ProxyConfig['deny'],\n grant_required: (parsed.grant_required ?? []) as ProxyConfig['grant_required'],\n }\n}\n\n/**\n * Load config as multi-agent format.\n * If the config has an `agents` array, use it directly.\n * Otherwise, convert single-agent format to multi-agent for backward-compat.\n */\nexport function loadMultiAgentConfig(path: string, overrides?: { mandatoryAuth?: boolean }): MultiAgentProxyConfig {\n const raw = readFileSync(path, 'utf-8')\n\n let parsed: Record<string, unknown>\n if (path.endsWith('.json')) {\n parsed = JSON.parse(raw)\n }\n else {\n parsed = parseTOML(raw) as Record<string, unknown>\n }\n\n const proxy = parsed.proxy as Record<string, unknown>\n if (!proxy?.listen) {\n throw new Error('Config must have [proxy] with listen')\n }\n\n const baseProxy: MultiAgentProxyConfig['proxy'] = {\n listen: proxy.listen as string,\n default_action: (proxy.default_action as MultiAgentProxyConfig['proxy']['default_action']) ?? 'block',\n mandatory_auth: overrides?.mandatoryAuth ?? (proxy.mandatory_auth as boolean | undefined),\n }\n\n // Multi-agent format: has agents array\n if (Array.isArray(parsed.agents)) {\n return {\n proxy: baseProxy,\n agents: parsed.agents as AgentConfig[],\n }\n }\n\n // Single-agent format: convert to multi-agent\n const idpUrl = proxy.idp_url as string\n const agentEmail = proxy.agent_email as string\n if (!idpUrl || !agentEmail) {\n throw new Error('Single-agent config requires proxy.idp_url and proxy.agent_email')\n }\n\n return {\n proxy: baseProxy,\n agents: [{\n email: agentEmail,\n idp_url: idpUrl,\n allow: (parsed.allow ?? []) as AgentConfig['allow'],\n deny: (parsed.deny ?? []) as AgentConfig['deny'],\n grant_required: (parsed.grant_required ?? []) as AgentConfig['grant_required'],\n }],\n }\n}\n","import type { IncomingMessage, ServerResponse } from 'node:http'\nimport type { Socket } from 'node:net'\nimport { createHash } from 'node:crypto'\nimport type { AgentConfig, AuditEntry, MultiAgentProxyConfig, ProxyConfig } from './types.js'\nimport { evaluateRules } from './matcher.js'\nimport { AuthError, verifyAgentAuth } from './auth.js'\nimport { GrantsClient } from './grants-client.js'\nimport { writeAudit } from './audit.js'\nimport { checkEgress } from './ssrf.js'\nimport { handleConnect } from './connect.js'\n\n/**\n * Compute a request hash that uniquely identifies the intent.\n * hash = sha256(METHOD + \" \" + FULL_URL + \"\\n\" + BODY)\n * This binds the grant to the exact request — no bait-and-switch.\n */\nasync function computeRequestHash(method: string, targetUrl: string, body: ArrayBuffer | null): Promise<string> {\n const hash = createHash('sha256')\n hash.update(`${method} ${targetUrl}\\n`)\n if (body && body.byteLength > 0) {\n hash.update(new Uint8Array(body))\n }\n return hash.digest('hex')\n}\n\n/** Legacy single-agent proxy */\nexport function createProxy(config: ProxyConfig) {\n const multiConfig: MultiAgentProxyConfig = {\n proxy: {\n listen: config.proxy.listen,\n default_action: config.proxy.default_action,\n mandatory_auth: config.proxy.mandatory_auth,\n },\n agents: [{\n email: config.proxy.agent_email,\n idp_url: config.proxy.idp_url,\n allow: config.allow,\n deny: config.deny,\n grant_required: config.grant_required,\n }],\n }\n return createMultiAgentProxy(multiConfig)\n}\n\n/**\n * Build the per-agent GrantsClient map used by both the HTTP forward-proxy\n * handler (in this file's `createMultiAgentProxy`) and the CONNECT handler\n * (in `connect.ts`). Exposed so `createNodeHandler` can share a single map\n * instead of letting each handler construct its own — keeps any future\n * per-agent token state coherent.\n */\nexport function buildGrantsClients(config: MultiAgentProxyConfig): Map<string, GrantsClient> {\n const grantsClients = new Map<string, GrantsClient>()\n for (const agent of config.agents) {\n grantsClients.set(agent.email, new GrantsClient(agent.idp_url))\n }\n return grantsClients\n}\n\n/** Multi-agent proxy with SSRF protection and mandatory auth */\nexport function createMultiAgentProxy(\n config: MultiAgentProxyConfig,\n grantsClients: Map<string, GrantsClient> = buildGrantsClients(config),\n) {\n // Backwards-compat: if caller didn't pre-build the map, we build our own.\n // createNodeHandler always passes one in so the CONNECT handler can share it.\n for (const agent of config.agents) {\n if (!grantsClients.has(agent.email)) {\n grantsClients.set(agent.email, new GrantsClient(agent.idp_url))\n }\n }\n\n const mandatoryAuth = config.proxy.mandatory_auth ?? false\n\n return {\n port: Number.parseInt(config.proxy.listen.split(':')[1] || '9090'),\n hostname: config.proxy.listen.split(':')[0] || '127.0.0.1',\n\n async fetch(req: Request): Promise<Response> {\n const url = new URL(req.url)\n const startTime = Date.now()\n\n // Health endpoint\n if (url.pathname === '/healthz') {\n return Response.json({\n status: 'ok',\n agents: config.agents.map(a => a.email),\n })\n }\n\n // Parse target URL from the path\n const targetUrl = url.pathname.slice(1) + url.search\n let targetParsed: URL\n try {\n targetParsed = new URL(targetUrl)\n }\n catch {\n return new Response(\n 'Invalid target URL. Send requests as: http://proxy:port/https://target.com/path',\n { status: 400 },\n )\n }\n\n const domain = targetParsed.hostname\n const method = req.method\n const path = targetParsed.pathname\n\n // SSRF / reachability check. Split outcomes so a non-existent host\n // doesn't ship as a misleading 403:\n // - `private` → policy refusal (403).\n // - `unresolvable` → upstream connectivity failure (502).\n const egress = await checkEgress(domain)\n if (egress.kind === 'private') {\n return new Response('Blocked: private/loopback IP', { status: 403 })\n }\n if (egress.kind === 'unresolvable') {\n return new Response(\n `DNS lookup failed for ${domain} (${egress.reason}).`,\n { status: 502 },\n )\n }\n\n // Read body once (needed for hash + forwarding)\n const bodyBuffer = req.body ? await req.arrayBuffer() : null\n\n // Verify agent identity — find IdP URL from first agent (for JWKS verification)\n // In multi-agent mode, we need the JWT to identify the agent first.\n // We try verification against each agent's IdP until one succeeds.\n let agentIdentity: { email: string, act: 'agent' } | null = null\n try {\n for (const agentConf of config.agents) {\n agentIdentity = await verifyAgentAuth(\n req.headers.get('proxy-authorization'),\n agentConf.idp_url,\n mandatoryAuth && config.agents.length === 1,\n )\n if (agentIdentity) break\n }\n\n // If mandatory auth and no identity found from any IdP\n if (mandatoryAuth && !agentIdentity) {\n throw new AuthError('JWT required')\n }\n }\n catch (err) {\n if (err instanceof AuthError) {\n return new Response(`Unauthorized: ${err.message}`, { status: 401 })\n }\n throw err\n }\n\n // Find the matching agent config\n const agentEmail = agentIdentity?.email\n let agentConf: AgentConfig | undefined\n\n if (agentEmail) {\n agentConf = config.agents.find(a => a.email === agentEmail)\n if (!agentConf) {\n return new Response(`Forbidden: unknown agent ${agentEmail}`, { status: 403 })\n }\n }\n else if (config.agents.length === 1) {\n // Non-mandatory auth, single agent: use the only agent config\n agentConf = config.agents[0]\n }\n else {\n return new Response('Unauthorized: JWT required for multi-agent proxy', { status: 401 })\n }\n\n const effectiveEmail = agentEmail ?? agentConf.email\n const grantsClient = grantsClients.get(agentConf.email)!\n\n // Build a ProxyConfig-shaped object for evaluateRules\n const rulesConfig: ProxyConfig = {\n proxy: {\n listen: config.proxy.listen,\n idp_url: agentConf.idp_url,\n agent_email: agentConf.email,\n default_action: config.proxy.default_action,\n },\n allow: agentConf.allow ?? [],\n deny: agentConf.deny ?? [],\n grant_required: agentConf.grant_required ?? [],\n }\n\n // Evaluate rules\n const action = evaluateRules(rulesConfig, domain, method, path)\n\n const baseAudit: Omit<AuditEntry, 'action' | 'rule'> = {\n ts: new Date().toISOString(),\n agent: effectiveEmail,\n domain,\n method,\n path,\n }\n\n // DENY\n if (action.type === 'deny') {\n writeAudit({ ...baseAudit, action: 'deny', rule: 'deny-list', grant_id: null })\n return new Response(`Blocked: ${action.note || 'deny rule'}`, { status: 403 })\n }\n\n // ALLOW (no grant needed)\n if (action.type === 'allow') {\n writeAudit({ ...baseAudit, action: 'allow', rule: 'allow-list', grant_id: null })\n return forwardRequest(req, targetUrl, bodyBuffer)\n }\n\n // GRANT REQUIRED\n const rule = action.rule\n const permissions = rule.permissions ?? [`${method.toLowerCase()}:${domain}`]\n\n // Compute request hash — binds grant to exact method + URL + body\n const requestHash = await computeRequestHash(method, targetUrl, bodyBuffer)\n\n // Check for existing grant\n const existing = await grantsClient.findExistingGrant(\n effectiveEmail,\n domain,\n 'proxy',\n permissions,\n ).catch(() => null)\n\n if (existing) {\n writeAudit({\n ...baseAudit,\n action: 'grant_approved',\n rule: 'standing-grant',\n grant_id: existing.id,\n request_hash: requestHash,\n })\n return forwardRequest(req, targetUrl, bodyBuffer)\n }\n\n // No existing grant — behavior depends on default_action\n if (config.proxy.default_action === 'block') {\n writeAudit({ ...baseAudit, action: 'deny', rule: 'no-grant (block mode)', grant_id: null })\n return new Response('No grant — blocked', { status: 403 })\n }\n\n if (config.proxy.default_action === 'request-async') {\n // Create grant request, return 407 immediately\n const grant = await grantsClient.requestGrant({\n requester: effectiveEmail,\n targetHost: domain,\n audience: 'ape-proxy',\n grantType: rule.grant_type,\n permissions,\n reason: `${method} ${targetUrl}`,\n requestHash,\n duration: rule.duration,\n }).catch(() => null)\n\n writeAudit({\n ...baseAudit,\n action: 'grant_denied',\n rule: 'grant_required (async)',\n grant_id: grant?.id ?? null,\n })\n\n return new Response(\n JSON.stringify({\n error: 'Grant required',\n grant_id: grant?.id,\n message: 'Grant request created. Retry after approval.',\n }),\n { status: 407, headers: { 'Content-Type': 'application/json' } },\n )\n }\n\n // BLOCKING mode: create grant request and wait\n console.error(`[proxy] Requesting grant for ${method} ${domain}${path} — waiting for approval...`)\n\n try {\n const grant = await grantsClient.requestGrant({\n requester: effectiveEmail,\n targetHost: domain,\n audience: 'ape-proxy',\n grantType: rule.grant_type,\n permissions,\n reason: `${method} ${targetUrl}`,\n requestHash,\n duration: rule.duration,\n })\n\n const approved = await grantsClient.waitForApproval(grant.id)\n\n const waitedMs = Date.now() - startTime\n\n if (approved.status === 'approved') {\n writeAudit({\n ...baseAudit,\n action: 'grant_approved',\n rule: 'grant_required',\n grant_id: approved.id,\n request_hash: requestHash,\n waited_ms: waitedMs,\n })\n return forwardRequest(req, targetUrl, bodyBuffer)\n }\n\n writeAudit({\n ...baseAudit,\n action: 'grant_denied',\n rule: 'grant_required',\n grant_id: approved.id,\n waited_ms: waitedMs,\n })\n return new Response(`Grant denied by ${approved.decided_by}`, { status: 403 })\n }\n catch (err) {\n const msg = err instanceof Error ? err.message : 'Unknown error'\n writeAudit({\n ...baseAudit,\n action: 'grant_timeout',\n rule: 'grant_required',\n error: msg,\n })\n return new Response(`Grant request failed: ${msg}`, { status: 504 })\n }\n },\n }\n}\n\n/**\n * Create a node:http compatible handler for use with http.createServer().\n * Returns both a request handler and a CONNECT handler.\n */\nexport function createNodeHandler(config: MultiAgentProxyConfig): {\n handleRequest: (req: IncomingMessage, res: ServerResponse) => void\n handleConnect: (req: IncomingMessage, socket: Socket, head: Buffer) => void\n} {\n // Build grantsClients once; share with both forward-proxy fetch path\n // (createMultiAgentProxy) and the CONNECT path (handleConnect). Without\n // sharing, CONNECT-time grant_required rules couldn't reach the IdP in\n // multi-agent mode without duplicating constructor work.\n const grantsClients = buildGrantsClients(config)\n const proxy = createMultiAgentProxy(config, grantsClients)\n\n return {\n handleRequest(req: IncomingMessage, res: ServerResponse) {\n // Standard HTTP_PROXY clients (curl, gh, git, npm) send the target as\n // an absolute URL in the request line for cleartext forward-proxying:\n // `GET http://example.com/path HTTP/1.1`\n // node:http surfaces that absolute URL via `req.url`. The legacy\n // `proxy.fetch` extraction expects a path-encoded form\n // (`/<full-target-url>`), so we adapt here: when `req.url` is\n // absolute, prefix it with a slash so `pathname.slice(1)` recovers\n // the same target string. Path-form clients (legacy) keep working.\n const reqUrl = req.url || '/'\n const isAbsolute = reqUrl.startsWith('http://') || reqUrl.startsWith('https://')\n const proxyHost = req.headers.host || 'localhost'\n const url = isAbsolute\n ? `http://${proxyHost}/${reqUrl}`\n : `http://${proxyHost}${reqUrl}`\n const chunks: Buffer[] = []\n\n req.on('data', (chunk: Buffer) => chunks.push(chunk))\n req.on('end', async () => {\n try {\n const body = chunks.length > 0 ? Buffer.concat(chunks) : undefined\n const headers = new Headers()\n for (const [key, value] of Object.entries(req.headers)) {\n if (value) {\n headers.set(key, Array.isArray(value) ? value.join(', ') : value)\n }\n }\n\n const request = new Request(url, {\n method: req.method,\n headers,\n body: body && req.method !== 'GET' && req.method !== 'HEAD' ? body : undefined,\n duplex: 'half',\n } as RequestInit)\n\n const response = await proxy.fetch(request)\n\n res.writeHead(response.status, response.statusText, Object.fromEntries(response.headers))\n if (response.body) {\n const reader = response.body.getReader()\n const pump = async (): Promise<void> => {\n const { done, value } = await reader.read()\n if (done) {\n res.end()\n return\n }\n res.write(value)\n return pump()\n }\n await pump()\n }\n else {\n res.end()\n }\n }\n catch {\n res.writeHead(502)\n res.end('Proxy error')\n }\n })\n },\n\n handleConnect(req: IncomingMessage, socket: Socket, head: Buffer) {\n handleConnect(config, grantsClients, req, socket, head)\n },\n }\n}\n\n/**\n * Forward a request to the target URL.\n * Strips proxy-specific headers, preserves the rest.\n */\nasync function forwardRequest(originalReq: Request, targetUrl: string, cachedBody?: ArrayBuffer | null): Promise<Response> {\n const headers = new Headers(originalReq.headers)\n // Remove proxy-specific headers\n headers.delete('proxy-authorization')\n headers.delete('proxy-connection')\n // Don't send host of the proxy\n headers.delete('host')\n\n const body = cachedBody && cachedBody.byteLength > 0 ? cachedBody : null\n\n try {\n const res = await fetch(targetUrl, {\n method: originalReq.method,\n headers,\n body,\n duplex: 'half',\n redirect: 'manual',\n })\n\n // Stream the response back\n const responseHeaders = new Headers(res.headers)\n // Remove hop-by-hop headers\n responseHeaders.delete('transfer-encoding')\n responseHeaders.delete('connection')\n\n return new Response(res.body, {\n status: res.status,\n statusText: res.statusText,\n headers: responseHeaders,\n })\n }\n catch (err) {\n const msg = err instanceof Error ? err.message : 'Upstream error'\n return new Response(`Proxy error: ${msg}`, { status: 502 })\n }\n}\n","import type { ProxyConfig, RuleAction, RuleEntry } from './types.js'\n\n/**\n * Match a glob pattern against a string.\n * Supports * (any segment) and ** (any number of segments).\n */\nfunction globMatch(pattern: string, value: string): boolean {\n // Simple glob: convert * to regex\n const regex = new RegExp(\n `^${\n pattern\n .replace(/[.+^${}()|[\\]\\\\]/g, '\\\\$&') // escape regex chars except *\n .replace(/\\*\\*/g, '<<<DOUBLESTAR>>>')\n .replace(/\\*/g, '[^/]*')\n .replace(/<<<DOUBLESTAR>>>/g, '.*')\n }$`,\n )\n return regex.test(value)\n}\n\nfunction matchesRule(rule: RuleEntry, domain: string, method: string, path: string): boolean {\n // Domain match (supports wildcards like *.github.com)\n if (!globMatch(rule.domain, domain)) return false\n\n // Method match (if specified)\n if (rule.methods && rule.methods.length > 0) {\n if (!rule.methods.includes(method.toUpperCase())) return false\n }\n\n // Path match (if specified)\n if (rule.path) {\n if (!globMatch(rule.path, path)) return false\n }\n\n return true\n}\n\n/**\n * Evaluate rules in order: deny → allow → grant_required → default_action\n */\nexport function evaluateRules(\n config: ProxyConfig,\n domain: string,\n method: string,\n path: string,\n): RuleAction {\n // 1. Check deny list first\n for (const rule of config.deny) {\n if (matchesRule(rule, domain, method, path)) {\n return { type: 'deny', note: rule.note }\n }\n }\n\n // 2. Check allow list\n for (const rule of config.allow) {\n if (matchesRule(rule, domain, method, path)) {\n return { type: 'allow' }\n }\n }\n\n // 3. Check grant_required rules (most specific first)\n for (const rule of config.grant_required) {\n if (matchesRule(rule, domain, method, path)) {\n return { type: 'grant_required', rule }\n }\n }\n\n // 4. Default action for unmatched hosts. Conceptually the proxy-side\n // counterpart of the IdP's per-agent YOLO policy — both decide what\n // happens when no specific rule matches, both live server-side (proxy or\n // IdP), neither leaks the decision to the agent. Differences:\n // - IdP YOLO is per-agent, evaluated at grant time, can have deny-patterns\n // and an expiry window.\n // - Proxy default_action is per-proxy-instance, evaluated at request time,\n // binary outcome (allow/deny/request-grant).\n //\n // Modes:\n // - 'block': hard deny — paranoid agent profile.\n // - 'allow': hard pass — transparent-audit profile (log every call,\n // enforce nothing). Equivalent role to a YOLO policy without a\n // deny-list.\n // - 'request' / 'request-async' (OpenApe-default): treat as\n // grant_required with a once-grant catch-all so every new host\n // surfaces an interactive grant decision.\n if (config.proxy.default_action === 'block') {\n return { type: 'deny', note: 'No matching rule (default: block)' }\n }\n if (config.proxy.default_action === 'allow') {\n return { type: 'allow' }\n }\n\n return {\n type: 'grant_required',\n rule: {\n domain: '*',\n grant_type: 'once',\n },\n }\n}\n","import { verifyJWT, createRemoteJWKS } from '@openape/core'\n\nexport interface AgentIdentity {\n email: string\n act: 'agent'\n}\n\nexport class AuthError extends Error {\n constructor(message: string) {\n super(message)\n this.name = 'AuthError'\n }\n}\n\n/**\n * Verify agent JWT from Proxy-Authorization header.\n * Returns the agent identity or null if invalid/missing.\n * When mandatory is true, throws AuthError if no valid JWT is provided.\n */\nexport async function verifyAgentAuth(\n authHeader: string | null,\n idpUrl: string,\n mandatory: boolean = false,\n): Promise<AgentIdentity | null> {\n if (!authHeader) {\n if (mandatory) throw new AuthError('JWT required')\n return null\n }\n\n const match = authHeader.match(/^Bearer (.+)$/i)\n if (!match) {\n if (mandatory) throw new AuthError('Invalid authorization header')\n return null\n }\n\n const token = match[1]\n\n try {\n const jwks = createRemoteJWKS(`${idpUrl}/.well-known/jwks.json`)\n const { payload } = await verifyJWT(token, jwks, { issuer: idpUrl })\n\n if (payload.act !== 'agent' || !payload.sub) {\n if (mandatory) throw new AuthError('Invalid agent token')\n return null\n }\n\n return {\n email: payload.sub as string,\n act: 'agent',\n }\n }\n catch (err) {\n if (err instanceof AuthError) throw err\n if (mandatory) throw new AuthError('JWT verification failed')\n return null\n }\n}\n","import type { OpenApeGrant, GrantType } from '@openape/core'\n\n/**\n * Client for the IdP's grant management API.\n * Creates grant requests and polls for approval.\n */\nexport class GrantsClient {\n private idpUrl: string\n private agentToken: string | undefined\n\n constructor(idpUrl: string) {\n this.idpUrl = idpUrl.replace(/\\/$/, '')\n }\n\n setAgentToken(token: string): void {\n this.agentToken = token\n }\n\n private headers(): Record<string, string> {\n const h: Record<string, string> = { 'Content-Type': 'application/json' }\n if (this.agentToken) {\n h.Authorization = `Bearer ${this.agentToken}`\n }\n return h\n }\n\n /**\n * Create a grant request on the IdP.\n */\n async requestGrant(opts: {\n requester: string\n targetHost: string\n audience: string\n grantType: GrantType\n permissions?: string[]\n reason?: string\n requestHash?: string\n duration?: number\n }): Promise<OpenApeGrant> {\n const res = await fetch(`${this.idpUrl}/api/grants`, {\n method: 'POST',\n headers: this.headers(),\n body: JSON.stringify({\n requester: opts.requester,\n target_host: opts.targetHost,\n audience: opts.audience,\n grant_type: opts.grantType,\n permissions: opts.permissions,\n reason: opts.reason,\n request_hash: opts.requestHash,\n duration: opts.duration,\n }),\n })\n\n if (!res.ok) {\n throw new Error(`Grant request failed: ${res.status} ${await res.text()}`)\n }\n\n return res.json() as Promise<OpenApeGrant>\n }\n\n /**\n * Poll a grant until it's approved, denied, or timeout.\n */\n async waitForApproval(\n grantId: string,\n timeoutMs: number = 300_000,\n pollIntervalMs: number = 2_000,\n ): Promise<OpenApeGrant> {\n const deadline = Date.now() + timeoutMs\n\n while (Date.now() < deadline) {\n const res = await fetch(`${this.idpUrl}/api/grants/${grantId}`, {\n headers: this.headers(),\n })\n\n if (!res.ok) {\n throw new Error(`Grant poll failed: ${res.status}`)\n }\n\n const grant = await res.json() as OpenApeGrant\n if (grant.status !== 'pending') {\n return grant\n }\n\n await new Promise(r => setTimeout(r, pollIntervalMs))\n }\n\n throw new Error(`Grant approval timed out after ${timeoutMs}ms`)\n }\n\n /**\n * Check if there's an existing approved grant for a host+audience+permissions combo.\n * Ignores `once` grants (they're single-use).\n */\n async findExistingGrant(\n requester: string,\n targetHost: string,\n audience: string,\n permissions?: string[],\n ): Promise<OpenApeGrant | null> {\n const params = new URLSearchParams({\n requester,\n status: 'approved',\n })\n\n const res = await fetch(`${this.idpUrl}/api/grants?${params}`, {\n headers: this.headers(),\n })\n\n if (!res.ok) return null\n\n const grants = await res.json() as OpenApeGrant[]\n const now = Math.floor(Date.now() / 1000)\n\n return grants.find((g) => {\n if (g.status !== 'approved') return false\n if (g.expires_at && g.expires_at <= now) return false\n if (g.request?.grant_type === 'once') return false\n if (g.request?.target_host !== targetHost) return false\n if (g.request?.audience !== audience) return false\n if (permissions?.length && g.request?.permissions?.length) {\n const grantedPerms = new Set(g.request.permissions)\n if (!permissions.every(p => grantedPerms.has(p))) return false\n }\n return true\n }) ?? null\n }\n}\n","import type { AuditEntry } from './types.js'\n\n/**\n * Format the request target for the stderr summary line. For HTTP forward-proxy\n * the path starts with `/` (so `${domain}${path}` reads `example.com/foo`), but\n * for CONNECT the path field already carries `host:port` and concatenating\n * with `domain` would print `example.comexample.com:443`.\n */\nfunction formatTarget(entry: AuditEntry): string {\n return entry.path.startsWith('/') ? `${entry.domain}${entry.path}` : entry.path\n}\n\n/**\n * Emit one operator-readable audit summary line to stderr. Intentionally NOT a\n * tamper-proof audit trail: anything written on the user's machine is also\n * writable by the user, so there's no integrity story we'd be willing to put\n * in front of a reviewer. The trustworthy audit lives server-side, recorded\n * by the IdP every time it processes a grant request — see the planned\n * per-agent audit route on `id.openape.ai`.\n *\n * Stderr here is purely a debugging convenience for the operator running\n * `apes proxy --` interactively. Persist nothing locally.\n */\nexport function writeAudit(entry: AuditEntry): void {\n const grantSuffix = entry.grant_id ? ` grant=${entry.grant_id}` : ''\n console.error(`[audit] ${entry.action} ${entry.method} ${formatTarget(entry)}${grantSuffix}`)\n}\n","import { resolve4, resolve6 } from 'node:dns/promises'\nimport { isIP } from 'node:net'\n\nconst PRIVATE_RANGES_V4 = [\n { prefix: 0x7F000000, mask: 0xFF000000 }, // 127.0.0.0/8\n { prefix: 0x0A000000, mask: 0xFF000000 }, // 10.0.0.0/8\n { prefix: 0xAC100000, mask: 0xFFF00000 }, // 172.16.0.0/12\n { prefix: 0xC0A80000, mask: 0xFFFF0000 }, // 192.168.0.0/16\n { prefix: 0xA9FE0000, mask: 0xFFFF0000 }, // 169.254.0.0/16\n { prefix: 0x00000000, mask: 0xFF000000 }, // 0.0.0.0/8\n]\n\nfunction ipv4ToNumber(ip: string): number {\n const parts = ip.split('.').map(Number)\n return ((parts[0] << 24) | (parts[1] << 16) | (parts[2] << 8) | parts[3]) >>> 0\n}\n\nfunction isPrivateIPv4(ip: string): boolean {\n const num = ipv4ToNumber(ip)\n return PRIVATE_RANGES_V4.some(r => ((num & r.mask) >>> 0) === r.prefix)\n}\n\nfunction isPrivateIPv6(ip: string): boolean {\n const normalized = ip.toLowerCase()\n\n // Loopback ::1\n if (normalized === '::1') return true\n\n // Unspecified ::\n if (normalized === '::') return true\n\n // Link-local fe80::/10\n if (normalized.startsWith('fe8') || normalized.startsWith('fe9')\n || normalized.startsWith('fea') || normalized.startsWith('feb')) {\n return true\n }\n\n // Unique local fd00::/8\n if (normalized.startsWith('fd')) return true\n\n // IPv4-mapped ::ffff:x.x.x.x\n const v4mapped = normalized.match(/^::ffff:(\\d+\\.\\d+\\.\\d+\\.\\d+)$/)\n if (v4mapped) return isPrivateIPv4(v4mapped[1])\n\n return false\n}\n\nfunction isPrivateIP(ip: string): boolean {\n if (isIP(ip) === 4) return isPrivateIPv4(ip)\n if (isIP(ip) === 6) return isPrivateIPv6(ip)\n return false\n}\n\n/**\n * Result of resolving a hostname for egress safety.\n *\n * - `ok` — resolved to at least one address, none of them private/loopback.\n * Caller forwards.\n * - `private` — at least one resolved address is private/loopback. Caller\n * refuses with a policy response (403).\n * - `unresolvable` — DNS returned NXDOMAIN, NODATA, or a query error. Caller\n * responds with an upstream-failure code (502). Distinct from `private`\n * because the host doesn't exist (or can't be reached) — that's not a\n * policy decision, it's a connectivity problem, and conflating the two\n * ships misleading 403s for typos and DNS hiccups.\n */\nexport type EgressCheckResult =\n | { kind: 'ok' }\n | { kind: 'private' }\n | { kind: 'unresolvable', reason: 'no-records' | 'dns-error' }\n\n/**\n * Check whether forwarding a connection to `hostname` is safe.\n *\n * IP literals are checked directly; hostnames are resolved via DNS (A and\n * AAAA in parallel) and every returned address is screened against the\n * private-range list. If any address is private the result is `private`.\n *\n * Note on DNS-rebinding: a careful attacker could return a public IP to our\n * pre-flight resolution and a private one to the kernel's `connect()`. The\n * proper fix for that is pinning the resolved IP across the actual socket\n * call, not blocking on uncertainty — so we no longer conflate \"I couldn't\n * resolve\" with \"this is private\". Callers can layer pinning on top.\n */\nexport async function checkEgress(hostname: string): Promise<EgressCheckResult> {\n if (isIP(hostname)) {\n return isPrivateIP(hostname) ? { kind: 'private' } : { kind: 'ok' }\n }\n\n if (hostname === 'localhost') return { kind: 'private' }\n\n let settled: PromiseSettledResult<string[]>[]\n try {\n settled = await Promise.allSettled([resolve4(hostname), resolve6(hostname)])\n }\n catch {\n return { kind: 'unresolvable', reason: 'dns-error' }\n }\n\n const addrs: string[] = []\n for (const r of settled) {\n if (r.status === 'fulfilled') addrs.push(...r.value)\n }\n\n if (addrs.length === 0) {\n return { kind: 'unresolvable', reason: 'no-records' }\n }\n\n return addrs.some(addr => isPrivateIP(addr)) ? { kind: 'private' } : { kind: 'ok' }\n}\n\n/**\n * Backwards-compatible boolean shim. Returns true for both `private` and\n * `unresolvable` because that matches the previous (overly conservative)\n * behaviour. New code should call `checkEgress` and distinguish.\n *\n * @deprecated Use `checkEgress` so the caller can return 502 for unresolvable\n * hosts and 403 only for actual private/loopback IPs.\n */\nexport async function isPrivateOrLoopback(hostname: string): Promise<boolean> {\n const result = await checkEgress(hostname)\n return result.kind !== 'ok'\n}\n","import type { IncomingMessage } from 'node:http'\nimport type { Socket } from 'node:net'\nimport { connect } from 'node:net'\nimport type { AgentConfig, MultiAgentProxyConfig, ProxyConfig } from './types.js'\nimport { AuthError, verifyAgentAuth } from './auth.js'\nimport { checkEgress } from './ssrf.js'\nimport { writeAudit } from './audit.js'\nimport { evaluateRules } from './matcher.js'\nimport type { GrantsClient } from './grants-client.js'\n\n/**\n * Handle HTTP CONNECT requests for tunneling (used by HTTP_PROXY clients).\n * Flow: Auth → SSRF → host-based rule evaluation (allow / deny / grant_required)\n * → TCP connect → bidirectional pipe.\n *\n * For HTTPS via CONNECT we only see the hostname (the TLS payload is opaque).\n * Rules with `methods` or `path` filters cannot be enforced at CONNECT time and\n * are skipped — they'd only match if the client also carried the same hostname\n * over cleartext-HTTP forward-proxy. This is intentional: there's no honest way\n * to gate a method we can't observe.\n */\nexport async function handleConnect(\n config: MultiAgentProxyConfig,\n grantsClients: Map<string, GrantsClient>,\n req: IncomingMessage,\n clientSocket: Socket,\n _head: Buffer,\n): Promise<void> {\n const target = req.url ?? ''\n const [host, portStr] = target.split(':')\n const port = Number.parseInt(portStr || '443')\n\n if (!host || !port) {\n clientSocket.write('HTTP/1.1 400 Bad Request\\r\\n\\r\\n')\n clientSocket.destroy()\n return\n }\n\n // SYSTEM BYPASS: outbound to any configured IdP host is unconditionally\n // allowed. The proxy itself talks to the IdP for JWT verification + grant\n // approval, and any process inside the proxy boundary may need to call the\n // IdP for `apes login` / `apes whoami` / token-exchange BEFORE it can ever\n // produce a valid Proxy-Authorization JWT. Blocking IdP traffic would\n // either deadlock the grant flow or lock users out of authentication.\n // We therefore skip auth (no JWT yet for first-login), SSRF (local-dev IdPs\n // can live at 127.0.0.1), and policy rules (operator must not be able to\n // override this system invariant).\n const idpHosts = new Set(\n config.agents\n .map((a) => {\n try {\n return new URL(a.idp_url).hostname.toLowerCase()\n }\n catch {\n return ''\n }\n })\n .filter(h => h.length > 0),\n )\n if (idpHosts.has(host.toLowerCase())) {\n writeAudit({\n ts: new Date().toISOString(),\n agent: 'system',\n action: 'allow',\n domain: host,\n method: 'CONNECT',\n path: target,\n rule: 'idp-system-bypass',\n })\n tunnel(host, port, clientSocket)\n return\n }\n\n const mandatoryAuth = config.proxy.mandatory_auth ?? false\n\n // Auth check — CONNECT always requires auth in mandatory mode\n let agentEmail: string | undefined\n try {\n const authHeader = req.headers['proxy-authorization'] as string | undefined\n let identity: { email: string, act: 'agent' } | null = null\n\n for (const agentConf of config.agents) {\n identity = await verifyAgentAuth(\n authHeader ?? null,\n agentConf.idp_url,\n mandatoryAuth && config.agents.length === 1,\n )\n if (identity) break\n }\n\n if (mandatoryAuth && !identity) {\n throw new AuthError('JWT required')\n }\n\n agentEmail = identity?.email\n\n // Verify agent is known\n if (agentEmail) {\n const known = config.agents.find(a => a.email === agentEmail)\n if (!known) {\n clientSocket.write('HTTP/1.1 403 Forbidden\\r\\n\\r\\n')\n clientSocket.destroy()\n return\n }\n }\n else if (config.agents.length > 1) {\n throw new AuthError('JWT required for multi-agent proxy')\n }\n }\n catch (err) {\n if (err instanceof AuthError) {\n clientSocket.write('HTTP/1.1 401 Unauthorized\\r\\n\\r\\n')\n clientSocket.destroy()\n return\n }\n throw err\n }\n\n // SSRF / reachability check. We split the two outcomes:\n // - `private` → policy refusal, 403 (the proxy will not forward).\n // - `unresolvable` → upstream not reachable, 502 (DNS NXDOMAIN /\n // NODATA / query error — distinct from \"I refuse on policy grounds\";\n // conflating them shipped misleading 403s for typos like\n // `apes proxy -- curl https://example.at`).\n const egress = await checkEgress(host)\n const auditAgent = agentEmail ?? config.agents[0]?.email ?? 'unknown'\n if (egress.kind === 'private') {\n writeAudit({\n ts: new Date().toISOString(),\n agent: auditAgent,\n action: 'deny',\n domain: host,\n method: 'CONNECT',\n path: target,\n rule: 'ssrf-blocked',\n })\n clientSocket.write('HTTP/1.1 403 Forbidden\\r\\n\\r\\n')\n clientSocket.destroy()\n return\n }\n if (egress.kind === 'unresolvable') {\n writeAudit({\n ts: new Date().toISOString(),\n agent: auditAgent,\n action: 'error',\n domain: host,\n method: 'CONNECT',\n path: target,\n rule: `dns-unresolvable (${egress.reason})`,\n })\n clientSocket.write(\n `HTTP/1.1 502 Bad Gateway\\r\\nContent-Type: text/plain\\r\\n\\r\\nDNS lookup failed for ${host} (${egress.reason}).\\r\\n`,\n )\n clientSocket.destroy()\n return\n }\n\n // Host-based rule evaluation. Pick the agent's config: in single-agent or\n // unauth mode this is just config.agents[0]; in multi-agent mode we use the\n // identity established above.\n const agentConf: AgentConfig | undefined = agentEmail\n ? config.agents.find(a => a.email === agentEmail)\n : config.agents[0]\n if (!agentConf) {\n clientSocket.write('HTTP/1.1 403 Forbidden\\r\\n\\r\\n')\n clientSocket.destroy()\n return\n }\n const effectiveEmail = agentEmail ?? agentConf.email\n\n const rulesConfig: ProxyConfig = {\n proxy: {\n listen: config.proxy.listen,\n idp_url: agentConf.idp_url,\n agent_email: agentConf.email,\n default_action: config.proxy.default_action,\n },\n allow: agentConf.allow ?? [],\n deny: agentConf.deny ?? [],\n grant_required: agentConf.grant_required ?? [],\n }\n\n // CONNECT carries no method/path beyond host:port. Pass 'CONNECT' as method\n // and '/' as path so rules with method-filters (which we can't enforce here)\n // simply don't match — operator must specify method-less rules to gate\n // HTTPS hosts.\n const action = evaluateRules(rulesConfig, host, 'CONNECT', '/')\n const baseAudit = {\n ts: new Date().toISOString(),\n agent: effectiveEmail,\n domain: host,\n method: 'CONNECT',\n path: target,\n } as const\n\n if (action.type === 'deny') {\n writeAudit({ ...baseAudit, action: 'deny', rule: 'deny-list' })\n const note = action.note ? ` ${action.note}` : ''\n clientSocket.write(`HTTP/1.1 403 Forbidden\\r\\nContent-Type: text/plain\\r\\n\\r\\nBlocked:${note}\\r\\n`)\n clientSocket.destroy()\n return\n }\n\n if (action.type === 'grant_required') {\n const grantsClient = grantsClients.get(agentConf.email)\n if (!grantsClient) {\n writeAudit({ ...baseAudit, action: 'deny', rule: 'grant_required (no client)' })\n clientSocket.write('HTTP/1.1 500 Internal Server Error\\r\\n\\r\\nNo grants client for agent\\r\\n')\n clientSocket.destroy()\n return\n }\n\n // Async-mode (`request-async`) is meaningful for HTTP forward-proxy where\n // the client can re-issue the request after approval. CONNECT clients\n // (curl, gh, …) won't transparently retry on 407 mid-handshake, so we\n // always block the tunnel until the grant resolves. Operator can shorten\n // the wait via per-rule `duration` or via the IdP's grant TTL.\n const startTs = Date.now()\n try {\n const grant = await grantsClient.requestGrant({\n requester: effectiveEmail,\n targetHost: host,\n audience: 'ape-proxy',\n grantType: action.rule.grant_type,\n permissions: action.rule.permissions,\n reason: `CONNECT ${host}:${port}`,\n duration: action.rule.duration,\n })\n const decided = await grantsClient.waitForApproval(grant.id)\n const waitedMs = Date.now() - startTs\n if (decided.status === 'approved') {\n writeAudit({ ...baseAudit, action: 'grant_approved', rule: 'grant_required', grant_id: decided.id, waited_ms: waitedMs })\n // Fall through to the TCP connect below.\n }\n else {\n writeAudit({ ...baseAudit, action: 'grant_denied', rule: 'grant_required', grant_id: decided.id, waited_ms: waitedMs })\n clientSocket.write(`HTTP/1.1 403 Forbidden\\r\\n\\r\\nGrant denied by ${decided.decided_by ?? 'policy'}\\r\\n`)\n clientSocket.destroy()\n return\n }\n }\n catch (err) {\n const msg = err instanceof Error ? err.message : 'Unknown error'\n writeAudit({ ...baseAudit, action: 'grant_timeout', rule: 'grant_required', error: msg })\n clientSocket.write(`HTTP/1.1 504 Gateway Timeout\\r\\n\\r\\nGrant request failed: ${msg}\\r\\n`)\n clientSocket.destroy()\n return\n }\n }\n else {\n // 'allow' — log and continue.\n writeAudit({ ...baseAudit, action: 'allow', rule: 'allow-list' })\n }\n\n tunnel(host, port, clientSocket)\n}\n\n/**\n * Open a TCP socket to host:port and bidirectionally pipe it with the client\n * socket. Used by both the policy-pass path (auth + rules approved) and the\n * IdP system-bypass path (no auth/policy involved).\n */\nfunction tunnel(host: string, port: number, clientSocket: Socket): void {\n const targetSocket = connect(port, host, () => {\n clientSocket.write('HTTP/1.1 200 Connection Established\\r\\n\\r\\n')\n targetSocket.pipe(clientSocket)\n clientSocket.pipe(targetSocket)\n })\n\n targetSocket.on('error', () => {\n clientSocket.write('HTTP/1.1 502 Bad Gateway\\r\\n\\r\\n')\n clientSocket.destroy()\n })\n\n clientSocket.on('error', () => {\n targetSocket.destroy()\n })\n\n clientSocket.on('close', () => targetSocket.destroy())\n targetSocket.on('close', () => clientSocket.destroy())\n}\n"],"mappings":"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","names":["parseTOML","config","hostname","import_node_net","config","port","agentConf","config","agentConf"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/ca-store.ts","../src/config.ts","../src/proxy.ts","../src/matcher.ts","../src/auth.ts","../src/grants-client.ts","../src/audit.ts","../src/ssrf.ts","../src/connect.ts","../src/mitm-connect.ts","../src/secrets-store.ts","../src/secrets-match.ts"],"sourcesContent":["#!/usr/bin/env node\nimport type { DaemonIdentity, MultiAgentProxyConfig } from './types.js'\nimport { existsSync, readFileSync } from 'node:fs'\nimport { createServer } from 'node:http'\nimport { homedir } from 'node:os'\nimport { join } from 'node:path'\nimport { parseArgs } from 'node:util'\nimport { createLeafCertCache, loadOrCreateCa } from './ca-store.js'\nimport { loadMultiAgentConfig } from './config.js'\nimport { createNodeHandler } from './proxy.js'\nimport { parseSecretsBlob } from './secrets-store.js'\n\nfunction loadIdentity(): DaemonIdentity {\n const path = join(homedir(), '.config', 'apes', 'auth.json')\n if (!existsSync(path)) {\n console.error(`[openape-proxy] missing ${path} — run \\`apes login\\` first`)\n process.exit(2)\n }\n const raw = JSON.parse(readFileSync(path, 'utf-8')) as {\n email?: string\n idp?: string\n access_token?: string\n bearer?: string\n }\n // Production apes-login writes OAuth-2.0 standard `access_token`; some test\n // harnesses use `bearer` for brevity. Accept either.\n const bearer = raw.access_token ?? raw.bearer\n if (!raw.email || !raw.idp || !bearer) {\n console.error(`[openape-proxy] malformed ${path} — re-run \\`apes login\\``)\n process.exit(2)\n }\n return { email: raw.email, idpUrl: raw.idp, bearer }\n}\n\nconst { values } = parseArgs({\n options: {\n config: { type: 'string', short: 'c', default: 'config.toml' },\n 'dry-run': { type: 'boolean', default: false },\n 'mandatory-auth': { type: 'boolean', default: false },\n global: { type: 'boolean', default: false },\n port: { type: 'string' },\n },\n})\n\nif (values.global) {\n // Bun's `node:tls` compat layer does not handle TLSSocket-on-existing-socket\n // reliably for the CONNECT-MITM pipeline (the inner TLS handshake never\n // completes — no `secure`, no `error`, no `close` events fire). Detect Bun\n // and refuse with a helpful pointer at Node before doing any work.\n if ((process.versions as Record<string, string | undefined>).bun) {\n console.error(\n '[openape-proxy] --global mode requires Node (Bun is not supported).\\n'\n + ' Bun\\'s node:tls compat layer does not handle TLSSocket-on-existing-socket\\n'\n + ' for the CONNECT-MITM pipeline. Re-run with `node` directly, or use the\\n'\n + ' built `openape-proxy` binary (its shebang invokes Node).',\n )\n process.exit(2)\n }\n\n // --global mode: read secrets TOML from stdin (max 4 KiB enforced by\n // parseSecretsBlob; we cap stdin at 8 KiB to surface oversize input early\n // with a friendlier error than waiting for the parser).\n const stdinBuf: Buffer[] = []\n let total = 0\n let aborted = false\n process.stdin.on('data', (chunk: Buffer) => {\n if (aborted) return\n total += chunk.length\n if (total > 8 * 1024) {\n console.error('[openape-proxy] stdin > 8 KiB, refusing')\n aborted = true\n process.exit(2)\n }\n stdinBuf.push(chunk)\n })\n process.stdin.on('end', () => {\n if (aborted) return\n const blob = Buffer.concat(stdinBuf).toString('utf-8')\n if (!blob.trim()) {\n console.error('[openape-proxy] --global requires secrets TOML on stdin')\n process.exit(2)\n }\n let store\n try {\n store = parseSecretsBlob(blob)\n }\n catch (err) {\n console.error(`[openape-proxy] secrets parse error: ${(err as Error).message}`)\n process.exit(2)\n }\n const identity = loadIdentity()\n const port = Number.parseInt(values.port ?? '18789')\n\n // Bootstrap (or reuse) the per-user MITM CA stored under\n // ~/.openape/proxy/. Tagging the CN with the operator email keeps\n // multi-account dev machines self-explanatory in the system trust store.\n const ca = loadOrCreateCa({\n certPath: join(homedir(), '.openape', 'proxy', 'ca.crt'),\n keyPath: join(homedir(), '.openape', 'proxy', 'ca.key'),\n subjectCN: `OpenApe Proxy CA (${identity.email})`,\n })\n if (ca.created) {\n console.log(`[openape-proxy] generated new CA at ~/.openape/proxy/ca.crt`)\n }\n const leafCache = createLeafCertCache(ca, { capacity: 256 })\n\n // Daemon mode synthesizes a minimal MultiAgentProxyConfig from the loaded\n // identity. `default_action: 'allow'` keeps Task 15 a pure bind/CA wiring\n // milestone — Task 16 will add the daemon-mode auth bypass on top.\n const config: MultiAgentProxyConfig = {\n proxy: { listen: `127.0.0.1:${port}`, default_action: 'allow' },\n agents: [{ email: identity.email, idp_url: identity.idpUrl }],\n }\n\n const handler = createNodeHandler(config, { secretsStore: store, leafCache, daemonMode: true })\n const server = createServer(handler.handleRequest)\n server.on('connect', handler.handleConnect)\n server.listen(port, '127.0.0.1', () => {\n // `--port 0` lets the OS pick a free port — surface the actual bound\n // port so harness/orchestrators can grep the banner reliably.\n const addr = server.address()\n const actualPort = typeof addr === 'object' && addr ? addr.port : port\n // Emit identity / secrets / OPENAPE_PROXY hint BEFORE the canonical\n // `listening on 127.0.0.1:<port>` banner. The banner is what spawners\n // grep to confirm the daemon is up; printing the hint first guarantees\n // it lives in the same captured chunk that the spawner reads.\n console.log(`[openape-proxy] identity: ${identity.email} (${identity.idpUrl})`)\n const names = store.entries.map(e => e.name).join(', ')\n console.log(`[openape-proxy] secrets: ${names}`)\n console.log(`[openape-proxy] export OPENAPE_PROXY=127.0.0.1:${actualPort}`)\n console.log(`[openape-proxy] listening on 127.0.0.1:${actualPort}`)\n })\n\n process.on('SIGINT', () => server.close(() => process.exit(0)))\n process.on('SIGTERM', () => server.close(() => process.exit(0)))\n })\n process.stdin.resume()\n}\nelse {\n const configPath = values.config!\n\n console.log(`[openape-proxy] Loading config from ${configPath}`)\n const config = loadMultiAgentConfig(configPath, {\n mandatoryAuth: values['mandatory-auth'] || undefined,\n })\n\n if (values['dry-run']) {\n console.log('[openape-proxy] DRY RUN mode — logging only, not blocking')\n console.log('[openape-proxy] Config loaded:')\n console.log(` Listen: ${config.proxy.listen}`)\n console.log(` Default action: ${config.proxy.default_action}`)\n console.log(` Mandatory auth: ${config.proxy.mandatory_auth ?? false}`)\n console.log(` Agents: ${config.agents.length}`)\n for (const agent of config.agents) {\n const allowCount = agent.allow?.length ?? 0\n const denyCount = agent.deny?.length ?? 0\n const grantCount = agent.grant_required?.length ?? 0\n console.log(` ${agent.email} (${agent.idp_url}) — ${allowCount} allow, ${denyCount} deny, ${grantCount} grant`)\n }\n process.exit(0)\n }\n\n const handler = createNodeHandler(config)\n\n const port = Number.parseInt(config.proxy.listen.split(':')[1] || '9090')\n const hostname = config.proxy.listen.split(':')[0] || '127.0.0.1'\n\n const server = createServer(handler.handleRequest)\n server.on('connect', handler.handleConnect)\n\n server.listen(port, hostname, () => {\n // When configured with port 0, the OS assigns a free port — use the actual\n // bound port for the log line so external orchestrators (apes proxy --) can\n // grep this line to discover where to connect.\n const addr = server.address()\n const actualPort = typeof addr === 'object' && addr ? addr.port : port\n console.log(`[openape-proxy] Listening on http://${hostname}:${actualPort}`)\n console.log(`[openape-proxy] CONNECT tunneling enabled`)\n console.log(`[openape-proxy] Mandatory auth: ${config.proxy.mandatory_auth ?? false}`)\n console.log(`[openape-proxy] Agents: ${config.agents.map(a => a.email).join(', ')}`)\n console.log(`[openape-proxy] Default action: ${config.proxy.default_action}`)\n })\n\n // Graceful shutdown\n process.on('SIGINT', () => {\n console.log('\\n[openape-proxy] Shutting down...')\n server.close()\n process.exit(0)\n })\n\n process.on('SIGTERM', () => {\n console.log('[openape-proxy] Shutting down...')\n server.close()\n process.exit(0)\n })\n}\n","import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'node:fs'\nimport { dirname } from 'node:path'\nimport forge from 'node-forge'\n\nexport interface LoadOrCreateCaOpts {\n certPath: string\n keyPath: string\n subjectCN: string\n}\n\nexport interface CaBundle {\n certPem: string\n keyPem: string\n created: boolean\n}\n\nexport function loadOrCreateCa(opts: LoadOrCreateCaOpts): CaBundle {\n if (existsSync(opts.certPath) && existsSync(opts.keyPath)) {\n return {\n certPem: readFileSync(opts.certPath, 'utf-8'),\n keyPem: readFileSync(opts.keyPath, 'utf-8'),\n created: false,\n }\n }\n\n const keys = forge.pki.rsa.generateKeyPair({ bits: 2048 })\n const cert = forge.pki.createCertificate()\n cert.publicKey = keys.publicKey\n cert.serialNumber = String(Date.now())\n cert.validity.notBefore = new Date()\n cert.validity.notAfter = new Date(Date.now() + 10 * 365 * 86400_000)\n // Tag the CN explicitly as UTF8String. node-forge defaults to PrintableString\n // for ASCII inputs, but its DER serializer can emit byte sequences that Go's\n // strict crypto/x509 parser flags (\"invalid PrintableString\"). UTF8String\n // sidesteps the strict-mode check and is universally accepted.\n // valueTagClass takes an asn1.Type value (the upstream @types annotation says\n // asn1.Class but node-forge's x509.js reads it from asn1.Type at runtime).\n const attrs = [\n // @types/node-forge types valueTagClass as asn1.Class, but node-forge's\n // x509.js reads it as an asn1.Type at runtime (see comment above). Cast to\n // satisfy the too-narrow typing without changing runtime behaviour.\n { name: 'commonName', value: opts.subjectCN, valueTagClass: forge.asn1.Type.UTF8 as unknown as forge.asn1.Class },\n ]\n cert.setSubject(attrs)\n cert.setIssuer(attrs)\n cert.setExtensions([\n { name: 'basicConstraints', cA: true },\n { name: 'keyUsage', keyCertSign: true, cRLSign: true },\n { name: 'subjectKeyIdentifier' },\n ])\n cert.sign(keys.privateKey, forge.md.sha256.create())\n\n const certPem = forge.pki.certificateToPem(cert)\n const keyPem = forge.pki.privateKeyToPem(keys.privateKey)\n\n mkdirSync(dirname(opts.certPath), { recursive: true, mode: 0o700 })\n writeFileSync(opts.certPath, certPem, { mode: 0o600 })\n writeFileSync(opts.keyPath, keyPem, { mode: 0o600 })\n\n return { certPem, keyPem, created: true }\n}\n\nexport interface LeafCert {\n certPem: string\n keyPem: string\n expiresAt: number\n}\n\nexport function mintLeafCert(ca: CaBundle, hostname: string): LeafCert {\n const caCert = forge.pki.certificateFromPem(ca.certPem)\n const caKey = forge.pki.privateKeyFromPem(ca.keyPem)\n\n const keys = forge.pki.rsa.generateKeyPair({ bits: 2048 })\n const cert = forge.pki.createCertificate()\n cert.publicKey = keys.publicKey\n cert.serialNumber = `${Date.now()}${Math.floor(Math.random() * 1e6)}`\n cert.validity.notBefore = new Date()\n const expiresAt = Date.now() + 24 * 3600_000\n cert.validity.notAfter = new Date(expiresAt)\n // Tag CN as UTF8String for the same reason as the CA — keeps Go's strict\n // crypto/x509 parser happy. The issuer attributes come straight from the\n // parsed CA, which is already UTF8-tagged.\n cert.setSubject([\n // See note in createCaCert: valueTagClass is an asn1.Type at runtime;\n // cast around the too-narrow @types/node-forge annotation.\n { name: 'commonName', value: hostname, valueTagClass: forge.asn1.Type.UTF8 as unknown as forge.asn1.Class },\n ])\n cert.setIssuer(caCert.subject.attributes)\n cert.setExtensions([\n { name: 'basicConstraints', cA: false },\n { name: 'keyUsage', digitalSignature: true, keyEncipherment: true },\n { name: 'extKeyUsage', serverAuth: true },\n { name: 'subjectAltName', altNames: [{ type: 2, value: hostname }] },\n ])\n cert.sign(caKey, forge.md.sha256.create())\n\n return {\n certPem: forge.pki.certificateToPem(cert),\n keyPem: forge.pki.privateKeyToPem(keys.privateKey),\n expiresAt,\n }\n}\n\nexport interface LeafCertCache {\n get: (hostname: string) => LeafCert\n}\n\nexport function createLeafCertCache(ca: CaBundle, opts: { capacity: number }): LeafCertCache {\n const cache = new Map<string, LeafCert>()\n return {\n get(hostname) {\n const existing = cache.get(hostname)\n if (existing && existing.expiresAt > Date.now()) {\n cache.delete(hostname)\n cache.set(hostname, existing) // refresh LRU position\n return existing\n }\n const fresh = mintLeafCert(ca, hostname)\n cache.set(hostname, fresh)\n while (cache.size > opts.capacity) {\n const oldestKey = cache.keys().next().value\n if (oldestKey === undefined) break\n cache.delete(oldestKey)\n }\n return fresh\n },\n }\n}\n","import { readFileSync } from 'node:fs'\nimport { parse as parseTOML } from 'smol-toml'\nimport type { AgentConfig, MultiAgentProxyConfig, ProxyConfig } from './types.js'\n\nexport function loadConfig(path: string): ProxyConfig {\n const raw = readFileSync(path, 'utf-8')\n\n let parsed: Record<string, unknown>\n if (path.endsWith('.json')) {\n parsed = JSON.parse(raw)\n }\n else {\n parsed = parseTOML(raw) as Record<string, unknown>\n }\n\n const proxy = parsed.proxy as ProxyConfig['proxy']\n if (!proxy?.listen || !proxy?.idp_url || !proxy?.agent_email) {\n throw new Error('Config must have [proxy] with listen, idp_url, and agent_email')\n }\n\n proxy.default_action ??= 'block'\n\n return {\n proxy,\n allow: (parsed.allow ?? []) as ProxyConfig['allow'],\n deny: (parsed.deny ?? []) as ProxyConfig['deny'],\n grant_required: (parsed.grant_required ?? []) as ProxyConfig['grant_required'],\n }\n}\n\n/**\n * Load config as multi-agent format.\n * If the config has an `agents` array, use it directly.\n * Otherwise, convert single-agent format to multi-agent for backward-compat.\n */\nexport function loadMultiAgentConfig(path: string, overrides?: { mandatoryAuth?: boolean }): MultiAgentProxyConfig {\n const raw = readFileSync(path, 'utf-8')\n\n let parsed: Record<string, unknown>\n if (path.endsWith('.json')) {\n parsed = JSON.parse(raw)\n }\n else {\n parsed = parseTOML(raw) as Record<string, unknown>\n }\n\n const proxy = parsed.proxy as Record<string, unknown>\n if (!proxy?.listen) {\n throw new Error('Config must have [proxy] with listen')\n }\n\n const baseProxy: MultiAgentProxyConfig['proxy'] = {\n listen: proxy.listen as string,\n default_action: (proxy.default_action as MultiAgentProxyConfig['proxy']['default_action']) ?? 'block',\n mandatory_auth: overrides?.mandatoryAuth ?? (proxy.mandatory_auth as boolean | undefined),\n }\n\n // Multi-agent format: has agents array\n if (Array.isArray(parsed.agents)) {\n return {\n proxy: baseProxy,\n agents: parsed.agents as AgentConfig[],\n }\n }\n\n // Single-agent format: convert to multi-agent\n const idpUrl = proxy.idp_url as string\n const agentEmail = proxy.agent_email as string\n if (!idpUrl || !agentEmail) {\n throw new Error('Single-agent config requires proxy.idp_url and proxy.agent_email')\n }\n\n return {\n proxy: baseProxy,\n agents: [{\n email: agentEmail,\n idp_url: idpUrl,\n allow: (parsed.allow ?? []) as AgentConfig['allow'],\n deny: (parsed.deny ?? []) as AgentConfig['deny'],\n grant_required: (parsed.grant_required ?? []) as AgentConfig['grant_required'],\n }],\n }\n}\n","import type { IncomingMessage, ServerResponse } from 'node:http'\nimport type { Socket } from 'node:net'\nimport { createHash } from 'node:crypto'\nimport type { AgentConfig, AuditEntry, MultiAgentProxyConfig, ProxyConfig, SecretsStore } from './types.js'\nimport type { LeafCertCache } from './ca-store.js'\nimport { evaluateRules } from './matcher.js'\nimport { AuthError, verifyAgentAuth } from './auth.js'\nimport { GrantsClient } from './grants-client.js'\nimport { writeAudit } from './audit.js'\nimport { checkEgress } from './ssrf.js'\nimport { handleConnect } from './connect.js'\n\n/**\n * Minimal fetch shape used for upstream calls. `typeof fetch` itself carries a\n * `preconnect` static (Node 22+) which we don't use, so we narrow to the call\n * signature to keep `MultiAgentProxyDeps.fetchImpl` easy to override in tests.\n */\nexport type UpstreamFetch = (input: Request | URL | string, init?: RequestInit) => Promise<Response>\n\n/**\n * Optional dependencies injected into `createMultiAgentProxy`. Kept additive so\n * existing single-call sites (legacy `createProxy`, `createNodeHandler`) keep\n * working without supplying any of these.\n */\nexport interface MultiAgentProxyDeps {\n /**\n * In-memory secret lookup. When set, the forward-proxy fetch path consults\n * the store right before forwarding and injects a header on a target match.\n */\n secretsStore?: SecretsStore\n /**\n * Override the upstream fetch implementation. Defaults to `globalThis.fetch`.\n * Tests can pass a stub here when they need to assert the outgoing request\n * shape without relying on `vi.spyOn(globalThis, 'fetch')`.\n */\n fetchImpl?: UpstreamFetch\n /**\n * Daemon mode: identity is implicit (loaded from `~/.config/apes/auth.json`\n * at startup) so per-request `Proxy-Authorization` JWT extraction is\n * skipped. The daemon's identity = `config.agents[0].email`. Inline mode\n * (`apes proxy --` ephemeral case) leaves this unset and keeps the existing\n * JWT-required path.\n */\n daemonMode?: boolean\n}\n\n/**\n * Compute a request hash that uniquely identifies the intent.\n * hash = sha256(METHOD + \" \" + FULL_URL + \"\\n\" + BODY)\n * This binds the grant to the exact request — no bait-and-switch.\n */\nasync function computeRequestHash(method: string, targetUrl: string, body: ArrayBuffer | null): Promise<string> {\n const hash = createHash('sha256')\n hash.update(`${method} ${targetUrl}\\n`)\n if (body && body.byteLength > 0) {\n hash.update(new Uint8Array(body))\n }\n return hash.digest('hex')\n}\n\n/** Legacy single-agent proxy */\nexport function createProxy(config: ProxyConfig) {\n const multiConfig: MultiAgentProxyConfig = {\n proxy: {\n listen: config.proxy.listen,\n default_action: config.proxy.default_action,\n mandatory_auth: config.proxy.mandatory_auth,\n },\n agents: [{\n email: config.proxy.agent_email,\n idp_url: config.proxy.idp_url,\n allow: config.allow,\n deny: config.deny,\n grant_required: config.grant_required,\n }],\n }\n return createMultiAgentProxy(multiConfig)\n}\n\n/**\n * Build the per-agent GrantsClient map used by both the HTTP forward-proxy\n * handler (in this file's `createMultiAgentProxy`) and the CONNECT handler\n * (in `connect.ts`). Exposed so `createNodeHandler` can share a single map\n * instead of letting each handler construct its own — keeps any future\n * per-agent token state coherent.\n */\nexport function buildGrantsClients(config: MultiAgentProxyConfig): Map<string, GrantsClient> {\n const grantsClients = new Map<string, GrantsClient>()\n for (const agent of config.agents) {\n grantsClients.set(agent.email, new GrantsClient(agent.idp_url))\n }\n return grantsClients\n}\n\n/** Multi-agent proxy with SSRF protection and mandatory auth */\nexport function createMultiAgentProxy(\n config: MultiAgentProxyConfig,\n grantsClients: Map<string, GrantsClient> = buildGrantsClients(config),\n deps: MultiAgentProxyDeps = {},\n) {\n // Backwards-compat: if caller didn't pre-build the map, we build our own.\n // createNodeHandler always passes one in so the CONNECT handler can share it.\n for (const agent of config.agents) {\n if (!grantsClients.has(agent.email)) {\n grantsClients.set(agent.email, new GrantsClient(agent.idp_url))\n }\n }\n\n const mandatoryAuth = config.proxy.mandatory_auth ?? false\n const secretsStore = deps.secretsStore\n const daemonMode = deps.daemonMode ?? false\n // Bind to globalThis so `vi.spyOn(globalThis, 'fetch')` in tests intercepts\n // the upstream call. Resolved per-call so spies installed after construction\n // still take effect.\n const fetchImpl: UpstreamFetch = deps.fetchImpl ?? ((input, init) => globalThis.fetch(input, init))\n\n return {\n port: Number.parseInt(config.proxy.listen.split(':')[1] || '9090'),\n hostname: config.proxy.listen.split(':')[0] || '127.0.0.1',\n\n async fetch(req: Request): Promise<Response> {\n const url = new URL(req.url)\n const startTime = Date.now()\n\n // Health endpoint\n if (url.pathname === '/healthz') {\n return Response.json({\n status: 'ok',\n agents: config.agents.map(a => a.email),\n })\n }\n\n // Parse target URL from the path\n const targetUrl = url.pathname.slice(1) + url.search\n let targetParsed: URL\n try {\n targetParsed = new URL(targetUrl)\n }\n catch {\n return new Response(\n 'Invalid target URL. Send requests as: http://proxy:port/https://target.com/path',\n { status: 400 },\n )\n }\n\n const domain = targetParsed.hostname\n const method = req.method\n const path = targetParsed.pathname\n\n // SSRF / reachability check. Split outcomes so a non-existent host\n // doesn't ship as a misleading 403:\n // - `private` → policy refusal (403).\n // - `unresolvable` → upstream connectivity failure (502).\n const egress = await checkEgress(domain)\n if (egress.kind === 'private') {\n return new Response('Blocked: private/loopback IP', { status: 403 })\n }\n if (egress.kind === 'unresolvable') {\n return new Response(\n `DNS lookup failed for ${domain} (${egress.reason}).`,\n { status: 502 },\n )\n }\n\n // Read body once (needed for hash + forwarding)\n const bodyBuffer = req.body ? await req.arrayBuffer() : null\n\n // Verify agent identity — find IdP URL from first agent (for JWKS verification)\n // In multi-agent mode, we need the JWT to identify the agent first.\n // We try verification against each agent's IdP until one succeeds.\n let agentIdentity: { email: string, act: 'agent' } | null = null\n if (daemonMode) {\n // Identity is implicit — the daemon was started by/for one specific\n // agent. The first (and only) agent in config.agents is the daemon's\n // identity, loaded from `~/.config/apes/auth.json` at startup.\n agentIdentity = { email: config.agents[0]!.email, act: 'agent' }\n }\n else {\n try {\n for (const agentConf of config.agents) {\n agentIdentity = await verifyAgentAuth(\n req.headers.get('proxy-authorization'),\n agentConf.idp_url,\n mandatoryAuth && config.agents.length === 1,\n )\n if (agentIdentity) break\n }\n\n // If mandatory auth and no identity found from any IdP\n if (mandatoryAuth && !agentIdentity) {\n throw new AuthError('JWT required')\n }\n }\n catch (err) {\n if (err instanceof AuthError) {\n return new Response(`Unauthorized: ${err.message}`, { status: 401 })\n }\n throw err\n }\n }\n\n // Find the matching agent config\n const agentEmail = agentIdentity?.email\n let agentConf: AgentConfig | undefined\n\n if (agentEmail) {\n agentConf = config.agents.find(a => a.email === agentEmail)\n if (!agentConf) {\n return new Response(`Forbidden: unknown agent ${agentEmail}`, { status: 403 })\n }\n }\n else if (config.agents.length === 1) {\n // Non-mandatory auth, single agent: use the only agent config\n agentConf = config.agents[0]\n }\n else {\n return new Response('Unauthorized: JWT required for multi-agent proxy', { status: 401 })\n }\n\n const effectiveEmail = agentEmail ?? agentConf.email\n const grantsClient = grantsClients.get(agentConf.email)!\n\n // Build a ProxyConfig-shaped object for evaluateRules\n const rulesConfig: ProxyConfig = {\n proxy: {\n listen: config.proxy.listen,\n idp_url: agentConf.idp_url,\n agent_email: agentConf.email,\n default_action: config.proxy.default_action,\n },\n allow: agentConf.allow ?? [],\n deny: agentConf.deny ?? [],\n grant_required: agentConf.grant_required ?? [],\n }\n\n // Evaluate rules\n const action = evaluateRules(rulesConfig, domain, method, path)\n\n const baseAudit: Omit<AuditEntry, 'action' | 'rule'> = {\n ts: new Date().toISOString(),\n agent: effectiveEmail,\n domain,\n method,\n path,\n }\n\n // DENY\n if (action.type === 'deny') {\n writeAudit({ ...baseAudit, action: 'deny', rule: 'deny-list', grant_id: null })\n return new Response(`Blocked: ${action.note || 'deny rule'}`, { status: 403 })\n }\n\n // ALLOW (no grant needed)\n if (action.type === 'allow') {\n writeAudit({ ...baseAudit, action: 'allow', rule: 'allow-list', grant_id: null })\n return forwardRequest(req, targetUrl, targetParsed, bodyBuffer, secretsStore, fetchImpl)\n }\n\n // GRANT REQUIRED\n const rule = action.rule\n const permissions = rule.permissions ?? [`${method.toLowerCase()}:${domain}`]\n\n // Compute request hash — binds grant to exact method + URL + body\n const requestHash = await computeRequestHash(method, targetUrl, bodyBuffer)\n\n // Check for existing grant\n const existing = await grantsClient.findExistingGrant(\n effectiveEmail,\n domain,\n 'proxy',\n permissions,\n ).catch(() => null)\n\n if (existing) {\n writeAudit({\n ...baseAudit,\n action: 'grant_approved',\n rule: 'standing-grant',\n grant_id: existing.id,\n request_hash: requestHash,\n })\n return forwardRequest(req, targetUrl, targetParsed, bodyBuffer, secretsStore, fetchImpl)\n }\n\n // No existing grant — behavior depends on default_action\n if (config.proxy.default_action === 'block') {\n writeAudit({ ...baseAudit, action: 'deny', rule: 'no-grant (block mode)', grant_id: null })\n return new Response('No grant — blocked', { status: 403 })\n }\n\n if (config.proxy.default_action === 'request-async') {\n // Create grant request, return 407 immediately\n const grant = await grantsClient.requestGrant({\n requester: effectiveEmail,\n targetHost: domain,\n audience: 'ape-proxy',\n grantType: rule.grant_type,\n permissions,\n reason: `${method} ${targetUrl}`,\n requestHash,\n duration: rule.duration,\n }).catch(() => null)\n\n writeAudit({\n ...baseAudit,\n action: 'grant_denied',\n rule: 'grant_required (async)',\n grant_id: grant?.id ?? null,\n })\n\n return new Response(\n JSON.stringify({\n error: 'Grant required',\n grant_id: grant?.id,\n message: 'Grant request created. Retry after approval.',\n }),\n { status: 407, headers: { 'Content-Type': 'application/json' } },\n )\n }\n\n // BLOCKING mode: create grant request and wait\n console.error(`[proxy] Requesting grant for ${method} ${domain}${path} — waiting for approval...`)\n\n try {\n const grant = await grantsClient.requestGrant({\n requester: effectiveEmail,\n targetHost: domain,\n audience: 'ape-proxy',\n grantType: rule.grant_type,\n permissions,\n reason: `${method} ${targetUrl}`,\n requestHash,\n duration: rule.duration,\n })\n\n const approved = await grantsClient.waitForApproval(grant.id)\n\n const waitedMs = Date.now() - startTime\n\n if (approved.status === 'approved') {\n writeAudit({\n ...baseAudit,\n action: 'grant_approved',\n rule: 'grant_required',\n grant_id: approved.id,\n request_hash: requestHash,\n waited_ms: waitedMs,\n })\n return forwardRequest(req, targetUrl, targetParsed, bodyBuffer, secretsStore, fetchImpl)\n }\n\n writeAudit({\n ...baseAudit,\n action: 'grant_denied',\n rule: 'grant_required',\n grant_id: approved.id,\n waited_ms: waitedMs,\n })\n return new Response(`Grant denied by ${approved.decided_by}`, { status: 403 })\n }\n catch (err) {\n const msg = err instanceof Error ? err.message : 'Unknown error'\n writeAudit({\n ...baseAudit,\n action: 'grant_timeout',\n rule: 'grant_required',\n error: msg,\n })\n return new Response(`Grant request failed: ${msg}`, { status: 504 })\n }\n },\n }\n}\n\n/**\n * Optional dependencies forwarded by `createNodeHandler` to both the\n * forward-proxy fetch path (`createMultiAgentProxy`) and the CONNECT path\n * (`handleConnect`). Kept optional so existing zero-arg callers (legacy\n * single-agent paths, simple multi-agent setups without secret injection or\n * MITM) keep working unchanged.\n */\nexport interface NodeHandlerDeps {\n /** In-memory secret lookup, forwarded to both the fetch and CONNECT paths. */\n secretsStore?: SecretsStore\n /**\n * Per-host leaf cert cache backed by the daemon's CA bundle. Required (in\n * combination with `secretsStore`) for CONNECT-MITM secret injection on\n * HTTPS targets; absent → CONNECT stays an opaque TCP tunnel.\n */\n leafCache?: LeafCertCache\n /**\n * Daemon mode: skip per-request `Proxy-Authorization` JWT extraction on\n * both the forward-proxy fetch path and the CONNECT path. Forwarded to\n * `createMultiAgentProxy` and `handleConnect`. Inline (ephemeral) mode\n * leaves this unset.\n */\n daemonMode?: boolean\n}\n\n/**\n * Create a node:http compatible handler for use with http.createServer().\n * Returns both a request handler and a CONNECT handler.\n */\nexport function createNodeHandler(config: MultiAgentProxyConfig, deps: NodeHandlerDeps = {}): {\n handleRequest: (req: IncomingMessage, res: ServerResponse) => void\n handleConnect: (req: IncomingMessage, socket: Socket, head: Buffer) => void\n} {\n // Build grantsClients once; share with both forward-proxy fetch path\n // (createMultiAgentProxy) and the CONNECT path (handleConnect). Without\n // sharing, CONNECT-time grant_required rules couldn't reach the IdP in\n // multi-agent mode without duplicating constructor work.\n const grantsClients = buildGrantsClients(config)\n const proxy = createMultiAgentProxy(config, grantsClients, {\n secretsStore: deps.secretsStore,\n daemonMode: deps.daemonMode,\n })\n\n return {\n handleRequest(req: IncomingMessage, res: ServerResponse) {\n // Standard HTTP_PROXY clients (curl, gh, git, npm) send the target as\n // an absolute URL in the request line for cleartext forward-proxying:\n // `GET http://example.com/path HTTP/1.1`\n // node:http surfaces that absolute URL via `req.url`. The legacy\n // `proxy.fetch` extraction expects a path-encoded form\n // (`/<full-target-url>`), so we adapt here: when `req.url` is\n // absolute, prefix it with a slash so `pathname.slice(1)` recovers\n // the same target string. Path-form clients (legacy) keep working.\n const reqUrl = req.url || '/'\n const isAbsolute = reqUrl.startsWith('http://') || reqUrl.startsWith('https://')\n const proxyHost = req.headers.host || 'localhost'\n const url = isAbsolute\n ? `http://${proxyHost}/${reqUrl}`\n : `http://${proxyHost}${reqUrl}`\n const chunks: Buffer[] = []\n\n req.on('data', (chunk: Buffer) => chunks.push(chunk))\n req.on('end', async () => {\n try {\n const body = chunks.length > 0 ? Buffer.concat(chunks) : undefined\n const headers = new Headers()\n for (const [key, value] of Object.entries(req.headers)) {\n if (value) {\n headers.set(key, Array.isArray(value) ? value.join(', ') : value)\n }\n }\n\n const request = new Request(url, {\n method: req.method,\n headers,\n body: body && req.method !== 'GET' && req.method !== 'HEAD' ? body : undefined,\n duplex: 'half',\n } as RequestInit)\n\n const response = await proxy.fetch(request)\n\n res.writeHead(response.status, response.statusText, Object.fromEntries(response.headers))\n if (response.body) {\n const reader = response.body.getReader()\n const pump = async (): Promise<void> => {\n const { done, value } = await reader.read()\n if (done) {\n res.end()\n return\n }\n res.write(value)\n return pump()\n }\n await pump()\n }\n else {\n res.end()\n }\n }\n catch {\n res.writeHead(502)\n res.end('Proxy error')\n }\n })\n },\n\n handleConnect(req: IncomingMessage, socket: Socket, head: Buffer) {\n handleConnect(config, grantsClients, req, socket, head, {\n secretsStore: deps.secretsStore,\n leafCache: deps.leafCache,\n daemonMode: deps.daemonMode,\n })\n },\n }\n}\n\n/**\n * Forward a request to the target URL.\n * Strips proxy-specific headers, preserves the rest.\n *\n * If a `secretsStore` is provided and a secret matches `targetParsed`, the\n * configured header is rendered from the entry's template and set on the\n * outgoing request (replacing any existing value with the same name).\n */\nasync function forwardRequest(\n originalReq: Request,\n targetUrl: string,\n targetParsed: URL,\n cachedBody: ArrayBuffer | null | undefined,\n secretsStore: SecretsStore | undefined,\n fetchImpl: UpstreamFetch,\n): Promise<Response> {\n const headers = new Headers(originalReq.headers)\n // Remove proxy-specific headers\n headers.delete('proxy-authorization')\n headers.delete('proxy-connection')\n // Don't send host of the proxy\n headers.delete('host')\n\n // Inject hook: consult the secrets store after grant decisions and before\n // forwarding. Logs are redacted — only name/header/method/url are printed,\n // never the secret value or rendered template.\n if (secretsStore) {\n const match = secretsStore.findFor(targetParsed)\n if (match) {\n const rendered = match.template.replace(/\\$\\{value\\}/g, match.value)\n headers.set(match.header, rendered)\n console.error(\n `[openape-proxy] injected secret '${match.name}' into ${match.header} for ${originalReq.method} ${targetUrl}`,\n )\n }\n }\n\n const body = cachedBody && cachedBody.byteLength > 0 ? cachedBody : null\n\n try {\n const upstreamReq = new Request(targetUrl, {\n method: originalReq.method,\n headers,\n body,\n duplex: 'half',\n redirect: 'manual',\n } as RequestInit)\n const res = await fetchImpl(upstreamReq)\n\n // Stream the response back\n const responseHeaders = new Headers(res.headers)\n // Remove hop-by-hop headers\n responseHeaders.delete('transfer-encoding')\n responseHeaders.delete('connection')\n\n return new Response(res.body, {\n status: res.status,\n statusText: res.statusText,\n headers: responseHeaders,\n })\n }\n catch (err) {\n const msg = err instanceof Error ? err.message : 'Upstream error'\n return new Response(`Proxy error: ${msg}`, { status: 502 })\n }\n}\n","import type { ProxyConfig, RuleAction, RuleEntry } from './types.js'\n\n/**\n * Match a glob pattern against a string.\n * Supports * (any segment) and ** (any number of segments).\n */\nfunction globMatch(pattern: string, value: string): boolean {\n // Simple glob: convert * to regex\n const regex = new RegExp(\n `^${\n pattern\n .replace(/[.+^${}()|[\\]\\\\]/g, '\\\\$&') // escape regex chars except *\n .replace(/\\*\\*/g, '<<<DOUBLESTAR>>>')\n .replace(/\\*/g, '[^/]*')\n .replace(/<<<DOUBLESTAR>>>/g, '.*')\n }$`,\n )\n return regex.test(value)\n}\n\nfunction matchesRule(rule: RuleEntry, domain: string, method: string, path: string): boolean {\n // Domain match (supports wildcards like *.github.com)\n if (!globMatch(rule.domain, domain)) return false\n\n // Method match (if specified)\n if (rule.methods && rule.methods.length > 0) {\n if (!rule.methods.includes(method.toUpperCase())) return false\n }\n\n // Path match (if specified)\n if (rule.path) {\n if (!globMatch(rule.path, path)) return false\n }\n\n return true\n}\n\n/**\n * Evaluate rules in order: deny → allow → grant_required → default_action\n */\nexport function evaluateRules(\n config: ProxyConfig,\n domain: string,\n method: string,\n path: string,\n): RuleAction {\n // 1. Check deny list first\n for (const rule of config.deny) {\n if (matchesRule(rule, domain, method, path)) {\n return { type: 'deny', note: rule.note }\n }\n }\n\n // 2. Check allow list\n for (const rule of config.allow) {\n if (matchesRule(rule, domain, method, path)) {\n return { type: 'allow' }\n }\n }\n\n // 3. Check grant_required rules (most specific first)\n for (const rule of config.grant_required) {\n if (matchesRule(rule, domain, method, path)) {\n return { type: 'grant_required', rule }\n }\n }\n\n // 4. Default action for unmatched hosts. Conceptually the proxy-side\n // counterpart of the IdP's per-agent YOLO policy — both decide what\n // happens when no specific rule matches, both live server-side (proxy or\n // IdP), neither leaks the decision to the agent. Differences:\n // - IdP YOLO is per-agent, evaluated at grant time, can have deny-patterns\n // and an expiry window.\n // - Proxy default_action is per-proxy-instance, evaluated at request time,\n // binary outcome (allow/deny/request-grant).\n //\n // Modes:\n // - 'block': hard deny — paranoid agent profile.\n // - 'allow': hard pass — transparent-audit profile (log every call,\n // enforce nothing). Equivalent role to a YOLO policy without a\n // deny-list.\n // - 'request' / 'request-async' (OpenApe-default): treat as\n // grant_required with a once-grant catch-all so every new host\n // surfaces an interactive grant decision.\n if (config.proxy.default_action === 'block') {\n return { type: 'deny', note: 'No matching rule (default: block)' }\n }\n if (config.proxy.default_action === 'allow') {\n return { type: 'allow' }\n }\n\n return {\n type: 'grant_required',\n rule: {\n domain: '*',\n grant_type: 'once',\n },\n }\n}\n","import { verifyJWT, createRemoteJWKS } from '@openape/core'\n\nexport interface AgentIdentity {\n email: string\n act: 'agent'\n}\n\nexport class AuthError extends Error {\n constructor(message: string) {\n super(message)\n this.name = 'AuthError'\n }\n}\n\n/**\n * Verify agent JWT from Proxy-Authorization header.\n * Returns the agent identity or null if invalid/missing.\n * When mandatory is true, throws AuthError if no valid JWT is provided.\n */\nexport async function verifyAgentAuth(\n authHeader: string | null,\n idpUrl: string,\n mandatory: boolean = false,\n): Promise<AgentIdentity | null> {\n if (!authHeader) {\n if (mandatory) throw new AuthError('JWT required')\n return null\n }\n\n const match = authHeader.match(/^Bearer (.+)$/i)\n if (!match) {\n if (mandatory) throw new AuthError('Invalid authorization header')\n return null\n }\n\n const token = match[1]\n\n try {\n const jwks = createRemoteJWKS(`${idpUrl}/.well-known/jwks.json`)\n const { payload } = await verifyJWT(token, jwks, { issuer: idpUrl })\n\n if (payload.act !== 'agent' || !payload.sub) {\n if (mandatory) throw new AuthError('Invalid agent token')\n return null\n }\n\n return {\n email: payload.sub as string,\n act: 'agent',\n }\n }\n catch (err) {\n if (err instanceof AuthError) throw err\n if (mandatory) throw new AuthError('JWT verification failed')\n return null\n }\n}\n","import type { OpenApeGrant, GrantType } from '@openape/core'\n\n/**\n * Client for the IdP's grant management API.\n * Creates grant requests and polls for approval.\n */\nexport class GrantsClient {\n private idpUrl: string\n private agentToken: string | undefined\n\n constructor(idpUrl: string) {\n this.idpUrl = idpUrl.replace(/\\/$/, '')\n }\n\n setAgentToken(token: string): void {\n this.agentToken = token\n }\n\n private headers(): Record<string, string> {\n const h: Record<string, string> = { 'Content-Type': 'application/json' }\n if (this.agentToken) {\n h.Authorization = `Bearer ${this.agentToken}`\n }\n return h\n }\n\n /**\n * Create a grant request on the IdP.\n */\n async requestGrant(opts: {\n requester: string\n targetHost: string\n audience: string\n grantType: GrantType\n permissions?: string[]\n reason?: string\n requestHash?: string\n duration?: number\n }): Promise<OpenApeGrant> {\n const res = await fetch(`${this.idpUrl}/api/grants`, {\n method: 'POST',\n headers: this.headers(),\n body: JSON.stringify({\n requester: opts.requester,\n target_host: opts.targetHost,\n audience: opts.audience,\n grant_type: opts.grantType,\n permissions: opts.permissions,\n reason: opts.reason,\n request_hash: opts.requestHash,\n duration: opts.duration,\n }),\n })\n\n if (!res.ok) {\n throw new Error(`Grant request failed: ${res.status} ${await res.text()}`)\n }\n\n return res.json() as Promise<OpenApeGrant>\n }\n\n /**\n * Poll a grant until it's approved, denied, or timeout.\n */\n async waitForApproval(\n grantId: string,\n timeoutMs: number = 300_000,\n pollIntervalMs: number = 2_000,\n ): Promise<OpenApeGrant> {\n const deadline = Date.now() + timeoutMs\n\n while (Date.now() < deadline) {\n const res = await fetch(`${this.idpUrl}/api/grants/${grantId}`, {\n headers: this.headers(),\n })\n\n if (!res.ok) {\n throw new Error(`Grant poll failed: ${res.status}`)\n }\n\n const grant = await res.json() as OpenApeGrant\n if (grant.status !== 'pending') {\n return grant\n }\n\n await new Promise(r => setTimeout(r, pollIntervalMs))\n }\n\n throw new Error(`Grant approval timed out after ${timeoutMs}ms`)\n }\n\n /**\n * Check if there's an existing approved grant for a host+audience+permissions combo.\n * Ignores `once` grants (they're single-use).\n */\n async findExistingGrant(\n requester: string,\n targetHost: string,\n audience: string,\n permissions?: string[],\n ): Promise<OpenApeGrant | null> {\n const params = new URLSearchParams({\n requester,\n status: 'approved',\n })\n\n const res = await fetch(`${this.idpUrl}/api/grants?${params}`, {\n headers: this.headers(),\n })\n\n if (!res.ok) return null\n\n const grants = await res.json() as OpenApeGrant[]\n const now = Math.floor(Date.now() / 1000)\n\n return grants.find((g) => {\n if (g.status !== 'approved') return false\n if (g.expires_at && g.expires_at <= now) return false\n if (g.request?.grant_type === 'once') return false\n if (g.request?.target_host !== targetHost) return false\n if (g.request?.audience !== audience) return false\n if (permissions?.length && g.request?.permissions?.length) {\n const grantedPerms = new Set(g.request.permissions)\n if (!permissions.every(p => grantedPerms.has(p))) return false\n }\n return true\n }) ?? null\n }\n}\n","import type { AuditEntry } from './types.js'\n\n/**\n * Format the request target for the stderr summary line. For HTTP forward-proxy\n * the path starts with `/` (so `${domain}${path}` reads `example.com/foo`), but\n * for CONNECT the path field already carries `host:port` and concatenating\n * with `domain` would print `example.comexample.com:443`.\n */\nfunction formatTarget(entry: AuditEntry): string {\n return entry.path.startsWith('/') ? `${entry.domain}${entry.path}` : entry.path\n}\n\n/**\n * Emit one operator-readable audit summary line to stderr. Intentionally NOT a\n * tamper-proof audit trail: anything written on the user's machine is also\n * writable by the user, so there's no integrity story we'd be willing to put\n * in front of a reviewer. The trustworthy audit lives server-side, recorded\n * by the IdP every time it processes a grant request — see the planned\n * per-agent audit route on `id.openape.ai`.\n *\n * Stderr here is purely a debugging convenience for the operator running\n * `apes proxy --` interactively. Persist nothing locally.\n */\nexport function writeAudit(entry: AuditEntry): void {\n const grantSuffix = entry.grant_id ? ` grant=${entry.grant_id}` : ''\n console.error(`[audit] ${entry.action} ${entry.method} ${formatTarget(entry)}${grantSuffix}`)\n}\n","import { resolve4, resolve6 } from 'node:dns/promises'\nimport { isIP } from 'node:net'\n\nconst PRIVATE_RANGES_V4 = [\n { prefix: 0x7F000000, mask: 0xFF000000 }, // 127.0.0.0/8\n { prefix: 0x0A000000, mask: 0xFF000000 }, // 10.0.0.0/8\n { prefix: 0xAC100000, mask: 0xFFF00000 }, // 172.16.0.0/12\n { prefix: 0xC0A80000, mask: 0xFFFF0000 }, // 192.168.0.0/16\n { prefix: 0xA9FE0000, mask: 0xFFFF0000 }, // 169.254.0.0/16\n { prefix: 0x00000000, mask: 0xFF000000 }, // 0.0.0.0/8\n]\n\nfunction ipv4ToNumber(ip: string): number {\n const parts = ip.split('.').map(Number)\n return ((parts[0] << 24) | (parts[1] << 16) | (parts[2] << 8) | parts[3]) >>> 0\n}\n\nfunction isPrivateIPv4(ip: string): boolean {\n const num = ipv4ToNumber(ip)\n return PRIVATE_RANGES_V4.some(r => ((num & r.mask) >>> 0) === r.prefix)\n}\n\nfunction isPrivateIPv6(ip: string): boolean {\n const normalized = ip.toLowerCase()\n\n // Loopback ::1\n if (normalized === '::1') return true\n\n // Unspecified ::\n if (normalized === '::') return true\n\n // Link-local fe80::/10\n if (normalized.startsWith('fe8') || normalized.startsWith('fe9')\n || normalized.startsWith('fea') || normalized.startsWith('feb')) {\n return true\n }\n\n // Unique local fd00::/8\n if (normalized.startsWith('fd')) return true\n\n // IPv4-mapped ::ffff:x.x.x.x\n const v4mapped = normalized.match(/^::ffff:(\\d+\\.\\d+\\.\\d+\\.\\d+)$/)\n if (v4mapped) return isPrivateIPv4(v4mapped[1])\n\n return false\n}\n\nfunction isPrivateIP(ip: string): boolean {\n if (isIP(ip) === 4) return isPrivateIPv4(ip)\n if (isIP(ip) === 6) return isPrivateIPv6(ip)\n return false\n}\n\n/**\n * Result of resolving a hostname for egress safety.\n *\n * - `ok` — resolved to at least one address, none of them private/loopback.\n * Caller forwards.\n * - `private` — at least one resolved address is private/loopback. Caller\n * refuses with a policy response (403).\n * - `unresolvable` — DNS returned NXDOMAIN, NODATA, or a query error. Caller\n * responds with an upstream-failure code (502). Distinct from `private`\n * because the host doesn't exist (or can't be reached) — that's not a\n * policy decision, it's a connectivity problem, and conflating the two\n * ships misleading 403s for typos and DNS hiccups.\n */\nexport type EgressCheckResult =\n | { kind: 'ok' }\n | { kind: 'private' }\n | { kind: 'unresolvable', reason: 'no-records' | 'dns-error' }\n\n/**\n * Check whether forwarding a connection to `hostname` is safe.\n *\n * IP literals are checked directly; hostnames are resolved via DNS (A and\n * AAAA in parallel) and every returned address is screened against the\n * private-range list. If any address is private the result is `private`.\n *\n * Note on DNS-rebinding: a careful attacker could return a public IP to our\n * pre-flight resolution and a private one to the kernel's `connect()`. The\n * proper fix for that is pinning the resolved IP across the actual socket\n * call, not blocking on uncertainty — so we no longer conflate \"I couldn't\n * resolve\" with \"this is private\". Callers can layer pinning on top.\n */\nexport async function checkEgress(hostname: string): Promise<EgressCheckResult> {\n if (isIP(hostname)) {\n return isPrivateIP(hostname) ? { kind: 'private' } : { kind: 'ok' }\n }\n\n if (hostname === 'localhost') return { kind: 'private' }\n\n let settled: PromiseSettledResult<string[]>[]\n try {\n settled = await Promise.allSettled([resolve4(hostname), resolve6(hostname)])\n }\n catch {\n return { kind: 'unresolvable', reason: 'dns-error' }\n }\n\n const addrs: string[] = []\n for (const r of settled) {\n if (r.status === 'fulfilled') addrs.push(...r.value)\n }\n\n if (addrs.length === 0) {\n return { kind: 'unresolvable', reason: 'no-records' }\n }\n\n return addrs.some(addr => isPrivateIP(addr)) ? { kind: 'private' } : { kind: 'ok' }\n}\n\n/**\n * Backwards-compatible boolean shim. Returns true for both `private` and\n * `unresolvable` because that matches the previous (overly conservative)\n * behaviour. New code should call `checkEgress` and distinguish.\n *\n * @deprecated Use `checkEgress` so the caller can return 502 for unresolvable\n * hosts and 403 only for actual private/loopback IPs.\n */\nexport async function isPrivateOrLoopback(hostname: string): Promise<boolean> {\n const result = await checkEgress(hostname)\n return result.kind !== 'ok'\n}\n","import type { IncomingMessage } from 'node:http'\nimport type { Socket } from 'node:net'\nimport { connect } from 'node:net'\nimport type { AgentConfig, MultiAgentProxyConfig, ProxyConfig, SecretsStore } from './types.js'\nimport { AuthError, verifyAgentAuth } from './auth.js'\nimport { checkEgress } from './ssrf.js'\nimport { writeAudit } from './audit.js'\nimport { evaluateRules } from './matcher.js'\nimport type { GrantsClient } from './grants-client.js'\nimport type { LeafCertCache } from './ca-store.js'\nimport { handleMitmConnect } from './mitm-connect.js'\n\n/**\n * Optional MITM injection deps. When BOTH `secretsStore` and `leafCache` are\n * present we terminate TLS in-process (CONNECT-MITM mode), inspect the inner\n * HTTPS request, and inject the matching secret header before forwarding.\n * When either is absent we fall back to the legacy raw TCP tunnel — opaque\n * pipe, no secret injection (cleartext-only inject still happens via the\n * forward-proxy fetch path).\n */\nexport interface ConnectMitmDeps {\n secretsStore?: SecretsStore\n leafCache?: LeafCertCache\n upstreamRejectUnauthorized?: boolean\n /**\n * Daemon mode: skip per-request `Proxy-Authorization` JWT extraction on\n * the CONNECT path. Identity is implicit — `config.agents[0]` is the\n * daemon's loaded identity. Inline (`apes proxy --` ephemeral) mode keeps\n * the existing JWT-required path.\n */\n daemonMode?: boolean\n}\n\n/**\n * Handle HTTP CONNECT requests for tunneling (used by HTTP_PROXY clients).\n * Flow: Auth → SSRF → host-based rule evaluation (allow / deny / grant_required)\n * → TCP connect → bidirectional pipe.\n *\n * For HTTPS via CONNECT we only see the hostname (the TLS payload is opaque).\n * Rules with `methods` or `path` filters cannot be enforced at CONNECT time and\n * are skipped — they'd only match if the client also carried the same hostname\n * over cleartext-HTTP forward-proxy. This is intentional: there's no honest way\n * to gate a method we can't observe.\n */\nexport async function handleConnect(\n config: MultiAgentProxyConfig,\n grantsClients: Map<string, GrantsClient>,\n req: IncomingMessage,\n clientSocket: Socket,\n head: Buffer,\n deps?: ConnectMitmDeps,\n): Promise<void> {\n const target = req.url ?? ''\n const [host, portStr] = target.split(':')\n const port = Number.parseInt(portStr || '443')\n\n if (!host || !port) {\n clientSocket.write('HTTP/1.1 400 Bad Request\\r\\n\\r\\n')\n clientSocket.destroy()\n return\n }\n\n // SYSTEM BYPASS: outbound to any configured IdP host is unconditionally\n // allowed. The proxy itself talks to the IdP for JWT verification + grant\n // approval, and any process inside the proxy boundary may need to call the\n // IdP for `apes login` / `apes whoami` / token-exchange BEFORE it can ever\n // produce a valid Proxy-Authorization JWT. Blocking IdP traffic would\n // either deadlock the grant flow or lock users out of authentication.\n // We therefore skip auth (no JWT yet for first-login), SSRF (local-dev IdPs\n // can live at 127.0.0.1), and policy rules (operator must not be able to\n // override this system invariant).\n const idpHosts = new Set(\n config.agents\n .map((a) => {\n try {\n return new URL(a.idp_url).hostname.toLowerCase()\n }\n catch {\n return ''\n }\n })\n .filter(h => h.length > 0),\n )\n if (idpHosts.has(host.toLowerCase())) {\n writeAudit({\n ts: new Date().toISOString(),\n agent: 'system',\n action: 'allow',\n domain: host,\n method: 'CONNECT',\n path: target,\n rule: 'idp-system-bypass',\n })\n tunnel(host, port, clientSocket)\n return\n }\n\n const mandatoryAuth = config.proxy.mandatory_auth ?? false\n const daemonMode = deps?.daemonMode ?? false\n\n // Auth check — CONNECT always requires auth in mandatory mode\n let agentEmail: string | undefined\n if (daemonMode) {\n // Identity is implicit — the daemon was started by/for one specific\n // agent. The first (and only) agent in config.agents is the daemon's\n // identity, loaded from `~/.config/apes/auth.json` at startup.\n agentEmail = config.agents[0]?.email\n }\n else {\n try {\n const authHeader = req.headers['proxy-authorization'] as string | undefined\n let identity: { email: string, act: 'agent' } | null = null\n\n for (const agentConf of config.agents) {\n identity = await verifyAgentAuth(\n authHeader ?? null,\n agentConf.idp_url,\n mandatoryAuth && config.agents.length === 1,\n )\n if (identity) break\n }\n\n if (mandatoryAuth && !identity) {\n throw new AuthError('JWT required')\n }\n\n agentEmail = identity?.email\n\n // Verify agent is known\n if (agentEmail) {\n const known = config.agents.find(a => a.email === agentEmail)\n if (!known) {\n clientSocket.write('HTTP/1.1 403 Forbidden\\r\\n\\r\\n')\n clientSocket.destroy()\n return\n }\n }\n else if (config.agents.length > 1) {\n throw new AuthError('JWT required for multi-agent proxy')\n }\n }\n catch (err) {\n if (err instanceof AuthError) {\n clientSocket.write('HTTP/1.1 401 Unauthorized\\r\\n\\r\\n')\n clientSocket.destroy()\n return\n }\n throw err\n }\n }\n\n // SSRF / reachability check. We split the two outcomes:\n // - `private` → policy refusal, 403 (the proxy will not forward).\n // - `unresolvable` → upstream not reachable, 502 (DNS NXDOMAIN /\n // NODATA / query error — distinct from \"I refuse on policy grounds\";\n // conflating them shipped misleading 403s for typos like\n // `apes proxy -- curl https://example.at`).\n const egress = await checkEgress(host)\n const auditAgent = agentEmail ?? config.agents[0]?.email ?? 'unknown'\n if (egress.kind === 'private') {\n writeAudit({\n ts: new Date().toISOString(),\n agent: auditAgent,\n action: 'deny',\n domain: host,\n method: 'CONNECT',\n path: target,\n rule: 'ssrf-blocked',\n })\n clientSocket.write('HTTP/1.1 403 Forbidden\\r\\n\\r\\n')\n clientSocket.destroy()\n return\n }\n if (egress.kind === 'unresolvable') {\n writeAudit({\n ts: new Date().toISOString(),\n agent: auditAgent,\n action: 'error',\n domain: host,\n method: 'CONNECT',\n path: target,\n rule: `dns-unresolvable (${egress.reason})`,\n })\n clientSocket.write(\n `HTTP/1.1 502 Bad Gateway\\r\\nContent-Type: text/plain\\r\\n\\r\\nDNS lookup failed for ${host} (${egress.reason}).\\r\\n`,\n )\n clientSocket.destroy()\n return\n }\n\n // Host-based rule evaluation. Pick the agent's config: in single-agent or\n // unauth mode this is just config.agents[0]; in multi-agent mode we use the\n // identity established above.\n const agentConf: AgentConfig | undefined = agentEmail\n ? config.agents.find(a => a.email === agentEmail)\n : config.agents[0]\n if (!agentConf) {\n clientSocket.write('HTTP/1.1 403 Forbidden\\r\\n\\r\\n')\n clientSocket.destroy()\n return\n }\n const effectiveEmail = agentEmail ?? agentConf.email\n\n const rulesConfig: ProxyConfig = {\n proxy: {\n listen: config.proxy.listen,\n idp_url: agentConf.idp_url,\n agent_email: agentConf.email,\n default_action: config.proxy.default_action,\n },\n allow: agentConf.allow ?? [],\n deny: agentConf.deny ?? [],\n grant_required: agentConf.grant_required ?? [],\n }\n\n // CONNECT carries no method/path beyond host:port. Pass 'CONNECT' as method\n // and '/' as path so rules with method-filters (which we can't enforce here)\n // simply don't match — operator must specify method-less rules to gate\n // HTTPS hosts.\n const action = evaluateRules(rulesConfig, host, 'CONNECT', '/')\n const baseAudit = {\n ts: new Date().toISOString(),\n agent: effectiveEmail,\n domain: host,\n method: 'CONNECT',\n path: target,\n } as const\n\n if (action.type === 'deny') {\n writeAudit({ ...baseAudit, action: 'deny', rule: 'deny-list' })\n const note = action.note ? ` ${action.note}` : ''\n clientSocket.write(`HTTP/1.1 403 Forbidden\\r\\nContent-Type: text/plain\\r\\n\\r\\nBlocked:${note}\\r\\n`)\n clientSocket.destroy()\n return\n }\n\n if (action.type === 'grant_required') {\n const grantsClient = grantsClients.get(agentConf.email)\n if (!grantsClient) {\n writeAudit({ ...baseAudit, action: 'deny', rule: 'grant_required (no client)' })\n clientSocket.write('HTTP/1.1 500 Internal Server Error\\r\\n\\r\\nNo grants client for agent\\r\\n')\n clientSocket.destroy()\n return\n }\n\n // Async-mode (`request-async`) is meaningful for HTTP forward-proxy where\n // the client can re-issue the request after approval. CONNECT clients\n // (curl, gh, …) won't transparently retry on 407 mid-handshake, so we\n // always block the tunnel until the grant resolves. Operator can shorten\n // the wait via per-rule `duration` or via the IdP's grant TTL.\n const startTs = Date.now()\n try {\n const grant = await grantsClient.requestGrant({\n requester: effectiveEmail,\n targetHost: host,\n audience: 'ape-proxy',\n grantType: action.rule.grant_type,\n permissions: action.rule.permissions,\n reason: `CONNECT ${host}:${port}`,\n duration: action.rule.duration,\n })\n const decided = await grantsClient.waitForApproval(grant.id)\n const waitedMs = Date.now() - startTs\n if (decided.status === 'approved') {\n writeAudit({ ...baseAudit, action: 'grant_approved', rule: 'grant_required', grant_id: decided.id, waited_ms: waitedMs })\n // Fall through to the TCP connect below.\n }\n else {\n writeAudit({ ...baseAudit, action: 'grant_denied', rule: 'grant_required', grant_id: decided.id, waited_ms: waitedMs })\n clientSocket.write(`HTTP/1.1 403 Forbidden\\r\\n\\r\\nGrant denied by ${decided.decided_by ?? 'policy'}\\r\\n`)\n clientSocket.destroy()\n return\n }\n }\n catch (err) {\n const msg = err instanceof Error ? err.message : 'Unknown error'\n writeAudit({ ...baseAudit, action: 'grant_timeout', rule: 'grant_required', error: msg })\n clientSocket.write(`HTTP/1.1 504 Gateway Timeout\\r\\n\\r\\nGrant request failed: ${msg}\\r\\n`)\n clientSocket.destroy()\n return\n }\n }\n else {\n // 'allow' — log and continue.\n writeAudit({ ...baseAudit, action: 'allow', rule: 'allow-list' })\n }\n\n // CONNECT-MITM mode: when both a secrets store and a leaf-cert cache are\n // wired in, terminate TLS in-process so we can inspect the inner request\n // and inject the matching secret header before forwarding upstream. The\n // 200-Established line is OUR responsibility here — `handleMitmConnect`\n // assumes the client is already in TLS-handshake mode on the socket.\n // Without both deps we keep the legacy opaque TCP pipe (inline mode); the\n // IdP system-bypass branch above also stays on the legacy path so we never\n // MITM the proxy's own auth traffic.\n if (deps?.secretsStore && deps?.leafCache) {\n // Pause to avoid data races between our 200-write and TLSSocket wrap. The\n // TLSSocket constructor in `handleMitmConnect` calls `resume()` once it has\n // attached its read pipeline.\n clientSocket.pause()\n clientSocket.write('HTTP/1.1 200 Connection Established\\r\\n\\r\\n')\n // Push any bytes the http parser already captured after the CONNECT headers\n // back into the socket so the TLSSocket sees them as the start of the\n // ClientHello. Rare for CONNECT but possible if the client pipelines.\n if (head.length > 0) {\n clientSocket.unshift(head)\n }\n const store = deps.secretsStore\n handleMitmConnect({\n clientSocket,\n host,\n port,\n leafCache: deps.leafCache,\n upstreamRejectUnauthorized: deps.upstreamRejectUnauthorized,\n onRequest: (mreq) => {\n // `mreq.host` is the SNI hostname only — `port` is carried separately\n // because handleMitmConnect splits CONNECT host:port at the boundary.\n // Reattach a non-default port so secret target globs like\n // `host:port/*` can match (the matcher's `targetString` keeps explicit\n // non-default ports verbatim).\n const portSuffix = port === 443 ? '' : `:${port}`\n const targetUrl = new URL(`https://${mreq.host}${portSuffix}${mreq.path}`)\n const match = store.findFor(targetUrl)\n if (!match) return { type: 'forward', mutatedHeaders: new Map() }\n const rendered = match.template.replace(/\\$\\{value\\}/g, match.value)\n // Redacted log line — only name/header/method/url, never the secret.\n console.error(\n `[openape-proxy] injected secret '${match.name}' into ${match.header} for ${mreq.method} ${targetUrl.href}`,\n )\n return {\n type: 'forward',\n mutatedHeaders: new Map([[match.header.toLowerCase(), rendered]]),\n }\n },\n })\n return\n }\n\n tunnel(host, port, clientSocket)\n}\n\n/**\n * Open a TCP socket to host:port and bidirectionally pipe it with the client\n * socket. Used by both the policy-pass path (auth + rules approved) and the\n * IdP system-bypass path (no auth/policy involved).\n */\nfunction tunnel(host: string, port: number, clientSocket: Socket): void {\n const targetSocket = connect(port, host, () => {\n clientSocket.write('HTTP/1.1 200 Connection Established\\r\\n\\r\\n')\n targetSocket.pipe(clientSocket)\n clientSocket.pipe(targetSocket)\n })\n\n targetSocket.on('error', () => {\n clientSocket.write('HTTP/1.1 502 Bad Gateway\\r\\n\\r\\n')\n clientSocket.destroy()\n })\n\n clientSocket.on('error', () => {\n targetSocket.destroy()\n })\n\n clientSocket.on('close', () => targetSocket.destroy())\n targetSocket.on('close', () => clientSocket.destroy())\n}\n","import { TLSSocket, createSecureContext, connect as tlsConnect } from 'node:tls'\nimport type { Socket } from 'node:net'\nimport type { LeafCertCache } from './ca-store.js'\n\nexport interface MitmRequest {\n method: string\n host: string\n path: string\n headers: Map<string, string>\n body: Buffer | null\n}\n\nexport type MitmDecision =\n | { type: 'short-circuit', status: number, body: string }\n | { type: 'forward', mutatedHeaders: Map<string, string> }\n\nexport interface MitmConnectOpts {\n clientSocket: Socket\n host: string\n port: number\n leafCache: LeafCertCache\n onRequest: (req: MitmRequest) => MitmDecision\n upstreamRejectUnauthorized?: boolean\n}\n\nexport function handleMitmConnect(opts: MitmConnectOpts): void {\n const leaf = opts.leafCache.get(opts.host)\n const ctx = createSecureContext({ cert: leaf.certPem, key: leaf.keyPem })\n // The CONNECT 200 response is the caller's responsibility (see `connect.ts`).\n // By the time we get here the client is already in TLS-handshake mode on the\n // socket, so we must not inject any plaintext bytes before TLS termination.\n const tls = new TLSSocket(opts.clientSocket, { isServer: true, secureContext: ctx })\n // Resume the underlying socket now that TLSSocket is attached so any paused\n // data starts flowing into the TLS layer. Safe even if the socket wasn't\n // paused — `resume()` is a no-op in that case.\n opts.clientSocket.resume()\n\n let buffer = Buffer.alloc(0)\n let dispatched = false\n const onData = (chunk: Buffer) => {\n if (dispatched) return // short-circuit path already wrote the response and ended the socket\n buffer = Buffer.concat([buffer, chunk])\n const headerEnd = buffer.indexOf('\\r\\n\\r\\n')\n if (headerEnd < 0) return // incomplete\n\n const headerBlock = buffer.subarray(0, headerEnd).toString('utf-8')\n const lines = headerBlock.split('\\r\\n')\n const requestLine = lines[0] ?? ''\n const [method, path] = requestLine.split(' ')\n const headers = new Map<string, string>()\n for (const line of lines.slice(1)) {\n const i = line.indexOf(':')\n if (i > 0) headers.set(line.slice(0, i).trim().toLowerCase(), line.slice(i + 1).trim())\n }\n\n const decision = opts.onRequest({\n method: method ?? '',\n host: opts.host,\n path: path ?? '/',\n headers,\n // inject decisions use only target+headers; raw body bytes still flow through to upstream below\n body: null,\n })\n\n if (decision.type === 'short-circuit') {\n dispatched = true\n const body = Buffer.from(decision.body, 'utf-8')\n tls.write(`HTTP/1.1 ${decision.status} OK\\r\\nContent-Length: ${body.length}\\r\\n\\r\\n`)\n tls.write(body)\n tls.end()\n return\n }\n\n // forward path: open upstream TLS, write the mutated request + any body bytes already buffered,\n // then pipe upstream ↔ client. Subsequent client bytes (request bodies for streaming uploads,\n // pipelined requests) are forwarded raw via tls.pipe(upstream).\n //\n // Race guard: the upstream TLS handshake is async; while we wait for\n // `secureConnect`, the client may send additional body chunks (think\n // `curl --data-binary @big.bin`). Without pausing, those chunks would\n // fire this same `data` listener again and be dropped. Pausing here\n // keeps the chunks buffered inside the TLSSocket; we resume them only\n // after `tls.pipe(upstream)` is wired *and* this listener is detached,\n // so they flow straight through the pipe instead of re-entering this\n // handler.\n dispatched = true\n tls.pause()\n const upstream = tlsConnect({\n host: opts.host,\n port: opts.port,\n servername: opts.host,\n rejectUnauthorized: opts.upstreamRejectUnauthorized ?? true,\n })\n upstream.once('secureConnect', () => {\n // Re-serialize the request: keep all original header lines except the ones being mutated;\n // append mutated headers at the end with canonical capitalization.\n const mutated = decision.mutatedHeaders\n const newHeaderLines: string[] = []\n for (const line of lines.slice(1)) {\n const i = line.indexOf(':')\n if (i <= 0) continue\n const lower = line.slice(0, i).trim().toLowerCase()\n if (mutated.has(lower)) continue // we'll write the mutated version below\n newHeaderLines.push(line)\n }\n for (const [name, value] of mutated.entries()) {\n newHeaderLines.push(`${capitalizeHeader(name)}: ${value}`)\n }\n const fullRequest = [requestLine, ...newHeaderLines, '', ''].join('\\r\\n')\n upstream.write(fullRequest)\n // body bytes that arrived in the same chunk as the header block:\n const remainder = buffer.subarray(headerEnd + 4)\n if (remainder.length > 0) upstream.write(remainder)\n upstream.pipe(tls)\n // detach this handler before resuming so paused chunks land in the\n // pipe rather than firing onData again (which would early-return on\n // `dispatched` and drop them).\n tls.removeListener('data', onData)\n // any further client-side bytes (streaming uploads / pipelined requests) flow upstream raw\n tls.pipe(upstream)\n tls.resume()\n })\n upstream.on('error', () => tls.end())\n }\n tls.on('data', onData)\n tls.on('error', () => opts.clientSocket.destroy())\n}\n\nfunction capitalizeHeader(name: string): string {\n return name\n .split('-')\n .map(p => (p[0]?.toUpperCase() ?? '') + p.slice(1))\n .join('-')\n}\n","import { parse as parseTOML } from 'smol-toml'\nimport type { SecretEntry, SecretsStore } from './types.js'\nimport { matchSecret } from './secrets-match.js'\n\nconst MAX_BLOB_BYTES = 4 * 1024\nconst SUPPORTED_VERSION = '1'\n\nexport function parseSecretsBlob(toml: string): SecretsStore {\n if (Buffer.byteLength(toml, 'utf-8') > MAX_BLOB_BYTES) {\n throw new Error(`Secrets blob too large (max ${MAX_BLOB_BYTES} bytes / 4 KiB)`)\n }\n\n const parsed = parseTOML(toml) as Record<string, unknown>\n const version = parsed.version\n if (version !== SUPPORTED_VERSION) {\n throw new Error(`Unsupported or missing version (expected \"${SUPPORTED_VERSION}\", got ${JSON.stringify(version)})`)\n }\n\n const secretsBlock = parsed.secrets as Record<string, Partial<SecretEntry>> | undefined\n const entries: SecretEntry[] = []\n if (secretsBlock) {\n for (const [name, raw] of Object.entries(secretsBlock)) {\n const required: (keyof SecretEntry)[] = ['target', 'header', 'template', 'value']\n for (const field of required) {\n if (!raw[field] || typeof raw[field] !== 'string') {\n throw new Error(`Secret '${name}' is missing required field '${field}'`)\n }\n }\n entries.push({\n name,\n target: raw.target!,\n header: raw.header!,\n template: raw.template!,\n value: raw.value!,\n })\n }\n }\n\n // Reject duplicate-specificity targets.\n const literalPrefix = (glob: string): string => {\n const i = glob.indexOf('*')\n return i < 0 ? glob : glob.slice(0, i)\n }\n const seen = new Map<string, string>()\n for (const e of entries) {\n const key = `${literalPrefix(e.target)}|${e.target}`\n if (seen.has(key)) {\n throw new Error(`Duplicate target with identical specificity: '${e.target}' (entries '${seen.get(key)}' and '${e.name}')`)\n }\n seen.set(key, e.name)\n }\n\n return {\n entries,\n findFor: target => matchSecret(target, entries),\n }\n}\n","import type { SecretEntry } from './types.js'\n\n/** Compile a glob pattern to a RegExp. Supports `*` (any segment) only. */\nfunction compileGlob(glob: string): RegExp {\n const escaped = glob.replace(/[.+^${}()|[\\]\\\\]/g, '\\\\$&').replace(/\\*/g, '.*')\n return new RegExp(`^${escaped}$`)\n}\n\nfunction literalPrefixLen(glob: string): number {\n const i = glob.indexOf('*')\n return i < 0 ? glob.length : i\n}\n\nfunction targetString(url: URL): string {\n // host[:port]/path — port included only when explicit and non-default.\n const port = url.port ? `:${url.port}` : ''\n const path = url.pathname === '/' ? '' : url.pathname\n return `${url.hostname}${port}${path}`\n}\n\nexport function matchSecret(target: URL, entries: readonly SecretEntry[]): SecretEntry | null {\n const targetStr = targetString(target)\n let best: { entry: SecretEntry, prefix: number, idx: number } | null = null\n\n for (let i = 0; i < entries.length; i++) {\n const entry = entries[i]!\n const re = compileGlob(entry.target)\n if (!re.test(targetStr) && !re.test(`${targetStr}/`)) continue\n const prefix = literalPrefixLen(entry.target)\n if (!best || prefix > best.prefix) {\n best = { entry, prefix, idx: i }\n }\n }\n return best?.entry ?? null\n}\n"],"mappings":"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","names":["import_node_fs","import_node_path","forge","import_node_fs","parseTOML","import_node_net","tlsConnect","agentConf","agentConf","import_smol_toml","parseTOML"]}
|