The inventory snapshot captures reader provenance before close() runs,
so a `hdiutil info -plist` entry recorded during cleanup never reached
CLI or MCP results. Detach entries already share that timing. Record
neither instead of implying the query is observable.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(artifacts): read AppKit connectors from flat keyed-archive NIBs
Flat AppKit NIBs archive NSNibOutletConnector and NSNibControlConnector
endpoints and the outlet or selector name under NSSource, NSDestination,
and NSLabel. The keyed-archive parser read only source, destination,
and label, so it skipped every such connector: XLD.app's 64 decoded NIBs
had no outlet or target-action edges. The NS-prefixed keys now follow the
same source and destination convention as compiled NIBArchive documents.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(artifacts): read AppKit control connectors from the control to its target
NSNibControlConnector archives NSSource as the sending control and
NSDestination as the action target. The previous commit inverted them
like the unprefixed fields, so every flat AppKit action ran from the
target to the control. The action trace selects a control and follows
control -> action, and the graph keys the selector by its target, so
neither could resolve these actions. The NS-prefixed keys are now read
as source and destination for outlets and actions alike.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(artifacts): key AppKit connector endpoints by their node identity
An NSSource or NSDestination object with an identifier field was
referenced by that identifier, but its node is keyed by its authored
object ID or archive UID. The connector then pointed at a synthetic
unknown node. NS-prefixed endpoints now use the referenced object's
node identity.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
An APFS disk image attaches as an image disk plus a synthesized APFS
container, and hdiutil reports both as whole disks. Detaching the
container also ejects the image disk, so the reverse-order detach then
failed with "No such file or directory" and every APFS DMG inventory
ended in "DMG detach or mount-root cleanup failed". APFS is the default
format of `hdiutil create` on current macOS.
When a detach fails, ask `hdiutil info -plist` whether the device is
still attached. A device that is gone counts as detached; one that is
still listed, or whose state cannot be read, remains a cleanup failure.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(artifacts): project compiled AppKit actions from the control to its target
NSNibControlConnector archives the sending control as NSSource and the
action target as NSDestination; a nil target is the first responder. The
NIBArchive projection swapped them for control connectors, so every
compiled AppKit action ran from its target to the control. The action
trace, which follows control -> action, could not find them, and the
selector node was keyed by the control instead of the target class. When
the target was nil, the swapped source was null and the action was
dropped: Chess lost 29 menu actions, KeepingYouAwake 7.
Connectors now keep NSSource -> NSDestination. The NIBArchive test
encoder moves to a fixture module so a bundle test can build compiled
NIBs.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* test(artifacts): report observed action routes when a compiled route is missing
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* test(artifacts): report compiled connector records when a storyboard route is missing
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* test(artifacts): write the storyboard fixture's action in Cocoa syntax
MacFixture.storyboard declared its button action with the iOS attribute,
<action selector="buildTapped:" destination="vc1">. Cocoa documents
name an action's receiver with target=. ibtool compiled the iOS spelling
into an NSNibControlConnector whose NSSource is the view controller
placeholder and whose NSDestination is the button. That is the reverse
of what Xcode-authored AppKit NIBs contain (Music, Chess,
KeepingYouAwake, and Transmission all archive NSSource as the control),
and it is what the old inversion had matched. With target= the compiled
fixture archives the button as NSSource. The temporary connector dump is
removed; the observed routes remain in the assertion message.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
An ambiguous exact-signature group was described as sharing a
non-name identity key, but that key includes the declared type and name,
so names cannot separate it. Ambiguity limitations now name the key the
group actually shares.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* perf(graph): avoid repeated traversal edge scans and copies
* fix(android)!: preserve JVM settings and reuse metadata sessions
Read class metadata without generating application source, retain immutable serialized APK sessions, and join owned cleanup on cancellation and shutdown.
BREAKING CHANGE: Android analysis now requires a full JDK 17 or newer with its compiler module for the packaged Java source bridge.
An undecoded member in an ambiguous exact-signature group stays unpaired,
like a one-sided member, but the counterpart check considered only
one-sided members. A decoded member with the same declared type and name
on the other side was therefore reported as removed or added. Ambiguous
candidates now count as possible changed counterparts too.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Reserving the __proto__ placeholder decodes the whole document, so
<![CDATA[�]]> or a comment with an out-of-range reference
threw a RangeError, and every XML plist reader failed on a valid
document. References beyond U+10FFFF now stay as written. The XML
decoder still rejects them outside comments and CDATA.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A receiver spelled indexedDB or caches does not prove the binding, so
indexedDB.open("GET", "/api") keeps its endpoint. Such receivers
still need a standard HTTP method literal: a computed first argument or
an uppercase database name such as "APP" reads as storage, not XHR.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
#877 reads a computed-method `.open(method, url)` as XMLHttpRequest when
the second literal could be a URL. IndexedDB uses the same callee name
with a database name and version, so `indexedDB.open(name, "2")` was
published as the network endpoint "2". Recognized storage `open` calls
(IndexedDB and Cache Storage) are no longer read as XHR. Both the bundle
and static analyzers still report them as storage.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
#885 decodes XML plists that contain a __proto__ key by omitting those
entries, but signature entitlements and Interface Builder plists dropped
the omission count. inspect_signature returned an entitlement set that
looked complete, and archive_decode stayed complete. Both now report the
omission: a signature limitation, and a partial archive_decode facet with
reason dictionary_entries_omitted and a per-document limitation.
The placeholder that stands in for __proto__ was reserved against the raw
XML text, so an entity-encoded key such as __rea_prototype_key__
decoded to the placeholder and was omitted as if it were __proto__. The
placeholder is now reserved against the decoded text.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
When the CLI header parsed but its metadata could not be admitted, the
native-boundary fallback reported hard-coded false flags and zero header
values. A corrupt-metadata ReadyToRun image therefore appeared IL-free
and non-native. The fallback now reports the parsed CLI header and
derives the summary from it as the full inspection does. When the CLI
header itself is unreadable, a limitation says the values are defaults.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A field that changed from a decodable to an undecoded signature was
reported as removed on the decoded side and unknown on the other. The
undecoded member with the same declared type and name may be that field
with a changed signature, so its decoded counterpart is now unknown too,
with a counterpart-signature-not-decoded limitation.
The compare_managed_members description now names the exact declared
type, name, and raw signature tier and the undecoded handling.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`compare_managed_members` gave undecoded (unsupported or malformed)
signatures no match key at all. In complete inventories every such member
was then reported as removed on one side and added on the other, even
for byte-identical assemblies: comparing PSReadLine.dll with itself
reported 457 added and 457 removed members, mostly generic
instantiations the signature decoder does not support.
The raw signature blob is the exact signature, so methods and fields now
pair by exact declared type, name, and raw signature without decoding.
An undecoded member that still has no counterpart is reported as unknown
with a `signature-not-decoded` limitation, never added or removed, because
no shape round could look for a changed counterpart.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`inspect_managed_native_boundaries` wrote the PE classification
(`not-managed` or `malformed`) into `coverage.state`, which accepts only
`complete`, `partial`, or `unavailable`. Its own result validation then
threw, so every native DLL and every PE with unreadable CLI metadata
failed as a generic execution failure advising a retry. The sibling
artifact and member inspectors succeed on the same files.
Report these files with `unavailable` coverage, as the member inspector
does, keeping the metadata status and parse issues.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`xhr.open(method, "api/users")` is a same-origin request, but the
computed-method fallback required an absolute URL or a path prefix.
Keep the literal unless it is provably not a URL: a Node fs open flag,
a reserved browsing-context name, or a call on a receiver spelled as a
browsing context or document.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* feat(browser): trace captured website module imports
Compose verified exported scripts, inert native import syntax and a narrow
Chromium resolution port. Preserve query/fragment identity, import-map context,
all reported capture candidates and explicit unknown execution. Share named
CLI/MCP contracts and retain native reports, diagnostics and cleanup failures.
Add focused boundary regressions and a real module-loading oracle to the
existing conditional Chrome lane. No captured application code is executed
by module tracing, and no selected website asset is refetched.
Refs #851, #774
Assisted-by: Yukari (gensokyo-skills:yukari-boundary-analysis)
Assisted-by: Kogasa (gensokyo-skills:kogasa-surprise-testing)
* fix(browser): preserve module contexts and source bindings
Keep capture completeness inline, validate actual source text against its
digest, and separate unsupported URL contexts from malformed input.
Use the importer origin for native module resolution and the selected map
base independently, including mixed HTTP/HTTPS and escaped HTML attributes.
Refs #851, #774
Assisted-by: Yukari (gensokyo-skills:yukari-boundary-analysis)
Assisted-by: Kogasa (gensokyo-skills:kogasa-surprise-testing)
The `plist` XML decoder throws on the legal dictionary key `__proto__`
(its CVE-2022-22912 guard). An app whose XML Info.plist held such an
entry could not be opened at all ("app has no readable
CFBundleExecutable"), and entitlements, XML keyed archives, and
Interface Builder plists failed the same way. On the plutil JSON route,
validation silently dropped the key from `inspect_plist` results.
Decode XML plists through a shared reader that substitutes such keys
before the decoder runs, then omits and counts those entries.
`inspect_plist` and `inspect_keyed_archive` report the omission as a
limitation instead of failing or dropping it silently.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Merge main (#874) and resolve the endpoint heuristics together. Receiver
spellings do not prove bindings: `parent.open("GET", "/api")` may be a
local XHR, `new Headers()` may be an imported client, and
`api.headers.get("/v1/x")` may be a nested client.
A literal HTTP method is read as XHR on any receiver; the browsing-
context exclusion now applies only to computed methods. A get/delete
literal that is a URL or path stays an endpoint even when the receiver
is spelled as a keyed collection.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
otool and dyld_info print the operand path before a slice's records,
and the Mach-O parsers read that header as output. A file name holding
`Load command` blocks or `(from lib)` lines therefore added a forged
LC_MAIN entrypoint, corrupted the Mach header fields, and injected
imports and exports into `inspect_macho`.
Strip the exact echoed operand header (`<path>:`, `<path> (architecture
<arch>):`, `<path> [<arch>]:`) before parsing. Output without that
header, such as replayed captures, is parsed unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The collection APIs share only `get` and `delete` with HTTP clients, so
`api.headers.post("/v1/headers")` and `api.searchParams.request("/s")`
keep their endpoints.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`window.open(url, "/preview")` has a computed first argument and a
path-shaped browsing-context name, so the computed-method XHR fallback
reported the target name as an endpoint. Browsing-context and document
`open` calls never take an XHR method and request URL; skip them.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>