axios+telemetry cleanup

This commit is contained in:
2026-04-02 15:19:11 +03:00
parent a3cbca1e11
commit 7e1eac8002
100 changed files with 3048 additions and 4491 deletions

View File

@@ -1,8 +1,8 @@
import axios from 'axios'
import type { HookEvent } from 'src/entrypoints/agentSdkTypes.js'
import { Agent } from 'undici'
import { createCombinedAbortSignal } from '../combinedAbortSignal.js'
import { logForDebugging } from '../debug.js'
import { errorMessage } from '../errors.js'
import { nativeRequest } from '../http.js'
import { getProxyUrl, shouldBypassProxy } from '../proxy.js'
// Import as namespace so spyOn works in tests (direct imports bypass spies)
import * as settingsModule from '../settings/settings.js'
@@ -122,7 +122,7 @@ function interpolateEnvVars(
*/
export async function execHttpHook(
hook: HttpHook,
_hookEvent: HookEvent,
_hookEvent: string,
jsonInput: string,
signal?: AbortSignal,
): Promise<{
@@ -186,34 +186,39 @@ export async function execHttpHook(
getProxyUrl() !== undefined &&
!shouldBypassProxy(hook.url)
let dispatcher: Agent | undefined
if (sandboxProxy) {
logForDebugging(
`Hooks: HTTP hook POST to ${hook.url} (via sandbox proxy :${sandboxProxy.port})`,
)
// For sandbox proxy, we'd ideally use a custom dispatcher, but for now
// assume global dispatcher or handled separately.
// Axios implementation used `proxy: sandboxProxy`.
} else if (envProxyActive) {
logForDebugging(
`Hooks: HTTP hook POST to ${hook.url} (via env-var proxy)`,
)
} else {
logForDebugging(`Hooks: HTTP hook POST to ${hook.url}`)
}
const response = await axios.post<string>(hook.url, jsonInput, {
headers,
signal: combinedSignal,
responseType: 'text',
validateStatus: () => true,
maxRedirects: 0,
// Explicit false prevents axios's own env-var proxy detection; when an
// env-var proxy is configured, the global axios interceptor installed
// by configureGlobalAgents() handles it via httpsAgent instead.
proxy: sandboxProxy ?? false,
// SSRF guard: validate resolved IPs, block private/link-local ranges
// (but allow loopback for local dev). Skipped when any proxy is in
// use — the proxy performs DNS for the target, and applying the
// guard would instead validate the proxy's own IP, breaking
// connections to corporate proxies on private networks.
lookup: sandboxProxy || envProxyActive ? undefined : ssrfGuardedLookup,
dispatcher = new Agent({
connect: {
lookup: ssrfGuardedLookup as any,
},
})
}
const response = await nativeRequest<string>(hook.url, {
method: 'POST',
headers,
body: jsonInput,
signal: combinedSignal,
responseType: 'text',
dispatcher,
})
cleanup()
@@ -224,7 +229,7 @@ export async function execHttpHook(
)
return {
ok: response.status >= 200 && response.status < 300,
ok: true,
statusCode: response.status,
body,
}

View File

@@ -1,7 +1,12 @@
import type { AddressFamily, LookupAddress as AxiosLookupAddress } from 'axios'
import { lookup as dnsLookup } from 'dns'
import { isIP } from 'net'
export type AddressFamily = 4 | 6
export type LookupAddress = {
address: string
family: AddressFamily
}
/**
* SSRF guard for HTTP hooks.
*
@@ -210,16 +215,14 @@ function extractMappedIPv4(addr: string): string | null {
* rebinding window between validation and connection.
*
* IP literals in the hostname are validated directly without DNS.
*
* Signature matches axios's `lookup` config option (not Node's dns.lookup).
*/
export function ssrfGuardedLookup(
hostname: string,
options: object,
options: any,
callback: (
err: Error | null,
address: AxiosLookupAddress | AxiosLookupAddress[],
family?: AddressFamily,
address: any,
family?: number,
) => void,
): void {
const wantsAll = 'all' in options && options.all === true