Commit Graph
6334 Commits
Author SHA1 Message Date
Matt Van Horn ab06b3e2ec fix(audit): report relative paths given to compiler path flags
go.build_paths only kept rooted paths, so retained build settings like
CGO_CFLAGS=-Iprivate-sdk read not_observed even though the setting names
a directory.

Take the relative argument of a C compiler path flag, attached or in the
next field. Scope this to compiler settings, because Go's own tools use
the same letters for non-paths such as -B gobuildid and -T 0x1000.
2026-11-02 01:08:50 -08:00
Matt Van Horn 236f2fb51a fix(audit): let native format facts outrank member names
examineMember tried the name-based text and map routes first, so a PE an
owner identified inside an ASAR, named payload.json, was read as text.
Its CodeView path and the packaged-native omission were both lost, and
native.pdb_path read not_applicable.

Route an owner-established native format to the native probe first, and
count an encrypted one as a packaged native image for the same reason.
2026-11-02 00:56:16 -08:00
Matt Van Horn c50d0ed1a0 fix(audit): keep coverage unknown for nested ASARs in package hosts
A member of a PyInstaller or .NET host reaches the binary probe as a
plain file. An ASAR among them has a NUL-bearing header, so the probe
dropped it with no traversal and no omission, and source map checks could
read not_observed with complete coverage.

Ask the ASAR owner's signature probe, and record a match as a nested
package that was not expanded, as inventory-classified ones already are.
2026-11-02 00:48:49 -08:00
Matt Van Horn 23fff0a227 fix(audit): keep completed findings when member cleanup fails
A descriptor close failure aborts the member scan, and the scan's state
was dropped with it, so the caller got a typed cleanup failure and no
report. Source maps already counted and credential hits already collected
were lost.

The reader now hands the service what it assembled, with coverage unknown
and a limitation saying so, alongside the failure. The service runs that
scan through its existing stopped-audit path: no later owner runs, and the
report goes on the error as its partial observation.
2026-11-01 23:44:43 -08:00
Matt Van Horn 2adc01b407 fix(audit): locate Go findings only in images that retain build paths
The exposed decision required non-empty paths, but the locations took
every image whose build info read. An image with no paths was named as a
source of the leak beside the one that had them.

One predicate now decides both.
2026-11-01 23:39:42 -08:00
Matt Van Horn 82d2ee477d fix(audit): admit binary plists in package member probes
A package member's probe judged text by its own NUL test, which accepted
byte order marks but not the binary plist signature decodeMemberText also
reads. A PyInstaller executable shipping a binary Localizable.strings was
rejected without a scan or a skipped count, so a UTF-16BE private key
marker inside it could report not_observed.

Name the encodings that explain their own NULs in one predicate, and move
the plist fixture builders to module scope.
2026-11-01 23:37:26 -08:00
Matt Van Horn 86163530af fix(audit): confirm FAT identity for probed package members
The package probe treated a decoded FAT table as a universal Mach-O. A
JVM class past the 4 KiB probe shares the FAT magic, so its truncated
prefix decoded as a table, the member counted as an unread native image,
and Electron could not be ruled out of a complete scan.

Pass the member's declared size to the sniff and take identity from
classifyFatContainerEvidence. A member with no usable size, or with
slices past the probe, stays counted as possibly native.
2026-11-01 22:20:57 -08:00
Matt Van Horn bdbb070000 fix(audit): detect credential markers that end in a hyphen
The Google, GitLab, and Slack rules allow a hyphen in the token but ended
on a word boundary, so a token ending in a hyphen at the rule's minimum
length was missed and the audit reported the check as not observed.
Open-ended rules now take the whole token, and the fixed-length Google
rule checks the full token alphabet after its last character.
2026-11-01 22:16:49 -08:00
Matt Van Horn a7c705bbe6 fix(audit): keep examining a page loaded as a script as HTML
A script element naming an .html member promoted that member to a
script, and the text pass then chose one reading. The page's own script
elements were never read, so an inline map in it was lost and a shipped
sourcesContent went unreported.

