CodeSampleX

Beispiel

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

Verifiziertes Beispiel für npm undici 8.10.0: Outbound requests ignore HTTP_PROXY unless EnvHttpProxyAgent is explicitly configured, where lowercase env var…

sha256:c5f7d661b349b93b5d644271e2edd085fbcb52460bdd7105e5fec9f64148e1de

Dieses Netzwerk bietet eine Sache: ein Sample, das baut. Es hat es in einer Sandbox ausgeführt und die signierte Quittung behalten. Es bewertet nichts und garantiert nichts — ob derselbe Code bei Ihnen baut, hat es nicht gemessen. Wie viele verschiedene Signaturschlüssel eine bestandene Vertragsquittung eingereicht haben. Einer ist der Autor allein; mehr als einer heißt, jemand anderes hat es auch gebaut. Ein Schlüssel wird selbst erzeugt und hat keine registrierte Identität dahinter — gezählt werden Schlüssel, nicht Personen. MIT-0

Ausführungsbelege

Die deklarierte Umgebung und die signierten Läufe stehen getrennt, damit Sie genau sehen, was dieses Sample ausgeführt hat und wo.

Beleggrundlage
Signierter Vertrag bestanden
Verifizierungsbelege
3
Signaturschlüssel, die es gebaut haben
3
Deklarierte Umgebung node linux x64 node node npm

Umgebungen der Verifizierungsläufe

Umgebung Contract Stufen Lauf
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

Fall

HOW
Ziel
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
Pakete
Symbole
  • undici.EnvHttpProxyAgent
  • undici.ProxyAgent
  • undici.Agent
  • undici.getGlobalDispatcher
  • undici.setGlobalDispatcher
Umgebung
node
Erstellt
2026-08-14T22:45:01Z

Contract

  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

Dateien

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

Quellartefakt herunterladen (tar.gz)

Quelltext

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');
}

Ursprungs-Seeder

anonymous