@cantinasecurity/apex-cli 0.1.36 → 0.1.38
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 +17 -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 +63 -13
- package/dist/apex.js +7 -1
- package/dist/api-client.js +19 -9
- package/dist/args.js +14 -0
- package/dist/commands.js +85 -39
- package/dist/findings.js +6 -1
- package/dist/help.js +15 -4
- package/dist/local-source-scan.js +25 -5
- package/dist/mcp.js +23 -4
- package/dist/repo-discovery.js +69 -16
- package/dist/session.js +24 -4
- package/dist/shell.js +15 -8
- package/dist/telemetry.js +2 -2
- package/dist/threat-model.js +21 -3
- package/dist/types.js +4 -1
- package/package.json +9 -8
- package/skills/apex-cli/SKILL.md +17 -1
|
@@ -13,7 +13,7 @@ This skill is bundled with Apex CLI and can be installed into the current reposi
|
|
|
13
13
|
|
|
14
14
|
Use Apex through the MCP server when the `apex-*` tools are available. The tools installed by `apex setup` use the local stdio server and device login.
|
|
15
15
|
|
|
16
|
-
For a production MCP client that does not need local filesystem access, use the hosted Streamable HTTP endpoint at `https://ai.cantina.xyz/mcp`. Read the organization service key from `APEX_SERVICE_KEY` and supply it only as `Authorization: Bearer <TOKEN>`; never put the raw key in a tool argument, result, prompt, source file, or log. The hosted tools are `apex-credits`, `apex-workspaces`, `apex-scan`, `apex-black-start`, `apex-status`, `apex-cancel-scan`, `apex-findings`, `apex-workspace-findings`, `apex-finding-tickets`, `apex-finding-link-ticket`, and `apex-finding-
|
|
16
|
+
For a production MCP client that does not need local filesystem access, use the hosted Streamable HTTP endpoint at `https://ai.cantina.xyz/mcp`. Read the organization service key from `APEX_SERVICE_KEY` and supply it only as `Authorization: Bearer <TOKEN>`; never put the raw key in a tool argument, result, prompt, source file, or log. The hosted tools are `apex-credits`, `apex-workspaces`, `apex-scan`, `apex-black-start`, `apex-status`, `apex-cancel-scan`, `apex-findings`, `apex-workspace-findings`, `apex-finding-tickets`, `apex-finding-link-ticket`, `apex-finding-send`, and `apex-finding-status`.
|
|
17
17
|
|
|
18
18
|
OAuth-capable clients may instead use the hosted endpoint’s OAuth discovery and Cantina manager-consent flow. Let the client manage OAuth credentials; never pass them to tools, the REST API, or local CLI. Local stdio MCP continues to use device login.
|
|
19
19
|
|
|
@@ -153,3 +153,19 @@ Keep `policy.version` at `3.1`. Supply a name, instructions, and all eight metri
|
|
|
153
153
|
allowed value and nonempty guidance for each allowed value. Omit disallowed
|
|
154
154
|
values. If Audit Setup or another workspace update is active, wait for it to
|
|
155
155
|
finish before saving again.
|
|
156
|
+
|
|
157
|
+
Scan target and recovery rules:
|
|
158
|
+
- CLI `apex scan [target...]` and repeatable `--repo` accept exact files, directories, or HTTPS GitHub/GitLab repository URLs; local MCP uses `repoPaths` for the same selections. No target means `cwd`.
|
|
159
|
+
- A file selection scans only that file, respecting ignore rules. Local snapshots exclude `.apex/` state. Remote URLs use the default branch and cannot fall back to uploading `cwd`; clone locally to choose a branch or commit.
|
|
160
|
+
- Use Standard for local changes, files, plain directories, and public repositories without provider access. Lite and Audit still require connected provider sources; do not silently substitute another mode.
|
|
161
|
+
- Unauthenticated headless CLI commands fail with explicit login guidance; they never start device login. Use the MCP auth tools for explicit MCP login. Retry transient refresh failures without clearing credentials.
|
|
162
|
+
- Audit Setup is an explicit Standard/Audit beta flow using saved workspace repositories and context. It does not preserve local targets. Never automatically substitute setup after `AUDIT_SETUP_REQUIRED`; use the error's selected-workspace command only when that saved scope is intended. Draft operations require the ID returned by preparation; eligible accounts receive setup commands in CLI help. Lite and PR do not use `--setup`.
|
|
163
|
+
|
|
164
|
+
Finding lifecycle:
|
|
165
|
+
- Close resolved issues with `apex-finding-status` and `status: "fixed"`; reopen with `status: "open"`.
|
|
166
|
+
- Local MCP takes `findingRef`, optional `cwd`/`scanId`, `note`, and `expectedUpdatedAt`, using device login. Hosted MCP takes `findingId` (UUID), no local paths, and requires explicit `findings:write`.
|
|
167
|
+
- The scripted equivalent is `apex findings status <finding-ref> <open|fixed> --note "rationale" --json`. Other statuses: acknowledged, in_review, pending_merge, false_positive, wont_fix, skipped.
|
|
168
|
+
- Status changes preserve scanner validity, severity, feedback, and linked ticket state. Invalid or duplicate findings cannot be reopened into the open queue until their classification is reviewed.
|
|
169
|
+
- Read `remediationStatus` in workspace finding lists for the current lifecycle; `status` remains the scanner verdict. `open` and `acknowledged` stay distinct even though both use the `accepted` review state.
|
|
170
|
+
- Use the finding updatedAt as expectedUpdatedAt when working from a prior snapshot. On FINDING_STATUS_CONFLICT, reload and review before retrying.
|
|
171
|
+
- Linear sync is Linear-to-Apex: started becomes acknowledged, completed becomes fixed. Do not promise Apex-to-Linear status updates or automatic Linear reopening.
|
|
@@ -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.38"
|
|
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.38"
|
|
18
18
|
},
|
|
19
19
|
"description": "Run Apex security scans and review findings from Claude Code.",
|
|
20
|
-
"version": "0.1.
|
|
20
|
+
"version": "0.1.38",
|
|
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.38 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.38 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.38 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.38 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.38 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.38 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.38 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.38 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.
|
|
@@ -222,6 +222,7 @@ Supported shell commands:
|
|
|
222
222
|
- `/findings send <finding-ref> <linear|jira>`
|
|
223
223
|
- `/findings link-ticket <finding-ref> <linear|jira> <issue-key|url>`
|
|
224
224
|
- `/findings comment <finding-id|finding-identifier> <comment>`
|
|
225
|
+
- `/findings status <finding-ref> <open|fixed|acknowledged|in_review|pending_merge|false_positive|wont_fix|skipped> [note]`
|
|
225
226
|
- `/findings feedback <finding-id|finding-identifier> valid [comment]`
|
|
226
227
|
- `/findings feedback <finding-id|finding-identifier> invalid <false-positive|by-design|not-relevant> [comment]`
|
|
227
228
|
- `/findings fix-review <finding-id|finding-identifier>`
|
|
@@ -260,6 +261,7 @@ Supported shell commands:
|
|
|
260
261
|
- `apex findings send <finding-ref> <linear|jira> [--scan <scan-id>] [--json]`
|
|
261
262
|
- `apex findings link-ticket <finding-ref> <linear|jira> <issue-key|url> [--scan <scan-id>] [--json]`
|
|
262
263
|
- `apex findings comment <finding-id|finding-identifier> --content <markdown> [--parent-comment <comment-id>] [--scan <scan-id>]`
|
|
264
|
+
- `apex findings status <finding-ref> <open|fixed|acknowledged|in_review|pending_merge|false_positive|wont_fix|skipped> [--note <rationale>] [--expected-updated-at <timestamp>] [--scan <scan-id>]`
|
|
263
265
|
- `apex findings feedback <finding-id|finding-identifier> <valid|invalid> [comment] [--comment <markdown>] [--scan <scan-id>] [--suggested-severity extreme|critical|high|medium|low|informational] [--dismissal-reason false-positive|by-design|not-relevant] [--label acknowledged|fixed] [--fix-pr-url <github-pr-url>]`
|
|
264
266
|
- `apex findings fix-review <finding-id|finding-identifier> [--scan <scan-id>] [--review-target pull-request|repository-current] [--fix-pr-url <github-pr-url>]`
|
|
265
267
|
- `apex export findings [--scan <scan-id>] [--format markdown|json|gitlab-sast] [--output <path>]`
|
|
@@ -310,7 +312,7 @@ The CLI automatically uses your company when only one is available. If you belon
|
|
|
310
312
|
|
|
311
313
|
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.
|
|
312
314
|
|
|
313
|
-
Service keys are organization-level credentials. Grant only the scopes the integration needs: `scans:create` reads credit availability and triggers scans, `scans:read` polls status, and `findings:read` retrieves findings. Omit `--scope` to grant those three default scopes.
|
|
315
|
+
Service keys are organization-level credentials. Grant only the scopes the integration needs: `scans:create` reads credit availability and triggers scans, `scans:read` polls status, and `findings:read` retrieves findings. Omit `--scope` to grant those three default scopes. Finding status changes and ticket management additionally require explicitly requesting `findings:write`. For a P1-only adapter that reads manually started scans, create a read-only key with `--scope scans:read,findings:read`.
|
|
314
316
|
|
|
315
317
|
A key with `findings:read` can retrieve unpublished draft findings for scans in its organization so an external hub can perform triage. Give the key only to systems that are allowed to handle those drafts.
|
|
316
318
|
|
|
@@ -356,7 +358,7 @@ Claude Code can expand the same environment variable from `.mcp.json`:
|
|
|
356
358
|
}
|
|
357
359
|
```
|
|
358
360
|
|
|
359
|
-
The hosted server exposes `apex-credits`, `apex-workspaces`, `apex-scan`, `apex-black-start`, `apex-status`, `apex-cancel-scan`, `apex-findings`, `apex-workspace-findings`, `apex-finding-tickets`, `apex-finding-link-ticket`, and `apex-finding-
|
|
361
|
+
The hosted server exposes `apex-credits`, `apex-workspaces`, `apex-scan`, `apex-black-start`, `apex-status`, `apex-cancel-scan`, `apex-findings`, `apex-workspace-findings`, `apex-finding-tickets`, `apex-finding-link-ticket`, `apex-finding-send`, and `apex-finding-status`. Ticket creation and linking require an explicit `findings:write` scope and a connected Linear/Jira integration. Hosted operations are scoped to the authenticated organization. Repository scans use connected GitHub repositories; Apex Black scans use explicitly authorized HTTPS origins. Use local stdio MCP for device auth, local filesystem/archive access, provider connection, setup, comments, feedback, fix reviews, and trial flows.
|
|
360
362
|
|
|
361
363
|
Apex Black is hosted-only: use `apex-black-start` when the connected deployment advertises it and the organization has Apex Black beta access. Check `apex-credits` first. Supply 1–16 exact HTTPS origins, a stable `idempotencyKey`, and `authorizationAccepted: true` only when the user has authorized every target and accepted the 200 Standard credit cost; honor authorization already provided in the session. Use the returned Bedrock `scanId` with `apex-status` and `apex-findings`. An identical retry replays a stored start failure, including `APEX_BLACK_START_FAILED`; retain its durable IDs and contact support before trying another request. Local CLI and stdio MCP do not expose this web-scan tool or a Black scan mode.
|
|
362
364
|
|
|
@@ -466,7 +468,18 @@ Local MCP exposes `apex-finding-link-ticket` with `findingRef`, `provider`, `iss
|
|
|
466
468
|
|
|
467
469
|
## Local Source Scans
|
|
468
470
|
|
|
469
|
-
`apex scan`
|
|
471
|
+
`apex scan [target...]` accepts files, directories, and HTTPS GitHub/GitLab repository URLs. With no targets, it scans the current working directory:
|
|
472
|
+
|
|
473
|
+
```bash
|
|
474
|
+
apex scan . --mode standard
|
|
475
|
+
apex scan ./contracts --mode standard
|
|
476
|
+
apex scan ./contracts/UniswapV2ERC20.sol --mode standard
|
|
477
|
+
apex scan https://github.com/Uniswap/v2-core --mode standard
|
|
478
|
+
```
|
|
479
|
+
|
|
480
|
+
A file target uploads only that file, never its siblings. Local snapshots include current local changes and honor Git ignore rules and `.apexignore`; Apex state under `.apex/` is excluded. Explicit remote URLs use the default branch and never fall back to uploading the current checkout. Use a local clone to scan a particular remote branch or commit.
|
|
481
|
+
|
|
482
|
+
Source planning:
|
|
470
483
|
|
|
471
484
|
- clean GitHub or GitLab checkouts can stay on the remote-materialization path
|
|
472
485
|
- dirty git worktrees fall back to a local snapshot upload by default
|
|
@@ -474,18 +487,22 @@ Local MCP exposes `apex-finding-link-ticket` with `findingRef`, `provider`, `iss
|
|
|
474
487
|
|
|
475
488
|
Useful flags:
|
|
476
489
|
|
|
477
|
-
- `--repo <path>`
|
|
490
|
+
- `--repo <path-or-url>` as a repeatable alternative to positional targets
|
|
478
491
|
- `--source-mode auto|remote|local` to control remote-first fallback behavior
|
|
479
492
|
- `--mode standard|audit|lite|pr` to choose the scan mode
|
|
480
493
|
- `--pr <number>` to select one or more GitHub pull requests for `--mode pr`
|
|
481
494
|
- `--pr <number:path,path>` or `--pr-path <path>` to limit a PR scan to changed paths
|
|
482
495
|
|
|
496
|
+
Unauthenticated `--json`, `--non-interactive`, and non-TTY commands fail immediately with `apex login` guidance. Run login explicitly before automation. Temporary refresh/network failures preserve credentials; retry after the service recovers. Provider connection commands return URLs without opening browsers in automation.
|
|
497
|
+
|
|
483
498
|
`auto` is the default. `remote` requires Apex to materialize from a remote repository. `local` forces a local snapshot upload even when a clean remote path is available.
|
|
484
499
|
|
|
485
500
|
Audit scans use `audit` as the scan mode and still require provider-backed GitHub or GitLab repositories that Apex can materialize remotely without a local snapshot fallback. `ultra` remains accepted as a backwards-compatible alias.
|
|
486
501
|
|
|
487
502
|
Lite scans use `lite` as the scan mode and require the Lite Scan beta feature plus provider-backed GitHub or GitLab repositories that Apex can materialize remotely. Local snapshot uploads are not supported for Lite scans.
|
|
488
503
|
|
|
504
|
+
If a backend returns `AUDIT_SETUP_REQUIRED`, the error includes a setup command for the selected workspace. This explicit beta workflow uses the workspace's saved repositories and context; it does not preserve positional file or directory selections. Do not automatically substitute it for a local target scan. An explicit Standard source scan requires current CLI and backend versions. Eligible accounts receive setup commands in CLI help; draft operations use the ID returned by preparation. Lite and PR scans do not use `--setup`.
|
|
505
|
+
|
|
489
506
|
PR scans require exactly one provider-backed GitHub repository. If the current directory resolves to multiple sources, pass `--repo <path>` to select the one that contains the pull request.
|
|
490
507
|
|
|
491
508
|
CLI and MCP scan results use the public Bedrock `scanId`. Kernel execution IDs are internal implementation details and are neither returned nor accepted as scan aliases.
|
|
@@ -602,6 +619,7 @@ The local stdio MCP server exposes Apex-specific tools for:
|
|
|
602
619
|
- cross-scan workspace findings through `apex-workspace-findings`, including direct workspace selection, first-class open state, web UI lifecycle fields, and server-side filters
|
|
603
620
|
- Lite scan start by calling `apex-scan` with `mode: "lite"`
|
|
604
621
|
- PR scan start by calling `apex-scan` with `mode: "pr"` and `pullRequests`
|
|
622
|
+
- close/reopen findings with `apex-finding-status`, preserving scanner validity and feedback
|
|
605
623
|
- finding comments and valid/invalid feedback with `apex-finding-comment` and `apex-finding-feedback`
|
|
606
624
|
- Create Linear/Jira tickets with `apex-finding-send` using the saved frontend destination
|
|
607
625
|
- Link existing Linear/Jira tickets with `apex-finding-link-ticket` and inspect links with `apex-finding-tickets`
|
|
@@ -669,7 +687,7 @@ Telemetry event posts do not include bearer tokens. The telemetry endpoint is `P
|
|
|
669
687
|
{
|
|
670
688
|
"schemaVersion": 1,
|
|
671
689
|
"event": "apex.invocation.completed",
|
|
672
|
-
"cliVersion": "0.1.
|
|
690
|
+
"cliVersion": "0.1.38",
|
|
673
691
|
"invocation": {
|
|
674
692
|
"surface": "mcp",
|
|
675
693
|
"command": "apex-scan",
|
|
@@ -700,7 +718,7 @@ The npm package also includes marketplace-ready plugin artifacts:
|
|
|
700
718
|
- `.claude-plugin/plugin.json` and `.mcp.claude.json` for Claude Code plugin installs
|
|
701
719
|
- `.claude-plugin/marketplace.json` for a Claude marketplace entry backed by the public npm package
|
|
702
720
|
|
|
703
|
-
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.38 apex-mcp`, so users do not need to install `apex` globally before enabling the plugin.
|
|
704
722
|
|
|
705
723
|
The repository also includes `.agents/plugins/marketplace.json` for local Codex marketplace testing from a checkout.
|
|
706
724
|
|
|
@@ -770,3 +788,35 @@ Keep `policy.version` at `3.1`. Supply a name, instructions, and all eight metri
|
|
|
770
788
|
allowed value and nonempty guidance for each allowed value. Omit disallowed
|
|
771
789
|
values. If Audit Setup or another workspace update is active, wait for it to
|
|
772
790
|
finish before saving again.
|
|
791
|
+
|
|
792
|
+
### Close and reopen findings
|
|
793
|
+
|
|
794
|
+
Use `apex-finding-status` to manage remediation directly from either MCP transport.
|
|
795
|
+
Local MCP accepts `findingRef` and optional `cwd`/`scanId` using device login.
|
|
796
|
+
Hosted MCP accepts `findingId` (UUID) and requires explicitly granted `findings:write`.
|
|
797
|
+
Pass `status: "fixed"` to close an issue or `status: "open"` to reopen it.
|
|
798
|
+
|
|
799
|
+
```sh
|
|
800
|
+
apex findings status KERN2-25 fixed --note "Resolved in PR #123" --json
|
|
801
|
+
apex findings status KERN2-25 open --note "Regression reproduced" --json
|
|
802
|
+
```
|
|
803
|
+
|
|
804
|
+
Other statuses are `acknowledged`, `in_review`, `pending_merge`, `false_positive`,
|
|
805
|
+
`wont_fix`, and `skipped`. Reopening stores `accepted`, retaining the fact that the
|
|
806
|
+
finding was reviewed. Status changes preserve scanner validity, severity, and
|
|
807
|
+
valid/invalid feedback; invalid or duplicate findings must have their classification
|
|
808
|
+
reviewed before they can enter the open queue.
|
|
809
|
+
|
|
810
|
+
REST integrations call `PATCH /api/apex/v1/findings/{findingId}/status` with
|
|
811
|
+
`{"status":"fixed","note":"Resolved in PR #123"}` and a bearer service key with
|
|
812
|
+
`findings:write`. Each change is audited. The response includes `findingId`,
|
|
813
|
+
`status`, `reviewState`, `fromReviewState`, `updatedAt`, and `changed`.
|
|
814
|
+
Workspace finding lists expose `remediationStatus`, keeping `open` and `acknowledged`
|
|
815
|
+
distinct from the scanner `status` and the persisted `reviewState`.
|
|
816
|
+
Pass the current finding `updatedAt` as `expectedUpdatedAt` (CLI:
|
|
817
|
+
`--expected-updated-at`) to reject stale edits with `FINDING_STATUS_CONFLICT` (409).
|
|
818
|
+
Reload and review the latest state before retrying a conflict.
|
|
819
|
+
|
|
820
|
+
Linear sync currently runs from Linear to Apex: started tickets become acknowledged
|
|
821
|
+
and completed tickets become fixed. Apex status changes do not update linked Linear
|
|
822
|
+
or Jira tickets. Use the direct status operation to manage issues from MCP or REST.
|
package/dist/apex.js
CHANGED
|
@@ -3,7 +3,7 @@ import { commandStartThreatModel, commandThreatModel, hasAuditSetupBeta, threatM
|
|
|
3
3
|
import { getMe } from "./auth.js";
|
|
4
4
|
import { ApexApiClient, formatApiError } from "./api-client.js";
|
|
5
5
|
import { isAuditSetupInvocation, parseArgs, validateCommandFlags } from "./args.js";
|
|
6
|
-
import { commandCancelScan, commandClaim, commandConnect, commandCredits, commandDoctor, commandExportFindings, commandFindingComment, commandFindingTickets, commandFindingLinkTicket, commandFindingSend, commandFindingFeedback, commandFindingFixReview, commandFindings, commandWorkspaceFindings, commandLogin, commandLogout, commandRedeem, commandScan, commandScans, commandServiceKeyCreate, commandServiceKeyList, commandServiceKeyRevoke, commandSetup, commandStatus, commandTelemetry, commandUpdate, commandWorkspace, commandWorkspaceUse, commandWorkspaces, getRedeemExitCode, } from "./commands.js";
|
|
6
|
+
import { commandCancelScan, commandClaim, commandConnect, commandCredits, commandDoctor, commandExportFindings, commandFindingComment, commandFindingStatus, commandFindingTickets, commandFindingLinkTicket, commandFindingSend, commandFindingFeedback, commandFindingFixReview, commandFindings, commandWorkspaceFindings, commandLogin, commandLogout, commandRedeem, commandScan, commandScans, commandServiceKeyCreate, commandServiceKeyList, commandServiceKeyRevoke, commandSetup, commandStatus, commandTelemetry, commandUpdate, commandWorkspace, commandWorkspaceUse, commandWorkspaces, getRedeemExitCode, } from "./commands.js";
|
|
7
7
|
import { cliHelpText } from "./help.js";
|
|
8
8
|
import { runMcpServer } from "./mcp.js";
|
|
9
9
|
import { runInteractiveShell } from "./shell.js";
|
|
@@ -109,6 +109,12 @@ async function dispatch(parsed) {
|
|
|
109
109
|
await commandWorkspace(cwd, parsed.flags);
|
|
110
110
|
return;
|
|
111
111
|
case "findings":
|
|
112
|
+
if (parsed.subcommand === "status") {
|
|
113
|
+
if (parsed.args.length !== 2)
|
|
114
|
+
throw new Error("Usage: apex findings status <finding-ref> <open|fixed|acknowledged|in_review|pending_merge|false_positive|wont_fix|skipped> [--note <rationale>]");
|
|
115
|
+
await commandFindingStatus(client, cwd, parsed.flags, parsed.args[0], parsed.args[1], typeof parsed.flags.note === "string" ? parsed.flags.note : undefined, typeof parsed.flags["expected-updated-at"] === "string" ? parsed.flags["expected-updated-at"] : undefined);
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
112
118
|
if (parsed.subcommand === "send") {
|
|
113
119
|
if (parsed.args.length !== 2)
|
|
114
120
|
throw new Error("Usage: apex findings send <finding-ref> <linear|jira>");
|
package/dist/api-client.js
CHANGED
|
@@ -1,9 +1,16 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
1
2
|
import { bindCredentialIssuerOriginIfCurrent, clearCredentials, clearCredentialsIfRefreshTokenMatches, loadConfig, loadCredentials, saveRefreshedCredentialsIfCurrent, } from "./config.js";
|
|
2
3
|
import { getTelemetryRequestHeaders } from "./telemetry.js";
|
|
3
4
|
import { redactSensitiveText, redactSensitiveValue, } from "./redaction.js";
|
|
4
5
|
export { redactSensitiveText } from "./redaction.js";
|
|
5
6
|
const CREDENTIAL_GENERATION_CHANGED_MESSAGE = "Apex credentials changed while this request was refreshing. The newer credentials were kept; retry the command.";
|
|
6
7
|
const HOSTED_APEX_ORIGIN = "https://ai.cantina.xyz";
|
|
8
|
+
const REFRESH_CREDENTIALS_SCHEMA = z.object({
|
|
9
|
+
accessToken: z.string().trim().min(1),
|
|
10
|
+
refreshToken: z.string().trim().min(1),
|
|
11
|
+
accessTokenExpiresAt: z.string().datetime({ offset: true }),
|
|
12
|
+
refreshTokenExpiresAt: z.string().datetime({ offset: true }),
|
|
13
|
+
});
|
|
7
14
|
const CREDENTIAL_ORIGIN_MISMATCH_MESSAGE = "Refusing to send stored Apex credentials because their issuer origin does not match the configured APEX_BASE_URL. Restore the origin used to sign in, or run `apex logout` before signing in to this origin.";
|
|
8
15
|
export class CredentialGenerationChangedError extends Error {
|
|
9
16
|
constructor() {
|
|
@@ -211,7 +218,11 @@ export class ApexApiClient {
|
|
|
211
218
|
});
|
|
212
219
|
}
|
|
213
220
|
async clearCredentialsAfterUnauthorized(expectedRefreshToken, error) {
|
|
214
|
-
|
|
221
|
+
// Transport errors, rate limits and server failures do not invalidate a
|
|
222
|
+
// token pair. Keep it recoverable for the next command.
|
|
223
|
+
const credentialsInvalid = (error instanceof ApiError && error.status === 401) ||
|
|
224
|
+
isTerminalGuestSessionError(error);
|
|
225
|
+
if (!expectedRefreshToken || !credentialsInvalid) {
|
|
215
226
|
return;
|
|
216
227
|
}
|
|
217
228
|
await this.clearCredentialsForGeneration(expectedRefreshToken, error);
|
|
@@ -244,20 +255,19 @@ export class ApexApiClient {
|
|
|
244
255
|
const effectiveRefreshToken = refreshToken ?? existing?.refreshToken ?? null;
|
|
245
256
|
if (!effectiveRefreshToken)
|
|
246
257
|
return null;
|
|
247
|
-
const payload =
|
|
258
|
+
const payload = await this.request("/api/cli/v1/auth/refresh", {
|
|
248
259
|
method: "POST",
|
|
249
260
|
auth: false,
|
|
250
261
|
json: { refreshToken: effectiveRefreshToken },
|
|
251
262
|
retryOnUnauthorized: false,
|
|
252
263
|
telemetryAttribution: false,
|
|
253
264
|
requestBaseUrl: baseUrl,
|
|
254
|
-
})
|
|
255
|
-
const
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
};
|
|
265
|
+
});
|
|
266
|
+
const parsed = REFRESH_CREDENTIALS_SCHEMA.safeParse(payload);
|
|
267
|
+
if (!parsed.success) {
|
|
268
|
+
throw new Error("Apex returned an invalid credential refresh response. Your credentials were kept; retry the command.");
|
|
269
|
+
}
|
|
270
|
+
const nextCredentials = parsed.data;
|
|
261
271
|
const persisted = await saveRefreshedCredentialsIfCurrent(effectiveRefreshToken, nextCredentials);
|
|
262
272
|
if (!persisted.saved) {
|
|
263
273
|
throw new CredentialGenerationChangedError();
|
package/dist/args.js
CHANGED
|
@@ -14,6 +14,7 @@ export const KNOWN_CLI_FLAG_NAMES = [
|
|
|
14
14
|
"content",
|
|
15
15
|
"dismissal-reason",
|
|
16
16
|
"expires-at",
|
|
17
|
+
"expected-updated-at",
|
|
17
18
|
"fix-pr-url",
|
|
18
19
|
"force",
|
|
19
20
|
"format",
|
|
@@ -25,6 +26,7 @@ export const KNOWN_CLI_FLAG_NAMES = [
|
|
|
25
26
|
"limit",
|
|
26
27
|
"mode",
|
|
27
28
|
"name",
|
|
29
|
+
"note",
|
|
28
30
|
"no-open",
|
|
29
31
|
"non-interactive",
|
|
30
32
|
"output",
|
|
@@ -94,6 +96,15 @@ export function parseArgs(argv) {
|
|
|
94
96
|
}
|
|
95
97
|
index += 1;
|
|
96
98
|
}
|
|
99
|
+
// Scan operands are source targets, never subcommands. Keep them on the
|
|
100
|
+
// same typed path as --repo so the CLI and MCP share source selection.
|
|
101
|
+
if (positional[0] === "scan" && positional.length > 1) {
|
|
102
|
+
if (flags.setup !== undefined) {
|
|
103
|
+
throw new Error("Audit Setup does not accept scan targets. Use --workspace-ref <workspace>.");
|
|
104
|
+
}
|
|
105
|
+
flags.repo = [...(flags.repo ?? []), ...positional.slice(1)];
|
|
106
|
+
positional.splice(1);
|
|
107
|
+
}
|
|
97
108
|
return {
|
|
98
109
|
command: positional[0] ?? null,
|
|
99
110
|
subcommand: positional[1] ?? null,
|
|
@@ -102,6 +113,9 @@ export function parseArgs(argv) {
|
|
|
102
113
|
};
|
|
103
114
|
}
|
|
104
115
|
export function validateCommandFlags(parsed) {
|
|
116
|
+
if (parsed.flags["source-mode"] !== undefined && !["auto", "remote", "local"].includes(String(parsed.flags["source-mode"]))) {
|
|
117
|
+
throw new Error("--source-mode must be auto, remote, or local.");
|
|
118
|
+
}
|
|
105
119
|
if (parsed.flags["workspace-ref"] === undefined) {
|
|
106
120
|
return;
|
|
107
121
|
}
|