mirror of
https://hubproxy.babadafafafafa.cn/https://github.com/usestrix/strix.git
synced 2026-09-20 08:03:42 +08:00
- Every payment-required error now ends with a "Next step" line: the platform hint when one is sent, else the topup command and the billing URL for the configured platform. JSON output gets the same text as next_step. The platform hint is no longer repeated inside the error. - An archive file passed to --source is rejected with guidance to pass the directory instead, which packs and excludes deps/build output. - An oversize archive names its largest files and points to --exclude and --dry-run --show-files. - uploads request help points to scans start --source for local code.
1149 lines
44 KiB
Python
1149 lines
44 KiB
Python
"""Declarative command table for `strix cloud`.
|
|
|
|
Each command maps one CLI verb to one managed API operation. The runner
|
|
builds the argument parser and the HTTP request from this table, so the
|
|
CLI surface stays aligned with the OpenAPI specification.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from dataclasses import dataclass
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class P:
|
|
"""One command parameter.
|
|
|
|
``kind`` is one of ``str``, ``int``, ``float``, ``bool``, ``list``, ``json``,
|
|
or ``json-list``.
|
|
"""
|
|
|
|
name: str
|
|
kind: str = "str"
|
|
required: bool = False
|
|
help: str = ""
|
|
# Command-line name when the field name collides with a common option.
|
|
flag: str | None = None
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class Cmd:
|
|
method: str
|
|
path: str
|
|
help: str
|
|
query: tuple[P, ...] = ()
|
|
body: tuple[P, ...] = ()
|
|
binary: bool = False
|
|
wait_path: str | None = None
|
|
# When true, `--wait` polls GET on this same path until the status is final.
|
|
wait_self: bool = False
|
|
# Response field that holds a URL a person must open, for example a hosted
|
|
# checkout page. The runner opens the browser for an interactive terminal
|
|
# and always prints the URL.
|
|
link: str | None = None
|
|
# Caller retries for this mutation must carry one stable opaque key. The
|
|
# platform binds it to the authenticated actor and exact request body.
|
|
idempotent: bool = False
|
|
|
|
|
|
def _q(*names: str) -> tuple[P, ...]:
|
|
return tuple(P(name) for name in names)
|
|
|
|
|
|
_SCAN_START_BODY = (
|
|
P(
|
|
"engagement_type",
|
|
help=("Test category: code_review, live_test, internal_infra, or compliance_pentest."),
|
|
),
|
|
P("domain_ids", "list", help="Domain asset IDs to test."),
|
|
P("domain_paths", "json", help="JSON map of domain ID to start paths."),
|
|
P("repository_ids", "list", help="Repository asset IDs to test."),
|
|
P("repository_branches", "json", help="JSON map of repository ID to branch."),
|
|
P("credentials", "json", help="JSON list of credential objects."),
|
|
P(
|
|
"headers",
|
|
"json",
|
|
help='JSON array of target header objects: [{"name":"...","value":"...","notes":"..."}].',
|
|
),
|
|
P("concerns", help="Free-form security concerns to investigate."),
|
|
P("focus", help="Free-form focus instructions for the agents."),
|
|
P("context", help="Extra context about the target."),
|
|
P("upload_ids", "list", help="Upload IDs to attach to the scan."),
|
|
P("connector_id", help="Network connector ID for internal targets."),
|
|
P("internal_targets", "list", help="Internal IP addresses or ranges."),
|
|
P("org_knowledge_enabled", "bool", help="Use the organization knowledge base."),
|
|
P("notify_on_completion", "bool", help="Send an email when the scan completes."),
|
|
P("notification_emails", "list", help="Extra notification email addresses."),
|
|
P(
|
|
"scan_tier",
|
|
help=(
|
|
"Scan tier: lite, standard, or ultra (default). "
|
|
"Not used for Enterprise or self-hosted scans."
|
|
),
|
|
),
|
|
P("model_config_id", help="Self-hosted only: model configuration ID to run with."),
|
|
P("max_budget_usd", "float", help="Self-hosted only: budget limit for the scan in USD."),
|
|
)
|
|
|
|
_TEST_USER_ADD_BODY = (
|
|
P("label", required=True, help="Display label for the test user."),
|
|
P("username", required=True, help="Sign-in username or email address."),
|
|
P("password", help="Sign-in password."),
|
|
P("notes", help="Free-form notes for the agents."),
|
|
P("login_url", help="URL of the sign-in page."),
|
|
P("mfa_method", help="MFA method: none, totp, email_otp, or magic_link."),
|
|
P("totp_secret", help="TOTP secret for MFA sign-in."),
|
|
P("mfa_email", help="Email address that receives MFA codes."),
|
|
P("scope_domain_ids", "list", help="Domain IDs where this user applies."),
|
|
)
|
|
|
|
_TEST_USER_BODY = (
|
|
P("label", help="Display label for the test user."),
|
|
P("username", help="Sign-in username or email address."),
|
|
P("password", help="Sign-in password."),
|
|
P("notes", help="Free-form notes for the agents."),
|
|
P("login_url", help="URL of the sign-in page."),
|
|
P("mfa_method", help="MFA method: none, totp, email_otp, or magic_link."),
|
|
P("totp_secret", help="TOTP secret for MFA sign-in."),
|
|
P("mfa_email", help="Email address that receives MFA codes."),
|
|
P("scope_domain_ids", "list", help="Domain IDs where this user applies."),
|
|
)
|
|
|
|
_GIT_TOKEN_BODY = (
|
|
P("access_token", required=True, help="Provider access token.", flag="provider-token"),
|
|
P("instance_url", help="GitLab base URL, for example https://gitlab.com."),
|
|
P("account_email", help="Bitbucket account email address."),
|
|
P("installation_id", "int", help="Existing installation ID to update."),
|
|
)
|
|
|
|
|
|
SPEC: dict[str, dict[str, Cmd]] = {
|
|
"scans": {
|
|
"list": Cmd(
|
|
"GET",
|
|
"/scans",
|
|
"List scans.",
|
|
query=(
|
|
P("page", "int", help="Results page (starts at 1)."),
|
|
P("limit", "int", help="Results per page (1-100)."),
|
|
*_q(
|
|
"status",
|
|
"scan_type",
|
|
"date_from",
|
|
"date_to",
|
|
"domain_id",
|
|
"repository_id",
|
|
"search",
|
|
),
|
|
P("include_retests", "bool", help="Include per-finding retest scans."),
|
|
P("sort_by", help="Sort key; currently created_at."),
|
|
P("sort_order", help="Sort order: asc or desc."),
|
|
),
|
|
),
|
|
"start": Cmd(
|
|
"POST",
|
|
"/scans",
|
|
"Start a scan.",
|
|
body=_SCAN_START_BODY,
|
|
wait_path="/scans/{id}",
|
|
idempotent=True,
|
|
),
|
|
"get": Cmd("GET", "/scans/{scanId}", "Get one scan."),
|
|
"delete": Cmd("DELETE", "/scans/{scanId}", "Delete a scan."),
|
|
"agents": Cmd("GET", "/scans/{scanId}/agents", "List the agents of a scan."),
|
|
"cancel": Cmd("POST", "/scans/{scanId}/cancel", "Cancel a running scan."),
|
|
"message": Cmd(
|
|
"POST",
|
|
"/scans/{scanId}/message",
|
|
"Send a message to the scan agents.",
|
|
body=(
|
|
P(
|
|
"message",
|
|
help="Message text for the agents. Required unless --cancel-current is used.",
|
|
),
|
|
P("cancel_current", "bool", help="Cancel the current task before delivery."),
|
|
P("agent_id", help="Target one agent instead of the root agent."),
|
|
),
|
|
),
|
|
"report": Cmd(
|
|
"GET",
|
|
"/scans/{scanId}/report",
|
|
"Download the scan report.",
|
|
query=(
|
|
P(
|
|
"format",
|
|
help=(
|
|
"Report content: technical (default), retest, attestation, or "
|
|
"executive_summary. Advanced formats require Enterprise."
|
|
),
|
|
),
|
|
P(
|
|
"type",
|
|
help="Rendered file type: pdf (default) or docx. DOCX requires Enterprise.",
|
|
),
|
|
P(
|
|
"providerName",
|
|
flag="provider-name",
|
|
help="Enterprise report-cover provider name (up to 80 characters).",
|
|
),
|
|
P(
|
|
"memberName0",
|
|
flag="member-name-0",
|
|
help="First Enterprise report preparer's name (up to 120 characters).",
|
|
),
|
|
P(
|
|
"memberEmail0",
|
|
flag="member-email-0",
|
|
help="First Enterprise report preparer's email address.",
|
|
),
|
|
P(
|
|
"memberName1",
|
|
flag="member-name-1",
|
|
help="Second Enterprise report preparer's name (up to 120 characters).",
|
|
),
|
|
P(
|
|
"memberEmail1",
|
|
flag="member-email-1",
|
|
help="Second Enterprise report preparer's email address.",
|
|
),
|
|
),
|
|
binary=True,
|
|
),
|
|
"rerun": Cmd(
|
|
"POST",
|
|
"/scans/{scanId}/rerun",
|
|
"Run the scan again.",
|
|
wait_path="/scans/{id}",
|
|
idempotent=True,
|
|
),
|
|
"retest-all": Cmd(
|
|
"POST",
|
|
"/scans/{scanId}/retest-all",
|
|
"Retest all open findings of a scan.",
|
|
body=(
|
|
P("scope", help="Retest scope."),
|
|
P("upload_ids", "list", help="Upload IDs with updated code."),
|
|
),
|
|
),
|
|
"retests": Cmd("GET", "/scans/{scanId}/retests", "List the retests of a scan."),
|
|
"sarif": Cmd(
|
|
"GET",
|
|
"/scans/{scanId}/sarif",
|
|
"Download the scan findings as SARIF.",
|
|
query=_q("repository"),
|
|
binary=True,
|
|
),
|
|
"sarif-upload": Cmd(
|
|
"POST",
|
|
"/scans/{scanId}/sarif",
|
|
"Upload the scan findings to GitHub code scanning.",
|
|
body=(
|
|
P("repository", help="Repository full name."),
|
|
P("ref", help="Git ref for the upload."),
|
|
P("commit_sha", help="Commit SHA for the upload."),
|
|
P("checkout_uri", help="Checkout URI for the upload."),
|
|
P("github_api_base_url", help="GitHub API base URL."),
|
|
),
|
|
),
|
|
"template": Cmd("GET", "/scans/{scanId}/template", "Get the scan configuration template."),
|
|
"trace": Cmd(
|
|
"GET",
|
|
"/scans/{scanId}/trace",
|
|
"List trace events for one agent of a scan.",
|
|
query=(
|
|
P("agent_id", required=True, help="Agent ID to read the trace for."),
|
|
P("cursor"),
|
|
P("limit", "int"),
|
|
P("tool_name"),
|
|
),
|
|
),
|
|
"trace-event": Cmd(
|
|
"GET", "/scans/{scanId}/trace/{eventId}", "Get one trace event of a scan."
|
|
),
|
|
},
|
|
"vulns": {
|
|
"list": Cmd(
|
|
"GET",
|
|
"/vulnerabilities",
|
|
"List vulnerabilities.",
|
|
query=(
|
|
P("page", "int", help="Results page (starts at 1)."),
|
|
P("limit", "int", help="Results per page (1-100)."),
|
|
*_q(
|
|
"scan_id",
|
|
"severity",
|
|
"status",
|
|
"search",
|
|
"from",
|
|
"to",
|
|
"domain_id",
|
|
"repository_id",
|
|
"finding_type",
|
|
"dependency_relation",
|
|
"reachability",
|
|
"sort_by",
|
|
),
|
|
P("sort_order", help="Sort order: asc or desc."),
|
|
),
|
|
),
|
|
"get": Cmd("GET", "/vulnerabilities/{vulnerabilityId}", "Get one vulnerability."),
|
|
"history": Cmd(
|
|
"GET",
|
|
"/vulnerabilities/{vulnerabilityId}/history",
|
|
"Get the change history of a vulnerability.",
|
|
),
|
|
"update": Cmd(
|
|
"PATCH",
|
|
"/vulnerabilities/{vulnerabilityId}",
|
|
"Update the status or severity of a vulnerability.",
|
|
body=(
|
|
P(
|
|
"status",
|
|
help=(
|
|
"New status: open, in_progress, snoozed, fixed, ignored, or not_affected."
|
|
),
|
|
),
|
|
P("note", help="Note that explains the change."),
|
|
P("severity", help="New severity."),
|
|
P("severity_reason", help="Reason for the severity change."),
|
|
),
|
|
),
|
|
"retest": Cmd(
|
|
"POST",
|
|
"/vulnerabilities/{vulnerabilityId}/retest",
|
|
"Retest one vulnerability.",
|
|
body=(P("upload_ids", "list", help="Upload IDs with updated code."),),
|
|
wait_path="/scans/{id}",
|
|
),
|
|
"fix-pr": Cmd(
|
|
"POST",
|
|
"/vulnerabilities/{vulnerabilityId}/create-fix-pr",
|
|
"Create a fix pull request for a vulnerability.",
|
|
),
|
|
"push": Cmd(
|
|
"POST",
|
|
"/vulnerabilities/{vulnerabilityId}/push",
|
|
"Push one vulnerability to an issue tracker.",
|
|
body=(
|
|
P("provider", required=True, help="Tracker provider, for example jira or linear."),
|
|
P("collection_id", help="Tracker project or collection ID."),
|
|
),
|
|
),
|
|
"push-bulk": Cmd(
|
|
"POST",
|
|
"/vulnerabilities/bulk-push",
|
|
"Push many vulnerabilities to an issue tracker.",
|
|
body=(
|
|
P("provider", required=True, help="Tracker provider, for example jira or linear."),
|
|
P("vulnerability_ids", "list", required=True, help="Vulnerability IDs to push."),
|
|
P("collection_id", help="Tracker project or collection ID."),
|
|
),
|
|
),
|
|
},
|
|
"domains": {
|
|
"list": Cmd(
|
|
"GET",
|
|
"/domains",
|
|
"List domain assets.",
|
|
query=(
|
|
P("page", "int", help="Results page (starts at 1)."),
|
|
P("limit", "int", help="Results per page (1-100)."),
|
|
*_q("search", "verified", "business_unit", "tags", "sort_by"),
|
|
P("sort_order", help="Sort order: asc or desc."),
|
|
),
|
|
),
|
|
"add": Cmd(
|
|
"POST",
|
|
"/domains",
|
|
"Add a domain asset.",
|
|
body=(
|
|
P("domain", required=True, help="Domain name or URL."),
|
|
P("asset_type", required=True, help="Asset type, for example web_app or api."),
|
|
P("context", help="Extra context about the asset."),
|
|
P("tags", "list", help="Tags for the asset."),
|
|
P("business_unit", help="Business unit for the asset."),
|
|
),
|
|
),
|
|
"update": Cmd(
|
|
"PATCH",
|
|
"/domains/{domainId}",
|
|
"Update a domain asset.",
|
|
body=(
|
|
P("context", help="Extra context about the asset."),
|
|
P("tags", "list", help="Tags for the asset."),
|
|
P("business_unit", help="Business unit for the asset."),
|
|
),
|
|
),
|
|
"remove": Cmd("DELETE", "/domains/{domainId}", "Remove a domain asset."),
|
|
"verify": Cmd("POST", "/domains/{domainId}/verify", "Verify domain ownership."),
|
|
"auto-verify": Cmd(
|
|
"POST",
|
|
"/domains/{domainId}/auto-verify",
|
|
"Verify domain ownership through a DNS provider.",
|
|
body=(P("provider", required=True, help="DNS provider name."),),
|
|
),
|
|
"test-users list": Cmd(
|
|
"GET", "/domains/{domainId}/test-users", "List the test users of a domain."
|
|
),
|
|
"test-users add": Cmd(
|
|
"POST",
|
|
"/domains/{domainId}/test-users",
|
|
"Add a test user to a domain.",
|
|
body=_TEST_USER_ADD_BODY,
|
|
),
|
|
"test-users update": Cmd(
|
|
"PATCH",
|
|
"/domains/{domainId}/test-users/{userId}",
|
|
"Update a test user.",
|
|
body=_TEST_USER_BODY,
|
|
),
|
|
"test-users remove": Cmd(
|
|
"DELETE", "/domains/{domainId}/test-users/{userId}", "Remove a test user."
|
|
),
|
|
"test-users provision-inbox": Cmd(
|
|
"POST",
|
|
"/domains/{domainId}/test-users/provision-inbox",
|
|
(
|
|
"Provision a Strix-managed inbox for email OTP or magic-link MFA. "
|
|
"Returns an address; it does not create a test user."
|
|
),
|
|
body=(P("label", help="Optional display label for the managed inbox."),),
|
|
),
|
|
"test-users inbox": Cmd(
|
|
"GET",
|
|
"/domains/{domainId}/test-users/{userId}/inbox",
|
|
"List the inbox messages of a test user.",
|
|
query=(P("limit", "int"),),
|
|
),
|
|
"test-users inbox-message": Cmd(
|
|
"GET",
|
|
"/domains/{domainId}/test-users/{userId}/inbox/{messageId}",
|
|
"Get one inbox message of a test user.",
|
|
),
|
|
"test-users verify": Cmd(
|
|
"POST",
|
|
"/domains/{domainId}/test-users/{userId}/verify",
|
|
"Verify that the test user credentials work.",
|
|
query=(P("force"),),
|
|
wait_self=True,
|
|
),
|
|
"test-users verify-status": Cmd(
|
|
"GET",
|
|
"/domains/{domainId}/test-users/{userId}/verify",
|
|
"Get the verification status of a test user.",
|
|
),
|
|
},
|
|
"repos": {
|
|
"list": Cmd(
|
|
"GET",
|
|
"/repositories",
|
|
"List repository assets.",
|
|
query=(
|
|
P("page", "int", help="Results page (starts at 1)."),
|
|
P("limit", "int", help="Results per page (1-100)."),
|
|
*_q("search", "business_unit", "tags", "sort_by"),
|
|
P("sort_order", help="Sort order: asc or desc."),
|
|
),
|
|
),
|
|
"add": Cmd("POST", "/repositories", "Add a repository asset. Use --data for the fields."),
|
|
"update": Cmd(
|
|
"PATCH",
|
|
"/repositories/{repositoryId}",
|
|
"Update a repository asset.",
|
|
body=(
|
|
P("pr_review_enabled", "bool", help="Turn PR reviews on or off."),
|
|
P("pr_review_approvals_enabled", "bool", help="Let reviews approve clean PRs."),
|
|
P("pr_review_non_blocking", "bool", help="Make review verdicts non-blocking."),
|
|
P("pr_review_on_push", "bool", help="Review new pushes to open PRs."),
|
|
P("tags", "list", help="Tags for the asset."),
|
|
P("business_unit", help="Business unit for the asset."),
|
|
),
|
|
),
|
|
"remove": Cmd("DELETE", "/repositories/{repositoryId}", "Remove a repository asset."),
|
|
"supply-chain scan": Cmd(
|
|
"POST",
|
|
"/repositories/{repositoryId}/supply-chain/scan",
|
|
"Start a supply-chain scan for a repository.",
|
|
),
|
|
"supply-chain summary": Cmd(
|
|
"GET",
|
|
"/repositories/{repositoryId}/supply-chain/summary",
|
|
"Get the supply-chain summary of a repository.",
|
|
query=_q("job_id", "snapshot_id"),
|
|
),
|
|
"supply-chain findings": Cmd(
|
|
"GET",
|
|
"/repositories/{repositoryId}/supply-chain/findings",
|
|
"List the supply-chain findings of a repository.",
|
|
query=_q("job_id", "snapshot_id", "component_id"),
|
|
),
|
|
"supply-chain components": Cmd(
|
|
"GET",
|
|
"/repositories/{repositoryId}/supply-chain/components",
|
|
"List the dependency components of a repository.",
|
|
query=(
|
|
*_q(
|
|
"job_id",
|
|
"snapshot_id",
|
|
"component_id",
|
|
"ecosystem",
|
|
"status",
|
|
"relationship",
|
|
"source_file",
|
|
"q",
|
|
"changed",
|
|
),
|
|
P("limit", "int", help="Maximum components to return."),
|
|
P("offset", "int", help="Number of components to skip."),
|
|
),
|
|
),
|
|
"supply-chain sbom": Cmd(
|
|
"GET",
|
|
"/repositories/{repositoryId}/supply-chain/sbom",
|
|
"Download the SBOM of a repository.",
|
|
query=_q("job_id", "snapshot_id", "format"),
|
|
binary=True,
|
|
),
|
|
"supply-chain policy": Cmd(
|
|
"PATCH",
|
|
"/repositories/{repositoryId}/supply-chain/policy",
|
|
"Update the supply-chain policy of a repository.",
|
|
body=(
|
|
P("supply_chain_enabled", "bool", help="Turn supply-chain scans on or off."),
|
|
P("supply_chain_pr_checks_enabled", "bool", help="Run checks on pull requests."),
|
|
P("supply_chain_policy_mode", help="Policy mode for new findings."),
|
|
),
|
|
),
|
|
},
|
|
"supply-chain": {
|
|
"summary": Cmd(
|
|
"GET", "/supply-chain/summary", "Get the organization supply-chain summary."
|
|
),
|
|
},
|
|
"schedules": {
|
|
"list": Cmd("GET", "/schedules", "List scan schedules."),
|
|
"create": Cmd(
|
|
"POST",
|
|
"/schedules",
|
|
"Create a scan schedule. Use --data for the fields.",
|
|
idempotent=True,
|
|
),
|
|
"get": Cmd("GET", "/schedules/{scheduleId}", "Get one schedule."),
|
|
"update": Cmd(
|
|
"PATCH",
|
|
"/schedules/{scheduleId}",
|
|
"Update a schedule. Use --data for fields that have no option.",
|
|
body=(
|
|
P("action", help="Lifecycle action, for example pause or resume."),
|
|
P("cron_expression", help="Cron expression for the schedule."),
|
|
P("timezone", help="Time zone for the cron expression."),
|
|
P("name", help="Display name of the schedule."),
|
|
P(
|
|
"max_budget_usd",
|
|
"float",
|
|
help=(
|
|
"Self-hosted only: budget limit per run in USD. "
|
|
"Use --data to set null and clear it."
|
|
),
|
|
),
|
|
P("scan_tier", help="Scan tier: lite, standard, or ultra."),
|
|
),
|
|
),
|
|
"delete": Cmd("DELETE", "/schedules/{scheduleId}", "Delete a schedule."),
|
|
"template": Cmd(
|
|
"GET", "/schedules/{scheduleId}/template", "Get the schedule configuration template."
|
|
),
|
|
"trigger": Cmd(
|
|
"POST",
|
|
"/schedules/{scheduleId}/trigger",
|
|
"Run a schedule now.",
|
|
idempotent=True,
|
|
),
|
|
},
|
|
"pr-reviews": {
|
|
"list": Cmd(
|
|
"GET",
|
|
"/pr-reviews",
|
|
"List PR reviews.",
|
|
query=(
|
|
P("page", "int", help="Results page (starts at 1)."),
|
|
P("limit", "int", help="Results per page (1-100)."),
|
|
*_q(
|
|
"search",
|
|
"status",
|
|
"group",
|
|
"pr_state",
|
|
"repository_full_name",
|
|
"date_from",
|
|
"date_to",
|
|
"sort_by",
|
|
"sort_order",
|
|
),
|
|
P("include_counts", "bool", help="Include exact disposition counts."),
|
|
),
|
|
),
|
|
"get": Cmd("GET", "/pr-reviews/{prReviewId}", "Get one PR review."),
|
|
"findings": Cmd(
|
|
"GET",
|
|
"/pr-reviews/findings",
|
|
"List PR review findings.",
|
|
query=(
|
|
P("page", "int", help="Results page (starts at 1)."),
|
|
P("limit", "int", help="Results per page (1-100)."),
|
|
*_q("severity", "pr_state", "search", "repository_full_name"),
|
|
P("include_stats", "bool", help="Include all-time impact statistics."),
|
|
),
|
|
),
|
|
"start": Cmd(
|
|
"POST",
|
|
"/pr-reviews/start",
|
|
"Start a PR review.",
|
|
body=(
|
|
P("provider", required=True, help="Git provider: github, gitlab, or bitbucket."),
|
|
P("installation_id", "int", required=True, help="Provider installation ID."),
|
|
P("repository_full_name", required=True, help="Repository full name."),
|
|
P("pr_number", "int", required=True, help="Pull request number."),
|
|
),
|
|
),
|
|
"settings": Cmd("GET", "/pr-reviews/settings", "Get the PR review settings."),
|
|
"settings update": Cmd(
|
|
"PATCH",
|
|
"/pr-reviews/settings",
|
|
"Update the PR review settings. Use --data for fields that have no option.",
|
|
body=(
|
|
P("review_on_push", "bool", help="Review new pushes to open PRs."),
|
|
P("block_on_findings", "bool", help="Block PRs that have findings."),
|
|
P("blocking_severities", "list", help="Severities that block a PR."),
|
|
P("approve_clean_prs", "bool", help="Approve PRs without findings."),
|
|
P("target_branches", "list", help="Branches that get reviews."),
|
|
),
|
|
),
|
|
},
|
|
"billing": {
|
|
"credits": Cmd("GET", "/billing/credits", "Get the credit balance of the workspace."),
|
|
"topup": Cmd(
|
|
"POST",
|
|
"/billing/topup",
|
|
"Buy credits with an agent payment (HTTP 402 flow).",
|
|
body=(P("credits", "int", required=True, help="Number of credits to buy."),),
|
|
),
|
|
"subscribe": Cmd(
|
|
"POST",
|
|
"/billing/checkout",
|
|
"Create a checkout link for a plan or a credit pack. A person completes the payment.",
|
|
body=(
|
|
P(
|
|
"product",
|
|
required=True,
|
|
flag="plan",
|
|
help="Product to buy: strix_cloud, strix_startup, or strix_top_up.",
|
|
),
|
|
P("success_url", help="Page to open after the payment."),
|
|
),
|
|
link="checkout_url",
|
|
),
|
|
"portal": Cmd(
|
|
"POST",
|
|
"/billing/portal",
|
|
"Create a billing portal link. A person manages the card and the plan there.",
|
|
link="portal_url",
|
|
),
|
|
"auto-topup": Cmd("GET", "/billing/auto-topup", "Get the automatic top-up settings."),
|
|
"auto-topup update": Cmd(
|
|
"PUT",
|
|
"/billing/auto-topup",
|
|
"Update the automatic top-up settings.",
|
|
body=(
|
|
P("enabled", "bool", required=True, help="Turn automatic top-up on or off."),
|
|
P("topup_credits", "int", required=True, help="Credits to buy on each top-up."),
|
|
P("monthly_cap_credits", "int", help="Monthly credit cap for automatic top-ups."),
|
|
),
|
|
),
|
|
},
|
|
"chat": {
|
|
"list": Cmd("GET", "/chat", "List chat sessions."),
|
|
"start": Cmd(
|
|
"POST",
|
|
"/chat",
|
|
"Start a chat session.",
|
|
body=(
|
|
P("message", required=True, help="First message of the session."),
|
|
P(
|
|
"repos",
|
|
"json",
|
|
help='JSON array of repository refs: [{"repoId":"...","branch":"main"}].',
|
|
),
|
|
P("domain_ids", "list", help="Domain asset IDs for context."),
|
|
),
|
|
),
|
|
"get": Cmd("GET", "/chat/{chatId}", "Get one chat session."),
|
|
"send": Cmd(
|
|
"POST",
|
|
"/chat/{chatId}/message",
|
|
"Send a message in a chat session.",
|
|
body=(
|
|
P(
|
|
"message",
|
|
help="Message text. Required unless --cancel-current or --stop-agent is used.",
|
|
),
|
|
P("cancel_current", "bool", help="Cancel the in-flight agent turn first."),
|
|
P("stop_agent", "bool", help="Park the target agent and its descendants."),
|
|
P(
|
|
"repos",
|
|
"json",
|
|
help='JSON array of repository refs: [{"repoId":"...","branch":"main"}].',
|
|
),
|
|
P("agent_id", help="Target one subagent instead of the root agent."),
|
|
),
|
|
),
|
|
"findings": Cmd("GET", "/chat/{chatId}/findings", "List the findings of a chat session."),
|
|
"finding": Cmd(
|
|
"GET", "/chat/{chatId}/findings/{findingId}", "Get one finding of a chat session."
|
|
),
|
|
"finding file": Cmd(
|
|
"POST",
|
|
"/chat/{chatId}/findings/{findingId}/file",
|
|
"File a chat finding into the organization issue list.",
|
|
),
|
|
"files": Cmd("GET", "/chat/{chatId}/files", "List the files of a chat session."),
|
|
"files download": Cmd(
|
|
"GET",
|
|
"/chat/{chatId}/files/download",
|
|
"Download one file of a chat session.",
|
|
query=(
|
|
P(
|
|
"path",
|
|
required=True,
|
|
help="Relative path in the session, or an absolute path under /workspace.",
|
|
),
|
|
),
|
|
binary=True,
|
|
),
|
|
"files archive": Cmd(
|
|
"GET",
|
|
"/chat/{chatId}/files/archive",
|
|
"Download all files of a chat session as an archive.",
|
|
binary=True,
|
|
),
|
|
"credentials": Cmd(
|
|
"GET",
|
|
"/chat/{chatId}/credentials",
|
|
"Get the credentials of a chat session.",
|
|
query=_q("scan_ids"),
|
|
),
|
|
"credentials set": Cmd(
|
|
"POST",
|
|
"/chat/{chatId}/credentials",
|
|
"Set the credentials of a chat session.",
|
|
body=(
|
|
P("test_user_ids", "list", help="Test user IDs to attach."),
|
|
P("credentials", "json", help="JSON list of credential objects."),
|
|
P("scan_ids", "list", help="Scan IDs that use the credentials."),
|
|
),
|
|
),
|
|
"credentials clear": Cmd(
|
|
"DELETE", "/chat/{chatId}/credentials", "Remove the credentials of a chat session."
|
|
),
|
|
"domains set": Cmd(
|
|
"PUT",
|
|
"/chat/{chatId}/domains",
|
|
"Set the domains of a chat session.",
|
|
body=(P("domain_ids", "list", help="Domain asset IDs."),),
|
|
),
|
|
"terminal": Cmd(
|
|
"POST",
|
|
"/chat/{chatId}/terminal",
|
|
"Run a command in the chat session sandbox.",
|
|
body=(
|
|
P("command", required=True, help="Shell command to run."),
|
|
P("cwd", help="Working directory for the command."),
|
|
),
|
|
),
|
|
"share": Cmd("POST", "/chat/{chatId}/share", "Create a share link for a chat session."),
|
|
},
|
|
"knowledge": {
|
|
"list": Cmd(
|
|
"GET",
|
|
"/knowledge",
|
|
"List knowledge documents.",
|
|
query=(
|
|
*_q("source_type", "search"),
|
|
P("limit", "int", help="Maximum documents to return."),
|
|
),
|
|
),
|
|
"add": Cmd(
|
|
"POST",
|
|
"/knowledge",
|
|
"Add a knowledge document.",
|
|
body=(
|
|
P("title", required=True, help="Document title."),
|
|
P("content", required=True, help="Document content."),
|
|
P("tags", "list", help="Tags for the document."),
|
|
P("metadata", "json", help="JSON metadata for the document."),
|
|
),
|
|
),
|
|
"update": Cmd(
|
|
"PATCH",
|
|
"/knowledge/{documentId}",
|
|
"Update a knowledge document.",
|
|
body=(
|
|
P("title", help="Document title."),
|
|
P("content", help="Document content."),
|
|
P("tags", "list", help="Tags for the document."),
|
|
P("metadata", "json", help="JSON metadata for the document."),
|
|
),
|
|
),
|
|
"delete": Cmd("DELETE", "/knowledge/{documentId}", "Delete a knowledge document."),
|
|
"policies": Cmd("GET", "/knowledge/policies", "List knowledge policies."),
|
|
"policies add": Cmd(
|
|
"POST",
|
|
"/knowledge/policies",
|
|
"Add a knowledge policy.",
|
|
body=(
|
|
P("policy_key", required=True, flag="key", help="Policy key."),
|
|
P("policy_value", required=True, flag="content", help="Policy content."),
|
|
P("policy_type", help="Policy type. Defaults to constraint."),
|
|
P("is_active", "bool", flag="enabled", help="Turn the policy on or off."),
|
|
P("metadata", "json", help="JSON metadata for the policy."),
|
|
),
|
|
),
|
|
"policies delete": Cmd(
|
|
"DELETE", "/knowledge/policies/{policyKey}", "Delete a knowledge policy."
|
|
),
|
|
"repos": Cmd("GET", "/knowledge/repos", "List repositories with knowledge entries."),
|
|
"repos entries": Cmd(
|
|
"GET", "/knowledge/repos/{repo}/entries", "List the knowledge entries of a repository."
|
|
),
|
|
"repos profile": Cmd(
|
|
"PATCH",
|
|
"/knowledge/repos/{repo}/profile",
|
|
"Update the knowledge profile of a repository. Use --data for the fields.",
|
|
),
|
|
},
|
|
"org": {
|
|
"get": Cmd("GET", "/organization", "Get the organization."),
|
|
"update": Cmd(
|
|
"PATCH",
|
|
"/organization",
|
|
"Update the organization.",
|
|
body=(P("name", required=True, help="Organization name."),),
|
|
),
|
|
"members": Cmd("GET", "/organization/members", "List the organization members."),
|
|
"members invite": Cmd(
|
|
"POST",
|
|
"/organization/members",
|
|
"Invite a member to the organization.",
|
|
body=(
|
|
P("email", required=True, help="Email address of the new member."),
|
|
P("role", help="Member role, for example admin, analyst, or viewer."),
|
|
P("scopes", "list", help="RBAC scopes for the member."),
|
|
),
|
|
),
|
|
"members update": Cmd(
|
|
"PATCH",
|
|
"/organization/members/{membershipId}",
|
|
"Update a member of the organization.",
|
|
body=(
|
|
P("role", required=True, help="Member role."),
|
|
P("scopes", "list", help="RBAC scopes for the member."),
|
|
),
|
|
),
|
|
"members remove": Cmd("DELETE", "/organization/members/{membershipId}", "Remove a member."),
|
|
"invitations": Cmd("GET", "/organization/invitations", "List open invitations."),
|
|
"invitations revoke": Cmd(
|
|
"DELETE", "/organization/invitations/{invitationId}", "Revoke an invitation."
|
|
),
|
|
},
|
|
"integrations": {
|
|
"list": Cmd("GET", "/integrations", "List the connected integrations."),
|
|
"connect": Cmd(
|
|
"POST",
|
|
"/integrations/{provider}/connect",
|
|
"Connect a Git provider. The provider is gitlab or bitbucket.",
|
|
body=_GIT_TOKEN_BODY,
|
|
),
|
|
"validate": Cmd(
|
|
"POST",
|
|
"/integrations/{provider}/validate",
|
|
"Validate a Git provider token. The provider is gitlab or bitbucket.",
|
|
body=_GIT_TOKEN_BODY,
|
|
),
|
|
"install": Cmd(
|
|
"POST",
|
|
"/integrations/{provider}/install-url",
|
|
"Create an installation link. The provider is github or slack. A person approves it.",
|
|
link="url",
|
|
),
|
|
"disconnect": Cmd(
|
|
"DELETE",
|
|
"/integrations/{provider}",
|
|
"Disconnect an integration.",
|
|
query=(
|
|
P(
|
|
"installation_id",
|
|
"int",
|
|
help=(
|
|
"Installation ID. Required for github, gitlab, and bitbucket; "
|
|
"unsupported for other providers."
|
|
),
|
|
),
|
|
),
|
|
),
|
|
},
|
|
"connectors": {
|
|
"list": Cmd("GET", "/connectors", "List network connectors."),
|
|
"create": Cmd(
|
|
"POST",
|
|
"/connectors",
|
|
"Create a network connector.",
|
|
body=(P("name", required=True, help="Connector name."),),
|
|
),
|
|
"get": Cmd(
|
|
"GET",
|
|
"/connectors/{connectorId}",
|
|
"Get one network connector.",
|
|
query=(
|
|
P(
|
|
"include_command",
|
|
"bool",
|
|
help=(
|
|
"Include the one-time Docker enrollment command. "
|
|
"The command contains sensitive connector credentials."
|
|
),
|
|
),
|
|
),
|
|
),
|
|
"status": Cmd(
|
|
"GET", "/connectors/{connectorId}/status", "Get the status of a network connector."
|
|
),
|
|
"delete": Cmd("DELETE", "/connectors/{connectorId}", "Delete a network connector."),
|
|
},
|
|
"webhooks": {
|
|
"list": Cmd("GET", "/webhooks", "List webhooks."),
|
|
"create": Cmd(
|
|
"POST",
|
|
"/webhooks",
|
|
"Create a webhook.",
|
|
body=(
|
|
P("url", required=True, help="Delivery URL."),
|
|
P("events", "list", required=True, help="Event names to deliver."),
|
|
P("business_unit", help="Business unit filter."),
|
|
P("is_active", "bool", help="Turn the webhook on or off."),
|
|
),
|
|
),
|
|
"get": Cmd("GET", "/webhooks/{webhookId}", "Get one webhook."),
|
|
"update": Cmd(
|
|
"PATCH",
|
|
"/webhooks/{webhookId}",
|
|
"Update a webhook.",
|
|
body=(
|
|
P("url", help="Delivery URL."),
|
|
P("events", "list", help="Event names to deliver."),
|
|
P("business_unit", help="Business unit filter."),
|
|
P("is_active", "bool", help="Turn the webhook on or off."),
|
|
P("rotate_secret", "bool", help="Create a new signing secret."),
|
|
),
|
|
),
|
|
"delete": Cmd("DELETE", "/webhooks/{webhookId}", "Delete a webhook."),
|
|
"deliveries": Cmd(
|
|
"GET",
|
|
"/webhooks/{webhookId}/deliveries",
|
|
"List the deliveries of a webhook.",
|
|
query=(
|
|
P("page", "int", help="Results page (starts at 1)."),
|
|
P("limit", "int", help="Results per page (1-100)."),
|
|
),
|
|
),
|
|
},
|
|
"analytics": {
|
|
"overview": Cmd(
|
|
"GET",
|
|
"/analytics/overview",
|
|
"Get the analytics overview.",
|
|
query=_q("range", "from", "to"),
|
|
),
|
|
"stats": Cmd("GET", "/analytics/stats", "Get the analytics statistics."),
|
|
"scan-frequency": Cmd(
|
|
"GET", "/analytics/scan-frequency", "Get the scan frequency data.", query=_q("tz")
|
|
),
|
|
},
|
|
"audit": {
|
|
"list": Cmd(
|
|
"GET",
|
|
"/audit",
|
|
"List audit log entries.",
|
|
query=(
|
|
P("page", "int", help="Results page (starts at 1)."),
|
|
P("limit", "int", help="Results per page (1-1000)."),
|
|
*_q("action", "resource_type", "actor_id", "date_from", "date_to"),
|
|
P(
|
|
"format",
|
|
help="Output format: json, csv, ndjson, jsonl, snowflake, or splunk.",
|
|
),
|
|
P("all", "bool", help="Stream all matches when exporting instead of one page."),
|
|
),
|
|
),
|
|
},
|
|
"costs": {
|
|
"overview": Cmd(
|
|
"GET",
|
|
"/llm-costs",
|
|
"Self-hosted only: show the LLM cost overview.",
|
|
query=_q("range", "from", "to"),
|
|
),
|
|
"run": Cmd(
|
|
"GET",
|
|
"/llm-costs/runs/{runType}/{runId}",
|
|
"Self-hosted only: get the LLM costs of one run.",
|
|
),
|
|
},
|
|
"llm-settings": {
|
|
"get": Cmd("GET", "/llm-settings", "Self-hosted only: get the LLM settings."),
|
|
"update": Cmd(
|
|
"PUT",
|
|
"/llm-settings",
|
|
"Self-hosted only: update the LLM settings.",
|
|
body=(
|
|
P(
|
|
"modelConfigs",
|
|
"json",
|
|
required=True,
|
|
flag="model-configs",
|
|
help="JSON list of model configurations.",
|
|
),
|
|
P("assignments", "json", required=True, help="JSON map of model assignments."),
|
|
),
|
|
),
|
|
},
|
|
"settings": {
|
|
"notifications": Cmd("GET", "/settings/notifications", "Get the notification settings."),
|
|
"notifications update": Cmd(
|
|
"PATCH",
|
|
"/settings/notifications",
|
|
"Update the notification settings.",
|
|
body=(
|
|
P("sla_reminders_enabled", "bool", help="Turn SLA reminders on or off."),
|
|
P("sla_reminder_email", "bool", help="Send SLA reminders by email."),
|
|
P("sla_reminder_slack", "bool", help="Send SLA reminders to Slack."),
|
|
P("sla_warning_days", "int", help="Days before an SLA warning."),
|
|
),
|
|
),
|
|
},
|
|
"license": {
|
|
"show": Cmd("GET", "/license", "Get the license information."),
|
|
},
|
|
"tokens": {
|
|
"list": Cmd("GET", "/tokens", "List API tokens.", query=_q("type")),
|
|
"create": Cmd(
|
|
"POST",
|
|
"/tokens",
|
|
"Create an API token.",
|
|
body=(
|
|
P("type", required=True, help="Token type, personal or service."),
|
|
P("name", required=True, help="Token name."),
|
|
P("scopes", "list", help="API scopes for the token."),
|
|
P(
|
|
"rbac_scopes",
|
|
"json-list",
|
|
help=(
|
|
"JSON array of resource restrictions; each item has type "
|
|
"target, tag, or business_unit and a value."
|
|
),
|
|
),
|
|
P(
|
|
"expires_at",
|
|
help=(
|
|
"Absolute expiration date/time (ISO 8601; mutually exclusive "
|
|
"with --expires-in-days)."
|
|
),
|
|
),
|
|
P("expires_in_days", "int", help="Days until the token expires."),
|
|
),
|
|
),
|
|
"revoke": Cmd("DELETE", "/tokens/{tokenId}", "Revoke an API token."),
|
|
},
|
|
"uploads": {
|
|
"request": Cmd(
|
|
"POST",
|
|
"/uploads/request",
|
|
"Request an upload URL. To scan local source, prefer `strix cloud scans start "
|
|
"--source DIR`, which packs, uploads, and starts the scan in one step.",
|
|
body=(
|
|
P("file_name", required=True, help="File name."),
|
|
P("file_size", "int", required=True, help="File size in bytes."),
|
|
P("category", help="Upload category."),
|
|
),
|
|
),
|
|
"complete": Cmd(
|
|
"POST",
|
|
"/uploads/complete",
|
|
"Mark an upload as complete.",
|
|
body=(P("upload_id", required=True, help="Upload ID."),),
|
|
),
|
|
"delete": Cmd("DELETE", "/uploads/{uploadId}", "Delete an upload."),
|
|
},
|
|
"workspaces": {
|
|
"list": Cmd("GET", "/workspaces", "List the workspaces of your account."),
|
|
"create": Cmd(
|
|
"POST",
|
|
"/workspaces",
|
|
"Create a workspace and become its admin.",
|
|
body=(P("name", required=True, help="Workspace name."),),
|
|
),
|
|
},
|
|
}
|
|
|
|
|
|
# Default verbs let a bare group name run its most common read command.
|
|
DEFAULT_VERBS: dict[str, str] = {
|
|
"scans": "list",
|
|
"vulns": "list",
|
|
"domains": "list",
|
|
"repos": "list",
|
|
"workspaces": "list",
|
|
"schedules": "list",
|
|
"pr-reviews": "list",
|
|
"billing": "credits",
|
|
"chat": "list",
|
|
"knowledge": "list",
|
|
"org": "get",
|
|
"integrations": "list",
|
|
"connectors": "list",
|
|
"webhooks": "list",
|
|
"analytics": "overview",
|
|
"costs": "overview",
|
|
"audit": "list",
|
|
"llm-settings": "get",
|
|
"settings": "notifications",
|
|
"license": "show",
|
|
"tokens": "list",
|
|
"supply-chain": "summary",
|
|
}
|
|
|
|
|
|
GROUP_HELP: dict[str, str] = {
|
|
"scans": "Start, watch, and manage scans",
|
|
"vulns": "Triage and remediate vulnerabilities",
|
|
"domains": "Manage domain assets and test users",
|
|
"repos": "Manage repository assets and supply-chain scans",
|
|
"supply-chain": "Organization supply-chain summary",
|
|
"schedules": "Manage scan schedules",
|
|
"pr-reviews": "Manage pull request reviews",
|
|
"billing": "Credits, top-ups, and automatic top-up",
|
|
"chat": "Interactive pentest chat sessions",
|
|
"knowledge": "Manage the knowledge base",
|
|
"org": "Manage the organization and its members",
|
|
"integrations": "Connect Git providers and other integrations",
|
|
"workspaces": "List, create, and switch workspaces",
|
|
"connectors": "Manage network connectors",
|
|
"webhooks": "Manage webhooks",
|
|
"analytics": "Read analytics data",
|
|
"audit": "Read the audit log",
|
|
"costs": "Self-hosted only: read LLM cost data",
|
|
"llm-settings": "Self-hosted only: manage LLM model settings",
|
|
"settings": "Manage notification settings",
|
|
"license": "Read license information",
|
|
"tokens": "Manage API tokens",
|
|
"uploads": "Upload files for scans",
|
|
}
|