Skip to content

Commit a88e425

Browse files
committed
fix: honor the requested output format when rerunning a scan
`scans rerun` called `runScan` without forwarding the requested output format, so it always used the interactive default. With `--json` or `--format jsonl` it could block on the credential-selection prompt that `scan` suppresses in the same situation, and it accepted `--format md`, which `scan` rejects as unsupported for scan results. Forward the format from the rerun command so both behaviors match `scan`. Fixes #39
1 parent a8fc009 commit a88e425

3 files changed

Lines changed: 82 additions & 2 deletions

File tree

sdk/typescript/src/cli.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -877,7 +877,14 @@ export async function main(
877877
scanId: z.string().min(1).describe("Saved scan identifier."),
878878
}),
879879
output: z.record(z.string(), z.unknown()).optional(),
880-
async run({ args, error: incurError }) {
880+
async run({ args, error: incurError, format }) {
881+
if (format === "md") {
882+
errorOutput.write(
883+
"codex-security: Markdown output is not supported for scan results.\n",
884+
);
885+
exitCode = 2;
886+
return;
887+
}
881888
let scanArguments: ScanArguments;
882889
try {
883890
const { recipe } = await dependencies.runWorkbench([
@@ -896,7 +903,12 @@ export async function main(
896903
exitCode,
897904
});
898905
}
899-
const outcome = await runScan(scanArguments, errorOutput, dependencies);
906+
const outcome = await runScan(
907+
scanArguments,
908+
errorOutput,
909+
dependencies,
910+
format !== "json" && format !== "jsonl",
911+
);
900912
exitCode = outcome.exitCode;
901913
if (outcome.error !== undefined) {
902914
return incurError({

sdk/typescript/tests-ts/cli-authentication.test.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -475,6 +475,43 @@ describe("CLI authentication", () => {
475475
}
476476
});
477477

478+
test("never prompts when scans rerun requests machine-readable output", async () => {
479+
for (const argv of [
480+
["scans", "rerun", "scan-original", "--json"],
481+
["scans", "rerun", "scan-original", "--format", "jsonl"],
482+
]) {
483+
const stderr = capture(true);
484+
let prompts = 0;
485+
const deps = dependencies({
486+
environment: { OPENAI_API_KEY: "synthetic-private-key" },
487+
onWorkbench: () => ({
488+
recipe: {
489+
repository: "/original/repository",
490+
target: { kind: "repository", paths: [] },
491+
mode: "standard",
492+
pluginVersion: "1.2.3",
493+
config: {},
494+
},
495+
}),
496+
});
497+
deps.hasStoredChatGPTSignIn = async () => true;
498+
deps.scanAuthenticationPrompt = {
499+
isInteractive: () => true,
500+
select: async <Value extends string>(
501+
_message: string,
502+
options: readonly { label: string; value: Value }[],
503+
): Promise<Value> => {
504+
prompts += 1;
505+
return options[0]!.value;
506+
},
507+
};
508+
509+
expect(await main(argv, capture().stream, stderr.stream, deps)).toBe(0);
510+
expect(prompts).toBe(0);
511+
expect(stderr.text()).not.toContain("synthetic-private-key");
512+
}
513+
});
514+
478515
test("rejects explicit API-key authentication before initializing a scan when no key is set", async () => {
479516
const stderr = capture();
480517
const deps = dependencies();

sdk/typescript/tests-ts/cli-workbench.test.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -517,6 +517,37 @@ describe("CLI workbench", () => {
517517
}
518518
});
519519

520+
test("rejects Markdown rerun output like scan does", async () => {
521+
const stderr = capture();
522+
let started = false;
523+
524+
expect(
525+
await main(
526+
["scans", "rerun", "scan-original", "--format", "md"],
527+
capture().stream,
528+
stderr.stream,
529+
dependencies({
530+
onRun: () => {
531+
started = true;
532+
},
533+
onWorkbench: () => ({
534+
recipe: {
535+
repository: "/original/repository",
536+
target: { kind: "repository", paths: [] },
537+
mode: "standard",
538+
pluginVersion: "1.2.3",
539+
config: {},
540+
},
541+
}),
542+
}),
543+
),
544+
).toBe(2);
545+
expect(started).toBe(false);
546+
expect(stderr.text()).toContain(
547+
"Markdown output is not supported for scan results.",
548+
);
549+
});
550+
520551
test("reruns canonical recipes with exact config, policy, plugin, and lineage", async () => {
521552
let config: CodexSecurityConfig | undefined;
522553
let repository: string | undefined;

0 commit comments

Comments
 (0)