Commit Graph
2170 Commits
Author SHA1 Message Date
morlutoandCursor Agent 1ff5a655bc fix(native)!: preserve value-flow absence semantics (#8785)
* fix(native)!: preserve value-flow absence semantics

* test(native): compare value-flow reasons with their owners

Stop freezing the unrecorded and Kuna unavailable sentences. Assert the
procedure-scoped unknown and that an explicit provider reason is preserved.

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
2026-11-05 02:32:15 +08:00
morlutoandCursor Agent dd4b83c220 fix(ghidra)!: search adjacent initialized memory blocks (#8840)
* fix(ghidra)!: search adjacent initialized memory blocks

BREAKING CHANGE: Ghidra analysis snapshots from earlier byte-search policies are refused; reopen the target and create a new snapshot. Historical Evidence remains importable.

* test(ghidra): supervise cross-block fixture process

* docs(ghidra): clarify contiguous search range

* test(ghidra): verify search metadata and cleanup

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
2026-11-05 02:31:32 +08:00
morluto d896ab7bd7 fix(mcp)!: distinguish unavailable batch pseudocode (#8737)
* fix(mcp)!: distinguish unavailable batch pseudocode

* test(mcp): preserve historical batch error details

* test(mcp): retain unavailable items in batch recovery

* test: update Ghidra batch unavailable expectation
2026-11-05 01:51:38 +08:00
morluto 6df2ac1d7e fix(evidence): report partial workflow question publication (#8848)
* fix(evidence): report partial workflow question publication

* fix: project nested recovery for caller lifetime
2026-11-05 01:49:36 +08:00
morluto b97023809f feat(native): retain decompile export source facts (#8805)
* feat(native): retain decompile export source facts

* test(native): verify historical export source facts

* test(native): retain source after export conflict
2026-11-05 01:48:53 +08:00
morluto 5b82a253dc fix(evidence): link snapshot replays to original observations (#8734)
* fix(evidence): link snapshot replays to original observations

* style(tests): format snapshot replay assertions

* test(evidence): cover replay parent metadata refresh

* test(evidence): surface snapshot replay failures

* test(mcp): check replay source after cleanup recovery
2026-11-05 01:47:24 +08:00
morluto 9196c30f6e fix(ghidra): guide packed program import recovery (#8820)
* fix(ghidra): guide packed program import recovery

* test(ghidra): verify packed import recovery facts
2026-11-05 01:45:22 +08:00
morluto 9a79ee8e9e fix(kuna): clarify single-function fallback evidence (#8742)
* fix(kuna): describe fallback evidence without guessing project state

* fix(kuna): qualify export capacity guidance by source
2026-11-05 01:43:00 +08:00
5990ff0324 feat(artifacts): read WeChat mini-program wxapkg containers (#8148)
* feat(artifacts): read WeChat mini-program wxapkg containers

Add a read-only reader for plaintext wxapkg packages: the 0xBE/0xED
18-byte big-endian header, the bounded name/offset/size member table,
and verbatim member bytes at absolute carrier offsets.

- Members retain declared names, absolute offsets and sizes, so
  source_location reports real carrier positions.
- V1MMWX PC-client packages are detected as encrypted coverage with
  null member facts; extraction refuses with a typed error and no
  decryption is attempted.
- Bounded table and directory budgets are admitted before allocation;
  malformed marks, truncated tables, invalid UTF-8 names, member
  extents inside the header or beyond the file, and counts that cannot
  fit their table are typed format or limit failures.
- Member reads stream in bounded 64 KiB chunks with carrier freshness
  checks; the reader joins outstanding streams before close and bounds
  active streams and repeated member I/O.

Refs #8121.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* fix(artifacts): bound wxapkg members to the declared data region

Normalize the two indexLength conventions before the table bounds check
and the table read. Member ranges must then lie in that declared data
region, which has to fit in the file.

Record wxapkg format metadata as not compared: header facts repeat the
package bytes, and entry source paths keep a producer root prefix.

* fix(artifacts): choose the wxapkg index after member ranges fit

Four trailing bytes made the rows-only layout end on the file and dropped
the count-inclusive layout. Keep every admitted layout until the parsed
rows and member ranges select one.

---------

Co-authored-by: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
2026-11-05 00:52:20 +08:00
morlutoandCursor Agent 52bca6de41 fix(native): retain value trace source evidence (#8384)
* fix(native): retain value trace source evidence

* fix(native): build trace error evidence only on failure

* test(mcp): expect unowned value trace refusal

* test(mcp): avoid freezing trace error wording

* test(native): keep value-trace stop coverage with source evidence

Main's stop and continuation cases now meet the trace's evidence-owner
precondition: direct calls supply a source id, and a completed MCP trace
runs in a session that can retain it.

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
2026-11-05 00:19:50 +08:00
김준호 30a11af923 fix(browser)!: keep the Chromium sandbox for REA-launched browsers (#8738)
* fix(browser)!: keep the Chromium sandbox for REA-launched browsers

Playwright appends --no-sandbox unless a launch passes
chromiumSandbox: true. Neither the browser scenario launch nor the native
module resolver passed it, so every REA-launched Chromium ran without its
sandbox and REA_BROWSER_NO_SANDBOX had no effect.

Both launch sites now take the sandbox decision from ChromiumSandbox.ts:
off on Android or when REA_BROWSER_NO_SANDBOX=true, on otherwise. When
Chromium reports that it cannot start its sandbox, the launch failure's
remediation names the opt-out.

Fixes #8722

BREAKING CHANGE: hosts where Chromium cannot create its sandbox, such as
root users and Linux containers without user namespaces or a setuid
sandbox, now fail to launch until REA_BROWSER_NO_SANDBOX=true is set.

* fix(browser): name the sandbox opt-out on any sandboxed launch failure

Chromium reports an unusable sandbox through several unrelated startup
messages, including a misconfigured setuid helper and a refused namespace
move, and Playwright recognizes only some of them. Matching individual
messages missed real failures, so a sandboxed launch failure now always
names REA_BROWSER_NO_SANDBOX=true as a conditional remediation and the
retained startup diagnostics show whether the sandbox was the cause.

* fix(browser): keep default recovery for resolver failures after launch

The module resolver's outer catch also covers context, navigation and
evaluation failures after the browser launched. Attaching the sandbox
remediation there replaced their default recovery guidance. Attach the
shared launch remediation only when a sandboxed launch itself fails.

* docs(browser): note the Android sandbox exception for module traces
2026-11-04 23:54:29 +08:00
星闌 b1b6cb64c2 fix(analysis-view)!: distinguish unavailable native collections (#8291) 2026-11-04 23:53:45 +08:00
Entrpi c13dd36d95 fix(artifacts): decide how compare_artifacts compares every format_metadata kind (#8231)
* feat(artifacts): inventory and extract ESP32 firmware images

ESP32-family app and bootloader images (magic 0xE9) had no reader, so
open_binary refused OTA downloads, app partitions and bootloaders.

Add the esp-image container format behind inspect_artifact and
extract_artifact. A pure decoder reads each byte once and keeps only
fixed-size fields. Derived facts cover the chip, from a table cited to
ESP-IDF v6.1; segment classification as ESP-IDF's should_map and
should_load; checksum and appended SHA-256 results; app and bootloader
descriptors; Secure Boot v2 signature blocks, observed and unverified;
and undescribed trailing byte ranges. Truncation and undescribed bytes
make coverage unknown. ESP8266 images are refused with a specific
reason, and ciphertext stays unrecognized.

Add the format to every target, Evidence, snapshot, live-summary and
artifact vocabulary, document it, and add an optional verify:esp-images
lane that compares esptool-built images with esptool image-info and
espsecure signature-info-v2.

Refs #3615

* test(artifacts): run the ESP image lane on esptool v5.6.0

The lane compared descriptor fields only when esptool image-info printed
one, so a descriptor that REA reports and esptool does not print passed
without comparison. Compare the descriptor kind in both directions.

Record that the lane passed for all 13 images with esptool and espsecure
v5.6.0, the version the layout facts cite, as well as with 4.12.0.

* refactor(artifacts): define ESP image layout sizes once

The decoder and the derived facts each defined the segment-header,
appended-digest and flash-sector sizes, the erased-byte scan and the
offset formatter. Define them once with the other fixed-layout structures
and import them in both places. The signature slot count is the one
constant that drives the slot scan.

Correct the observedEnd comment: it is the end of the last chunk read.

* fix(artifacts): report ESP-IDF's segment length limit by chip

ESP-IDF's verify_segment_header rejects a segment of 16 MiB or more
unless the chip maps flash through 32-bit cache addresses
(SOC_SPI_MEM_SUPPORT_CACHE_32BIT_ADDR_MAP: ESP32-S3, P4, C5, C61, H4 and
S31 in v6.1). Record that capability in the chip table and add a
limitation for such segments; unknown chips get none.

`loaded` repeats should_load. It does not mean the bootloader accepts the
segment: verify_load_addresses still rejects one outside the chip's RAM,
which REA does not model. Say so in the docs, the skill reference and the
inventory limitation.

* test(artifacts): check ESP lane tools first and verify extraction

The lane started work before checking its tools, so a missing RISC-V
target, ld.lld, esptool or espsecure surfaced as a raw spawn or command
error. Check each one before building and name the variable to set. Warn
when esptool is not v5.6.0, the release the layout facts cite.

Assert each segment's class for the ESP32-C3 app, its variants and the
bootloader, as the Xtensa image already did. Extract the app and the
signed image through the CLI and compare every file with the bytes at
esptool's segment offsets or in the appended signature sector; the
source's SHA-256 must be unchanged and equal the reported root_sha256.

* docs(artifacts): state what the ESP image lane does not establish

The Verification section listed the lane's checks but not its gaps: only
ESP32-C3 and ESP32 images come from a real producer, no real project
build, OTA download or flash dump is inspected, no signature is verified,
and the lane does not run in CI.

Record the timing of the final lane run on esptool v5.6.0.

* fix(artifacts): compare ESP segment declarations

Segment entries hash only their data, so compare_artifacts reported a
segment that moved to another load address with the same bytes as
unchanged; only the image itself changed. Compare each segment's
load_addr and data_len as its declaration, as Node SEA and PyInstaller
members are compared, and count a changed declaration as a metadata
change, also when content is unavailable.

Header offsets are layout and the classification is derived, so neither
is compared.

* test(artifacts): run ESP lane commands as owned process trees

The lane ran its tools through execFile. A timeout killed only the direct
child, so a linker or assembler that Clang or GCC had started could keep
running while the lane removed its workspace. Run every command through
createVerifierCommands, check the tools inside withVerifierCleanup, and
remove the workspace only after commands.close() has stopped every tree;
a failed close keeps the workspace.

Only a nonzero exit now counts as a refusal or an expected esptool
failure; timeouts and spawn failures propagate. Supervising each command
roughly doubles the lane's run time.

* test(artifacts): cancel ESP decoding after its first decoded chunk

The cancellation test requested cancel or close on setImmediate, which
can land while the file is still being opened and checked, so it never
showed that decoding had started. It also passed against a reader that
ignored both requests while decoding. Acknowledge the decoder's first
chunk, then cancel or close, join the operation, and assert that no
later chunk reaches the decoder and that the reader ends closed.

* docs(artifacts): record which ESP metadata comparison ignores

Segments are compared by their declaration. The image's and the
signature blocks' format_metadata are not: every field comes from their
own bytes, which content comparison already covers, and a block's
image_digest_matches follows the image's content.

* fix(artifacts): decide declaration comparison for every metadata kind

compare_artifacts listed the format_metadata kinds it compared by hand,
so a new kind went uncompared without notice; ESP segment load addresses
were first missed this way. One table beside the schemas now gives every
kind a declaration projection or the reason comparison uses none of its
facts, and a kind without an entry does not compile. Consumers read one
result per pair instead of naming formats.

- Godot PCK entries stay uncompared. Godot looks members up by the
  simplified path without res://, which logical_path holds apart from
  Unicode normalization. Godot 4.3 exports store res:// and 4.4.1
  exports do not, so comparing source_path would mark every member
  changed. The reader admits only the encryption flag, which the
  occurrence's encrypted field records.
- A changed PyInstaller declaration is now a metadata change also when
  the member's content is unavailable, as for ASAR, Node SEA and ESP
  segments. It was unknown before, for no recorded reason.
- Root kinds and ESP signature blocks stay uncompared: their fields come
  from their own bytes, which content comparison covers.
- A declaration that only one side records now adds one generic
  limitation for every compared kind. Only Node SEA added one before,
  with its own text.

* fix(artifacts): keep content comparison for metadata records with content

The record rule compared two metadata occurrences by their declaration
alone. A hand-built record that names content, with an equal declaration,
then hid a content change; PyInstaller records already did so before the
table. The rule now needs both records without an artifact_id, which is
every metadata record a reader emits.

Review changes in the same area:

- The unknown-declaration limitation also covers declarations of different
  kinds and non-container observations, and tests compare it with its
  constant.
- docs/godot-pck.md gives each Godot version's lookup path, the backslash
  case, and that a member that becomes encrypted compares unknown. The
  table points to it instead of restating it.
- The PyInstaller regression moves to an acceptance test whose MCP client
  validates the advertised output schema, and the domain tests keep one
  representative case per behavior.

* fix(artifacts): compare encryption changes when content is unavailable

An encrypted member has no content. Comparison then took the
content-unavailable fallback, which counted only ASAR storage and format
declarations, so a Godot PCK, ZIP or PYZ member that became encrypted
compared unknown although both sides record `encrypted`. The record rule
compared node-less metadata records by format declaration alone, so a PYZ
namespace record whose encryption flipped compared unchanged.

`OccurrenceMetadata` now owns the encryption comparison beside ASAR
storage and the format declaration. Every occurrence records `encrypted`,
so a pair's comparison is never unknown. The fallback, the record rule and
the metadata dimension read it there; `metadataChanged` no longer compares
`encrypted` itself. The record rule compares all of `OccurrenceMetadata`:
a changed value is changed in metadata, with availability when another is
unknown.

Decision: occurrence limitations still do not count for node-less
records. Readers derive them from the declared facts (the PYZ caveat
follows `encrypted`), and their wording can change between REA versions
while the record stays the same. The fallback does not compare them
either.

Behavior changes:
- A pair with unavailable content whose `encrypted` differs is changed
  [metadata, availability], not unknown [availability].
- A node-less record whose `encrypted` differs is changed [metadata], not
  unchanged.
- A hand-built node-less record that records ASAR storage now compares
  it; no reader emits such records.

Remaining:
- The fallback still ignores `entry_kind` and `executable`, which readers
  also declare, so a file that becomes a symlink, or a mode change on an
  encrypted ZIP member, still compares unknown. `executable` has its own
  dimension, so its fix needs content-free checks split from node checks.
  Each is its own behavior decision.
- Node-bearing pairs still compare limitation wording; records do not.
  Whether limitation wording is ever a comparison fact needs one decision.

* fix(artifacts): compare entry kind and executable when content is unavailable

When either side lacked verified content, comparison took a fallback that
counted only ASAR storage, encryption and format declarations. It compared
`entry_kind` only in the metadata check of pairs with content, and
`executable` only beside the node checks. A ZIP member that became a
symlink, whose target REA does not read, and an encrypted ZIP member whose
Unix mode gained an execute bit therefore compared unknown, although
readers declare both fields on every occurrence.

`OccurrenceMetadata` now owns the entry kind comparison beside encryption;
it is never unknown. Comparison checks fall into two groups: declared facts
that need no content (`executable` and the metadata declarations), and
facts that need both content nodes (content, kind, format, size, relations
and node metadata). The fallback, the record rule and pairs with content
share the declared checks and one rule for unknown declarations. Kind and
format stay content checks: without content they are guesses from the path.

Behavior changes:
- Unless both sides have content, a pair whose entry kind differs is
  changed [metadata, availability], not unknown [availability].
- Such a pair whose `executable` differs is changed [executable,
  availability], with metadata when another declared fact also differs.
- A hand-built node-less record whose `executable` differs is changed
  [executable]. No reader emits such records.

Remaining: declared sizes and integrity digests still compare only through
observed bytes, so they do not count when content is unavailable.

* fix(artifacts): never mark a symlink executable

On POSIX hosts the directory reader recorded a symlink's own permission
bits as `executable`. Linux reports 0777 for every symlink and macOS
usually 0755, so directory, app bundle and DMG symlinks read true. zip.js,
the PyInstaller reader and the Windows branch record false. The previous
commit compares `executable` without content, so a symlink that two
readers or two hosts record differently compared changed in `executable`,
and a non-executable file that became a symlink also reported `executable`.

`hostExecutableMarking` now gives a symlink no marking on any host:
running a symlink runs its target, and the link's own bits do not control
that.

Behavior change: directory, app bundle and DMG inventories record
`executable: false` for symlinks. Occurrence IDs do not change, but the
graph digests and manifest IDs of inventories with symlinks do. Comparing
an inventory stored before this change with a new one therefore reports
changed [executable, availability] once for each such symlink.

* fix(artifacts): stop comparing limitation wording

Pairs with content compared node and occurrence limitations as metadata,
while node-less records, the content-unavailable fallback and inventory
limitations did not. Limitations explain what an observation could
establish. Readers mostly derive them from fields that comparison already
reads, from other occurrences or from what REA could read, and their
wording changes between REA versions. So adding `A` beside an unchanged
ZIP member `a` marked `a` changed in metadata through its case-collision
note, and an older and a newer inventory of equal bytes could differ
wherever a reader reworded a note.

No comparison path reads limitations now. Inventory limitations still
appear in the result, and occurrence limitations stay in the inventory
Evidence. Readers write no node limitations, so only hand-built inventories
compared them.

Behavior change: for a pair with content, limitation text no longer adds
metadata. A pair that differs only there compares unchanged, or unknown
[availability] when a declaration is unknown.

Remaining: a few facts appear only in limitation text, or also in fields
that comparison does not read, so their changes are no longer reported
while the compared bytes stay equal: an ASAR block integrity table or an
unsupported integrity declaration, a Godot member's all-zero MD5, a ZIP
name's leading `./` (also in `source_path`), and a PyInstaller PYZ image
digest (also in `pyz_sha256`; the archive root still reports the change).
Each needs a field or a comparison decision of its own if it matters.

* fix(artifacts): compare declared digests when content is unavailable

Without observed bytes on both sides, comparison ignored the containers'
declared integrity claims. Two encrypted Godot PCK members therefore
compared unknown even when their declared MD5s showed that the script had
changed. Godot's exporter stores the MD5 of a member's original bytes, so
the MD5 does not depend on the encryption IV. This comes from the 4.3,
4.4.1 and 4.7.1 exporter sources; no real encrypted export was compared.

When either side has no content, claims of one algorithm that differ now
make the path changed in metadata and availability. Equal claims add no
change, because they are unverified, and observed bytes still decide when
both sides have them. Godot PCK and ASAR readers record such claims.

For a readable member, a directory that declares or omits an MD5 only
decides whether REA can check its bytes, so such a member compares
unchanged. An encrypted member with an MD5 on only one side stays unknown.

Remaining: declared sizes still compare only through observed bytes.

* docs(artifacts): check the contract's compared kinds against the table

docs/mcp-contracts.md named the format_metadata kinds that
compare_artifacts compares in prose, by hand, so a kind added to the
declaration table could go unnamed. The integration branch already omits
esp-flash-partition, which the ESP flash layout branch adds to the table.

The paragraph now names each compared kind by its identifier. A
conformance test requires it to name exactly the kinds whose table entry
has a declaration, which artifactFormatMetadata.ts exports as
COMPARED_FORMAT_METADATA_KINDS, and no kind that comparison ignores.

* test(artifacts): share the comparison acceptance MCP fixture

Six artifact comparison acceptance tests each copied the same MCP
steps: open_binary, inspect_artifact, the inventory_artifact substep
Evidence, then compare_artifacts. The coverage test repeated the
inspect steps three times and kept its own inventory lookup.

tests/fixtures/artifactComparisonMcp.ts now owns these steps on the
schema-validating connectLocalToolsMcp client.
connectArtifactComparisonMcp() returns call, inspect(path) and
compare(left, right). inventoryEvidence() reads the inventory Evidence
that an inspection retains, from MCP or CLI output. Each test still
writes its own authored input and keeps its expectations.

The coverage lookup matched evidence.operation, not the substep
operation. The inspection schema fixes both to inventory_artifact, so
both select the same substep.

The plist acceptance test does not change. It compares the inspection
Evidence itself across CLI and MCP and passes the comparison Evidence
to verify_reconstruction and find_changed_behavior.

No assertion or scenario changes.

* fix(artifacts): leave .NET bundle member declarations uncompared

Upstream main compares no .NET bundle member facts. Comparing `file_type`
changed `compare_artifacts` output beyond this branch's fixes, so the kind
stays uncompared until that is decided. A changed manifest still shows as a
content change of the host.

* fix(artifacts): leave HEX region declarations uncompared

The comparison table compared a region's start_address and length. A
region's path names its start and end addresses, and its length follows
from them, so two regions that pair at one path always declare the same
facts: the entry could never find a change. It only made a region with
equal bytes unknown when the other side recorded no metadata, such as a
directory of extracted regions. The kind is not compared, as on main.

The contract paragraph also stops naming .NET bundle members, which
3e51a7760 left uncompared.

* test(artifacts): compare node-less records of any compared kind

The record rule follows the comparison table, not PyInstaller, but only
PyInstaller records exercised it. An ESP segment record shows both
outcomes: an equal declaration compares unchanged and a moved one changed
in metadata, without the availability that the fallback for unread
content would add.

* test(artifacts): compare a node-less record's recorded ASAR storage

The record rule compares ASAR storage beside the declaration and
`encrypted`, but no test recorded it on a metadata record. A changed
value is changed in metadata, and a value that only one side records
is unknown in availability, as for any occurrence.

* test(artifacts): compare UnityFS member declarations through MCP

The table compares a UnityFS member's declared source_path and raw
flags, but no comparison test covered the kind, so dropping either field
from the projection left every test passing. Bundles with equal member
bytes now show both: other flags, or a stored path in decomposed form,
compare changed in metadata, and reordered members, whose index and
stream offset are layout, compare unchanged.

* test(artifacts): use the shared comparison fixture for UnityFS

The UnityFS comparison test arrived from the bottom branch with its own
copy of the open_binary, inspect_artifact and compare_artifacts steps,
which tests/fixtures/artifactComparisonMcp.ts owns here. It now uses
connectArtifactComparisonMcp() like the other comparison acceptance
tests. No assertion changes.

* test(artifacts): find the contract's compared kinds by marker

The docs conformance test found its paragraph by an exact English phrase,
so rewording "also compares" failed it while the named kinds stayed
right. The paragraph now sits between BEGIN and END markers, as the
allocation report's sections do, and the test reads the kinds between
them. It still requires exactly the kinds whose table entry has a
declaration.
2026-11-04 23:53:01 +08:00
morluto 2d16a40a49 fix(ghidra): report selected project identity failures (#8379)
* fix(ghidra): report selected project identity failures

* fix(ghidra): retain refusal until client shutdown
2026-11-04 19:58:50 +08:00
Ha1baraA1 bd7002b441 fix(cli): export source Evidence for native value traces (#8297)
* fix(cli): export source Evidence for native value traces

* fix(cli): export source Evidence for native value traces

* fix(cli): preserve trace completeness in summaries
2026-11-04 19:56:50 +08:00
064bb3f713 fix(firmware): reject staged files cut off at the per-file limit (#4430)
* fix(firmware): keep producer reports writable under small staging budgets

extract_firmware passed max_output_bytes straight to prlimit --fsize. RLIMIT_FSIZE
applies to every regular file the producer writes, including the mandatory
report.json and unblob.log outside the staging tree, so a staging budget smaller
than the report truncated it and failed the extraction even when every staged
file fit. Derive the per-file ceiling as the larger of the staging budget and the
8 MiB report budget; polling and the final inventory still enforce the aggregate
staging budget.

Fixes #3888

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

* fix(firmware): keep files stopped at the size ceiling over budget

The per-file RLIMIT_FSIZE ceiling equalled the largest size its readers
accept, and both the staging inventory and the report reader accept a
file of exactly their budget. A write the kernel stopped at the ceiling
therefore left a truncated file that passed as complete. Raise the
ceiling by one byte so such a file exceeds its budget and is rejected.

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

* fix(firmware): report the effective file-size ceiling on limit failures

The exit path re-derived the staging budget for SIGXFSZ and process-limit
recovery, so a small-budget run killed at the 8 MiB + 1 ceiling reported
file_size_bytes 64 and said the cap was max_output_bytes. Pass the
ceiling runFirmwareCommand installs and describe how it is derived.

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

* fix(firmware): reject staged files cut off at the per-file limit

RLIMIT_FSIZE stops a write at the limit without extending the file, and
the staging inventory admits a file of exactly max_output_bytes. With a
budget of 8 MiB or more the producer limit equalled the budget, so a
staged file the kernel cut short published as a complete extraction;
an inherited ceiling below the budget did the same.

Derive the per-run limit once in firmwareFileSizeLimit as one byte above
the largest budget, still capped by the inherited ceiling, so a file
stopped at the limit exceeds its budget. When the inherited ceiling
removes that byte, both staging inventories refuse a regular file that
reaches the installed limit with a nonretryable file-size constraint
naming the inherited limit.

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

* fix(firmware): derive limit adjustability from the per-run limit

The one byte of headroom makes the per-run file limit equal an
inherited ceiling of exactly max_output_bytes + 1, but adjustability
still compared that ceiling with the raw budget. A SIGXFSZ then advised
raising max_output_bytes, which the same ceiling clamps back to the
same limit.

Decide adjustability in firmwareFileSizeAdjustable from
firmwareFileSizeLimit itself: recommend a larger budget only when it
raises that limit.

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

* test(firmware): compare file-size recovery with its owners

The SIGXFSZ adjustability cases and the inherited-ceiling workflow matched
fragments of remediation prose, so a harmless rewrite would fail them.
Compare the emitted guidance with firmwareSignalLimitError for the expected
branch, check that the two branches stay distinct, and compare the inventory
refusal with firmwareFileSizeLimitReached. The selected limit, resource and
retryability stay asserted as structured facts.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
2026-11-04 19:56:05 +08:00
ShuaixinHuang 33e0bf101a fix(adb): admit completed path probes after daemon startup (#8858)
* fix(adb): admit completed path probes after daemon startup

* test(adb): preserve startup probe and digest evidence
2026-11-04 19:55:13 +08:00
JANG BYUNGKUNandgensokyo-dev abc647bf6c feat(desktop): suggest managed members for Unity script assemblies (#5511)
* feat(desktop): suggest managed members for Unity predefined script assemblies

Unity Mono triage listed only inspect_managed_artifact probes, mostly for
engine and framework assemblies. Assembly-CSharp.dll and
Assembly-CSharp-firstpass.dll directly in a managed directory with a
UnityEngine reference witness now get an inspect_managed_members probe,
ordered before every managed artifact probe.

* docs(desktop): describe Unity Mono managed member probes

* refactor(desktop): read Unity managed directory from the witness fact

* refactor(desktop): reuse the managed directory index for predefined assemblies

* feat(desktop): point Unity member probes at summary delivery first

* test(desktop): no member probes without a Unity reference witness

* docs(desktop): trim Unity member probe note

* feat(desktop): suggest managed members in Unity IL2CPP backup folders

Unity IL2CPP builds write <Product>_BackUpThisFolder_ButDontShipItWithYourGame
next to the player. When shipped, its Managed/ holds pre-IL2CPP assemblies.
A path convention now records the folder on Assembly-CSharp*.dll directly in
its Managed/, including when the admitted root is the folder itself, and
triage offers inspect_managed_members for those files. Correspondence to
the shipped native code stays unknown.

* docs(desktop): note IL2CPP backup folder member probes

* docs(desktop): list IL2CPP metadata versions 24 and 27 for the metadata probe

* style(desktop): merge stacked doc comments on desktopPathConventions

* test(desktop): cover member probes in a macOS bundle managed directory

* docs(desktop): note that member probes need the game folder as root

* test(desktop): no member probe when the root is the Unity data folder

* test(desktop): check the data-folder root still gets managed probes

* test(desktop): check the IL2CPP backup convention against the probe bound

---------

Co-authored-by: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
2026-11-04 19:53:00 +08:00
Christopher Pruijsen 398eb0d1f1 feat(setup): register LM Studio during setup (#8611)
* feat(setup): register LM Studio during setup

Register rea in mcp.json in the LM Studio home through a new lm_studio
client. The home follows LM Studio's own lookup, read-only: the path in
~/.lmstudio-home-pointer, else an existing ~/.cache/lm-studio, else
~/.lmstudio. An unreadable or relative pointer reports a path error
instead of a guessed home.

LM Studio parses mcp.json with strict JSON.parse, so comments, trailing
commas and a BOM are rejected before any backup or write. The entry
carries command, args and env with no type or timeout. Local models have
small context windows, so the registration selects
REA_MCP_INPUT_SCHEMA_PROFILE=compact unless a profile is set explicitly,
and doctor reports an entry without a profile as stale. One predicate now
owns that default for Kimi Code and LM Studio.

* fix(doctor): resolve provider budgets for LM Studio as setup does
2026-11-04 19:52:16 +08:00
morluto 103684bce1 fix(ghidra): guide recovery from unresolved procedure selectors (#8470)
* fix(ghidra): guide recovery from missing procedures

* fix(ghidra): preserve mapped offset lookup recovery

* fix(ghidra): distinguish unmapped procedure offsets

* fix(ghidra): guide ambiguous selector recovery

* fix(ghidra): clarify fresh import excludes project edits
2026-11-04 19:51:27 +08:00
hhangandhhang ebea0f009f fix(snapshot): retain one-shot Ghidra pseudocode results (#8089)
* fix(snapshot): retain one-shot Ghidra pseudocode results

* test(snapshot): cover evidence-only pseudocode migration

---------

Co-authored-by: hhang <169027046+xu91102@users.noreply.github.com>
2026-11-04 19:50:39 +08:00
Ace 848f165417 feat(harmony): disclose unclassified inventory files (#4968)
* feat(harmony): disclose unclassified inventory files

* test(harmony): let createEvidence own JSON admission in the partial-inventory case

* test(harmony): extract the projection assertion from the archive journey

* docs(harmony): qualify the unclassified disclosure rules and pin exact-case manifests

* test(harmony): replace limitation prose with structured coverage facts

The partial-inventory control froze the sentence "Source inventory coverage is
partial; absence is unknown." verbatim. docs/testing.md asserts structured
limit facts instead of freezing explanatory sentences and puts documentation
wording in review, so the sentence is no longer substring-matched.

`coverage.status` is a disjunction of independent causes in this projection:
incomplete inventory scope, unavailable content, and an exceeded bridge budget.
Asserting only `status: "partial"` left that attribution unstated, so the two
other causes are now pinned as well. `unavailable_components` being empty and
`bridge_candidate_coverage` being complete with zero candidates make the partial
result attributable to the missing occurrence alone.

Also drops an `artifact_id` comparison that could not fail: the fixture's four
files carry the same bytes, the preceding assertion already pins that shared
digest, and `artifact_id` is derived from the digest. Distinct paths sharing
content stay covered by the path array and the independent digest expectations.

Narrows a comment that claimed exact-case matching for every name in the
control; only the manifest names are case-sensitive, because the App Pack child
suffix match is case-insensitive.

* docs(harmony): name the disclosure location for a nested child package

The unclassified-disclosure paragraph used "there" to refer back to a clause
that had just named `app_pack_children`, so a reader could take a nested `.hap`
or `.hsp` member of a HAP or HSP root to be reported as an App Pack child. It is
reported in `unclassified_components`: the child list is populated only for an
App Pack root. Names the location explicitly.
2026-11-04 19:44:18 +08:00
ShuaixinHuang 65311d4992 fix(process): preserve literal temporary-root absence text (#8859) 2026-11-04 19:42:49 +08:00
Rohit Ghumare f042aaaf90 fix(gdb): keep sessions usable after an oversized stream record (#8029)
* fix(gdb): keep sessions usable after an oversized stream record

A single MI console, target or log record over the 16 MiB line limit
failed the whole session protocol, so close could not re-synchronize
and GDB plus its inferior stayed owned with no recovery path.

MI stream records carry only output and c-strings escape newlines, so
a raw LF always ends the record. #receive now drops the remainder of
an oversized stream record, marks pending command output and retained
records truncated, and keeps parsing later lines; oversized result
and async records still fail closed as before.

Fixes #8023.

* fix(gdb): keep earlier records in a chunk with an oversized stream record

* test(gdb): drop superseded oversized-record test copies
2026-11-04 19:32:00 +08:00
Dragolone 1f8084c7db fix(node-sea): bound ELF note scans by the notes a carrier holds (#8031)
Artifact classification probes every ELF root for a Node SEA note. Each
PT_NOTE program header was scanned on its own, one read per note, so
headers that declare the same note bytes multiplied the work: a 295 KB
ELF with 4,096 such headers kept inspect-artifact busy for about three
and a half minutes.

Every note header takes at least 12 bytes, so disjoint tables hold at
most one note per 12 file bytes. The scan now stops there. Past that
count the remaining tables are unreadable: an ELF without a confirmed
blob keeps its native classification, and a confirmed blob fails closed.
2026-11-04 19:31:14 +08:00
Dragolone d857b61bc5 fix(android): ignore action-less filters in activity export defaults (#8030)
Android's package parser discards an activity, activity-alias or receiver
intent filter that declares no action, so such a filter neither makes the
component exported by default nor triggers the API 31 explicit-export
requirement. Services keep action-less filters. The manifest inventory
counted every declared filter, reporting exported_default true (or an
explicit-export validation error) for components Android does not export.
intent_filter_count still reports every declared filter.
2026-11-04 19:28:59 +08:00
Beqa 4910e149ad docs(setup): describe FreeBuff as published setup support (#7246)
Setup against npm latest already accepts --client freebuff. Replace the
stale proposed-integration wording while keeping the unverified native
client loading caveat.
2026-11-04 19:22:36 +08:00
Christopher Pruijsen 5b953e13c4 fix(mcp): keep detail guidance in compact input schemas (#8034)
Compact input schemas are the default advertisement since #5975, and the
profile drops every property description. On 23 of the 31 tools with a
guided `detail` option, including inspect_artifact and inspect_macho,
`detail` is advertised as a bare complete|summary enum, so callers take the
complete default. On a universal macOS app that reply was about 699k
characters for inspect_artifact and 336k for inspect_macho, against about
4.4k and 3.2k with summary.

Keep the canonical description of a root `detail` property in the inline
compact input form. The reduced form already keeps first-level guidance, and
compact output schemas are unchanged. Advertised compact input schemas grow
from 127,616 to 133,358 bytes across 233 tools; the largest schema stays at
4,107 bytes, within the 13,312-byte budget.

The new boundary test compares `detail` guidance between the full and compact
tools/list and fails before the change.
2026-11-04 19:14:52 +08:00
hhangandhhang f8ec48095a fix(cli): guide delimited analysis view exports (#8730)
Co-authored-by: hhang <169027046+xu91102@users.noreply.github.com>
2026-11-04 19:10:35 +08:00
RRFZ 52ff3123cb docs(mcp): list every tool that omits the untrusted-content marker (#8837) 2026-11-04 19:04:57 +08:00
김준호 67552d56cc docs(limits): say setup forwards the selected MCP response budget (#8342)
The design guide said the setup allowlist does not copy
REA_MCP_MAX_RESPONSE_BYTES, but SELECTED_REGISTRATION_SETTINGS includes
it and setup forwards an explicit selection, as the client guide states.

Fixes #7420
2026-11-04 19:03:56 +08:00
Vinícius Andrade 6ba69cdcc1 docs(cli): document standard input for JSON request arguments (#7139)
An argument that accepts inline JSON or a JSON file path also reads the
request from standard input when it is "-". The behavior shipped without
documentation. Describe it next to the request-file example, with its
UTF-8, byte-order mark and 8 MiB limits.

Fixes #7138
2026-11-04 19:02:54 +08:00
SathishM 578e4bb71a docs(installation): list every engine accepted by doctor --provider (#4767)
The One analysis engine readiness row offered ghidra (or hopper, ida) while
the CLI accepts hopper, ghidra, ida, jadx and kuna for doctor --provider.
Add the missing IDs and keep the table aligned.

Related to #4624: docs/cli.md carries the same stale enumeration and is
updated separately in #4651.
2026-11-04 19:02:32 +08:00
ShuaixinHuang 1998e65705 fix(ghidra): discover installed versioned Homebrew JDKs (#8853)
* fix(ghidra): discover installed versioned Homebrew JDKs

* test(ghidra): protect Homebrew Java candidate precedence

* fix(ghidra): continue automatic JDK discovery after a failed keg
2026-11-04 16:10:46 +08:00
morluto f8f9095d2d feat(kuna): verify release 1.990 (#8813) 2026-11-04 11:49:36 +08:00
Rishikesh Gowda K K e75d1c741c feat(elf): decode Linux core dump process and memory mapping notes (#8830)
* feat(elf): decode Linux core dump process and memory mapping notes

Inspect Linux core dump note tables and decode structured metadata without
requiring external tools or debuggers:
- NT_PRPSINFO (type 3): decode process ID, parent ID, process group,
  session, UID, GID, execution state, filename, and command line arguments
  bounded by the text budget.
- NT_FILE (type 0x46494c45): decode mapped memory ranges (start_address,
  end_address, file_offset_pages) and mapped file paths with page size.
- NT_SIGINFO (type 0x53494749): decode faulting signal number, errno, and code.
- NT_AUXV (type 6): decode auxiliary vector pairs with known AT_* tag names.
- Support both 32-bit and 64-bit, little-endian and big-endian targets.
- Retain unknown core note types gracefully without partial success errors.
- Add comprehensive unit tests, fixtures, acceptance tests, and documentation.

* fix(elf): preserve raw path bytes and enforce auxv termination

* fix(elf): account for path hex text budget and enforce auxv trailing padding

* fix(elf): account for row and scan limits, ABI signal order, and lossless source IDs

* fix(elf): cache auxv padding scans, pre-charge entries, and isolate page-size test
2026-11-04 11:46:11 +08:00
morluto c3647ade1e fix(objdump)!: preserve unknown executable identity (#8824)
* fix(objdump)!: preserve unknown executable identity

* fix(objdump): avoid claiming rejected architecture output

* fix(objdump): align launch errors with command identity

* test(objdump): avoid coupling identity checks to wording

* test(objdump): align launch failure expectations
2026-11-04 02:01:44 +08:00
Cursor Agent 8c857ce56c docs(readme): move Discord button below site links
Place the Join us on Discord button under each hero's website, guides,
and showcases line. The invite and alt text stay the same.
2026-11-03 16:56:44 +08:00
morluto 28bffe92c8 docs(readme): move Discord button below site links (#8755) 2026-11-03 16:51:45 +08:00
morluto 4f7c4cc218 docs(readme): add Join us on Discord button (#8754) 2026-11-03 16:45:28 +08:00
Paritosh LahreandClaude Opus 5.5 8db538a7ca fix(browser): keep negative script columns Chrome reports (#8684)
* fix(browser): keep negative script columns Chrome reports

Chrome reported a Debugger.scriptParsed event with startColumn -60 and
endColumn -15 for an inline script in WordPress's helper frame. CDP declares
these columns as plain integers, but REA's admission required min(0), so one
script failed the whole observe-web-execution run with unreadable_output.

CDP resource columns now share one representation, cdpResourceColumnSchema,
for script start/end columns, initiator call frames, parser initiators and
event listeners. Positions on a script's first line include its start column,
so keeping the reported values keeps the documented script-relative
conversion consistent. The domain contract and web runtime docs state that a
negative column does not locate a column in the resource.

Fixes #8674

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

* fix(browser): apply the CDP column policy to passive capture

inspect_web_page and discover_webmcp_tools still read CDP columns through
cdpSourceCoordinates, whose own >= 0 check turned a negative column into
null and reported invalid_protocol_value. WebMCP discovery also dropped the
tool's whole registration_source.

cdpResourceColumnSchema now lives with the domain's CDP observation schemas.
Runtime admission, the passive coordinate helper and the runtime, console,
initiator and WebMCP output schemas all use it. Lines keep their
non-negative rule.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 14:50:00 +08:00
Wenyu Chiou f5cfe09c07 fix(environment): disclose truncated comparison differences (#7673)
Preserve the twenty-entry diagnostic prefix and report the full count,
omitted count and truncation fact at the owning report projection. Document
the recovery limitation and cover the public HAR-to-comparison workflow.

Fixes #7621

Validation: 121 source tests; three selected CLI regressions (five other
cases unselected); nine README conformance cases; full --noEmit typecheck;
documentation facts/metadata/schema/licenses; module boundaries and
architecture guards. VitePress rendering is blocked by Windows EPERM on its
worktree-local Vue symlink. Full aggregate coverage remains for Draft CI.

Independent code review: APPROVE, relayed by parent task
01a17716-6299-75fe-87e9-b0398b8c9019. Trigger: >=50 changed lines / >=3 files.
Reviewed tree: ba964efcd6
Reviewed full-index patch SHA256:
972dcfc219dd7b9606175b1848068f92210fa3f7d0a8a244c1ac7ce296ae32d9
2026-11-03 14:48:54 +08:00
Beqaandmorluto f59d884f35 fix(flutter): read unlabeled Dart Version::String() from engines (#7232)
* fix(flutter): read unlabeled Dart Version::String() from engines

Release libflutter.so embeds Dart's Version::String() without the
standalone VM's "Dart VM version:" label, so identify_flutter_build left
dart_version null on real APKs. Accept that channel-bearing form beside
the labeled scanner path.

* fix(flutter): qualify unlabeled version candidates

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-11-03 14:48:21 +08:00
Jeremy Levartovsky 5ed283d168 fix(har): accept Chrome DevTools bracketed IPv6 server addresses (#7225)
Chrome DevTools HAR exports declare creator WebInspector 537.36 and
report IPv6 serverIPAddress values in brackets: its HAR writer strips
the port with lastIndexOf(':') but its bracket removal,
replace(/\[\]/g, ''), only matches the literal pair '[]'. The pinned
schema then rejects the whole archive at the first such entry.

Extend the existing Playwright 1.58.2 adaptation to this declared
producer. Brackets are removed only in the disposable validation copy
and only around a valid IPv6 literal; reported spellings are retained
and the per-entry limitation names the declared producer.
2026-11-03 14:46:10 +08:00
Beqa d892e7861a fix(docs): drop duplicate native-inspection guide row (#7480)
Remove the incomplete Choose a guide duplicate that omitted kernel-modules.md so the docs index keeps one complete native-inspection entry.

Fixes #7405
2026-11-03 14:45:03 +08:00
morluto 193e01abb1 fix(kuna): keep timeout recovery from changing workers (#8687)
* fix(kuna): keep timeout recovery from changing workers

* test(kuna): verify timeout recovery with real provider
2026-11-03 10:10:11 +08:00
ShuaixinHuang 22c493ad5c fix(native): preserve implementation file locations (#8667)
fix(native): keep UI handler offsets distinct from method records
2026-11-03 09:38:12 +08:00
ShuaixinHuang b7911b2d30 fix(evm): explain limiter configuration and access failures (#8669) 2026-11-03 09:38:01 +08:00
JANG BYUNGKUN 6442ea02cb feat(binary-layout): admit ELF64 AArch64 and name the refused host (#8711)
* feat(binary-layout): admit ELF64 AArch64 and name the refused host

inspect_binary_layout refused every AArch64 ELF, and on a non-x64 host the
refusal read "supports Linux x64 only", which looked like a target refusal
(#8709 ran on arm64 WSL and suspected an e_machine check).

The pwntools adapter now admits little-endian ELF64 AArch64 EXEC/DYN/REL
objects alongside the existing profiles, pwntools must report aarch64/64/
little, and the Evidence subject names arm64. The host refusal now names the
detected platform/arch and says the target was not read; the host check
itself is unchanged.

verify:binary:layout cross-compiles an AArch64 executable and relocatable
object (never executed) and checks sections, symbols and relocations
against GNU readelf, decoding ELF64 r_info as a 32-bit type under a 32-bit
symbol index. The real-binary-layout workflow installs the toolchain.

Refs #8709

* test(binary-layout): refuse RISC-V as the unsupported machine and assert the host refusal path

verify:binary:layout used e_machine 183 (AArch64) as its unsupported
target, which this branch now admits; use EM_RISCV (243) instead.

The unsupported-host provider test now asserts the refusal names this
host's platform/arch and says the target was not read, so reverting the
decoder to the old message fails it.
2026-11-03 09:37:41 +08:00
ShuaixinHuang 510e83eadc fix(firmware): recommend the operation-specific engine (#8668) 2026-11-03 09:17:33 +08:00