@cantinasecurity/apex-cli 0.1.15 → 0.1.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/skills/apex-cli/SKILL.md +1 -1
- package/.claude-plugin/marketplace.json +3 -3
- package/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/.mcp.claude.json +1 -1
- package/.mcp.codex.json +1 -1
- package/MARKETPLACE.md +1 -1
- package/README.md +9 -8
- package/dist/apex.js +1 -1
- package/dist/commands.js +16 -14
- package/dist/help.js +13 -2
- package/dist/session.js +6 -3
- package/package.json +1 -1
- package/skills/apex-cli/SKILL.md +1 -1
|
@@ -33,7 +33,7 @@ If the Apex MCP server is not configured, fall back to the local CLI:
|
|
|
33
33
|
|
|
34
34
|
- Prefer scripted commands over the interactive shell.
|
|
35
35
|
- Service-key management has no MCP tools. Never ask a user to paste a service key into a prompt, and never place one in an MCP call, tool result, source file, or log.
|
|
36
|
-
- If a user explicitly needs a new service key, tell them to run `apex login` if needed, then run `apex service-key create --
|
|
36
|
+
- If a user explicitly needs a new service key, tell them to run `apex login` if needed, then run `apex service-key create --name <name> --scope <scope[,scope]>` directly in their own terminal. Use only the required scopes: `scans:create` to trigger, `scans:read` to poll status, and `findings:read` to retrieve findings. A P1-only adapter can use `--scope scans:read,findings:read`; omitting `--scope` grants all three. The CLI automatically selects the company when only one is available and prompts when there are several; use `--company <id-or-handle>` to skip that prompt or resolve a non-interactive command. Service-key commands never initiate login, and the secret is shown once.
|
|
37
37
|
- For a headless HTTP adapter, read the service key from a secret-backed environment variable, send it as a bearer token to `/api/apex/v1`, and redact it from errors and logs. Do not shell out to `apex` or launch MCP from the adapter.
|
|
38
38
|
- Hosted API v1 requires the GitHub repository to be linked to an active Apex workspace. It never creates or mutates workspaces; use optional `workspaceId` to disambiguate matches and treat zero or ambiguous matches as HTTP `409` setup errors.
|
|
39
39
|
- Hosted scan triggers require `Idempotency-Key`. Reuse one stable key only for an identical transport/timeout/5xx retry. After correcting a definitive 4xx response, or for any new intended scan, use a new key.
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
},
|
|
7
7
|
"metadata": {
|
|
8
8
|
"description": "Cantina agent plugins for security review workflows.",
|
|
9
|
-
"version": "0.1.
|
|
9
|
+
"version": "0.1.16"
|
|
10
10
|
},
|
|
11
11
|
"plugins": [
|
|
12
12
|
{
|
|
@@ -14,10 +14,10 @@
|
|
|
14
14
|
"source": {
|
|
15
15
|
"source": "npm",
|
|
16
16
|
"package": "@cantinasecurity/apex-cli",
|
|
17
|
-
"version": "0.1.
|
|
17
|
+
"version": "0.1.16"
|
|
18
18
|
},
|
|
19
19
|
"description": "Run Apex security scans and review findings from Claude Code.",
|
|
20
|
-
"version": "0.1.
|
|
20
|
+
"version": "0.1.16",
|
|
21
21
|
"author": {
|
|
22
22
|
"name": "Cantina",
|
|
23
23
|
"email": "support@cantina.xyz"
|
package/.mcp.claude.json
CHANGED
package/.mcp.codex.json
CHANGED
package/MARKETPLACE.md
CHANGED
|
@@ -13,7 +13,7 @@ This package is prepared as both a Claude Code plugin and a Codex plugin. The pu
|
|
|
13
13
|
The plugin MCP configs launch the pinned npm CLI package with:
|
|
14
14
|
|
|
15
15
|
```bash
|
|
16
|
-
npx -y -p @cantinasecurity/apex-cli@0.1.
|
|
16
|
+
npx -y -p @cantinasecurity/apex-cli@0.1.16 apex-mcp
|
|
17
17
|
```
|
|
18
18
|
|
|
19
19
|
That keeps marketplace installs independent of a user's global `apex` install.
|
package/README.md
CHANGED
|
@@ -151,9 +151,9 @@ Supported shell commands:
|
|
|
151
151
|
- `apex doctor`
|
|
152
152
|
- `apex login`
|
|
153
153
|
- `apex logout`
|
|
154
|
-
- `apex service-key create --company <id-or-handle>
|
|
155
|
-
- `apex service-key list --company <id-or-handle> [--json]`
|
|
156
|
-
- `apex service-key revoke <key-id> --company <id-or-handle> [--json]`
|
|
154
|
+
- `apex service-key create --name <name> [--company <id-or-handle>] [--scope <scope[,scope]>] [--expires-at <ISO-8601>] [--json]`
|
|
155
|
+
- `apex service-key list [--company <id-or-handle>] [--json]`
|
|
156
|
+
- `apex service-key revoke <key-id> [--company <id-or-handle>] [--json]`
|
|
157
157
|
- `apex setup [all|codex|claude|copilot]`
|
|
158
158
|
- `apex telemetry [status|enable|disable]`
|
|
159
159
|
- `apex update`
|
|
@@ -175,12 +175,13 @@ An organization manager can create a service key with an existing Apex device-lo
|
|
|
175
175
|
|
|
176
176
|
```bash
|
|
177
177
|
apex service-key create \
|
|
178
|
-
--company acme \
|
|
179
178
|
--name internal-audit-hub \
|
|
180
179
|
--scope scans:create,scans:read,findings:read \
|
|
181
180
|
--expires-at 2027-01-01T00:00:00Z
|
|
182
181
|
```
|
|
183
182
|
|
|
183
|
+
The CLI automatically uses your company when only one is available. If you belong to multiple companies, it prompts you to choose one. Pass `--company <id-or-handle>` to skip that prompt, or when a non-interactive or `--json` command cannot prompt.
|
|
184
|
+
|
|
184
185
|
The secret is returned only by `create` and cannot be retrieved later. Store it directly in a secret manager and do not put it in source control, prompts, chat messages, or logs. `--json` also includes the shown-once secret so it can be captured programmatically; handle that output as a credential.
|
|
185
186
|
|
|
186
187
|
Service keys are organization-level credentials. Grant only the scopes the integration needs: `scans:create` triggers scans, `scans:read` polls status, and `findings:read` retrieves findings. Omit `--scope` to grant all three scopes. For a P1-only adapter that reads manually started scans, create a read-only key with `--scope scans:read,findings:read`.
|
|
@@ -190,8 +191,8 @@ A key with `findings:read` can retrieve unpublished draft findings for scans in
|
|
|
190
191
|
List metadata or revoke a key without exposing its secret:
|
|
191
192
|
|
|
192
193
|
```bash
|
|
193
|
-
apex service-key list
|
|
194
|
-
apex service-key revoke <key-id>
|
|
194
|
+
apex service-key list
|
|
195
|
+
apex service-key revoke <key-id>
|
|
195
196
|
```
|
|
196
197
|
|
|
197
198
|
Service-key management is intentionally available only through the scripted CLI. It is not exposed as an MCP tool or interactive-shell command, which keeps raw secrets out of model tool results and transcripts. The CLI requires an existing device-login bearer session, never initiates login from these commands, and never stores a created service key locally.
|
|
@@ -424,7 +425,7 @@ Telemetry event posts do not include bearer tokens. The telemetry endpoint is `P
|
|
|
424
425
|
{
|
|
425
426
|
"schemaVersion": 1,
|
|
426
427
|
"event": "apex.invocation.completed",
|
|
427
|
-
"cliVersion": "0.1.
|
|
428
|
+
"cliVersion": "0.1.16",
|
|
428
429
|
"invocation": {
|
|
429
430
|
"surface": "mcp",
|
|
430
431
|
"command": "apex-scan",
|
|
@@ -455,7 +456,7 @@ The npm package also includes marketplace-ready plugin artifacts:
|
|
|
455
456
|
- `.claude-plugin/plugin.json` and `.mcp.claude.json` for Claude Code plugin installs
|
|
456
457
|
- `.claude-plugin/marketplace.json` for a Claude marketplace entry backed by the public npm package
|
|
457
458
|
|
|
458
|
-
These plugin installs launch the pinned npm package with `npx -y -p @cantinasecurity/apex-cli@0.1.
|
|
459
|
+
These plugin installs launch the pinned npm package with `npx -y -p @cantinasecurity/apex-cli@0.1.16 apex-mcp`, so users do not need to install `apex` globally before enabling the plugin.
|
|
459
460
|
|
|
460
461
|
The repository also includes `.agents/plugins/marketplace.json` for local Codex marketplace testing from a checkout.
|
|
461
462
|
|
package/dist/apex.js
CHANGED
|
@@ -52,7 +52,7 @@ async function dispatch(parsed) {
|
|
|
52
52
|
await commandServiceKeyRevoke(client, parsed.flags, parsed.args[0] ?? "");
|
|
53
53
|
return;
|
|
54
54
|
}
|
|
55
|
-
throw new Error("Usage: apex service-key <create|list|revoke> [key-id] --company <id-or-handle>");
|
|
55
|
+
throw new Error("Usage: apex service-key <create|list|revoke> [key-id] [--company <id-or-handle>]");
|
|
56
56
|
case "update":
|
|
57
57
|
await commandUpdate(parsed.flags);
|
|
58
58
|
return;
|
package/dist/commands.js
CHANGED
|
@@ -42,9 +42,10 @@ const FIX_REVIEW_TARGETS = [
|
|
|
42
42
|
"pull_request",
|
|
43
43
|
"repository_current",
|
|
44
44
|
];
|
|
45
|
-
const SERVICE_KEY_CREATE_USAGE = "Usage: apex service-key create --company <id-or-handle>
|
|
46
|
-
const SERVICE_KEY_LIST_USAGE = "Usage: apex service-key list --company <id-or-handle> [--json]";
|
|
47
|
-
const SERVICE_KEY_REVOKE_USAGE = "Usage: apex service-key revoke <key-id> --company <id-or-handle> [--json]";
|
|
45
|
+
const SERVICE_KEY_CREATE_USAGE = "Usage: apex service-key create --name <name> [--company <id-or-handle>] [--scope <scope[,scope]>] [--expires-at <ISO-8601>] [--json]";
|
|
46
|
+
const SERVICE_KEY_LIST_USAGE = "Usage: apex service-key list [--company <id-or-handle>] [--json]";
|
|
47
|
+
const SERVICE_KEY_REVOKE_USAGE = "Usage: apex service-key revoke <key-id> [--company <id-or-handle>] [--json]";
|
|
48
|
+
const SERVICE_KEY_COMPANY_PROMPT = "Select the Apex company whose service keys you want to manage:";
|
|
48
49
|
const SERVICE_KEY_EXPIRATION_SCHEMA = z.string().datetime({ offset: true });
|
|
49
50
|
const SERVICE_KEY_NAME_CONTROL_CHARACTER_PATTERN = /[\u0000-\u001f\u007f-\u009f]/;
|
|
50
51
|
const SERVICE_KEY_NAME_SCHEMA = z
|
|
@@ -65,11 +66,6 @@ async function saveCompanyDefault(companyId) {
|
|
|
65
66
|
function formatCommandArgument(value) {
|
|
66
67
|
return /\s/.test(value) ? `"${value}"` : value;
|
|
67
68
|
}
|
|
68
|
-
function requireExplicitServiceKeyCompany(flags, usage) {
|
|
69
|
-
if (!getFlagString(flags, "company")) {
|
|
70
|
-
throw new Error(`${usage}\nService-key commands require an explicit --company.`);
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
69
|
async function requireExistingServiceKeySession(client) {
|
|
74
70
|
const me = await getMe(client);
|
|
75
71
|
if (!me) {
|
|
@@ -588,12 +584,14 @@ export async function commandLogout(client, flags) {
|
|
|
588
584
|
}
|
|
589
585
|
}
|
|
590
586
|
export async function commandServiceKeyCreate(client, flags) {
|
|
591
|
-
requireExplicitServiceKeyCompany(flags, SERVICE_KEY_CREATE_USAGE);
|
|
592
587
|
const name = normalizeServiceKeyName(getFlagString(flags, "name"));
|
|
593
588
|
const scopes = normalizeServiceKeyScopes(flags);
|
|
594
589
|
const expiresAt = normalizeServiceKeyExpiration(getFlagString(flags, "expires-at"));
|
|
595
590
|
const me = await requireExistingServiceKeySession(client);
|
|
596
|
-
const company = await chooseCompany(me, flags, null
|
|
591
|
+
const company = await chooseCompany(me, flags, null, {
|
|
592
|
+
prompt: SERVICE_KEY_COMPANY_PROMPT,
|
|
593
|
+
useSavedDefault: false,
|
|
594
|
+
});
|
|
597
595
|
const response = await withLoadingIndicator("Creating service key...", flags, () => createServiceApiKey(client, company.id, {
|
|
598
596
|
name,
|
|
599
597
|
expiresAt,
|
|
@@ -623,9 +621,11 @@ export async function commandServiceKeyCreate(client, flags) {
|
|
|
623
621
|
return payload;
|
|
624
622
|
}
|
|
625
623
|
export async function commandServiceKeyList(client, flags) {
|
|
626
|
-
requireExplicitServiceKeyCompany(flags, SERVICE_KEY_LIST_USAGE);
|
|
627
624
|
const me = await requireExistingServiceKeySession(client);
|
|
628
|
-
const company = await chooseCompany(me, flags, null
|
|
625
|
+
const company = await chooseCompany(me, flags, null, {
|
|
626
|
+
prompt: SERVICE_KEY_COMPANY_PROMPT,
|
|
627
|
+
useSavedDefault: false,
|
|
628
|
+
});
|
|
629
629
|
const response = await withLoadingIndicator("Loading service keys...", flags, () => listServiceApiKeys(client, company.id));
|
|
630
630
|
const payload = {
|
|
631
631
|
company,
|
|
@@ -653,13 +653,15 @@ export async function commandServiceKeyList(client, flags) {
|
|
|
653
653
|
return payload;
|
|
654
654
|
}
|
|
655
655
|
export async function commandServiceKeyRevoke(client, flags, requestedKeyId) {
|
|
656
|
-
requireExplicitServiceKeyCompany(flags, SERVICE_KEY_REVOKE_USAGE);
|
|
657
656
|
const keyId = requestedKeyId.trim();
|
|
658
657
|
if (!keyId) {
|
|
659
658
|
throw new Error(SERVICE_KEY_REVOKE_USAGE);
|
|
660
659
|
}
|
|
661
660
|
const me = await requireExistingServiceKeySession(client);
|
|
662
|
-
const company = await chooseCompany(me, flags, null
|
|
661
|
+
const company = await chooseCompany(me, flags, null, {
|
|
662
|
+
prompt: SERVICE_KEY_COMPANY_PROMPT,
|
|
663
|
+
useSavedDefault: false,
|
|
664
|
+
});
|
|
663
665
|
await withLoadingIndicator("Revoking service key...", flags, () => revokeServiceApiKey(client, company.id, keyId));
|
|
664
666
|
const payload = {
|
|
665
667
|
company,
|
package/dist/help.js
CHANGED
|
@@ -33,7 +33,7 @@ export const CLI_HELP_TEXT = `Usage:
|
|
|
33
33
|
apex connect gitlab Open the GitLab connection flow
|
|
34
34
|
|
|
35
35
|
Flags:
|
|
36
|
-
--company <id-or-handle>
|
|
36
|
+
--company <id-or-handle> Override the automatically selected Apex company
|
|
37
37
|
--name <name> Name a new service key
|
|
38
38
|
--scope <scope[,scope]> Limit a service key to scans:create, scans:read, and/or findings:read
|
|
39
39
|
--expires-at <ISO-8601> Set an optional service-key expiration time
|
|
@@ -73,7 +73,8 @@ Tips:
|
|
|
73
73
|
audit is the current scan mode for audit scans; ultra remains accepted as a legacy alias.
|
|
74
74
|
apex workspace use accepts a workspace name, prefix, or ID.
|
|
75
75
|
Finding comments, feedback, and fix review scans use the same Apex login credentials as other CLI commands.
|
|
76
|
-
Service-key commands require an existing login
|
|
76
|
+
Service-key commands require an existing login; run apex login first when needed.
|
|
77
|
+
Apex automatically selects your company when only one is available. With multiple companies, choose one interactively or pass --company.
|
|
77
78
|
Service-key creation also requires --name and reveals the secret only once; omit --scope to grant all hosted API scopes.
|
|
78
79
|
Service-key management is available only in the scripted CLI, not the interactive shell or MCP server.
|
|
79
80
|
Invalid finding feedback requires --dismissal-reason.
|
|
@@ -120,11 +121,21 @@ Commands:
|
|
|
120
121
|
/help Show this help
|
|
121
122
|
/exit Exit Apex
|
|
122
123
|
|
|
124
|
+
Scripted-only commands (run outside the interactive shell):
|
|
125
|
+
apex service-key create --name <name> [--company <id-or-handle>] [--scope <scope[,scope]>]
|
|
126
|
+
Create an organization service key (shown once)
|
|
127
|
+
apex service-key list [--company <id-or-handle>]
|
|
128
|
+
List service-key metadata
|
|
129
|
+
apex service-key revoke <key-id> [--company <id-or-handle>]
|
|
130
|
+
Revoke a service key
|
|
131
|
+
|
|
123
132
|
Tips:
|
|
124
133
|
PR scans require a GitHub PR number and provider-backed repository access.
|
|
125
134
|
audit is the current scan mode for audit scans; ultra remains accepted as a legacy alias.
|
|
126
135
|
/workspace use accepts a workspace name, prefix, or ID.
|
|
127
136
|
/findings comment, /findings feedback, and /findings fix-review use the current Apex login.
|
|
137
|
+
Service-key commands automatically use your company when only one is available. With multiple companies, choose one interactively or pass --company.
|
|
138
|
+
Service-key secrets are shown once. Run these commands in your terminal, not in the interactive shell or through MCP.
|
|
128
139
|
Invalid finding feedback requires a dismissal reason.
|
|
129
140
|
Fix review scans use saved Fix PR URLs when present; otherwise they review the current repository when Bedrock has original scan repository context. Use scripted CLI or MCP to supply Fix PR URLs directly.
|
|
130
141
|
Use scripted CLI flags for advanced feedback options such as suggested severity or dismissal reason.
|
package/dist/session.js
CHANGED
|
@@ -23,10 +23,13 @@ export function createWorkspaceBinding(result, currentBinding) {
|
|
|
23
23
|
lastScanUrl: preservedBinding?.lastScanUrl ?? null,
|
|
24
24
|
};
|
|
25
25
|
}
|
|
26
|
-
export async function chooseCompany(me, flags, binding) {
|
|
26
|
+
export async function chooseCompany(me, flags, binding, options = {}) {
|
|
27
27
|
const requested = getFlagString(flags, "company");
|
|
28
28
|
const config = await loadConfig();
|
|
29
|
-
const defaultRef = requested ??
|
|
29
|
+
const defaultRef = requested ??
|
|
30
|
+
(options.useSavedDefault === false
|
|
31
|
+
? null
|
|
32
|
+
: (binding?.companyId ?? config.defaultCompanyId ?? null));
|
|
30
33
|
if (defaultRef) {
|
|
31
34
|
const matched = me.companies.find((company) => company.id === defaultRef ||
|
|
32
35
|
(company.handle ?? "").toLowerCase() === defaultRef.toLowerCase());
|
|
@@ -46,7 +49,7 @@ export async function chooseCompany(me, flags, binding) {
|
|
|
46
49
|
if (!canPromptInteractively(flags)) {
|
|
47
50
|
throw new Error("Multiple companies available. Re-run with --company <id-or-handle>.");
|
|
48
51
|
}
|
|
49
|
-
const selected = await chooseOne("Select the Apex company to use for this directory:", me.companies.map((company) => ({
|
|
52
|
+
const selected = await chooseOne(options.prompt ?? "Select the Apex company to use for this directory:", me.companies.map((company) => ({
|
|
50
53
|
label: company.handle
|
|
51
54
|
? `${company.name ?? company.id} (${company.handle})`
|
|
52
55
|
: (company.name ?? company.id),
|
package/package.json
CHANGED
package/skills/apex-cli/SKILL.md
CHANGED
|
@@ -31,7 +31,7 @@ Guidelines:
|
|
|
31
31
|
|
|
32
32
|
- Do not rely on interactive CLI prompts. The MCP tools are intentionally non-interactive.
|
|
33
33
|
- Service-key management has no MCP tools. Never ask a user to paste a service key into a prompt, and never place one in an MCP call, tool result, source file, or log.
|
|
34
|
-
- If a user explicitly needs a new service key, tell them to run `apex login` if needed, then run `apex service-key create --
|
|
34
|
+
- If a user explicitly needs a new service key, tell them to run `apex login` if needed, then run `apex service-key create --name <name> --scope <scope[,scope]>` directly in their own terminal. Use only the required scopes: `scans:create` to trigger, `scans:read` to poll status, and `findings:read` to retrieve findings. A P1-only adapter can use `--scope scans:read,findings:read`; omitting `--scope` grants all three. The CLI automatically selects the company when only one is available and prompts when there are several; use `--company <id-or-handle>` to skip that prompt or resolve a non-interactive command. Service-key commands never initiate login, and the secret is shown once.
|
|
35
35
|
- For a headless HTTP adapter, read the service key from a secret-backed environment variable, send it as a bearer token to `/api/apex/v1`, and redact it from errors and logs. Do not shell out to `apex` or launch MCP from the adapter.
|
|
36
36
|
- Hosted API v1 requires the GitHub repository to be linked to an active Apex workspace. It never creates or mutates workspaces; use optional `workspaceId` to disambiguate matches and treat zero or ambiguous matches as HTTP `409` setup errors.
|
|
37
37
|
- Hosted scan triggers require `Idempotency-Key`. Reuse one stable key only for an identical transport/timeout/5xx retry. After correcting a definitive 4xx response, or for any new intended scan, use a new key.
|