mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-08 09:30:43 +00:00
234 lines
7.4 KiB
TypeScript
234 lines
7.4 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import {
|
|
findManagedProxyRuntimeMutationLines,
|
|
findManagedProxyRuntimeMutations,
|
|
isAllowedManagedProxyRuntimeMutation,
|
|
} from "../../scripts/check-managed-proxy-runtime-mutation.mjs";
|
|
|
|
describe("check-managed-proxy-runtime-mutation", () => {
|
|
it("finds assignments and deletes for proxy env vars", () => {
|
|
const source = `
|
|
process.env.HTTP_PROXY = "http://proxy";
|
|
process.env["HTTPS_PROXY"] = "http://proxy";
|
|
delete process.env.NO_PROXY;
|
|
delete process.env["GLOBAL_AGENT_NO_PROXY"];
|
|
`;
|
|
|
|
expect(findManagedProxyRuntimeMutationLines(source)).toEqual([2, 3, 4, 5]);
|
|
});
|
|
|
|
it("finds global object alias GLOBAL_AGENT mutations", () => {
|
|
const source = `
|
|
const globalRecord = global;
|
|
const agent = globalRecord.GLOBAL_AGENT;
|
|
globalRecord.GLOBAL_AGENT = {};
|
|
globalRecord["GLOBAL_AGENT"] = {};
|
|
delete globalRecord.GLOBAL_AGENT;
|
|
delete globalRecord["GLOBAL_AGENT"];
|
|
agent.HTTP_PROXY = "http://proxy";
|
|
`;
|
|
|
|
expect(findManagedProxyRuntimeMutationLines(source)).toEqual([4, 5, 6, 7, 8]);
|
|
});
|
|
|
|
it("finds GLOBAL_AGENT mutations", () => {
|
|
const source = `
|
|
global.GLOBAL_AGENT = {};
|
|
global.GLOBAL_AGENT.NO_PROXY = "localhost";
|
|
global["GLOBAL_AGENT"].HTTP_PROXY = "http://proxy";
|
|
delete global.GLOBAL_AGENT.HTTPS_PROXY;
|
|
`;
|
|
|
|
expect(findManagedProxyRuntimeMutationLines(source)).toEqual([2, 3, 4, 5]);
|
|
});
|
|
|
|
it("finds Object.assign and Object.defineProperty mutations", () => {
|
|
const source = `
|
|
Object.assign(global.GLOBAL_AGENT, { NO_PROXY: "localhost" });
|
|
Object.assign(process.env, { NO_PROXY: "localhost" });
|
|
Object.defineProperty(process.env, "HTTP_PROXY", { value: "http://proxy" });
|
|
`;
|
|
|
|
expect(findManagedProxyRuntimeMutationLines(source)).toEqual([2, 3, 4]);
|
|
});
|
|
|
|
it("finds missing managed-proxy env key mutations", () => {
|
|
const source = `
|
|
process.env.GLOBAL_AGENT_FORCE_GLOBAL_AGENT = "true";
|
|
process.env.OPENCLAW_PROXY_LOOPBACK_MODE = "gateway-only";
|
|
`;
|
|
|
|
expect(findManagedProxyRuntimeMutationLines(source)).toEqual([2, 3]);
|
|
});
|
|
|
|
it("finds defineProperty mutations with constant proxy keys", () => {
|
|
const source = `
|
|
const proxyKey = "HTTP_PROXY";
|
|
const agentKey = "NO_PROXY";
|
|
Object.defineProperty(process.env, proxyKey, { value: "http://proxy" });
|
|
Object.defineProperty(global.GLOBAL_AGENT, agentKey, { value: "localhost" });
|
|
`;
|
|
|
|
expect(findManagedProxyRuntimeMutationLines(source)).toEqual([4, 5]);
|
|
});
|
|
|
|
it("finds destructured process.env alias mutations", () => {
|
|
const source = `
|
|
const { env } = process;
|
|
env.HTTP_PROXY = "http://proxy";
|
|
env["NO_PROXY"] = "localhost";
|
|
`;
|
|
|
|
expect(findManagedProxyRuntimeMutationLines(source)).toEqual([3, 4]);
|
|
});
|
|
|
|
it("finds process.env alias and constant key mutations", () => {
|
|
const source = `
|
|
const env = process.env;
|
|
const proxyKey = "HTTP_PROXY";
|
|
env.HTTPS_PROXY = "http://proxy";
|
|
env[proxyKey] = "http://proxy";
|
|
delete env.NO_PROXY;
|
|
Object.assign(env, { GLOBAL_AGENT_HTTP_PROXY: "http://proxy" });
|
|
Object.defineProperty(env, "OPENCLAW_PROXY_ACTIVE", { value: "1" });
|
|
`;
|
|
|
|
expect(findManagedProxyRuntimeMutationLines(source)).toEqual([4, 5, 6, 7, 8]);
|
|
});
|
|
|
|
it("finds dynamic process.env key mutations from forbidden key arrays", () => {
|
|
const source = `
|
|
const proxyKeys = ["HTTP_PROXY", "HTTPS_PROXY", "NO_PROXY"];
|
|
for (const key of proxyKeys) {
|
|
process.env[key] = "http://proxy";
|
|
}
|
|
for (const key of proxyKeys) {
|
|
delete process.env[key];
|
|
}
|
|
`;
|
|
|
|
expect(findManagedProxyRuntimeMutationLines(source)).toEqual([4, 7]);
|
|
});
|
|
|
|
it("finds dynamic process.env key mutations from spread-built forbidden key arrays", () => {
|
|
const source = `
|
|
const lower = ["http_proxy", "https_proxy"];
|
|
const upper = ["HTTP_PROXY", "HTTPS_PROXY"];
|
|
const all = [...lower, ...upper, "OPENCLAW_PROXY_LOOPBACK_MODE"];
|
|
for (const key of all) {
|
|
delete process.env[key];
|
|
}
|
|
`;
|
|
|
|
expect(findManagedProxyRuntimeMutationLines(source)).toEqual([6]);
|
|
});
|
|
|
|
it("ignores dynamic process.env key mutations from unrelated key arrays", () => {
|
|
const source = `
|
|
const normalKeys = ["PATH", "HOME"];
|
|
for (const key of normalKeys) {
|
|
process.env[key] = "value";
|
|
}
|
|
`;
|
|
|
|
expect(findManagedProxyRuntimeMutationLines(source)).toEqual([]);
|
|
});
|
|
|
|
it("finds GLOBAL_AGENT alias mutations", () => {
|
|
const source = `
|
|
const agent = global.GLOBAL_AGENT;
|
|
agent.HTTP_PROXY = "http://proxy";
|
|
agent["NO_PROXY"] = "localhost";
|
|
delete agent.HTTPS_PROXY;
|
|
`;
|
|
|
|
expect(findManagedProxyRuntimeMutationLines(source)).toEqual([3, 4, 5]);
|
|
});
|
|
|
|
it("finds globalThis.GLOBAL_AGENT mutations alongside global.GLOBAL_AGENT", () => {
|
|
const source = `
|
|
globalThis.GLOBAL_AGENT = {};
|
|
globalThis.GLOBAL_AGENT.NO_PROXY = "localhost";
|
|
globalThis["GLOBAL_AGENT"].HTTP_PROXY = "http://proxy";
|
|
delete globalThis.GLOBAL_AGENT.HTTPS_PROXY;
|
|
`;
|
|
|
|
expect(findManagedProxyRuntimeMutationLines(source)).toEqual([2, 3, 4, 5]);
|
|
});
|
|
|
|
it('finds process["env"] mixed access mutations', () => {
|
|
const source = `
|
|
process["env"].HTTP_PROXY = "http://proxy";
|
|
process["env"]["HTTPS_PROXY"] = "http://proxy";
|
|
delete process["env"].NO_PROXY;
|
|
`;
|
|
|
|
expect(findManagedProxyRuntimeMutationLines(source)).toEqual([2, 3, 4]);
|
|
});
|
|
|
|
it("does not flag Object.assign on a non-process .env namespace", () => {
|
|
const source = `
|
|
Object.assign(config.env, { NO_PROXY: "localhost" });
|
|
Object.defineProperty(config.env, "HTTP_PROXY", { value: "http://proxy" });
|
|
`;
|
|
|
|
expect(findManagedProxyRuntimeMutationLines(source)).toEqual([]);
|
|
});
|
|
|
|
it("ignores reads, unrelated env vars, comments, and strings", () => {
|
|
const source = `
|
|
const current = process.env.HTTP_PROXY;
|
|
process.env.PATH = "/usr/bin";
|
|
const text = "process.env.NO_PROXY = '*'";
|
|
// global.GLOBAL_AGENT.NO_PROXY = '*';
|
|
`;
|
|
|
|
expect(findManagedProxyRuntimeMutationLines(source)).toEqual([]);
|
|
});
|
|
|
|
it("reports the enclosing owner scope for each mutation", () => {
|
|
const source = `
|
|
function applyProxyEnv() {
|
|
process.env.HTTP_PROXY = "http://proxy";
|
|
}
|
|
|
|
class NoProxyLeaseManager {
|
|
release() {
|
|
delete process.env.NO_PROXY;
|
|
}
|
|
}
|
|
|
|
const updateProxy = () => {
|
|
global.GLOBAL_AGENT.NO_PROXY = "localhost";
|
|
};
|
|
`;
|
|
|
|
expect(findManagedProxyRuntimeMutations(source)).toEqual([
|
|
{ line: 3, scope: "applyProxyEnv" },
|
|
{ line: 8, scope: "NoProxyLeaseManager.release" },
|
|
{ line: 13, scope: "updateProxy" },
|
|
]);
|
|
});
|
|
|
|
it("allows approved owner scopes without exact line allowlists", () => {
|
|
expect(
|
|
isAllowedManagedProxyRuntimeMutation({
|
|
relativePath: "src/infra/net/proxy/proxy-lifecycle.ts",
|
|
scope: "applyProxyEnv",
|
|
}),
|
|
).toBe(true);
|
|
expect(
|
|
isAllowedManagedProxyRuntimeMutation({
|
|
relativePath: "extensions/browser/src/browser/cdp-proxy-bypass.ts",
|
|
scope: "NoProxyLeaseManager.release",
|
|
}),
|
|
).toBe(true);
|
|
expect(
|
|
isAllowedManagedProxyRuntimeMutation({
|
|
relativePath: "src/infra/net/proxy/proxy-lifecycle.ts",
|
|
scope: "startProxy",
|
|
}),
|
|
).toBe(false);
|
|
});
|
|
});
|