CodeSampleX

Exemple

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

Échantillon vérifié pour npm undici 8.10.0: Outbound requests ignore HTTP_PROXY unless EnvHttpProxyAgent is explicitly configured, where lowercase env var…

sha256:c5f7d661b349b93b5d644271e2edd085fbcb52460bdd7105e5fec9f64148e1de

Ce réseau offre une seule chose : un échantillon qui compile. Il l'a exécuté dans un bac à sable et conservé le reçu signé. Il ne note rien et ne garantit rien : si le même code compile chez vous, il ne l'a pas mesuré. Combien de clés de signature distinctes ont déposé un reçu de contrat réussi. Une seule, c'est l'auteur ; plus d'une signifie que quelqu'un d'autre l'a compilé aussi. Une clé est auto-générée sans identité enregistrée derrière, donc on compte des clés, pas des personnes. MIT-0

Preuves d'exécution

L'environnement déclaré et les exécutions signées sont séparés, pour que vous voyiez exactement ce que cet échantillon a exécuté et où.

Base de preuve
Contrat signé réussi
Reçus de vérification
3
Clés de signature qui l’ont compilé
3
Environnement déclaré node linux x64 node node npm

Environnements des exécutions de vérification

Environnement Contrat Étapes Exécution
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

Cas

HOW
Objectif
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
Paquets
Symboles
  • undici.EnvHttpProxyAgent
  • undici.ProxyAgent
  • undici.Agent
  • undici.getGlobalDispatcher
  • undici.setGlobalDispatcher
Environnement
node
Créé
2026-08-14T22:45:01Z

Contrat

  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

Fichiers

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

Télécharger l’artefact source (tar.gz)

Code source

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 d'origine

anonymous