CodeSampleX

Exemplo

undici 8.10.0: Outbound requests ignore HTTP_PROXY unless EnvHttpProxyAgent is explicitly configured, where lowercase env var spelling wins and HTTP proxy acts as fallback for HTTPS but not vice versa

Amostra verificada para npm undici 8.10.0: Outbound requests ignore HTTP_PROXY unless EnvHttpProxyAgent is explicitly configured, where lowercase env var…

sha256:c5f7d661b349b93b5d644271e2edd085fbcb52460bdd7105e5fec9f64148e1de

Esta rede oferece uma coisa: uma amostra que compila. Ela a executou em um sandbox e guardou o recibo assinado. Não classifica nem garante nada — se o mesmo código compila onde você está, ela não mediu. Quantas chaves de assinatura distintas enviaram um recibo de contrato aprovado. Uma é só o autor; mais de uma significa que outra pessoa também o compilou. Uma chave é gerada por conta própria e não tem identidade registrada por trás, então conta chaves, não pessoas. MIT-0

Evidência de execução

O ambiente declarado e as execuções assinadas ficam separados, para você ver exatamente o que esta amostra executou e onde.

Base da evidência
Contrato assinado aprovado
Recibos de verificação
3
Chaves de assinatura que o compilaram
3
Ambiente declarado node linux x64 node node npm

Ambientes das execuções de verificação

Ambiente Contrato Etapas Execução
node 22 · linux alpine/x64 · docker ed25519:ac1544ece1e22594 PASS compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS
CONTAINER_RUN · node-typescript@1
2026-08-14
node 22 · linux alpine/x64 · docker ed25519:d91480838ac982c9 PASS compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS
CONTAINER_RUN · node-typescript@1
2026-08-18
node 22 · linux alpine/x64 · docker ed25519:c1973797be207ac4 PASS compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS
CONTAINER_RUN · node-typescript@1node:22-alpine@sha256:c610fcdfb1d5…
2026-09-07

Caso

HOW
Objetivo
Outbound requests ignore HTTP_PROXY unless EnvHttpProxyAgent is explicitly configured, where lowercase env var spelling wins and HTTP proxy acts as fallback for HTTPS but not vice versa
Pacotes
Símbolos
  • undici.EnvHttpProxyAgent
  • undici.ProxyAgent
  • undici.Agent
  • undici.getGlobalDispatcher
  • undici.setGlobalDispatcher
Ambiente
node
Criado
2026-08-14T22:45:01Z

Contrato

  1. assert getGlobalDispatcher() defaults to an unproxied Agent that ignores proxy environment variables
  2. assert ProxyAgent throws without an explicit uri whereas EnvHttpProxyAgent loads from environment
  3. assert lowercase http_proxy takes precedence over uppercase HTTP_PROXY
  4. assert omitted https_proxy falls back to http_proxy but omitted http_proxy leaves HTTP direct
  5. assert no_proxy rules bypass proxy dispatching for matched hostnames, subdomains, and ports
  6. assert process.env.no_proxy is evaluated dynamically on each dispatch while proxy URIs are bound at construction
  7. assert setGlobalDispatcher(new EnvHttpProxyAgent()) activates environment-driven proxying

Arquivos

  • NOTES.md
  • csx.json
  • package-lock.json
  • package.json
  • test/contract.mjs

Baixar o artefato de código-fonte (tar.gz)

Código-fonte

