122 lines
4.7 KiB
TypeScript
122 lines
4.7 KiB
TypeScript
import { mkdtempSync, readFileSync, rmSync } from "node:fs";
|
|
import { tmpdir } from "node:os";
|
|
import { join } from "node:path";
|
|
import { appendOutput, configureTaskOutput, taskFullOutput } from "../src/components/microservices/code-queue/src/task-output";
|
|
import { sanitizeTaskOutputText } from "../src/components/microservices/code-queue/src/output-redaction";
|
|
import type { JsonValue, QueueTask } from "../src/components/microservices/code-queue/src/types";
|
|
|
|
type JsonRecord = Record<string, unknown>;
|
|
|
|
function assertCondition(condition: unknown, message: string, detail: unknown = {}): void {
|
|
if (!condition) throw new Error(`${message}: ${JSON.stringify(detail)}`);
|
|
}
|
|
|
|
function fixtureTask(): QueueTask {
|
|
const at = "2026-05-23T00:00:00.000Z";
|
|
return {
|
|
id: "codex_gh_auth_redaction_contract",
|
|
queueId: "default",
|
|
queueEnteredAt: at,
|
|
prompt: "redaction fixture",
|
|
basePrompt: "redaction fixture",
|
|
referenceTaskIds: [],
|
|
referenceInjection: null,
|
|
providerId: "D601",
|
|
cwd: "/workspace",
|
|
model: "gpt-5.5",
|
|
reasoningEffort: null,
|
|
executionMode: "default",
|
|
maxAttempts: 1,
|
|
status: "running",
|
|
createdAt: at,
|
|
updatedAt: at,
|
|
startedAt: at,
|
|
finishedAt: null,
|
|
readAt: null,
|
|
currentAttempt: 1,
|
|
currentMode: "initial",
|
|
codexThreadId: null,
|
|
activeTurnId: null,
|
|
finalResponse: "",
|
|
lastError: null,
|
|
lastJudge: null,
|
|
judgeFailCount: 0,
|
|
promptHistory: [],
|
|
output: [],
|
|
events: [],
|
|
attempts: [],
|
|
cancelRequested: false,
|
|
nextPrompt: null,
|
|
nextMode: null,
|
|
};
|
|
}
|
|
|
|
function assertNoTokenFragments(value: string, label: string): void {
|
|
assertCondition(!/\bgh[pousr]_[A-Za-z0-9_]{6,}\b/u.test(value), `${label} must redact gh token-like values`, value);
|
|
assertCondition(!/\bgithub_pat_[A-Za-z0-9_]{6,}\b/u.test(value), `${label} must redact GitHub PAT-like values`, value);
|
|
assertCondition(!/Token:\s*\S+/iu.test(value), `${label} must not expose raw gh auth token lines`, value);
|
|
assertCondition(!/Token scopes?:\s*\S+/iu.test(value), `${label} must not expose raw gh auth scope lines`, value);
|
|
}
|
|
|
|
export function runCodeQueueGhAuthRedactionContract(): JsonRecord {
|
|
const tmp = mkdtempSync(join(tmpdir(), "code-queue-gh-auth-redaction-"));
|
|
const task = fixtureTask();
|
|
let seq = 0;
|
|
try {
|
|
configureTaskOutput({
|
|
config: { maxInMemoryOutputRecords: 1000, outputArchiveDir: tmp },
|
|
allocateSeq: () => {
|
|
seq += 1;
|
|
return seq;
|
|
},
|
|
errorToJson: (error: unknown): JsonValue => error instanceof Error ? { message: error.message } : String(error),
|
|
logger: () => undefined,
|
|
markTaskDirty: () => undefined,
|
|
nowIso: () => "2026-05-23T00:00:01.000Z",
|
|
schedulePersistState: () => undefined,
|
|
});
|
|
|
|
const rawGhAuth = [
|
|
"github.com",
|
|
" \u2713 Logged in to github.com account example (keyring)",
|
|
" - Active account: true",
|
|
" - Git operations protocol: ssh",
|
|
" - Token: ghp_abcdef1234567890abcdef1234567890",
|
|
" - Token scopes: 'repo', 'read:org'",
|
|
"generic token=github_pat_abcdef1234567890abcdef1234567890",
|
|
].join("\n");
|
|
const sanitized = sanitizeTaskOutputText(rawGhAuth);
|
|
assertNoTokenFragments(sanitized, "direct sanitizer output");
|
|
assertCondition(sanitized.includes("[redacted gh auth status line]"), "sanitizer must redact gh auth status lines", sanitized);
|
|
assertCondition(sanitized.includes("bun scripts/cli.ts gh auth status"), "sanitizer must include UniDesk gh wrapper hint", sanitized);
|
|
|
|
appendOutput(task, "command", `${rawGhAuth}\n`, "item/commandExecution/outputDelta", "call-gh-auth", true);
|
|
const retained = JSON.stringify(task.output);
|
|
const archived = readFileSync(join(tmp, `${task.id}.jsonl`), "utf8");
|
|
const full = JSON.stringify(taskFullOutput(task));
|
|
assertNoTokenFragments(retained, "retained output");
|
|
assertNoTokenFragments(archived, "archived output");
|
|
assertNoTokenFragments(full, "full output replay");
|
|
assertCondition(full.includes("bun scripts/cli.ts gh auth status"), "full output replay must keep wrapper hint", full);
|
|
|
|
return {
|
|
ok: true,
|
|
checks: [
|
|
"raw gh auth status token and scope lines are redacted",
|
|
"token-like GitHub values are redacted before retained output persistence",
|
|
"output archive replay remains redacted",
|
|
"redacted output nudges runner toward bun scripts/cli.ts gh auth status",
|
|
],
|
|
};
|
|
} finally {
|
|
rmSync(tmp, { recursive: true, force: true });
|
|
}
|
|
}
|
|
|
|
try {
|
|
process.stdout.write(`${JSON.stringify(runCodeQueueGhAuthRedactionContract(), null, 2)}\n`);
|
|
} catch (error) {
|
|
process.stderr.write(`${error instanceof Error ? error.stack ?? error.message : String(error)}\n`);
|
|
process.exit(1);
|
|
}
|