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
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,9 @@ codex-security scan . --provider amazon-bedrock --model openai.gpt-5.6-luna
export OPENROUTER_API_KEY="<your-openrouter-api-key>"
codex-security scan . --provider openrouter --model anthropic/claude-sonnet-4.5

export ORCAROUTER_API_KEY="<your-orcarouter-api-key>"
codex-security scan . --provider orcarouter --model orcarouter/fusion

export FIREWORKS_API_KEY="<your-fireworks-api-key>"
codex-security scan . --provider fireworks --model accounts/fireworks/models/qwen3-235b-a22b
```
Expand Down
1 change: 1 addition & 0 deletions plugins/codex-security/.mcp.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
"CODEX_MCP_NODE_PATH",
"OPENAI_API_KEY",
"OPENROUTER_API_KEY",
"ORCAROUTER_API_KEY",
"FIREWORKS_API_KEY",
"AWS_BEARER_TOKEN_BEDROCK",
"AWS_ACCESS_KEY_ID",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ assert.deepEqual(
"CODEX_MCP_NODE_PATH",
"OPENAI_API_KEY",
"OPENROUTER_API_KEY",
"ORCAROUTER_API_KEY",
"FIREWORKS_API_KEY",
"AWS_BEARER_TOKEN_BEDROCK",
"AWS_ACCESS_KEY_ID",
Expand Down
3 changes: 3 additions & 0 deletions sdk/typescript/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,9 @@ For other inference providers:
export OPENROUTER_API_KEY="<your-openrouter-api-key>"
npx @openai/codex-security scan . --provider openrouter --model anthropic/claude-sonnet-4.5

export ORCAROUTER_API_KEY="<your-orcarouter-api-key>"
npx @openai/codex-security scan . --provider orcarouter --model orcarouter/fusion

export FIREWORKS_API_KEY="<your-fireworks-api-key>"
npx @openai/codex-security scan . --provider fireworks --model accounts/fireworks/models/qwen3-235b-a22b

Expand Down
8 changes: 7 additions & 1 deletion sdk/typescript/src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,7 @@ export type ScanAuthentication =
| "OPENAI_API_KEY"
| "CODEX_API_KEY"
| "OPENROUTER_API_KEY"
| "ORCAROUTER_API_KEY"
| "FIREWORKS_API_KEY";
verified: false;
}
Expand Down Expand Up @@ -3325,7 +3326,11 @@ export function selectedScanEnvironment(
Object.entries(environment).filter(([name]) => {
const key = name.toUpperCase();
if (key === "OPENAI_API_KEY" || key === "CODEX_API_KEY") return false;
if (key === "OPENROUTER_API_KEY" || key === "FIREWORKS_API_KEY") {
if (
key === "OPENROUTER_API_KEY" ||
key === "ORCAROUTER_API_KEY" ||
key === "FIREWORKS_API_KEY"
) {
return (
!bedrockProvider &&
(selectedProviderKey === null || key === selectedProviderKey)
Expand Down Expand Up @@ -3365,6 +3370,7 @@ function environmentApiKeyEntry(
| "OPENAI_API_KEY"
| "CODEX_API_KEY"
| "OPENROUTER_API_KEY"
| "ORCAROUTER_API_KEY"
| "FIREWORKS_API_KEY";
value: string;
} | null {
Expand Down
2 changes: 1 addition & 1 deletion sdk/typescript/src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -299,7 +299,7 @@ const VALUE_OPTIONS = new Set([
"--linear-assignee",
]);
const PROVIDER_OPTION = z
.enum(["openai", "openrouter", "fireworks", "amazon-bedrock"])
.enum(["openai", "openrouter", "orcarouter", "fireworks", "amazon-bedrock"])
.default("openai")
.describe("Inference provider for scans.");
const CREATE_PR_OPTION = z
Expand Down
8 changes: 8 additions & 0 deletions sdk/typescript/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,16 @@ export const FIREWORKS_CODEX_PROVIDER = {
wire_api: "responses",
} as const satisfies JsonObject;

export const ORCAROUTER_CODEX_PROVIDER = {
name: "OrcaRouter",
base_url: "https://api.orcarouter.ai/v1",
env_key: "ORCAROUTER_API_KEY",
wire_api: "responses",
} as const satisfies JsonObject;

export const EXTERNAL_CODEX_PROVIDERS = {
openrouter: OPENROUTER_CODEX_PROVIDER,
orcarouter: ORCAROUTER_CODEX_PROVIDER,
fireworks: FIREWORKS_CODEX_PROVIDER,
} as const;

Expand Down
1 change: 1 addition & 0 deletions sdk/typescript/src/runtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ const PLUGIN_HELPER_SECRET_ENVIRONMENT_VARIABLES = new Set([
"OPENAI_API_KEY",
"CODEX_API_KEY",
"OPENROUTER_API_KEY",
"ORCAROUTER_API_KEY",
"FIREWORKS_API_KEY",
]);
const PREPARE_SCAN_ARTIFACT_RESTORER_PROGRAM = `
Expand Down
6 changes: 5 additions & 1 deletion sdk/typescript/tests-ts/api-events.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,11 @@ describe("one-shot scan events", () => {
});