Run the script reading and the HTML reading independently, and list the
script target allowance among the cases that leave source map coverage
undecided.
2026-11-01 21:56:40 -08:00
Matt Van Horn 2cec5132d4 fix(audit): bound the paths kept for HTML script targets
A base href is copied into every script target it resolves, so a page under
the text member allowance could retain hundreds of megabytes of paths. Charge
each new target's resolved path to a byte allowance and leave targets past it
unassessed.
2026-11-01 21:52:51 -08:00
Matt Van Horn c4552f1c7c fix(audit): charge unsuccessful text reads to the byte allowance
An oversized, undecodable, or failed text read left textBytes unchanged, so
every member got the full per-member budget and the cumulative allowance did
not bound what a package decompressed. Count the bytes member reads produce
and charge them whatever the read yields. An oversized file on disk still
costs only its probe.
2026-11-01 21:49:11 -08:00
Matt Van Horn a765050cb1 fix(audit): report cleanup failures raised by member reads
A close failure during an embedded-package probe, member read or inventory
reaches the shared read filter as an ordinary reader failure. The filter kept
only cancellation and integrity, so the member became a skipped read, inspect
succeeded, and the retained descriptor was reported nowhere. MCP does not close
the service after each request, so no caller saw it.

Propagate any failure that carries cleanup, whatever its reason, so it reaches
releaseExposureFailure and the audit reports its retained resources.
2026-11-01 21:44:18 -08:00
Matt Van Horn a250696cac fix(audit): charge sibling map reads to the map count
Every generic .map beside a selected file got a full 1 MiB read and a
parse, with no cumulative bound, so the 4,096-entry listing allowed about
4 GiB of reading for one small selection.

Move the slot rule into chargeMapRead, which examineMap and sibling
classification now share. Past the count a sibling reads only the header:
a linker map is still identified, and one that may be a source map stays
a skipped companion.
2026-11-01 21:09:46 -08:00
Matt Van Horn 6f0b971639 fix(audit): examine scripts HTML loads whatever they are called
isJavaScriptMember knows a script by its suffix or an assigned kind, and the
reader dropped the script references the HTML owner parses. An extensionless
main loaded by <script src="main"> was scanned as ordinary text, so a complete
inventory reported javascript.source_map_sources not_observed with shipped
sources in its inline map.

The reader now resolves each JavaScript script reference against the page and
its base href, and a local target without a JavaScript name is examined as a
script when reached. A target read before its HTML file, never reached, or not
placeable on disk counts as a skipped map, so the check stays unknown.
2026-11-01 21:05:14 -08:00
Matt Van Horn 2e9eacb8cf fix(audit): charge map reads to the count before opening the member
An oversized or unreadable .map returned before readSourceMap counted it, so
the allowance of 32 never ran out. An unexpanded .NET or PyInstaller package
could then decompress every listed map member, about 1 MiB each, up to the
4,096 entry cap.

The count bounds reads, so examineMap now reserves the slot before it opens the
member. A map past the count is skipped unopened, and a linker map past it is
still settled from its bounded prefix. An inline data: map is already in memory
and keeps its charge at the point it is parsed.
2026-11-01 20:20:05 -08:00
Matt Van Horn cfd9681a7e fix(audit): settle fuses only on an identified host, and read Bun and HTML maps
A retained helper set electron_executable_included because the rank marks
every .exe or extensionless sibling of resources/app.asar. With more than four
siblings, or an inventory that stopped early, safe fuses on a helper reported
not_observed while the host went unread. Rank stays a sampling hint. The check
now settles only on an executable named as the host, or when inventory was
complete and every executable that could be the host was sampled.

A Bun sourcemap region is named .bunmap, so its role was lost and a complete
audit reported no source maps. Carry the region role from the reader's metadata
into the member view, in the expanded and unexpanded paths, and treat it as a
JavaScript map so an unparsable region stays unresolved, not a debug companion.

An HTML member was scanned as text but its inline scripts were never checked
for source maps. The HTML owner now returns each body, the reader examines it
like a script file, and bodies past the owner's limit count as skipped.
2026-11-01 20:15:06 -08:00
Matt Van Horn 098173807c fix(audit): keep safe fuses unknown past a packaged native image
The fuse check counted sampled-out images as possibly unread but not native
images inside a package. A complete directory scan with a safe-fuse image on
disk, no ASAR hint, and an unopened packaged image reported the four Electron
checks as not_observed, although the Electron executable could be that image.

