* fix(native): preserve Mach-O target selection in macOS tools
* fix(native): retain exact Mach-O slice signature coverage
* fix(native): admit x86_64 LIB64 signature selectors
* test(native): match signature checks to actual slices
* fix(native): recognize vtool refusals on private Mach-O aliases
Match vtool's container refusal to the operand the command received so a
parenthesized universal path keeps completed facts and the refusal
limitation. Expect codesign -a only when the target format is universal.
---------
Co-authored-by: Cursor Agent <[email protected]>
* feat(artifacts): inventory and extract U-Boot FIT images
open_binary refused U-Boot Flattened Image Tree files as an unsupported
format, so inspect_artifact and extract_artifact could not list or
recover a FIT's kernel, devicetree, ramdisk and loadable images.
Recognize an FDT v16/v17 with a top-level /images node and parse it with
the existing parseDevicetree. Each /images child becomes an inventory
entry named images/<unit-name>.bin that extracts its stored bytes from
embedded data, data-offset (past the 4-aligned devicetree end) or
data-position (from the file start). Image format_metadata records the
declared type, arch, os, compression, load and entry; root metadata
records the description, timestamp and configurations.
crc32, md5, sha1, sha256, sha384 and sha512 hash nodes are verified in
one streamed pass per image; other algorithms are reported unverified.
Signatures are reported, not verified, and nothing is decompressed,
decrypted or executed. Data outside the file, overlapping or duplicate
image data, a missing or repeated data source and hash mismatches fail
closed. The devicetree is bounded by the fixed 16 MiB allowance;
external image data is streamed from disk.
Refs #7129
* fix(artifacts): bound FIT configuration references and suggest analysis only for plain images
* fix(artifacts): retain buffered FIT stream cancellation
* test(artifacts): gate FIT faults on the observed read phase
* fix(artifacts): verify FIT links and summary identities
* fix(artifacts): bound FIT diagnostics and retain unknown signatures
* fix(artifacts): compare FIT declarations and resolved data links
* fix(artifacts): admit FIT raw probes through the shared loader contract
* fix(artifacts): preserve FIT field availability independently
* fix(artifacts): isolate nested entry metadata from reader facts
---------
Co-authored-by: gensokyo-dev <[email protected]>
Co-authored-by: gensokyo-dev <342580529+users.noreply.github.com>
* feat(artifacts): inventory and extract ESP32 firmware images
ESP32-family app and bootloader images (magic 0xE9) had no reader, so
open_binary refused OTA downloads, app partitions and bootloaders.
Add the esp-image container format behind inspect_artifact and
extract_artifact. A pure decoder reads each byte once and keeps only
fixed-size fields. Derived facts cover the chip, from a table cited to
ESP-IDF v6.1; segment classification as ESP-IDF's should_map and
should_load; checksum and appended SHA-256 results; app and bootloader
descriptors; Secure Boot v2 signature blocks, observed and unverified;
and undescribed trailing byte ranges. Truncation and undescribed bytes
make coverage unknown. ESP8266 images are refused with a specific
reason, and ciphertext stays unrecognized.
Add the format to every target, Evidence, snapshot, live-summary and
artifact vocabulary, document it, and add an optional verify:esp-images
lane that compares esptool-built images with esptool image-info and
espsecure signature-info-v2.
Refs #3615
* test(artifacts): run the ESP image lane on esptool v5.6.0
The lane compared descriptor fields only when esptool image-info printed
one, so a descriptor that REA reports and esptool does not print passed
without comparison. Compare the descriptor kind in both directions.
Record that the lane passed for all 13 images with esptool and espsecure
v5.6.0, the version the layout facts cite, as well as with 4.12.0.
* refactor(artifacts): define ESP image layout sizes once
The decoder and the derived facts each defined the segment-header,
appended-digest and flash-sector sizes, the erased-byte scan and the
offset formatter. Define them once with the other fixed-layout structures
and import them in both places. The signature slot count is the one
constant that drives the slot scan.
Correct the observedEnd comment: it is the end of the last chunk read.
* fix(artifacts): report ESP-IDF's segment length limit by chip
ESP-IDF's verify_segment_header rejects a segment of 16 MiB or more
unless the chip maps flash through 32-bit cache addresses
(SOC_SPI_MEM_SUPPORT_CACHE_32BIT_ADDR_MAP: ESP32-S3, P4, C5, C61, H4 and
S31 in v6.1). Record that capability in the chip table and add a
limitation for such segments; unknown chips get none.
`loaded` repeats should_load. It does not mean the bootloader accepts the
segment: verify_load_addresses still rejects one outside the chip's RAM,
which REA does not model. Say so in the docs, the skill reference and the
inventory limitation.
* test(artifacts): check ESP lane tools first and verify extraction
The lane started work before checking its tools, so a missing RISC-V
target, ld.lld, esptool or espsecure surfaced as a raw spawn or command
error. Check each one before building and name the variable to set. Warn
when esptool is not v5.6.0, the release the layout facts cite.
Assert each segment's class for the ESP32-C3 app, its variants and the
bootloader, as the Xtensa image already did. Extract the app and the
signed image through the CLI and compare every file with the bytes at
esptool's segment offsets or in the appended signature sector; the
source's SHA-256 must be unchanged and equal the reported root_sha256.
* docs(artifacts): state what the ESP image lane does not establish
The Verification section listed the lane's checks but not its gaps: only
ESP32-C3 and ESP32 images come from a real producer, no real project
build, OTA download or flash dump is inspected, no signature is verified,
and the lane does not run in CI.
Record the timing of the final lane run on esptool v5.6.0.
* fix(artifacts): compare ESP segment declarations
Segment entries hash only their data, so compare_artifacts reported a
segment that moved to another load address with the same bytes as
unchanged; only the image itself changed. Compare each segment's
load_addr and data_len as its declaration, as Node SEA and PyInstaller
members are compared, and count a changed declaration as a metadata
change, also when content is unavailable.
Header offsets are layout and the classification is derived, so neither
is compared.
* test(artifacts): run ESP lane commands as owned process trees
The lane ran its tools through execFile. A timeout killed only the direct
child, so a linker or assembler that Clang or GCC had started could keep
running while the lane removed its workspace. Run every command through
createVerifierCommands, check the tools inside withVerifierCleanup, and
remove the workspace only after commands.close() has stopped every tree;
a failed close keeps the workspace.
Only a nonzero exit now counts as a refusal or an expected esptool
failure; timeouts and spawn failures propagate. Supervising each command
roughly doubles the lane's run time.
* test(artifacts): cancel ESP decoding after its first decoded chunk
The cancellation test requested cancel or close on setImmediate, which
can land while the file is still being opened and checked, so it never
showed that decoding had started. It also passed against a reader that
ignored both requests while decoding. Acknowledge the decoder's first
chunk, then cancel or close, join the operation, and assert that no
later chunk reaches the decoder and that the reader ends closed.
* docs(artifacts): record which ESP metadata comparison ignores
Segments are compared by their declaration. The image's and the
signature blocks' format_metadata are not: every field comes from their
own bytes, which content comparison already covers, and a block's
image_digest_matches follows the image's content.
* docs(artifacts): state that ESP mmu_page_size is a base-2 logarithm
The app descriptor's mmu_page_size keeps ESP-IDF's name and value, the
MMU page size in log base 2 as esp_app_desc_t declares it, so 16 means
64 KiB. Say so in docs/esp-image.md and the schema comment; a reader
could otherwise take 16 as 16 KiB.
* fix(esp-image): retain member consumption and share lifecycle regressions
* test(artifacts): scope MCP catalogs and budget full-schema validation
* test(artifacts): verify compact output profiles and document bounded catalogs
* test(artifacts): close readers when cleanup-test admission fails
* test(artifacts): compare ESP image limitations with their owners
The decoder and reader tests froze complete limitation sentences, so a
harmless rewording failed them. The wording now has two owners:
ESP_IMAGE_LIMITATIONS in espImageFacts.ts, which the decoder, the inventory
and the reader use, and ESP_DESCRIPTOR_LIMITATIONS beside the descriptor
decoder. Tests compare delivered limitations with the owner, called with the
facts each case establishes (segment index, declared and available bytes,
offsets, chip, page range), next to the structured assertions they already
make. Delivered text is unchanged.
* test(esp-image): assert structured facts and owned guidance
* fix(esp-image): keep spi_flash_mode and descriptor fields before a truncated tail
ESP-IDF v6.1's esp_app_desc_t declares spi_flash_mode at byte 181, after
mmu_page_size, as the committed reference layout shows; the descriptor now
keeps it as stored. The decoder also required all 256 descriptor bytes,
though it reads none past byte 181, so a file ending in the reserved tail
lost the version, project, digest and revision facts it does hold. The app
descriptor now needs only its fields; the segment's truncation is reported
as before.
* test(esp-image): pin the P-192 public key digest to espsecure's layout
espsecure packs ECDSA signature blocks as "<BBBx32sB64s64s1031x" and
zero-pads a 48-byte P-192 point to 64 bytes, so the signature starts at
byte 101 for every SHA-256 curve. signature_info_v2 hashes sig_block[36:101]
and _digest_sbv2_public_key the curve ID with the padded point. No test built
a P-192 block; this one checks public_key_sha256 against that definition.
* fix(esp-image): keep each app descriptor field the file holds in full
A file can end anywhere inside the 256-byte esp_app_desc_t. The decoder
needed every field byte, so a cut at byte 176 dropped the version, project
and app_elf_sha256 it does hold. Once the 4-byte magic identifies an app
descriptor, each field is now kept when the file holds all of its bytes and
is null otherwise; observed_length records where the descriptor ends, so a
cut field is told apart from text that is not UTF-8. The fields after the
magic become nullable in the metadata schema.
* fix(esp-image): report a signature block whose image digest does not match
A CRC-valid Secure Boot v2 block can carry another image's digest. The
block recorded image_digest_matches false, but no limitation said so, unlike
a checksum or appended-SHA mismatch, so CLI and MCP summaries showed only
the generic unverified note. The mismatch is now a limitation; coverage
stays complete and signature_status stays unverified. The three other
signature-sector limitations move into ESP_IMAGE_LIMITATIONS with their
text unchanged, so all ESP image wording has one owner.
* refactor(esp-image): remove redundant descriptor byte guards
---------
Co-authored-by: gensokyo-dev <[email protected]>
* feat(artifacts): add U-Boot legacy uImage reader (part of #7129)
Recognize legacy uImages by their 0x27051956 magic and inventory them as containers: 64-byte header facts (arch/os/type/compression raw and named, load/entry addresses, name, timestamp) with header and data CRC32 verified, stored payload extractable as payload.bin, and multi-file images split from the stored size table into image-<n>.bin. Compressed payloads stay as stored; nothing is executed or decompressed. Wires the format through classification, the artifact reader dispatch, extraction, and the evidence/snapshot format vocabularies. Related to #7129 (uImage part only); HEX/SREC and FIT are out of scope.
* fix(uimage): preserve aligned component ranges and exact header facts
* fix(uimage): retain buffered members through consumption and cancellation
* fix(uimage): preserve active read and validation failure precedence
---------
Co-authored-by: gensokyo-dev <[email protected]>
* feat(artifacts): read UnityFS AssetBundle containers
inspect-artifact and extract-artifact now recognize the UnityFS signature
and treat format versions 6 through 9 as containers. Header facts (format
version, player/engine strings, declared size, dataflags, blocks-info
bounds and the declared 16-byte data hash) are retained alongside a
directory of CAB-* and .resS members. Member bytes stream on demand:
only the blocks covering the requested member are read and decompressed
with bounded expansion and cancellation.
Bundles declaring asset-bundle encryption keep header facts and report
unknown directory coverage. LZMA blocks-info reports unknown coverage;
LZMA member reads fail as unavailable while the directory stays
readable. Malformed counts, out-of-stream members and declared sizes
beyond the file fail as format errors. UnityFS classifies by bytes in
root detection, inventory graph construction and desktop inspection,
which emits a unityfs-header fact.
Validation: tsc --noEmit, oxlint (no new findings on touched files),
17 reader tests covering LZ4HC/uncompressed tables, blocks-info at end,
format 6 without padding alignment, multi-block members, malformed
input, unsupported compression, cancellation and abandoned streams;
full artifacts/domain/conformance suites pass. Verified against one
real YooAsset bundle (Unity 2021.3.32f1, format 8, LZ4HC): the
streamed SerializedFile contained its managed assembly byte-identical
to a UnityPy extraction. Other producer variants unverified.
* fix(unityfs): own bounded member reads and preserve partial evidence
* refactor(unityfs): clarify producer revision flag decisions
* fix(artifacts): preserve coverage authority and bound UnityFS block work
---------
Co-authored-by: gensokyo-dev <[email protected]>
* feat(artifacts): inventory and extract Intel HEX and Motorola S-record regions
open_binary refused Intel HEX and S-record programming files as an
unsupported format, so inspect_artifact and extract_artifact could not
answer which bytes load at which addresses.
Recognize both encodings by a fully valid first record and decode them
statically into address-contiguous regions, each an inventory entry named
0x<start>-0x<end>.bin and extractable as raw bytes. Root format_metadata
records the encoding and variant, record counts, unique and duplicate
bytes, address bounds, termination, declared start address, S0 header and
validated S5/S6 count. Gaps are never filled; start addresses are declared
facts, not proven entry points.
Checksum, record-type, byte-count, line-length, overlap-conflict, address
wrap and post-termination errors fail closed with the line number; a
missing termination record leaves coverage unknown. Decoding is bounded by
a 128 MiB file ceiling (decoded bytes are at most half the file and are
reserved after the size check) and 65,536 address-discontiguous runs.
Refs #7129
* fix(artifacts): refuse Intel HEX extended address records with a non-zero address
* fix(artifacts): bound HEX region copies and retain stream cancellation
---------
Co-authored-by: gensokyo-dev <[email protected]>
Share exact live target variants across open and session responses, preserving historical Evidence and snapshot admission. Consolidate equivalent ELF metadata aliases at their owner.
* feat(domain): parse and validate GameCube/Wii DOL headers
Add a pure parser for the 0x100-byte DOL header (7 text and 11 data
slots, BSS, entry point). Used slots must lie after the header and
inside the file and the 0x80000000..0x81800000 load window; text slots
are 4-byte aligned; sections may not overlap in memory or in the file;
the entry point must be inside a text section. BSS may overlap data
sections, as .sdata/.sdata2 do in shipped DOLs.
Refs #1741
* feat(binary): admit caller-selected GameCube/Wii DOL targets
* feat(ghidra): import GameCube/Wii DOL targets through a section-map pre-script
* test(ghidra): cover DOL launcher arguments, attestation and raw profile digests
* test(ghidra): add the real verify:ghidra:dol lane and DOL docs
* fix(ghidra): read only the DOL header and section ranges instead of the whole file
* test(ghidra): derive DOL lane addresses from readelf and admit Gekko from the install catalog
* fix(ghidra): attest DOL block permissions, space, overlay and load state
* fix(ghidra): keep load-image permissions optional for older inspections
* fix(contracts): accept DOL targets in binary_session output
* fix(ghidra): adapt the DOL lane to the current upstream address schema
Validate DOL load-image coordinates with the upstream observed-address
schema, drop a duplicate import left by the rebase, and re-pin the raw
profile digests to the unmodified upstream resolver.
* fix(ghidra): preserve bridge inventory symbol provenance
* fix(ghidra): align DOL verification with shared workflow contracts
* feat(domain): parse and validate GameCube/Wii DOL headers
Add a pure parser for the 0x100-byte DOL header (7 text and 11 data
slots, BSS, entry point). Used slots must lie after the header and
inside the file and the 0x80000000..0x81800000 load window; text slots
are 4-byte aligned; sections may not overlap in memory or in the file;
the entry point must be inside a text section. BSS may overlap data
sections, as .sdata/.sdata2 do in shipped DOLs.
Refs #1741
* feat(binary): admit caller-selected GameCube/Wii DOL targets
* feat(ghidra): import GameCube/Wii DOL targets through a section-map pre-script
* test(ghidra): cover DOL launcher arguments, attestation and raw profile digests
* test(ghidra): add the real verify:ghidra:dol lane and DOL docs
* fix(ghidra): read only the DOL header and section ranges instead of the whole file
* test(ghidra): derive DOL lane addresses from readelf and admit Gekko from the install catalog
* fix(ghidra): attest DOL block permissions, space, overlay and load state
* fix(ghidra): keep load-image permissions optional for older inspections
* fix(contracts): accept DOL targets in binary_session output
* fix(ghidra): adapt the DOL lane to the current upstream address schema
Validate DOL load-image coordinates with the upstream observed-address
schema, drop a duplicate import left by the rebase, and re-pin the raw
profile digests to the unmodified upstream resolver.
* docs(ghidra): qualify the DOL memory window
* refactor(ghidra): own DOL BSS ranges in the parsed header
* docs(ghidra): record real DOL workflows and task costs
* docs(ghidra): record final DOL integration verification
* fix(mcp): share the native procedure search row contract
* docs(ghidra): record public procedure search verification
---------
Co-authored-by: gensokyo-dev <[email protected]>
* fix(native): admit thin arm64_32 Mach-O targets for static inspection
Target admission mapped Mach-O CPU types only to x86, x86_64, arm and
arm64, so a thin arm64_32 object was rejected as "unsupported Mach-O
architecture" before list-architectures, inspect-macho or
inspect-signature could run, although the same slice inside a FAT file
was inventoried.
Recognize CPU_TYPE_ARM64_32 as its own Mach-O-only architecture with a
dedicated thin target variant, so ELF, PE and raw-binary paths cannot
carry it. Add its slice policy so 32-bit dispatch metadata refuses it
like other 32-bit Mach-O images instead of reporting a CPU mismatch.
Ghidra, Hopper and IDA have no verified arm64_32 lane and decline it
explicitly; Hopper's declined families now share one predicate.
Fixes#5713
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* ci(apple): run the thin arm64_32 CLI regression on macOS
The regression compiles an arm64_32 object with Apple clang, so it skips
on the Ubuntu jobs. List it in the macOS Apple artifact job and its
change scope so the job runs it and changes to it select that job.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
* feat(artifacts): read Godot PCK packs
Add a Godot Engine PCK artifact reader for standalone packs and packs
embedded behind an executable trailer (formats 2 and 3). Members are
uncompressed byte ranges verified against their declared MD5 on read.
Encrypted directories, sparse bundles, patch removal entries, unknown
flags and format 1 fail closed as unavailable; encrypted members are
reported without being read. inspect-artifact and extract-artifact
recognize standalone packs by their GDPC magic.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(artifacts): bound Godot PCK metadata and own member streams
Cap member paths at 4096 bytes and the retained directory at 64 MiB
before allocation. Read members with positional chunk reads through
pipeline so cancellation is typed, abandoned consumers release their
source, and the shared descriptor stays reader-owned. Retain the root
descriptor for godot-pck inventory, as for ZIP and universal Mach-O.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* feat(artifacts): complete Godot PCK facts, integrity and selected views
* fix(artifacts): preserve atomic-root inventory Evidence
* fix(artifacts): bound aggregate PCK member expansion
---------
Co-authored-by: Samucahub <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
Co-authored-by: gensokyo-dev <[email protected]>
* fix(domain): admit Qualcomm Hexagon (EM_HEXAGON, 164) ELF binaries
Resolve missing ELF admission case for Qualcomm Hexagon DSP targets (e_machine = 164, EM_HEXAGON).
- Add 'hexagon' to BINARY_ARCHITECTURES and HeaderArchitecture
- Map EM_HEXAGON (164) in elfArchitecture() to 'hexagon'
- Include 'hexagon' in GhidraProvider's SUPPORTED_ARCHITECTURES
- Gracefully decline 'hexagon' with Ghidra-directed diagnostic in Hopper and IDA
- Add unit tests for Hexagon ELF parsing and adapter admission checks
Fixes#4781
* fix(hopper,ghidra): add Ghidra guidance to Hopper Hexagon refusal and add Ghidra admission regression test
* feat(ghidra): support raw big-endian m68k analysis
Admit `m68k` for headerless raw-binary targets. Admission requires a
2-byte aligned entry and a mapping within 0x0..0xffffffff, and Ghidra
imports with BinaryLoader using 68000:BE:32:default, the default compiler
specification, and the `ram` default address space.
`6502` and `m68k` now share one RAW_ONLY_ARCHITECTURES owner, so detected
targets, Evidence subjects, snapshots, and lifecycle output contracts
exclude both instead of repeating a 6502 literal.
Ghidra's default 68000 language decodes the 68040 superset; separate
68020/68030/CPU32/ColdFire variants, Amiga/Atari containers, and reset
vector discovery remain unsupported. Windows Ghidra P0 still admits raw
6502 only.
Verified with Ghidra 12.1.4 on macOS against an 8-byte self-built fixture
(nop; nop; moveq #1,d0; rts at 0x1000): load image verified in `ram`,
entry decompiles to `return 1`.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
* test(ghidra): verify raw m68k analysis with real Ghidra
Add npm run verify:ghidra:m68k. The verifier loads a source-owned 22-byte
68000 fixture (annotated hex, no assembler) at 0x1000 and checks, through
the compiled CLI and stdio MCP server: the 68000:BE:32:default BinaryLoader
import, load-image parity, exact mapped bytes, both functions, the bsr.b
call reference, decompilation by address and file offset, rejection of a
misaligned entry and of a mapping past 0xffffffff without replacing the
active session, unchanged source bytes, and runtime cleanup.
Run with Ghidra 12.1.4 and JDK 21 on macOS arm64. Linux has not been
exercised; Windows Ghidra P0 does not admit m68k.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
---------
Co-authored-by: Claude Opus 5.5 (1M context) <[email protected]>
Admit Windows ARMNT/Thumb-2 and ARM64 PE targets using preserved COFF machine facts. Keep unsupported machine gates explicit and distinguish loader-selected versus configured language provenance.
Verified with real Windows Ghidra 12.1.4 CLI/MCP workflows, ARM fixtures and Microsoft-signed Coreinfo ARM64 / MSVCR120 ARM inputs. Resource limits, analyzer overrides, startup timeout observations and verification gaps are documented in the PR. Final CI and automated review passed.
Fixes#2186
* feat(ghidra): admit 32-bit big-endian PowerPC ELF
Recognize EM_PPC as a provider-neutral `powerpc` family that keeps its ELF
class, byte order, type and flags, and admit ELF32 big-endian ET_EXEC through
a bounded Ghidra lane. ELF64, little-endian, relocatable/shared objects and
EF_PPC_EMB are refused with a specific reason; Hopper and IDA do not gain
PowerPC support, and Windows Ghidra P0 is unchanged.
Ghidra's ELF loader selects PowerPC:BE:32:e500:default for a plain EM_PPC
header, which misreads 750-family code. The lane binds
PowerPC:BE:32:default/default in the profile (language_selection
"admitted-lane-v1"), imports with it and checks the reported language. A
language set through REA_GHIDRA_LANGUAGE_ID still replaces the lane default.
Add a source-owned fixture, fixed-encoding probe, GNU readelf cross-check and
the optional `verify:ghidra:ppc` real-provider lane, plus docs/ghidra-ppc.md.
Refs #1741
* style(ghidra): format shared PowerPC profile integration
* fix(ghidra): preserve caller-selected PowerPC languages
---------
Co-authored-by: morluto <[email protected]>
* fix(ghidra): report unapplied PE CodeView PDB limitation (#2259)
When Ghidra analyzes a PE target that references companion debug symbols
in its CodeView (IMAGE_DEBUG_TYPE_CODEVIEW / RSDS or NB10) debug directory,
Ghidra's headless analyzer cannot automatically locate or apply the companion
PDB from isolated runtime directories without companion admission. Procedure
names remain analysis-generated (FUN_<address>).
Parse CodeView debug metadata (RSDS GUID/age and NB10 age) during PE target
resolution, and report an Evidence limitation informing callers and agents
that referenced debug symbols were not applied and procedure names are
analysis-generated.
Fixes#2259
* test(pe): group CodeView header coverage separately
* fix(ghidra): avoid contradictory PDB limitations
* fix(android): make DEX descriptor pattern portable
* fix(ci): isolate Linux Hopper process fixture inventory
* fix(comparison): recheck historical calls-only changes (#2223)
* fix(comparison): preserve incomplete function call evidence
Keep missing, unknown, partial and unresolved call relationships unknown in function comparisons and reconstruction claims. Aggregate IDA caller and callee coverage without overwriting uncertainty.
Validate with 144 focused tests, typecheck/lint, docs checks and test discovery. Real providers were not verified; the full headless IDA session suite requires unavailable Windows native controls.
* fix(comparison): recheck historical calls-only changes
---------
Co-authored-by: morluto <[email protected]>
* style: format identity comparison and IDA runner
---------
Co-authored-by: morluto <[email protected]>
Co-authored-by: Lucien <[email protected]>
* feat(ghidra): admit raw NMOS 6502 analysis
* test(ghidra): require verified POSIX verifier cleanup
* test(ghidra): verify 6502 low-memory raw imports
* docs(skill): document raw NMOS 6502 support
* fix(ghidra): preserve requested Windows raw source paths
* docs(ghidra): include raw 6502 in Windows installation scope
* test(ghidra): verify Windows raw junction rejection through CLI and MCP
* fix(validation): make fixtures portable and preserve explicit Codex home
* fix(ghidra): attest raw mapping state and address space
* fix(ghidra): align diagnostics and regression groups
* fix(ghidra): bind raw attestation to language address space
* chore(ghidra): avoid verifier insertion conflict
* docs(ghidra): align linked raw admission guides
* fix(ghidra): retain 6502 verifier workspace on failure
* test(ghidra): isolate Windows authority doctor fixture
* fix(binary): re-admit Windows raw paths on session reopen
* test(binary): require fresh provider execution after path change
* Constrain 6502 to raw lifecycle and snapshot targets
* Bypass saved reads for unadmitted Windows paths
* Guard direct CLI snapshot replay on Windows paths
* fix(snapshot): gate Windows Ghidra replay on native admission
* fix(snapshot): constrain nested 6502 subjects to raw images
* test: make dyld slice fixtures portable
Reuse the domain ZIP format list for session schemas, verify full Harmony
CLI/MCP lifecycles, and correct native view guidance and audited docs drift.
Refresh locked dependency notices and gate inventory/provenance consistency,
including notice inputs in the cached documentation gate.
Fixes#2454
Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
* feat(ghidra): admit RISC-V ELF analysis through a pinned generic lane
Refs #2063.
Recognize `EM_RISCV` (243) as a provider-neutral family that carries its own ELF
facts, and bound the first Ghidra lane to little-endian `ET_EXEC` images that
declare the double-float ABI, pinned to `RISCV:LE:32:default` or
`RISCV:LE:64:default` with the `gcc` compiler specification.
Ghidra 12.1.4 is not reliable without the pin: its `riscv.opinion` pins
`variant="default"` for the 64-bit constraint and leaves the 32-bit constraint
with the comment that the loader "must choose between 'default' and 'AndeStar'
variants". Measured on the installed release tree, a source-owned generic RV32
ELF with no Andes declaration imports as `RISCV:LE:32:AndeStar_v5:gcc`, while
RV64 resolves to `RISCV:LE:64:default:gcc`. The lane therefore states the
language per width and refuses any caller-selected processor outside it instead
of inheriting the header guess.
Refusals name the declaration rather than falling back to a guessed profile:
big-endian images (Ghidra 12.1.4 ships no big-endian RISC-V language),
non-`ET_EXEC` images, `EF_RISCV_RVE`, `EF_RISCV_TSO`, and every float ABI other
than double, which the pinned generic compiler specifications model. The raw
e_flags bits stay in Evidence as declarations, not as instruction semantics.
Family recognition now has one owner for the ELF-metadata families:
`ELF_METADATA_ARCHITECTURES`, `isElfMetadataArchitecture`, and
`HeaderArchitecture` replace ten `Exclude<BinaryArchitecture, "mips">` sites that
would each have needed a second hand-edited member. `ghidraPinnedLanguage` is
that owner for the import commitment: the session readback previously re-derived
only the MIPS pin, so a profile committing any second pinned language failed with
a language-mismatch error before the bridge started.
Hopper and IDA refuse RISC-V by name, matching the MIPS wording. Caller-supplied
raw-binary entry keeps its existing four-family list; this lane is ELF-header
admission only.
Verification on macOS 15.7.7 arm64, Node 24.18.0, Ghidra 12.1.4 PUBLIC and JDK
21.0.12.1, with the host decompiler built from the release sources for
`os/mac_arm_64`: `npm run check` passes; `npm run test:fast` passes 396 test
files; `npm run verify:test-discovery` discovers all 917 test files exactly once.
Source-owned RV32 and RV64 double-float `ET_EXEC` fixtures complete
`rea analyze --provider ghidra` and `rea decompile rea_riscv_entry`, committing
`RISCV:LE:32:default` and `RISCV:LE:64:default` with `language_selection:
provider-pinned-v1`; the soft-float RV64 shape is refused with the named
float-ABI reason, and the full refusal matrix including the RV32 and
width-mismatch shapes is covered by unit tests. No `verify:ghidra:riscv` lane
script is included in this change; the runs above were performed manually against
the installed engine. Fork pull requests receive no CI check runs, so the
real-provider scope above is the evidence available for review.
* fix(ghidra): bound the RISC-V lane by declared flags and document it
Address review feedback on #2151.
`e_flags` bits outside `EF_RISCV_RVC | EF_RISCV_FLOAT_ABI` are now refused with
the offending value named, so a reserved or newer producer declaration cannot
enter the verified generic lane by falling through the RVE, TSO and float-ABI
checks.
Admission reads `e_type` only, so the refusal text no longer calls `ET_EXEC`
static: a fixed-address declaration does not establish static linkage, and
`PT_INTERP` and dynamic dependencies are not inspected. The session records that
boundary as a limitation rather than inferring a load model.
Add `docs/ghidra-riscv.md` in the shape of the MIPS lane guide (admission
boundary, refusal table, why the language is pinned per class, Evidence fields,
and what the fixtures do not establish), and correct
`docs/firmware-analysis.md`, which still told users that MIPS and RISC-V
decompilation had not been added.
Verification after the change: `npm run check` and `npm run docs:check` pass; the
RISC-V module tests pass (17 lane cases plus 9 parser cases); on Ghidra 12.1.4
with JDK 21.0.12.1 the RV32 and RV64 double-float fixtures still analyze and
decompile with `RISCV:LE:32:default` / `RISCV:LE:64:default` and
`language_selection: provider-pinned-v1`, the soft-float fixture is still refused
by name, and the new linkage limitation appears in the emitted Evidence.
* fix(ghidra): align RISC-V pinning with admission
Reuse the complete lane admission boundary when selecting a language, retain unsigned unsupported flag bits in diagnostics, and derive float ABI metadata from the source declaration.
* test(ghidra): verify RISC-V CLI and MCP workflows
Build source-owned RV32/RV64 fixtures with Clang and LLD, establish independent ELF and symbol facts with LLVM readelf, and verify real provider evidence, instruction bytes, calls, decompilation, schema projections, snapshot replay, refusal diagnostics, source immutability and cleanup.
Verified on macOS ARM64 with Ghidra 12.1.4, JDK 21 and LLVM/LLD 23.1.2. Dynamic linkage, big-endian, Andes and RVE/TSO workflows remain unverified; ISA attributes are not inspected.
---------
Co-authored-by: morluto <[email protected]>
Universal Mach-O admission reported the selected slice architecture while the IDA provider's private input copy handed the whole FAT container to upstream idalib, which then picked its own slice (x86_64 for /bin/cat) while Evidence labeled the session arm64. FAT parsing now exports the selected slice's byte range as universalSlice, and the private input copy writes exactly those bytes: the whole source digest still anchors the copy against admission-time changes, and the thinned result anchors the post-operation identity check.
Thin targets keep the whole-file copy, and mach-o-universal remains the container format across Evidence, snapshots, and provider routing.
Validation: typecheck, oxlint (no new warnings), oxfmt, and targeted vitest runs — ida adapters 12 files/116 tests, domain and services projects 60 files/736 tests, ghidra/hopper behavior 2 files/50 tests, filesystem and universal-extraction boundary 2 files/49 tests, native signature MCP 1 file/2 tests, plus a new thinning suite (5 tests, red before the fix: 3 failed). The thinned product was verified byte-identical to the `clang -arch arm64` input of a `lipo -create` universal fixture. Controlled fixtures only; no live IDA analysis was performed for this change.
Closes#2139
Keep FAT container format distinct from selected slice architecture through target admission, Evidence, snapshots, and CLI/MCP lifecycle results. Preserve native, Hopper, Ghidra, extraction, and dylib routing for the Mach-O family.
Reject selected FAT ranges overlapping their table or too short for a Mach-O header. Replace empty-slice fixtures with in-bounds executable headers and cover thin, universal, single-slice, FAT64, and swapped containers.
Validation: static and architecture checks, generated docs, targeted producer and public MCP tests. Provider routing uses controlled fixtures; no live Hopper, IDA, JADX, or Ghidra analysis was performed for this change.
* feat(native): admit raw binaries with caller-supplied architecture and entry
* fix(native): honor raw-binary preparation and entry identity
* fix(ghidra): stream raw snapshot attestation with cancellation
* fix(native): preserve and validate raw lifecycle entries
---------
Co-authored-by: morluto <[email protected]>
* feat(harmony): project HarmonyOS package inventory evidence
First HarmonyOS increment from #1431: HAP, HSP, and App Pack roots are
classified through the hardened ZIP reader and projected into an
execution-free application graph with packaging-model hints, exact
component paths and hashes, App Pack children by path, runtime-family
hints, and path-based N-API bridge hypotheses. HarmonyOS .har stays
unclassified by suffix because .har also names HTTP Archive JSON.
Adds the project_harmony_application_graph tool (CLI and MCP), synthetic
composition coverage, a pinned real-package verify lane, and docs.
* refactor(domain): share application graph projection scaffolding
The HarmonyOS projection mirrors the Android and Apple projections by
design, which pushed jscpd duplication over its 2.5 percent budget.
Extract the identical pieces (inventory input schema, projected
component schema and occurrence flattening, bridge candidate schema
factory, coverage assembly, comparator, canonical digest) into
domain/mobileApplicationGraphProjection.ts and route all three
projections plus the composition fixtures through it. Duplication drops
from 2.52 to 2.49 percent with no behavior change: projection ids,
classifications, and limitations are byte-identical.
* fix(harmony): address review, stable app-pack roots, and rotated fixture
- rebased onto main; the shared projection scaffolding now keeps main's
nativeRuntimeConvention loop and the harmony runtime families adopt the
same react-native/flutter/unity path conventions
- the .app App Pack suffix is now a root-input rule only (verified ZIP
magic in classifyRootFormat and BinaryTargetResolver); nested .app ZIP
members inside other packages keep the generic zip classification, with
a regression test, so existing inventories do not change
- the signing limitation now states that an empty signing list does not
mean unsigned because packaging-tool signatures live in the ZIP signing
block, and the app-pack root rule is disclosed as suffix-based
- repinned the real-package fixture to the current VHome 2.6.14-beta asset
after the upstream deleted v5.0.111-beta and replaced the v2.6.14-beta
asset within hours; the pin records both observed digests
---------
Co-authored-by: morluto <[email protected]>
* feat(native): admit bounded MIPS ELF analysis through Ghidra
Retain MIPS ELF identity and interpretation through Evidence and snapshots.
Keep provider admission bounded to ELF32 ET_EXEC o32 MIPS32r2, without
implicitly widening Hopper, IDA or Windows P0 coverage. Add source-owned
fixtures and an optional real-provider verifier; real Ghidra verification
remains pending in this contribution environment.
Refs #718
* fix(ghidra): bound MIPS floating-point mode admission
Reject explicit EF_MIPS_FP64 and EF_MIPS_NAN2008 declarations until their
semantics have a real-provider verification lane. Add PSP PRX/Allegrex,
o64/EABI and floating-point-mode refusal regressions. Document that the
header gate is not a full .MIPS.abiflags check and advance the profile lane
identity to invalidate older interpretation bindings.
Validation: 24 standalone checks against the transpiled production profile
module passed; the two new flag cases failed before this change. Clang 17
and LLD compiled the source-owned fixture for both endian targets. The
complete Vitest/typecheck suite and real Ghidra verification remain pending.
Refs morluto/rea#718; PSP support remains follow-up morluto/rea#1330.
* fix(native): validate MIPS ABI records before Ghidra admission
Read bounded ELF32 PT_MIPS_ABIFLAGS and SHT_MIPS_ABIFLAGS records through
the already-open target handle. Preserve declared ABI fields, reject
conflicting/duplicate/truncated records, and keep missing records unknown.
Wire inspection into target resolution before provider selection. Require
compatible declarations for the deliberately narrow Ghidra lane and bind
all ABI fields into profile identity (v3) for snapshot invalidation.
Clang 17 produced eight source-owned fixtures spanning both byte orders
and FPXX/FP32/FP64/soft-float modes with identical ELF header flags. The
new reader and policy distinguish them. 24 focused regression test bodies
passed after TypeScript transpilation using node:test in place of Vitest;
strict isolated TypeScript checking of the reader and target/result types
passed. These are limited checks, not full Vitest/project typechecking or
real-Ghidra verification, which remain pending.
Advances morluto/rea#718. PSP/Allegrex remains morluto/rea#1330 follow-up.
* fix(native): repair MIPS integration boundaries and test resolver behavior
Keep ELF targets out of Hopper Mach-O preparation and scope existing PE fixture types correctly. Move ABI tests into the filesystem boundary lane, use shared cleanup, add resolver regressions, apply the pinned formatter, and register the real MIPS verifier through the existing package-command pattern. Validated with repository-pinned Node/npm, check:ci, actual focused Vitest, discovery and docs:check. Real Ghidra conformance remains pending.
* fix(test): honor caller-selected JDK in real MIPS verification
* test(native): strengthen MIPS Ghidra conformance
Cross-check the source-owned fixture with bounded GNU readelf inspection, compare production ELF/ABI interpretation and independent symbol addresses, and verify effective Ghidra language/compiler identity through existing public operations. Add a source-owned fixed-encoding probe for immediate and conditional branch checks plus global/string byte assertions.
Add 16 assertion/producer-boundary regression cases without widening production admission, changing provider contracts, or introducing proprietary dependencies. Local focused suite: 99 passed, one existing root-permission test skipped. Typecheck, lint/module boundaries, formatting and 700-file discovery pass; full hosted checks and real Ghidra verification remain pending for this checkpoint.
* fix(ghidra): preserve native-tool architecture checks after MIPS integration
The conflict-free upstream merge failed TypeScript compilation because includes on the union of MIPS-only and other architecture arrays requires never. Use equality-based membership without casts or changing accepted native hosts. Extend the existing native-decompiler rejection table with a MIPS executable at a Linux x64 tool path; retain upstream FAT and unknown-header behavior. Hosted verification follows this commit; no success is claimed yet.
---------
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
- Carry the Info.plist that declared the program file on the target as
bundleInfoPlist, so inspect_plist defaults to it even when the program
file is a symlink into another bundle directory.
- Accept a readable Info.plist symlink, as reading the plist directly did.
- Treat only a missing path as an absent layout or wrapper; a permission
denial now reaches the resolver's permission-denied classification.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* feat(ghidra): admit explicit DOS COM analysis
Carry caller-selected COM interpretation through CLI, MCP and target
identity, seed real-mode entry/context before Ghidra analysis, and verify
complete loaded bytes, source mappings and measured register context.
Keep original binaries unchanged and PSP/runtime coverage explicit.
Assisted-by: Nitori (gensokyo-skills:nitori-reverse-engineering)
* fix(verification): validate COM package selection before allocating fixtures
Validate DOS MZ load modules and import them with Ghidra's 16-bit
real-mode profile. Preserve complete observed inclusive function body
ranges through inventory, function analysis, Evidence and snapshots.
Add a public source-generated DOS CLI/MCP verification lane and
regressions for malformed headers, body membership and cache profiles.
Assisted-by: Kogasa (gensokyo-skills:kogasa-surprise-testing)
Assisted-by: Suika (gensokyo-skills:suika-scatter-gather-planning)