mirror of
https://github.com/morluto/rea.git
synced 2026-11-12 17:59:08 +00:00
5eef239dfd774331d622aaa4762f42fa632121ab
184
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
7aca4cef64 |
feat(audit): report what a shipped app exposes to static analysis (#8525)
* 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.
* 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.
* 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.
* 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.
* 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.
* 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.
* 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.
* 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.
* 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.
* 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.
* 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.
* 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.
* 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.
* 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.
* 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.
* 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.
* 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.
* 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.
* 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.
* 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.
* 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.
* 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.
* 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.
* 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.
* 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.
* 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.
* 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.
* 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.
* 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.
* 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.
* 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.
* 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.
* 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.
* 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.
* 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.
* 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.
* 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.
* 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.
* 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.
* 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.
* 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.
* test(javascript): expect retained text on SVG inline scripts
The SVG test from main predates this branch's inlineScripts text field,
so each side passed alone and only their merge failed.
* fix(audit): keep an unresolved Electron host's fuses unknown
fuseCheck decided from whatever outcomes the images reported, so a host
with no fuse answer read not_observed as soon as any other image in the
directory reported safe fuses.
Pass the images named as the host into the report and leave a check
unknown when one of them has no answer for it. Other images stay free to
have no fuses, because helpers in a real Electron install have none.
* fix(audit): bound the paths kept for local source map targets
noteMapTarget kept every declared target resolved against its member's
directory, so a short URL under a long directory could retain far more
than the script's own size, with no cumulative limit.
Add the allowance noteScriptTarget already has. A target past it stays
unassessed and counts as a skipped map, and is never read.
* fix(audit): borrow the audit's scope admission for inventory
inspect holds resources.run for the whole audit, and inventoryArtifact
entered the same scope again. With failed cleanup pending from another
audit, the inner run waits for a drain that waits for the outer run.
Add inventoryArtifactInScope, which takes the caller's admission.
* fix(audit): run the exposure verifier's commands under supervision
The verifier launched npm and the CLI with raw execFile and then removed
its temporary tree, so a timed-out child could outlive the tree it was
using.
Use the shared verifier commands and close them before removing the tree.
Their exit error carries stdout on its snapshot, so the audit's exit-code
branch reads it from there.
* fix(audit): read relative -L paths and split flag arguments
The relative-path reader handled C compiler settings only, so a directory
kept through Go's own linker flag, such as -ldflags='-L private-sdk -s -w',
reported go.build_paths as not_observed.
A quoted flag and argument also reached it as one field. The separating
space stayed on the argument, which reported rooted paths twice and kept a
leading space on relative ones.
Give -ldflags its own pattern that matches only -L, so -B gobuildid and
-T 0x1000 stay ignored, and consume the separator in the flag patterns.
* fix(audit): keep embedded leaves beside schema-invalid map fields
The reader recovers independent indexed leaves only after a
SourceMapFormatFailure, but inspectSourceMap let the schema's ZodError
through. A sibling with mappings "!", a section with a bad offset or a
root with a bad field skipped recovery, so a valid leaf with
sourcesContent reported javascript.source_map_sources as unknown.
Normalize schema rejection in SourceMapFormat, which owns these parses,
so every structural failure reaches consumers as a format failure. The
message is unchanged, and the codec and web fetcher already map both
cases to the same result.
* fix(audit): keep packaged executables' own packages unknown
examinePackagedBinary counted a native image inside a package and moved
on, while only on-disk images reached the embedded-package probes. An
ASAR carrying a PyInstaller, Bun, Node SEA or .NET executable with a
source map therefore scanned complete and reported
javascript.source_map_sources as not_observed.
Run the same classifier on packaged images. The probes read carrier
offsets from a file, so the member's bytes stay in a temporary file for
the probe alone. A detected package keeps member coverage unknown, as a
nested ASAR does, and an image past one file's total byte allowance
stays unprobed and unknown. A plain native member leaves coverage intact.
* fix(audit): read an app's sampled images for fuses
A .app target sent only the bundle candidate to the fuse reader, which
resolves to the outer Electron Framework. A sidecar with RunAsNode enabled
was never read, so the check reported not_observed in the app view while
the directory view reported exposed.
Send the bundle candidate first and then the sampled native images. An
image whose digest matches the bundle's read is skipped, so the framework
is not read twice. A sidecar's safe fuses cannot settle the outer framework,
so a bundle read with no answer for a fuse keeps that check unknown.
* fix(audit): read a Node shebang as script identity
The path classifier gives an extensionless executable a resource kind,
so a script such as bin/runner with a `#!/usr/bin/env node` header was
scanned as text without its inline maps being examined or counted. A
complete inventory then reported no shipped sources for it.
The probe holds the first bytes a member reader sees, so establish the
identity there. The script keeps the usual outcomes: a map that is read
counts, and one past an allowance or undecodable stays unknown.
* fix(audit): keep linker strings out of Go build paths
Reading `-X 'main.usage=-L level'` as a library directory made
go.build_paths report `level`, so a plain string could fail an audit
under --fail_on low.
Read the optional package selector once, before the setting's first
flag, and keep the argument of -X attached to it, so a string it sets
is never scanned as another option.
* fix(audit): keep a failed probe directory removal owned
A packaged image's probe copies the member's full bytes into a temporary
directory. When removing it failed, the error became an ordinary
unreadable probe: the audit could still succeed, nothing could retry the
removal, and the client heard nothing about the leftover bytes.
Hold the directory in the reader's resource scope, so close retries it,
and report the unfinished cleanup without replacing a primary failure.
* fix(audit): keep a declared .map from reading as a linker map
A script's sourceMappingURL=assets.map was dropped because the name
already looked like a map. A malformed assets.map then settled as a
linker map, so both source-map checks read not_observed and
native.debug_companions reported an exposure the release does not ship.
Keep a generic .map name as a declared target and read it as a
JavaScript map, in either scan order, so it stays unresolved.
* test(audit): rethrow typed errors from release exposure checks
A failed Result was rewrapped in a plain Error, which dropped its type,
cause and cleanup details from the test output.
* fix(audit): match companions by member identity, not spelling
An ASAR member's location is relative, and a declared map target is
absolute, so a malformed assets.map read before z.js stayed a linker
companion. The examined-map lookup, which is normalized, then returned
early, leaving both source-map checks not_observed.
Index companions by memberLocationKey and keep each reported spelling
under it. Spellings stay distinct because symlinks can make them differ.
* fix(audit): retract only guessed linker maps on a declaration
A script declaring sourceMappingURL=symbols.pdb after the scan reached
symbols.pdb removed the shipped PDB from the companion list, because a
late declaration retracted any companion. native.debug_companions could
then read not_observed.
Keep name-identified companions in their own set, and retract only maps
that were read as non-JSON and guessed to be linker maps.
* fix(audit): record an unfollowed map link as skipped
The directory and ASAR readers do not follow links, so a symlinked
app.js.map dropped out before any map counter moved. Its target was
scanned as plain text, and both source-map checks read not_observed.
Count a link that names a map as skipped wherever entries reach the
audit, and mark it examined so a declaration does not count it twice.
* fix(audit): read relative paths in Go compiler and assembler flags
A binary built with -gcflags=-I private-sdk keeps that setting, but
relative parsing covered only C compiler settings and -ldflags, so the
directory dropped out of go.build_paths.
Give -gcflags and -asmflags their own path options, taken from what
go tool compile and go tool asm document, and strip the package selector
as -ldflags does. The assembler's -D defines a symbol, so it is not one.
* docs(audit): state the packaged image read allowance
Packaged ELF, Mach-O and PE images are read in full and copied to a
temporary file, sharing one max_file_bytes allowance, but the docs and
coverage.allowances still said only a PE gets more than a header read.
Name the allowance and the copies in docs/release-exposure.md,
RELEASE_EXPOSURE_ALLOWANCES and the max_file_bytes description.
* docs(audit): say how go.build_paths treats rooted -X values
A rooted -X value cannot be told from a file path, so the audit reports
it. State that, and why, so the rule is a contract rather than a side
effect of how flags are scanned.
* fix(audit): keep a declared sibling map out of the linker guess
When the selected file declared a sibling map, the sibling classifier
never consulted the declaration. A malformed assets.map then read as a
linker map and raised native.debug_companions.
Leave a declared sibling to the unreached-target accounting, as members
already are, so it stays an unresolved source map.
* fix(audit): keep undecodable Go and Rust metadata unknown
Unsupported meant two things: a container this reader does not handle,
and metadata the image holds in a form the reader cannot decode, such as
a compressed .dep-v0 section. Both read as not_applicable, so an image
that carries metadata reported that the check did not apply.
Mark the second case on the format failure and carry it on
AnalysisUnsupportedTargetError, so the audit records a failed read and
stays unknown. The tools' own errors and messages do not change.
* fix(audit): keep coverage unknown for a PE header past the probe
A packaged member whose PE signature lies beyond the probe prefix stayed
unclassified, so the embedded-package probe never ran and coverage stayed
complete. A map inside such an image then read as not_observed.
Record the member as an unexamined package where it is already marked as
a possible native image.
* fix(audit): read the scripts of a standalone SVG
An SVG can carry a script with an inline source map, but only .html and
.htm members reached the script owner, so such a file left every map
counter at zero and javascript.source_map_sources read not_observed.
Treat .svg as a script-bearing page. htmlArtifactReferences already
reads SVG script elements, so this adds no parsing.
* fix(audit): keep every possible host when no name picks one out
With no named Electron host, every sibling ranks alike and
electron_host_paths stayed empty, so one image's safe fuses settled the
checks even when another possible host reported none.
Keep all sampled candidates as hosts in that case, so a candidate
without fuses leaves the checks unknown.
* fix(audit): read a text-named native member by its bytes
A PE member called payload.json took the text route, so it reached no
native probe, and its CodeView path and packaged-native uncertainty were
lost.
Decide from the bytes the text read already loaded, so real text costs
no extra read and the map and text allowances stay as they were.
* fix(audit): let a map or script read identify a native member
A package lists names without formats, so a PE called payload.js.map
took the map route and never reached a native probe. Its CodeView path
was lost, and with nothing counted as failed, native.pdb_path could read
not_applicable. A PE at a declared script or map target was lost the same
way, because declaration overrode the route.
Decide from the bytes the route already read, in the map route as in the
text route, and keep the listed kind for a promoted script target. Real
text and maps cost no extra read, so those allowances are unchanged.
* fix(audit): name a symlinked sibling debug companion
Selecting an executable skipped every symlink beside it before the name
check, so an app.pdb link left no companion path and no skip count, and
native.debug_companions read not_observed. Selecting the directory
reports the same link.
Check the name first. The map branch already needs a regular file, so no
link is followed.
* fix(audit): keep map coverage unknown for a linked script
A link named app.js to an extensionless payload was skipped as a link,
and its target was read as plain text with no map directives parsed. An
inline map there left no parsed or skipped count, so
javascript.source_map_sources read not_observed. Only links with a map
name were counted.
Count a link under any name that can declare a map, the set an unread
member already uses. It stays one omission when a page or a directive
also names it. The target is still not followed.
* fix(audit): keep a declared JavaScript map beside a selected file unknown
A local sourceMappingURL with a JavaScript map name such as app.js.map
was dropped on the assumption that it would be examined as a member.
When the selection is a file, sibling .js.map files are never read, so
the declared map left javascript.source_map_sources not_observed while
the same target spelled as a file: URL left it unknown. Record every
local target so an unreached one counts as skipped.
* fix(audit): read paths a C compiler forwards to the linker
CGO flags can hand linker options through -Wl,a,b or -Xlinker a. The
compiler flag parser saw -Wl,-L,private-sdk as one field and missed the
relative library directory, so go.build_paths reported not_observed.
Split forwarded options out before reading paths and read them with the
linker's own path options (-L, -T, --library-path, --script). This also
stops -Xlinker -Bsymbolic from being read as the compiler's -B prefix.
* fix(audit): read forwarded linker options by their linker meaning
The forwarded linker flag pattern read -Ttext=0x400000 as a script path
named text=0x400000, and it ignored -I, so -Xlinker -Ilib/ld-custom.so
lost the interpreter path the compiler flag pattern found before the
split.
Exclude the -T section and segment address options (bss, data, text,
text-segment, rodata-segment, ldata-segment) and read -I and
--dynamic-linker as paths.
* fix(audit): split Go flags as Go does and read external linker flags
Go splits a flag value with quoted.Split: a field that starts with a
quote runs to the matching quote, and any other quote is a literal
character. The reader stripped every quote, so CGO_CFLAGS
-I"/home/O'Neil/include" reported /home/ONeil/include, while cc was
given the directory "/home/O'Neil/include", quotes included.
-ldflags kept -extldflags values with the Go linker flags, where only
-L counts, so -extldflags=-Lprivate-sdk reported no path. The values
are now read as the C compiler driver reads them, including -Wl, and
-Xlinker forwarding.
A package member named payload.json or payload.js.map that holds an
ASAR took the text or map route, which checked only native signatures,
so the nested archive was scanned as text and coverage read complete.
The route's own read now recognizes the ASAR signature as well, and the
nested package leaves coverage unknown.
* fix(audit): keep a named script beside a selected HTML file unknown
A script element whose target had a JavaScript name such as app.js was
dropped on the assumption that it would be examined as a member. When
the selection is an HTML file, sibling scripts are never read, so a
sibling holding an inline map left javascript.source_map_sources
not_observed while the same target named app left it unknown. Record
every local script target so an unreached one counts as skipped.
* fix(audit): read a retained -pgo setting as one profile path
`-pgo file` names a single profile, and under -trimpath Go keeps only
its file name, so the flag readers dropped values such as
`private-training.pgo` and split paths with spaces or commas. Report
the value whole, skipping `auto` and `off`.
* fix(audit): read -extldflags paths as the external linker splits them
With -ldflags set to -extldflags '-L "/srv/private sdk"', the outer
flag reader could not match the path behind the inner quote, and the
external reader kept only relative paths, so go.build_paths read
not_observed. Read each -extldflags value once, as go tool link splits
it with quoted.Split, and collect rooted and relative paths from those
same arguments. The rest of the setting is read as before.
* fix(audit): read the Go linker's other path options
go.build_paths read only -L as a Go linker path option, so a retained
-ldflags='-tmpdir=private-obj -s -w' reported no path and --fail_on low
missed the directory.
Read every option `go tool link -h` documents as taking a directory,
path or file: -I, -L, -benchmarkprofile, -capturehostobjs, -cpuprofile,
-fipso, -importcfg, -memprofile, -o, -r and -tmpdir. -extld, -extar and
-libgcc name a program or `none`, so only their rooted values count.
* fix(audit): keep a quoted Go argument whole
goFields already reads -L "/tmp/private - sdk" as two arguments, but
each field was then split again before every whitespace-led dash, so
go.build_paths reported /tmp/private instead of the retained directory.
Split again only a field that begins with a flag, after any package
selector, as a quoted "-L dir -s -w" does. Any other field is one
argument and is kept whole, in -ldflags, -extldflags and CGO flags.
* fix(audit): keep coverage unknown for an unread packaged image
When a listed package member is a native image but its first read
fails, examinePackagedBinary returned before probePackagedImage could
mark its possible embedded package unexamined. Coverage stayed complete
and javascript.source_map_sources could report not_observed for a
payload nobody read.
Mark member coverage unknown wherever a native member goes unread,
including an encrypted one, as an image the probe could not read
already does, and say so in the limitations.
* fix(audit): read rooted Go tool path arguments whole
`-L "/tmp/private,sdk"` and `-L=/tmp/a=b` name one directory, so a `,` or
`=` in a documented path flag's argument is literal. `-trimpath` keeps its
prefix=replacement structure.
* fix(audit): count an unread packaged member once
* fix(audit): keep trimpath rewrites and directory fields apart from selectors
* fix(audit): keep a .map unsettled once a declaration was discarded
* fix(audit): skip env option arguments when reading a Node shebang
* fix(audit): keep companion coverage unresolved for a generic .map link
* fix(audit): count a dropped linker-map guess as a skipped companion
* fix(audit): read env settings after -- in a Node shebang
* fix(audit): route a selected native image named .map by its format
A directly selected or listed native image has no package format, so
examineMember skipped its native-first branch and sent a file such as
release.map through map routing. Its bytes then became a linker-map
guess and marked native.debug_companions exposed. Native format now
outranks the name for every member, packaged or not.
* fix(audit): read forwarded -rpath and -rpath-link as linker paths
A cgo build with CGO_LDFLAGS=-Wl,-rpath,private-runtime kept the
relative directory, but the forwarded linker parser only knew -L, -T,
-I, --library-path, --dynamic-linker and --script, so go.build_paths
reported not_observed. Add -rpath and -rpath-link to the same parser,
in the attached, = and next-field forms. Loader-expanded values such as
$ORIGIN/../lib and @loader_path/Frameworks name no build-host directory
and stay unreported.
* fix(audit): read each entry of a forwarded linker search list
-rpath and -rpath-link take a colon-separated search list. The loader
token filter tested the whole list, so -Wl,-rpath,$ORIGIN:/opt/private-runtime
dropped /opt/private-runtime along with $ORIGIN, and the rooted-path pass
cannot see an entry that follows a colon. Split each search list into its
entries and drop only the loader-expanded ones.
* fix(audit): expand loader tokens only in a linker search list
The runtime loader expands $ORIGIN and @loader_path only in a -rpath or
-rpath-link search list. The token filter also ran on the argument of -T,
-L and -I, so -Wl,-T,$ORIGIN.ld dropped a literal linker script path and
a complete read could report go.build_paths as not observed. Search list
entries keep the filter; every other linker path argument is kept as is.
* fix(audit): count a .map link beside a selected file as a skipped companion
The sibling scan skipped any .map entry that was not a regular file, so a
symlink such as symbols.map -> payload beside a selected executable left
debug companion coverage at not observed. The reader never follows links,
so the linker map such a link may name is unread, not absent. Count a
generic .map sibling link as a skipped companion without following it,
as noteUnfollowedMapLink already does inside a selected directory.
* fix(audit): split a linker search list only for an ELF image
Only the ELF linker reads a -rpath or -rpath-link argument as a
colon-separated list. The Mach-O linker stores each -rpath argument as one
directory, so -Wl,-rpath,@loader_path/lib:extra is a single loader-relative
entry, yet it was split and extra was reported as a build path. Carry the
native image format into the Go flag reader and split search lists only for
ELF; other formats read the whole argument as one entry.
* fix(audit): expand only the image loader's runtime tokens
A runtime search path token is expanded only by the loader of the image's
format. The ELF loader expands $ORIGIN, $LIB and $PLATFORM; dyld expands
@executable_path, @loader_path and @rpath. Each is literal text to the
other loader, so -Wl,-rpath,$ORIGIN:/opt/private-runtime on a Mach-O image
names a literal directory, yet it was dropped and go.build_paths reported
not_observed. Pick the token set from the native image format; the PE
loader reads no search list, so it expands none.
* fix(audit): keep a declared .map link beside a selected script a source map
The sibling scan counted any .map symlink as a skipped debug companion
before checking whether the selected script declared it as its source
map. classifySiblingMap already excludes a declared target that is a
regular file, so a declared symbols.map link added a false unknown to
debug_companions. Apply the declared-target check before either file-type
branch; noteUnreachedTargets still counts the unread map as skipped.
* fix(audit): byte-check members whose kind is only an inventory path role
Inventory gives a packaged member a role from its name, such as resource
for payload.json or source-map for payload.map. The packaged-format guard
treated any kind other than "file" as owner-stated, so a nested ASAR an
ASAR lists under one of those names skipped signature recognition and
cleared coverage. Track whether the package owner stated the kind (Bun
sourcemap region, SEA main script) and only skip the byte check then.
* fix(audit): read Go's linker -r setting as a runtime search list
go tool link -r sets the ELF dynamic linker search path, dir1:dir2:...,
which the linker writes to RUNPATH. It was read as one whole path, so a
release built with -ldflags='-r=$ORIGIN/../lib' reported the
loader-relative entry as a build path and go.build_paths as exposed.
Read -r entry by entry with the same image-loader-aware split and token
filter a forwarded -rpath uses: ELF splits at ':' and drops $ORIGIN
entries; Mach-O keeps the argument as one directory.
* fix(audit): keep a selected package executable's native carrier format
A directly selected PyInstaller executable named release.map recorded the
package format as its member format, even though its ELF or PE carrier was
already recognized. examineMember then missed the native-first branch and
treated the executable as a linker map, reporting a debug companion.
Forward the native carrier format for the selected file, as listed members
already do.
* fix(audit): keep coverage unknown for an unread unclassified member
A packaged member with no format of its own, such as a PyInstaller
payload.bin, recorded only a skipped text member when its read failed
before the header. Member coverage then stayed complete, though the
member could be a native image or carry a nested package. Leave
coverage unknown and say which member could not be read, as an unread
native member already does.
* fix(audit): keep ASAR header admission distinct from max_file_bytes
A directory holding an ASAR whose header exceeds the header admission
was reported as a file-size refusal with max_file_bytes advice, which
cannot fix it. Report the header admission as its own bound instead.
* fix(audit): thread the shared ASAR header admission into release exposure
createReleaseExposureService built ReleaseExposureReader with no admission,
so its inventory and every reopened ASAR reader stayed on the 16 MiB
default and ignored REA_ASAR_MAX_HEADER_BYTES and asar.max_header_bytes.
Resolve the admission in the composition with runtimeAsarHeaderAdmission,
as the Electron and V8 observation providers do, and pass it to both
inventoryArtifactInScope calls, the reopened container readers, and the
nested ASAR signature probe. The refusal now names the setting that can
raise it instead of claiming there is no runtime override.
* fix(audit): keep a nested ASAR probe stopped at the admission undecided
The release exposure reader treated an ASAR signature probe that stopped
at the header admission as "not an ASAR". A packaged member such as
payload.json holding an ASAR whose JSON starts past asar.max_header_bytes
was then read as plain text, its inner maps went unread, and
javascript.source_map_sources could stay not_observed.
A probe that reaches the admission limit, or runs out of a prefix read
after its extents parse, now marks package coverage unknown and records
a limitation, while the member is still examined as text.
* fix(cli): parse audit-release-exposure --max-file-bytes as a decimal token
Main now requires every numeric CLI option to admit decimal tokens and
reject 0x10 and 1e3 (tests/boundary/cli/cliDecimalArguments.test.ts).
Wrap the release exposure maxFileBytes option in cliDecimalField like the
other maxFileBytes options, so the domain field keeps its bounds and default.
* docs(specs): add the release exposure audit specification
Add spec.md, plan.md and tasks.md under docs/specs/artifacts/release-exposure/
as docs/development/documentation.md requires for a new feature, and list
them in docs/specs/README.md. The existing guide at docs/release-exposure.md
keeps describing current behavior and its route is unchanged.
* fix(audit): bind unpacked ASAR member reads to their inventoried digests
A selected ASAR is captured and checked against its inventoried digest, which
binds packed members. An unpacked member is read from its .unpacked companion
on disk instead, and readContainerMember and readEntryBytes never compared
those bytes with the member digest inventory recorded, so a companion
replaced between inventory and the scan was accepted.
readContainerMember now passes the member's inventoried digest for unpacked
entries, and readStreamBounded hashes the full stream, draining past the read
allowance when needed, and fails with "ASAR member changed before
interpretation", which surfaces as an artifact_changed error like the
container check. A regression test replaces a companion after inventory. The
guide, spec, plan and tasks describe the binding.
* fix(audit): let stated JavaScript roles outrank a .map name
isDeclaredMap() routed any member whose name ended in .map to map
handling before the owner's stated role or a page's script declaration
was considered. A Node SEA main script at boot.map, which the SEA reader
marks as javascript, and a .map script an HTML file loads both became
linker-map guesses, so their inline maps were never read and
native.debug_companions was reported exposed.
An owner-stated source-map role or a declared map target still makes a
member a map. Otherwise a stated javascript kind or a script target now
wins, and only then does the .map suffix, or the inventory path role that
repeats it, decide. Regression tests cover both cases, and the guide
notes that a SEA main script is read as a script whatever it is called.
* fix(audit): keep coverage unknown when a packaged text member fails to read
A compressed member named as text, such as payload.json in an unexpanded
PyInstaller host, can fail inflation before any header is returned. The
.bin case already leaves member coverage unknown because an unread header
may hide a nested image or package, but examineTextMember() only counted
the member as skipped text, so otherwise complete coverage stayed
complete and package-dependent checks could settle.
An unread packaged member whose kind its package did not state now goes
through noteUnreadBinary(), which counts it as skipped text, marks
coverage unknown, and records the limitation. A regression test corrupts
the zlib stream of payload.json in a CArchive host and checks the intact
member keeps coverage complete. The guide describes the rule.
* fix(audit): keep the unpacked ASAR digest check inside the read allowance
ea87cf8 made readStreamBounded hash an unpacked ASAR companion to its end
so it could compare against the inventoried digest, which drained the
stream past the read allowance. A 1 GiB unpacked module was read in full
for a 4 KiB header probe and again for a package probe, and a companion
that grew after inventory had no byte limit on that path.
readStreamBounded now stops at the allowance as every other member read
does, and compares the digest only when the member ended within the
allowance. A companion past the allowance stays over_allowance without a
digest verification. A test counts the bytes each scan read pulls from an
8 MiB companion. The guide and spec state the bound.
* fix(audit): read a script target whose bytes are a source map as a map
|
||
|
|
b6820d5794 | chore(verification): retire historical calibration studies (#10098) | ||
|
|
58a1216a57 |
fix(settings): mark per-call parameters and keep historical receipts
artifact_limits and web-network budgets report update_mode parameter because update_analysis_settings cannot change them. The legacy-name scan no longer reads docs, and the 2026-11-09 Ghidra receipts are restored to the fields they recorded. |
||
|
|
be20bdfb26 |
feat(settings)!: replace effect with update_mode and pending_restart
Callers read update_mode to see whether update_analysis_settings can change a limit, and pending_restart only when a configured value is not active in this process. |
||
|
|
30f96aef39 |
docs(ntfs): format verification records (#10019)
Run oxfmt on the NTFS verification notes and the MFT plan so format:check matches the repository formatter. Co-authored-by: Cursor Agent <cursoragent@cursor.com> |
||
|
|
0e88049d59 |
fix(docs): pin the NTFS extension fixture source link
Co-authored-by: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com> |
||
|
|
6d8c71f8b3 |
docs(ntfs): record extension ownership and public workflow verification
Co-authored-by: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com> |
||
|
|
bf671f20a2 |
docs(ntfs): record independent paired-volume verification
Co-authored-by: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com> |
||
|
|
bae4ec0cda |
docs(ntfs): record metadata page ownership and review verification
Co-authored-by: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com> |
||
|
|
09f58bf044 |
docs(ntfs): record complete CLI delivery measurements
Co-authored-by: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com> |
||
|
|
1f20c57627 |
docs(ntfs): record verification with Windows 11 metadata samples
Co-authored-by: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com> |
||
|
|
40239b31e7 |
docs(ntfs): record scoped page reuse and complete MCP measurements
Co-authored-by: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com> |
||
|
|
cb4e5cfbff |
feat(analysis): make resource budgets recoverable across CLI and MCP (#9706)
* refactor(analysis): centralize resource budgets and recovery facts * feat(analysis): expose ASAR and Ghidra runtime admission controls * feat(artifacts): expose reader admission and preserve limited recognition * feat(javascript): expose recoverable semantic precision budgets * feat(web): add recoverable source-map budgets and compact retained views * feat(web): make historical capture decoding budgets recoverable * fix(desktop): enforce selected view policies across capture and export * feat(runtime): make Frida and LLDB retention budgets selectable * feat(native): expose diagnostic budgets and preserve exhaustion causes * feat(managed): expose recoverable snapshot and representation budgets * feat(metadata): make Go and Rust inspection budgets recoverable * feat(wasm): expose representation and producer admissions * feat(evm): expose per-call worker resource budgets * feat(android): make resource and string-pool omissions recoverable * feat(producers): expose command and recorded-capture budgets * docs(analysis): record recovery contracts and integrated validation * fix(docs): link calibration oracle from published verification pages * fix(docs): restore desktop task anchors and receipt links * test(analysis): align fixtures with selectable budget contracts * fix(analysis): remove source-location projector dependency cycle * test(analysis): refresh budget recovery and verifier regressions * test(analysis): preserve format and publication recovery expectations * test(windows): align conformance with owned runtime admission * fix(desktop): preserve selected alias kinds in exported state * test(analysis): align provider fixtures with recoverable budgets * test(desktop): validate emitted alias metadata structurally * fix(ghidra): classify bounded Windows endpoint refusals * test(cli): assert selectable artifact admission recovery * fix(cli): preserve capture provenance in environment builds |
||
|
|
deae5fbe6d |
docs: correct workflow claims and clarify historical specifications (#9655)
* docs: correct lifecycle and analysis workflow claims * docs: distinguish historical specifications from current guides * docs: align translated setup and MCP recovery guidance |
||
|
|
22f28644fe |
feat(artifacts): inventory and extract Deno compile executables (#8527)
* feat(artifacts): inventory and extract Deno compile executables Adds Deno standalone artifact support for Mach-O, ELF, and PE carriers, exposing virtual files, entry points, recorded options, and file digests without running the executable. Extraction uses the existing safe output tree, preserves symlink targets as metadata, and assigns indexed paths to unsafe or oversized names. Environment-file values and certificate bytes remain in local Evidence; npm snapshots are reported by length without decoding. The documentation describes supported layouts, parsing limits, and refusal behavior. * fix(artifacts): resolve nested Intel Deno carriers to the enclosing app The backward trailer search stopped at the first confirmed sentinel, so an Intel app storing another Intel Deno executable as a virtual file reported the inner app. Search to the slice start, drop sentinels inside a confirmed payload, and fail closed on side-by-side trailers that differ. Correct the docs on retained environment values and generated regions. * fix(artifacts): keep Deno coverage, remote paths and PE resources truthful Report unknown coverage when the virtual filesystem cannot be listed, keep resolved specifiers when another remote ID is missing, and percent-encode characters in remote paths that a Windows destination would refuse. Reject a directory at the PE resource language level, and correct the guide's description of colliding generated regions. * fix(artifacts): normalize Deno remote escapes so hex case cannot collide Uppercase every percent escape in a generated remote path, so specifiers that differ only in escape case reach the path registry as one name and the second gets an indexed path instead of colliding on a case-insensitive disk. * fix(artifacts): fail closed on truncated enclosing Deno trailers and encode reserved remote path characters The Intel trailer scan rejected an overlong sentinel before reading its marker, so a truncated enclosing app vanished and the nested app was reported. Read the marker first and fail closed when a confirmed overlong trailer encloses another trailer. encodeURIComponent leaves `*` alone and the sanitizer covered only part of the Windows reserved set. remoteDestination now encodes the whole set once as uppercase percent hex; source_path keeps the original. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KuWHGvzJkrhCqjtEHUwqyH * fix(artifacts): defer truncated Intel Deno trailer failure until enclosing trailers resolve A damaged Intel asset stored before a valid one inside a valid app made the backward trailer search fail before it reached the outer trailer. Hold that failure until the search ends and drop it when a confirmed enclosing trailer holds the sentinel as data. * fix(pe): fail closed when a resource type branch is unreadable beside a found name namedResourceTargets skipped a type branch whose names directory, or a name entry whose string, could not be read. A duplicate RT_RCDATA entry pointing out of bounds therefore vanished once another branch matched, and locatePeResourcePayloads returned only the readable ranges while parsePeResources rejected the same tree. The walk now records unresolved branches. hasPeResourceName still reports false for a tree that never reaches the name, but once locatePeResourcePayloads finds the name it raises a format failure if any branch was left unresolved. * fix(artifacts): skip Intel Deno sentinels that a native segment maps A non-Deno x86_64 Mach-O that stores an Intel Deno executable in __DATA had the asset's trailer promoted to the root application, because the backward sentinel search never checked who owned the bytes it scanned. locateMachOSlice now records the file ranges of every segment other than __SUI and __LINKEDIT (libsui extends __LINKEDIT over the trailer), and the Intel search skips any sentinel inside one of them. A slice whose load commands do not frame stays with native inventory, since ownership cannot be shown there. * fix(artifacts): reject a universal Deno image with an unreadable sibling slice The universal Mach-O locator skipped any slice whose load commands could not be read or framed, and any slice with a FAT range issue. When another slice confirmed Deno, that slice was reported as the whole image, so a sibling declaring a different Deno application went unexamined. locateMachOSlice now reports why part of a slice was not examined (unreadable or unframed load commands, a truncated slice, or the Intel sentinel allowance). locateMachO collects those reasons for every declared and hidden slice and fails closed once any slice confirms a Deno section. With none confirmed, the image stays with native inventory as before. * fix(scripts): supervise the Deno verifier version check Run `deno --version` through the verifier command owner with a deadline instead of raw execFile, and close the owner when the version is refused. * fix(artifacts): keep unconfirmed Deno carriers native and fail truncated Intel trailers An ELF SUI note whose descriptor is too short for the full d3n0l4nd name now stays unconfirmed instead of failing closed; failures after the name matches still fail closed. Every marker-confirmed Intel trailer whose payload runs past the slice is now kept until the backward search ends and fails unless an enclosing payload contains it, so an Intel Deno app missing its last byte reports a Deno failure instead of native inventory. The Deno MCP boundary test now calls open_binary and inspect_artifact through artifactMcpCall, as docs/testing.md requires. * test(deno): drop source-text checks from verifier pin test Keep the wrong-version subprocess check, which covers the pinned release through behavior, and stop asserting on the verifier script's source spelling. * test(deno): close readers in finally in symlink and cancellation tests A failed assertion or read error in either test skipped reader.close(), leaving the file open while temp-directory cleanup ran. Both tests now close the reader in finally, as CONTRIBUTING.md requires. The closed-reader assertion in the symlink test stays after the finally. * docs(specs): add deno-standalone spec, plan and tasks Add docs/specs/artifacts/deno-standalone/ with spec.md, plan.md and tasks.md for the Deno compile executable reader, as AGENTS.md requires for a feature, and list them in the specifications index. * docs: list the Deno compile guide in the documentation sidebar * fix(deno): confirm the d3n0l4nd section before claiming Mach-O damage A __SUI segment alone no longer makes later load-command damage Deno's. A Mach-O whose __SUI segment holds only a differently named section now stays absent and keeps native inventory, while damage seen before or beside a confirmed d3n0l4nd section still fails closed. * fix(deno): keep a slice with an unparsed segment command unread A Mach-O segment command that frames but cannot be parsed may be the __SUI segment, so the slice is no longer treated as fully examined. A universal image whose other slice confirms Deno now fails closed instead of letting that sibling supply the whole inventory, and with no confirmed sibling the image still keeps native inventory. * fix(deno): confirm an ELF SUI note before rejecting its length A SUI note whose descsz runs past its note table was recorded as plain damage before its descriptor name was read, so a malformed Deno carrier fell back to native inventory. The owner and the d3n0l4nd name prefix are now read within the table apart from the claimed extent, and a confirmed note with an oversized extent fails closed. * fix(deno): confirm a truncated __SUI section record by its name A section record cut short after its 16-byte name was skipped, so a damaged Deno carrier fell back to native inventory. The name is now read apart from the rest of the record, and a truncated record that names d3n0l4nd fails closed. The range checks for a confirmed record move into their own helper. * fix(deno): reject nonzero bytes after the last ELF note When fewer than 12 bytes remained in a note table after its last note, the walk stopped without looking at them, so a Deno note was selected with complete coverage beside unaccounted data. The remainder is now read: zero padding is accepted, and nonzero bytes are recorded as damage, which fails closed once a SUI note is confirmed and otherwise leaves the ELF to native inventory, matching the native note parser. * fix(deno): validate FAT slice identity before choosing the search The locator took a universal slice's CPU type from its FAT row, so a row that called an x86_64 slice arm64 skipped that slice's Intel trailer and let the arm64 payload stand for the whole image. Each slice's header is now checked against its row with validateFatSliceIdentity. A mismatch leaves the slice unread, so a universal image whose other slice confirms Deno fails closed. The Intel fixture header now states CPU_SUBTYPE_X86_64_ALL so its FAT rows can match it exactly. * fix(deno): check the Intel trailer marker before judging its length A trailer length word of 2^53 or more was dropped before its d3n0l4nd marker was read, so an enclosing app with that length was skipped and a nested app's range stood for the whole image. The marker is now read first, and a marker-confirmed trailer whose length runs past the slice, safe or not, joins the deferred truncated candidates and fails closed unless an enclosing trailer holds it as data. * fix(deno): count short ELF note tables against the note allowance A note table shorter than one note header made a read of its trailing bytes but counted no note, so many distinct tiny tables could force one sequential read each past the 256-note allowance. Every nonempty table now counts at least one entry, both in the up-front estimate and while scanning, so an image past the allowance records the limit instead of reading every table. The trailing-byte check moves into its own helper. * fix(deno): fail closed on ELF extended section numbering beside a SUI note A zero e_shnum with a nonzero e_shoff moves the section count into section zero, so the section table was skipped and its SHT_NOTE tables were never compared. Record it as damage, as PN_XNUM already is, so a confirmed SUI note fails closed instead of reporting full coverage. * fix(deno): keep undersized Intel trailers until enclosing ownership is known A trailer length word below 8 was dropped before its d3n0l4nd marker was read, so an enclosing app with a length of 0 to 7 was skipped and a nested app's trailer was selected as the root. The marker is now read first, and a marker-confirmed trailer too short to hold it is kept as malformed and fails closed unless an enclosing trailer holds it as data. * fix(pe): treat non-directory RT_RCDATA branches as unresolved A duplicate typeId root entry whose target is a data entry was skipped without marking the walk unresolved, so locatePeResourcePayloads returned the readable named resource as complete. Route non-directory type branches through the same unresolved path as unreadable names directories so the Deno reader fails closed. * fix(deno): fail closed on a zero-fill d3n0l4nd Mach-O section denoSectionRange admitted the declared offset and size of a confirmed __SUI,d3n0l4nd record without checking its section type, so a S_ZEROFILL, S_GB_ZEROFILL or S_THREAD_LOCAL_ZEROFILL section could be read as a file payload while MachoImage reports it as not file backed. Reject zero-fill types with the shared isMachoZeroFillSection predicate before assigning a physical range. * fix(deno): bound the d3n0l4nd Mach-O section by its segment file extent denoSectionRange checked a confirmed __SUI,d3n0l4nd record only against the slice and file bounds, so a section reaching past its segment's fileoff/filesize was still read as a complete payload while MachoImage reports the same section as not file backed. Reject a section outside its enclosing segment's file extent before admitting the physical range. * style(deno): apply oxfmt to the DenoStandaloneLocation embedding union * fix(artifacts): preserve Deno carrier ownership * fix(artifacts): admit Deno parent segments * fix(deno): preserve native segment ownership * test(deno): cover mismatched Mach-O segment widths --------- Co-authored-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: morluto <76467478+morluto@users.noreply.github.com> |
||
|
|
a67525cdf1 |
docs: organize canonical documentation layout and verify new pages (#2791)
* docs: organize canonical documentation layout (URGENTE) Group all existing authored guides by domain and ownership, expose complete navigation, and preserve their public routes. Update physical path consumers and release configuration while retaining historical records. Document domain-based specs and ADR slugs without global numbering. Co-Authored-By: Codex <noreply@openai.com> * fix(docs): honor home layout in all-page route verification * style: format reviewed source changes * fix(docs): preserve routes and navigation cache inputs * fix: repair the static-check failures on the default branch (#2895) * fix: repair the static-check failures on the default branch The default branch fails its own required `Static checks` job three ways, so every pull request reports that job as failed regardless of its own change. - `npm run format:check` reports `src/domain/functionComparison.test.ts` and `src/ida/IdaOperationRunner.ts`. - `npm run lint` reports a duplicate `import { join } from "node:path"` in `tests/boundary/mcp/ghidraInventoryMcp.test.ts` (lines 4 and 17). - `npm run typecheck` reports `format: "devin"` missing from the client format union in `src/application/SupportedClients.ts`, and an unchecked index in `tests/boundary/cli/clientProfileLifecycle.test.ts`, whose pair array is inferred as `string[][]`. Apply the pinned formatter, drop the duplicate import, list the `"devin"` format that its own definition already uses, and mark the pair array `as const`. Validation: `npm run check:fast` and `npm run format:check` pass. * fix(setup): keep Devin on the JSON configuration dialect --------- Co-authored-by: 吴佳浩 <wujiahao@supremind.com> Co-authored-by: morluto <76467478+morluto@users.noreply.github.com> * fix(docs): update links after guide relocation * docs: correct verified documentation route counts * fix(docs): update browser capture guide link after move (#2820) Refresh gettext protected-link comments alongside the authored href. Co-authored-by: Codex <noreply@openai.com> * fix(docs): include http3 lab in stable routes * fix(docs): preserve historical release evidence and links * fix(ghidra): restore Windows verifier identity import * docs(wakaru): preserve pinned upstream policy link * docs(index): retain recently added guide links * docs(mcp): preserve parent retention clarification * fix(cli): preserve standalone import guidance * fix(test): preserve translated guide link drift coverage --------- Co-authored-by: Matheus Breguêz <matbrgz@gmail.com> Co-authored-by: Codex <noreply@openai.com> Co-authored-by: morluto <17176810+morluto@users.noreply.github.com> Co-authored-by: 吴佳浩Alben <35291372+XiaoBinGan@users.noreply.github.com> Co-authored-by: 吴佳浩 <wujiahao@supremind.com> |
||
|
|
bacc1e3846 |
perf(mcp)!: defer retained views in progressive catalogs (#9151)
Keep full schemas and canonical analysis unchanged while loading the cross-domain view through existing named discovery. Qualify real recent-tool workflows with token ledgers, latency, CPU, family RSS and retained-follow-up controls. BREAKING CHANGE: The opt-in hot-cold catalog no longer preloads inspect_analysis_view. Discover it before calling it, batch with its producer when needed, or use the unchanged default full profile. |
||
|
|
8084916859 |
perf(eval): measure MCP catalog usage through the real Codex harness (#9035)
* perf(eval): measure MCP catalog usage through real Codex sessions * fix(eval): control the active Codex code mode host * fix(eval): reject unavailable host controls without hiding client errors * test(eval): cover the supported default and actionable client errors * docs(eval): record pinned Codex catalog and real package measurements * fix(eval): pin matrix identity and retain failed trial observations --------- Co-authored-by: gensokyo-dev <gensokyo-dev@users.noreply.github.com> |
||
|
|
560674e2de |
feat(native): report binary hardening for ELF, PE and Mach-O (#8524)
* feat(native): report binary hardening for ELF, PE and Mach-O Documents inspect_binary_hardening and its CLI equivalent, including per-property states, supporting evidence, read budgets, and one report per Mach-O slice. Explains that missing protection imports remain unknown, signature presence does not establish validity, and hardened runtime requires inspect_signature. Adds hardening links throughout the documentation. The supplied diff also removes the advanced CodeQL workflow and configuration, along with snapshot-binding mismatch guidance. * fix(native): validate declared payloads and dynamic tables before hardening rows Run the shared range checks for ELF, read imports from the dynamic tables rather than section metadata, and keep an unresolved symbol count apart from an absent table. Check every Mach-O command before building rows and retain only the offsets the rows need. Make the verifier's readelf output wide, check the emitted format before parsing Mach-O, and place the sources before the libraries in the link. * fix(native): share Mach-O segment admission with hardening reads A segment with a section count its cmdsize cannot hold passed the hardening reader and could enable restrict_segment. Move the size, section count and file range checks to a shared admission that readMachoImage and the hardening reader both use, and pin the ELF load-size case with a regression test. * fix(hardening): reject Mach-O section payloads past the slice and empty ELF interpreters Segment admission never checked each section's payload range, so a file-backed section past the slice still produced rows. Decode sections with the section header owner and mark the slice malformed when a payload leaves it; zero-fill and empty sections own no file bytes and are exempt. admitInterp accepted a NUL first byte in a longer extent, which counted an empty interpreter as PIE evidence. It now requires a nonempty path. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KuWHGvzJkrhCqjtEHUwqyH * fix(native): read CFG instrumentation from decoded GuardFlags Use the IMAGE_GUARD_CF_INSTRUMENTED flag that guardFlagRecord already decodes instead of masking the raw GuardFlags word a second time. * fix(native): reject ELF table spans a second PT_LOAD remaps The hardening reader mapped DT_STRTAB, DT_SYMTAB and hash tables through the one PT_LOAD containing the table's first address. A second PT_LOAD starting inside the table could map the rest of it from other file bytes and the reader still reported parsed import evidence. Cap each mapping at the next PT_LOAD that starts inside it, reject a requested range that crosses that boundary, and stop hash reads at the same limit, matching the conflicting-mapping refusal in readElfSymbols. * fix(native): dedupe ELF note tables only on shared boundaries The hardening reader dropped any note table whose bytes another table already covered. A covering SHT_NOTE can hold a non-GNU note whose descriptor is itself a complete GNU property note, while PT_NOTE names that inner note directly. Walking only the outer stream missed the GNU marker and reported CET disabled. Drop an alias only when it starts and ends on boundaries of admitted tables, which are note boundaries of the walked streams. Any other covered alias leaves the feature rows unknown, as a partial overlap already does. * fix(native): admit every Mach-O symbol record after a canary match The Mach-O stack protector scan returned as soon as it found an undefined ___stack_chk_fail, so a later undefined symbol whose n_strx lay outside the string table was never checked. The same damaged table parsed or failed as malformed depending only on record order. Keep the match and finish the bounded record walk. Records after the match are only checked against the string table bounds, so their names are not read or charged to the scan budget. The ELF reader already admits its whole symbol table before matching names. * fix(native): accept ELF load aliases with the same file translation The hardening reader treated every PT_LOAD that starts inside a table, or that also contains the table's address, as a conflicting mapping. A load with the same address-to-file translation names the same file bytes, so tables it overlaps still have one interpretation, and the canonical ElfLoadMap already merges such spans. Treat equivalent loads as one interpretation. A load with a different translation, or the zero-filled tail of an equivalent load, still ends the usable range, so a remapped table is still rejected. * fix(native): keep same-span ELF note tables with different alignments unknown A section and a program header that share endpoints were treated as one note stream even when their effective note alignments differed. Walking the same bytes at 4 and 8 can frame different notes, so dropping the second table could hide a GNU property word and report CET or BTI as disabled. Require matching effective alignment at both endpoints, and no alignment change inside the covered span, before treating a table as an alias. Otherwise mark the feature rows unknown. The fixture's .note.gnu.property section now declares the class's note alignment, as toolchains emit it. * refactor(native): read LC_SYMTAB through the shared fixed-word decoder Move the Mach-O fixed uint32 command layouts out of MachoCommandNames into domain/apple/machoLoadCommands so there is one owner of those offsets. MachoCommandNames and the hardening reader now both decode through it; the hardening reader keeps its own bounds and import policy. * fix(native): skip Mach-O common definitions in canary imports A common definition sets N_UNDF|N_EXT like an import but stores its allocation size in n_value. An object holding only `int __stack_chk_fail;` therefore reported stack_protector: enabled. Require a zero n_value before an undefined external counts as an import, for both nlist widths. * refactor(native): read linkedit data commands through the shared fixed-word decoder readMachoLinkeditDataCommand reproduced the dataoff/datasize offsets that FIXED_WORD_FIELDS already owns. Take the command and decode through decodeMachoFixedWordFields, as readMachoSymtabCommand does, keeping the truncation and endianness behavior. * fix(native): skip empty SHT_NOTE sections in ELF hardening A zero-sized SHT_NOTE owns no bytes, so the section directory marks it not file-backed and the hardening reader rejected the whole image as malformed, while readElfImage accepted the same bytes. Skip empty note sections before the file-backing check; they have nothing to walk. * fix(scripts): run hardening verifier commands through the shared owner The binary hardening verifier started its probes and compiler drivers with raw spawnSync, which bypassed the src/process/ supervision that AGENTS.md requires. Every launch now goes through createVerifierCommands, and the temporary root is removed only after the owner closes cleanly. * fix(scripts): clean up hardening verifier commands on Ctrl-C SIGINT or SIGTERM ended Node before withVerifierCleanup ran, so detached compiler trees and the temporary root could outlive the verifier. The verifier now turns those signals into an abort that every owned command receives, so cleanup closes the owner and removes the root before exit. * fix(scripts): fail the hardening verifier when a signal lands outside a command Only the owned commands read the interruption signal, so a SIGINT or SIGTERM during the final cleanup or between commands was recorded and then dropped, and the verifier exited 0. After cleanup settles, the verifier now throws the interruption reason. A failure the operation or its cleanup already raised still leaves first and is not replaced. * fix(native): fail Mach-O slices with a malformed encryption command The hardening reader turned every malformed status from the shared encryption reader into an unknown `encrypted` row, so a short LC_ENCRYPTION_INFO_64 or an encrypted range past the slice still left the slice parsed with a full report. Only two conflicting encryption commands stay an unknown row now. Any other encryption defect fails the slice like other structural command failures. * fix(native): ignore STB_LOCAL undefined ELF symbols as imports A local undefined __stack_chk_fail or __memcpy_chk is never resolved against another object, so it does not establish an external import. Skip STB_LOCAL bindings when reading protector signs, and give the fixture's positive symbols STB_GLOBAL binding. --------- Co-authored-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
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) |
||
|
|
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) |
||
|
|
a989c0e458 |
refactor(evidence): unify retained inputs across domains (#8299)
Declare logical Evidence sources once and resolve exact retained inputs through shared application workflows across 20 composition tools. Preserve domain-owned authentication, source closure, caller choices, coverage and resource admission. Validation includes public CLI/MCP regressions, local Codex terra/high trials and fresh-import real-artifact workflows. Measurements, failed attempts and coverage limits are recorded in docs/verification/evidence-input-composition-2026-10-31.md. Fixes #8293. Fixes #8281. Assisted-by: Suika (gensokyo-skills:suika-scatter-gather-planning) |
||
|
|
269630f4e1 |
docs: archive historical verification and release inventories (#7519)
Retire redundant historical JSON copies from the active documentation tree after verifying identical published release snapshots and an immutable route-report archive. Preserve current release data, original evidence, release publication logic and historical access. Validation: 48 release tests; documentation conformance and rendered archive link/digest; Git blob/SHA-256 checks; selected current-head CI passed. Greptile: 5/5 with no actionable findings. Codex GitHub review unavailable due quota. Closes #7517. |
||
|
|
570abe95f2 |
feat(android): link native declarations to static JNI candidates (#7472)
Link original DEX native declarations to per-ABI function exports and relocated static JNI table candidates through shared CLI/MCP workflows. Preserve global attribution ambiguity, exact provenance, partial coverage and retained Evidence; default MCP delivery to bounded summaries with filtered/batch pages. Use one immutable ELF relocation decoder with streaming IL2CPP materialization, cooperative bounded export/version parsing and bounded JNI state. Reuse validated session inventories without loading target libraries or executing JNI code. Validation: relevant type/lint/build and parser/domain/public CLI/MCP regressions; genuine four-ABI NDK fixtures and independent library/slot oracles; actual JDK/JADX Conscrypt workflows with retained-source removal. Full measurements and limitations are recorded in docs/verification/android-jni-bindings-2026-10-27.md. Closes #6403. |
||
|
|
03f17cf41d |
feat(android): apply caller-selected R8 mapping evidence (#7456)
Apply caller-selected R8/ProGuard mappings to shared Android inspections, preserving raw identities, exact verification, ambiguity and provenance. Add retained summaries and bounded/cached parsing, with measured real R8/Conscrypt workflows and review fixes. Fixes #6329. Co-authored-by: 555aaditya <107977627+555aaditya@users.noreply.github.com> |
||
|
|
2817d30ab2 |
fix(docs): preserve verification reports after experiment archival (#7462)
Move the retained allocation report and unchanged receipt JSON into verification documentation. Keep one report producer, repair current and archived links, document test-support ownership, and remove retired lint inputs. Fixes #7458. Validated focused/Python checks, report consistency, docs rendering, and real Bash CLI/MCP facts against an independent oracle. Greptile reviewed all seven files without comments. CI source/Windows/shard-8 failures match clean-main or existing-main controls; the requester authorized merging with those established failures. Codex review quota was unavailable. |
||
|
|
5cf7133f82 |
refactor(javascript)!: consolidate renderer and route ownership
Remove the derived package renderer property from new application graphs. Use renderer_declarations for source fields and package loads edges for the selected candidate; previously retained Evidence remains readable. |
||
|
|
d109a39105 |
feat(cli): validate browser scenarios offline (#7343)
Add validate-browser-scenario for offline schema and semantic checks of inline JSON, local files and stdin. Return compact validity/action/secret counts without reading secret values or discovering, launching, attaching to or navigating a browser. Execution and validation share CLI input admission and the canonical domain schema. Preserve existing capture lifecycle, diagnostics, cancellation and caller-relative executable paths. Integrates #7149 with attribution. Fixes #5488. Validation includes 24 focused tests, static/build/docs checks, and the preserved real PocketBase scenario with its backend stopped and secret variables unset. Verification and resource/token measurements are recorded in docs/verification/browser-scenario-validation-2026-10-27.md. Co-authored-by: Ace <1160684883@qq.com> |
||
|
|
3fcbabe593 |
feat(browser): reuse saved response values across scenario steps (#7267)
Save typed response fields and reuse generated IDs or authentication values across browser scenario requests, UI observations and navigation through the shared CLI/MCP workflow.
Preserve exact numbers, retained Evidence, explicit source/coverage limits and prospective page-value semantics. Bound response decoding, retained spellings and request serialization before allocation, and protect marked response values across source records, follow-up text and planned screenshots.
Integrates Edgar Arakelyan's #7162, including follow-ups through
|
||
|
|
6d5532a204 |
feat(web): page retained bundle endpoint observations and inferences (#7259)
Page retained web-bundle endpoint observations and inferences through shared CLI/MCP projections, preserving source identities, scoped unknowns, coverage and complete Evidence access.
Based on HarsH’s original implementation in #7213 (
|
||
|
|
5508b2affd |
fix(analysis): retain source evidence through directory exports (#7250)
Retain authenticated provider source records independently of snapshot replay eligibility and return them explicitly to plain application callers, including late cancellation. CLI/MCP commit source records before delivering links; function dossiers share the canonical Evidence constructor. Keep directory replies compact and preserve successful retry verification. Real Ghidra CLI/MCP investigations verify 652-function ls, historical report provenance, cross-session resume, unchanged source/project content and complete offline Evidence. Final full 14,338-function libcrypto comparison matches all metadata/pseudocode facts and independent fresh-import observations: 14,194 written, 143 external, one recorded deadline; 494.03 s, 445.63 CPU s, 1.584 GiB sampled family peak under 2-CPU/8-GiB guards, with no OOM or cleanup failure. 144 focused tests and static/docs/site checks pass; latest-main integration passes 289 cases plus typecheck/lint and actual CLI snapshot source retrieval. Final CI source/static/build/docs/quality/package checks pass. Four ADB shard failures also occur in main CI 40349559391; cancelled coverage remains unverified and author declarations stay unchecked under the user's explicit bypass authorization. No human-review attestation is made. |
||
|
|
f3ed1657f8 |
feat(mcp): support runtime analysis limit recovery (#7049)
* feat(mcp): support runtime analysis limit recovery * refactor(config): remove unused limit re-exports * fix(android): keep fixture release metadata dependency-free * fix(settings): preserve provider budgets and recovery facts * fix(tests): resolve lint plugins from their repository owner * refactor(mcp): verify guidance delivery without frozen wording * fix(android): align availability with runtime timeout * test(recovery): assert limit facts instead of frozen wording * fix(mcp): share runtime settings with the default JADX provider * test(mcp): bound acknowledgements and release held work on failure * fix(catalog): declare runtime settings tools as MCP-only |
||
|
|
3d84215bb2 |
feat(pe): inspect TLS callbacks and load configuration (#7008)
* feat(pe): inspect TLS callbacks and load configuration Adds inspect-pe-load-config and inspect_pe_load_config for PE32 and PE32+ images, with artifact hashes and file ranges in the returned evidence. Reports callback addresses and mappings, plus size-covered security-cookie, SafeSEH, CFG, EH-continuation and XFG fields; flag-derived mitigation claims are explicitly labeled inferred. Missing directories remain null, and malformed storage or exhausted read budgets return errors without partial success. Adds documentation and tests for service validation, CLI/MCP access, malformed inputs and stable-file handling. x86 images that declare the legacy 64-byte load-config directory are read to the structure's own Size when those bytes are file-backed, as the Windows loader does. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016y53PdpyMAp9RSW65fatHe * fix(pe): retain snapshot facts and bound load-config section lookup Keep the completed stable-read path, SHA-256 and byte count as a file-snapshot-hash partial observation when descriptor close fails or is cancelled. Report the checked Size span as structure_location and add it to Evidence locations beside the declared directory range. Resolve callback sections through a sorted segment index built once per image, keeping the ambiguity failure. Pin every load-config field to offsets transcribed from the PE/COFF spec and llvm COFF.h at its exact Size boundary for PE32 and PE32+. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016y53PdpyMAp9RSW65fatHe --------- Co-authored-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
cab78de506 |
feat(ghidra): analyze GameCube/Wii DOL executables through Ghidra (#6240)
* 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 <342580529+gensokyo-dev@users.noreply.github.com> |
||
|
|
f2fa7f648d |
feat(android): project a structured manifest component inventory (#5025)
* feat(android): project a structured manifest component inventory * fix(android): derive provider export defaults from the target SDK and match namespaced attributes * fix(android): bound resolved names and authorities in the component inventory * fix(android): separate nonliteral exported values from omission * fix(android): bound manifest component projection * fix(android): preserve permission override semantics * fix(android): retain unresolved package identities * test(android): cover multibyte manifest name budget * fix(android): measure inherited permission once * fix(android): preserve unknown symbolic permissions * fix(android): preserve manifest component uncertainty * fix(android): report unsupported manifest facets * fix(android): preserve manifest identities * fix(android): preserve alias target references * fix(android): validate alias targets and authorities * fix(android): bound alias target expansion * fix(android): index alias target spellings * fix(android): preserve alias identities and isolate legacy defaults * fix(android): keep legacy component defaults typed as unavailable * docs(android): record manifest projection and real JADX costs * fix(android): preserve SDK defaults and manifest namespaces * docs(android): verify SDK and namespace ownership corrections * fix(android): accept DEX class names and hexadecimal SDK levels * docs(android): record DEX and SDK follow-up verification * fix(android): validate manifest package identity separately * docs(android): record final package-identity verification * fix(android): preserve exact DEX component and alias names * docs(android): record exact-name integration verification * fix(android): retain declared foreground service type text * docs(android): record final declared-value validation --------- Co-authored-by: BaaRai <baarai@users.noreply.github.com> Co-authored-by: morluto <76467478+morluto@users.noreply.github.com> Co-authored-by: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com> |
||
|
|
3ce311757d |
feat(il2cpp): link generic method instances behind --generic-instances (#5785)
* feat(il2cpp): read generic containers on demand and render method spec names Generic container indices and records are demand readers on the same metadata snapshot, so links that do not ask for generic instances allocate nothing. Type rendering keeps one graph walk with its workspace, depth and cycle guards; the C#-like names and the Il2CppDumper GetMethodSpecName names are two styles over it. * feat(il2cpp): link generic method instances with admitted rows and kept issues generic_instances links every genericMethodTable row through methodSpecs, genericInsts and genericMethodPointers, choosing the one CodeRegistration layout whose counts bound every row. The table is reserved against the report budget before any row is resolved; rows and issues are admitted before retention, and a contradicting row stays unknown with an address-backed issue instead of failing the link. Views page the rows by definition, state, address and name, unrequested and historical collections stay unknown, and Ghidra seeds fold instances after the definitions. * feat(il2cpp): accept --generic-instances on the CLI and describe it in tool contracts * docs(il2cpp): describe generic method instances and their limits * fix(il2cpp): keep issued generic rows unknown and name their row A method spec that contradicted the metadata still reported its pointer slot as code, while the schema and provider limitation promised unknown. Such rows now keep no address, every issue carries its genericMethodTable row, the domain validator requires unknown exactly on issued rows, and shared specs name one other row instead of all of them. The row-size lower bound moves to the metadata admission owner, the hex helper is shared, and budget wording says checked rather than reserved. * test(il2cpp): reject ambiguous generic layouts and pin budget, yield and limit paths * fix(il2cpp): keep unmapped generic slots per row and reject undeclared arguments Layout selection no longer requires every genericMethodPointers slot to be mapped; each row reads its own slot, so one unmapped slot leaves only that row unknown with an issue. Method specs whose class or method argument count differs from the owner's declared generic container are issued and unnamed. Fixtures now declare the containers their generic rows fill. * fix(il2cpp): page generic method issues instead of retaining them in summaries Summaries keep generic_methods without its issues and count them in counts.generic_method_issues; the generic_method_issues collection pages them by rows, indices and fields, as code_gen_modules already does. Docs also name the method_indices filter the request schema accepts. * fix(il2cpp): read split generic arguments per slot and method containers on demand Generic argument arrays may span adjacent loader ranges: admit them with hasPointerArrayCoverage and read each slot through readPointer, instead of requiring the whole array in one mapping. Method generic container indices are read on demand from the method record, like type containers, so no link path allocates a per-method column. An owner whose container index is out of range now leaves the method spec unnamed with an issue instead of naming it unchecked. * fix(il2cpp): report generic entries past the address space at their row A damaged methodSpecs or genericMethodPointers pointer could make a spec or slot address exceed 64 bits. The issue kept that address, which the address schema rejects, so inspection failed instead of leaving the row unknown. Check the entry range first and attach the issue to the genericMethodTable row (or its slot field). * chore(il2cpp): remove unrelated formatting changes * fix: restore adb float pattern and mobile fixture from main The merge commit's lint-staged run rewrote these two main files: oxlint --fix dropped the escape in [+\-], which is invalid under the RegExp v flag. * fix(il2cpp): preserve unreadable generic method identities * docs(il2cpp): record generic-instance workflow verification * docs(il2cpp): qualify final integration checks --------- Co-authored-by: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com> |
||
|
|
781760a195 |
feat(apple): inspect IPS crash reports (#6511)
* feat(apple): inspect IPS crash reports Add inspect_apple_ips for caller-selected Apple .ips files. Parse the first-line metadata object and, when bug_type is the string 309, the crash body: process identity, exception and termination facts, threads, usedImages, lossless integer registers, and documented classification rules. Other bug types including 288 stackshots return no crash result. Opened Mach-O and dSYM UUID joins remain unobserved. Related to #4297 * fix(apple): keep IPS register literals exact Use the arm64e fixture integer that matches 0x8527000193ed4944 instead of an IEEE-754-rounded decimal, and count the lossless JSON example length from the source text. * fix(apple): budget IPS parsing and keep typed presence Parse the IPS metadata and crash body through a projection of the documented members. Members the report does not interpret are syntax-checked without being materialized, and thread, frame, image and the new max_registers budgets run before each row is parsed, so a large selected file fails with a typed resource error instead of first expanding every row. Fixed allowances bound JSON nesting, unlisted key names and termination reasons. Present values whose JSON type differs from the documented one are no longer read as absence: they are listed in coverage.type_mismatches and coverage becomes partial, including wrong-type or missing threads and usedImages collections. osVersion keeps the producer dictionary or the string form. Frame roles follow physical frame depth, so the top frame of a non-faulting thread is current_instruction and inlined rows share the following frame's role. SIGKILL classes compare the canonical hex once, so 0xdeadd0cc no longer matches dead10cc. Invalid UTF-8 is refused instead of being replaced, and JSON strings are decoded by copying unescaped spans. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ra7LM2NyQ7DumdssxbUq5g * fix(apple): bound integer text expansion and reconcile coverage * refactor(apple): retain image metadata at its owning row * refactor(apple): validate row capacity through reconciled coverage * test(apple): verify resource recovery at the public error boundary * docs(apple): record complete IPS task and corpus measurements * docs(apple): record integrated checks and stdio corpus evidence * docs(apple): record PE integration and baseline cleanup controls * fix(native): bound IPS string decoding and preserve invalid identities * refactor(native): use exact optional IPS source evidence * docs(native): verify IPS escape capacity and complete-task costs * test(native): verify IPS through production stdio MCP * style(tests): format IPS advertised schema bindings * fix(native): preserve unresolved IPS references and recorded frame semantics * docs(native): record IPS reference review evidence * fix(apple-ips): enforce JSON and report consistency * docs(apple-ips): record final consistency verification --------- Co-authored-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com> |
||
|
|
72ba1cd92b |
fix(verifiers): centralize failure and cleanup ownership
Own operation results and exact rejections in withVerifierCleanup, including undefined, null and independent cleanup failures. Remove the optional-primary cleanup contract and migrate every caller, preserving returns, annotations, signal restoration and dependent cleanup order. Make Frida/JEB reporters fail on reasonless rejections and retain JEB fixture files while provider cleanup is incomplete. Own the nested packaged WASM verifier and expose its composite process/root cleanup capability for retry. Update runnable docs and isolated fixture dependencies. Validation: 547 focused cases in 20 files, eight isolated boundary unit checks, static/build/docs/discovery gates and duplication check (3.58%). Node process, SDK and descriptor cleanup are exercised; provider and packaging results in orchestration tests are simulated. Pack/install subprocess ownership remains with the existing shared package runner outside the nested-command change. |
||
|
|
e5965adb29 |
feat(cli): accept JavaScript source selections from a JSON array or file (#5029)
* feat(cli): accept JavaScript source selections from a JSON array or file Add --source-paths-json to analyze and analyze-javascript-application. It takes an inline JSON array or a JSON file path of exact inventoried paths and appends them after any --source-path values, so a long selection no longer has to fit on one command line (cmd.exe through npx on Windows stops at 8,191 characters). Entries stay artifact-relative and go through the existing source_paths validation; the native lane rejects the option like --source-path. Refs #4987 * fix(cli): cancel --source-paths-json file reads with the command Pass the command's abort signal through parseCliJsonInput to readJsonInputFile, so SIGINT/SIGTERM stop a selection-file read instead of waiting for it to finish. A cancelled read now propagates its abort rather than being reported as a file read failure. * test(cli): preserve ASAR identity for JSON file selections * docs(cli): record source-selection task costs and identity verification --------- Co-authored-by: KJLavender <KJLavender@users.noreply.github.com> Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com> |
||
|
|
4ef916da03 |
feat(pe): inspect import and delay-load tables (#6509)
* feat(pe): inspect import and delay-load tables Add inspect_pe_imports / inspect-pe-imports to read PE32/PE32+ import and delay-load directories without executing the image. The report keeps DLL names, named and ordinal functions, ILT/IAT RVAs, and a pefile import hash of the standard import table. A missing directory is observed absence, not proof the image never imported APIs. * fix(pe): default PE import fixture options Allow peImportImage() without an argument so CLI and filesystem tests can use the KERNEL32 sample without repeating an empty options object. * test(pe): keep import path cases on the dedicated MCP contract inspect_pe_imports already rejects relative MCP paths in its own boundary test. Leave the shared absolute-path matrix to tools whose handler issue projection matches that suite's expected shape. * fix(native): index virtual-to-file mapping lookups mappedFileOffset scans every mapping twice per span, so a parser that translates one span per table entry costs sections x entries. Add fileMappingLookup, which builds an O(n log n) interval index once and answers each span in O(log n) with the same accept, reject and translation results. A differential test checks it against mappedFileOffset over randomized overlapping mappings and zero fills. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ra7LM2NyQ7DumdssxbUq5g * fix(pe): report partial import evidence and exact pefile hashes - Withhold imphash with an explicit reason whenever the standard import table is partial (unreadable names or thunks, missing tables), or when pefile 2024.8.26 would hash a different row sequence: invalid or truncated names, ordinal 0, descriptors without functions, tables its bounds or repeat/spread heuristics discard, and its 8192-thunk import symbol cap. Delay-load issues do not withhold it. - Hash ordinal imports with pefile's complete ordlookup tables and its ord<decimal> fallback instead of declining unmapped ordinals. - Report a nonterminal descriptor whose FirstThunk is zero, and a delay-load descriptor whose IAT or INT RVA is zero, as malformed_entry partial coverage instead of a complete empty inventory. - Directory evidence lists the file spans actually consumed for the descriptor array and terminator; discontiguous mappings produce separate spans and separate Evidence ranges. - Carry exhausted limits as PeImportLimitFailure.limit and derive recovery from it; the fixed 16 MiB retained-text ceiling reports a fixed allowance instead of max_file_bytes advice. - Translate RVAs through the indexed mapping lookup and yield with a cancellation check every 1024 table entries. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ra7LM2NyQ7DumdssxbUq5g * fix(pe): read only own ordinal tables for DLL names DLL names such as constructor and __proto__ pass pefile's name rule but resolved inherited object members, throwing before the ord<n> fallback. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ra7LM2NyQ7DumdssxbUq5g * fix(native): preserve PE import facts and prove hash compatibility * test(native): verify PE import workflows and measured costs * refactor(native): narrow PE facts across the container union * docs(native): record integrated PE import validation * fix(native): bound every PE import RVA span * docs(native): measure RVA guard completed workflows * fix(pe): reconcile import coverage with retained rows * fix(pe): retain partial imports at descriptor mapping gaps --------- Co-authored-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com> |
||
|
|
9df94a62cd |
feat(javascript): link assignment right-hand sides to their writes (#6206)
* feat(javascript): link assignment right-hand sides to their writes A declaration carries its value forward, but a later assignment to a variable, a property or through a compound operator stopped the forward trace at the right-hand-side read. The semantic IR now records one syntactic assignment flow per simple assignment target (ranges, operator, owning callable), and the graph projection connects each retained read of the right-hand side to the write occurrence through a defines relation. Compound assignments also consume the previous value of the target; the written value itself stays unknown, keeping the boundary of #4342 intact. Fixes #6193 * fix(javascript): preserve computed key assignment ownership * test(javascript): retain nested callback assignment ownership * docs(javascript): record assignment influence verification * test(mcp): isolate mocks and acknowledge cancellation boundaries * test(javascript): retain CLI diagnostics and current-main verification --------- Co-authored-by: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com> |
||
|
|
1a48a82020 |
feat(analysis)!: default function MCP output to retained summaries (#5660)
* feat(ghidra): bound one procedure's decompile without closing the session Fixes #5340 * fix(ghidra): report decompile limit as a request timeout Keeps BinarySession from marking every Ghidra operation unavailable after one procedure's decompile reaches the limit, and names the MCP tool-call deadline in the remediation. * fix(ghidra): budget the decompile limit and report cancellation-closed sessions Adds REA_GHIDRA_DECOMPILE_TIMEOUT_SECONDS to the derived Codex/Kimi tool deadline and forwards it through setup. Marks a remote failure that settles after caller cancellation so the timeout projection no longer promises session reuse. * feat(ghidra)!: default analyze_function to a retained summary analyze_function was the one analysis tool never wired into REA's detail: summary|complete convention, so it always returned the complete native dossier inline (0.2-10 MB, dominated by value-flow and references). Agents on tool-result-capped harnesses never reached the pseudocode. Add `detail` to analyze_function, defaulting to summary: return a compact dossier (procedure identity, pseudocode size, facet counts, value-flow availability) and retain the complete dossier Evidence in the session. Page pseudocode, references, assembly and value-flow through normalized_result.parent_evidence_id with inspect_analysis_view, which already projects every native facet. detail: complete returns the full dossier inline as before. Delivery detail never reaches the provider or Evidence identity. BREAKING CHANGE: analyze_function now defaults to the compact summary view instead of the complete dossier inline. Pass detail: "complete" for the previous behavior. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AoJZDPcrgmMsCqLG4LfiFb * feat(cli): expose analyze_function detail on the function command Wire `rea function` into the shared CLI detail-delivery convention (--detail, --evidence_output) so the CLI and MCP adapters offer the same summary | complete choice for analyze_function. Summary delivery writes the complete dossier Evidence to a bundle and returns the compact summary view; it fails before analysis when --evidence_output is missing, so no provider work is wasted. The CLI default stays complete because a one-shot command has no in-process Evidence retention. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AoJZDPcrgmMsCqLG4LfiFb * refactor(ghidra): harden analyze_function summary after independent review FlatlineRoundtable cross-model review (6 lanes) converged on the summary projection's availability semantics being underspecified. Document that facet_availability is authoritative over raw pseudocode length, that code_units are UTF-16 (the unit inspect_analysis_view pages by), and that value_flow.truncated is meaningful only when value-flow is available. Parenthesize the nullish-coalescing/comparison for clarity. No behavior change; declined claims (value-flow "truncated collapse", unresolved_calls dead code) were schema-impossible or already correct. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AoJZDPcrgmMsCqLG4LfiFb * test(ghidra): cover analyze_function detail wiring on both adapters Close the roundtable's top convergent finding: the summary path had only domain-projection coverage, not the server/CLI wiring where the breaking behavior lives. MCP server (registerEnhancedTools): the default delivers a retained summary whose complete parent is recorded for paging; delivery detail is stripped before the provider request; detail: complete returns the full dossier inline; a registration without retention rejects a summary request before any provider work. CLI (rea function): the default returns the complete dossier; --detail summary without --evidence_output fails before analysis; with the bundle sink it returns the compact summary and writes the complete Evidence bundle. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AoJZDPcrgmMsCqLG4LfiFb * fix(ghidra): keep analyze_function delivery detail out of provider inputs Address the five automated review findings on #5660. P1: `detail` lived in the shared enhancedInputSchemas.analyze_function, so inspect_native_api composed `{procedure, detail: "summary"}` and the strict Ghidra analyze_function schema rejected it. Delivery detail is now owned by the analyze_function MCP tool contract; the handler separates it before any provider call, and executeFunctionAnalysisEvidence parses the provider input only. The MCP regression test validates provider input exactly as the production Ghidra client does and exercises inspect_native_api. P2: the analyze_function summary Evidence now carries the parent's relationship_coverage like native facet views, so partial or unknown caller/callee discovery is not reported as complete. P2: the summary reports the dossier's declared facet_availability, nulls the counts of facets the provider declared unavailable, and lists each reason in unknowns, so Kuna's empty references, comments and basic blocks are not mistaken for observed absence. P2: inspect-analysis-view evidence-file sources accept the Evidence bundle that --evidence_output writes, selecting a record with source.evidence_id when the bundle holds several; the CLI test reads the saved bundle back through that route. Tests that assert the complete dossier over public analyze_function now pass detail complete, and the oversized-dossier test asserts the summary view it previously expected to be unsupported. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AoJZDPcrgmMsCqLG4LfiFb * fix(ghidra): keep omitted function facets unknown in the summary The analyze_function summary coerced an omitted unresolved_calls record to a count of 0 while the native facet view reports the same dossier as "not recorded", and it carried no counts at all for the pageable swift_string_literals facets. Count optional records as null when the dossier omits them, name them in unknowns, and add nullable counts for both Swift literal facets. The summary Evidence envelope also dropped the function address: createAnalysisViewEvidence now derives one function-view predicate for both the address location and relationship coverage. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AoJZDPcrgmMsCqLG4LfiFb * fix(scripts): request complete analyze_function dossiers in real-provider checks The real-provider check scripts call analyze_function over MCP and then consume the complete dossier: requireFunctionDossier, functionDossierSchema, .procedure/.pseudocode reads, operation assertions, compare_functions inputs and the oversized-dossier rejection path. The summary default broke them. Every such call site now passes detail: "complete". Provider-direct callers are unchanged because detail is an MCP contract field; the admission-error sites stay unchanged because provider failures return before the summary branch. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AoJZDPcrgmMsCqLG4LfiFb * fix(ghidra): keep unavailable value-flow counts unknown in the summary summarizeDossier coerced the five value_flow_* counts to zero when native_value_flow was null (IDA, Kuna) or declared unavailable (Ghidra), so the summary reported empty collections while value_flow.available said otherwise. The counts are now null unless value flow is available, with the reason already named in unknowns. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AoJZDPcrgmMsCqLG4LfiFb * fix(evidence): budget authentication headroom by file shape and serialized size The evidence-file headroom reserved two parsed copies, calibrated for one record, and charged strings at two bytes per character. Bundle authentication additionally holds the schema-parsed bundle, the re-parsed records and two JSON.stringify results, which expand control characters and lone surrogates sixfold: a bundle whose raw_result is a million NUL characters estimated at 2 MB parsed but needed 12 MB of serialized text. The budget now has a parsed term and a serialized term per file shape, owned by EvidenceAuthenticationHeadroom and shared by the analysis-view reader and the bundle importer, which had no guard. Two transient peaks the budget did not name are removed at their producers: the canonical digest hashes a large leaf directly instead of concatenating it into its buffer, and the JSON validator walks containers by cursor so a wide array no longer queues one entry per member. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AoJZDPcrgmMsCqLG4LfiFb * fix(mcp): route analyze_function summary replies to dossier consumers in docs and prompts The MCP guide, the version-comparison and crash-triage prompt contracts, the authored skill and the Kuna, IDA and function-matching docs still told agents to pass analyze_function replies straight to compare_functions or build_call_path. Under the summary default those consumers reject the reply. They now name the retained-evidence reference for compare_functions, the observations fields of match_native_functions, and detail: "complete" for build_call_path, which accepts only inline dossiers. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AoJZDPcrgmMsCqLG4LfiFb * test(evidence): pin validator member order and keep interrupted validation yielding Independent review of the cursor validator found the interrupted-validation fixture relied on the old last-first walk: its over-deep head was reached before the first cooperative yield. The padding now precedes the deep element so the walk yields first, which is the invariant the test guards. A new case pins first-defect-in-member-order reporting, skipped non-enumerable keys no longer count toward the yield cadence, and the import-headroom test loads the reader through a fresh module graph so it holds under the services project's shared workers. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AoJZDPcrgmMsCqLG4LfiFb * fix(ghidra): align deadline bounds and cancellation outcomes * refactor(ghidra): shorten deadline recovery guidance * fix(mcp): clarify the scope of inferred decompile deadlines * test(ghidra): verify deadline workflows and document measured costs Separate failure and registration cases by their distinct contracts. Record actual Linux Ghidra, CLI and MCP evidence with measured context, elapsed time, CPU and memory, and explicit remaining coverage limits. * docs(ghidra): pin integration and distinguish baseline format failures * fix(ghidra): own failure lifecycle wording in the provider * test(ghidra): budget timeout fixture generation before allocation * fix(analysis): preserve summary provenance and bound bundle guidance * fix(ghidra): align capability and traversal deadline guidance * style(config): align deadline guidance formatting * docs(ghidra): record capability and traversal verification * docs(analysis): use absolute paths for Evidence files * docs(analysis): record completed function summary measurements * docs(analysis): update main integration validation --------- Co-authored-by: Syed Rishad <rishadzzzz@gmail.com> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> Co-authored-by: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com> |
||
|
|
a062529bb2 |
feat(ghidra): bound one procedure's decompile without closing the session (#5662)
* feat(ghidra): bound one procedure's decompile without closing the session Fixes #5340 * fix(ghidra): report decompile limit as a request timeout Keeps BinarySession from marking every Ghidra operation unavailable after one procedure's decompile reaches the limit, and names the MCP tool-call deadline in the remediation. * fix(ghidra): budget the decompile limit and report cancellation-closed sessions Adds REA_GHIDRA_DECOMPILE_TIMEOUT_SECONDS to the derived Codex/Kimi tool deadline and forwards it through setup. Marks a remote failure that settles after caller cancellation so the timeout projection no longer promises session reuse. * fix(ghidra): align deadline bounds and cancellation outcomes * refactor(ghidra): shorten deadline recovery guidance * fix(mcp): clarify the scope of inferred decompile deadlines * test(ghidra): verify deadline workflows and document measured costs Separate failure and registration cases by their distinct contracts. Record actual Linux Ghidra, CLI and MCP evidence with measured context, elapsed time, CPU and memory, and explicit remaining coverage limits. * docs(ghidra): pin integration and distinguish baseline format failures * fix(ghidra): own failure lifecycle wording in the provider * test(ghidra): budget timeout fixture generation before allocation * fix(ghidra): align capability and traversal deadline guidance * style(config): align deadline guidance formatting * docs(ghidra): record capability and traversal verification --------- Co-authored-by: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com> |
||
|
|
a91412aaf9 |
feat(native): export function pseudocode to resumable directories (#6170)
* feat(decompile): write function pseudocode to a directory and return a manifest Add decompile_all and `rea decompile-all` so a caller can write one provider pseudocode file per function, or a filtered set, and receive only a small manifest. The result stays bounded when the program is larger than a local model's tool-result cap. Resume continues an export for the same target digest, provider, analysis profile, and layout. Matching files are skipped, conflicting files stay unchanged, and one function's failure does not stop the rest. Cancellation leaves files already written. Requesting dwarf layout reports that compilation-unit paths are unavailable. Simulated-provider tests cover the manifest, resume, filters, limits, cancellation, and the failure cap. MCP registration, CLI coverage, schema validity, the product catalog, check:fast, and docs:check passed. Real Ghidra was not exercised. Refs #6090 * fix(decompile): keep unknown export identity unresumable and recover torn journals Resume treated two missing target or profile digests as a match, so an export of one binary could continue into another. Resume now requires both digests, and a decompiler observation without the export's profile, or with a different target, is recorded as profile_changed. A crash during a journal append left a torn final line that made the export unresumable. Text after the last newline is now treated as uncommitted and truncated before the next append, and stale temporary files are removed. An inventory row with an empty address aborted the whole export because its failure record failed the journal schema. Such rows are now excluded and counted in the limitations. A limited resume retried the same recorded failures first and never reached new functions. Functions not yet attempted now run before retries. File path reservations come from every journal line, so an output_io record no longer releases a path. The index is rebuilt from the journal in numeric address order, and journal close and cleanup failures return a Result. The cancellation test now cancels after a file is committed and checks that file survives and is skipped on resume. Redundant snapshot-replay checks are removed, the CLI limit uses the contract constant, and --snapshot no longer claims decompile-all updates it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AoJZDPcrgmMsCqLG4LfiFb * fix(decompile): refuse resume of a missing export and repair journals by byte offset A second review round found that resume against a directory that does not exist silently started a full new export and reported it as resumed. Resume now requires an existing export directory. The torn-line repair measured the committed journal from decoded text, so damaged UTF-8 could move the truncation point past the end of the file. The journal is now read as bytes and cut at the last newline byte, and an intact journal is not truncated, so a concurrent append is kept. A per-function observation for a different target is now recorded as target_changed instead of profile_changed. A failure to remove a partly created output directory is reported instead of hidden. The journal handle is closed on every path again, including an unexpected throw. The path allocator no longer gives up after 1,000 collisions and reuses a taken name. Tests now use createTestTempDirectory, assert journal lines against the journal schema, and cover resume of a missing directory, resume with an unknown analysis profile, and a target-mismatched observation. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AoJZDPcrgmMsCqLG4LfiFb * fix(decompile): keep a function's first file name across failed retries Reserving every journal path stopped other addresses from taking a path, but an address looked up only its latest record. After its file went missing and a retry failed without a path, the next resume moved the function to a hash-suffixed name and orphaned the original. Each address now keeps the first path the journal gave it. A matched commit still ran the decompiler but did not count toward limit. The limit now counts every function taken on after skipping verified files. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AoJZDPcrgmMsCqLG4LfiFb * fix(decompile): never follow a journal symlink and check procedure_info identity Torn-line repair truncates the journal, and the journal was opened through any symlink in the export directory, so repair could truncate a file outside it. The journal must now be a regular file and is opened without following symlinks where the platform supports it. Body ranges from procedure_info were written without checking that the observation belonged to this export or described the requested function. An observation with a different provider, profile or target, or another entry point, now leaves the body unavailable with that reason. File, journal and index writes now use FileHandle.writeFile, which writes the whole buffer instead of trusting one write call. The stale temporary sweep is removed: a matching file name does not prove this export created the file. The inverted-range test now uses one address space so it reaches the inversion check. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AoJZDPcrgmMsCqLG4LfiFb * fix(decompile): bound returned caveats and keep verified checksums Ghidra names each terminal call's address in its per-function caveats, so decompile_all copied one new caveat per function into the returned Evidence and the answer grew with the program. The returned Evidence now keeps the eight most often observed distinct per-function caveats, grouped by provider and text as they are rendered, plus a count of the rest. Inventory caveats are unchanged, and other workflows are not bounded. Each function keeps the caveats of the observations it used: a written file in its header, now including procedure_info caveats, and a failed attempt in its journal and index record. A procedure_info observation for another target or profile is foreign and contributes none. A failure record for a function that was already written replaced the record holding its checksum, so after a transient read error a resume decompiled that function again. The journal now keeps the last successful write separately from the latest attempt. Resume verifies against that write and records it again once the file verifies. A caller abort that arrives while the export finishes now returns cancellation with the caller's reason instead of a success manifest, as #6143 does for other late finalization. A publish, index or close failure stays the primary error. Also make the missing-journal resume error say what to do, and add boundary tests: one runs decompile-all through the real CLI against a fake Kuna to check flag mapping, the relative output directory, filters and limit; one runs decompile_all through production MCP registration to check that the caveat bound is applied. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AoJZDPcrgmMsCqLG4LfiFb * fix(decompile): keep each function's complete caveats in the index The returned Evidence keeps eight distinct per-function caveats and counts the rest, pointing readers at the export. A written function's file header truncates its caveats after 4,000 characters, and its journal record kept none, so a large function with many terminal calls could lose caveats from both the answer and the export. Every journal record, written or failed, now keeps the complete caveats of the observations its attempt used, and decompile-index.json carries them. The header stays a readable summary and, when truncated, says that the index keeps the complete list. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AoJZDPcrgmMsCqLG4LfiFb * fix(decompile): report external functions instead of failing them Ghidra classifies imports as external functions with an empty body and returns null pseudocode for them, as its limitations state. decompile_all recorded each one as an invalid_pseudocode failure ("not text"), so a dynamically linked program reported every import as a decompiler failure: 34 of 116 functions in /usr/bin/xxd. procedure_info owns the external classification. When it classifies a function as external, the export now records status "external", counts it in counts.external, writes no file and requests no pseudocode. The record keeps the body fact as observed. The journal header pins the target, provider and profile, so a resume skips a recorded external function without asking the provider again; a limit below the external count still leaves nothing remaining. A null pseudocode result for a function whose classification is not external is reported as no_pseudocode. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AoJZDPcrgmMsCqLG4LfiFb * fix(decompile): report finalization failures after a cancelled export When the caller's abort stopped the publish loop, decompile_all returned that cancellation before checking the index write and the journal close, which had already run. A failure to finalize the export was hidden, so the caller could not tell the saved index or journal was incomplete. Publish failures stay primary, then index and close failures. A cancellation counts as the caller's only when the caller's signal is aborted, and is then reported after a successful finalization with the caller's own reason. A provider's own cancellation, without a caller abort, stays a publish failure like any other. Tests cover an index failure and a close failure after the caller stops the loop, a stopped loop with a successful finalization keeping the caller's reason, and a provider cancellation staying primary over an index failure. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AoJZDPcrgmMsCqLG4LfiFb * fix(decompile): remove a created output directory that cannot be secured createPrivateDirectory ran mkdir and chmod as one step. When mkdir succeeded and chmod failed, it returned an error before #create reached its cleanup, leaving an empty directory behind. A fresh export then refused the existing directory, and a resume refused it for having no journal, so the caller had to remove it by hand. mkdir and chmod are now separate. A mkdir failure created nothing, so nothing is removed. A chmod failure after this call created the directory removes it with a non-recursive rmdir: it is still empty, so a path another process filled or replaced meanwhile is refused rather than deleted, and that refusal is reported. A filesystem boundary test fails chmod for the new root and checks the directory is gone and a retry succeeds. A module test confirms that a function written earlier and now classified as external still reports a changed file at its path as output_exists. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AoJZDPcrgmMsCqLG4LfiFb * refactor(process): share the socket lease with owners other than Hopper The socket lease that keeps two REA processes from opening the same Hopper document lived only in HopperLease, with Hopper error messages built in. A decompile export needs the same guarantee for its output directory. OwnedLease now holds the lease mechanism unchanged: a listening local socket that the operating system ends with its process, owner identity answered by the live holder, and recovery of a stale socket serialized through a recovery directory. Callers map a tagged LeaseFailure to their own error. HopperLease keeps its exports and maps each failure to the same HopperStartError message as before. On Windows the lease is a named pipe, which needs no stale recovery. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AoJZDPcrgmMsCqLG4LfiFb * fix(decompile): hold the output directory per run and stop on repeated write failures Two runs could resume the same output directory at once. Each read the journal once, and the index each wrote reflected only its own view, so the last writer could drop the other run's records. A run's torn-line repair could also truncate the other run's append. DecompileExport now holds a lease on the output directory, named by its device and inode, from before the journal is read (or, for a new export, from just after the directory is created and before its journal exists) until the journal is closed. A second run is refused as a retryable capacity constraint that reports the holder's process ID. The lease ends with its process, so a crashed run leaves no lock to clear. The lease is released on every open failure, and a failed release is reported. An export also kept decompiling every remaining function when its files had stopped being writable mid-run, for example on a full disk, failing each one with output_io. After five consecutive functions whose files could not be written or read, the export now stops with an output error that carries the counts and the last failures. A verified file or any other outcome ends a run of failures. Committed files and records stay, the index is still written, and a later resume continues. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AoJZDPcrgmMsCqLG4LfiFb * docs(native): record directory export verification Record matched real Ghidra outputs, bounded caveat and cleanup regressions, actual catalog-inclusive context estimates, runtime measurements and coverage limits for the pinned integration. * fix(native): bound export summaries and reserve portable paths Use one case-folded reservation owner for allocation and resumed journals. Budget inline failures and provider caveats by encoded JSON bytes while retaining complete records in the output index. Align the tool contract with the collector order and verify SDK result-window behavior. * docs(native): refresh final export measurements --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com> |
||
|
|
5d2c8c2b78 |
feat(cli)!: export row-oriented results as JSON Lines (#5629)
* feat(cli): add JSON Lines output for row-oriented results (#5487) - Support opt-in JSON Lines format (--format jsonl) for row-oriented results and collection views with clear record boundaries - Preserve stable source identities and stream records deterministically line-by-line - Emit final coverage and limitation metadata as a distinct line with https://rea.dev/schemas/jsonl-coverage-v1.json to prevent confusion with data rows - Distinguish error outputs with https://rea.dev/schemas/jsonl-error-v1.json - Reject non-row-oriented results that lack record boundaries with an unreadable_output error - Document JSON Lines streaming conventions in docs/cli.md - Add boundary tests covering empty results, multiline strings, partial coverage, large fields, and non-row rejection * fix(cli): preserve diagnostics, provenance, and nested collections in JSONL * fix(cli): frame classified failures and reject unrepresented JSONL facets --------- Co-authored-by: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com> |
||
|
+12 |
169512a35b |
feat(cli): report version and runtime with an explicit output format (#6530)
* feat(cli): report version and runtime with an explicit output format
`rea --version --json` and `--version --format <format>` ignored the format
and printed only the bare version, so support reports gathered the runtime
and platform by hand.
A root --version request that selects a format now renders a documented
report: package name and version, JavaScript runtime and version, platform,
architecture, and whether the runtime satisfies the supported range. It
contains no paths, environment values, user or host names, and starts no
provider. Plain --version is unchanged, --help keeps precedence, and other
output controls keep Incur's plain version.
The runtime verdict uses a new shared supportsJavaScriptRuntime rule that
rea doctor now uses too, so both judge Bun by its Node.js compatibility
version. mcp doctor's private JSON/format renderer moves to cliOutput as
renderCliDocument and is shared with the version report.
Closes #5483
* refactor(errors): centralize input failure construction
Carry caller messages through the existing schema issue owner instead of constructing and discarding an intermediate input error. Remove forwarding schema helpers and migrate equivalent constructors across investigation, firmware, dylib, process, and screenshot workflows while preserving diagnostic paths and causes.
* docs(readme): add Roman Urdu edition (#6499)
* docs(readme): add Roman Urdu edition
Register ur-Latn with a pending website catalog and preserve English command examples.
Script-tag handling follows morluto's shared helper proposed in PR #2043 (
|
||
|
|
0d596fa8f7 |
docs(limits): describe how to design admission recovery (#6368)
* fix(reconstruction): dedupe residual unknown ids with uniqueSorted Merged obligation candidates called uniqueBy, which is not defined, so the project did not typecheck. String ids already use uniqueSorted. Co-authored-by: morluto <morluto@users.noreply.github.com> * docs(limits): describe how to design admission recovery Add a short contributor guide for named defaults, settings, recovery text, and the agent recovery ladder, and mirror that ladder into the authored skill. Co-authored-by: morluto <morluto@users.noreply.github.com> * fix(hopper): pass distribution readers into install-host tests systemLinuxHopperInstallHost now requires the caller's distribution reader. The Linux install tests still passed only the environment, so typecheck failed. Co-authored-by: morluto <morluto@users.noreply.github.com> * docs(limits): name the parser doctor shares with startup Co-authored-by: morluto <morluto@users.noreply.github.com> * test: align pcap, storage, and regex fixtures with current contracts Source and portable CI failed on fixtures that still described the previous pcap client identity, legacy storage fingerprints, and the old regex deadline sentence. Co-authored-by: morluto <morluto@users.noreply.github.com> * docs: link repository experiments outside the built site --------- Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: morluto <morluto@users.noreply.github.com> |
||
|
|
e93e3b1e7d | feat(apk): inspect signing declarations with retained fact queries | ||
|
|
7d0efef62d | feat(javascript): retain queryable static route inventories (#6019) | ||
|
|
efab044f30 | feat(gpu): inspect static CUDA and HIP code objects (#5813) |