Skip to content

feat: Go port of CloudSecurity-AF — installing this repo now gives you the Go node - #8

Merged
AbirAbbas merged 5 commits into
mainfrom
feat/go-port
Aug 20, 2026
Merged

feat: Go port of CloudSecurity-AF — installing this repo now gives you the Go node#8
AbirAbbas merged 5 commits into
mainfrom
feat/go-port

Conversation

@AbirAbbas

Copy link
Copy Markdown
Contributor

Summary

Complete 1:1 port of CloudSecurity-AF from Python to Go on the AgentField Go SDK, following the porting pattern established by pr-af (Agent-Field/pr-af#53, #54, #64) and SWE-AF (Agent-Field/SWE-AF#94), and the sibling SEC-AF port (Agent-Field/sec-af#130). The Python implementation is byte-untouched — every diff lives under go/, plus docker-compose.go.yml, one root-README section, the root-manifest redirect, and a new Go CI workflow (this repo previously had no .github/).

The root manifest declares superseded_by: …//go, so af install https://github.com/Agent-Field/cloudsecurity-af installs the Go package and replaces an existing Python install in place. The Go manifest registers the node id the process actually uses — cloudsecurity (the Python code's default; the root manifest's stale cloudsecurity-af id is corrected alongside, with a packaging parity test pinning both manifests to app.py). Default port 8015; docker-compose.go.yml runs the Go node as cloudsecurity-go beside the Python stack.

Parity contract honored

  • Registration surface: all 22 reasoners under their exact Python names — scan, prove plus the 20 router reasoners tagged ["cloud","security","infrastructure"] — each publishing the same signature-derived input schema the Python SDK registers (captured from a live Python node into a committed fixture and asserted through /discover).
  • Same DAG: the orchestrator drives the five phases through tracked agent.Calls with identical targets/kwargs; the phases fan out exactly as Python does (depth-mapped hunters with a semaphore, one path constructor with depth-mapped max_paths, static/live provers by tier with the attack-path kwarg only when applicable, fix generators for confirmed/likely findings).
  • Deterministic Terraform parsing: _terraform_parser.py is ported on hcl/v2 producing the same inventory.json shape (ids, providers, references, reverse references); the committed Python-generated inventory for the repo's own vulnerable fixture is the test oracle, and the fast graph builder is byte-identical on it. Harness fallbacks preserved.
  • Result JSON: pydantic model_dump() key sets, defaults, strict enums (this repo's models reject unknown enum values — reproduced, unlike pr-af's coercing pattern), exclude_none at the same sites, pydantic-isoformat timestamps.
  • Prompts byte-verbatim: all 15 templates embedded with a drift test; every prompt builder golden-tested against Python.
  • .ai() parity: the structured-output path reproduces the Python SDK's tolerant parsing (salvage + retries), shared with the SEC-AF port.

Verification

  • go build ./… && go vet ./… && go test ./… && go test -race ./… green; gofmt -l empty; ~500 Go test functions derived from the Python tests/behavior; also run on the CI-pinned go1.21 toolchain; golangci-lint clean.
  • Live DAG parity against a real control plane: both nodes registered on one isolated control plane with a deterministic mock opencode harness; the same scan (depth standard) triggered on the repo's vulnerable_infra fixture. The Go and Python runs are strictly identical: same 37 executions, same 18-edge multiset, same statuses — and the final scan payloads (findings, verdicts, risk scores, SARIF, counts) are byte-identical after canonicalizing concurrent-completion list order. CloudSecurity has no live-LLM DAG nondeterminism (verdicts come from the harness-driven provers), so the comparison is exact with no allowances.
  • Docker image builds from the repo root (checksum-verified aforge fetch) and smoke-tests (non-root user, entrypoint generates opencode.json).
  • Python tree untouched: git diff origin/main -- src tests pyproject.toml Dockerfile docker-compose.yml is empty.

Known accepted differences (documented in code)

  • SDK /health body ({"status":"ok"}) — consumers only check the status code.
  • AGENT_CALLBACK_URL unset: the SDK derives the real listen address instead of Python's hardcoded Docker-Desktop-only host.docker.internal:8020 fallback.
  • Non-constant Terraform expressions render as their source text rather than pyhcl2's AST-dataclass repr (byte-offset noise); the divergent leaf paths on the fixtures are enumerated by a test so new ones cannot appear silently. DAG shape and finding identity are unaffected.
  • Three stale Python test files (test_schemas.py imports removed classes; test_graph_context.py / test_utils.py target moved paths/signatures) don't run against today's src/; the port pins the current code's interpreter-verified behavior and documents the staleness.

🤖 Generated with Claude Code

AbirAbbas and others added 5 commits August 20, 2026 02:11
The shared substrate every ported package builds on: afx (map<->struct
binding, both _unwrap variants incl. the strict phases/orchestrator one,
exclude_none), pyfmt (Python round/str/repr/json.dumps parity), appx
(the Harness/AI/Note/Call agent seam + recording test fake), harnessx
(generic structured-harness runner resolving committed pydantic schema
fixtures by Go type name), aix (the .ai(schema=) path with the Python
SDK's strictify, salvage and parse-retry semantics), byte-verbatim
embedded prompts with a drift test, config (env parity, depth tables,
scan config), schemas (every pydantic model with default-seeding
UnmarshalJSON, strict enums, pydantic-isoformat timestamps) and scoring
(risk model pinned by a 600-row Python-generated matrix).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The deterministic Terraform inventory parser (hcl/v2 port of the pyhcl2
walk, same inventory.json shape) with the harness fallback, the fast
graph builder, cloud connector and drift detector, the 7 hunters over
the shared graph-context assembly, the attack-path constructor, the
static/live provers and the fix generator, plus the SARIF/JSON/Markdown
emitters. Prompts and artifacts are golden-tested byte-for-byte against
the Python implementations.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
internal/phases carries recon/hunt/chain/prove/remediation_phase with
the exact Python call targets, kwargs, depth tables, semaphore bounds
and fallback shapes, so the control-plane DAG is identical to the Python
node's. internal/orch ports ScanOrchestrator: the five sequential phase
calls, checkpoints, budget bookkeeping and generate_output (benchmark
severity floors, risk scores, drift/shadow-IT counts, SARIF).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
internal/reasoners registers scan/prove plus the 20 router reasoners
under their exact Python names, tags and signature-derived input schemas
(captured from a live Python node and embedded as the parity fixture).
internal/node ports app.py: agent construction from the environment,
the scan/prove handlers building CloudSecurityInput, the workspaces
resolver with the writability fallback, and error mapping. The packaging
parity test pins manifests, compose and CI against the Python sources.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The root manifest gains superseded_by pointing at go/, whose manifest
declares the same package name and the node id the process actually
registers (cloudsecurity), so a git install lands the Go node and
replaces an existing Python install in place; a local-path install
remains the documented Python escape hatch. Adds the multi-stage Go
Dockerfile (checksum-verified AForge fetch + opencode, non-root user),
the model-aware entrypoint, a compose add-on joining the Python stack
under the cloudsecurity-go id, Makefile, go/README, a root-README
section and a Go CI workflow.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@AbirAbbas
AbirAbbas merged commit 5ea2867 into main Aug 20, 2026
2 checks passed
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.

1 participant