Thomas Piacentini
← Atelier

Chapitre

inspect-artifact

Texte original · anglais

inspect-artifact reads the file as a container, hashes it, names its format, and returns one Evidence record that tells you which tool to run next.

You built ledger-arm64-O1 from ledger.c and ledger-main.c with clang. An agent is about to analyze it, and you want the first call to be cheap, offline, and honest about what it did not do. inspect-artifact is that call. It needs no Ghidra, it returns in about a second, and its record ends with the name of the next tool.

When you finish this section, you can run inspect-artifact on a file you built and read its manifest and its one occurrence. You can also say why the record names open_binary as the next probe.

One command, one record

the command and its exittextFull source
$ rea inspect-artifact fixtures/ledger-arm64-O1 --format json
[exit 0] (…) -> 03-inspect-artifact.json
Two lines from the command index. The time in parentheses changes on every run and is cut.
Original source
title: the command and its exit
source: capture/out/00-commands.txt
lang: text
note: Two lines from the command index. The time in parentheses changes on every run and is cut.
---
$ rea inspect-artifact fixtures/ledger-arm64-O1 --format json
[exit 0] (…) -> 03-inspect-artifact.json

The record comes from the provider rea-artifact-graph, named "REA safe artifact graph provider", version 1. No analysis engine runs. The tool opens the file, hashes it, names the format from the header, and builds an inventory of the file as a container.

Artifact graph: the inventory rea builds of one file and the files inside it. A node is one distinct content, identified by its SHA-256. An occurrence is one place where that content appears, with a logical path. An edge joins a container to a member.

the head of the recordjsonFull source
{
  "evidence_id": "ev_0e878985…",
  "subject": {
    "name": "ledger-arm64-O1",
    "digest": {
      "sha256": "01b5d08b…"
    },
    "local_path": "$CAPTURE/fixtures/ledger-arm64-O1",
    "format": "mach-o",
    "architecture": null
  },
  "provider": {
    "id": "rea-artifact-graph",
    "name": "REA safe artifact graph provider",
    "version": "1"
  },
  …
  "operation": "inspect_artifact",
  "parameters": {
    "integrity_policy": "fail"
  },
  …
}
The substeps array and everything after it are cut. The capture kit masks local_path.
Original source
title: the head of the record
source: capture/out/03-inspect-artifact.json
lang: json
note: The substeps array and everything after it are cut. The capture kit masks local_path.
---
{
  "evidence_id": "ev_0e878985…",
  "subject": {
    "name": "ledger-arm64-O1",
    "digest": {
      "sha256": "01b5d08b…"
    },
    "local_path": "$CAPTURE/fixtures/ledger-arm64-O1",
    "format": "mach-o",
    "architecture": null
  },
  "provider": {
    "id": "rea-artifact-graph",
    "name": "REA safe artifact graph provider",
    "version": "1"
  },
  …
  "operation": "inspect_artifact",
  "parameters": {
    "integrity_policy": "fail"
  },
  …
}

The SHA-256 is the one 00-fixtures.txt lists for the file, and every later record about this file carries the same digest. The format is mach-o, read from the magic bytes. The architecture is null. This provider does not read cputype, and the next section shows the tool that does. The one parameter is integrity_policy, set to fail.

The manifest and the occurrence

The record has one substep, inventory_artifact, with status: "completed" and its own Evidence record nested inside. Its manifest counts the graph.

the manifest, the node, and the occurrencejsonFull source
"manifest": {
  "manifest_id": "agm_8e2e5c4c…",
  …
  "graph_sha256": "b6c8ab6f…",
  "node_count": 1,
  "occurrence_count": 1,
  "edge_count": 0
},
"nodes": [
  {
    "artifact_id": "art_8906b5a7…",
    "format": "mach-o",
    "sha256": "01b5d08b…",
    "size": 33872,
    …
    "content_state": "materialized",
    "limitations": []
  }
],
"occurrences": [
  {
    "occurrence_id": "occ_35a9bb48…",
    …
    "logical_path": ".",
    "entry_kind": "file",
    "artifact_kind": "executable",
    "artifact_format": "mach-o",
    "declared_size": 33872,
    …
    "executable": true,
    "encrypted": false,
    "hash_status": "verified",
    …
  }
],
Cut from the inventory substep to the manifest counts, the one node, and the one occurrence.
Original source
title: the manifest, the node, and the occurrence
source: capture/out/03-inspect-artifact.json
lang: json
note: Cut from the inventory substep to the manifest counts, the one node, and the one occurrence.
---
"manifest": {
  "manifest_id": "agm_8e2e5c4c…",
  …
  "graph_sha256": "b6c8ab6f…",
  "node_count": 1,
  "occurrence_count": 1,
  "edge_count": 0
},
"nodes": [
  {
    "artifact_id": "art_8906b5a7…",
    "format": "mach-o",
    "sha256": "01b5d08b…",
    "size": 33872,
    …
    "content_state": "materialized",
    "limitations": []
  }
],
"occurrences": [
  {
    "occurrence_id": "occ_35a9bb48…",
    …
    "logical_path": ".",
    "entry_kind": "file",
    "artifact_kind": "executable",
    "artifact_format": "mach-o",
    "declared_size": 33872,
    …
    "executable": true,
    "encrypted": false,
    "hash_status": "verified",
    …
  }
],

