Skip to content
Open
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
16 changes: 14 additions & 2 deletions sdk/typescript/src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -877,7 +877,14 @@ export async function main(
scanId: z.string().min(1).describe("Saved scan identifier."),
}),
output: z.record(z.string(), z.unknown()).optional(),
async run({ args, error: incurError }) {
async run({ args, error: incurError, format }) {
if (format === "md") {
errorOutput.write(
"codex-security: Markdown output is not supported for scan results.\n",
);
exitCode = 2;
return;
}
let scanArguments: ScanArguments;
try {
const { recipe } = await dependencies.runWorkbench([
Expand All @@ -896,7 +903,12 @@ export async function main(
exitCode,
});
}
const outcome = await runScan(scanArguments, errorOutput, dependencies);
const outcome = await runScan(
scanArguments,
errorOutput,
dependencies,
format !== "json" && format !== "jsonl",
);
exitCode = outcome.exitCode;
if (outcome.error !== undefined) {
return incurError({
Expand Down
37 changes: 37 additions & 0 deletions sdk/typescript/tests-ts/cli-authentication.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -475,6 +475,43 @@ describe("CLI authentication", () => {
}
});

test("never prompts when scans rerun requests machine-readable output", async () => {
for (const argv of [
["scans", "rerun", "scan-original", "--json"],
["scans", "rerun", "scan-original", "--format", "jsonl"],
]) {
const stderr = capture(true);
let prompts = 0;
const deps = dependencies({
environment: { OPENAI_API_KEY: "synthetic-private-key" },
onWorkbench: () => ({
recipe: {
repository: "/original/repository",
target: { kind: "repository", paths: [] },
mode: "standard",
pluginVersion: "1.2.3",
config: {},
},
}),
});
deps.hasStoredChatGPTSignIn = async () => true;
deps.scanAuthenticationPrompt = {
isInteractive: () => true,
select: async <Value extends string>(
_message: string,
options: readonly { label: string; value: Value }[],
): Promise<Value> => {
prompts += 1;
return options[0]!.value;
},
};

expect(await main(argv, capture().stream, stderr.stream, deps)).toBe(0);
expect(prompts).toBe(0);
expect(stderr.text()).not.toContain("synthetic-private-key");
}
});

test("rejects explicit API-key authentication before initializing a scan when no key is set", async () => {
const stderr = capture();
const deps = dependencies();
Expand Down
31 changes: 31 additions & 0 deletions sdk/typescript/tests-ts/cli-workbench.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -517,6 +517,37 @@ describe("CLI workbench", () => {
}
});

test("rejects Markdown rerun output like scan does", async () => {
const stderr = capture();
let started = false;

expect(
await main(
["scans", "rerun", "scan-original", "--format", "md"],
capture().stream,
stderr.stream,
dependencies({
onRun: () => {
started = true;
},
onWorkbench: () => ({
recipe: {
repository: "/original/repository",
target: { kind: "repository", paths: [] },
mode: "standard",
pluginVersion: "1.2.3",
config: {},
},
}),
}),
),
).toBe(2);
expect(started).toBe(false);
expect(stderr.text()).toContain(
"Markdown output is not supported for scan results.",
);
});

test("reruns canonical recipes with exact config, policy, plugin, and lineage", async () => {
let config: CodexSecurityConfig | undefined;
let repository: string | undefined;
Expand Down