Skip to content

feat(detector): inventory Rust packages and audit Cargo configuration - #234

Merged
ashishkurmi merged 4 commits into
step-security:mainfrom
raysubham:feat/cargo-inventory
Oct 7, 2026
Merged

ashishkurmi merged 4 commits into
step-security:mainfrom
raysubham:feat/cargo-inventory

Conversation

@raysubham

Copy link
Copy Markdown
Contributor

Stacked on #230 (Go module inventory). Until #230 merges, this diff also shows its commits; the Cargo change is the last commit, b48b522. Draft until #230 lands and the API contract changes below are agreed.

What does this PR do?

Adds Rust/Cargo evidence to enterprise telemetry as two optional sections, cargo_inventory and cargo_config_audit (each schema_version: 1), in a new cargo_scan phase right after go_scan. Both are full bounded snapshots sent on every run. The shape mirrors the Go scanner and reuses its guards, source IDs and budgets.

cargo_inventory (static, no Rust toolchain needed)

  • Declared requirements from Cargo.toml: normal, dev, build and target-specific tables; aliases, features, optional and explicit default-features; registry, Git and path selectors. Workspace inheritance is applied; only workspace.dependencies entries that members inherit are followed.
  • Workspaces: members (literal and single-level globs), exclude, in-root path dependencies and explicit package.workspace. Membership that can't be established is workspace_unresolved.
  • Locked packages: Cargo.lock formats 3 and 4, once per workspace root, honoring resolver.lockfile-path. Other formats are unsupported_format.
  • Registry cache (registry/cache + registry/src), Git checkouts (full commit only from a 40/64-hex .git/HEAD), vendored directories, local registries and .crates.toml / .crates2.json install receipts with bin presence.
  • Recorded checksums from Cargo.lock and .cargo-checksum.json, always not_verified.

cargo_config_audit

  • .cargo/config and .cargo/config.toml per project and ancestor inside the search roots, the Cargo home config, includes (depth and count bounded, cycles flagged), and an allowlisted process environment, all resolved per invocation context in precedence order (config_source_ids).
  • Findings cargo-001..cargo-004: HTTP registry/source index, insecure Git source, plaintext token in config, http.check-revoke = false.
  • Credentials files are Stat-only.

Safety properties

  • No cargo, rustc or git command, shell, network call, PATH search or UserAwareExecutor. Every read goes through guarded, bounded executor file methods; .crate archives are read in memory with header, size and path-traversal bounds.
  • Protected directories stay excluded even with include_tcc_protected; there is no Cargo TCC exception.
  • Redaction happens on the device: URL userinfo, query and fragment are stripped (including URL-shaped table names such as [patch."https://…"]), tokens show only as configured, and custom credential providers become custom with arguments dropped.
  • Any refusal, failure or limit marks the affected source partial with a reason code. The record budget is charged during collection, and every owner left unfinished is marked record_limit. The combined output is capped at 16 MiB with a status envelope.
  • Stable source IDs and deterministic ordering, so identical logical inputs produce identical bytes.

Other: github.com/pelletier/go-toml/v2 v2.3.1 → v2.4.3. Community scan output is unchanged.

Contract: internal/model/testdata/cargo_inventory_v1_golden.json holds IDs recomputed from its displayed paths, and it decodes strictly and round-trips byte-identically. For it to pass the Agent API validator, the API needs to:

  • allow cached_package + vendor_unknown when the owner is a local_registry source;
  • allow a pathless user config source only when it is unsupported with path_unresolved.

Type of change

  • Bug fix
  • Enhancement
  • Documentation (SCAN_COVERAGE.md, README.md)

Testing

  • Tested on macOS (version: 27.0)
  • Binary runs without errors (make smoke): pending
  • JSON output is valid: not applicable, the sections are enterprise telemetry only and community JSON is unchanged
  • No secrets or credentials included
  • Lint passes: gofmt, go vet, golangci-lint (0 issues)
  • Tests pass: go test ./... -race -count=1