The fuse check, the Go and Rust checks, and the Electron rule-out each decided
this for themselves and had drifted apart. Move the decision to one domain
predicate, nativeImagesMayBeUnread, and use it in all three. A hinted
executable that was read still settles the check.
2026-11-01 19:07:41 -08:00
Matt Van Horn 2a6415c2c5 fix(audit): keep cleanup-incomplete errors from composed readers
A desktop, fuse, Go or Rust failure became a string on that check, so an
error with unfinished cleanup lost its resources and live bindings while the
audit reported success, and the MCP handler does not close the service after
each request.

Treat unfinished cleanup like cancellation: stop the remaining owners and
return the error with the partial audit. When the owner attached its own
partial, keep the error and replace the partial, so cleanup stays attributed.
2026-11-01 17:57:19 -08:00
Matt Van Horn bd7e340f1d fix(audit): count a packaged PE with an unread header as a native image
A package member whose PE signature lies beyond the 4 KiB probe recorded only
a PDB failure. The image was neither read nor listed under packaged native
images, so a directory whose on-disk hosts had no fuse sentinel read as not
Electron and all four fuse checks as not applicable.

Count it as a packaged native image, the omission that keeps the question
open, and keep probing it for text as before.
2026-11-01 17:56:59 -08:00
Matt Van Horn 49c9e15cdf fix(audit): check plist text against the budget before decoding it
decodeBinaryPlistText decoded each stored object before comparing its size
with the remaining budget, and it keeps going after an object is rejected so
later text is still read. A plist under the input limit could repeat a large
range through overlapping table entries and force gigabytes of decoding that
the audit then discarded.

Separate finding a stored payload from decoding it, so a range past the
remaining budget is rejected without allocating its text. Earlier text and
the incomplete result stay as they were.
2026-11-01 17:51:30 -08:00
Matt Van Horn 19b3380e84 fix(audit): read escaped inline maps and extensionless SEA entries
A data URL may percent-escape its base64 payload, so a map such as one padded
with %3D did not decode and the audit counted no embedded source map. Decode
the escapes before reading the base64.

A directory walk classed a SEA entry by its code path, so a main script whose
code path names no script was never scanned as JavaScript and its inline map
went unseen. Class it by the role the SEA metadata gives it.
2026-11-01 17:11:50 -08:00
Matt Van Horn 45c45c43aa fix(audit): keep readable plist text when one object is unreadable
Throwing on one unreadable stored object discarded every string already
decoded, so a UTF-16BE credential beside a truncated unused object was lost:
the raw bytes that replaced it cannot match NUL-separated text.

Return the decoded text with complete false instead, and keep the raw bytes
in the scan beside it so ASCII in the unread object is still seen. The member
stays unassessed. Text past the budget ends the same way.
2026-11-01 17:06:48 -08:00
Matt Van Horn 9e06282e3a fix(audit): keep packaged native images from ruling out Electron
The applicability check only looked at the "native images" omission. An
Electron executable inside a compressed .NET or PyInstaller member is recorded
under "packaged native images", so a directory whose on-disk hosts had no fuse
sentinel read as not Electron and all four fuse checks as not applicable.

Treat that omission as an unread image too.
2026-11-01 17:03:10 -08:00
Matt Van Horn 12d864be45 fix(audit): scan every string a binary plist stores
The plist scan read only the decoded value, which drops objects the root no
longer references and entries a later duplicate key replaced. Those bytes
still ship, but the audit marked the scan complete, so a credential stored
there read as not observed and --fail_on high exited 0.

Read strings, keys and data from the object table instead. A stored object
that cannot be read leaves the member unassessed, and a total byte budget
bounds table entries that overlap to repeat the same text.
2026-11-01 16:36:47 -08:00
Matt Van Horn f31c8c55f8 fix(release-exposure): keep UTF-8 members that hold a NUL
Any NUL in a member's first 512 bytes made the decoder give up, so a script
with a literal NUL in a string never reached the credential or inline-map
checks. An exposed marker in it read as unknown, and --fail_on high exited 0.

Validity alone cannot tell wide text from UTF-8, since ASCII in UTF-16 is
valid UTF-8. Treat a header that is an eighth or more NUL bytes as wide text
and anything not UTF-8 as undecoded, and scan the rest.