One node, one occurrence, no edges. The executable is its own container and has no members. The limitation "Artifact has no child container entries." says the same in words. The node reports size: 33872, the byte count in 00-fixtures.txt, and content_state: "materialized". The occurrence has logical_path: ".", the root, and artifact_kind: "executable". For an .asar or a .zip, this is where every member file would appear with its own node and path.

Branches and the next probe

what the inspection did not dojsonFull source
"unexplored_branches": [
  {
    "branch_id": "aib_ff20889e…",
    "reason": "format-specific-analysis-required",
    "detail": "open_binary was not run by provider-neutral artifact inspection.",
    "next_probe": {
      "operation": "open_binary",
      "rationale": "Bind one deep native provider before function-level analysis.",
      "authority": "native-analysis-provider"
    },
    …
  }
],
…
"confidence": "observed",
"authority": "shipped-artifact",
"environment": null,
"limitations": [
  "Artifact has no child container entries.",
  "Inspection derives bounded static hypotheses and next probes; it does not execute the artifact or claim format-specific semantics."
],
The hypotheses and contradictions arrays are empty and are cut with the coverage block.
Original source
title: what the inspection did not do
source: capture/out/03-inspect-artifact.json
lang: json
note: The hypotheses and contradictions arrays are empty and are cut with the coverage block.
---
"unexplored_branches": [
  {
    "branch_id": "aib_ff20889e…",
    "reason": "format-specific-analysis-required",
    "detail": "open_binary was not run by provider-neutral artifact inspection.",
    "next_probe": {
      "operation": "open_binary",
      "rationale": "Bind one deep native provider before function-level analysis.",
      "authority": "native-analysis-provider"
    },
    …
  }
],
…
"confidence": "observed",
"authority": "shipped-artifact",
"environment": null,
"limitations": [
  "Artifact has no child container entries.",
  "Inspection derives bounded static hypotheses and next probes; it does not execute the artifact or claim format-specific semantics."
],

rea builds one unexplored branch for every next probe it lists, with the reason format-specific-analysis-required. The second limitation is a constant in the same file. The record is observed with authority shipped-artifact: rea read the bytes of the file you gave it and derived nothing from another record. The coverage block reports complete-within-substeps with one substep, one observation, one branch, one probe, and nothing omitted.

Fig. 3.1the inspect-artifact record for ledger-arm64-O1tree
One cheap read gives a hashed subject, a one-node inventory, and an explicit branch that names open_binary as the next probe.
One cheap read gives a hashed subject, a one-node inventory, and an explicit branch that names open_binary as the next probe. Read from the top. The teal box is the record, the green boxes are what it saw, and the amber box is what it left for a later tool. From capture/out/03-inspect-artifact.json.

Original SVG

Original source
id: fig-3-1
kind: tree
title: the inspect-artifact record for ledger-arm64-O1
claim: One cheap read gives a hashed subject, a one-node inventory, and an explicit branch that names open_binary as the next probe.
caption: Read from the top. The teal box is the record, the green boxes are what it saw, and the amber box is what it left for a later tool. From capture/out/03-inspect-artifact.json.

What the record is not

The record is not a format parse. It holds no load commands, no sections, no symbols, and no entry point. For those, the next section runs inspect-macho. For a function, Part 4 binds Ghidra with open_binary, the tool the branch names. The whole record is 198 lines, and most of them repeat the subject digest. That repetition is the point: every nested record can stand alone and still name the same bytes.

Original source
- Run `inspect-artifact` first: it needs no engine and returns in about a second.
- Read `node_count`, `occurrence_count`, and `edge_count` before you treat a container as flat.
- Treat `unexplored_branches` as the list of tools the record did not run.
- Carry the subject `sha256` into every later record about the same file.

Sources: capture/out/00-commands.txt, capture/out/00-fixtures.txt, capture/out/03-inspect-artifact.json; rea 6.7.0 at 27dc42d: src/application/InvestigationProviders.ts, src/domain/artifactInspection.ts, src/artifacts/inventory/reader.ts

Code et livrables 1
Ce que vous en retenez

Enregistré dans votre carnet sur cet appareil.

Sources et limites

Les repères de lecture sont déclaratifs. Les résultats de tests sont enregistrés séparément ; ils valent pour les cas exécutés.

AI Engineering from Scratch · 3f9715c · MIT

Télécharger ce fichier ↓