Also:

  • CGO_ENABLED=0 builds for linux/amd64, darwin/arm64 and windows/amd64; go mod tidy shows no drift.
  • gosec: no new findings in the Cargo files.
  • Regression tests cover URL table-name redaction, partial selection when a higher-precedence config is unreadable, no null arrays, record-limit ownership under every budget, inherited external path dependencies (followed) versus unused ones (not followed), and unreadable bins (Unix, non-root).

Not yet done (release checks):

  • make smoke.
  • Three-VM acceptance (macOS, Linux, Windows) against cargo metadata references.
  • The API validator changes above, then running the shared golden through the real validator.

Related Issues

raysubham and others added 3 commits September 30, 2026 23:53
Add cargo_inventory and cargo_config_audit (schema_version 1) to
enterprise telemetry in a new cargo_scan phase after go_scan.

The inventory reads Cargo.toml declarations with workspace inheritance,
Cargo.lock formats 3 and 4, the registry cache, Git checkouts, vendored
sources and local registries, and cargo install receipts. The audit
reads .cargo/config(.toml) files, their includes and an allowlisted
process environment per invocation context, with findings
cargo-001..cargo-004.

Everything is static: no cargo, rustc or git command, shell or network
call. Reads go through guarded, bounded executor file methods, URL
credentials are redacted on the device, credentials files are checked
for presence only, and any refusal, failure or limit marks the affected
source partial. Bumps go-toml/v2 to v2.4.3.
@raysubham
raysubham marked this pull request as ready for review October 7, 2026 14:59
@ashishkurmi
ashishkurmi requested a balanced review from Copilot October 7, 2026 17:31

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Workspace exclusions, incomplete-status propagation, deterministic serialization, and the unenforced golden contract need correction.

4 open findings
What changed in this PR

Adds bounded, static Rust/Cargo inventory and configuration auditing to enterprise telemetry.

Changes:

  • Inventories Cargo projects, workspaces, lockfiles, caches, vendored packages, Git checkouts, and installed tools.
  • Adds redacted Cargo configuration auditing, wire models, and a new telemetry phase.
  • Updates documentation, tests, Go scanner fixes, and the TOML dependency.
File Description
SCAN_COVERAGE.md Documents Cargo scan coverage and safeguards.
README.md Adds Rust inventory and configuration support.
go.mod Upgrades go-toml.
go.sum Updates go-toml checksums.
internal/​telemetry/​telemetry.go Runs and serializes the Cargo phase.
internal/​model/​model.go Defines Cargo wire models and vocabulary.
internal/​model/​testdata/​cargo_inventory_v1_golden.json Adds the Cargo contract fixture.
internal/​detector/​cargoscan.go Implements bounded Cargo inventory collection.
internal/​detector/​cargoscan_test.go Tests major inventory behavior and limits.
internal/​detector/​cargometadata.go Parses Cargo metadata and archives.
internal/​detector/​configaudit/​cargoconfig.go Implements Cargo configuration auditing.
internal/​detector/​configaudit/​cargoconfig_test.go Tests redaction and partial selection.
internal/​detector/​goscan.go Corrects fallback-root status handling.
internal/​detector/​goscan_test.go Covers unknown Go root redirects.
internal/​detector/​configaudit/​goenv.go Refines Go proxy fallback detection.
internal/​detector/​configaudit/​goenv_test.go Tests Go proxy fallback cases.

🧠 Review effort: Balanced


💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +689 to +695
func (g *cargoScan) excludes(root, s *cargoManifestState) bool {
under := func(entries []string) bool {
return slices.ContainsFunc(entries, func(e string) bool {
return cargoInside(g.goos, s.dir, filepath.Join(root.dir, filepath.FromSlash(e)))
})
}
return under(root.m.workspace.exclude) && !under(root.m.workspace.members)
Comment on lines +1764 to +1766
default:
b.Presence = model.CargoBinUnreadable
}
for _, inc := range includes {
b.add("include", inc.path, false)
}
slices.SortStableFunc(b.out, func(x, y cargoSetting) int { return strings.Compare(x.Key, y.Key) })
@@ -0,0 +1,1344 @@
{
@ashishkurmi
ashishkurmi merged commit 2e7fe13 into step-security:main Oct 7, 2026
12 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.

3 participants