fix(slack): resolve matching quoted env-file secrets (#376)

Signed-off-by: Rudy Celekli <[email protected]>
This commit is contained in:
Rudy Mizrahi Celekli authored and GitHub committed 2026-10-01 18:15:41 -07:00
1 parent 6b60035416
commit e6828b73e1
2 files changed
+51 -4

No files matched your search

@@ -31,10 +31,11 @@ export function parseEnvFile(text: string): Record<string, string> {
const t = line.trim();
if (!t || t.startsWith("#") || !t.includes("=")) continue;
const i = t.indexOf("=");
out[t.slice(0, i).trim()] = t
.slice(i + 1)
.trim()
.replace(/^"|"$/g, "");
const value = t.slice(i + 1).trim();
const quote = value[0];
out[t.slice(0, i).trim()] = value.length >= 2 && (quote === '"' || quote === "'") && value.endsWith(quote)
? value.slice(1, -1)
: value;
}
return out;
}
@@ -0,0 +1,46 @@
import { createServer } from "node:http";
import { once } from "node:events";
import { mkdtempSync, writeFileSync, rmSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { expect, it } from "vitest";
import { parseEnvFile, resolveSecret } from "../src/domain/gateway/slack/secrets.js";
import { callWebApi, type FetchImpl } from "../src/domain/gateway/slack/slack-api.js";
it("authenticates with unquoted, single-quoted and double-quoted file secrets", async () => {
const scratch = mkdtempSync(join(tmpdir(), "openrig-slack-secret-"));
const token = "synthetic-fixture-token";
const server = createServer((req, res) => {
res.setHeader("content-type", "application/json");
res.end(JSON.stringify(req.headers.authorization === `Bearer ${token}`
? { ok: true } : { ok: false, error: "invalid_auth" }));
});
server.listen(0, "127.0.0.1");
await once(server, "listening");
try {
const address = server.address();
if (!address || typeof address === "string") throw new Error("missing fixture address");
// Only the transport destination is redirected: native fetch still carries
// the production client's actual Authorization header to a local server.
const fetchImpl: FetchImpl = (_url, init) => fetch(`http://127.0.0.1:${address.port}`, init);
const envFile = join(scratch, "secret.env");
for (const quote of ["", "'", '"']) {
writeFileSync(envFile, `SLACK_BOT_TOKEN=${quote}${token}${quote}\n`, { mode: 0o600 });
const resolved = resolveSecret("SLACK_BOT_TOKEN", { env: {}, envFile });
expect(resolved).toBe(token);
expect((await callWebApi("auth.test", resolved!, {}, fetchImpl, 1000)).ok).toBe(true);
expect(resolveSecret("SLACK_BOT_TOKEN", { env: { SLACK_BOT_TOKEN: "env-wins" }, envFile })).toBe("env-wins");
}
} finally {
server.closeAllConnections();
await new Promise<void>((resolve) => server.close(() => resolve()));
rmSync(scratch, { recursive: true, force: true });
}
});
it("preserves unmatched quotes and quote characters within unquoted values", () => {
const values = ['"prefix', 'suffix"', "'prefix", "suffix'", "a'b", 'a"b', "'mixed\"", '"mixed\''];
for (const value of values) {
expect(parseEnvFile(`TOKEN=${value}\n`).TOKEN).toBe(value);
}
});