test("does not mistake external-provider keys for OpenAI API organizations", async () => {
for (const source of ["OPENROUTER_API_KEY", "FIREWORKS_API_KEY"] as const) {
for (const source of [
"OPENROUTER_API_KEY",
"ORCAROUTER_API_KEY",
"FIREWORKS_API_KEY",
] as const) {
for (const status of ["not_granted", "unknown"] as const) {
const scanDir = await copyCompletedScan(await temporaryDirectory());
const warnings: string[] = [];
Expand Down
8 changes: 8 additions & 0 deletions sdk/typescript/tests-ts/api-preflight-config.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
import {
FIREWORKS_CODEX_PROVIDER,
OPENROUTER_CODEX_PROVIDER,
ORCAROUTER_CODEX_PROVIDER,
scanModelProvider,
writeCodexConfig,
type JsonObject,
Expand All @@ -32,6 +33,13 @@ const EXTERNAL_PROVIDER_CASES = [
"anthropic/claude-sonnet-4.5",
OPENROUTER_CODEX_PROVIDER,
],
[
"OrcaRouter",
"orcarouter",
"ORCAROUTER_API_KEY",
"orcarouter/fusion",
ORCAROUTER_CODEX_PROVIDER,
],
[
"Fireworks AI",
"fireworks",
Expand Down
29 changes: 23 additions & 6 deletions sdk/typescript/tests-ts/api.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ import {
import {
FIREWORKS_CODEX_PROVIDER,
OPENROUTER_CODEX_PROVIDER,
ORCAROUTER_CODEX_PROVIDER,
type JsonObject,
} from "../src/config.js";
import { estimateScanCost, type ScanCost } from "../src/cost.js";
Expand Down Expand Up @@ -194,6 +195,13 @@ const EXTERNAL_PROVIDER_CASES = [
"anthropic/claude-sonnet-4.5",
OPENROUTER_CODEX_PROVIDER,
],
[
"OrcaRouter",
"orcarouter",
"ORCAROUTER_API_KEY",
"orcarouter/fusion",
ORCAROUTER_CODEX_PROVIDER,
],
[
"Fireworks AI",
"fireworks",
Expand Down Expand Up @@ -1267,7 +1275,7 @@ describe("CodexSecurity orchestration", () => {
{
environment: {
OPENAI_API_KEY: "synthetic-openai-key",
[provider === "openrouter"
[provider === "openrouter" || provider === "orcarouter"
? "FIREWORKS_API_KEY"
: "OPENROUTER_API_KEY"]: "synthetic-competing-provider-key",
},
Expand Down Expand Up @@ -1299,7 +1307,9 @@ describe("CodexSecurity orchestration", () => {
let codexOptions: CodexOptions | null = null;
let authentication: ScanAuthentication | undefined;
const competingApiKey =
provider === "openrouter" ? "FIREWORKS_API_KEY" : "OPENROUTER_API_KEY";
provider === "openrouter" || provider === "orcarouter"
? "FIREWORKS_API_KEY"
: "OPENROUTER_API_KEY";
const environment = {
OPENAI_API_KEY: "synthetic-openai-key",
[competingApiKey]: "synthetic-competing-provider-key",
Expand Down Expand Up @@ -1394,6 +1404,7 @@ describe("CodexSecurity orchestration", () => {
OPENAI_API_KEY: "synthetic-openai-key",
CODEX_API_KEY: "synthetic-codex-key",
OPENROUTER_API_KEY: "synthetic-openrouter-key",
ORCAROUTER_API_KEY: "synthetic-orcarouter-key",
FIREWORKS_API_KEY: "synthetic-fireworks-key",
...credentials,
};
Expand Down Expand Up @@ -1470,6 +1481,7 @@ describe("CodexSecurity orchestration", () => {
"OPENAI_API_KEY",
"CODEX_API_KEY",
"OPENROUTER_API_KEY",
"ORCAROUTER_API_KEY",
"FIREWORKS_API_KEY",
]) {
expect((codexOptions as CodexOptions | null)?.env).not.toHaveProperty(
Expand Down Expand Up @@ -4971,10 +4983,15 @@ describe("CodexSecurity orchestration", () => {
[
["OPENAI_API_KEY", "gpt-5.6-sol", undefined],
["OPENROUTER_API_KEY", "anthropic/claude-sonnet-4.5", "openrouter"],
["ORCAROUTER_API_KEY", "orcarouter/fusion", "orcarouter"],
] as const
).map(async ([apiKey, model, provider], index) => {
const scanDir = join(root, `parallel-api-key-scan-${index}`);
await mkdir(scanDir, { mode: 0o700 });
const providerConfig =
provider === "openrouter"
? OPENROUTER_CODEX_PROVIDER
: ORCAROUTER_CODEX_PROVIDER;
return new TestClient(
{
pluginPath: PLUGIN_ROOT,
Expand All @@ -4985,7 +5002,7 @@ describe("CodexSecurity orchestration", () => {
: {
model_provider: provider,
model_providers: {
[provider]: OPENROUTER_CODEX_PROVIDER,
[provider]: providerConfig,
},
}),
},
Expand All @@ -5007,15 +5024,15 @@ describe("CodexSecurity orchestration", () => {
: {
model_provider: provider,
model_providers: {
[provider]: OPENROUTER_CODEX_PROVIDER,
[provider]: providerConfig,
},
}),
});
return {
startThread: () => ({
id: null,
async runStreamed() {
if (++scansStarted === 2) releaseScans();
if (++scansStarted === 3) releaseScans();
await concurrentScans;
throw new Error("parallel API-key scan reached");
},
Expand All @@ -5039,7 +5056,7 @@ describe("CodexSecurity orchestration", () => {
}),
});
}
expect(scansStarted).toBe(2);
expect(scansStarted).toBe(3);
} finally {
releaseScans();
await Promise.all(clients.map(async (client) => await client.close()));
Expand Down
38 changes: 35 additions & 3 deletions sdk/typescript/tests-ts/cli.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ import {
DEFAULT_CODEX_CONFIG,
FIREWORKS_CODEX_PROVIDER,
OPENROUTER_CODEX_PROVIDER,
ORCAROUTER_CODEX_PROVIDER,
scanModelConfiguration,
} from "../src/config.js";
import {
Expand Down Expand Up @@ -149,7 +150,13 @@ describe("CLI", () => {
enum: ["minimal", "low", "medium", "high", "xhigh", "max"],
},
provider: {
enum: ["openai", "openrouter", "fireworks", "amazon-bedrock"],
enum: [
"openai",
"openrouter",
"orcarouter",
"fireworks",
"amazon-bedrock",
],
},
failOnSeverity: { enum: ["critical", "high", "medium", "low"] },
patch: { type: "boolean" },
Expand Down Expand Up @@ -871,6 +878,12 @@ describe("CLI", () => {
"anthropic/claude-sonnet-4.5",
OPENROUTER_CODEX_PROVIDER,
],
[
"OrcaRouter",
"orcarouter",
"orcarouter/fusion",
ORCAROUTER_CODEX_PROVIDER,
],
[
"Fireworks AI",
"fireworks",
Expand Down Expand Up @@ -2357,7 +2370,7 @@ describe("CLI", () => {
);
expect(help.text()).toContain("--model <string>");
expect(help.text()).toContain(
"--provider <openai|openrouter|fireworks|amazon-bedrock>",
"--provider <openai|openrouter|orcarouter|fireworks|amazon-bedrock>",
);
expect(help.text()).toContain(
`OpenAI model to use (default: ${DEFAULT_SCAN_MODEL_CONFIGURATION.model}).`,
Expand Down Expand Up @@ -2429,7 +2442,7 @@ describe("CLI", () => {
expect(help.text()).not.toContain("--outputDir");
expect(help.text()).not.toContain("--maxAttempts");
expect(help.text()).toContain(
"--provider <openai|openrouter|fireworks|amazon-bedrock>",
"--provider <openai|openrouter|orcarouter|fireworks|amazon-bedrock>",
);
expect(stderr.text()).toBe("");
});
Expand Down Expand Up @@ -2485,6 +2498,13 @@ describe("CLI", () => {
"google/gemini-2.5-pro",
OPENROUTER_CODEX_PROVIDER,
],
[
"OrcaRouter",
"orcarouter",
"orcarouter/fusion",
"orcarouter/fusion-mini",
ORCAROUTER_CODEX_PROVIDER,
],
[
"Fireworks AI",
"fireworks",
Expand Down Expand Up @@ -2665,6 +2685,7 @@ describe("CLI", () => {
).toThrow("--effort conflicts with --codex model_reasoning_effort");
for (const provider of [
"openrouter",
"orcarouter",
"fireworks",
"amazon-bedrock",
] as const) {
Expand Down Expand Up @@ -2772,6 +2793,10 @@ describe("CLI", () => {
["scan", ".", "--provider", "openrouter"],
"--model is required when using --provider openrouter",
],
[
["scan", ".", "--provider", "orcarouter"],
"--model is required when using --provider orcarouter",
],
[
["scan", ".", "--provider", "fireworks"],
"--model is required when using --provider fireworks",
Expand Down Expand Up @@ -3283,6 +3308,13 @@ describe("CLI", () => {
OPENROUTER_CODEX_PROVIDER,
{ OPENROUTER_API_KEY: "synthetic-openrouter-key" },
],
[
"OrcaRouter",
"orcarouter",
"orcarouter/fusion",
ORCAROUTER_CODEX_PROVIDER,
{ ORCAROUTER_API_KEY: "synthetic-orcarouter-key" },
],
[
"Fireworks AI",
"fireworks",
Expand Down
3 changes: 3 additions & 0 deletions sdk/typescript/tests-ts/runtime.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,7 @@ describe("plugin runtime preparation", () => {
const providerKeys = [
"OPENAI_API_KEY",
"OPENROUTER_API_KEY",
"ORCAROUTER_API_KEY",
"FIREWORKS_API_KEY",
"AWS_BEARER_TOKEN_BEDROCK",
"AWS_ACCESS_KEY_ID",
Expand Down Expand Up @@ -4487,6 +4488,7 @@ describe("runtime directories and plugin Python boundary", () => {
"assert os.environ.get('OPENAI_API_KEY') is None",
"assert os.environ.get('CODEX_API_KEY') is None",
"assert os.environ.get('OPENROUTER_API_KEY') is None",
"assert os.environ.get('ORCAROUTER_API_KEY') is None",
"assert os.environ.get('FIREWORKS_API_KEY') is None",
"payload = sys.stdin.read()",
"print(json.dumps({'ok': True, 'label': '出力', 'inputLength': len(payload), 'details': 'x' * (5 * 1024 * 1024)}, ensure_ascii=False))",
Expand All @@ -4503,6 +4505,7 @@ describe("runtime directories and plugin Python boundary", () => {
OPENAI_API_KEY: "must-not-reach-python",
CODEX_API_KEY: "also-must-not-reach-python",
OPENROUTER_API_KEY: "openrouter-must-not-reach-python",
ORCAROUTER_API_KEY: "orcarouter-must-not-reach-python",
FIREWORKS_API_KEY: "fireworks-must-not-reach-python",
},
},
Expand Down