Skip to content

Add Cloud Security (CNAPP) support: 'limacharlie cloudsec' CLI + SDK#310

Merged
maximelb merged 3 commits into
masterfrom
feat/cloudsec-cli
Jul 10, 2026
Merged

Add Cloud Security (CNAPP) support: 'limacharlie cloudsec' CLI + SDK#310
maximelb merged 3 commits into
masterfrom
feat/cloudsec-cli

Conversation

@maximelb

@maximelb maximelb commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds first-class CLI + SDK support for the new Cloud Security (CNAPP) product, wrapping the full /cloudsec/{oid}/... gateway REST surface (35 routes, verified against the gateway route registrations and backend handlers).

New SDK classlimacharlie/sdk/cloudsec.py (CloudSec(org)), one method per route, returning the raw JSON response shapes.

New command grouplimacharlie cloudsec ...:

Subgroup / command Covers
overview, changes, risk-trend, scan-status composed risk overview, created/closed feed, score history, per-provider collection status
finding list/facets/get merged risk-ranked findings worklist (repeatable --severity/--class/--status/--account, --kev/--reachable, -q, sort + keyset pagination)
finding resolve/bulk-resolve/set-owner/set-ticket triage writes; --kind open reopens (clears the disposition, keeps owner/ticket)
attack-path list marquee toxic-combination paths
ciem public-access/facets identity access views
inventory list/facets, data-security facets, resource get resource inventory, DSPM facets, canonical urn point lookup
graph neighbors, query list/run 1-hop graph expansion; query pack + named/text/DSL graph queries
compliance report/frameworks/assignments per-control assessment (whole-estate framework or scoped assignment)
chokepoint list/dismiss/restore estate-wide chokepoints
resolve sensors/assets bulk sensor <-> cloud asset fusion mapping
`caasm assets/coverage/policy get set/ingest`
provider test pre-save provider credential preflight (ephemeral creds or hive://secret/<name>)

Every command has --ai-help explain text; the group help documents the cloudsec.get/cloudsec.set permissions and the ext-cloud-inventory enable gate.

Deliberately out of scope

  • Provider/policy CRUD: cloudsec_provider, cloudsec_policy, and cloudsec_query are hive records already fully manageable via limacharlie hive ...; the explain texts point there instead of duplicating shortcuts.
  • Server-side CSV exports (?format=csv on findings/inventory/query/compliance): the client's JSON parsing doesn't carry raw CSV bodies; the global --output csv covers the need client-side.

Testing

  • tests/unit/test_sdk_cloudsec.py: asserts the exact URL, repeated-key query pairs, and JSON body shape for every SDK method against a mocked client.
  • tests/unit/test_cli_cloudsec.py: CliRunner coverage of arg parsing, repeatable filters, boolean tri-state flags, mutually-exclusive inputs, JSON/file inputs, and choice validation.
  • Lazy-loading regression snapshot updated (top-level set, module map, subcommand snapshot).
  • Full unit suite: 3561 passed, 5 skipped (pre-existing).

🤖 Generated with Claude Code

https://claude.ai/code/session_01JrqM5AzjBtH7F9hFN4aGjw

Backend follow-ups surfaced by review (not fixable in this repo)

  • Bulk reopen: the single-finding status RPC special-cases kind: open (clear disposition), the bulk RPC does not — so bulk reopen is impossible platform-wide. The CLI now blocks open on bulk, but the capability gap belongs to the graph service's bulk handler.
  • Phantom resolve cap: the gateway enforces/advertises 500 ids per GET /resolve/* request, but the ~8KB LB URL limit caps a real request near ~190 ids — no client can reach the advertised cap. Either a POST-based bulk resolve route or a documented cap matching GET reality would remove the trap (this SDK chunks at 100 to stay safe).

Wraps the full /cloudsec/{oid}/... gateway REST surface (35 routes) in
a new CloudSec SDK class and a 'cloudsec' command group:

- findings worklist: list/facets/get + triage writes (resolve incl.
  reopen via kind=open, bulk-resolve, set-owner, set-ticket)
- attack paths, CIEM (public-access, facets)
- inventory (list/facets), DSPM data-security facets, resource point
  lookup by urn
- security graph: 1-hop neighbors, query pack list, graph query run
  (named/text/DSL)
- compliance: report (framework or scoped assignment), frameworks,
  assignments
- overview, chokepoints (list/dismiss/restore), changes, risk-trend,
  scan-status
- sensor <-> cloud asset resolution (bulk)
- CAASM: assets, coverage gaps, expected-coverage policy get/set,
  raw record ingest
- provider credential preflight (POST providers/test)

Reads need cloudsec.get, writes cloudsec.set; all routes are behind
the ext-cloud-inventory subscription gate. Provider configs and the
cloudsec policies remain hive records (cloudsec_provider /
cloudsec_policy / cloudsec_query) managed via the existing hive
commands; the explain texts point there.

Repeatable query params (severity, finding_class, sid, urn, ...) are
sent as repeated keys via list-of-pairs query_params; write bodies are
JSON. Unit tests cover the SDK request shapes and the CLI arg parsing;
the lazy-loading regression snapshot gains the new group.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JrqM5AzjBtH7F9hFN4aGjw
lcbill
lcbill previously approved these changes Jul 10, 2026
…s, chunked resolve

Fixes from the multi-agent review of this PR:

- finding bulk-resolve: drop 'open' from the accepted kinds — the bulk
  API rejects it (reopen is single-finding only); help/explain updated
- scan-status --provider and caasm ingest --source: no longer
  click.Choice — both are growing server-side registries (7 providers
  live today) validated server-side with clean errors
- JSON `null` inputs no longer bypass shape validation: --records-file,
  --record-json, --query-json, --policy-json all get unconditional type
  checks; `query run --text ""` now fails client-side instead of
  round-tripping
- resolve sensors/assets: chunk bulk batches (100 ids/request) so any
  batch size works — repeated query params previously hit the ~8KB LB
  URL limit near 190 ids while docstrings advertised 500
- file inputs (--input-file/--records-file) now accept YAML or JSON
  like every other command module, and policy set / provider test read
  piped stdin — replaces the bespoke JSON-only loader
- Client: urlencode query_params with doseq=True (sequence values
  expand to repeated keys instead of a Python list repr) and widen the
  query_params type hints; regression test added
- sdk/cloudsec: collapse the per-method query-pair boilerplate into a
  single _query_pairs builder
- tests: per-subgroup help coverage for every cloudsec subgroup, plus
  the null-input, empty-text, bulk-open, YAML, stdin, and chunking cases

Full unit suite: 3585 passed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JrqM5AzjBtH7F9hFN4aGjw
lcbill
lcbill previously approved these changes Jul 10, 2026
…rs, shared loader

- get_scan_status lowercases the provider before sending: the backend
  scan-state read is a case-sensitive lookup keyed on lowercase provider
  ids, so a raw "AWS" silently read as never-scanned
- extract commands/_input_helpers.py (load_file/load_stdin/
  parse_yaml_or_json) as the canonical YAML-or-JSON input loader with
  clean BadParameter errors — cloudsec now imports it instead of
  carrying the codebase's 7th private copy; malformed piped stdin gets
  a usage error instead of a raw JSONDecodeError traceback
- _one_of: None means absent but an explicit empty string is rejected —
  both alone (empty selector) and alongside a real option (ambiguous
  invocation), restoring the strict exactly-one arbitration
- fix stale contract text the previous commit missed: resolve command
  docstrings no longer claim "max 500", scan-status SDK docstring no
  longer claims a closed gcp|aws|azure list, and the provider-test
  explain text now shows the correct v2 hive syntax (--hive-name +
  --input-file, not positional + --data)
- tests/unit/conftest.py: autouse guard that fails any unit test
  reaching limacharlie.client.urlopen — local dev machines carry live
  credentials, so an unmocked invocation that slips past argument
  validation would otherwise hit a real org (this bit during
  development: a YAML-tolerant parse of '{"a": }' let an unmocked test
  make a live call)

Full unit suite: 3588 passed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JrqM5AzjBtH7F9hFN4aGjw
@maximelb maximelb requested a review from a team July 10, 2026 18:51
@maximelb maximelb marked this pull request as ready for review July 10, 2026 18:51
@maximelb maximelb merged commit dc55448 into master Jul 10, 2026
7 checks passed
@maximelb maximelb deleted the feat/cloudsec-cli branch July 10, 2026 18:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants