mirror of
https://github.com/morluto/rea.git
synced 2026-11-07 07:16:13 +00:00
97aff282018907e7e48dc67fc12043e1237e2c99
250
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
641a8395ee |
fix(cli): refuse a JavaScript integrity policy the binary analyze lane cannot honor (#2808)
* fix(cli): refuse an integrity policy the binary analyze lane cannot honor `analyze` declares --integrity-policy for its JavaScript lane and forwards it only there. On a binary target the flag was accepted and then dropped: inspect_artifact-style integrity handling does not exist on this path, and binary_overview is a strictObject with no input at all, so the request could never take effect and the run proceeded as if the caller had not asked. The same branch already refuses source_paths on a binary target with a typed AnalysisInputError, and the sibling `inspect` command refuses the flag outright, so the refusal is the established handling here. The policy option carries a default, so the check keys off a value that differs from the owner schema's own default rather than the flag's presence; otherwise every binary analyze would be refused. The default is read from the schema instead of being restated. Verified with 5 integrity policy CLI cases, check:fast and verify:test-discovery (1023 files) on macOS arm64, and by running the built CLI for the no-flag, explicit-default and record-and-continue cases. The regression case failed with the command reaching the binary lane before the change. * fix(cli): reject explicit default integrity policies for binaries * fix(cli): keep an omitted integrity policy absent on the binary lane `javascriptApplicationOptions.shape.integrityPolicy` fills an omission with its default, and `.optional()` keeps reporting that parsed value, so `options.integrityPolicy !== undefined` was true for every `analyze <target>` call: ordinary binary analysis was rejected before the target was even read. List `z.undefined()` first in a union so an omitted option stays absent while the owner still validates a supplied value, and reapply the owner's metadata so help and `--schema` keep describing the option. Validation: tests/boundary/cli/javascriptIntegrityPolicyCli.test.ts reports 1 failed | 5 passed without this change (the case "still routes a binary analyze target without an integrity policy" returned invalid_request for integrity_policy instead of reaching the target) and 6 passed with it; `npm run check:fast` passes on macOS 26.5.1 arm64 with Node.js 24.18.0. * fix(cli): omit integrity defaults from binary option schema * test(ci): isolate report fixtures and declare PowerPC Java bounds --------- Co-authored-by: 吴佳浩 <wujiahao@supremind.com> Co-authored-by: morluto <76467478+morluto@users.noreply.github.com> |
||
|
|
b9b3a9098b |
fix(gemini): compare consolidated allowlists through one normalizer (#2495)
* fix(gemini): compare consolidated allowlists through one normalizer geminiServerPolicyBlock consolidates allowlists by normalized intersection but tested the consolidated list against the raw server key. Because the intersection keeps the spelling of whichever policy came first, the same pair of policies blocked the server in one arrangement and allowed it in the other, and the excluded test had the mirror-image asymmetry. Normalize the comparison key once and use the same normalizer for the exclusion test, the intersection and the membership test. The messages keep the caller-facing server key, so lookup and display stay separate. Verified with 88 client profile, lifecycle and registration tests, check:fast and verify:test-discovery on macOS arm64. The regression test drives setup twice with the same policies in both arrangements; before the change one arrangement failed and the other passed. * test(gemini): cover normalized exclusions and padded allowlist entries The regression case added with the fix drove the allowlist intersection in both spellings but left the other two comparisons the change touches uncovered: the exclusion membership test and the trimming applied to padded names. Reverting either behaviour kept the suite green, so neither was pinned. Greptile raised exactly this on the PR. An exclusion is now exercised as " REA " against the selected server, and a padded allowlist entry is exercised as the only policy, so a padded entry has to permit the same server its exact spelling permits rather than fall to the membership test. Verified with `npm run test:focused -- tests/boundary/cli/clientProfiles.test.ts`: 14 passed with the change; reverting only ClientConfigurationDocument.ts to main fails both cases, the arrangement case with `rea+REA: expected 1 to be +0` and the new one on both the excluded and the padded-allowlist assertions. * fix(process): report no remaining time after a provider deadline fires Node timers can fire while performance.now() is still a fraction of a millisecond short of the monotonic deadline. ProviderStartupDeadline then reported interruption "timeout" and an aborted signal while remainingMs() returned ceil(0.x) = 1, so polling loops guarded by remainingMs() > 0 (Hopper and Ghidra startup) could run past an elapsed deadline, and the real-timer test "aborts a bounded wait when its real monotonic deadline expires" failed intermittently in CI (expected 1 to be 0). Once the deadline's own timer has classified a timeout, report zero remaining time. A deterministic regression test fires the timer with the monotonic clock 0.4 ms short of the deadline. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: 吴佳浩 <wujiahao@supremind.com> Co-authored-by: morluto <76467478+morluto@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> |
||
|
|
c6166d4b56 |
fix(javascript): retain bridge conditions on IPC joins (#2747)
* fix(javascript): retain bridge conditions on IPC joins * fix(javascript): retain IPC condition ownership * style(javascript): format bridge path regression * fix(process): report no remaining time after a provider deadline fires Node timers can fire while performance.now() is still a fraction of a millisecond short of the monotonic deadline. ProviderStartupDeadline then reported interruption "timeout" and an aborted signal while remainingMs() returned ceil(0.x) = 1, so polling loops guarded by remainingMs() > 0 (Hopper and Ghidra startup) could run past an elapsed deadline, and the real-timer test "aborts a bounded wait when its real monotonic deadline expires" failed intermittently in CI (expected 1 to be 0). Once the deadline's own timer has classified a timeout, report zero remaining time. A deterministic regression test fires the timer with the monotonic clock 0.4 ms short of the deadline. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(javascript): preserve Electron join condition coverage --------- Co-authored-by: morluto <76467478+morluto@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: Codex <codex@users.noreply.github.com> |
||
|
|
7072f87baf |
fix(artifacts): preserve Mach-O load-command string identity (#2110)
* fix(artifacts): preserve Mach-O load-command string identity
* fix(process): report no remaining time after a provider deadline fires
Node timers can fire while performance.now() is still a fraction of a
millisecond short of the monotonic deadline. ProviderStartupDeadline then
reported interruption "timeout" and an aborted signal while remainingMs()
returned ceil(0.x) = 1, so polling loops guarded by remainingMs() > 0
(Hopper and Ghidra startup) could run past an elapsed deadline, and the
real-timer test "aborts a bounded wait when its real monotonic deadline
expires" failed intermittently in CI (expected 1 to be 0).
Once the deadline's own timer has classified a timeout, report zero
remaining time. A deterministic regression test fires the timer with the
monotonic clock 0.4 ms short of the deadline.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit
|
||
|
|
4acda94b76 |
fix(hopper): recover provenance-verified Spotlight exits
Verify the native helper's sealed identifier and pinned Apple signing team in BridgeLauncher, then carry that provenance to HopperClient. Only a verified native helper's complete not-found exit confirms no document handoff, allowing runtime and target-lease cleanup and retry; wrappers retain unknown document state. Share publisher identity with setup through the domain contract. Correct fixture teardown ordering and parameterized MCP coverage. Validation: 54 focused tests passed across boundary, CLI, MCP acceptance, diagnostics and installer coverage (one Linux-only case skipped); check:fast passed. Real codesign verification accepted the installed official Hopper helper; Spotlight failures and bridge recovery used process fixtures. |
||
|
|
6510d0d57f |
perf(ci): start source feedback early and diagnose stalled runs (#3013)
* perf(ci): start source feedback early and diagnose stalled runs * fix(ci): verify workflow identities before main comparison limits * fix(ci): preserve diagnostics across passing checks and install failures * fix(ci): preserve ambiguous workflow run ordering * fix(ci): preserve failure-report candidate and request budgets * fix(ci): retain candidate errors while checking later main evidence * fix(ci): retain partial reports and require complete health scans * test(ci): prune stale fixtures and duplicated assumptions |
||
|
|
4aa51bfe19 |
fix(reverse): reconcile cleanup ownership and mutation diagnostics (#2799)
* feat(process): add owned debugger shutdown lifecycle * feat(reverse): add local inspection commands * feat(gdb): add persistent debugger integration * feat(cutter): add Cutter bridge integration * test(reverse): enforce complete integration coverage * fix(process): preserve debugger shutdown ownership * fix(reverse): retain evidence across interrupted commands * fix(gdb): honor captured provider environment * test(process): cover debugger shutdown retries * fix(reverse): retain evidence for interrupted providers * test(process): cover debugger shutdown retries * fix(reverse): retain evidence for interrupted providers * fix(server): snapshot provider environment at creation * fix(reverse): close review gaps with complete coverage * fix(reverse): address inspection review findings * fix(gdb): bound sessions and retain cleanup ownership * fix(ci): require reverse-tools quality lane * fix(process): unref inherited debugger output pipes * fix(reverse): address remaining PR 1960 findings * fix(gdb): address remaining PR 1961 findings * fix(reverse): account for unrestricted provider execution * fix(process): retain debugger ownership until safe shutdown * fix(process): preserve replacement debugger sessions * fix(reverse): preserve admission and output provenance * fix(process): retain exited debugger until output settles * fix(mcp): bound provider admission and debugger cleanup * fix(reverse): preserve failure provenance and classify E2BIG * fix(gdb): preserve startup cleanup and detach safety * fix(process): retain debugger ownership after detach failure * fix(reverse): preserve mutation and failure provenance * fix(gdb): address debugger review findings * fix(reverse): rebind sessions after external mutation * fix(process): cap debugger output after exit * fix(gdb): protect one-shot detach verification * feat(debugger): add reverse tool coverage gate * test(debugger): cover integrated failure paths * fix(gdb): bound and preserve session observations * fix(gdb): address review regressions * style: format merged integration files * fix(reverse): preserve session and output evidence * refactor(gdb): share repeated test and partial result setup * fix(gdb): invalidate TTY proof for Python aliases * fix(reverse): retain failure output byte counts * test(process): synchronize late debugger output * fix(gdb): invalidate TTY proof for wrapped commands * fix(gdb): recognize wit command abbreviation * test(reverse): wait for cancelled descendant readiness * test(reverse): bound descendant cancellation cleanup * style(reverse): format integration tests and adapters * style: format reverse engineering integration files * test(reverse): remove duplicate environment harness * test(gdb): share session startup assertions * fix(gdb): invalidate output proof for command abbreviations * test(reverse): share composition edge case scenarios * fix(ci): recognize verification and mutation tool entries * test(rizin): cover successful fake inferior detach * test(cli): align full output failure envelope * test(cli): isolate reverse command lifecycle service * test(process): share debugger output fixture setup * fix(binary): preserve high-bit Windows signature bytes * style(binary): format Windows signature parser * test(mcp): preserve call coverage in unchanged comparison * fix(ci): include reverse tools Vitest configs in lint cache * fix(mcp): preserve target content on delivery errors * ci: align duplication ceiling with expanded suite * test(reverse): allow signal admission mutants * fix(reverse): bound retained cleanup failures to process owners * fix(test): replace drifting mutation ranges with owned selectors * fix(reverse): admit persistent commands and retain cleanup ownership * fix(cutter): recover timed-out commands from exact receipts * fix(cutter): reject unsupported bridge request kinds * fix(ci): repair client format and shared verification fixtures * fix(native): preserve debugger cleanup ownership and bridge authentication * test(native): retain diagnostics without perfect coverage requirements * chore(licenses): refresh locked dependency inventory and notices * fix(test): exclude transient command locks from mutation sandboxes * fix(gdb): admit only platforms with safe detach verification * fix(cutter): retain bounded completion receipts until acknowledgement * fix(cutter): acknowledge commands rejected before UI dispatch * test(reverse): fail cancellation checks before fixture teardown * test(gdb): clean up the retained broken-stdin fixture owner * docs(test): clarify focused provider verification scope * fix(rizin): retain close cleanup failures * fix(gdb): wait for inferior stop and bound MI buffering * fix(cutter): retain failed receipt acknowledgements * fix(rizin): return retained session identity on rebind failure * fix(gdb): preserve startup cancellation and lossy status * fix(rizin): retain launch provenance and assemble frames once * fix(cutter): recover receipts across callers and bound discovery * docs: describe debugger evidence and bridge recovery * fix(process): keep cleanup incomplete while writers remain Stopping a debugger no longer reports success when inherited output streams stay open, so BinarySession cannot reopen a binary a child is still changing. Concurrent Cutter cleanup retries share one receipt acknowledgement instead of treating a consumed receipt as failure. * fix(debuggers): recover cleanup with captured process identities * fix(analysis): retire sessions after unrestricted commands * fix(cli): preserve completed evidence after cleanup recovery * fix(cutter): bound frame collection and coalesce cleanup * fix(windows): resolve selected provider paths absolutely * fix(gdb): report decoding loss in failed startup evidence * fix(rizin): scope decoding loss to collected command frames * test(mcp): cover snapshot retirement in reverse tools CI --------- Co-authored-by: Daniel Gonçalves Araujo <47118053+dandgabr@users.noreply.github.com> Co-authored-by: REA Maintainers <noreply@github.com> Co-authored-by: OpenAI Codex <codex@users.noreply.github.com> Co-authored-by: Cursor Agent <cursoragent@cursor.com> |
||
|
|
dd36b4a8a0 | feat(analysis-view): report observed query parameter names (#2982) | ||
|
|
93e29d242a | feat(cli): summarize committed reference-source inventories (#2768) | ||
|
|
62c8b3da1b |
feat(browser): attach passive tools to browsers without HTTP discovery (#2346)
* feat(browser): attach passive tools to browsers without HTTP discovery Accept a literal loopback browser WebSocket, ws://127.0.0.1:PORT/devtools/browser/ID, as the cdp_endpoint of the six passive browser tools. Discovery then reads Browser.getVersion and Target.getTargets over that socket, and the target session adopts the same socket, so a browser that asks for consent per connection prompts once per call. The socket identifier gates access, so Evidence records the endpoint as ws://127.0.0.1:PORT/devtools/browser/[REDACTED], and runtime reconciliation accepts that recorded form. HTTP endpoints and their Evidence are unchanged. Electron, web runtime, scenario and Inspector endpoints stay HTTP-only. Refs #2288 * test(browser): match browser-socket captures against HTTP discovery Check in the real Chrome lane that DevToolsActivePort names the same browser socket as /json/version, and that inspecting the selected page through that socket leaves DOM structure and scripts unchanged compared with an inspection through HTTP discovery. Refs #2288 * fix(browser): bound browser-socket handshake and report discovery timeouts Run the browser-socket WebSocket handshake and both discovery commands under one discovery deadline, so a port that accepts the connection but never completes the upgrade times out instead of waiting forever. Report a reached deadline as a timeout without carrying the internal abort as its cause, so CLI and MCP project provider_timeout rather than cancelled. Caller cancellation is still reported as cancelled. Refs #2288 |
||
|
|
df3d96da80 |
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>
|
||
|
|
d4f0c5e664 |
fix(cli): start on Windows when USERPROFILE is empty (#2345)
* fix(cli): start on Windows when USERPROFILE is empty Node's Windows home lookup throws ERR_SYSTEM_ERROR (uv_os_homedir ENOENT) for an empty USERPROFILE instead of consulting the account profile. Incur resolves the home while it is imported, so every command, including `rea --version`, stopped with a raw stack trace before REA ran. Treat the empty selection as unset in the launcher, before any runtime import, and cover startup with a compiled-CLI regression. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(process): report no remaining time after a provider deadline fires Node timers can fire while performance.now() is still a fraction of a millisecond short of the monotonic deadline. ProviderStartupDeadline then reported interruption "timeout" and an aborted signal while remainingMs() returned ceil(0.x) = 1, so polling loops guarded by remainingMs() > 0 (Hopper and Ghidra startup) could run past an elapsed deadline, and the real-timer test "aborts a bounded wait when its real monotonic deadline expires" failed intermittently in CI (expected 1 to be 0). Once the deadline's own timer has classified a timeout, report zero remaining time. A deterministic regression test fires the timer with the monotonic clock 0.4 ms short of the deadline. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: morluto <76467478+morluto@users.noreply.github.com> Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com> |
||
|
|
59298923a5 |
fix(apktool): read the resource locale grammar from its owner (#2738)
Squash merge of https://github.com/morluto/rea/pull/2738. Original pull request submitted by @XiaoBinGan. Co-authored-by: 吴佳浩 <wujiahao@supremind.com> |
||
|
|
8b1549a264 |
test: share repeated boundary preambles to restore the jscpd gate (#2691)
Squash merge of https://github.com/morluto/rea/pull/2691. Co-authored-by: NIU-123370 <191000457+NIU-123370@users.noreply.github.com> |
||
|
|
ed93abed02 |
fix(cli): label each uninstall skill root by its client (#2197)
* fix(cli): label each uninstall skill root by its client rea uninstall reported every managed skill destination under the shared "skill" label (with only Claude Code special-cased), so a retained or failed directory could not be told apart from the others. Use the destination's client identifier so the aggregated detail names each root: "shared: retained (...)", "claude_code: removed (...)", "antigravity: retained (...)". Retained and failed entries now point to the specific directory that needs attention. Fixes #2111 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test(cli): align skill summary assertions with client and path labels --------- Co-authored-by: wjl8636 <wjl8636@users.noreply.github.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com> Co-authored-by: morluto <76467478+morluto@users.noreply.github.com> |
||
|
|
7369b5ee63 |
fix(ida): retain cleanup facts and let one-shot commands exit (#2814)
* fix(ida): let one-shot commands exit after retaining cleanup When a headless IDA request outlives timeoutMs (for example idb_open on a large binary still running auto-analysis), REA cannot prove the worker stopped and correctly retains the upstream connection and private workspace. The retained stdio child and its pipes stayed referenced, so a one-shot CLI printed its cleanup_incomplete result and then never exited, keeping the supervisor, worker and database alive. Detach the retained connection's child process and pipes from the event loop when cleanup fails. A long-lived MCP server can still retry through the retained connection; a one-shot CLI exits with status 1, and the supervisor observes end of input. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(ida): report an unverified upstream worker after failed cleanup Upstream ida-pro-mcp spawns headless workers in their own session and deliberately leaves them running when its supervisor exits, so neither a retained connection nor REA exiting proves that a timed-out worker stopped. When cleanup fails before the owned worker's release is confirmed, the cleanup diagnostics now name its private session and state that it may still be running until it finishes and reaches its idle timeout. Failures after a confirmed release, or before any open was attempted, do not report a worker. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(ida): name the inventory sessions that block worker release When idb_close confirmed the requested worker gone but the follow-up inventory still found a different peer on the private input, the upstream_worker diagnostic named the released session and could misdirect manual cleanup. Record the session ids of the inventory entries that actually blocked release and report those; fall back to the requested private session only when no inventory evidence exists (an incomplete open or an unanswered request). The field becomes session_ids, since more than one entry can block release. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(ida): keep blocker evidence and backend in cleanup diagnostics The release diagnostic reset its observed blockers on every close, so a retry whose inventory was unavailable fell back to the already-released session; and it kept only session ids, describing a blocking GUI session with detached-worker guidance. Blocker evidence is now replaced only by a successful inventory, and the diagnostic becomes upstream_sessions with each session's id and backend, with guidance that distinguishes workers from GUI sessions. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(ida): stop naming a worker after its close was confirmed When idb_close confirmed the requested worker's release but the following inventory failed, no blocker evidence existed yet and the diagnostic fell back to the confirmed-absent worker. After a confirmed close the diagnostic now reports an empty session list, meaning any remaining same-input sessions are unknown. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(ida): group cleanup diagnostic cases in their own suite The added cases pushed the reaped-worker describe block past the 150-line limit enforced by lint:built. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(ida): keep ownership and pre-close peers in cleanup diagnostics Blocking sessions dropped the parsed ownership fact, so an external unowned worker looked the same as REA's own; and recovery inventory recorded same-input peers only when the requested worker was missing, so a peer seen beside it was lost if the following close failed. Each reported session now carries owned (null when no inventory has reported it), and recovery records every same-input session until a later successful inventory replaces that evidence. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(ida): drop a confirmed worker from retained cleanup blockers Recovery inventory now records the requested worker beside any peers, so once idb_close confirmed its release a failing follow-up inventory still reported it as unreleased. Remove the confirmed session from the retained blockers at close confirmation and keep the remaining peers. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(ida): retry cleanup through a detached stdio connection The retry coverage used a recording connection whose detach() only counts calls, so it could not show that detaching keeps a retained connection usable. Run a real stdio supervisor in a long-lived host, fail the first post-close inventory, and require the retry through the same detached connection to confirm release and remove the workspace. The test fails if detach() destroys the pipes instead of unref-ing them. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(ida): close the stdio supervisor in failed retry tests Merge main and give its new IdaNativeProtocol connection double the required detach() method. The real-stdio retry test now closes its connection in an unconditional teardown, so a failing assertion cannot leave the supervisor waiting on stdin. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(ida): share the cleanup diagnostic expectation in session tests Repeated upstream_sessions assertions and the unrelated live peer literal pushed repository duplication over the jscpd threshold. Build the expected diagnostic with one helper and reuse one peer constant. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(ida): distinguish observed ownership from unreported input bindings * fix(ida): keep requested-ID conflicts in recovery cleanup Recovery inventory kept only same-input entries, unlike the startup and post-close filters. After an explicitly rejected open, an entry under the requested session ID on another input was dropped, so cleanup could close the connection, remove the private workspace and report success while that session stayed registered. Match the requested ID as well so the entry is retained as a blocker and cleanup stays incomplete. Also split the cleanup diagnostics suite under the lint:built function length limit, and make two startup-inventory fixtures pass a defined session id so the test file typechecks. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(ida): preserve retained session inventory facts --------- Co-authored-by: Xingchuan Ma <xingchuan.ma@msn.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: morluto <morluto@users.noreply.github.com> |
||
|
|
f0b8ae1fc8 |
fix(javascript): trace nested Electron bridge members (#2745)
* fix(javascript): trace nested Electron bridge members * fix(javascript): preserve shallow Electron bridge paths * fix(javascript): preserve exact Electron bridge member paths * fix(javascript): preserve exact Electron bridge paths * fix(javascript): preserve bridge entry ownership * fix(javascript): preserve bridge path identity * fix(javascript): normalize typed feature seeds before tracing * test(javascript): normalize direct selector seed inputs * refactor(javascript): consume normalized feature seed defaults --------- Co-authored-by: morluto <76467478+morluto@users.noreply.github.com> |
||
|
|
96d1f0f3fb |
fix(cli): refuse scenario steps on passive native UI observation (#2712)
observe-native-ui and capture-native-ui-scenario were registered in one
loop with a shared option schema and description. The passive command
therefore advertised `--steps` ("Observe or run a scenario"), accepted
any value, and then dispatched observe_native_ui without it, so a
caller who selected the wrong command never learned that no scenario ran.
Register each command against its own contract. Observation declares
only the passive scope options, so `--steps` is rejected as an unknown
flag; the scenario command keeps its JSON step decoding and validation.
Each command now has its own description.
Fixes #2674
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
|
||
|
|
5d12a0b4b6 |
test(web): cover nullable HTTP captures through CDP and CLI (#1834)
* fix(browser): preserve unknown HTTP response comparison fields * fix(browser): retain proven request inventory changes * fix(javascript): preserve Electron identity and avoid quadratic property scans Share visible Electron mutations with wrapper expansion, index static object literal properties once, and align MCP assertions with current contracts. * fix(javascript): track writes through symbolic Electron aliases Resolve mutation targets through the same factories, module tables, containers, and conditional origins as call analysis. Share mutation coverage between direct and wrapper scans while isolating origin caches and preserving exact property paths, local rebinding semantics, and analysis resource limits. * fix(javascript): preserve container slots and imported mutation origins * perf(javascript): defer container mutation replay until property reads * fix(javascript): preserve captured object identities and write order * fix(javascript): preserve ordered mutation origins * fix(javascript): honor property mutation constraints * test(web): narrow PR to HTTP and CLI comparison regressions --------- Co-authored-by: morluto <76467478+morluto@users.noreply.github.com> |
||
|
|
5dd2ce3125 |
fix(ida): let one-shot commands exit after retaining cleanup (#1841)
* fix(ida): let one-shot commands exit after retaining cleanup When a headless IDA request outlives timeoutMs (for example idb_open on a large binary still running auto-analysis), REA cannot prove the worker stopped and correctly retains the upstream connection and private workspace. The retained stdio child and its pipes stayed referenced, so a one-shot CLI printed its cleanup_incomplete result and then never exited, keeping the supervisor, worker and database alive. Detach the retained connection's child process and pipes from the event loop when cleanup fails. A long-lived MCP server can still retry through the retained connection; a one-shot CLI exits with status 1, and the supervisor observes end of input. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(ida): report an unverified upstream worker after failed cleanup Upstream ida-pro-mcp spawns headless workers in their own session and deliberately leaves them running when its supervisor exits, so neither a retained connection nor REA exiting proves that a timed-out worker stopped. When cleanup fails before the owned worker's release is confirmed, the cleanup diagnostics now name its private session and state that it may still be running until it finishes and reaches its idle timeout. Failures after a confirmed release, or before any open was attempted, do not report a worker. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(ida): name the inventory sessions that block worker release When idb_close confirmed the requested worker gone but the follow-up inventory still found a different peer on the private input, the upstream_worker diagnostic named the released session and could misdirect manual cleanup. Record the session ids of the inventory entries that actually blocked release and report those; fall back to the requested private session only when no inventory evidence exists (an incomplete open or an unanswered request). The field becomes session_ids, since more than one entry can block release. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(ida): keep blocker evidence and backend in cleanup diagnostics The release diagnostic reset its observed blockers on every close, so a retry whose inventory was unavailable fell back to the already-released session; and it kept only session ids, describing a blocking GUI session with detached-worker guidance. Blocker evidence is now replaced only by a successful inventory, and the diagnostic becomes upstream_sessions with each session's id and backend, with guidance that distinguishes workers from GUI sessions. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(ida): stop naming a worker after its close was confirmed When idb_close confirmed the requested worker's release but the following inventory failed, no blocker evidence existed yet and the diagnostic fell back to the confirmed-absent worker. After a confirmed close the diagnostic now reports an empty session list, meaning any remaining same-input sessions are unknown. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(ida): group cleanup diagnostic cases in their own suite The added cases pushed the reaped-worker describe block past the 150-line limit enforced by lint:built. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(ida): keep ownership and pre-close peers in cleanup diagnostics Blocking sessions dropped the parsed ownership fact, so an external unowned worker looked the same as REA's own; and recovery inventory recorded same-input peers only when the requested worker was missing, so a peer seen beside it was lost if the following close failed. Each reported session now carries owned (null when no inventory has reported it), and recovery records every same-input session until a later successful inventory replaces that evidence. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(ida): drop a confirmed worker from retained cleanup blockers Recovery inventory now records the requested worker beside any peers, so once idb_close confirmed its release a failing follow-up inventory still reported it as unreleased. Remove the confirmed session from the retained blockers at close confirmation and keep the remaining peers. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(ida): retry cleanup through a detached stdio connection The retry coverage used a recording connection whose detach() only counts calls, so it could not show that detaching keeps a retained connection usable. Run a real stdio supervisor in a long-lived host, fail the first post-close inventory, and require the retry through the same detached connection to confirm release and remove the workspace. The test fails if detach() destroys the pipes instead of unref-ing them. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(ida): close the stdio supervisor in failed retry tests Merge main and give its new IdaNativeProtocol connection double the required detach() method. The real-stdio retry test now closes its connection in an unconditional teardown, so a failing assertion cannot leave the supervisor waiting on stdin. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(ida): share the cleanup diagnostic expectation in session tests Repeated upstream_sessions assertions and the unrelated live peer literal pushed repository duplication over the jscpd threshold. Build the expected diagnostic with one helper and reuse one peer constant. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(ida): distinguish observed ownership from unreported input bindings * fix(ida): keep requested-ID conflicts in recovery cleanup Recovery inventory kept only same-input entries, unlike the startup and post-close filters. After an explicitly rejected open, an entry under the requested session ID on another input was dropped, so cleanup could close the connection, remove the private workspace and report success while that session stayed registered. Match the requested ID as well so the entry is retained as a blocker and cleanup stays incomplete. Also split the cleanup diagnostics suite under the lint:built function length limit, and make two startup-inventory fixtures pass a defined session id so the test file typechecks. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Xingchuan Ma <xingchuan.ma@msn.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: morluto <morluto@users.noreply.github.com> |
||
|
|
59d33a2733 |
fix(native): keep lipo path echoes out of FAT records (#2605)
* fix(native): keep lipo path echoes out of FAT records Parse FAT records only after separating the exact echoed operand path. Pass the same lipo operand at all three consumers and cover multiline paths through parser, slice-reader and compiled CLI regressions. * fix(native): use parsed lipo framing for container offsets * test(native): use explicit FAT fixtures for CLI regressions |
||
|
|
ece15ed6a1 |
fix(electron): identify each unresolved scenario path (#2279)
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com> |
||
|
|
9c14999efd |
feat(ghidra): support raw NMOS 6502 analysis (#2079)
* feat(ghidra): admit raw NMOS 6502 analysis * test(ghidra): require verified POSIX verifier cleanup * test(ghidra): verify 6502 low-memory raw imports * docs(skill): document raw NMOS 6502 support * fix(ghidra): preserve requested Windows raw source paths * docs(ghidra): include raw 6502 in Windows installation scope * test(ghidra): verify Windows raw junction rejection through CLI and MCP * fix(validation): make fixtures portable and preserve explicit Codex home * fix(ghidra): attest raw mapping state and address space * fix(ghidra): align diagnostics and regression groups * fix(ghidra): bind raw attestation to language address space * chore(ghidra): avoid verifier insertion conflict * docs(ghidra): align linked raw admission guides * fix(ghidra): retain 6502 verifier workspace on failure * test(ghidra): isolate Windows authority doctor fixture * fix(binary): re-admit Windows raw paths on session reopen * test(binary): require fresh provider execution after path change * Constrain 6502 to raw lifecycle and snapshot targets * Bypass saved reads for unadmitted Windows paths * Guard direct CLI snapshot replay on Windows paths * fix(snapshot): gate Windows Ghidra replay on native admission * fix(snapshot): constrain nested 6502 subjects to raw images * test: make dyld slice fixtures portable |
||
|
|
1ca5fd38b4 |
fix(cli)!: keep a selected JSON null in --filter-output output (#2333)
createCliResultOutput rendered an explicit null selected by
--filter-output as the empty document {}, the same output it uses when
the selection has no value at all. On the streamed JSON/JSONL path,
a consumer could not distinguish a selected null from a missing field.
Write a selected null as null in streamed JSON and JSONL. This also
preserves an unfiltered top-level null. The {} fallback stays for
undefined, functions and symbols. The --full-output envelope already
carried data: null for a selected null. The --token-count path still
hands results that fit the formatted-string limit back to Incur and
prints 0 for a selected null. Other formats and Incur-owned result
paths are unchanged.
Consumers must accept JSON null for a present null and distinguish it
from a missing selection. Use --full-output if an object envelope is
required. Keeping the old shared {} representation would continue
hiding this distinction.
Claude-Session: https://claude.ai/code/session_019TTGAv6fe859ebbCLLfLHP
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
|
||
|
|
68ecdcd7a1 |
fix(reference): preserve directory intent when resolving source imports (#2770)
Keep directory requests from becoming unresolved or pointing at sibling files when constructing reference-source dependency graphs. Canonicalize index candidates and preserve directory intent while retaining ordinary lookup rules. Constraint: Preserve current TypeScript candidate objects and exact-before-Unicode lookup Rejected: Reorder all file probes before index probes | Changes ordinary CommonJS candidate priority Confidence: high Scope-risk: narrow Tested: Public CLI regression fails on base; focused reference and resolver suites pass; npm run check Not-tested: Full suite on refreshed base; earlier full run had 12 baseline-reproduced failures |
||
|
|
c95315d2ce | fix(cli): reject unterminated output filter slices (#2315) | ||
|
|
fb28ec1537 |
test(apple): keep the same-named slice dyld fixture openable on x64 hosts (#2755)
Add a plain x86_64 slice to the dyld fixture while retaining both same-named arm64e slices and their independent environment assertions. The original fixture was rejected by real x64 CLI admission before tracing. The contributing PR's complete Linux CI run passes all 30 jobs, including the portable suite. Codex and Greptile reviews completed without findings; no unresolved threads remain. This restores the main test baseline needed for #2635. The change is limited to test data and preserves the original author's contribution. Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com> |
||
|
|
2995ec6910 |
fix(ghidra): resolve file offsets in decompile (#2101)
* fix(native): expose loaded image bases in overviews * test(ghidra): retain resources when verifier cleanup fails * docs(ghidra): spell out file-offset address mapping * fix(ci): separate and retry NativeAOT dependency restoration * fix(test): resolve CI script file URLs on Windows * fix(native): retain conflict observations and actionable CLI guidance * fix(fixtures): keep NativeAOT retry logs credential-safe * fix(fixtures): redact authenticated URLs at diagnostic creation * fix(ghidra): restrict source-coordinate observations to decompilation * fix(cli): identify conflicting file-offset selectors accurately * fix(ghidra): bind file offsets to admitted target sources * fix(ghidra): preserve resolved file-offset failure context * test(ghidra): verify ELF address and file-offset round trips * test(nativeaot): align diagnostic regressions with fixture host support |
||
|
|
ec3db37457 |
fix(native): preserve literal line breaks in signing identifiers (#2107)
* fix(native): preserve literal line breaks in signing identifiers * fix(native): infer codesign framing without a matching path |
||
|
|
52d4ce6ced |
fix(apple): derive dyld environment findings from each traced slice (#2733)
deriveFindings looked up each traced root's slice again by architecture name. A universal executable with arm64e and arm64e.x1 slices reports both as "arm64e", so both roots resolved to the first slice: a later slice's LC_DYLD_ENVIRONMENT produced no finding, or the first slice's command was reported twice. The per-slice dyld_environment arrays were already correct. traceDylibLoading already has the exact slice when it records a root, so pass it to deriveFindings instead of re-deriving it from the name. The public roots and findings shapes are unchanged. Fixes #2661 Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
11f8b76257 | fix(apktool): accept --no-strings CLI option (#2707) | ||
|
|
b955b825fc |
fix(artifacts): retain read cleanup ownership through CLI and MCP shutdown
Keep failed stable-read handles and WABT runtime roots under retryable resource scopes. Preserve cancellation causes and primary errors, close the same service instances bound to MCP tools, and consolidate CLI operation/cleanup projection. Validation: fast typecheck and lint, cached runtime build, docs checks, SDK schema validation, focused service and real-file cleanup tests, CLI cleanup regressions, and MCP lifecycle/shutdown workflows. Provider process fixtures simulate engine output; real WABT and Linux EVM execution are not established by this pass. |
||
|
|
4276aaf18b |
feat(signals): discover installed decoders and option help (#2487)
* feat(signals): discover installed decoders and selected option help * fix(signals): bound retained discovery data and preserve resource failures * fix(signals): complete CLI help and portable decoder patterns * test(signals): verify case-insensitive decoder help queries * fix(signals): retain discovery operation across cleanup retries |
||
|
|
9c34c091b3 |
feat(captures): discover installed protocol and field definitions (#2496)
* feat(captures): discover installed protocol and field definitions * test(captures): isolate the direct glossary oracle profile * test(captures): accept padded kernel limit reports * fix(captures): detach every retained glossary column * fix(captures): describe every field discovery CLI option * fix(captures): retain discovery operation across cleanup retries --------- Co-authored-by: N0zoM1z0 <161784452+N0zoM1z0@users.noreply.github.com> |
||
|
|
bb96afdbe3 |
fix(pcap): name the host tool to configure in capability refusals (#2507)
* fix(pcap): name the host tool to configure in capability refusals Missing or misconfigured tshark/capinfos, the unsupported macOS capture boundary and an unusable Linux prlimit were projected with the generic "unavailable for the current target; choose another target" advice, even for inspect_pcap_client, which has no target. Route tool refusals through one pcapToolUnavailable owner that names REA_TSHARK_COMMAND and REA_CAPINFOS_COMMAND, give host-boundary refusals their own reason, and keep the private workspace refusal's retryable flag like WABT and source maps do. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(pcap): cover the Linux prlimit refusal guidance Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
16f5ded446 | test(cli): consolidate development runner workflow coverage | ||
|
|
9581e067ad |
feat(pcap): add bounded capture summaries through CLI and MCP
Summarize saved Wireshark4.x captures with enforced native resource limits, serialized admission, stable snapshot verification and retained fact/text pages. Preserve complete inventories within resource budgets and source uncertainty; report cleanup failure with partial Evidence and retained ownership. Verified six recorded corpora /13026 packets with independent Scapy/Python oracles, real CLI/MCP/schema parity,60 SNI names,132 DNS addresses and1000 URIs. Real TShark CI now exercises summary/paging. Actual Windows/Bun Wireshark remains unverified; macOS capture requires a separate enforceable-bound decision. Co-authored-by: 3QL <85309073+3QL@users.noreply.github.com> Co-authored-by: N0zoM1z0 <161784452+N0zoM1z0@users.noreply.github.com> |
||
|
|
91b8f587cd |
feat(signals): decode recordings with bounded sigrok output (#2339)
Decode caller-selected protocols from existing recordings through shared CLI/MCP Evidence. Page literal output lines after complete decoding; preserve explicit full output, source identity and failed cleanup ownership. Verified real sigrok-cli 0.7.2 workflows, published UART/SPI/I2C recordings, independent UART values, selector limits, SDK schemas and cleanup regressions. Co-authored-by: Olorinm <59321404+Olorinm@users.noreply.github.com> Assisted-by: Kogasa (gensokyo-skills:kogasa-surprise-testing) |
||
|
|
7b15762d09 |
fix(reference): report sloppy scripts, PHP, C# and enclosing Git facts (#1770)
* fix(reference): report sloppy scripts, PHP, C# and enclosing Git facts Importing the source/ subtree of a legacy PHP and C# checkout produced four wrong or missing facts: - Babel's unambiguous mode parses a module first and keeps its strict-mode recovery errors after falling back to a script, so a valid sloppy-mode `delete messages;` was reported as a parse failure and the semantic IR marked the file as parser-recovered. Parsing now goes through one owner that re-parses such a script as a script. - .php and .cs files had no language; they are now detected as PHP and C# (detection only, no parsing). - A root without its own .git was reported as having no version control even inside a checkout. The adapter now resolves the nearest enclosing repository, as Git does, and reports `none` only when no ancestor has one. - The CLI recorded importer_version as null although the package version is known. * fix(reference): keep module strictness and classify C# and PHP as source Review follow-up. `.mjs` and `.mts` files are modules by extension, but the script fallback re-parsed them in sloppy mode and erased real strict-mode errors. The parser boundary now owns the extension rule, parses explicit modules as modules and applies the fallback only when the mode is ambiguous; the require-binding analysis uses the same rule. `.cs` and `.php` received a language but stayed classified as unknown, so source-to-bundle comparison dropped them. Both are now source extensions, like the other detected languages. * fix(reference): honor package module scope and keep unknown-mode strict errors Review follow-up. A `.js` file under a package.json with `"type": "module"` is an ES module, but it went through the script fallback that erases strict-mode errors. The import now reads each in-inventory package.json and parses `.js` files in a module scope as modules. Other `.js` and `.ts` sources keep an unknown mode, because bundlers treat them as modules without that field: they are no longer forced to script, and errors that only the module parse reports are kept as an entry limitation. * fix(reference): bound package scopes and keep application parsing unchanged Review follow-up. - `.cjs` files parse as scripts, so module-only strict errors no longer become limitations on CommonJS trees. `.cts` keeps an unknown mode because TypeScript compiles ES import syntax there. - An unreadable or linked package.json now bounds the module scope with an unknown type instead of letting an outer `"type": "module"` apply. - The application pipeline parses as on main again. Its module-only strict errors keep a source partial; package-mode handling stays in the reference importer. * refactor(reference): keep unmarked module modes unknown instead of resolving package scope Review follow-up. Resolving Node package scope for `.js` kept pulling in more of Node's lookup semantics (require bindings, excluded or directory-named manifests). Only extensions now select a mode: `.mjs` and `.mts` parse as modules and `.cjs` as scripts. Every other source keeps an unknown mode, so module-only strict errors stay visible as an entry limitation instead of being dropped or asserted. This matches the require-binding analysis, which also treats unmarked paths as unknown. |
||
|
|
8224aeba9d |
test(cli): align reference import failure envelopes
Expect ok:false and the preserved error payload in full-output mode, including Windows unsupported-host failures. Retain plain JSON, selected-path, remediation, permission, logging, and source-preservation coverage. Validation: focused compiled CLI output and reference-import tests; Windows-only cases await CI. |
||
|
|
f611d8ce72 |
feat(captures): inspect recorded packets with bounded TShark delivery (#2318)
Inspect selected fields in existing captures through shared CLI/MCP Evidence. Page matching frames without changing dissection or two-pass read coverage; retain failed descriptor/process cleanup ownership and reject excess admission. Verified with real TShark 4.0.17 and 4.2.2, public Wireshark recordings, owned HTTP application traffic, real CLI/MCP workflows and schema checks. Co-authored-by: Olorinm <59321404+Olorinm@users.noreply.github.com> Assisted-by: Kogasa (gensokyo-skills:kogasa-surprise-testing) |
||
|
|
570c40cde5 | fix(cli): preserve failure semantics across full-output formats | ||
|
|
2397137615 |
fix(setup): honor config roots and display terminal dry-run plans
Normalize empty XDG config homes once for configuration writers and registration readers. Render the resolved plan for terminal dry runs while preserving structured output. Cover registration placement and a real PTY dry run, including absence of configuration writes. |
||
|
|
bb26622670 |
fix(cli): retain correction details across adjacent browser commands (#2294)
* fix(cli): retain correction details across browser commands Fold the complementary implementation from #2247 into the earlier #2230 proposal, retaining the original contribution history. Co-authored-by: LouisChen <3361008+chqcjq@users.noreply.github.com> * fix(browser): retain diagnostics from the selected input branch * fix(cli): recognize incomplete browser scenarios by their owned fields --------- Co-authored-by: LouisChen <3361008+chqcjq@users.noreply.github.com> |
||
|
|
0043d9b134 |
fix(browser): report mismatched event family selection as unknown in capture comparison (#2239)
* fix(browser): report mismatched event family selection as unknown in capture comparison * fix(browser): retain selected event families and unknown coverage Fold the complementary implementation from #2253 into the earlier #2239 proposal, retaining the original contribution history. Co-authored-by: itsF4LCON <107556180+itsF4LCON@users.noreply.github.com> --------- Co-authored-by: LouisChen <3361008+chqcjq@users.noreply.github.com> Co-authored-by: morluto <76467478+morluto@users.noreply.github.com> Co-authored-by: itsF4LCON <107556180+itsF4LCON@users.noreply.github.com> |
||
|
|
916ac97bf9 |
fix(cli): retain browser and Inspector input correction details (#2230)
Reuse the shared safe issue projection for all six CLI validation boundaries and cover malformed inputs, credential safety, and parser diagnostics. Fixes morluto/rea#2180 Developed with assistance from OpenAI Codex. |
||
|
|
1770f38cfc |
test(native): verify real PE and DOS entry views
Record real Ghidra/MCP and Codex CLI verification of shipped ShareX native PE and FreeDOS DOS MZ entry views. Preserve original Evidence provenance, separate run-specific references from facts, and reject stale/colliding producer identities or unsupported model claims.
Sixteen focused experiment regression cases, original public-reply/transcript replays, and CI run 38273269072 pass for head
|
||
|
|
7711b00005 |
fix(apple): preflight XML plist nesting before keyed archive decoding (#2161)
* fix(apple): preflight XML plist nesting before keyed archive decoding inspect_keyed_archive documented a 128-level nesting limit, but the XML plist path reached recursive decoding before any depth check: plist and the prototype-key traversal in parseXmlPropertyList recurse once per nesting level, so a ~75 KiB, 3000-level XML plist exhausted the call stack and reported Maximum call stack size exceeded instead of the bounded-resource limit that shallower over-limit documents (129, 200) already produced. The CLI classified both as truncated, so no crash or classification bypass was involved; the diagnostic was the defect. Reject excessive nesting in parseXmlPropertyList before any recursive pass, using the projection's own limit (MAX_PLIST_DEPTH) so the error is identical to the one projectPlistValue raises: callers keep the limit/truncated classification with no error-handler translation. Depth counts values the way the projection does - container members nest one level deeper, dictionary keys name members rather than nesting, and a self-closing element is the value at its own level - so the guard rejects exactly the documents the projection rejected, at the same boundary, and every in-limit document decodes unchanged. The guard sits at the shared decoder boundary, so Interface Builder, app Info.plist resolution and native inspect_plist gain the same deterministic limit; the binary plist path keeps its existing expansion preflight. Binary plist behavior, ordinary XML decode, __proto__ omission counting, and archive digest/Evidence provenance are unchanged. OpenStep autodetection is untouched by this guard, as the existing Interface Builder preflight also scopes its depth check to XML. Regression coverage: decoder unit tests at and just past the limit for dictionary and array nesting with self-closing and opaque markup, module tests through decodeKeyedArchiveBytes including a valid NSKeyedArchiver XML envelope at the limit, a production-boundary test through inspectBundleKeyedArchive, and the compiled public CLI reporting code truncated / reason limit for a 3000-level plist. * fix(apple): check XML nesting against parsed element structure Reuse the iterative XML decoder document for depth preflight and prototype-key lookup. Bound recursive plist wrappers alongside dictionaries and arrays without interpreting markup inside attributes or DOCTYPE declarations as values. Validated with 104 focused tests including compiled CLI, MCP prototype-key, and Interface Builder coverage; npm run check and verify:test-discovery passed. No real-provider verification was run. --------- Co-authored-by: Zburgers <Zburgers@users.noreply.github.com> Co-authored-by: morluto <76467478+morluto@users.noreply.github.com> |
||
|
|
86de439ba4 |
fix(frida): name the argument each Frida input error rejects (#2217)
Frida session lookups, script lookups, unreadable script files and the instrument-with-frida PID and script checks returned invalid_request with no issues, while the remediation told callers to correct the listed arguments. Attach the session_id, script_id, path, target or script issue with the reason so CLI and MCP callers can see what to fix. Fixes #2213 Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
e4d3e1399d |
feat(flutter): identify Dart snapshot builds and inspect AOT payloads by pure APK parsing (#1582)
* feat(flutter): identify Dart snapshot builds by pure APK parsing Add a flutter-provider family with one operation, identify_flutter_build, that reads an APK's lib/<abi>/libapp.so and libflutter.so through the hardened ZIP reader and reports build facts as observations: - The Dart VM snapshot hash, read 20 bytes after every snapshot magic (f5f5dcdc). The vm and isolate sections are cross-checked: agreeing sections collapse to one snapshot_hash, disagreeing sections keep every candidate with snapshot_hash null and partial coverage. - Per-ABI library digests and byte sizes, plus the engine library's GNU build-id note and any labeled 'Dart VM version' or Android clang/LLVM toolchain strings, verbatim; release builds without a version string report null rather than a guess. - The APK is digested before parsing so Evidence binds the observation to the exact input, and a target with no libapp.so reports not-detected with complete coverage. The scanner is streaming with explicit pending-magic and rescan bookkeeping, so chunk boundaries cannot double-count or drop sections, and magic-free streams never accumulate. No engine, no external lookups: the hash-to-SDK mapping stays with external datasets by design. Verified against Flutter Gallery 2.9.2 (112 MB, three ABIs): every ABI reports the same agreeing hash 65817c30a78bb44c3dc3771876b6010a from two sections, per-ABI build-ids, and the clang toolchain line; a plain aapt2-built APK reports not-detected with complete coverage. Scanner goldens come from the same libraries. READMEs carry the new 'Flutter apps' row in every supported language. * feat(flutter): inspect Dart AOT snapshots through the ELF symbol table Add inspect_dart_aot to the flutter family: for one ABI's libapp.so it locates the four _kDart*Snapshot* symbols through the ELF dynamic symbol table (ELF32 and ELF64 little-endian, bounds-checked reads; a payload without exported symbols is refused), reports each section's offset and size, validates data-section headers (magic, kind, length; instructions sections honestly report nulls for the fields their layout does not carry), and projects a bounded string pool from the isolate data section: distinct package: source URIs (the app's dependency inventory), dart: SDK URIs, and identifier-shaped tokens, each with counts, bounds, and partial coverage when truncated. The string pool is labeled as a byte-pattern projection, not deserialized cluster semantics: typed class and function recovery remains an SDK-specific parser, which the snapshot hash now unlocks. The ABI is caller-selected with a deterministic default (arm64-v8a, x86_64, then first available). Verified against Flutter Gallery 2.9.2: all four symbols on both ELF64 (arm64-v8a) and ELF32 (armeabi-v7a) images, header validation, hash agreement with identify_flutter_build, and a 756 package: / 201 dart: URI pool from the isolate data section; goldens in DartElf.test.ts come from the same libraries. * fix(flutter): own snapshots, bound image reads, and preserve cancellation Mirror the ownership bar the maintainers applied across the merged JEB, ADB, and Apktool PRs: - Copy the admitted APK into a provider-owned, read-only snapshot (EXCL create, chmod 0400) before digesting and parsing; every later read uses the snapshot, so a concurrently modified original cannot mix into one observation. The result reports the snapshot's byte size and digest. - Retain failed snapshot-root cleanup in a provider set and retry it on close(), throwing an aggregate error when a root is still owned; root removal is injectable so the retention is testable. - Allocate the AOT image buffer at exactly the declared library size before reading (bounded before allocation, not concat-after-stream), keep the 256 MiB ceiling, and check cancellation between digest and stream chunks through a captured abort reason. - Accept APK paths containing spaces (covered by a fixture test) and escape the ABI pattern's dash for v-mode regex compatibility. Also add tests for cleanup retention with close retry and for cancellation before any snapshot root is created. * docs(flutter): cover the Italian README with the Flutter row The Italian translation landed between the merged feature PRs; its table now carries every merged row plus this one, keeping its documentation-link set identical to README.md. * fix(flutter): make the ABI pattern portable for v-mode validators Use a hex-escaped hyphen in the ABI character class, matching the form the ADB portability fix uses, so clients compiling advertised schemas in unicodeSets mode accept the inspect_dart_aot input schema. * refactor(flutter): reuse shared harnesses and deduplicate the vertical The duplicate-detection gate (jscpd, 2.5% threshold) tipped to 2.51% with the flutter files added. Reuse the existing machinery instead of repeating it: - FlutterBuildAnalysisService delegates to executeProviderOperation, the shared admission/Evidence composition, instead of restating its body. - The CLI registers both commands from one table-driven spec. - Boundary tests use the shared serveCli and assertAdvertisedMcpContracts harnesses rather than hand-rolled equivalents. - The provider test consolidates the four payload-shape cases into one table-driven case; the service test uses one admission table and distinct phrasings. Duplicate lines fall from 2.51% to 2.48%, back under the gate. * fix(flutter): address review findings from real-app verification CassieFox23 verified the PR against three real F-Droid Flutter apps and a control app, and found five gaps. All are fixed and re-verified against cone (info.tangential.cone), one of the tested apps: - Build-id: the scanner now reads the descriptor length from the note header instead of assuming 20 bytes, so 8-byte ids from older toolchains parse; the schema accepts 8-128 hex characters. Verified against cone's readelf-reported ids on all three ABIs. - Compiler line: the toolchain capture bound rises from 200 to 500 characters, keeping cone's 262-character clang line verbatim. - Zero-byte libapp.so: the extraction target is created before streaming, so an empty entry yields a clean unsupported_target refusal for inspect_dart_aot and elf_header_valid: false with partial coverage for identify_flutter_build, instead of a raw ENOENT with code UNKNOWN. - Truncated library: identify_flutter_build marks fewer than two snapshot sections as partial coverage with a limitation naming the count, and a missing ELF magic gets its own limitation. - Docs: the default ABI preference now matches the code exactly (arm64-v8a, x86_64, x86, armeabi-v7a, then the rest alphabetically). Also: every test that read the author's /tmp/opencode goldens now builds a self-contained synthetic ELF64 image with dynsym, four snapshot symbols, two snapshot sections, and a string pool, so the suite runs anywhere; the verify lane now requires a present hex build-id per ABI instead of accepting null. * fix(flutter): address the Codex review findings Eight findings, all applied: - P1: SnapshotHashScanner keeps candidates in a Set (no quadratic Array.includes) with a 64-distinct-hash cap, so a hostile payload of magic+hash records cannot stall the scan or bloat the response. - P1: projectStringPool stops admitting new distinct entries once each category reaches twice its projection bound and reports collectionCapped; allocation and sorting now track the response, not the payload. - P2: ZIP reader rejections during entry iteration or entry streaming map into the typed error algebra (cancellation vs input refusal) instead of escaping as unhandled exceptions in both operations. - P2: magic_valid now marks coverage only for the *Data symbols; a complete four-symbol image with valid data magics reports complete coverage (the instruction symbols intentionally lack the magic). - P2: the MCP server closes the Flutter provider during shutdown, so a retained snapshot root is retried and reported, not leaked. - P2: LabeledStringScanner commits matches only when a printable run is delimiter-terminated; the carry grows to 600 bytes so a 500-character toolchain line survives any chunk boundary untruncated. - P2: both Flutter tool effects declare writesFilesystem: true; the owned APK snapshot and extracted libapp.so are temporary writes. - P2: the verify lane asserts every ABI agrees on exactly one snapshot hash instead of discarding the computed set. * fix(flutter): address the second Codex review round Nine findings on the first-round fixes: - P1: identify_flutter_build enforces a cumulative extraction budget (768 MiB) across every library entry, not just per library, so an APK with many ABIs cannot stream an unbounded total. - P1: a single NUL-free printable run stops materializing at 1024 bytes and marks the pool capped, so an adversarial one-run section cannot allocate unbounded strings. - P1: ELF cString bounds the NUL-terminator scan to the name maximum plus one, so malformed symbol tables cannot scan the whole image per entry. - P2: mapReaderFailure distinguishes ArtifactReaderFailure texts — budget/limit rejections map to resource constraints, integrity failures (CRC/overlap/signature) to their own input refusal — instead of collapsing all reader failures into one message. - P2: the labeled-string carry is cleared when a delimiter commits a run, so consecutive toolchain lines are never concatenated. - P2: string-pool counts are renamed *_count_at_least and documented as lower bounds, because capped collection discards values beyond twice the projection bound. - P2: inspect_dart_aot marks hash disagreements, non-pair source counts, and candidate-cap saturation as partial coverage. - P2: only valid lowercase-hex hash records count as snapshot sources; complete magic records with non-hex fields do not inflate agreement. - P2: the per-entry size refusal in inspect_dart_aot moves after ABI selection, so an oversized unselected ABI no longer refuses a small request. * fix(flutter): address the third Codex review round Five findings: - P2: the owned snapshot revalidates the source after the copy (size, mtime, inode), so an in-place replacement or swap mid-copy refuses the observation instead of publishing mixed bytes. - P2: a failed ZipArtifactReader close retains the snapshot root for close() retry instead of silently swallowing the descriptor leak, in both operations. - P2: snapshot headers are parsed within their symbol bounds and a symbol smaller than the 16-byte header reports magic_valid false, so trailing-image bytes can no longer fake a valid data section. - P2: the verify lane counts provider.close() cleanup failures as lane failures instead of certifying a run with retained roots. - P2: LabeledStringScanner reports toolchain_capped when further distinct matching lines are observed past the 4-line projection; identify marks such results partial with a naming limitation. * fix(flutter): address rounds three and four of the Codex review Round three (one missed finding) plus round four (five findings): - P2: the GNU build-id note descriptor size is decoded from all four bytes of the little-endian field, rejecting nonzero high bytes instead of masking them to 16 bits. - P1: the Flutter MCP handlers route through createContractToolHandler and the session's analysisAdmission, so close_binary and snapshot operations drain in-flight Flutter calls instead of racing them. - P2: the owned-snapshot copy is a streamed, abort-checked write (no copyFile), so cancellation stops the write promptly while the source-identity revalidation (size, mtime, inode) still guards against mid-copy replacement. - P2: identify marks any snapshot_hash_sources value other than exactly two as partial, matching the inspection path's pair requirement. - P2: the verify lane refuses to run without --apk (exit 2) instead of printing a false-green pass, and the engine check requires every libapp-carrying ABI to have a present libflutter with a valid hex build-id rather than filtering missing engines away vacuously. * fix(flutter): address the fifth Codex review round Six findings plus the upstream duplication baseline: - P2: the flutter-provider kind reports available in the capability inventory, so binary_session no longer lists always-registered Flutter tools as provider_missing. - P2: printable runs that exceed the 1024-byte cap are discarded at their delimiter instead of emitting their truncated prefix as a fabricated observation. - P2: ELF symbols must fit inside the file-backed portion of their PT_LOAD segment (p_filesz), so addresses in gaps or BSS are refused instead of accepting unrelated bytes. - P2: the build-id carry spans the complete 80-byte maximum note (16-byte header + 64-byte descriptor) so chunk-boundary splits cannot drop the first byte. - P2: the labeled-string carry is sized for the toolchain pattern's true 605-byte maximum, not the previous 600. - P2: the AOT completeness predicate requires exactly two sources and one candidate unconditionally, so zero-source images with valid data magics report partial instead of complete. Also: the shared ELF fixture builder and snapshot-section helper moved to a dedicated FlutterTestFixtures module, eliminating cross-file test duplication; jscpd now passes at 2.50% while main sits at 2.52%. * fix(flutter): address the sixth Codex review round Five findings: - P2: snapshot-hash scanning is bounded to the exported data symbols' byte ranges, so decoy or stale magic/hash sequences elsewhere in the image cannot inflate source counts or contribute candidates. - P2: labeled-string runs longer than the carry bound are rejected instead of truncated, preventing a regex prefix from being published as a verbatim source string. - P2: the AOT image reread checks cancellation between file reads and before the synchronous parsing stage, so a cancelled caller cannot receive a success from a completed 256 MiB read. - P2: the verify lane requires exactly two snapshot sources per ABI, matching the provider's pair requirement, instead of accepting any count >= 2. - P2: ELF section and program header entry sizes are validated against the class-defined minimum widths before walking either table, so undersized entries cannot cause field reads to cross into neighboring bytes. * fix(flutter): address the seventh Codex review round Six findings: - P2: data-symbol ranges are deduplicated by offset and size, so aliased symbols mapping to the same bytes are scanned once and the result marks partial coverage with a limitation naming the shared range. - P2: openEntrySafely forwards return/throw to the underlying Readable's iterator, so an early for-await exit closes the ZIP entry stream and releases its producer instead of leaking a backpressured pipe. - P2: inspect_dart_aot now reports snapshot_hash_candidates and snapshot_hash_sources alongside snapshot_hash, so a null hash from conflicting evidence is distinguishable from missing hash data. - P2: overlong printable runs only set toolchain_capped when they begin with a recognized label prefix (Dart VM version: or Android (); other long strings such as license text are skipped without marking partial. - P2: readElfSymbol falls back to PT_DYNAMIC/DT_SYMTAB when section headers are absent (e_shnum 0, ET_DYN), so stripped Flutter libraries with exported snapshot symbols are no longer refused; the dynamic table is bounded by DT_STRSZ, DT_SYMENT, and the image end. - P2: the verify lane exercises inspect_dart_aot for every ABI carrying libapp.so, not just the preferred default. * fix(flutter): address the eighth Codex review round Five findings: - P2: each data-symbol range gets a fresh SnapshotHashScanner so the carry from one noncontiguous slice cannot fabricate a record spanning into the next; results are aggregated after scanning. - P2: dynsym name lookups are bounded to the linked string table — st_name offsets outside the table and terminators beyond it are rejected instead of reading unrelated payload bytes as symbol names, in both the section-header and PT_DYNAMIC paths. - P2: identify_flutter_build routes its reader.entries and reader.open calls through the same collectEntriesSafely and openEntrySafely wrappers as inspect_dart_aot, so malformed ZIPs surface as input refusals instead of AnalysisProtocolError. - P2: mapReaderFailure recognizes ArtifactReaderFailure texts carrying a cancelled reason, mapping them to AnalysisCancelledError instead of AnalysisInputError. - P2: the cumulative extraction cap is enforced inside the chunk loop, not after each library completes, so refusal happens before the remaining bytes are streamed and hashed. * fix(flutter): harden streaming, sizes, and cancellation after self-review Four issues found in a systematic self-audit across all bug classes from eight Codex rounds: - createReadStream in both the digest function and the owned-snapshot copy now receives the AbortSignal directly, so cancellation aborts the filesystem read at the source instead of waiting for the next chunk to arrive on a slow filesystem. - The owned snapshot enforces a 2 GiB APK-level size cap before any copy I/O, so a path pointing at an enormous file is refused before disk usage grows. - identify_flutter_build preflights the sum of declared entry sizes against the 768 MiB cumulative budget, refusing before any ZIP extraction when the declared total already exceeds it. - The LabeledStringScanner triple result() call is collapsed to a single invocation (the previous code called result() three times in the emission, relying on end() being idempotent). No behavior change for valid inputs; all 36 tests, check:fast, and the cone verify lane pass unchanged. * fix(flutter): require structurally parseable ELF headers for complete coverage CassieFox23's third corrupt-header case: an ELF with valid magic but zeroed section/program header offsets passes the 4-byte magic check and the byte scanner still finds hash patterns in the data sections, so identify_flutter_build reports complete coverage for a structurally broken file. The elf_header_valid check now also verifies beyond the magic: e_type must be a known value (ET_REL/ET_EXEC/ET_DYN), at least one of e_shoff/e_phoff must be non-zero, and at least one of e_shnum/e_phnum must be non-zero. A file with valid magic but zeroed structure reports partial coverage with the existing ELF-magic limitation. * fix(flutter): validate ELF and snapshot coverage Share bounded ELF header parsing between identification and inspection. Accumulate headers across ZIP chunks and validate table entry widths and extents before claiming complete identification coverage. Bind AOT hashes to the exported VM and isolate headers, validate stored snapshot lengths including the magic bytes, and preserve partial evidence for missing, malformed, aliased, or overlapping data headers. Preserve native stream cancellations and APK resource constraints across both operations. Bound snapshot copies while streaming and retain useful input-failure diagnostics and lower-bound count wording. Validation: 72 focused tests; check:fast; check including formatting; docs:check; test discovery; jscpd; real cone APK and non-Flutter control on Linux. Verified CLI/MCP parity for valid and corrupted ELF headers and AOT inspection across three ABIs. Native Windows and macOS were not run. * fix(flutter): unify artifact ownership and bound ELF evidence * fix(flutter): preserve ELF provenance and partial coverage Read GNU build IDs only from bounded ELF note ranges and retain conflicting candidates. Preserve source versus owned storage failures and honor cancellation through parsing and final cleanup. Separate snapshot structure from string-pool coverage. Verify every Gallery ABI with digest-bound engine labels and URI counts, and document the supported snapshot-kind layout. --------- Co-authored-by: REA Contributor <contributor@rea.tools> Co-authored-by: morluto <76467478+morluto@users.noreply.github.com> |