The docs now separate BOM-marked UTF-16, which is scanned, from wide text
with no byte order mark, which is not.
2026-11-01 15:36:28 -08:00
Matt Van Horn cc96be1b4a fix(release-exposure): decode binary plists before scanning
A binary plist stores each string as ASCII or UTF-16BE. Scanning the raw
bytes as UTF-8 missed any string the encoder kept in UTF-16, such as one
with a non-ASCII character, and still counted the member as fully
scanned, so text.credential_markers read not_observed.

Decode through the plist owner and scan its strings, dictionary keys and
data. A plist that cannot be decoded keeps its raw bytes in the scan and
counts as skipped, so coverage stays unknown.
2026-11-01 15:30:55 -08:00
Matt Van Horn ec3d2ed66c fix(release-exposure): keep fuses unknown on incomplete inventory
A directory inventory that stops at an acquisition limit never reports
the images it did not reach, so the native image sample has no omission
and safe fuses on an included helper settled to not_observed even though
scan coverage was unknown.

Treat unknown coverage like a truncated sample until the Electron
executable is accounted for. A selected native file is that executable,
so it keeps its decided fuse result.
2026-11-01 15:26:48 -08:00
Matt Van Horn 957f25dc7e fix(release-exposure): match backslash-relative Go flag paths
GO_FLAG_PATH accepted dot-relative roots only with a forward slash, so
`-I..\sdk\include` and `-I.\sdk` produced no path and go.build_paths came
back not_observed. Accept either separator after the dots.

The CLI acceptance test now expects unknown for electron.run_as_node on a
directory with no native images, matching the service.
2026-11-01 15:01:38 -08:00
Matt Van Horn 630352b450 fix(release-exposure): follow file: URL source map declarations
The scheme guard in noteLocalMapReference returned on every URL scheme, so
a declaration such as file:///release/debug.json was dropped with the remote
ones. A member without a .map name was then scanned as ordinary text and
javascript.source_map_sources reported not_observed.

Convert file: URLs with fileURLToPath and record them in mapTargets through
the same step as relative declarations. A target that is never reached, or
that was already scanned as text, counts as skipped, and so does a malformed
file: URL. A file: URL names one absolute location, so a .map target outside
the inventory stays unassessed rather than being trusted by its name. Remote
schemes stay ignored.
2026-11-01 14:57:47 -08:00
Matt Van Horn 81fc997582 fix(release-exposure): decode wide text members before scanning
A member's kind bypassed the NUL guard, so a UTF-16 plist was decoded as
UTF-8, matched nothing and still counted as scanned. A directory holding
only that member reported text.credential_markers as not_observed.

Decode by byte order mark at one boundary instead. UTF-16 is scanned in
either byte order, and inline maps read the same text. UTF-32 and NULs no
mark explains count as skipped, whatever the member's kind. A binary
plist keeps its raw scan. Unnamed members with a mark now reach the
scanner, and the plist decoder shares the same mark detection.
2026-11-01 14:51:49 -08:00
Matt Van Horn 127be46a7c fix(release-exposure): keep Electron undecided without positive proof
The desktop owner names Electron only from an ASAR layout, so an asar: false
build has no candidate. A complete scan with no candidate was reported as
not_electron, which outranks unresolved and safe fuse outcomes and hid them
as not_applicable.

Leave the family unknown unless the fuse holder rules Electron out: an app
bundle with no Electron Framework, or a directory with complete coverage
whose native images were all read and hold no sentinel. A directory with no
native image is not proof, so its fuse checks now report unknown.
2026-11-01 14:45:46 -08:00
Matt Van Horn 169c978598 fix(release-exposure): collect UNC roots from Go flag paths
A flag such as -I\\buildhost\sdk\include matched none of the path roots
in GO_FLAG_PATH, so a network path that was the only one in complete
build info left go.build_paths at not_observed. Accept \\host\share
roots alongside the slash-rooted, dot-relative and drive-letter forms.
2026-11-01 12:26:37 -08:00
Matt Van Horn 12222227d2 fix(release-exposure): bound the indexed leaf fallback by decoder limits
The fallback walk after a map format failure expanded every section and
built a full ancestor path per leaf, so a map refused for nesting depth
could still allocate far past the decoder budgets. Walk only to the
64-level section depth and the shared record budget, keeping leaf maps
without materializing section paths.
2026-11-01 12:22:02 -08:00
Matt Van Horn a8eefe0ab8 fix(release-exposure): count an unexpanded nested package as a readable member
An unencrypted nested package that inventory recognized but did not
expand is still a member its parent package already read. Count it as
readable so packaging.extractable_payload reports exposed, while its
own contents keep coverage unknown.
2026-11-01 12:16:32 -08:00
Matt Van Horn f553459c9a fix(release-exposure): recover indexed leaves after any map format failure
An indexed map whose one leaf has malformed mappings (such as AA) threw a
format failure that only recovered leaves for unsupported sections, so a
valid sibling leaf with sourcesContent was dropped and
javascript.source_map_sources read unknown instead of exposed. Leaves are
now recovered for any format failure, with the envelope parse guarded so
invalid JSON still falls through to the unparsed count.

