Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 2 additions & 16 deletions scripts/kpi-gate.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { tmpdir } from "node:os";
import { join } from "node:path";
import { spawnSync } from "node:child_process";
import { hasUnsafeSourceId } from "./lib/source-id.mjs";
import { createKpiChildEnvironment } from "./lib/kpi-child-environment.mjs";

const parsedArgs = parseArgs(process.argv.slice(2));
const logPath = parsedArgs.positionals[0] ?? process.env.NOEMA_KPI_LOG_PATH ?? "exchange-30d.ndjson";
Expand Down Expand Up @@ -114,28 +115,13 @@ const guardCommands = [
},
];

const kpiChildEnvironment = { ...process.env };
for (const key of [
"NODE_OPTIONS",
"NODE_PATH",
"GITHUB_TOKEN",
"GH_TOKEN",
"NVIDIA_NIM_API_KEY",
"COPILOT_GITHUB_TOKEN",
]) {
delete kpiChildEnvironment[key];
}

let failed = false;
const stepSummaries = [];

for (const step of guardCommands) {
const child = spawnSync(step.command[0], step.command.slice(1), {
encoding: "utf8",
env: {
...kpiChildEnvironment,
...(step.env ?? {}),
},
env: createKpiChildEnvironment(step.name, process.env, step.env ?? {}),
});
const output = child.stdout || "";
if (output) process.stdout.write(output);
Expand Down
36 changes: 36 additions & 0 deletions scripts/lib/kpi-child-environment.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
const ALERT_KEYS = [
"NOEMA_ALERT_5M_FAILURE_RATE",
"NOEMA_ALERT_5M_P95_MS",
"NOEMA_ALERT_RATE_LIMIT_MINUTES",
"NOEMA_ALERT_WORKFLOW_SPIKE_MULTIPLIER",
];

/**
* Build the complete environment for one KPI child process from an explicit
* allowlist. Ambient parent state is never copied wholesale: the strict KPI
* checker receives only its requested window, while the alert evaluator may
* receive only the four reviewed alert-threshold inputs above.
*
* @param {string} stepName Closed child-step identity.
* @param {Record<string, unknown>} parentEnvironment Ambient parent values.
* @param {Record<string, unknown>} stepEnvironment Explicit per-step values.
* @returns {Record<string, string>} Least-authority child environment.
* @throws {Error} When the child-step identity is not part of the closed contract.
*/
export function createKpiChildEnvironment(stepName, parentEnvironment = {}, stepEnvironment = {}) {
if (stepName === "kpi-check") {
const value = stepEnvironment.NOEMA_KPI_REQUIRE_WINDOW_DAYS;
return typeof value === "string" ? { NOEMA_KPI_REQUIRE_WINDOW_DAYS: value } : {};
}

if (stepName === "kpi-alert") {
const environment = {};
for (const key of ALERT_KEYS) {
const value = parentEnvironment[key];
if (typeof value === "string") environment[key] = value;
}
return environment;
}

throw new Error(`Unknown KPI child step: ${stepName}`);
}
98 changes: 98 additions & 0 deletions test/kpi-child-environment.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import { readFileSync } from "node:fs";
import { describe, expect, it } from "vitest";

const hostileParentEnvironment: NodeJS.ProcessEnv = {
PATH: "/synthetic/bin",
HOME: "/synthetic/home",
NODE_OPTIONS: "--require=/synthetic/preload.cjs",
NODE_PATH: "/synthetic/node_modules",
GITHUB_TOKEN: "synthetic-github-token",
GH_TOKEN: "synthetic-gh-token",
COPILOT_GITHUB_TOKEN: "synthetic-copilot-token",
NVIDIA_NIM_API_KEY: "synthetic-nim-key",
NOEMA_LLM_API_KEY: "synthetic-model-key",
NOEMA_MAINTAINER_APP_CLIENT_ID: "synthetic-maintainer-client",
NOEMA_MAINTAINER_APP_PRIVATE_KEY: "synthetic-maintainer-private-key",
NOEMA_REVIEWER_APP_CLIENT_ID: "synthetic-reviewer-client",
NOEMA_REVIEWER_APP_PRIVATE_KEY: "synthetic-reviewer-private-key",
CLOUDFLARE_API_TOKEN: "synthetic-cloudflare-token",
CLOUDFLARE_ACCOUNT_ID: "synthetic-cloudflare-account",
AWS_SECRET_ACCESS_KEY: "synthetic-provider-key",
HTTP_PROXY: "http://synthetic-proxy.invalid",
HTTPS_PROXY: "https://synthetic-proxy.invalid",
ALL_PROXY: "socks5://synthetic-proxy.invalid",
NOEMA_UNRELATED_STATE: "synthetic-unrelated-state",
NOEMA_KPI_REQUIRE_WINDOW_DAYS: "999",
NOEMA_ALERT_5M_FAILURE_RATE: "0.07",
NOEMA_ALERT_5M_P95_MS: "640",
NOEMA_ALERT_RATE_LIMIT_MINUTES: "4",
NOEMA_ALERT_WORKFLOW_SPIKE_MULTIPLIER: "5",
};

async function loadEnvironmentFactory() {
const modulePath = "../scripts/lib/kpi-child-environment.mjs";
return import(modulePath);
}

describe("KPI child-process least-authority environment", () => {
it("routes every KPI child through the declared environment contract", () => {
const source = readFileSync("scripts/kpi-gate.mjs", "utf8");

expect(source).toContain(
'import { createKpiChildEnvironment } from "./lib/kpi-child-environment.mjs";',
);
expect(source).not.toContain("const kpiChildEnvironment = { ...process.env }");
expect(source).toContain("env: createKpiChildEnvironment(step.name, process.env, step.env ?? {}),");
});

it("gives kpi-check only its explicit strict-window input", async () => {
const { createKpiChildEnvironment } = await loadEnvironmentFactory();

expect(createKpiChildEnvironment(
"kpi-check",
hostileParentEnvironment,
{ NOEMA_KPI_REQUIRE_WINDOW_DAYS: "30" },
)).toEqual({
NOEMA_KPI_REQUIRE_WINDOW_DAYS: "30",
});
});

it("gives kpi-alert only the four reviewed alert-threshold inputs", async () => {
const { createKpiChildEnvironment } = await loadEnvironmentFactory();

expect(createKpiChildEnvironment("kpi-alert", hostileParentEnvironment, {})).toEqual({
NOEMA_ALERT_5M_FAILURE_RATE: "0.07",
NOEMA_ALERT_5M_P95_MS: "640",
NOEMA_ALERT_RATE_LIMIT_MINUTES: "4",
NOEMA_ALERT_WORKFLOW_SPIKE_MULTIPLIER: "5",
});
});

it("omits unset allowlisted values instead of emitting undefined child entries", async () => {
const { createKpiChildEnvironment } = await loadEnvironmentFactory();

expect(createKpiChildEnvironment(
"kpi-check",
hostileParentEnvironment,
{ NOEMA_KPI_REQUIRE_WINDOW_DAYS: undefined },
)).toEqual({});
expect(createKpiChildEnvironment(
"kpi-alert",
{
NOEMA_ALERT_5M_FAILURE_RATE: undefined,
NOEMA_ALERT_5M_P95_MS: undefined,
},
{},
)).toEqual({});
});

it("fails closed instead of widening authority for an unknown child", async () => {
const { createKpiChildEnvironment } = await loadEnvironmentFactory();

expect(() => createKpiChildEnvironment(
"future-kpi-child",
hostileParentEnvironment,
{},
)).toThrow(/Unknown KPI child step/u);
});
});
Loading