Files
reverse-engineering-anything/docs/packet-captures.md
T
92a9591a41 refactor(pcap): give TShark selection and version admission one owner (#3407)
* refactor(pcap): give TShark selection and version admission one owner

The pcap and packet-capture families wrapped the same caller-supplied
TShark with diverging rules. inspect_pcap_* accepted an absolute
REA_TSHARK_COMMAND or tshark from PATH, while inspect_packet_* and the
server availability policy required an absolute REA_TSHARK_COMMAND and
parsed the version banner with a separate regex.

PcapConfiguration now owns the rule: selectWiresharkTool selects an
absolute environment path or the bare name on PATH and refuses relative
values; locateWiresharkTool resolves a PATH selection to the first
absolute PATH entry holding an executable file; parseWiresharkVersion
admits Wireshark 4.x banners for both providers. TsharkCaptureProvider
and the session availability policy consume these facts instead of
reinterpreting REA_TSHARK_COMMAND, so all four tools accept and refuse
the same configurations. inspect_packet_* still resolves, hashes and
rechecks the located executable, and records selection_source in its
tool Evidence.

Host support and resource bounds are unchanged; unifying them is the
next milestone in #3403.

Refs #3403

* fix(pcap): execute the TShark the shared locator selects

resolvePcapTools still returned bare tool names for PATH selections, so
the operating system could run a tool from a relative PATH entry while
the packet tools skipped that entry and selected a different install.

On POSIX hosts, resolve PATH selections through locateWiresharkTool and
run that absolute executable, failing with the existing tshark_missing or
capinfos_missing code when no absolute PATH entry holds the tool. Both
selections are validated before PATH is consulted, so a relative
environment value still reports its own configuration error. Windows
keeps PATH/PATHEXT resolution in the process launcher.

Refs #3403

* chore: restore main's copies of files the merge hook reformatted

The pre-commit oxlint --fix touched two files this PR does not change; keep them identical to main.

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

* fix(pcap): share selected tool identity verification

* perf(pcap): retain compact executable identity

* fix(pcap): report missing selected tools clearly

* fix(pcap): read historical capture evidence

* fix(pcap): preserve executable read failures

* test(pcap): cover version refusal and packet-tool availability selection

Address review follow-ups on the shared TShark owner:

- Send a 3.x TShark banner and a non-Wireshark banner through the packet
  provider, pinning the capability-unavailable reason for each and that
  only the version probe ran before refusal.
- Cover the session availability rule for inspect_packet_capture over
  MCP: a PATH-only install and an absolute REA_TSHARK_COMMAND are
  available on Linux, a relative REA_TSHARK_COMMAND is refused even when
  PATH holds TShark, and no absolute PATH entry reports provider_missing.
- Rename the locator test's missing absolute PATH entry so it no longer
  shadows node:path relative.

Refs #3403

* fix(pcap): name the configured path when a selected tool is missing

Resolving the selected executable before launch replaced the runner's
"Cannot run selected tool <path>" refusal with a message that kept the
configured path only in diagnostics, so the public CLI no longer told the
caller which REA_TSHARK_COMMAND or REA_CAPINFOS_COMMAND value to fix and
the pcap CLI boundary test failed. Include the configured command in the
capability-unavailable message and assert it there.

Refs #3403

* test(captures): give the late-cancellation fixture a TShark selection source

The service fixture added on main in 719d56ebf predates the required tool.selection_source field, so its report failed output validation before reaching the late-cancellation path the test covers.

Refs #3403

* fix(pcap): report a probe's pending cleanup when a post-run check fails

When a tool probe returned with its owned process still awaiting cleanup,
a failed post-run executable re-read or identity check threw before run()
reported that cleanup. Probes have no snapshot to carry it, so callers
saw an ordinary error with cleanupIncomplete false while the provider
kept the process and refused the next request.

After the command returns, combine such a failure with the probe's
ProviderCleanupError (cancellation and other command failures keep their
own cleanup outcome). Classify request failures through one
analysisFailure owner shared by run() and executeAdmitted(), and let the
availability probe rethrow any cleanup-incomplete failure instead of
reporting the tool unavailable.

Refs #3403

* fix(pcap): keep a failed executable re-read's cleanup live with the probe's

The combined post-run failure was bound only to the probe's pending
process cleanup. When the executable re-read had also failed to close its
file, the classified failure owned that cleanup, but the combined error's
live cleanupResources omitted the file and could clear cleanupIncomplete
once the process stopped. Bind both the classified failure and the probe
cleanup as dependencies.

Refs #3403

---------

Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-23 02:34:14 +08:00

11 KiB
Raw Blame History

Recorded packet capture inspection

inspect_packet_capture answers which selected protocol fields TShark observes in an existing capture. Network, USB or Bluetooth recordings can use the same field/filter interface when the installed TShark supports their encapsulation and dissectors. This tool does not acquire traffic, open an interface or control a device. HAR/SAZ/mitmproxy web-capture workflows remain separate.

Supply an existing TShark 4.x installation on Linux, with util-linux prlimit available at /usr/bin/prlimit. Override its absolute path with REA_TSHARK_PRLIMIT_COMMAND if required. Unsupported hosts, including macOS and Windows, are refused before inspection because this integration requires an enforced address-space limit.

TShark selection follows the same rule as inspect_pcap_capture: an absolute REA_TSHARK_COMMAND, otherwise tshark from the first absolute PATH entry that holds an executable file. A relative REA_TSHARK_COMMAND is refused rather than looked up. Either way the selected executable is resolved, hashed and rechecked after each run, and Evidence records its selection_source (environment or path):

export REA_TSHARK_COMMAND=/absolute/path/to/tshark   # optional when tshark is on PATH
rea inspect-packet-capture /artifacts/device.pcapng \
  --fields ip.addr --fields udp.port --fields data.data \
  --display_filter 'udp' --format json

The corresponding MCP call is:

{
  "name": "inspect_packet_capture",
  "arguments": {
    "path": "/artifacts/device.pcapng",
    "fields": ["ip.addr", "udp.port", "data.data"],
    "display_filter": "udp"
  }
}

frame.number is always included. Repeated values are ordered string arrays, for example "ip.addr": ["192.0.2.1", "192.0.2.2"]. Missing fields remain absent; they are not converted to zero or empty arrays. Export-only index/score metadata is discarded. No payload bytes are automatically added; select a supported byte field such as data.data or @data when needed.

Selectors are limited before argv expansion: at most 1,024 fields and 1,024 decode_as rules, 8,192 UTF-16 code units per selector, and 32 KiB total encoded selector arguments (UTF-8 text, terminators, option names and argv pointers). This leaves headroom below Linux's execve argument/environment limits; an unusually large inherited environment can still prevent launching.

Output is paged after filtering: match_limit defaults to 50 and match_offset to 0. The result retains original frame numbers and reports output_window with the matching count within the input read scope, returned count and has_more. Request later pages with the offset, or explicit match_limit: null (CLI --all_matches) for all remaining matches. Paging does not shorten input reading or change two-pass reassembly. Producer output must still fit its resource limit; use selected fields, a display filter or an input-packet limit when dissection output is too large. Each call checks the recording's current digest, so pages from different source digests must not be combined.

For a question about a known input prefix, select packet_limit for that prefix, the fields needed for the answer, and a matching output window. For example, a question about the first twelve PostgreSQL TCP packets within original frames 1–2,000 can use fields tcp.srcport, tcp.dstport, tcp.flags, filter tcp.port == 5432, packet_limit: 2000 and match_limit: 12. The matching count then describes that input prefix. A filter containing frame.number <= 2000 without an input limit still reads the entire capture; returning twelve rows does not limit producer output. These controls answer a bounded question and must not be used to infer whole-capture absence.

Discover unfamiliar field names with inspect_packet_fields, using an exact protocol and a narrow query. If the installed names are already established, direct field selection avoids an additional glossary probe. Count discovery and any retries when comparing investigation cost; a small page alone does not establish a faster workflow.

Additional selections:

  • display_filter: Wireshark display-filter syntax; omitted means no filter.
  • decode_as: explicit dissector overrides, such as ["udp.port==5555,dns"]. Repeat --decode_as in the CLI for multiple overrides.
  • packet_limit: optional maximum input packets read before filtering. A limit of 1 can therefore yield zero matching packets even when later frames match. Omitted means read to EOF within resource limits. The report records to-eof or up-to-input-packet-limit, never inferring total capture size from the returned packet count.
  • two_pass: defaults to true for fields that need later packets and reassembly dependencies. MCP may set false; CLI uses --no-two_pass for a single pass.

See the TShark manual for field names, display filters, decode-as syntax and pass semantics. Invalid fields or filters return producer diagnostics. An empty result does not prove absence of traffic outside the selected recording, filter or read range. Dissector results are observations, not independently verified device behavior.

Evidence includes source path/digest/size, exact selections, typed packet fields, configured and resolved executable paths/digests/full version banner, arguments and stderr. Warnings remain visible even on exit zero. tool.version_diagnostics also retains warnings from the separate version probe. Post-run identity failures identify the affected private copy, executable or limiter; resource-signal errors retain the signal and effective ceilings. Two-pass mode cannot recover missing capture bytes.

Each call uses a private read-only capture copy and an empty personal configuration directory (WIRESHARK_CONFIG_DIR), with name resolution disabled. The executable, resource limiter and private input copy are rechecked after execution. Owned processes, file descriptors and private files are cleaned up; failed cleanup retains ownership and completed observations for retry. The one-shot CLI awaits a cleanup retry while preserving the original failure and completed Evidence. Completed observations remain in the MCP session after cancellation or cleanup failure; bounded post-run integrity checks finish before honoring a late abort. Source files are never passed to TShark. Installed libraries, global configuration and plugins remain trusted, are not fully fingerprinted, and can influence dissection. This is not an OS sandbox.

Input is limited to 32 MiB before reading, each command to 30 seconds, and combined stdout/stderr to 4 MiB during collection. Output overflow fails instead of returning truncated success. Before execution, prlimit enforces 1 GiB address space per process, 64 MiB per file, 30 CPU seconds and no core dumps. Stricter inherited soft/hard limits are preserved; Evidence records the effective ceilings. Both limits are set before execution, so dissector allocations and compressed-capture expansion cannot exceed the per-process address-space cap. These are not aggregate process-memory or filesystem quotas. The selected limiter is trusted; its path, digest and arguments are retained in Evidence. No arbitrary process flags are accepted.

Verification

npm run build:cached
REA_TSHARK_COMMAND=/absolute/path/to/tshark node scripts/verify-packet-capture.mjs

The verifier generates its own Ethernet/IPv4/UDP/DNS and Bluetooth HCI PCAPs. It exercises real compiled CLI/MCP parity, advertised schemas, repeated/missing fields, filtering, decode-as, both pass modes, a packet limit before matching frames, Evidence persistence, failure diagnostics, source identity and cleanup. An optional positional REA entrypoint verifies an installed package.

The real-tool lane requires Linux and TShark 4.x. A separate kernel-limit regression checks the inherited soft/hard limits and verifies an oversized allocation is refused before it can consume memory. The Ubuntu workflow uses its distribution package; check its outcome before claiming that build verified. The earlier macOS check does not establish the current bounded integration, which deliberately refuses unsupported hosts. Other formats (including PCAPNG), USB, BLE ATT and actual physical capture are not established by these fixtures.

Each provider admits one inspection at a time, including acquisition and cleanup. An occupied provider returns a retryable capacity error before acquiring another file or worker. Closing the provider stops new admission and drains accepted work; failed cleanup remains owned and can be retried.

Discover protocols and field names

inspect_packet_fields / inspect-packet-fields queries the installed TShark registration glossary before selecting fields for capture inspection:

rea inspect-packet-fields --query usb
rea inspect-packet-fields --kind fields --protocol usb --query idVendor
rea inspect-packet-fields --kind fields --protocol udp --all

kind defaults to protocols; fields selects field definitions. protocol selects an exact protocol abbreviation (the protocol itself or its child fields). query is a case-insensitive literal substring in name, abbreviation or field description. offset skips matching records; limit defaults to 50. MCP limit: null or CLI --all requests all remaining matches within the resource budget. --all and --limit conflict. The window reports examined records of the selected kind before filters, matching records, returned records and has_more; it never counts capture packets.

Returned records preserve field type, parent protocol, display base, bitmask, description and original glossary line number. Empty producer strings remain empty; protocol records have null field-only properties. Repeated abbreviations remain separate registrations. A listed field is an installation capability, not proof that the field appears in a selected recording.

The adapter uses the documented tshark -G fields grammar under its existing private empty profile and Linux process limits. TShark 4.6.9's full glossary was measured at 22,303,197 bytes; discovery admits at most 32 MiB combined command output (capture inspection remains at 4 MiB). Parsing walks lines once and constructs objects only for the selected page. Returned strings are copied so retained pages do not retain the complete glossary string. Before constructing a selected record it charges six times the source UTF-8 bytes plus 512 bytes against an 8 MiB representation budget, covering JSON escaping and record bookkeeping. If that budget would be exceeded, narrow the query or page size; results are not silently truncated. This is a conservative admission budget, not measured heap usage. Parent pipe buffers and producer per-process limits remain distinct.

Tool/limiter identity, version, stderr and selected arguments are retained in Evidence. No capture, interface or device is opened. Discovery shares capture analysis's admission slot and cleanup owner. Each call rereads the glossary; installed plugins, libraries and global configuration remain trusted and are not fully fingerprinted. There is no cache or snapshot-consistent pagination claim. The real Linux lane checks public CLI/MCP queries against the direct glossary, including USB vendor-ID typing, negative queries and paging.