A nested package that inventory recognized but did not expand was probed
as a plain binary and left the source-map checks at not_observed. It now
marks coverage unknown and records a limitation.

Reader tests assert the javascript.source_map_sources status for the NFC
map-name cases as well.
2026-11-01 10:35:37 -08:00
Matt Van Horn 3002348166 fix(release-exposure): normalize declared map targets and count unreached ones
A sourceMappingURL that decodes to a decomposed name (cafe%CC%81.json)
never matched the inventoried member, whose logical path is NFC, so the
map was scanned as ordinary text and its sourcesContent went uncounted.
Map-target lookups now use an NFC key on both the record and lookup
side; the declared URL is left as written.

A declared map target that is never reached is now counted as a skipped
map, so the source-map checks read as unknown coverage instead of
not_observed.
2026-11-01 10:33:55 -08:00
Matt Van Horn 67c49e9cec fix(release-exposure): match data: source map scheme case-insensitively
URL schemes are case-insensitive, so an inline declaration such as
//# sourceMappingURL=DATA:application/json;base64,... was routed to the
local-reference path, which ignores scheme URLs. Its embedded sources were
silently dropped and source_map_sources reported not_observed.
2026-11-01 10:33:55 -08:00
Matt Van Horn 5cbed6a184 fix(release-exposure): bind package members by ordinal and follow named map references
Package members were matched to container entries by sourcePath, which is
not unique in a PyInstaller executable, through a queue that only moved when
a member was reopened. Bind by container-relative path and duplicate ordinal,
the numbering inventory uses, so read order cannot change which bytes a
member gets.

A sourceMappingURL naming a file without a map extension was ignored. Local
targets are now examined as maps whatever their name, and a target already
scanned as plain text leaves coverage unknown.
2026-11-01 10:33:55 -08:00
Matt Van Horn ffc2f2309e fix(audit): match package members by source spelling and close remaining coverage gaps
Reopen package members by their source spelling and keep caller path
traversal in directory reads. Parse quoted and attached Go build flags, bind a
native image fuse read to its own digest, and keep reader resources and the
audit's partial report when cleanup or cancellation combine failures.
2026-11-01 07:29:15 -08:00
Matt Van Horn d224dfc51c fix(audit): keep incomplete coverage unknown across text, fuses, Go flags and cancellation
Probe files with unrecognized names as text, keep undecided fuse slices
unknown, forward opaque container coverage, read paths from Go build flags,
and compare inventoried digests before accepting owner facts.

