Files
rea/docs/rust-binaries.md
T
Kaoru0822andClaude Opus 5.5 b928963675 feat(native): inspect a universal Mach-O slice in Go and Rust metadata readers (#6219)
* feat(native): inspect a universal Mach-O slice in Go and Rust metadata readers

inspect_go_binary and inspect_rust_binary refused universal Mach-O files and
offered no slice selection, so hosts without lipo could not inspect them.

Both now accept architecture (x86, x86_64, arm, arm64). One selector reads
REA's own FAT table, validates the slice range, alignment and header CPU,
and returns the thin image with its macho-selected-image-v1 identity. The
universal file stays the artifact; results add selected_slice and keep
every source range a file offset. Missing, absent, duplicate or misplaced
selections fail on architecture and list the slices present. Subject
format mach-o-universal now has one owner shared by the services and the
retained Go/Rust views.

Fixes #4102

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(native): reject damaged universal files and self-contradicting slice reports

A FAT table whose slice could not be read was treated as a non-universal
file, so callers were told to omit architecture. Its reason now fails on
path. Go and Rust reports with a selected slice must be Mach-O and name the
slice's CPU family, through one shared agreement check. The CLI
architecture flag is described in help.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 21:18:35 -07:00

6.3 KiB

Rust binary dependency metadata

inspect_rust_binary / inspect-rust-binary answers which crate names, versions, sources, and dependency edges are embedded by cargo-auditable in one selected local executable or library. It reads the file without running it and requires no Rust installation, active binary target, Hopper, Ghidra, or IDA.

rea inspect-rust-binary /absolute/path/to/application --json

The CLI resolves relative paths against the caller's working directory. MCP requires an absolute filesystem path and preserves that exact selection:

{
  "name": "inspect_rust_binary",
  "arguments": { "path": "/artifacts/application" }
}

Observations

Complete delivery remains the default. For a dependency question, select the needed records on the first call instead of returning the complete graph:

{
  "name": "inspect_rust_binary",
  "arguments": {
    "path": "/artifacts/application",
    "view": {
      "collection": "packages",
      "names": ["serde", "serde_derive"],
      "fields": ["name", "version", "source", "dependencies"]
    }
  }
}

detail: "summary" returns section ranges, root index, package/edge/cycle counts and compiler-hash count. A view selects compact delivery automatically; combining it with explicit detail: "complete" is an input error. The complete parent Evidence is retained in the MCP session and remains exportable. Subsequent inspect_analysis_view calls accept that parent_evidence_id, or its portable Evidence file, without reopening the binary:

{
  "source": {
    "kind": "retained-evidence",
    "evidence_id": "ev_<parent digest>"
  },
  "view": {
    "kind": "rust",
    "collection": "edges",
    "from_indices": [12],
    "limit": 20
  }
}

Package filters accept exact names, versions and original indices; edge filters accept original edge indices, from_indices and to_indices. Filters intersect, and an empty filter array selects nothing. offset counts matching records; limit defaults to 64. Pages retain original indices scoped to the parent Evidence, selected fields, examined coverage and the next offset. Default package fields omit dependency vectors; explicitly select dependencies to obtain direct references. No dependency path is traversed.

The CLI uses the same views and requires an exclusive output bundle for compact delivery so the complete Evidence survives process exit:

rea inspect-rust-binary /artifacts/application --json \
  --view '{"collection":"packages","names":["serde"],"fields":["name","version"]}' \
  --evidence_output /artifacts/rust-evidence.json

Compact results distinguish complete embedded-record coverage from unknown runtime dependency coverage. A missing section remains unknown rather than an empty dependency set. Retained views describe the captured artifact digest; they do not assert that the current file or running application is unchanged.

The inline Evidence result contains the artifact path, SHA-256, byte length, container format, architecture, word size, and byte order. When a .dep-v0 section is present, auditable_metadata contains:

  • section_name, .dep-v0 for ELF and PE or __DATA,.dep-v0 for Mach-O;
  • file_offset and section_bytes, the exact file range of the compressed section;
  • compressed_bytes and uncompressed_bytes;
  • format_version, the embedded integer, or null when the field is omitted;
  • packages, in the section's original order, with name, version, source, kind, dependencies, and root;
  • root_package_index, the single package marked root, or null when none is marked;
  • dependency_edges, pairs of package indices in package-then-dependency order;
  • cyclic_dependency_indices, sorted unique indices that participate in a cycle.

An omitted kind stays null. The embedded format treats that omission as runtime, and that default is not stored in the section. An omitted dependency list is an empty array. An omitted root flag is false.

rustc_commit is present only when a /rustc/<40-hex>/ path is found in read-only data. Its evidence_kind is inferred. The hash is not a claim that that compiler produced the binary. Several distinct hashes leave a single commit unknown.

Limits and failures

The selected file is limited to 256 MiB. A dependency section is limited to 1 MiB compressed. Inflated JSON is limited to the smaller of 8 MiB and 64 times the compressed size. The complete report is limited to 8 MiB. Empty sections, truncated or trailing zlib bytes, invalid JSON, unknown fields, dependency indices outside the package list, more than one root package, and compressed dependency sections are typed failures. A cycle is reported and is not walked.

A missing section returns success with auditable_metadata: null and a limitation. Absence of .dep-v0 does not mean the binary has no dependencies. Unknown embedded format revisions are retained in format_version and interpreted using the highest known preceding revision, following the cargo-auditable schema. The output includes a limitation that identifies this fallback and notes that the particular revision has not been independently verified. An omitted revision remains null and follows revision 0 semantics. Embedded names and versions are file content. They do not authenticate the packages or the compiler.

ELF, PE, thin Mach-O and one selected slice of a universal Mach-O are the supported containers. A universal (FAT) Mach-O needs architecture (x86, x86_64, arm or arm64; CLI --architecture). REA reads its own FAT table, without lipo, and validates the selected slice's range, alignment and Mach-O header CPU against the table. The result keeps the universal file as artifact (its SHA-256 and size), adds selected_slice with the slice offset, length, raw CPU type/subtype and the FAT table digest, and reports the section and rustc-path offsets as a file offset inside that slice. Without architecture, or when the file has no such slice or several, the request fails on architecture and lists the slices present. architecture on any other image is rejected rather than ignored.

Package and dependency counts are checked against the minimum required JSON representation before graph allocation or schema cloning. Oversized graphs report the output byte budget; the compiler-path capacity reports maximum_rustc_hashes in hashes, independently of byte limits.