Commit Graph
31 Commits
Author SHA1 Message Date
860fdd8fbe feat(macho): report per-slice encryption status in inspect_macho (#5359)
* refactor(macho): extract non-throwing container and slice load-command readers

Split readMachoImage into readMachoContainer, which classifies a file as
thin, FAT or not Mach-O and keeps FAT range issues per slice, and
readSliceLoadCommands, which reads one slice header and frames its load
commands, returning every command framed before a failure.

readMachoImage keeps its strict policy on top of them: commands framed
before a framing defect still decode first, an unsupported FAT slice
still yields to a later malformed one, and the first FAT range issue is
still reported. A characterization test pins that precedence for inputs
with several defects; it passes against both the previous and the new
implementation.

Part of #4306.

* feat(macho): report per-slice encryption status in inspect_macho

inspect_macho now reports the LC_ENCRYPTION_INFO(_64) state of every
slice in a new `encryption` collection, read in process from load
commands without reading code bytes.

- Each slice is encrypted, not-encrypted, absent, malformed or unknown.
  Absent requires a completed load-command walk; malformed and unknown
  carry a reason and keep commands read before the failure.
- The named range is end-exclusive in slice and input-file coordinates
  and maps to file-backed segment fragments, VM addresses and the
  overlapping nonempty sections.
- A ciphertext range adds a limitation naming its input-file bytes.
- The in-process read and the native tools are bracketed by one regular
  file descriptor and stat identity checks; a change reports
  artifact_changed. A missing or inaccessible target keeps the tool
  facts and reports encryption as non-exhaustive, never absent.
- Retained Evidence recorded before this field is parsed by the owning
  schema and reports encryption as unknown in the macho view and in
  managed/native verification.

Fixes #4306

* fix(macho): keep slice facts and failures under large tables and close errors

- Frame load commands through a visitor instead of retaining every
  command, restoring the pre-refactor memory profile (2M eight-byte
  commands: peak heap 370 MiB -> 0.9 MiB).
- Confirm a large FAT container from a valid slice's Mach-O header so a
  sibling's range fault stays per slice instead of hiding healthy slices.
- Name only sections whose bytes the range and their segment both cover.
- Throw inspection failures inside the owned descriptor callback so a
  close failure keeps them as its primary error.

* fix(macho): map encrypted ranges only within segment VM extents

mapRange gave every file-backed segment fragment VM addresses, even for a
segment with no VM size, whose file bytes dyld never maps, and past vmsize
when a segment declared more file bytes than memory.

Read vmsize with the segment. A segment with no VM size keeps its fragment
with null VM addresses. A file size beyond the VM size, or a VM range past
the address space, makes the mapping unknown with a reason while the slice
keeps its encryption status.

The docs also claimed the encryption read never reads code bytes. Like
every Mach-O container read, a 0xcafebabe input of at most 16 MiB is read
whole to rule out a JVM class file; say so.

* fix(macho): read encryption through the admitted descriptor facts

Main's withArtifactRegularFile now hands callers the admitted handle and
its initial stats. Use those facts instead of re-stating the handle, and
pass the legacy Evidence payload straight to createEvidence.

* fix(macho): retain a finished report when the target close fails

The file owner keeps the completed callback value when the descriptor close
fails or cancellation arrives during close. inspect_macho now validates that
value and retains the report as the error's partial observation, as
inspect_signature does, instead of returning only the cleanup or
cancellation error.

* test(native): supply readEncryption in merged dyld refusal cases

main's new dyld refusal cases build NativeMachoInspectionContext without
the encryption reader this branch requires.

* test(views): bind the Mach-O encryption page to its facet

main's facet-ownership case builds inspect_macho Evidence without the
encryption coverage this branch requires; the fixture now carries one
slice row and the loop checks the encryption page like the other facets.

* fix(macho): bound encryption commands and range mapping across slices

A slice retains at most two encryption load commands, since a second one
already makes it malformed; the reason keeps the full count. Range mapping
in every slice shares one budget of segment and section records, charged
from each segment command's capacity before it is decoded. A slice past
the budget keeps its encryption status and reports an unknown mapping.

* style(macho): format encryption reader and binding test

* fix(macho): stop retaining segments past the mapping budget

Segment commands are collected during framing only while their mapping
records still fit in the budget shared by all slices, and the budget is
charged only when the slice maps its range.

* docs(macho): clarify retained mapping allowance

* fix(ci): own the Mach-O encryption test in Apple scope

---------

Co-authored-by: t <t@t>
Co-authored-by: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
2026-10-29 18:19:39 +08:00
gensokyo-dev e3d8d20236 fix(native): preserve dispatch metadata format refusals (#8013)
* fix(native): preserve dispatch metadata format refusals

* test(native): normalize CLI format refusal envelopes

* test(native): assert admitted metadata target paths
2026-10-29 15:48:42 +08:00
gensokyo-dev 45cc935964 fix(native): accept walkable Mach-O command tables (#8002)
* fix(native): accept walkable Mach-O command tables

* test(native): verify large command tables through macOS CLI and MCP
2026-10-29 14:39:20 +08:00
morluto 640e19d79d fix(native)!: own Mach-O load commands in binary reader (#7952)
* fix(native): retain selected Mach-O slice identity

* fix(native)!: own Mach-O load commands in binary reader

* fix(native): preserve malformed Mach-O and image base limits

* fix(native): preserve leading BOM in Mach-O names

* test(native): update Mach-O provenance verification

* fix(native): retain completed Mach-O inspection on cleanup failure

* fix: bound Mach-O projected section output

* fix(native): own retained Mach-O command bytes

* fix(native): retain Mach-O outcomes on final cancellation

* fix(cli): preserve caller path in partial evidence

* fix: reserve Mach-O projection envelope budget
2026-10-29 08:37:36 +08:00
morluto aa9043f6b6 fix(native): preserve qualified Swift protocol identities 2026-10-24 12:07:33 +08:00
morluto a4e4863029 test(apple): distinguish root discovery from dylib resolution coverage 2026-10-24 08:22:14 +08:00
morluto 93de7bc7e3 fix: preserve mixed Swift demangler record outcomes 2026-10-24 02:35:59 +08:00
8b478d0f15 fix(native): preserve Mach-O install-name whitespace (#4979)
* fix(native): preserve whitespace in otool load-command strings

otool prints dylib install names and rpaths as `<key> %s (offset %u)`
with exactly one separator space around the recorded string. The parser
trimmed the value and stripped the offset suffix with `\s+`, so a valid
install name such as "  leading.dylib  " became "leading.dylib" in both
the dependency inventory and the raw load-command fields.

Parse these offset-suffixed string fields by their exact format and
strip only the single-space offset suffix.

Fixes #3384

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

* test(native): verify exact Mach-O install names through CLI and MCP

---------

Co-authored-by: Bhaaraadwaj <bharadwajdaya@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-23 06:02:37 +08:00
Kaoru0822andClaude Opus 5.5 c02945bc44 feat(native): decode Swift nominal type declarations from __swift5_types (#6257)
* feat(native): decode Swift nominal type declarations from __swift5_types

Fill swift_decls from the nominal type context descriptors that survive
stripping: kind (class, struct, enum, actor), own and qualified name,
module, generic flag, anonymous-context flag, metadata accessor and
descriptor location, with evidence citing the type record. Unresolved
parent contexts keep qualified_name null and the entry partial.

One shared reader walks each context once; class vtable recovery now
consumes its descriptors instead of re-reading the records, so a type
record that cannot be read keeps both facets partial. Remove the
never-populated swift_decls fields that descriptors do not carry.

Fixes #5341

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

* fix(native): bound Swift context walks and decode vtables in record order

Address review of the nominal-type reader:

- Cache intrinsic context depth (distance to the module) so a cached
  parent cannot bypass the 32-context bound, and never cache a stop that
  depends on where the walk began. Bound qualified names to 4096
  characters so nesting cannot multiply name storage.
- Visit each descriptor as soon as its kind and name are read, so a
  class vtable spends the record budget in record order instead of after
  every later declaration.
- Report an accessor outside executable mapped code as null and keep the
  declaration partial.
- Forward a name failure to vtable coverage only when the record is a
  class or its kind is unknown.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 00:19:15 -07:00
Kaoru0822andClaude Opus 5.5 ef5ab1f1f2 fix(apple): decode NIBArchive booleans with AppKit's polarity (#6255)
The NIBArchive value decoder read type tag 4 as true and 5 as false, the
reverse of what ibtool writes and AppKit's decoder returns, so every
compiled-nib boolean in decode-interface-builder output was inverted. The
test fixture encoder shared the reversed mapping, hiding it in round trips.

Name the value-type tags once and use them in both the read and skip
paths, fix the fixture encoder, pin raw tags in a unit test, and make
verify:interface-builder compare both polarities with ibtool --export.

Fixes #5541

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 23:32:21 -07:00
gensokyo-dev f86206535f fix(ci): validate artifact MCP replies against named schemas (#6021)
Bind artifact lifecycle and analysis calls to their advertised named SDK
output schemas, sharing discovery per connection and rebinding on reconnect.
Reject schema overrides and ambiguous catalogs, preserve request options,
and retain primary failures while attempting all owned cleanup.

Add real-stdio boundary regressions for malformed output, discovery reuse,
reconnection, schema ambiguity, override rejection and timeout preservation.

Assisted-by: Reimu (gensokyo-skills:reimu-incident-triage)
Assisted-by: Kogasa (gensokyo-skills:kogasa-surprise-testing)
2026-10-21 23:28:12 +08:00
morluto d0067ba34a fix(native)!: preserve Swift pointer nullability and import bindings
Resolve relative pointers using field-owned ABI encodings. Keep null, raw, rebased, imported and unknown targets distinct, and retain imported conformance rows with exact Mach-O binding provenance. Decode legacy rebase membership with source-derived memory and work bounds, preserving validated prefix facts when streams are incomplete.

Validation: check:ci, docs:check and build passed; 158 source tests and 20 schema/acceptance tests passed. Ten source-built Apple dispatch variants and four issue-specific CLI/MCP workflows passed without executing targets. Paired cold/warm CLI/MCP cost observations retained locally; model tokens and maximum-sized input runtime were not measured.

Closes #5683
Closes #5685

BREAKING CHANGE: Swift conformance type_name and protocol_name can be null when names cannot be resolved; consumers must use typed type_binding and protocol_binding evidence. MachoImportBinding replaces the former Objective-C-specific binding type and schema.
2026-10-21 08:17:12 +08:00
morluto 1bb2fa23de test(apple): match plist provenance to the selected workflow 2026-10-21 05:50:50 +08:00
morluto 5518363aa6 test(artifacts): compare matching resource-root workflows 2026-10-21 05:28:03 +08:00
23196daa11 fix(apple): read LC_LAZY_LOAD_DYLIB_INFO dependency payloads (#5650)
* fix(apple): read LC_LAZY_LOAD_DYLIB_INFO dependency payloads

Parse the linkedit install name for lazy-load info commands, resolve those
edges without traversing them, and stop inspect-macho from treating an otool
dependency list as exhaustive when that payload is omitted.

Fixes #5551
Fixes #5649

Co-authored-by: morluto <morluto@users.noreply.github.com>

* fix(apple): bound lazy-load payload reads and image offsets

Reuse identical LC_LAZY_LOAD_DYLIB_INFO ranges, cap their combined size,
and reject flag or chain-start offsets outside the slice VM range.

Co-authored-by: morluto <morluto@users.noreply.github.com>

* fix(apple): bound lazy-load string scans and contained reads

Reject payload strings that start in the 24-byte header, validate each
file-offset string once, and reuse payload bytes already read for a
containing range.

Co-authored-by: morluto <morluto@users.noreply.github.com>

* fix(apple): reject cached lazy-load strings outside a payload

Remember each cached string's terminating offset and require that NUL to
fall inside the payload being decoded.

Co-authored-by: morluto <morluto@users.noreply.github.com>

* fix(apple): bound lazy-load payload range lookups

Count containment checks across earlier payload reads and stop at the
existing 16 MiB work limit.

Co-authored-by: morluto <morluto@users.noreply.github.com>

* fix(apple): count every lazy-info string reference

Charge repeated and overlapping LC_LAZY_LOAD_DYLIB_INFO scans against the
16 MiB validation budget, and stop a scan once that budget is exhausted.

Co-authored-by: morluto <morluto@users.noreply.github.com>

* fix(apple): cap retained lazy-info dependency results

Stop after 1024 LC_LAZY_LOAD_DYLIB_INFO commands so a load-command region
full of identical commands cannot become a million traced edges.

Co-authored-by: morluto <morluto@users.noreply.github.com>

* fix(apple): reject a lazy-load path NUL in the final byte

dyld stops the load-path scan before the payload's last byte, so a
terminator in that byte leaves the image malformed.

Co-authored-by: morluto <morluto@users.noreply.github.com>

* fix(apple): keep may-be-missing lazy loads optional

A missing LC_LAZY_LOAD_DYLIB_INFO with the may-be-missing bit is weak, so the lazy finding says dyld continues on first use instead of failing.

Co-authored-by: morluto <morluto@users.noreply.github.com>

* fix(apple): cap lazy-info symbol string references

A payload of distinct one-byte symbol strings stayed under the byte budgets while caching one entry per offset. A slice now stops at 65536 symbol references.

Co-authored-by: morluto <morluto@users.noreply.github.com>

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: morluto <morluto@users.noreply.github.com>
2026-10-21 03:33:07 +08:00
Kaoru0822andClaude Opus 5.5 95b561f2e9 fix(native): retain Apple dispatch metadata descriptor cleanup ownership (#4745)
* fix(native): retain Apple dispatch metadata descriptor cleanup ownership

inspect_native_dispatch_metadata opened the target with raw
openRegularFile and closed it in a finally block, so a rejected close
replaced integrity, capacity or cancellation failures and left no owner
to retry. The read now uses the shared owned regular-file operation in a
client resource scope; the physical failure stays the cleanup primary,
client close retries the exact descriptor, and a confirmed release
clears the original error's cleanup uncertainty. Decoding runs after the
descriptor is released.

Fixes #4743

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

* fix(verify): give the Apple dispatch verifier an owned file scope

Pass an ArtifactResourceScope to inspectAppleDispatchMetadata and close
it after inspection so a failed descriptor close is retained and retried
rather than reaching an undefined scope.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 04:20:40 -07:00
Yijie Luandbaron 3b0ea590fc fix(native): honor Mach-O chained pointer locations and library ordinals (#1709)
* fix(native): honor Mach-O chained pointer locations and library ordinals

Walk declared page starts and next links before decoding chained pointers,
preserving plain pointers between fixups, zero-offset rebases, and known
facts in malformed chains. Interpret positive dylib ordinals with dyld's
special-ordinal cutoff so valid dependency provenance is retained.

Give the Swift verification fixture an explicit module name so link-mode
filenames do not produce invalid identifiers.

Validation: 46 focused regressions; pre-fix reproductions in an isolated
copy; real macOS CLI/MCP verification of 10 compiled Objective-C/Swift,
arm64/arm64e, chained/legacy and symbols/stripped fixture variants.

* perf(native): reuse parsed Mach-O chain membership

* test(native): validate parsed-chain repair against local gates

* test(native): pin chained fixup page failure provenance

* fix(native): bound parsed chained page facts and metadata

* test(server): pin shared partial Evidence provenance

---------

Co-authored-by: baron <richstyles@gmail.com>
2026-10-13 07:11:26 +09:00
morluto e19879328a refactor: prune obsolete provider and verification code
Remove unused imports, private inferred types, superseded limit constants and a duplicate MCP assertion. Preserve intentional field omissions while deleting unused local bindings.

Validation: typecheck, lint, formatting, unused-code and architecture checks pass. Runtime behavior is unchanged.
2026-10-12 08:59:14 +08:00
Arthasandmorluto b777e5d4d8 fix(wasm): preserve debug module names in WABT output (#1967)
* fix(wasm): preserve debug module names in WABT output

* fix(native): accept otool dylib-use options

Preserve modern dylib options while requiring one encoding metadata row and retaining duplicate and multiline-output checks. Share field parsing between preservation and validation.

Verify delayed dylibs through real macOS CLI/MCP workflows and refresh the high-address golden with the provider's -h -l capture. Validated 85 focused native tests, npm run check, and verify:macos-bundle on macOS arm64.

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-12 05:44:15 +08:00
morluto 3b490a743f test(apple): consolidate keyed archive integer verification (#1933) 2026-10-12 02:34:24 +08:00
morlutoandCursor Agent b4db35f092 fix: bind identity and coverage to the producing rule (#1513)
* fix: bind identity and coverage to the producing rule

Logical paths, container families, examined bytes, path-segment conventions, destination collisions, file-id leases, Mach-O CPU selection, and firmware cleanup now follow the observation that owns each rule.

* fix: fail unmatched Mach-O slices and keep firmware roots independent

A fat target whose architecture lipo did not list now stops before slice tools run. Chained fixups still count unread LC_DYLD_INFO rebase bytes, and a firmware cleanup failure no longer blocks the next workspace.

* test: cover path identity and runtime families at the projection boundary

NFC joins and segment conventions are now asserted by the path resolver, the Apple projection, and a real APK inventory instead of helper unit tests.

* test: expect unread dyld rebase bytes in the Apple dispatch fixture

The legacy LC_DYLD_INFO fixture keeps a rebase range this reader does not decode, so the public CLI and MCP check now requires that partial coverage beside the bind opcodes.

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
2026-10-10 17:14:47 +08:00
Ysopwnable ce16f7bc95 test(native): verify macOS provenance through real CLI and MCP
Verify five native operations through the public CLI and real SDK stdio MCP using a source-built macOS fixture. Retain normalized provenance, response parity and unchanged input bytes.

Refs #1430, #1065, #723.
2026-10-10 13:05:12 +08:00
morluto 392f1f3107 refactor: consolidate boundary semantics and prune redundant tests
Share parser modes and collision-free composite keys, enforce architecture guards, and consolidate redundant unit assertions into filesystem, CLI, MCP, and producer-backed workflows. Preserve concurrent upstream boundary fixes and exclude legacy Objective-C class-name bookkeeping labels.

Validation: source/composition suites (3358 passing), focused canonical digest and MCP checks, typecheck/lint, Knip, formatting, architecture guards, and real Foundation archive and Apple legacy simulator CLI/MCP verification. No real Hopper, Ghidra, or IDA verification claim.
2026-10-10 05:56:01 +08:00
morluto b112d44a39 fix(native)!: preserve unknown Objective-C metadata
BREAKING CHANGE: Objective-C attribute tokens now contain only name and value. Properties report nullable atomicity and readonly facts; class flags, ivar counts, and method requirement flags are nullable when unobserved.
2026-10-10 00:35:16 +08:00
Ysopwnable 4bb4c6fe6d test(apple): verify compiled Mach-O root occurrence facts (#1300)
test(artifacts): verify compiled Mach-O roles and permissions

Assisted-by: Kogasa (gensokyo-skills:kogasa-surprise-testing)
2026-10-09 15:24:53 +00:00
morlutoandRudy Mizrahi Celekli 4034883e98 fix(artifacts): preserve keyed archive integer precision
Integrate the numeric precision correction from #1135 with complete supplemental
metadata handling. Keep decoded numbers with an explicit limitation for mixed XML
content or unclassified integer text, rather than assigning another literal's
exact decimal. Project nonnumeric UID and CF$UID dictionary values recursively
while preserving numeric references, graph identity and original byte digests.

Add filesystem, built CLI and MCP regressions for the review findings, adjacent
XML representations and numeric reference controls. Retain the original native
Foundation qualification fixtures and verifier.

Verification: 84 focused tests; npm run check; npm run docs:check;
npm run verify:test-discovery; npm run verify:keyed-archive (real Foundation,
CLI and stdio MCP). No real Hopper or Ghidra verification was needed or run.

Co-authored-by: Rudy Mizrahi Celekli <47457359+rudycelekli@users.noreply.github.com>
Signed-off-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-09 07:13:32 +08:00
morluto e1df5e8555 fix(apple): account for repeated archive projections 2026-10-09 06:32:10 +08:00
morluto ee043e2cbf fix(verify): report final success only after resource cleanup 2026-10-09 04:12:49 +08:00
morluto fe297e56e8 fix(apple): classify empty dyld settings by consumer semantics (#1110) 2026-10-09 01:54:32 +08:00
Kaoru0822andClaude Opus 5.5 e5c6fccc35 Merge origin/main into feat/dylib-resolution
Follow #952's Apple ownership: the Mach-O load-command reader, dylib
resolution adapter and fixture move to src/artifacts/apple/, the resolver,
dyld path semantics and findings to src/domain/apple/, and the dylib lane
helper to scripts/verify/apple/.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 13:28:14 +00:00
ysopwnable ef1a0cc65a refactor(apple): group artifact producers and verification (#952)
* refactor(apple): group artifact producers and verification

* test(apple): run affected native plist CLI regressions
2026-10-07 21:09:33 +08:00