NOTES.md
A model would expect default undici requests to automatically honour `process.env.HTTP_PROXY` or instantiate `new ProxyAgent()` with no arguments to pick up proxy settings.
The wrong version fails silently with a green build by bypassing the proxy and making direct unproxied outbound requests, or fails loudly with `InvalidArgumentError: Proxy uri is mandatory` if using `new ProxyAgent()`.
csx.json
{"case":{"caseId":"case:sha256:99c5c121016a6b47e1b4d36c7080f1581322e13cd5ba731a4de0a63790c653e3","constraints":{"runtime":"node"},"contract":["assert getGlobalDispatcher() defaults to an unproxied Agent that ignores proxy environment variables","assert ProxyAgent throws without an explicit uri whereas EnvHttpProxyAgent loads from environment","assert lowercase http_proxy takes precedence over uppercase HTTP_PROXY","assert omitted https_proxy falls back to http_proxy but omitted http_proxy leaves HTTP direct","assert no_proxy rules bypass proxy dispatching for matched hostnames, subdomains, and ports","assert process.env.no_proxy is evaluated dynamically on each dispatch while proxy URIs are bound at construction","assert setGlobalDispatcher(new EnvHttpProxyAgent()) activates environment-driven proxying"],"goal":"Outbound requests ignore HTTP_PROXY unless EnvHttpProxyAgent is explicitly configured, where lowercase env var spelling wins and HTTP proxy acts as fallback for HTTPS but not vice versa","kind":"HOW","packages":["pkg:npm/undici@8.10.0"],"schemaVersion":1,"symbols":["undici.EnvHttpProxyAgent","undici.ProxyAgent","undici.Agent","undici.getGlobalDispatcher","undici.setGlobalDispatcher"]},"contractCommand":["node","test/contract.mjs"],"environment":{"arch":"x64","ecosystem":"npm","executionContext":"node","language":"node","os":"linux","packageManager":"npm","runtime":"node","schemaVersion":1},"license":"MIT-0","packages":["pkg:npm/undici@8.10.0"],"schemaVersion":1,"symbols":["undici.EnvHttpProxyAgent","undici.ProxyAgent","undici.Agent","undici.getGlobalDispatcher","undici.setGlobalDispatcher"],"verifierAdapter":"node-typescript@1"}
package-lock.json
{
  "name": "npm-undici-proxy",
  "version": "1.0.0",
  "lockfileVersion": 3,
  "requires": true,
  "packages": {
    "": {
      "name": "npm-undici-proxy",
      "version": "1.0.0",
      "dependencies": {
        "undici": "8.10.0"
      }
    },
    "node_modules/undici": {
      "version": "8.10.0",
      "resolved": "https://registry.npmjs.org/undici/-/undici-8.10.0.tgz",
      "integrity": "sha512-HvltHd7avK13QIw/oLe4qoOLyoVSoafqJ2jYOrtMRBkbYT31eiBQ8O0ehRKZiEZCMEyLFQNIADpgCWC5fALvYQ==",
      "license": "MIT",
      "engines": {
        "node": ">=22.19.0"
      }
    }
  }
}
package.json
{
  "name": "npm-undici-proxy",
  "version": "1.0.0",
  "type": "module",
  "dependencies": {
    "undici": "8.10.0"
  }
}
test/contract.mjs
import assert from 'node:assert/strict';
import { Agent, ProxyAgent, EnvHttpProxyAgent, getGlobalDispatcher, setGlobalDispatcher } from 'undici';

// Helper to extract internal dispatcher symbols from EnvHttpProxyAgent
const sampleSymbols = Object.getOwnPropertySymbols(new EnvHttpProxyAgent());
const kNoProxyAgent = sampleSymbols.find((s) => s.description === 'no proxy agent');
const kHttpProxyAgent = sampleSymbols.find((s) => s.description === 'http proxy agent');
const kHttpsProxyAgent = sampleSymbols.find((s) => s.description === 'https proxy agent');

assert.ok(kNoProxyAgent && kHttpProxyAgent && kHttpsProxyAgent, 'Dispatcher symbols must exist');

function getProxyUri(proxyAgentInstance) {
  if (!proxyAgentInstance) return null;
  const syms = Object.getOwnPropertySymbols(proxyAgentInstance);
  const kProxy = syms.find((s) => s.description === 'proxy agent options');
  return kProxy ? proxyAgentInstance[kProxy]?.uri : null;
}

// Trap 1: Default global dispatcher does NOT honour proxy environment variables
const defaultDispatcher = getGlobalDispatcher();
assert.equal(defaultDispatcher.constructor.name, 'Agent', 'Default dispatcher must be direct Agent');
assert.notEqual(defaultDispatcher.constructor.name, 'EnvHttpProxyAgent', 'Default dispatcher is not EnvHttpProxyAgent');

// Trap 2: ProxyAgent vs EnvHttpProxyAgent constructor contract
assert.throws(() => new ProxyAgent(), { message: 'Proxy uri is mandatory' });
assert.doesNotThrow(() => new EnvHttpProxyAgent());

// Trap 3: Option precedence over environment variables
{
  const agent = new EnvHttpProxyAgent({
    httpProxy: 'http://opt-proxy.example:8080',
    httpsProxy: 'http://opt-https-proxy.example:8443'
  });
  assert.equal(getProxyUri(agent[kHttpProxyAgent]), 'http://opt-proxy.example:8080/');
  assert.equal(getProxyUri(agent[kHttpsProxyAgent]), 'http://opt-https-proxy.example:8443/');
}

