@cantinasecurity/apex-cli 0.1.41 → 0.1.42
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-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 -9
- package/dist/code-access.js +8 -3
- package/dist/commands.js +56 -0
- package/package.json +1 -1
|
@@ -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.42"
|
|
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.42"
|
|
18
18
|
},
|
|
19
19
|
"description": "Run Apex security scans and review findings from Claude Code.",
|
|
20
|
-
"version": "0.1.
|
|
20
|
+
"version": "0.1.42",
|
|
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.42 apex-mcp
|
|
17
17
|
```
|
|
18
18
|
|
|
19
19
|
That keeps marketplace installs independent of a user's global `apex` install.
|
package/README.md
CHANGED
|
@@ -158,30 +158,30 @@ unclaimed local trial proof so `apex claim` can still use it.
|
|
|
158
158
|
Agent-safe copy/paste prompt (substitute the issued code):
|
|
159
159
|
|
|
160
160
|
```text
|
|
161
|
-
Install and run Cantina Apex CLI 0.1.
|
|
161
|
+
Install and run Cantina Apex CLI 0.1.42 to start one free standard security scan of this repository immediately.
|
|
162
162
|
|
|
163
163
|
Use this trial code only where <TRIAL_CODE> appears below: SPBT-DEVREL-XXXXXXXX
|
|
164
164
|
|
|
165
165
|
Run the commands yourself from the repository root; do not stop after setup or only explain the steps. Use terminal CLI commands only. Do not use an Apex MCP tool or run `apex setup`, `apex mcp`, `apex login`, `apex-auth-start`, or `apex-auth-wait`. Never use `--replace` or `--force`.
|
|
166
166
|
|
|
167
167
|
Use this exact command prefix for every Apex command:
|
|
168
|
-
npx -y -p @cantinasecurity/apex-cli@0.1.
|
|
168
|
+
npx -y -p @cantinasecurity/apex-cli@0.1.42 apex
|
|
169
169
|
|
|
170
170
|
1. Before redeeming, confirm every command will use the same persistent, user-accessible machine or container, HOME, filesystem, and repository root. Confirm Node.js 20+ and npx are available. If `APEX_BASE_URL` is set, continue only when its origin is `https://ai.cantina.xyz`. Run the exact prefix with `--help` and continue only if help lists both `redeem` and `claim`. If any check fails, STOP before sending the code; do not install system packages.
|
|
171
171
|
2. Redeem, with at most one response-loss recovery retry:
|
|
172
|
-
npx -y -p @cantinasecurity/apex-cli@0.1.
|
|
172
|
+
npx -y -p @cantinasecurity/apex-cli@0.1.42 apex redeem <TRIAL_CODE> --json
|
|
173
173
|
This terminal command is the only allowed tool call containing the code. Never echo or restate the code, store it in a file or environment variable, put it in a URL or MCP call, or include it in your response. Parse the JSON from stdout only; do not merge stderr into stdout, because npm may write warnings to stderr. Continue when the command exits 0 and stdout is JSON with `redeemed: true` and `outcome` equal to `redeemed` or `idempotent`. `idempotent` means the existing guest session resumed and it may already own a workspace. If and only if the first attempt ends with an ambiguous transport/connection failure or an interrupted/missing response and no typed server error code, retry this exact same command once on the same machine, HOME, and repository root. Never retry a typed server rejection and never make more than one recovery retry. If the allowed retry does not return a successful JSON outcome, STOP.
|
|
174
174
|
3. Immediately start exactly one scan; do not wait for another instruction:
|
|
175
|
-
npx -y -p @cantinasecurity/apex-cli@0.1.
|
|
175
|
+
npx -y -p @cantinasecurity/apex-cli@0.1.42 apex scan --mode standard --non-interactive --no-open --json
|
|
176
176
|
The CLI validates the complete guest local archive before it reserves a workspace. An unbound renamed checkout may recover only the sole empty guest workspace when no explicit workspace name was supplied. On any error or nonzero exit, STOP. After a successful scan JSON response, inspect `localPersistence.workspaceBindingSaved`. If it is `false`, the server scan already started: capture and report `binding.lastScanId`, then STOP; do not run status or findings until local repository permissions are repaired. Otherwise capture `binding.lastScanId` from the JSON as <SCAN_ID>; if it is absent, STOP.
|
|
177
177
|
4. About every 60 seconds, poll that exact scan:
|
|
178
|
-
npx -y -p @cantinasecurity/apex-cli@0.1.
|
|
178
|
+
npx -y -p @cantinasecurity/apex-cli@0.1.42 apex status --scan <SCAN_ID> --json
|
|
179
179
|
Read `progress.progress.status`. Wait while it is `created`, `pending`, `queued`, `starting`, `running`, `in_progress`, or `processing`. Continue on `completed`. STOP on `failed`, `cancelled`, a missing or unknown status, or a command failure.
|
|
180
180
|
5. Fetch that exact scan's findings:
|
|
181
|
-
npx -y -p @cantinasecurity/apex-cli@0.1.
|
|
181
|
+
npx -y -p @cantinasecurity/apex-cli@0.1.42 apex findings --scan <SCAN_ID> --json
|
|
182
182
|
On success, summarize findings by severity, most severe first, including identifier, title, status, and confidence. This command does not return file locations, so do not invent them. On failure, STOP.
|
|
183
183
|
6. Do not claim automatically. Tell me the claim deadline from step 2 and remind me to run this exact command myself from the same repository root, machine, and HOME:
|
|
184
|
-
npx -y -p @cantinasecurity/apex-cli@0.1.
|
|
184
|
+
npx -y -p @cantinasecurity/apex-cli@0.1.42 apex claim
|
|
185
185
|
It may open a browser so I can create or sign in to a Cantina account and keep the results. Claim moves the trial into the currently selected Cantina workspace when I can run scans there, or into my existing personal workspace. If the selected workspace cannot run scans, tell me to switch to one that can and retry before the claim deadline.
|
|
186
186
|
|
|
187
187
|
Never paste raw command transcripts. For any failure, report only the step, exit code, and sanitized error. Redact the trial code and every `apex_at_`, `apex_rt_`, or `apex_ct_` value from all reports.
|
|
@@ -687,7 +687,7 @@ Telemetry event posts do not include bearer tokens. The telemetry endpoint is `P
|
|
|
687
687
|
{
|
|
688
688
|
"schemaVersion": 1,
|
|
689
689
|
"event": "apex.invocation.completed",
|
|
690
|
-
"cliVersion": "0.1.
|
|
690
|
+
"cliVersion": "0.1.42",
|
|
691
691
|
"invocation": {
|
|
692
692
|
"surface": "mcp",
|
|
693
693
|
"command": "apex-scan",
|
|
@@ -718,7 +718,7 @@ The npm package also includes marketplace-ready plugin artifacts:
|
|
|
718
718
|
- `.claude-plugin/plugin.json` and `.mcp.claude.json` for Claude Code plugin installs
|
|
719
719
|
- `.claude-plugin/marketplace.json` for a Claude marketplace entry backed by the public npm package
|
|
720
720
|
|
|
721
|
-
These plugin installs launch the pinned npm package with `npx -y -p @cantinasecurity/apex-cli@0.1.
|
|
721
|
+
These plugin installs launch the pinned npm package with `npx -y -p @cantinasecurity/apex-cli@0.1.42 apex-mcp`, so users do not need to install `apex` globally before enabling the plugin.
|
|
722
722
|
|
|
723
723
|
The repository also includes `.agents/plugins/marketplace.json` for local Codex marketplace testing from a checkout.
|
|
724
724
|
|
package/dist/code-access.js
CHANGED
|
@@ -20,13 +20,18 @@ Internal employee beta:
|
|
|
20
20
|
apex triage findings --workspace-ref <id>
|
|
21
21
|
apex triage review <finding-id> --workspace-ref <id> --expected-updated-at <timestamp from findings> --validity valid|partially_valid|invalid [--severity high] [--note <rationale>] [--request-id <uuid>]
|
|
22
22
|
apex triage edit <finding-id> --workspace-ref <id> --file <edit.json> --expected-updated-at <timestamp from findings> [--request-id <uuid>]
|
|
23
|
-
Edit JSON: {"action":"comment","content":"..."}
|
|
24
|
-
{"action":"review","validity":"valid","severity":"high","note":"..."}, or {"action":"cvss","vector":"CVSS:3.1/...","note":"..."}.
|
|
23
|
+
Edit JSON supports comments only: {"action":"comment","content":"..."}.
|
|
25
24
|
`;
|
|
26
25
|
export function hasCodeAccess(me) {
|
|
27
26
|
return Boolean(me?.user.triageWorkspaces?.length);
|
|
28
27
|
}
|
|
29
28
|
const uuid = z.string().uuid();
|
|
29
|
+
const commentEdit = z
|
|
30
|
+
.object({
|
|
31
|
+
action: z.literal("comment"),
|
|
32
|
+
content: z.string().trim().min(1).max(10_000),
|
|
33
|
+
})
|
|
34
|
+
.strict();
|
|
30
35
|
const quote = (s) => `'${s.replaceAll("'", "'\\''")}'`;
|
|
31
36
|
export function createSsmPluginOutputFilter() {
|
|
32
37
|
const prefix = Buffer.from("\nStarting session with SessionId:");
|
|
@@ -429,7 +434,7 @@ export async function commandInternalTriage(client, flags, action, id) {
|
|
|
429
434
|
const findingId = uuid.parse(id).toLowerCase(), file = getFlagString(flags, "file");
|
|
430
435
|
if (!file)
|
|
431
436
|
throw new Error("--file is required");
|
|
432
|
-
const edit = JSON.parse(readFileSync(file, "utf8"));
|
|
437
|
+
const edit = commentEdit.parse(JSON.parse(readFileSync(file, "utf8")));
|
|
433
438
|
const expectedUpdatedAt = z
|
|
434
439
|
.string()
|
|
435
440
|
.datetime({ offset: true })
|
package/dist/commands.js
CHANGED
|
@@ -23,6 +23,45 @@ import { commandUpdate as runCliUpdate } from "./update.js";
|
|
|
23
23
|
import { mkdir, writeFile } from "node:fs/promises";
|
|
24
24
|
import path from "node:path";
|
|
25
25
|
import { z } from "zod";
|
|
26
|
+
function getRequestedWorkspaceSrDoctorPayload(me, flags) {
|
|
27
|
+
const requested = getFlagString(flags, "company")?.trim().toLowerCase();
|
|
28
|
+
if (!requested)
|
|
29
|
+
return null;
|
|
30
|
+
if (me.companies.some((company) => company.id.toLowerCase() === requested ||
|
|
31
|
+
company.handle?.toLowerCase() === requested)) {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
const triageWorkspaces = me.user.triageWorkspaces ?? [];
|
|
35
|
+
const scopes = triageWorkspaces.filter((workspace) => workspace.companyId.toLowerCase() === requested ||
|
|
36
|
+
workspace.companyHandle?.toLowerCase() === requested);
|
|
37
|
+
if (scopes.length === 0) {
|
|
38
|
+
const companyIdsMissingHandles = [
|
|
39
|
+
...new Set(triageWorkspaces
|
|
40
|
+
.filter((workspace) => !workspace.companyHandle)
|
|
41
|
+
.map((workspace) => workspace.companyId)),
|
|
42
|
+
];
|
|
43
|
+
if (companyIdsMissingHandles.length > 0) {
|
|
44
|
+
throw new Error(`Workspace SR access metadata is missing a company handle. Retry with --company ${companyIdsMissingHandles.join(" or --company ")}.`);
|
|
45
|
+
}
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
const company = scopes[0];
|
|
49
|
+
const workspaceIds = [...new Set(scopes.map((scope) => scope.workspaceId))];
|
|
50
|
+
return {
|
|
51
|
+
authenticatedAs: me.user.username ?? me.user.id,
|
|
52
|
+
accessMode: "workspace_sr",
|
|
53
|
+
company: {
|
|
54
|
+
id: company.companyId,
|
|
55
|
+
name: company.companyName ?? null,
|
|
56
|
+
handle: company.companyHandle ?? null,
|
|
57
|
+
},
|
|
58
|
+
workspaces: workspaceIds.map((workspaceId) => ({
|
|
59
|
+
workspaceId,
|
|
60
|
+
findingsCommand: `apex triage findings --workspace-ref ${workspaceId}`,
|
|
61
|
+
createSandboxCommand: `apex code-access create --workspace-ref ${workspaceId}`,
|
|
62
|
+
})),
|
|
63
|
+
};
|
|
64
|
+
}
|
|
26
65
|
const WORKSPACE_USE_PATTERN = "<workspace-name|workspace-prefix|workspace-id>";
|
|
27
66
|
const WORKSPACE_BINDING_GUIDANCE = "Run `apex workspaces` to list workspace names, prefixes, and IDs, then bind one with `apex workspace use \"<workspace name>\"`, or run `apex scan` to create or resolve one automatically.";
|
|
28
67
|
const WORKSPACE_SELECTION_GUIDANCE = "Run `apex workspaces` to list available workspace names, prefixes, and IDs, then retry `apex workspace use \"<workspace name>\"`.";
|
|
@@ -1005,6 +1044,22 @@ export async function commandDoctor(client, cwd, flags) {
|
|
|
1005
1044
|
const me = existingMe ?? (await ensureAuthenticated(client, flags));
|
|
1006
1045
|
const authState = await getCommandAuthState(client, me);
|
|
1007
1046
|
setTelemetryAuthKind(authState.authKind);
|
|
1047
|
+
const workspaceSrPayload = getRequestedWorkspaceSrDoctorPayload(me, flags);
|
|
1048
|
+
if (workspaceSrPayload) {
|
|
1049
|
+
if (isJsonMode(flags)) {
|
|
1050
|
+
printJson(workspaceSrPayload);
|
|
1051
|
+
return workspaceSrPayload;
|
|
1052
|
+
}
|
|
1053
|
+
logLine(`Authenticated as ${workspaceSrPayload.authenticatedAs}`, flags);
|
|
1054
|
+
logLine(`Company: ${workspaceSrPayload.company.handle ?? workspaceSrPayload.company.id}`, flags);
|
|
1055
|
+
logLine("Access: workspace-scoped SR", flags);
|
|
1056
|
+
for (const workspace of workspaceSrPayload.workspaces) {
|
|
1057
|
+
logLine(`Workspace: ${workspace.workspaceId}`, flags);
|
|
1058
|
+
logLine(`Findings: ${workspace.findingsCommand}`, flags);
|
|
1059
|
+
logLine(`Create sandbox: ${workspace.createSandboxCommand}`, flags);
|
|
1060
|
+
}
|
|
1061
|
+
return workspaceSrPayload;
|
|
1062
|
+
}
|
|
1008
1063
|
const result = await withLoadingIndicator("Inspecting this directory and checking Apex setup...", flags, async () => {
|
|
1009
1064
|
if (authState.authKind === "guest") {
|
|
1010
1065
|
const selection = await selectWorkspaceTarget(cwd, me, flags);
|
|
@@ -1015,6 +1070,7 @@ export async function commandDoctor(client, cwd, flags) {
|
|
|
1015
1070
|
const localSnapshotCount = result.resolve.plannedSources.filter((source) => source.sourceKind === "local_archive").length;
|
|
1016
1071
|
const payload = {
|
|
1017
1072
|
authenticatedAs: me.user.username ?? me.user.id,
|
|
1073
|
+
accessMode: "company",
|
|
1018
1074
|
company: result.company,
|
|
1019
1075
|
workspaceName: result.workspaceName,
|
|
1020
1076
|
binding: result.binding,
|