133 lines · 5.1 KB · TypeScript
RawDownload
// How the inherited allowlist and the declared env combine, per platform.//// The sibling `stdio-env-isolation` suite spawns a real child and asks what it// received. That answers the security question, but it can only ever measure// the platform it runs on, and the defect here is Windows-only: a case-variant// key collision. So this suite exercises the merge directly and passes the// platform in, which lets the Windows behaviour be asserted from any host. import { describe, expect, it } from "@effect/vitest"; import { mergeStdioEnv } from "./stdio-connector"; /** The keys the SDK's own safe-list places underneath our result on Windows. */const windowsSdkKeys = ["APPDATA", "PATH", "SYSTEMROOT", "TEMP", "USERPROFILE"]; describe("mergeStdioEnv on a case-sensitive platform", () => {  it("keeps both spellings, because they are two real variables", () => {    // On Unix `HTTP_PROXY` and `http_proxy` are unrelated variables that    // different tooling reads, and the allowlist inherits both on purpose.    // Collapsing them here would drop configuration the host meant to pass.    const merged = mergeStdioEnv({      platform: "linux",      inherited: { HTTP_PROXY: "http://host.invalid:3128" },      declared: { http_proxy: "http://declared.invalid:8080" },    });     expect(merged).toStrictEqual({      HTTP_PROXY: "http://host.invalid:3128",      http_proxy: "http://declared.invalid:8080",    });  });   it("lets the declared env win an exact key collision", () => {    const merged = mergeStdioEnv({      platform: "darwin",      inherited: { HTTPS_PROXY: "http://host.invalid:3128" },      declared: { HTTPS_PROXY: "http://declared.invalid:8080" },    });     expect(merged).toStrictEqual({ HTTPS_PROXY: "http://declared.invalid:8080" });  });}); describe("mergeStdioEnv on win32", () => {  it("drops the inherited alias so the declared value is the only one", () => {    // The defect: a case-sensitive spread emits `HTTP_PROXY` beside    // `http_proxy`, both reach the child's environment block, and Windows    // resolves the case-insensitive name to whichever comes first — not    // necessarily the declared one. One entry can only resolve one way.    const merged = mergeStdioEnv({      platform: "win32",      inherited: { HTTP_PROXY: "http://host.invalid:3128" },      declared: { http_proxy: "http://declared.invalid:8080" },    });     expect(Object.keys(merged)).toStrictEqual(["http_proxy"]);    expect(merged.http_proxy).toBe("http://declared.invalid:8080");    expect(merged.HTTP_PROXY).toBeUndefined();  });   it("collapses aliases the allowlist itself produces", () => {    // `process.env` is case-insensitive on Windows, so reading the allowlist's    // uppercase and lowercase proxy spellings returns the same variable twice.    // The child should be handed it once.    const merged = mergeStdioEnv({      platform: "win32",      inherited: {        HTTPS_PROXY: "http://host.invalid:3128",        https_proxy: "http://host.invalid:3128",      },    });     expect(Object.keys(merged)).toStrictEqual(["https_proxy"]);    expect(merged.https_proxy).toBe("http://host.invalid:3128");  });   it("emits a declared key with the SDK's spelling when the SDK also sets it", () => {    // The SDK spreads `getDefaultEnvironment()` underneath this result with a    // case-sensitive spread, and its Windows list spells the variable `PATH`.    // Returning `Path` would leave `PATH` standing beside it; returning `PATH`    // replaces it, which is what a source that overrides its interpreter    // lookup is asking for.    const merged = mergeStdioEnv({      platform: "win32",      inherited: {},      declared: { Path: "C:\\server\\bin" },      sdkKeys: windowsSdkKeys,    });     expect(Object.keys(merged)).toStrictEqual(["PATH"]);    expect(merged.PATH).toBe("C:\\server\\bin");  });   it("leaves a key the SDK does not set under its declared spelling", () => {    const merged = mergeStdioEnv({      platform: "win32",      inherited: {},      declared: { My_Api_Token: "declared-value" },      sdkKeys: windowsSdkKeys,    });     expect(merged).toStrictEqual({ My_Api_Token: "declared-value" });  });   it("still passes an inherited variable the source does not declare", () => {    // Deduplication must not turn into dropping: the proxy and CA settings are    // the reason the allowlist exists.    const merged = mergeStdioEnv({      platform: "win32",      inherited: { NODE_EXTRA_CA_CERTS: "C:\\certs\\corporate.pem" },      declared: { DECLARED_TOKEN: "declared-value" },      sdkKeys: windowsSdkKeys,    });     expect(merged).toStrictEqual({      NODE_EXTRA_CA_CERTS: "C:\\certs\\corporate.pem",      DECLARED_TOKEN: "declared-value",    });  });   it("does not invent an entry for an SDK key nobody declared", () => {    // The SDK supplies its own safe-list; this merge only decides spelling for    // keys that are actually being passed.    const merged = mergeStdioEnv({      platform: "win32",      inherited: {},      declared: {},      sdkKeys: windowsSdkKeys,    });     expect(merged).toStrictEqual({});  });});