fix(security): use crypto.randomInt/randomUUID in chipotle + URL parser in test (#3285)

Integrated into release/v3.8.12. CodeQL hardening on the Chipotle executor: Math.random → crypto.randomInt/randomUUID, and a strict URL hostname check in the test. Fixed the node:crypto import (crypto.randomInt is not on the Web Crypto global → would crash at WS-connect) and added a regression guard exercising both helpers.
This commit is contained in:
Paijo
2026-06-06 14:16:51 +07:00
committed by GitHub
parent ba734b01b2
commit 1344843a45
2 changed files with 32 additions and 8 deletions

View File

@@ -1,3 +1,5 @@
import { randomInt, randomUUID } from "node:crypto";
import { BaseExecutor, type ExecuteInput } from "./base.ts";
import type { ProviderCredentials } from "./base.ts";
import { sanitizeErrorMessage } from "../utils/error.ts";
@@ -6,14 +8,15 @@ const BASE_URL = "https://amelia.chipotle.com";
const DOMAIN_CODE = "chipotle";
const DOMAIN_ID = "23700760-e1e5-4c3c-931d-8804e29a6775";
function randomServerId(): string {
return String(Math.floor(Math.random() * 1000)).padStart(3, "0");
// Exported for unit testing — these run at WS-connect time, so a missing
// node:crypto import (crypto.randomInt is NOT on the Web Crypto global) would
// otherwise only surface as a runtime crash deep in _connect().
export function randomServerId(): string {
return String(randomInt(0, 1000)).padStart(3, "0");
}
function randomSessionId(): string {
return Array.from({ length: 8 }, () =>
Math.random().toString(36).substring(2, 6),
).join("");
export function randomSessionId(): string {
return randomUUID().replace(/-/g, "").slice(0, 32);
}
interface AmeliaSession {

View File

@@ -1,6 +1,10 @@
import { describe, it } from "node:test";
import assert from "node:assert";
import { ChipotleExecutor } from "../../open-sse/executors/chipotle.ts";
import {
ChipotleExecutor,
randomServerId,
randomSessionId,
} from "../../open-sse/executors/chipotle.ts";
const executor = new ChipotleExecutor();
@@ -12,7 +16,24 @@ describe("ChipotleExecutor", () => {
it("buildUrl returns Amelia endpoint", () => {
const url = executor.buildUrl("pepper-1", false);
assert.ok(url.includes("amelia.chipotle.com"));
const parsed = new URL(url);
assert.strictEqual(parsed.hostname, "amelia.chipotle.com");
});
// Regression guard for the node:crypto import — randomInt is NOT on the Web
// Crypto global, so a bare `crypto.randomInt` would throw at WS-connect time.
it("randomServerId yields a 3-digit numeric string (crypto.randomInt available)", () => {
for (let i = 0; i < 50; i++) {
const id = randomServerId();
assert.match(id, /^\d{3}$/, `expected 3 digits, got "${id}"`);
assert.ok(Number(id) >= 0 && Number(id) <= 999);
}
});
it("randomSessionId yields 32 hex chars (crypto.randomUUID available)", () => {
const id = randomSessionId();
assert.match(id, /^[0-9a-f]{32}$/, `expected 32 hex chars, got "${id}"`);
assert.notStrictEqual(randomSessionId(), randomSessionId());
});
it("transformRequest passes model through", () => {