// Trap 4: Asymmetric proxy fallback for HTTP vs HTTPS
{
  // Only HTTP proxy configured -> HTTPS falls back to HTTP proxy
  const httpOnly = new EnvHttpProxyAgent({ httpProxy: 'http://http-proxy.example:8080' });
  assert.notEqual(httpOnly[kHttpProxyAgent], httpOnly[kNoProxyAgent]);
  assert.equal(httpOnly[kHttpsProxyAgent], httpOnly[kHttpProxyAgent]);

  // Only HTTPS proxy configured -> HTTP stays unproxied direct Agent
  const httpsOnly = new EnvHttpProxyAgent({ httpsProxy: 'http://https-proxy.example:8443' });
  assert.equal(httpsOnly[kHttpProxyAgent], httpsOnly[kNoProxyAgent]);
  assert.notEqual(httpsOnly[kHttpsProxyAgent], httpsOnly[kNoProxyAgent]);
}

// Trap 5: Dispatch routing and NO_PROXY matching rules (no network)
{
  const agent = new EnvHttpProxyAgent({
    httpProxy: 'http://http-gw.example:8080',
    httpsProxy: 'http://https-gw.example:8443',
    noProxy: 'bypass.example,*.sub.example,host-with-port.example:9000'
  });

  let target = null;
  agent[kNoProxyAgent].dispatch = () => { target = 'direct'; return true; };
  agent[kHttpProxyAgent].dispatch = () => { target = 'http-proxy'; return true; };
  agent[kHttpsProxyAgent].dispatch = () => { target = 'https-proxy'; return true; };

  agent.dispatch({ origin: 'http://api.example.com', path: '/' }, {});
  assert.equal(target, 'http-proxy', 'HTTP requests route to http proxy');

  agent.dispatch({ origin: 'https://api.example.com', path: '/' }, {});
  assert.equal(target, 'https-proxy', 'HTTPS requests route to https proxy');

  agent.dispatch({ origin: 'http://bypass.example', path: '/' }, {});
  assert.equal(target, 'direct', 'NO_PROXY exact host routes direct');

  agent.dispatch({ origin: 'https://service.sub.example', path: '/' }, {});
  assert.equal(target, 'direct', 'NO_PROXY wildcard domain routes direct');

  agent.dispatch({ origin: 'http://host-with-port.example:9000', path: '/' }, {});
  assert.equal(target, 'direct', 'NO_PROXY matching port routes direct');

  agent.dispatch({ origin: 'http://host-with-port.example:8080', path: '/' }, {});
  assert.equal(target, 'http-proxy', 'NO_PROXY mismatched port routes to proxy');
}

// Trap 6: Dynamic NO_PROXY evaluation vs Static HTTP_PROXY binding
{
  const origHttp = process.env.http_proxy;
  const origNo = process.env.no_proxy;
  const origHttpUpper = process.env.HTTP_PROXY;
  const origNoUpper = process.env.NO_PROXY;

  try {
    delete process.env.no_proxy;
    delete process.env.NO_PROXY;
    process.env.http_proxy = 'http://init-proxy.example:8080';

    const dynAgent = new EnvHttpProxyAgent();
    let target = null;
    dynAgent[kNoProxyAgent].dispatch = () => { target = 'direct'; return true; };
    dynAgent[kHttpProxyAgent].dispatch = () => { target = 'proxy'; return true; };

    dynAgent.dispatch({ origin: 'http://dyn.example', path: '/' }, {});
    assert.equal(target, 'proxy', 'Initial dispatch uses proxy');

    process.env.no_proxy = 'dyn.example';
    dynAgent.dispatch({ origin: 'http://dyn.example', path: '/' }, {});
    assert.equal(target, 'direct', 'Mutating process.env.no_proxy updates dispatch dynamically');

    process.env.http_proxy = 'http://changed-proxy.example:9999';
    assert.equal(getProxyUri(dynAgent[kHttpProxyAgent]), 'http://init-proxy.example:8080/');
  } finally {
    if (origHttp !== undefined) process.env.http_proxy = origHttp; else delete process.env.http_proxy;
    if (origNo !== undefined) process.env.no_proxy = origNo; else delete process.env.no_proxy;
    if (origHttpUpper !== undefined) process.env.HTTP_PROXY = origHttpUpper; else delete process.env.HTTP_PROXY;
    if (origNoUpper !== undefined) process.env.NO_PROXY = origNoUpper; else delete process.env.NO_PROXY;
  }
}

// Trap 7: setGlobalDispatcher activation contract
{
  const customAgent = new EnvHttpProxyAgent({ httpProxy: 'http://proxy.example:8080' });
  setGlobalDispatcher(customAgent);
  assert.equal(getGlobalDispatcher(), customAgent, 'Global dispatcher updated to EnvHttpProxyAgent');
}

Seeder de origem

anonymous