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
8 changes: 5 additions & 3 deletions sdk/typescript/src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1629,7 +1629,8 @@ export async function main(
`Effective scan authentication: API key from ${authentication.source}.\n`,
);
errorOutput.write(
"To use a ChatGPT sign-in, unset OPENAI_API_KEY and CODEX_API_KEY.\n",
"To use a ChatGPT sign-in, remove OPENAI_API_KEY and CODEX_API_KEY " +
"from the environment, then run the command again.\n",
);
}
} else if (exitCode === 0 && !options.withApiKey) {
Expand All @@ -1654,8 +1655,9 @@ export async function main(
: "your ChatGPT sign-in";
errorOutput.write(
loginWarning +
`To use ${storedCredentials}, pass '--auth chatgpt' or run ` +
`'unset ${configuredApiKeyVariables.join(" ")}'.\n`,
`To use ${storedCredentials}, pass '--auth chatgpt' or remove ` +
`${configuredApiKeyVariables.join(" and ")} from the environment, ` +
"then run the command again.\n",
);
}
}
Expand Down
68 changes: 55 additions & 13 deletions sdk/typescript/tests-ts/cli-authentication.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -170,25 +170,26 @@ describe("CLI authentication", () => {
`Effective scan authentication: API key from ${expectedSource}.`,
);
expect(stderr.text()).toContain(
"To use a ChatGPT sign-in, unset OPENAI_API_KEY and CODEX_API_KEY.",
"To use a ChatGPT sign-in, remove OPENAI_API_KEY and CODEX_API_KEY " +
"from the environment, then run the command again.",
);
expect(stderr.text()).not.toContain("SYNTHETIC_SECRET");
}
});

test("explains interactive choice and how to unset every shadowing key after ChatGPT login", async () => {
for (const [argv, environment, source, unsetCommand] of [
test("explains interactive choice and how to remove every shadowing key after ChatGPT login", async () => {
for (const [argv, environment, source, removeVariables] of [
[
["login"],
{ OPENAI_API_KEY: "sk-proj-SYNTHETIC_SECRET_123" },
"OPENAI_API_KEY",
"unset OPENAI_API_KEY",
"OPENAI_API_KEY",
],
[
["login", "--device-auth"],
{ Codex_Api_Key: "sk-proj-SYNTHETIC_SECRET_456" },
"CODEX_API_KEY",
"unset Codex_Api_Key",
"Codex_Api_Key",
],
[
["login"],
Expand All @@ -197,7 +198,7 @@ describe("CLI authentication", () => {
CODEX_API_KEY: "sk-proj-SYNTHETIC_SECRET_456",
},
"OPENAI_API_KEY",
"unset OPENAI_API_KEY CODEX_API_KEY",
"OPENAI_API_KEY and CODEX_API_KEY",
],
] as const) {
const stdout = capture();
Expand All @@ -219,30 +220,32 @@ describe("CLI authentication", () => {
`noninteractive scans will use ${source}.`,
);
expect(stderr.text()).toContain("--auth chatgpt");
expect(stderr.text()).toContain(`'${unsetCommand}'`);
expect(stderr.text()).toContain(
`remove ${removeVariables} from the environment, then run the command again.`,
);
expect(stderr.text()).not.toContain("SYNTHETIC_SECRET");
}
});

test("warns when an environment API key overrides a successful access-token login", async () => {
for (const [environment, source, unsetCommand] of [
for (const [environment, source, removeVariables] of [
[
{ OPENAI_API_KEY: "sk-proj-SYNTHETIC_SECRET_123" },
"OPENAI_API_KEY",
"unset OPENAI_API_KEY",
"OPENAI_API_KEY",
],
[
{ Codex_Api_Key: "sk-proj-SYNTHETIC_SECRET_456" },
"CODEX_API_KEY",
"unset Codex_Api_Key",
"Codex_Api_Key",
],
[
{
OPENAI_API_KEY: "sk-proj-SYNTHETIC_SECRET_123",
CODEX_API_KEY: "sk-proj-SYNTHETIC_SECRET_456",
},
"OPENAI_API_KEY",
"unset OPENAI_API_KEY CODEX_API_KEY",
"OPENAI_API_KEY and CODEX_API_KEY",
],
] as const) {
const stdout = capture();
Expand All @@ -261,14 +264,53 @@ describe("CLI authentication", () => {
`Access-token login succeeded, but noninteractive scans will use ${source}.`,
);
expect(stderr.text()).toContain(
"To use your stored credentials, pass '--auth chatgpt' or run ",
"To use your stored credentials, pass '--auth chatgpt' or remove ",
);
expect(stderr.text()).toContain(
`remove ${removeVariables} from the environment, then run the command again.`,
);
expect(stderr.text()).toContain(`'${unsetCommand}'`);
expect(stderr.text()).not.toContain("ChatGPT login succeeded");
expect(stderr.text()).not.toContain("SYNTHETIC_SECRET");
}
});

test("uses shell-neutral guidance when an API key overrides the stored login", async () => {
const posixOnlyShellCommands = [/\bunset\b/, /\bexport\s+\w+=/, /\$env:/i];

const statusStdout = capture();
const statusStderr = capture();
expect(
await main(
["login", "status"],
statusStdout.stream,
statusStderr.stream,
dependencies({ environment: { OPENAI_API_KEY: "sk-proj-SYNTHETIC" } }),
),
).toBe(0);
for (const pattern of posixOnlyShellCommands) {
expect(statusStderr.text()).not.toMatch(pattern);
}

const loginStdout = capture();
const loginStderr = capture();
expect(
await main(
["login"],
loginStdout.stream,
loginStderr.stream,
dependencies({
environment: {
OPENAI_API_KEY: "sk-proj-SYNTHETIC_123",
CODEX_API_KEY: "sk-proj-SYNTHETIC_456",
},
}),
),
).toBe(0);
for (const pattern of posixOnlyShellCommands) {
expect(loginStderr.text()).not.toMatch(pattern);
}
});

test("does not report a ChatGPT login warning for failed or API-key logins", async () => {
const environment = { OPENAI_API_KEY: "synthetic-private-key" };

Expand Down