mirror of
https://github.com/morluto/rea.git
synced 2026-11-08 07:46:28 +00:00
* docs: organize canonical documentation layout (URGENTE) Group all existing authored guides by domain and ownership, expose complete navigation, and preserve their public routes. Update physical path consumers and release configuration while retaining historical records. Document domain-based specs and ADR slugs without global numbering. Co-Authored-By: Codex <noreply@openai.com> * fix(docs): honor home layout in all-page route verification * style: format reviewed source changes * fix(docs): preserve routes and navigation cache inputs * fix: repair the static-check failures on the default branch (#2895) * fix: repair the static-check failures on the default branch The default branch fails its own required `Static checks` job three ways, so every pull request reports that job as failed regardless of its own change. - `npm run format:check` reports `src/domain/functionComparison.test.ts` and `src/ida/IdaOperationRunner.ts`. - `npm run lint` reports a duplicate `import { join } from "node:path"` in `tests/boundary/mcp/ghidraInventoryMcp.test.ts` (lines 4 and 17). - `npm run typecheck` reports `format: "devin"` missing from the client format union in `src/application/SupportedClients.ts`, and an unchecked index in `tests/boundary/cli/clientProfileLifecycle.test.ts`, whose pair array is inferred as `string[][]`. Apply the pinned formatter, drop the duplicate import, list the `"devin"` format that its own definition already uses, and mark the pair array `as const`. Validation: `npm run check:fast` and `npm run format:check` pass. * fix(setup): keep Devin on the JSON configuration dialect --------- Co-authored-by: 吴佳浩 <wujiahao@supremind.com> Co-authored-by: morluto <76467478+morluto@users.noreply.github.com> * fix(docs): update links after guide relocation * docs: correct verified documentation route counts * fix(docs): update browser capture guide link after move (#2820) Refresh gettext protected-link comments alongside the authored href. Co-authored-by: Codex <noreply@openai.com> * fix(docs): include http3 lab in stable routes * fix(docs): preserve historical release evidence and links * fix(ghidra): restore Windows verifier identity import * docs(wakaru): preserve pinned upstream policy link * docs(index): retain recently added guide links * docs(mcp): preserve parent retention clarification * fix(cli): preserve standalone import guidance * fix(test): preserve translated guide link drift coverage --------- Co-authored-by: Matheus Breguêz <matbrgz@gmail.com> Co-authored-by: Codex <noreply@openai.com> Co-authored-by: morluto <17176810+morluto@users.noreply.github.com> Co-authored-by: 吴佳浩Alben <35291372+XiaoBinGan@users.noreply.github.com> Co-authored-by: 吴佳浩 <wujiahao@supremind.com>
336 lines
21 KiB
Markdown
336 lines
21 KiB
Markdown
# Electron file-page observation
|
|
|
|
REA can attach to an Electron/Chromium CDP endpoint and inspect existing `file://` renderer pages without evaluating JavaScript or invoking Electron APIs. The request supplies the endpoint directly; REA accepts only literal-port loopback HTTP endpoints. Selecting an endpoint exposes every Electron target it serves, including local file paths and page metadata.
|
|
|
|
Disconnecting the inspection's page session can clear caller-owned
|
|
`navigator.platform` emulation inside Chromium. REA cannot restore another
|
|
client's complete user-agent/client-hint configuration. Use a disposable target
|
|
or let the owning client reapply its configuration; see the shared
|
|
[CDP state limitation](../web/browser-observation.md#provider-authorities).
|
|
|
|
This passive runtime surface is distinct from the target-free static
|
|
[`analyze_javascript_application`](javascript-artifact-reconstruction.md)
|
|
workflow. Static analysis reads a local ASAR or extracted directory without
|
|
executing application code. Passive observation inspects an existing Electron
|
|
page. The results keep static inferences and runtime observations distinct.
|
|
Packaged fuse state is a separate static read:
|
|
[`inspect_electron_fuses`](../../electron-fuses.md) reports whether this binary will
|
|
honor Inspector flags, `ELECTRON_RUN_AS_NODE`, embedded ASAR integrity, and
|
|
`app.asar`-only loading.
|
|
Use the separate
|
|
[`reconcile_javascript_runtime`](javascript-runtime-reconciliation.md) workflow
|
|
when both Evidence sets already exist.
|
|
|
|
## Target boundary
|
|
|
|
The request supplies a literal-port loopback endpoint and, for inspection, a
|
|
target ID. REA accepts local hostless `file://` URLs that resolve to regular
|
|
files, including pages a packaged application serves from inside `app.asar`.
|
|
For those, the first `.asar` path component that is a regular file is the
|
|
archive, the remainder must name a regular file in its header, and the reported
|
|
path is the canonical archive path joined with that member. Remote hosts,
|
|
encoded separators in the pathname, ASAR directories or links, and nonexistent paths are
|
|
rejected. The endpoint exposes every eligible target and its local path
|
|
metadata. Query strings and fragments do not participate in filesystem path
|
|
admission, including their encoded separator text. Listing and inspection
|
|
Evidence both retain the observed browser product/revision; host platform and
|
|
architecture remain explicitly unknown.
|
|
|
|
Cancellation stops further file authorization after an already-started filesystem
|
|
operation settles. ASAR descriptor-close failures remain typed incomplete cleanup
|
|
with an exact retryable owner; they are not treated as a missing or unauthorized
|
|
page. A later authorization and provider shutdown retry retained descriptors
|
|
before reporting their release. Failed listing retains its already-authorized
|
|
target prefix and explicit remaining-target uncertainty through cleanup errors
|
|
and exact-owner retries.
|
|
|
|
## Workflow
|
|
|
|
`list_electron_targets` returns every eligible target served by the supplied endpoint in one inline array.
|
|
|
|
For the MCP follow-up call, pass the same literal-loopback endpoint and the selected target ID to `inspect_electron_page`. REA rediscovers targets and validates the ID against the live endpoint before inspection.
|
|
|
|
```bash
|
|
rea list-electron-targets http://127.0.0.1:9223 --json
|
|
rea inspect-electron-page http://127.0.0.1:9223 TARGET_ID \
|
|
--observation-ms 100 --json
|
|
```
|
|
|
|
Script content is excluded by default. Setting `include_script_sources` in the
|
|
request returns the selected target's script sources:
|
|
|
|
```bash
|
|
rea inspect-electron-page http://127.0.0.1:9223 TARGET_ID \
|
|
--include-script-sources \
|
|
--json
|
|
```
|
|
|
|
The normalized result contains canonical local paths, frame and DOM
|
|
structure, resource metadata, stable script/resource identities, explicit
|
|
completeness, and content-addressed script artifacts. Resource sizes are unknown
|
|
when omitted or invalid; supplied invalid sizes also produce an explicit
|
|
completeness diagnostic.
|
|
|
|
DOM node indices retain their snapshot positions. Invalid rows are omitted with
|
|
completeness diagnostics, so `total_nodes` can exceed the retained node count
|
|
and a parent index can refer to an omitted source node.
|
|
|
|
`dom.node_value_length_unit` is `utf8-bytes`: each node's `node_value_length`
|
|
counts the bytes needed to encode its value as UTF-8, not characters. Web
|
|
observations instead declare `utf16-code-units`. These existing measurements
|
|
are preserved; the unit is reported once for the whole DOM, without retaining
|
|
text values. Historical captures without the unit leave it unknown; reading
|
|
them does not infer a unit or convert their stored lengths.
|
|
|
|
Script metadata retains its execution-context frame ID when CDP supplies one and
|
|
records admission separately in `frame_scope`. An `authorized` scope refers to
|
|
the observed document generation; `unknown` retains the known frame ID without
|
|
claiming authorization and makes script coverage attach-limited. Historical
|
|
captures may omit this field, which leaves that admission fact unknown.
|
|
Scripts from document generations whose frames were explicitly excluded by
|
|
local-file policy are omitted before script-source reads. Frame identity can
|
|
come from the script event's own auxiliary data or its observed execution
|
|
context; contradictory identities are not aligned.
|
|
`frame_claims.script_event_frame_id` and
|
|
`frame_claims.execution_context_frame_id` preserve the actual observed claims
|
|
from those two sources. Both remain available when their disagreement leaves
|
|
the resolved `frame_id` null and `frame_scope` unknown. An omitted claim is
|
|
unobserved, not proof that a frame was absent.
|
|
Frames describe the current inventory; script observations can retain earlier
|
|
authorized frames that have since detached.
|
|
|
|
`has_source_url` preserves CDP's sourceURL declaration flag as true, false, or
|
|
unknown (`null`, or omitted in historical captures). A script's canonical
|
|
`file_path` is the admitted local-file URL or supplied sourceURL label. It does
|
|
not verify that the runtime bytes came from that disk file; captured source
|
|
bytes have their own digest. `reported_url` retains the original URL or label
|
|
before canonical path admission. Labels remain distinct from filesystem provenance.
|
|
The capture also
|
|
inventories worker, service-worker, and shared-worker targets with
|
|
validated opener-target and parent-frame IDs. Worker discovery uses passive
|
|
target metadata; REA does not attach to or execute code in those targets.
|
|
Script metadata uses the shared 8 MiB JSON-byte allowance for each retained
|
|
representation: incoming drafts and normalized capture metadata. Admission
|
|
counts escaped strings, frame claims, and provenance fields before retaining
|
|
them or serializing metadata for a digest. Exhaustion preserves earlier records
|
|
and reports counted `resource_budget_exhausted` script omissions; a later
|
|
smaller record may still fit. This allowance adds no script-count cap and is
|
|
separate from script-source bytes.
|
|
Script-source collection retains at most 32 MiB of UTF-8 text across scripts
|
|
and shares a 20-second collection deadline. Omitted sources retain their script
|
|
metadata and an explicit reason; later smaller sources may still fit after an
|
|
oversized source is omitted. The separate 64 MiB CDP response limit bounds one
|
|
wire message before retention admission, rather than total transient memory.
|
|
The source budget adds no script-count cap. Like every
|
|
target, frame, script, and resource, a worker URL must resolve to a local
|
|
file before it is retained. Relationship IDs improve
|
|
attribution but do not prove which static module started a worker or that its
|
|
work completed.
|
|
Missing opener and parent relationships leave worker ownership unknown. Such
|
|
targets are withheld with an `unattributed_target` exclusion and unavailable
|
|
`workers` coverage, even when their URL names a local file. Explicit foreign
|
|
relationships remain `out_of_target_scope`; malformed supplied relationship IDs
|
|
are `invalid_protocol_value`.
|
|
|
|
Inspection does not retain DOM values, execute renderer code, navigate, click,
|
|
invoke Electron IPC, close a target, or terminate the application.
|
|
|
|
## Active Electron scenarios
|
|
|
|
The scenario request supplies the Electron executable and application entry
|
|
point, plus any arguments and actions. The application root defaults to the
|
|
entry point's parent directory and can be supplied when the app uses a different
|
|
root. REA launches that executable through
|
|
the official Playwright Electron API and owns its lifetime. It accepts click/wait actions,
|
|
window-targeted renderer reload/crash actions, and synthetic `open-url` or
|
|
`second-instance` deep-link delivery. It records capture-scoped window and
|
|
WebContents identities, process metrics, and IPC channel and value-shape metadata.
|
|
Payload values are not retained.
|
|
|
|
For the exact Playwright launch sequence, the active hook removes only REA's
|
|
bootstrap arguments from the application-visible `process.argv`. The vector keeps
|
|
the selected entry point and every caller argument, in order, including repeated
|
|
hook paths, `.asar` filenames, and debug-like options. The private launch handoff
|
|
is consumed before application code runs. If the observed sequence cannot be
|
|
matched exactly, argv stays unchanged and capture coverage reports `hook_conflict`;
|
|
REA does not guess an entry point or remove arguments by filename patterns.
|
|
|
|
`window_index` selects from Playwright's current Page list at the start of each action. REA resolves that Page to its corresponding BrowserWindow once; the action's `target` uses the BrowserWindow's capture-scoped `window_id`, matching the window inventory. Reordering either enumeration after selection does not retarget the action. Handle cleanup failures retain the completed action records and remain owned for provider cleanup retry.
|
|
|
|
Run the real fixture verifier with an operator-provided Electron runtime:
|
|
|
|
```bash
|
|
REA_ELECTRON_EXECUTABLE=/absolute/path/to/electron npm run verify:electron
|
|
```
|
|
|
|
This operation actively launches and interacts with the selected target. It is
|
|
not a passive CDP observation. The owned process keeps normal host filesystem
|
|
and network privileges, so lifecycle ownership does not make it a sandbox.
|
|
|
|
The CLI and MCP surfaces accept the same schema. For a JSON request file:
|
|
|
|
```bash
|
|
rea capture-electron-scenario scenario.json --json
|
|
```
|
|
|
|
The result records one status and target per declared action; actions after a
|
|
failed or cancelled action are not run and are recorded as `cancelled` with a
|
|
not-executed error. It also records correlated app/window/WebContents,
|
|
preload, session, navigation, shell, permission, popup, download, protocol,
|
|
native-addon, process, and IPC timeline events. IPC channels and argument-shape
|
|
metadata retain their observed lengths; payload values are never retained.
|
|
Renderer crash/restart and deep-link actions are synthetic
|
|
scenario controls; their attempted and observed outcomes remain in the
|
|
timeline. The active hook blocks and records the shell methods listed below
|
|
and selected navigation, permission, download, popup, updater, and
|
|
OS-integration APIs. These method patches do not contain direct filesystem,
|
|
network, native-addon, or other unpatched target effects. The timeline
|
|
is explicitly partial when attachment starts after application activity. The
|
|
startup, including the initial state snapshot, has a 60-second deadline.
|
|
Actions wait until completion or request cancellation; a click whose target
|
|
never appears, or a `wait` without `window_index` in an app that opens no
|
|
window, remains pending until cancelled. After cancellation, the final state
|
|
read has 5 seconds before REA keeps the initial snapshot instead; the
|
|
cancellation keeps the caller's reason and records that expiry in its
|
|
diagnostics.
|
|
The shell policy is owned by `blockedShellResults` in
|
|
`scripts/electron-active-hook-boundaries.cjs`. Its synchronous blocks are:
|
|
`showItemInFolder` returns `undefined` and `writeShortcutLink` returns `false`.
|
|
Their `shell-attempt` events record the blocked phase and argument shapes;
|
|
neither result means the requested host operation occurred. The existing
|
|
`openExternal`, `openPath`, and `trashItem` blocks reject their promises.
|
|
The `trashItem` block prevents that call from reaching the OS trash operation;
|
|
it does not intercept other ways the application can remove files.
|
|
Process rejection and uncaught-exception events are observed at dispatch without
|
|
adding exception handlers. The runtime and application retain their warning,
|
|
handler, and fatal-exit policies, including for rejected shell-block promises.
|
|
CLI SIGINT/SIGTERM requests cancellation and awaits owned-process cleanup.
|
|
Window IDs, URLs, and cached titles come from one main-process Electron snapshot,
|
|
so a crashed renderer does not require a successful renderer-side title query.
|
|
Window instrumentation uses Electron's window-created event, including runtimes
|
|
with a read-only `BrowserWindow` export. Preload configuration comes from the
|
|
window's runtime metadata, including Electron's optional internal preload
|
|
descriptor when web preferences omit it. This descriptor is verified on
|
|
Electron 44.7.0; runtimes that expose neither representation retain an unavailable
|
|
path. A configured path or working IPC handler does not prove preload execution.
|
|
If cleanup cannot be verified, the error retains collected observations in
|
|
`details.partial_observation` and marks their application cleanup `unverified`.
|
|
Cleanup uncertainty does not authorize signaling unrelated processes.
|
|
|
|
On Linux, Chromium-based processes can overwrite the initial environment memory
|
|
exposed through `/proc/<pid>/environ`. The environment may be readable while
|
|
`REA_PROCESS_RUN_ID` is absent, even if the application still sees it through
|
|
`process.env`. REA reports the missing marker and leaves lineage ownership and
|
|
cleanup unverified; the PID, executable, or parent relationship cannot replace
|
|
that ownership proof. This limitation can affect owned Electron captures on
|
|
native Linux as well as WSL. It does not establish that all Electron versions or
|
|
Linux environments behave the same way.
|
|
|
|
Main-process IPC recording keeps original-function removal for `on` and `once`
|
|
registrations and records a fired `once` callback once. Native Node.js owns
|
|
`once` registration, firing and callable dispatch as well as removal order and
|
|
traversal, including listeners registered by removal observers;
|
|
the hook translates only its own recording wrappers at the removal boundary.
|
|
Wrapper metadata uses weak keys and does not retain removed registrations.
|
|
Wrapper metadata operations, private collection methods and traversal constructors are captured
|
|
before target startup; later replacement of their global bindings does not
|
|
change listener registration or removal.
|
|
|
|
Native runtimes differ in whether `once` consults callback `.call` or `.apply`.
|
|
The hook observes actual callback entry without imposing another runtime's
|
|
property reads or argument-list representation. Application methods selected by
|
|
native dispatch keep their original receiver and argument-list object; opaque
|
|
methods produce a `custom-call` or `custom-apply` observation gap when callback
|
|
execution cannot be established.
|
|
|
|
If native wrapper creation or registration-method lookup could expose private
|
|
instrumentation through an application getter, setter or Proxy, registration
|
|
stays native and unwrapped. The `once-metadata` observation gap identifies that
|
|
unobserved registration attempt; it does not prove registration or delivery. The original callback,
|
|
metadata writes and native method receivers are preserved, including when
|
|
registration fails. Later registrations resume normal observation. Non-callable
|
|
`call`/`apply` properties produce failed observation gaps when native dispatch reads
|
|
them; callback entry is not fabricated.
|
|
|
|
An `on` listener's application-defined `.apply` receives the original argument-list
|
|
object unchanged. Borrowing the exposed method preserves its dynamic receiver.
|
|
Because that method owns argument interpretation and may not
|
|
invoke the callback, the hook records a bounded `ipc-observation-gap` timeline
|
|
entry with the registered channel rather than an IPC invocation. Its empty
|
|
`argument_shapes` means unobserved arguments, not a zero-argument callback;
|
|
callback execution and result remain unknown. Coverage and retention include
|
|
these gaps even when their individual records are dropped. Normal native
|
|
dispatch continues to record actual invocation frames. Evidence snapshots copy
|
|
retained arrays and counters without invoking application iterators or Array
|
|
species.
|
|
|
|
Listener-chain recognition distinguishes owned, unowned and unknown metadata.
|
|
Data links may be inherited; absence is established only after completing the
|
|
bounded prototype lookup. Exhausting either walk, encountering a getter or seeing a Proxy leaves
|
|
ownership unknown. The hook preserves native registration without adding another
|
|
recording wrapper and retains a `listener-chain` observation gap after successful
|
|
registration. This gap does not prove IPC delivery. An existing inner recording
|
|
wrapper can still report its actual invocation; otherwise callback activity
|
|
remains unobserved. Setter-only properties have a known undefined read.
|
|
Data-descriptor parsing is shared with removal normalization.
|
|
|
|
This does not establish complete JavaScript function introspection equivalence.
|
|
Caller-defined `.listener` properties can change the identity exposed by
|
|
`listeners()` and meta-events; accessors and Proxy metadata are not followed for
|
|
wrapper recognition. Re-registering a raw recording wrapper on another channel
|
|
retains the original registration's channel attribution.
|
|
Mutating `.listener` on a raw recording wrapper can also prevent removal through
|
|
application decorators that bypass the hook's removal methods. When a removal
|
|
request reaches the hook's methods and its registration order is known, private
|
|
callback ownership preserves removal by the original function and the newest
|
|
matching registration when public metadata is changed or deleted. Native removal
|
|
still determines getter execution, exceptions and removal meta-events.
|
|
|
|
If registration or removal methods differ from the Node methods captured at hook
|
|
initialization, the hook leaves listener callbacks unwrapped and records a
|
|
`listener-registration` observation gap. The application keeps its original
|
|
callback and receiver; callback execution remains unobserved. This comparison
|
|
assumes Node's EventEmitter prototype has not itself been globally decorated
|
|
before hook initialization. The provider loads the hook before the application
|
|
and excludes `NODE_OPTIONS` from the child environment. It preserves selected
|
|
`DISPLAY`, `XAUTHORITY` and `WAYLAND_DISPLAY` values, including the caller-selected
|
|
X11 authorization-file path.
|
|
|
|
An opaque later listener or unsupported emitter state can prevent proof of
|
|
removal order. The hook then delegates removal
|
|
to the native method and records a `listener-removal` gap rather than claiming
|
|
which registration was removed. These gaps are distinct from dropped records
|
|
and do not establish IPC delivery or successful removal.
|
|
|
|
### Target fuse prerequisites
|
|
|
|
Active scenario capture requires that the target Electron executable honors Node CLI inspector flags (`--inspect` or `--remote-debugging-port`) so that Playwright can establish its DevTools protocol session.
|
|
|
|
When an Electron executable explicitly disables the `EnableNodeCliInspectArguments` fuse (or removes the capability), `capture-electron-scenario` fails immediately before process launch with an `AnalysisUnsupportedTargetError` ("unsupported_target") rather than waiting for the startup deadline. For targets with disabled inspector fuses, use passive CDP inspection via `inspect_electron_page` or run against an unrestricted development binary with the extracted `app.asar`.
|
|
|
|
The real verifier exercises compiled CLI and stdio MCP discovery, page/script
|
|
inspection, static/runtime reconciliation, active observation, and CLI SIGTERM
|
|
cleanup before checking the IPC, renderer crash/reload, utility/child-process,
|
|
deep-link, and blocked-effect fixture. A cleanup error keeps the full lane failed
|
|
even when the retained capture contains useful observations.
|
|
|
|
Active Electron Evidence can also be supplied to
|
|
[`reconcile_javascript_runtime`](javascript-runtime-reconciliation.md). That
|
|
projection is intentionally target-only and partial: it binds the selected
|
|
application path and capture outcome to the static graph, while frames, scripts,
|
|
workers, and execution claims remain unavailable until a separate passive runtime
|
|
capture provides them.
|
|
|
|
ASAR observation reads the header through one open file handle and validates its
|
|
declared lengths against the archive size. Header/index admission is the same
|
|
budget used by static ASAR reads: 16 MiB / 16777216 bytes by default, seeded by
|
|
`REA_ASAR_MAX_HEADER_BYTES` (at most 256 MiB). In MCP,
|
|
`update_analysis_settings` can override `asar.max_header_bytes` for new
|
|
operations without restarting REA. Cached headers are checked against each
|
|
operation's admission, including after a decrease. See the
|
|
[settings design](../../design/limits-and-recovery.md#failure-and-retry).
|
|
An oversized header is
|
|
not parsed; selecting a member path does not bypass admission, and the page
|
|
stays unauthorized. The header cache separately retains at most that many bytes
|
|
of already-admitted encoded headers across eight archives. Evicting that cache does not raise admission.
|