Keep typed cleanup failures and attach the partial report when a later owner
is cancelled. Pin the verifier's @electron/fuses version and move the tests
to the named contract family.
2026-11-01 07:02:32 -08:00
Matt Van Horn f183381739 feat(audit): report what a shipped app exposes to static analysis
Adds `audit_release_exposure` and `rea audit-release-exposure` to report
source maps, Electron fuse implications, extractable payloads, debug
information, build metadata, and credential markers. Checks carry stable
IDs, observations, statuses, and fixed remediation, while incomplete and
unassessed checks remain visible without implying a security verdict.
Credential findings include only rule IDs and locations. The CLI
supports `--fail_on` severity thresholds and exits 3 when an exposed
check meets the threshold.
2026-11-01 02:41:37 -08:00
gensokyo-dev e18594b247 refactor(composition): unify capability bindings and cleanup ownership (#8441)
Bind typed service creation, observed availability and retryable cleanup in one composition owner. Reuse selected CLI/MCP factories and canonical contract groups without copying provider operation inventories.

Closes #8429. Refs #740.

Validated paired real application workflows, actual local Codex CLI Terra/high use, complete Evidence and Unknown checkpoint parity, compiler-backed import boundaries, SDK schemas and final real mixed routing. Automated review findings were fixed at their producing owners. Existing exact-base CodeQL alerts are documented on the PR under the owner-authorized unrelated-check exception; no global alerts or security rules were changed.

Assisted-by: Suika (gensokyo-skills:suika-scatter-gather-planning)
2026-11-01 13:30:21 +08:00
morluto 6e9ee97a69 fix!: preserve feature traces when owner lookup is unavailable (#8368)
* fix!: preserve feature traces when owner lookup is unavailable

* test: strengthen feature trace cancellation assertions

* fix: preserve feature trace provider readiness errors

* test: verify all feature trace reference sources

* test: verify IDA guidance at its owning boundary

* style: format feature trace regression
2026-11-01 05:38:40 +08:00
gensokyo-dev f72638f6b1 feat(investigation)!: retain records across engine cleanup (#8361)
Separate engine cleanup, investigation reset and terminal disposal in the shared application owner. Keep stable Evidence IDs and full Unknown revision history through engine close and target changes; serialize reset with all record writers and cleanup owners, optionally acknowledging a complete checkpoint before clearing. Bound retained representation without eviction.

Validation: 43 focused files / 533 distinct tests, source/type/lint/schema/documentation checks, nine genuine source cases across eight families, real Ghidra session identity and cleanup, and nine actual local Codex CLI terra/high runs with independent full-result and checkpoint audits. See docs/verification/investigation-lifecycle.md for measurements, partial attempts and verification limits.

Fixes #8347.

BREAKING CHANGE: close_binary retains investigation records. Use reset_investigation to clear them. Cross-process recovery requires an explicitly acknowledged Evidence bundle checkpoint.

Assisted-by: Suika (gensokyo-skills:suika-scatter-gather-planning)
2026-10-31 21:34:45 +00:00
Kaoru0822andClaude Opus 5.5 d54a4661da fix(ghidra): compare the DOS verifier's CLI window before its own rename (#8339)
The DOS verifier compared a fresh CLI session with an MCP session that
its address-boundary helper had renamed and restored, so the entry's
symbol source differed (imported vs user_defined). Compare the CLI with
the pre-edit MCP window, then assert the restoration changes only the
symbol provenance.

Fixes #8189

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-31 11:13:04 -07:00
Kaoru0822andClaude Opus 5.5 d2518a19f8 feat(native): label binary overview counts with their inventory scope (#8338)
* feat(native): label binary overview counts with their inventory scope

`binary_overview` described its counts as "exhaustive", and its result was
a pair of bare scalars. An agent could read them as a discovery of every
function and string in the file, or as comparable across providers. They
are exact totals of the rows each provider's inventories return.

The shared `projectBinaryOverview` now adds one provider-neutral
`inventory_coverage` record, so Ghidra's direct summary and the
listing fallback carry the same record:
`{ scope: "provider_inventory", discovery_coverage: "unknown" }`.
The record reuses the `discovery_coverage` vocabulary that
`list_procedures({detail:"summary"})` already reports, now owned by one
domain schema. The tool description states the scope and that counts are
not comparable across providers. Counts, segment omission semantics and
limitations are unchanged.

Fixes #8287

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

* fix(native): label overviews in the workflow so older snapshots stay valid

Analysis snapshots replay the projected inventory summary. A snapshot saved
before inventory_coverage existed would then fail the overview output schema.
The record describes what overview counts mean, whoever produced them, so
the binary_overview workflow now adds it on both its provider-summary and
listing paths. Provider values and projectBinaryOverview are unchanged, and
the composed overview's linked atomic record stays as the provider
reported it.

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

* fix(native): refresh composed overviews from before inventory_coverage

Bump the binary_overview workflow revision to 5, so snapshot and CLI replay
recompute a revision-4 composed overview from its atomic record. Replay
would otherwise return the cached overview without its count labels. The
revision-4 case joins the existing refresh scenario. The atomic Evidence
is not rewritten.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-31 11:12:52 -07:00
Kaoru0822andClaude Opus 5.5 a36c9dd9c8 fix(kuna): omit Mach-O data symbols from the procedure inventory (#8335)
* fix(kuna): omit Mach-O data symbols from the procedure inventory

Kuna's `functions --json` registers every Mach-O export as a function
entry, so globals in `__DATA,__data`, constants in `__TEXT,__const` and
`__mh_execute_header` were listed, counted and decompiled as procedures.

The Kuna session now classifies each inventory entry with the target's own
section table, read by the shared `inspect_macho` load-command reader from
the header bytes kept while its private copy is hashed. Entries in sections
that declare no instructions, or in the header and load commands, are data:
listings and searches omit them and report per-region counts, and
procedure operations refuse them by name or address while `address_name`
and `xrefs` still accept them. Instruction sections, symbol stubs and
symbol-pointer import slots are kept; addresses outside every section stay
with an explicit limitation, an unreadable section table keeps the whole
inventory unclassified, and ELF inventories are unchanged. Composed
workflows such as `decompile_all` and `binary_overview` follow the listing.

Fixes #8168

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

* fix(kuna): keep an entry's classification caveat on direct answers

Resolution now returns the selected entry's Mach-O placement and the
caveat it leaves open, built by the same constructor as the listings: the
one-entry unplaced wording, or the unreadable section table's reason.
Pseudocode, assembly, analyze_function (including the dossier's own
limitations), procedure_address, call queries by name and address_name
carry it; code and import-slot entries carry none.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-31 11:12:41 -07:00
Kaoru0822andClaude Opus 5.5 8a6ddec83a fix(mcp): name the close_binary retry for retained cleanup (#8329)
* fix(mcp): name the close_binary retry for retained cleanup

A close_binary that retired its target but could not confirm cleanup
returned cleanup_incomplete with retryable: false and only the generic
"Review the reported local resources" remediation, although the session
keeps every failed owner and the next close_binary on the same connection
retries them. Agents could abandon the connection that owns the only retry.

The MCP close tool now passes its retry operation to the session. The
session binds that typed AnalysisCleanupRetry to a failed retirement only
while it retains the cleanup for its next close; the close tool binds it to
process-capture failures its own scope retains and to compositions whose
session part was offered. Projection then reports
recovery.kind=retry_cleanup and appends a conditional instruction: keep the
connection, let the condition settle or resolve it, call close_binary
without snapshot_path and check the result. The hint disappears once that
cleanup resolves. retryable keeps describing the failed operation.

One-shot CLI closes, open_binary, analysis tools and failed snapshot saves
(which keep the target open) offer no retry. Cleanup composition copies the
owner's remediation without a retry offer, so the text appears once.

Fixes #8211

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

* test: derive close retry guidance from its owning formatters

The new close_binary retry assertions froze ProviderCleanupError's
explanatory sentence, so a harmless wording edit would break them. The MCP
test now compares delivered remediation with analysisErrorRemediationAction
applied to the delivered typed error plus cleanupRetryRemediation. The
domain tests use the same formatters, check that unbound and released errors
carry no retry text, and check that a bound composition adds the offer once.
Recovery fields, cleanup state and retry behavior keep separate assertions.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-31 11:12:30 -07:00
Kaoru0822andClaude Opus 5.5 c534e249d1 perf(process): reuse a verified Darwin ownership helper across processes (#8318)
* perf(process): reuse a verified Darwin ownership helper across processes

Every REA process on macOS compiled the Swift ownership helper into a fresh
temporary root and then paid macOS's first-launch assessment of that new
executable: 408 ms + 325 ms of a 1.4 s `rea inspect-macho /bin/ls`.

With the default xcrun compiler and module cache, the reader now keeps one
content-addressed copy under ~/Library/Caches/rea/process-helper/<key>/.
The key covers the helper sources, toolchain selection, architecture and
Darwin release. A copy is executed only after the cache root, entry and
files are verified as current-user owned, not group- or world-writable,
not links, and digest-matched to the entry manifest. Malformed entries
owned by the user are removed and rebuilt; linked or foreign entries are
left untouched and the per-process compile runs as before. Any cache
failure falls back to that compile, so the cache never fails an ownership
operation.

A miss compiles exactly as before, then publishes a copy through an atomic
rename, keeping the first concurrent publisher, and runs the shared copy.
That way its first-launch assessment is paid once. Per-run roots keep their
existing cleanup; published entries are never removed by close or exit.
An explicit REA_DARWIN_SWIFTC_PATH or REA_DARWIN_MODULE_CACHE_PATH keeps the
per-process compile and writes nothing to the cache. `rea uninstall
--purge-data` removes the namespace.

Fixes #2483

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

* fix(process): harden the shared Darwin helper cache

Address review findings on the shared ownership-helper cache:

- Verify every real parent of the cache root: each must belong to the user
  or root and not be writable by others (sticky root-owned directories
  excepted), so no other user can rename a verified entry. Execute through
  the real path.
- Open manifest and helper through the owned non-blocking regular-file
  reader, so a FIFO cannot block preparation, and retain handles whose close
  fails.
- Key entries on the swiftc and SDK that xcrun itself resolves, including
  their real paths, sizes and modification times. DEVELOPER_DIR, TOOLCHAINS,
  xcode-select and in-place updates therefore select new entries.
- Never remove or replace an invalid or published entry. Removal could race
  another process that has verified or is running that path, so such an
  entry falls back to the per-process compile.
- Give the cache a close() that retries retained staging removals and
  handle closes and reports ProviderCleanupError while any remain. The
  reader closes it with its per-run root.

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

* fix(process): keep cache lookups owned and fall back from unlaunchable helpers

Address the second round of review on the shared ownership-helper cache:

- Pass the preparation signal to both xcrun toolchain lookups, and wait for
  every started lookup before reporting the first failure. Cancellation and
  close then stop a stalled lookup instead of waiting out its deadline.
  A failed or cancelled key computation is retried by the next lookup.
- When the verified shared copy cannot launch (EACCES, EPERM, ENOEXEC,
  ENOENT or an unrequested SIGKILL, for example from a noexec home volume),
  the reader stops using the cache. It reuses its own compiled helper or
  compiles one, and retries the read once.
- Anchor the cache's returned cleanup errors as cleanupRoot does, so every
  error returned while cleanup was pending resolves after a retry succeeds.
- Build the cache path with appendCallerPath, so a home with symlinks and
  `..` keeps the caller's meaning instead of being normalized lexically.

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

* fix(process): keep concurrent fallbacks and dual cleanup failures

Each helper read now records before awaiting whether it launched the shared
copy, so a concurrent read's fallback cannot hide its own. When the per-run
root and the cache's retained resources both fail one close, the returned
error composes both. It stays incomplete until both owners recover.

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

* fix(process): refuse shared helpers that an ACL lets others write

macOS ACLs can grant writes that the POSIX mode bits do not show. On
Darwin, lookup and publication list the real ancestors, the entry and its
files with `ls -lde`, and refuse sharing when any ACL entry allows a
write-class right. The default `group:everyone deny delete` entries on
macOS home folders are unaffected. The unlaunchable-helper test now removes
the execute bit from a verified copy, which refuses launch on both Linux
and macOS.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-31 11:12:02 -07:00
Kaoru0822andClaude Opus 5.5 41e7333cb5 feat(guidance): distinguish focused matches from broad parent sections (#8305)
* feat(guidance): distinguish focused matches from broad parent sections

Guidance search treated a parent section, including every descendant, as one
match. Terms scattered across unrelated subsections made a 49K-character root
the only result for "switch provider annotations", and every ancestor of a
focused hit was listed alongside it.

The section parser now records each section's own text (heading and body
before its first subsection) and its parent. Search keeps the same admission
rule but classifies each match by match_scope: "section" when every term is
in the section's own text, "subsections" when the terms are only spread
across subsections. Ancestors whose terms all sit inside one subsection are
no longer listed, section matches rank first, and subsections matches carry
own_text_terms plus bounded greedy pointers to the subsections holding the
remaining terms. Every match reports content_length so callers can judge a
read before making it. Read still returns the complete section.

Own text is tokenized once per request instead of each expanded subtree.

Fixes #8290

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

* test(guidance): require the scattered-match checks to run

Count the subsections matches the real-bundle test inspects and fail when
none were checked, and document that a parent whose own text holds every
term stays listed beside its matching subsection.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-31 11:11:05 -07:00