feat(fiddler): inspect native SAZ captures

Inspect bounded recorded raw HTTP/XML SAZ archives and page retained capture Evidence. Preserve exact stored bytes and provenance, validate ZIP/XML integrity, and require an enforceable decoder memory bound.

Verified 356 real recorded sessions against independent byte and metadata oracles, public CLI/MCP parity, retained pages, and related interface checks. Current Fiddler Everywhere GUI and non-Linux producer workflows remain unverified.

Co-authored-by: zahhak <1792039+zahhak@users.noreply.github.com>
Co-authored-by: N0zoM1z0 <161784452+N0zoM1z0@users.noreply.github.com>
This commit is contained in:
authored and GitHub committed 2026-10-13 02:51:14 +00:00
1 parent f254f5ac2b
commit a2d8b94dd5
61 files changed
+2237 -285

No files matched your search

@@ -2,7 +2,7 @@
name: reverse-engineer-anything
description: Reverse engineer native, managed, Electron/JavaScript, packaged, firmware, and browser targets with REA. Use shipped-artifact or requested runtime evidence to explain features, compare versions, decompile code, or guide a reconstruction. Skip REA for ordinary source-repository architecture analysis.
metadata:
version: "40"
version: "41"
---
# REA
@@ -61,7 +61,11 @@ explicit path or endpoint and do not need it.
consult the NativeAOT workflow in `references/native-and-artifacts.md` for
optional metadata recovery and its supported host/layout boundary.
- User-owned browser page already open: `list_browser_targets`.
- Retained HAR/native mitmproxy capture: `inspect_web_network_capture`. Select
- Fiddler Everywhere traffic or a native `.saz` capture: read the
[Fiddler workflow](references/runtime-observation.md#fiddler-everywhere-traffic-debugging).
Inspect saved SAZ with `inspect_web_network_capture` and `format: "saz"`;
connect Everywhere's separate MCP server for live traffic.
- Retained HAR/SAZ/native mitmproxy capture: `inspect_web_network_capture`. Select
original record ordinals when useful; inspect producer fields and byte/number
sidecars without fetching recorded URLs or claiming live attribution. See
[historical captures](https://github.com/morluto/rea/blob/main/docs/web-network-captures.md) for the exact upstream
@@ -26,3 +26,18 @@ passive browser/Electron Evidence exist. Prefer captured-source digest identity;
report path/digest disagreement as a mismatch and preserve ambiguity. Explicit
path mappings are inference inputs and never broaden filesystem or CDP authority.
A bundle observed at runtime does not prove every contained module executed.
## Fiddler Everywhere traffic debugging
For large saved captures, request `detail: "summary"`, then page retained Evidence
with `inspect_analysis_view` and `view: {kind: "capture", detail: "preview", offset: 0, limit: 20}`.
Select complete record pages only for needed headers, metadata or payloads; views
read the historical parent without repeating capture decoding.
Inspect native `.saz` snapshots with `inspect_web_network_capture` and
`format: "saz"`; encrypted archives are unsupported. For live
traffic analysis, connect Everywhere's separate MCP server and use its
advertised session tools with an explicit `sessionsSource` (`LiveTraffic` or
`AgentCalls`). Preserve existing sessions and filters when inspecting a
recorded run. See the [capture and MCP guide](https://github.com/morluto/rea/blob/main/docs/web-network-captures.md)
for the workflow and Telerik's public setup and traffic-debugging skills.
+1 -1
View File
@@ -161,7 +161,7 @@ Additional tools and host support depend on the target:
| Recorded Linux crashes | Raw notes, every recorded thread's registers/signals and optional mapping candidates | Caller-supplied pwntools on Linux x64; optional GDB/pwndbg; [recorded crashes](docs/recorded-crashes.md) |
| JavaScript / Electron | Modules, imports, source maps, routes, IPC and native add-on relationships | Node.js and npm; [application analysis](https://rea.tools/guides/javascript/) |
| Websites | Page structure, scripts, network observations and requested screenshots | A Chrome-family browser; [browser analysis](https://rea.tools/guides/browser/) |
| Saved network captures | Requests, responses, exposed payloads and source locations | HAR; mitmdump on Linux for native mitmproxy captures; [capture guide](docs/web-network-captures.md) |
| Saved network captures | Requests, responses, exposed payloads and source locations | HAR or Fiddler Everywhere SAZ; mitmdump on Linux for native mitmproxy; [capture guide](docs/web-network-captures.md) |
| .NET assemblies | Metadata, CIL instructions, declared native dependencies and build comparisons | None (static only); [managed-code guide](docs/managed-code-analysis.md) |
| Android APKs | Manifest declarations, classes, decompiled methods and references | Headless JADX and a full JDK on Linux/macOS/Windows x64; [Android guide](docs/android-analysis.md) |
| Android devices | Devices, packages, processes, logs, dumps, screen captures, and APK/file transfers | Caller-supplied adb (emulator or hardware); [ADB guide](docs/adb-device-analysis.md) |
+4
View File
@@ -37,6 +37,10 @@ export default defineConfig({
link: "/javascript-application-workflows",
},
{ text: "Browser observation", link: "/browser-observation" },
{
text: "Web network captures",
link: "/web-network-captures",
},
{ text: "Electron observation", link: "/electron-observation" },
{ text: "Process capture", link: "/process-capture" },
{ text: ".NET assemblies", link: "/managed-code-analysis" },
+4
View File
@@ -8,6 +8,10 @@ exported script's native imports under an explicit URL/import-map context.
REA can attach to a user-owned Chrome-family browser through the Chrome DevTools Protocol (CDP) and produce bounded Evidence about an existing page. This is a passive reverse-engineering capability, not a general browser automation or remote-control surface.
For request/response bodies and traffic debugging, see
[Fiddler Everywhere captures and MCP](web-network-captures.md#reading-fiddler-captures-saz).
Inspect native SAZ or HAR 1.2 offline with REA, or connect Everywhere's separate MCP server.
## Shipped surfaces
- `list_browser_targets` / `rea list-browser-targets` discovers all eligible page targets at the endpoint by default, with an optional exact-origin filter.
+6
View File
@@ -6,6 +6,12 @@ for the reviewed setup transaction and [client IDs](installation.md#supported-ag
The versioned verification notes below describe their stated runs; live service
acceptance is verified only where explicitly recorded.
## Fiddler Everywhere alongside REA
Connect Fiddler Everywhere's MCP server separately for HTTP traffic debugging,
preserving your REA client entry. See the
[setup instructions and public skills](web-network-captures.md#fiddler-everywhere-mcp-and-public-skills).
## GitHub Copilot CLI
For GitHub Copilot CLI 1.0.95, `gpt-4.1` model metadata can block ordinary
+1 -1
View File
@@ -4,7 +4,7 @@
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/mitmproxy web-capture workflows remain separate.
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
+106 -7
View File
@@ -25,6 +25,36 @@ and original zero-based producer ordinals; duplicate or out-of-range selections
are input errors. Every record is validated before selection, so selecting one
entry does not hide a malformed entry elsewhere.
For agent discovery over MCP, request `detail: "summary"`. REA retains the
complete selected Evidence in the session and returns identity and counts.
Use `normalized_result.parent_evidence_id` with `inspect_analysis_view`:
```json
{
"source": {
"kind": "retained-evidence",
"evidence_id": "<parent_evidence_id>"
},
"view": { "kind": "capture", "detail": "preview", "offset": 0, "limit": 20 }
}
```
Preview pages select recorded HTTP headline fields, event names/times when
present, original locations and byte descriptors; they omit headers, XML and
payload content. Choose `detail: "complete"` in that view for exact record
pages, including all sidecars. Offsets follow the parent's selected record
order; producer ordinals remain unchanged. `total_records` counts the original
capture while `retained_records` and page coverage count records in the parent.
An exhausted page does not establish absence from unselected records.
Views do not reopen the file or rerun its decoder. They remain historical
Evidence if the source changes or disappears; inspect the capture again to
observe new bytes. The inspection tool's default `detail: "complete"` preserves
existing inline delivery. For CLI use, save complete inspection JSON and pass
it as inline Evidence to `inspect-analysis-view`; the same summary and capture
page contracts apply. Large complete records can still exceed MCP's response
budget; export the retained Evidence when a single record cannot fit.
## Formats and upstream profiles
HAR uses unchanged `har-schema@2.0.0` draft-06 schemas, `ajv@8.20.0`,
@@ -57,11 +87,80 @@ tested upstream source is tag `v12.2.3`, commit
an explicit unsupported-profile diagnostic. Non-Linux native decoding is not
supported by this adapter.
## Reading Fiddler captures (`.saz`)
Fiddler Everywhere saves traffic in **Session Archive Zip (`.saz`)** files.
Inspect native SAZ directly, without installing Fiddler or converting to HAR:
```sh
rea inspect-web-network-capture /absolute/session.saz saz --json
rea inspect-web-network-capture /absolute/session.saz saz --record 0 --json
```
MCP uses the same `capture_path`, `format: "saz"`, and optional
`record_ordinals` fields as the examples above. REA pairs `raw/<id>_c.txt`
requests, `_s.txt` responses, and `_m.xml` metadata by filename stem. Archive
member names identify the source; zero-based ordinals follow each session's
first member in ZIP directory order, not its numeric ID or capture time.
HTTP fields and ordered duplicate headers are retained, with stored body bytes
in binary sidecars. Bodies are not dechunked or decompressed. Metadata is an
XML node tree; optional `_w.txt` and `_g.txt` files retain opaque WebSocket and
gRPC message bytes. Missing members remain null, distinct from empty bodies.
See Telerik's [SAZ format reference](https://www.telerik.com/fiddler/fiddler-everywhere/documentation/knowledge-base/fiddler-archives).
The existing ZIP reader verifies read members; expanded archive contents are
limited to 32 MiB in addition to the input/output budgets below. Encrypted
archives and unrecognized raw session members are unsupported. SAZ requires an
enforced decoder memory bound: Node's fixed heap, or the Windows Job Object
commit limit. Bun on hosts without that Job Object limit returns capability
unavailable before starting the SAZ decoder. Non-session
members are inventoried without decoding their content. Original files remain
unchanged. Linux verification includes independently checked public recorded
Mattermost captures from [FOREST](https://github.com/dfrc-korea/FOREST/tree/405c9154658a128ea203a6c10040cec55111ed03/api-results/dataset)
and the Fiddler author's [encoding corpus](https://github.com/ericlaw1979/Clearinet/blob/b6faf78cde1e72042980364f180ceb34b77d1434/Test/data/Encodings.saz),
plus synthetic malformed/limit fixtures. Their exporting product versions are
not authenticated. An interactive export from a current Fiddler Everywhere
installation and its live MCP server remain unverified. Support here is the
bounded documented raw HTTP/XML SAZ layout, not every product/version profile.
For visual inspection, use Everywhere's **Import Snapshot**, then **Inspectors**
and **Overview**. HAR remains an alternative: select sessions, choose
**Export > HTTPArchive**, and confirm `log.version` is `"1.2"`. Preserve the
original SAZ when exporting. See the [import/export instructions](https://www.telerik.com/fiddler/fiddler-everywhere/documentation/collaboration/exporting-and-importing).
## Fiddler Everywhere MCP and public skills
For agent-driven traffic debugging, connect Everywhere's MCP server separately
from REA. Run a current Everywhere release with MCP access (Trial, Pro, or
higher), internet access, and a compatible MCP client. Use
**Settings > MCP Server > Coding agent**, or **Manual setup** with the generated
API key. The default endpoint is `http://localhost:8868/mcp`. See Telerik's
[MCP setup guide](https://www.telerik.com/fiddler/fiddler-everywhere/documentation/agent-tools/fiddler-mcp-server)
and [agent-skills prerequisites](https://www.telerik.com/fiddler/fiddler-everywhere/documentation/agent-tools/agent-skills).
Telerik's [public skills](https://github.com/telerik/fiddler-agent-tools) cover
installation (`fiddler-download-setup`), connection setup, and traffic analysis:
```sh
npx skills add telerik/fiddler-agent-tools --skill fiddler-mcp-setup
npx skills add telerik/fiddler-agent-tools --skill fiddler-traffic-debugging
```
After setup, reload the agent client and use the connected server's advertised
tool names and schemas. Check status, list sessions, then inspect relevant
session IDs. Set `sessionsSource` to `LiveTraffic` for HTTP(S) traffic or
`AgentCalls` for enriched LLM calls; active filters affect the session list.
For an existing run, skip the skill's pre-capture steps that replace process
filters and clear sessions. The documented MCP session sources do not provide
a saved-snapshot selector; use the SAZ workflow above for saved files. REA does
not register or wrap the Fiddler MCP server.
## Reading the evidence
- `artifact` identifies the caller-selected file and stable snapshot digest.
- `records` preserve producer order/ordinals and original JSON pointers or byte
ranges. No live browser transaction or scenario IDs are invented.
- `records` preserve producer order/ordinals and original JSON pointers, byte
ranges, or SAZ member names. No live browser transaction or scenario IDs are invented.
- `reported` retains the producer's fields. HAR container metadata is returned
separately, with `log.entries` replaced by `null` and `records_pointer`
identifying the record collection. This is an inspection representation,
@@ -156,7 +255,7 @@ response fields, including HAR's `status: 0` and an empty `mimeType`, remain
unknown, not invented statuses or MIME types.
The view makes no handler-identity, schema or causality claim. It performs no
network activity. Native mitmproxy captures return a typed unsupported error.
network activity. Native mitmproxy and SAZ captures return a typed unsupported error.
Parents inspected with `sensitive_values`, or whose Evidence does not record
`sensitive_value_count`, return a typed unsupported error whose reason is the
canonical token `sensitive_values_unretained`. The view would write path
@@ -192,14 +291,14 @@ Complete evidence has a 32 MiB input, 96 MiB decoder reply and 64-level nesting
budget. Exceeding a budget returns no partial success.
Oversized selected captures return an input error identifying `capture_path`
and the byte budget; owned reply/output failures remain output or provider errors.
HAR runs in an owned
192 MiB Node old-generation heap with one V8 worker. Native decoding applies
HAR and SAZ run in an owned
192 MiB Node old-generation heap with one V8 worker. Mitmproxy decoding applies
Linux limits of 768 MiB address space, 30 seconds CPU and 96 MiB file output;
both commands have a 30-second wall deadline and independently supervised
process cleanup. Owned decoder diagnostics retain at most 1 MiB across stdout
and stderr; the collector drops overflow as it arrives, reports the limit
failure and verifies cleanup. Heap limits alone do not establish aggregate RSS limits.
When REA runs on Bun, the HAR decoder runs on Bun, which ignores these V8 heap
When REA runs on Bun, the HAR/SAZ decoder runs on Bun, which ignores these V8 heap
flags; its limitations and resource failures then omit the heap sizes. On
Windows, the decoder's owned Job Object instead limits its committed memory to
704 MiB, the 192 MiB budget plus a 512 MiB reserve for Bun itself. The
@@ -207,7 +306,7 @@ limitations state that limit, and reaching it returns `resource_constraint`
with `process_memory_limit_bytes`, whatever exit status Bun reports.
Observed decoder memory exhaustion returns `resource_constraint`, with the
selected capture, applicable limits and retained process diagnostics. HAR's
selected capture, applicable limits and retained process diagnostics. HAR/SAZ's
old-generation heap is fixed; inherited `NODE_OPTIONS` cannot raise it.
Record selection happens after complete decoding and does not reduce that
workload. Use a smaller capture exported by its producer or another decoder
+5
View File
@@ -272,6 +272,11 @@ export const providerCatalog = (sources) => {
contracts:
sources.webNetworkCaptureContracts.WEB_NETWORK_CAPTURE_TOOL_CONTRACTS,
},
{
identity: sources.webNetworkCaptureProvider.SAZ_CAPTURE_PROVIDER_IDENTITY,
contracts:
sources.webNetworkCaptureContracts.WEB_NETWORK_CAPTURE_TOOL_CONTRACTS,
},
{
identity:
sources.webNetworkCaptureProvider.MITMPROXY_CAPTURE_PROVIDER_IDENTITY,
+2 -14
View File
@@ -16,6 +16,7 @@ import { jsonObjectSchema } from "../domain/jsonValue.js";
import { err, ok, type Result } from "../domain/result.js";
import {
inspectWebNetworkCaptureInputSchema,
captureSensitiveValues,
webNetworkCaptureSchema,
} from "../domain/webNetworkCapture.js";
@@ -32,21 +33,8 @@ export class WebNetworkCaptureService {
): Promise<Result<Evidence, AnalysisError>> {
if (options?.signal?.aborted)
return err(new AnalysisCancelledError(OPERATION));
const declarations =
inspectWebNetworkCaptureInputSchema.shape.sensitive_values.safeParse(
typeof rawInput === "object" &&
rawInput !== null &&
"sensitive_values" in rawInput
? rawInput.sensitive_values
: undefined,
);
const failure = (error: AnalysisError): Result<never, AnalysisError> =>
err(
redactExplicitFailure(
error,
declarations.success ? declarations.data : [],
),
);
err(redactExplicitFailure(error, captureSensitiveValues(rawInput)));
const parsed = inspectWebNetworkCaptureInputSchema.safeParse(rawInput);
if (!parsed.success)
return failure(
@@ -47,14 +47,8 @@ export const inspectAnalysisViewValidated = (
input: InspectAnalysisViewInput,
lookup?: EvidenceLookup,
): Result<Evidence, AnalysisError> => {
const inline = input.source.kind === "inline" ? input.source.evidence : null;
const inlineCapture =
typeof inline === "object" &&
inline !== null &&
Object.getOwnPropertyDescriptor(inline, "operation")?.value ===
"inspect_web_network_capture";
const accumulator =
input.view.kind === "api-surface" || inlineCapture
input.view.kind === "api-surface"
? new WebNetworkApiSurfaceAccumulator()
: undefined;
if (input.source.kind === "inline" && accumulator !== undefined) {
+4 -4
View File
@@ -4,7 +4,7 @@ import { projectAnalysisError } from "../../domain/analysisErrorProjection.js";
import { analysisErrorProjectionSchema } from "../../contracts/errorSchemas.js";
import { inspectWebNetworkCaptureInputSchema } from "../../domain/webNetworkCapture.js";
import { historicalCaptureFailure } from "./CaptureFailures.js";
import { HAR_CAPTURE_PROCESS_MEMORY_LIMIT_BYTES } from "./CaptureRelease.js";
import { NODE_CAPTURE_PROCESS_MEMORY_LIMIT_BYTES } from "./CaptureRelease.js";
import { redactExplicitFailure } from "../../domain/explicitSensitiveFailure.js";
import { OwnedCommandFailure } from "../../process/OwnedCommand.js";
@@ -118,7 +118,7 @@ it.each([
},
});
// The enforced limit is reported only where Bun runs on Windows.
if (HAR_CAPTURE_PROCESS_MEMORY_LIMIT_BYTES === null)
if (NODE_CAPTURE_PROCESS_MEMORY_LIMIT_BYTES === null)
expect(JSON.stringify(projected)).not.toContain(
"process_memory_limit_bytes",
);
@@ -126,12 +126,12 @@ it.each([
expect(projected).toMatchObject({
details: {
reported_limits: {
process_memory_limit_bytes: HAR_CAPTURE_PROCESS_MEMORY_LIMIT_BYTES,
process_memory_limit_bytes: NODE_CAPTURE_PROCESS_MEMORY_LIMIT_BYTES,
},
},
remediation: {
action: expect.stringContaining(
`${String(HAR_CAPTURE_PROCESS_MEMORY_LIMIT_BYTES)} bytes`,
`${String(NODE_CAPTURE_PROCESS_MEMORY_LIMIT_BYTES)} bytes`,
),
},
});
+13 -11
View File
@@ -18,9 +18,9 @@ import {
} from "../../domain/webNetworkCapture.js";
import { OwnedCommandFailure } from "../../process/OwnedCommand.js";
import {
HAR_CAPTURE_HEAP_ENFORCED,
HAR_CAPTURE_HEAP_LIMITS,
HAR_CAPTURE_PROCESS_MEMORY_LIMIT_BYTES,
NODE_CAPTURE_HEAP_ENFORCED,
NODE_CAPTURE_HEAP_LIMITS,
NODE_CAPTURE_PROCESS_MEMORY_LIMIT_BYTES,
} from "./CaptureRelease.js";
const OPERATION = "inspect_web_network_capture";
@@ -78,7 +78,7 @@ export const historicalCaptureFailure = (
);
if (
phase === "decoder" &&
input.format === "har" &&
input.format !== "mitmproxy" &&
cause.reason === "process" &&
(cause.snapshot?.signal === "SIGABRT" ||
(cause.snapshot?.signal === null && cause.snapshot.exitCode === 134)) &&
@@ -195,15 +195,17 @@ export const captureMemoryFailure = (
format: input.format,
maximum_input_bytes: WEB_NETWORK_CAPTURE_LIMITS.inputBytes,
maximum_reply_bytes: WEB_NETWORK_CAPTURE_LIMITS.outputBytes,
...(input.format === "har" && HAR_CAPTURE_HEAP_ENFORCED
...(input.format !== "mitmproxy" && NODE_CAPTURE_HEAP_ENFORCED
? {
old_generation_heap_mib: HAR_CAPTURE_HEAP_LIMITS.oldGenerationMiB,
semi_space_heap_mib: HAR_CAPTURE_HEAP_LIMITS.semiSpaceMiB,
old_generation_heap_mib: NODE_CAPTURE_HEAP_LIMITS.oldGenerationMiB,
semi_space_heap_mib: NODE_CAPTURE_HEAP_LIMITS.semiSpaceMiB,
}
: {}),
...(input.format === "har" &&
HAR_CAPTURE_PROCESS_MEMORY_LIMIT_BYTES !== null
? { process_memory_limit_bytes: HAR_CAPTURE_PROCESS_MEMORY_LIMIT_BYTES }
...(input.format !== "mitmproxy" &&
NODE_CAPTURE_PROCESS_MEMORY_LIMIT_BYTES !== null
? {
process_memory_limit_bytes: NODE_CAPTURE_PROCESS_MEMORY_LIMIT_BYTES,
}
: {}),
...(cause instanceof OwnedCommandFailure
? {
@@ -214,7 +216,7 @@ export const captureMemoryFailure = (
},
{
cause,
remediationAction: `Use a smaller capture exported by its producer, or another decoder with sufficient capacity for the original capture. Preserve the original for provenance; a subset does not establish complete-capture coverage.${input.format === "har" && HAR_CAPTURE_HEAP_ENFORCED ? ` REA's HAR old-generation heap is fixed at ${HAR_CAPTURE_HEAP_LIMITS.oldGenerationMiB} MiB; inherited NODE_OPTIONS cannot raise it.` : ""}${input.format === "har" && HAR_CAPTURE_PROCESS_MEMORY_LIMIT_BYTES !== null ? ` REA's HAR decoder process commit limit is fixed at ${String(HAR_CAPTURE_PROCESS_MEMORY_LIMIT_BYTES)} bytes.` : ""}`,
remediationAction: `Use a smaller capture exported by its producer, or another decoder with sufficient capacity for the original capture. Preserve the original for provenance; a subset does not establish complete-capture coverage.${input.format !== "mitmproxy" && NODE_CAPTURE_HEAP_ENFORCED ? ` REA's Node capture old-generation heap is fixed at ${NODE_CAPTURE_HEAP_LIMITS.oldGenerationMiB} MiB; inherited NODE_OPTIONS cannot raise it.` : ""}${input.format !== "mitmproxy" && NODE_CAPTURE_PROCESS_MEMORY_LIMIT_BYTES !== null ? ` REA's Node capture decoder process commit limit is fixed at ${String(NODE_CAPTURE_PROCESS_MEMORY_LIMIT_BYTES)} bytes.` : ""}`,
...(cause instanceof OwnedCommandFailure && cause.snapshot !== null
? {
capturedOutput: {
+1 -1
View File
@@ -1,7 +1,7 @@
/** Expected malformed, unsupported or oversized producer data at a historical decoder boundary. */
export class CaptureFormatError extends Error {
constructor(
readonly reason: "format" | "unsupported" | "input-limit",
readonly reason: "format" | "unsupported" | "input-limit" | "limit",
message: string,
readonly pointer = "",
) {
+13 -6
View File
@@ -8,19 +8,19 @@ export const HAR_CAPTURE_PROVIDER_IDENTITY = {
version: "har-schema@2.0.0;jsonc-parser@3.3.1;lossless-json@4.3.1",
} as const;
/** Fixed V8 heap settings used by the owned HAR decoder process. */
export const HAR_CAPTURE_HEAP_LIMITS = {
/** Fixed V8 heap settings used by the owned Node capture decoder process. */
export const NODE_CAPTURE_HEAP_LIMITS = {
oldGenerationMiB: 192,
semiSpaceMiB: 8,
} as const;
/** The HAR decoder runs on this executable; Bun accepts but ignores V8 heap flags. */
export const HAR_CAPTURE_HEAP_ENFORCED =
/** The Node capture decoder runs on this executable; Bun accepts but ignores V8 heap flags. */
export const NODE_CAPTURE_HEAP_ENFORCED =
javaScriptRuntime(process.versions).id === "node";
/** Windows Job Object commit limit replacing the ignored heap flags under Bun; otherwise null. */
export const HAR_CAPTURE_PROCESS_MEMORY_LIMIT_BYTES =
javaScriptWorkerMemoryLimitBytes(HAR_CAPTURE_HEAP_LIMITS.oldGenerationMiB);
export const NODE_CAPTURE_PROCESS_MEMORY_LIMIT_BYTES =
javaScriptWorkerMemoryLimitBytes(NODE_CAPTURE_HEAP_LIMITS.oldGenerationMiB);
/** Native executable profile verified with unchanged mitmproxy raw-state decoding. */
export const MITMPROXY_CAPTURE_PROVIDER_IDENTITY = {
@@ -28,3 +28,10 @@ export const MITMPROXY_CAPTURE_PROVIDER_IDENTITY = {
name: "REA offline native mitmproxy adapter",
version: "12.2.3",
} as const;
/** Native SAZ member decoding with the existing ZIP and XML package profiles. */
export const SAZ_CAPTURE_PROVIDER_IDENTITY = {
id: "fiddler-saz",
name: "REA Fiddler SAZ capture adapter",
version: "1;zip.js@2.15.0;xmldom@0.9.12",
} as const;
-33
View File
@@ -1,33 +0,0 @@
import { fileURLToPath } from "node:url";
import type { HistoricalCaptureFormatAdapter } from "./HistoricalCaptureFormatAdapter.js";
import {
HAR_CAPTURE_PROVIDER_IDENTITY,
HAR_CAPTURE_HEAP_LIMITS,
HAR_CAPTURE_PROCESS_MEMORY_LIMIT_BYTES,
} from "./CaptureRelease.js";
/** HAR's parser/schema packages run in one bounded Node heap, without network or target execution. */
export class HarCaptureAdapter implements HistoricalCaptureFormatAdapter {
readonly format = "har";
readonly identity = HAR_CAPTURE_PROVIDER_IDENTITY;
async command(requestPath: string): Promise<{
command: string;
arguments: readonly string[];
memoryLimitBytes?: number;
}> {
return {
command: process.execPath,
arguments: [
`--max-old-space-size=${HAR_CAPTURE_HEAP_LIMITS.oldGenerationMiB}`,
`--max-semi-space-size=${HAR_CAPTURE_HEAP_LIMITS.semiSpaceMiB}`,
"--v8-pool-size=1",
"--single-threaded",
fileURLToPath(new URL("./HarCaptureProcess.js", import.meta.url)),
requestPath,
],
...(HAR_CAPTURE_PROCESS_MEMORY_LIMIT_BYTES === null
? {}
: { memoryLimitBytes: HAR_CAPTURE_PROCESS_MEMORY_LIMIT_BYTES }),
};
}
}
+12 -11
View File
@@ -4,9 +4,9 @@ import {
captureMemoryFailure,
} from "./CaptureFailures.js";
import {
HAR_CAPTURE_HEAP_ENFORCED,
HAR_CAPTURE_HEAP_LIMITS,
HAR_CAPTURE_PROCESS_MEMORY_LIMIT_BYTES,
NODE_CAPTURE_HEAP_ENFORCED,
NODE_CAPTURE_HEAP_LIMITS,
NODE_CAPTURE_PROCESS_MEMORY_LIMIT_BYTES,
} from "./CaptureRelease.js";
import { projectAnalysisError } from "../../domain/analysisErrorProjection.js";
import type { HistoricalCaptureFormatAdapter } from "./HistoricalCaptureFormatAdapter.js";
@@ -208,10 +208,11 @@ export class HistoricalCaptureDecoder {
format: input.format,
runtime_attribution: "unknown",
limitations: [
...(adapter.limitations ?? []),
"This is retained producer evidence, without live browser transaction IDs, scenario provenance, execution attribution or deployment authenticity. Recorded URLs are never fetched.",
"Every record is decoded before selection; no partial success is returned on malformed input or a resource limit.",
"Known authentication headers/cookies and explicitly marked literal values are excluded. Unknown extension names do not establish sensitivity.",
`A 32 MiB input, 96 MiB reply and 64-level nesting budget bound complete evidence. ${harMemoryLimitation()}; native decoding uses a 768 MiB Linux address-space limit. Each owned command has a 30-second deadline with independent cleanup.`,
`A 32 MiB input, 96 MiB reply and 64-level nesting budget bound complete evidence. ${nodeMemoryLimitation()}; mitmproxy decoding uses a 768 MiB Linux address-space limit. Each owned command has a 30-second deadline with independent cleanup.`,
],
});
} catch (cause: unknown) {
@@ -251,13 +252,13 @@ export class HistoricalCaptureDecoder {
}
}
/** The HAR decoder runs on this executable; only Node.js enforces its V8 heap flags. */
const harMemoryLimitation = (): string => {
if (HAR_CAPTURE_HEAP_ENFORCED)
return `HAR uses a ${String(HAR_CAPTURE_HEAP_LIMITS.oldGenerationMiB)} MiB Node old-generation heap`;
return HAR_CAPTURE_PROCESS_MEMORY_LIMIT_BYTES === null
? "HAR runs on Bun, which does not enforce REA's V8 heap flags"
: `HAR runs on Bun, which does not enforce REA's V8 heap flags, under a ${String(HAR_CAPTURE_PROCESS_MEMORY_LIMIT_BYTES)}-byte Windows Job Object commit limit`;
/** The Node capture decoder runs on this executable; only Node.js enforces its V8 heap flags. */
const nodeMemoryLimitation = (): string => {
if (NODE_CAPTURE_HEAP_ENFORCED)
return `HAR/SAZ use a ${String(NODE_CAPTURE_HEAP_LIMITS.oldGenerationMiB)} MiB Node old-generation heap`;
return NODE_CAPTURE_PROCESS_MEMORY_LIMIT_BYTES === null
? "HAR/SAZ run on Bun, which does not enforce REA's V8 heap flags"
: `HAR/SAZ run on Bun, which does not enforce REA's V8 heap flags, under a ${String(NODE_CAPTURE_PROCESS_MEMORY_LIMIT_BYTES)}-byte Windows Job Object commit limit`;
};
const replyFailureReason = (cause: unknown): string => {
@@ -3,6 +3,7 @@ import type { InspectWebNetworkCaptureInput } from "../../domain/webNetworkCaptu
/** Format-specific process profile keeps mature upstream code behind a replaceable adapter seam. */
export interface HistoricalCaptureFormatAdapter {
readonly format: InspectWebNetworkCaptureInput["format"];
readonly limitations?: readonly string[];
readonly identity: {
readonly id: string;
readonly name: string;
@@ -0,0 +1,15 @@
import { expect, it, vi } from "vitest";
import { AnalysisCapabilityUnavailableError } from "../../domain/analysisErrorCore.js";
import { NodeCaptureAdapter } from "./NodeCaptureAdapter.js";
vi.mock("./CaptureRelease.js", async (original) => ({
...(await original<typeof import("./CaptureRelease.js")>()),
NODE_CAPTURE_HEAP_ENFORCED: false,
NODE_CAPTURE_PROCESS_MEMORY_LIMIT_BYTES: null,
}));
it("refuses a SAZ worker when the runtime has no enforceable memory bound", async () => {
await expect(
new NodeCaptureAdapter("saz").command("/owned/request.json"),
).rejects.toBeInstanceOf(AnalysisCapabilityUnavailableError);
});
+58
View File
@@ -0,0 +1,58 @@
import { fileURLToPath } from "node:url";
import type { HistoricalCaptureFormatAdapter } from "./HistoricalCaptureFormatAdapter.js";
import {
NODE_CAPTURE_HEAP_ENFORCED,
HAR_CAPTURE_PROVIDER_IDENTITY,
SAZ_CAPTURE_PROVIDER_IDENTITY,
NODE_CAPTURE_HEAP_LIMITS,
NODE_CAPTURE_PROCESS_MEMORY_LIMIT_BYTES,
} from "./CaptureRelease.js";
import { AnalysisCapabilityUnavailableError } from "../../domain/analysisErrorCore.js";
/** Package-backed capture decoders share one bounded offline Node process profile. */
export class NodeCaptureAdapter implements HistoricalCaptureFormatAdapter {
readonly identity;
readonly limitations;
constructor(readonly format: "har" | "saz") {
this.identity =
format === "har"
? HAR_CAPTURE_PROVIDER_IDENTITY
: SAZ_CAPTURE_PROVIDER_IDENTITY;
this.limitations =
format === "saz"
? [
"SAZ ordinals follow first session-member occurrence in ZIP directory order, not chronology or numeric session ID.",
"HTTP fields use byte-preserving Latin-1 text; start-line separators and header delimiters are not projected. Bodies retain stored bytes without transfer/content decoding. Absent members are null, distinct from empty bodies.",
"XML preserves node order and attribute/text values, not its original byte serialization. WebSocket and gRPC members retain opaque message-file bytes, not decoded protocol events.",
"Non-session archive members are inventoried but their content is not decoded.",
]
: [];
}
async command(requestPath: string) {
if (
this.format === "saz" &&
!NODE_CAPTURE_HEAP_ENFORCED &&
NODE_CAPTURE_PROCESS_MEMORY_LIMIT_BYTES === null
)
throw new AnalysisCapabilityUnavailableError(
this.identity.id,
"inspect_web_network_capture",
"SAZ decoding requires an enforced decoder memory limit. Run REA with Node.js on this host.",
);
return {
command: process.execPath,
arguments: [
`--max-old-space-size=${NODE_CAPTURE_HEAP_LIMITS.oldGenerationMiB}`,
`--max-semi-space-size=${NODE_CAPTURE_HEAP_LIMITS.semiSpaceMiB}`,
"--v8-pool-size=1",
"--single-threaded",
fileURLToPath(new URL("./NodeCaptureProcess.js", import.meta.url)),
requestPath,
this.format,
],
...(NODE_CAPTURE_PROCESS_MEMORY_LIMIT_BYTES === null
? {}
: { memoryLimitBytes: NODE_CAPTURE_PROCESS_MEMORY_LIMIT_BYTES }),
};
}
}
@@ -3,7 +3,6 @@ import { z } from "zod";
import { readStableArtifact } from "../../artifacts/readStableArtifact.js";
import { WEB_NETWORK_CAPTURE_LIMITS } from "../../domain/webNetworkCapture.js";
import { CaptureFormatError } from "./CaptureFormatError.js";
import { decodeHarCapture, decodeHarText } from "./HarCapture.js";
const requestSchema = z.strictObject({
snapshot_path: z.string(),
@@ -11,24 +10,34 @@ const requestSchema = z.strictObject({
sensitive_values: z.array(z.string().min(1)),
});
/** Decode untrusted HAR in an owned, bounded heap rather than the long-lived MCP process. */
/** Parse untrusted captures in an owned heap and return one complete bounded reply. */
const main = async (): Promise<void> => {
const format = z.enum(["har", "saz"]).parse(process.argv[3]);
const request = requestSchema.parse(
JSON.parse(await readFile(process.argv[2] ?? "", "utf8")),
);
let reply: unknown;
try {
const snapshot = await readStableArtifact(
request.snapshot_path,
WEB_NETWORK_CAPTURE_LIMITS.inputBytes,
);
reply = {
ok: true,
value: decodeHarCapture(
let value;
if (format === "har") {
const { decodeHarCapture, decodeHarText } =
await import("./HarCapture.js");
const snapshot = await readStableArtifact(
request.snapshot_path,
WEB_NETWORK_CAPTURE_LIMITS.inputBytes,
);
value = decodeHarCapture(
decodeHarText(snapshot.bytes),
request.sensitive_values,
),
};
);
} else {
const { decodeSazCapture } = await import("./SazCapture.js");
value = await decodeSazCapture(
request.snapshot_path,
request.sensitive_values,
);
}
reply = { ok: true, value };
} catch (cause: unknown) {
reply =
cause instanceof CaptureFormatError
@@ -41,7 +50,7 @@ const main = async (): Promise<void> => {
: {
ok: false,
reason: "decoder",
message: `HAR decoder failed unexpectedly (${cause instanceof Error ? cause.name : "unknown exception"}).`,
message: `Capture decoder failed unexpectedly (${cause instanceof Error ? cause.name : "unknown exception"}).`,
pointer: "",
};
}
@@ -50,7 +59,7 @@ const main = async (): Promise<void> => {
text = JSON.stringify({
ok: false,
reason: "limit",
message: "HAR reply exceeds the complete-evidence output budget.",
message: "Capture reply exceeds the complete-evidence output budget.",
pointer: "",
});
await writeFile(request.reply_path, text, { flag: "wx", mode: 0o600 });
+174
View File
@@ -0,0 +1,174 @@
import { ZipArtifactReader } from "../../artifacts/ZipArtifactReader.js";
import {
ArtifactReaderFailure,
type ArtifactEntry,
} from "../../artifacts/ArtifactReader.js";
import {
WEB_NETWORK_CAPTURE_LIMITS,
type WebNetworkCaptureRecord,
} from "../../domain/webNetworkCapture.js";
import { SAZ_CAPTURE_PROVIDER_IDENTITY } from "./CaptureRelease.js";
import { CaptureFormatError } from "./CaptureFormatError.js";
import { decodeSazHttp } from "./SazHttp.js";
import { decodeSazMetadata } from "./SazMetadata.js";
import { projectSazRecord, type SazValue } from "./SazProjection.js";
/** Decode native SAZ sessions without extracting files, running Fiddler or converting to HAR. */
export const decodeSazCapture = async (
snapshotPath: string,
sensitiveValues: readonly string[],
) => {
const reader = new ZipArtifactReader(
snapshotPath,
"zip",
WEB_NETWORK_CAPTURE_LIMITS.inputBytes,
);
try {
const groups = new Map<string, ArtifactEntry[]>();
const inventory: SazValue[] = [];
const paths = new Set<string>();
let declaredBytes = 0;
for await (const entry of reader.entries()) {
if (paths.has(entry.path))
throw new CaptureFormatError(
"format",
"SAZ contains duplicate archive member names.",
);
paths.add(entry.path);
if (entry.encrypted)
throw new CaptureFormatError(
"unsupported",
"Encrypted SAZ archives are unsupported; export an unencrypted copy from Fiddler Everywhere.",
);
if (entry.kind !== "file" && entry.kind !== "directory")
throw new CaptureFormatError(
"unsupported",
"SAZ archive links are unsupported.",
);
if (entry.declaredSize !== null) {
if (!Number.isSafeInteger(entry.declaredSize) || entry.declaredSize < 0)
throw new CaptureFormatError("format", "SAZ member size is invalid.");
declaredBytes += entry.declaredSize;
}
if (declaredBytes > WEB_NETWORK_CAPTURE_LIMITS.inputBytes)
throw new CaptureFormatError(
"limit",
"SAZ expanded members exceed the 32 MiB complete-evidence budget.",
);
inventory.push({
path: entry.path,
kind: entry.kind,
bytes: entry.declaredSize,
});
if (entry.kind !== "file") continue;
const match = /^raw\/(\d+)_(c\.txt|s\.txt|m\.xml|w\.txt|g\.txt)$/.exec(
entry.path,
);
if (match?.[1] !== undefined) {
const group = groups.get(match[1]) ?? [];
group.push(entry);
groups.set(match[1], group);
} else if (entry.path.startsWith("raw/")) {
throw new CaptureFormatError(
"unsupported",
"SAZ contains an unrecognized raw session member.",
);
}
}
if (groups.size === 0)
throw new CaptureFormatError(
"format",
"Archive contains no SAZ session members.",
);
const records: WebNetworkCaptureRecord[] = [];
for (const [session_id, entries] of groups) {
const source: Record<string, SazValue> = {
session_id,
request: null,
response: null,
metadata: null,
websocket: null,
grpc: null,
};
for (const entry of entries) {
const stream = await reader.open(entry);
const chunks: Buffer[] = [];
let length = 0;
for await (const chunk of stream) {
if (!Buffer.isBuffer(chunk))
throw new CaptureFormatError(
"format",
"SAZ member stream returned non-byte data.",
);
length += chunk.length;
if (length > (entry.declaredSize ?? 0))
throw new CaptureFormatError(
"format",
"SAZ member exceeds its declared size.",
);
chunks.push(chunk);
}
if (length !== entry.declaredSize)
throw new CaptureFormatError(
"format",
"SAZ member does not match its declared size.",
);
const bytes = Buffer.concat(chunks, length);
if (entry.path.endsWith("_c.txt"))
source.request = {
member: entry.path,
...decodeSazHttp(bytes, true),
};
else if (entry.path.endsWith("_s.txt"))
source.response = {
member: entry.path,
...decodeSazHttp(bytes, false),
};
else if (entry.path.endsWith("_m.xml"))
source.metadata = {
member: entry.path,
document: decodeSazMetadata(bytes),
};
else
source[entry.path.endsWith("_w.txt") ? "websocket" : "grpc"] = {
member: entry.path,
messages: bytes,
};
}
records.push({
ordinal: records.length,
location: {
kind: "archive-members",
members: entries.map((entry) => entry.path),
},
...projectSazRecord(source, sensitiveValues),
limitations: [],
});
}
const container = projectSazRecord({ members: inventory }, sensitiveValues);
return {
decoder: { ...SAZ_CAPTURE_PROVIDER_IDENTITY },
container: {
reported: container.reported,
numeric_literals: container.numeric_literals,
redactions: container.redactions,
records_pointer: null,
},
total_records: records.length,
records,
};
} catch (cause: unknown) {
if (cause instanceof CaptureFormatError) throw cause;
if (cause instanceof ArtifactReaderFailure && cause.reason === "limit")
throw new CaptureFormatError(
"limit",
"SAZ archive exceeds the ZIP reader's resource budget.",
);
throw new CaptureFormatError(
"format",
"SAZ archive or session data is malformed or fails ZIP integrity validation.",
);
} finally {
await reader.close();
}
};
+72
View File
@@ -0,0 +1,72 @@
import { CaptureFormatError } from "./CaptureFormatError.js";
/** Preserve ordered HTTP fields and the stored body; never dechunk, decompress or fetch. */
export const decodeSazHttp = (bytes: Buffer, request: boolean) => {
const crlfSplit = bytes.indexOf("\r\n\r\n");
// Once a CRLF separator is found, only an earlier LF separator can win.
// Avoid searching an otherwise unrelated large body a second time.
const lfSplit = bytes
.subarray(0, crlfSplit < 0 ? bytes.length : crlfSplit)
.indexOf("\n\n");
// The first separator ends the headers. Later separators belong to the
// stored body, which may use different line endings or contain binary data.
const split =
crlfSplit >= 0 && (lfSplit < 0 || crlfSplit < lfSplit) ? crlfSplit : -1;
const boundary = split < 0 ? lfSplit : split;
if (boundary < 0)
throw new CaptureFormatError(
"format",
"SAZ HTTP message has no header/body separator.",
);
const lines = bytes
.subarray(0, boundary)
.toString("latin1")
.split(split < 0 ? "\n" : "\r\n");
const first = lines.shift() ?? "";
const fields = request
? /^(\S+) +(\S+) +(HTTP\/\d+(?:\.\d+)?)$/.exec(first)
: /^(HTTP\/\d+(?:\.\d+)?) +(\d{3})(?: (.*))?$/.exec(first);
if (fields === null)
throw new CaptureFormatError("format", "SAZ HTTP start line is malformed.");
const headers: { name: string; value: string }[] = [];
for (const line of lines) {
if (/^[ \t]/.test(line)) {
const previous = headers.at(-1);
if (previous === undefined)
throw new CaptureFormatError(
"format",
"SAZ HTTP header continuation has no field.",
);
previous.value += `${split < 0 ? "\n" : "\r\n"}${line}`;
continue;
}
const colon = line.indexOf(":");
// HTTP/2 pseudo-headers use the second colon as the delimiter.
const delimiter = colon === 0 ? line.indexOf(":", 1) : colon;
if (delimiter < 1)
throw new CaptureFormatError("format", "SAZ HTTP header is malformed.");
const name = line.slice(0, delimiter);
if (!/^:?[!#$%&'*+.^_`|~0-9A-Za-z-]+$/.test(name))
throw new CaptureFormatError(
"format",
"SAZ HTTP header name is malformed.",
);
headers.push({ name, value: line.slice(delimiter + 1) });
}
const start = request
? {
method: fields[1] ?? "",
url: fields[2] ?? "",
http_version: fields[3] ?? "",
}
: {
http_version: fields[1] ?? "",
status: fields[2] ?? "",
reason: fields[3] ?? "",
};
return {
...start,
headers,
body: bytes.subarray(boundary + (split < 0 ? 2 : 4)),
};
};
+83
View File
@@ -0,0 +1,83 @@
import { DOMParser, ParseError, type Node } from "@xmldom/xmldom";
import type { JsonValue } from "../../domain/jsonValue.js";
import { WEB_NETWORK_CAPTURE_LIMITS } from "../../domain/webNetworkCapture.js";
import { CaptureFormatError } from "./CaptureFormatError.js";
/** Preserve XML node order, attribute names and text without interpreting producer-specific flags. */
export const decodeSazMetadata = (bytes: Buffer): JsonValue => {
let xml: string;
try {
const encoding =
bytes[0] === 0xff && bytes[1] === 0xfe
? "utf-16le"
: bytes[0] === 0xfe && bytes[1] === 0xff
? "utf-16be"
: "utf-8";
xml = new TextDecoder(encoding, { fatal: true }).decode(bytes);
} catch {
throw new CaptureFormatError(
"unsupported",
"SAZ metadata must use UTF-8 or BOM-marked UTF-16.",
);
}
let parseFailed = false;
let document: ReturnType<DOMParser["parseFromString"]>;
try {
document = new DOMParser({
normalizeLineEndings: (source) => source,
onError: () => {
parseFailed = true;
},
}).parseFromString(xml, "application/xml");
} catch (cause: unknown) {
if (!(cause instanceof ParseError)) throw cause;
throw new CaptureFormatError(
"format",
"SAZ metadata is not well-formed XML.",
);
}
// The pinned parser does not resolve external or DTD-defined entities.
// Inspect its parsed node; text inside comments/CDATA is ordinary evidence.
if (document.doctype !== null)
throw new CaptureFormatError(
"unsupported",
"SAZ metadata DTDs are unsupported.",
);
if (parseFailed)
throw new CaptureFormatError(
"format",
"SAZ metadata is not well-formed XML.",
);
if (document.documentElement?.tagName !== "Session")
throw new CaptureFormatError(
"format",
"SAZ metadata must have a Session document element.",
);
const visit = (node: Node, depth: number): JsonValue => {
if (depth > WEB_NETWORK_CAPTURE_LIMITS.depth)
throw new CaptureFormatError(
"input-limit",
"SAZ metadata exceeds the complete-evidence nesting budget.",
);
const children: JsonValue[] = [];
for (let child = node.firstChild; child !== null; child = child.nextSibling)
children.push(visit(child, depth + 1));
const attributes: JsonValue[] = [];
if (node.nodeType === 1) {
const element = node as import("@xmldom/xmldom").Element;
for (let i = 0; i < element.attributes.length; i++) {
const attribute = element.attributes.item(i);
if (attribute !== null)
attributes.push({ name: attribute.name, value: attribute.value });
}
}
return {
name: node.nodeName,
type: node.nodeType,
value: node.nodeValue,
attributes,
children,
};
};
return visit(document, 0);
};
+131
View File
@@ -0,0 +1,131 @@
import { createHash } from "node:crypto";
import { isUtf8 } from "node:buffer";
import type { JsonValue } from "../../domain/jsonValue.js";
import {
WEB_NETWORK_CAPTURE_LIMITS,
type WebNetworkCaptureRecord,
} from "../../domain/webNetworkCapture.js";
import { CaptureRedaction, capturePointer } from "./CaptureRedaction.js";
import { CaptureFormatError } from "./CaptureFormatError.js";
export type SazValue =
| JsonValue
| Buffer
| { readonly [key: string]: SazValue }
| readonly SazValue[];
/** Project native byte fields with the same credential and explicit-literal exclusions as HAR. */
export const projectSazRecord = (
source: SazValue,
sensitiveValues: readonly string[],
) => {
const redactor = new CaptureRedaction(sensitiveValues);
const binary_fields: WebNetworkCaptureRecord["binary_fields"] = [];
const visit = (
value: SazValue,
pointer: string,
depth: number,
parent?: Readonly<Record<string, SazValue>>,
): JsonValue => {
if (depth > WEB_NETWORK_CAPTURE_LIMITS.depth)
throw new CaptureFormatError(
"input-limit",
"SAZ projection exceeds the complete-evidence nesting budget.",
pointer,
);
if (parent !== undefined && redactor.harCredential(pointer, parent))
return redactor.credential(pointer);
if (Buffer.isBuffer(value)) {
const base64 = value.toString("base64");
const sha256 = createHash("sha256").update(value).digest("hex");
const hidden =
redactor.sensitiveBytes(value) ||
sensitiveValues.some(
(literal) =>
base64.includes(literal) ||
sha256.includes(literal) ||
String(value.length).includes(literal),
);
if (hidden) {
binary_fields.push({
pointer,
representation: "producer-bytes",
state: "redacted",
content_base64: null,
bytes: null,
sha256: null,
});
redactor.redactions.push({
pointer,
reason: "explicit-sensitive-value",
});
return null;
}
binary_fields.push({
pointer,
representation: "producer-bytes",
state: "retained",
content_base64: base64,
bytes: value.length,
sha256,
});
return isUtf8(value) ? value.toString("utf8") : null;
}
if (typeof value === "string") {
// HTTP fields are Latin-1 views of bytes. A UTF-8 declaration must also
// exclude its byte spelling, even when its display is mojibake.
if (
/^\/(request|response)\//.test(pointer) &&
!pointer.endsWith("/member") &&
redactor.sensitiveBytes(Buffer.from(value, "latin1"))
) {
redactor.redactions.push({
pointer,
reason: "explicit-sensitive-value",
});
return null;
}
return redactor.text(
value,
pointer,
redactor.harTransportUrl(pointer, parent),
);
}
if (value === null || typeof value === "boolean") return value;
if (typeof value === "number")
return redactor.text(String(value), pointer) === null ? null : value;
if (Array.isArray(value))
return value.map((child: SazValue, i: number) =>
visit(child, capturePointer(pointer, String(i)), depth + 1),
);
return Object.fromEntries(
Object.entries(value).flatMap(([key, child]) => {
if (sensitiveValues.some((literal) => key.includes(literal))) {
redactor.redactions.push({
pointer,
reason: "explicit-sensitive-value",
scope: "property-name",
});
return [];
}
return [
[
key,
visit(
child,
capturePointer(pointer, key),
depth + 1,
value as Readonly<Record<string, SazValue>>,
),
],
];
}),
);
};
return {
reported: visit(source, "", 0),
binary_fields,
numeric_literals: [],
redactions: redactor.redactions,
};
};
+5 -2
View File
@@ -1,4 +1,5 @@
import { z } from "incur";
import { WEB_NETWORK_CAPTURE_FORMATS } from "../domain/webNetworkCapture.js";
import { createWebNetworkCaptureService } from "../composition/webNetworkCaptures.js";
import { CLI_COMMANDS } from "../cliCommandNames.js";
import { logCliCommand } from "../cliLogging.js";
@@ -16,10 +17,12 @@ export const registerWebNetworkCaptureCommands = (
const service = createWebNetworkCaptureService(environment);
cli.command(CLI_COMMANDS.inspectWebNetworkCapture, {
description:
"Inspect retained HAR or native mitmproxy records without fetching recorded URLs",
"Inspect retained HAR, native mitmproxy, or Fiddler SAZ records without fetching recorded URLs",
args: z.object({
capturePath: z.string().describe("Absolute local capture path"),
format: z.enum(["har", "mitmproxy"]).describe("Producer capture format"),
format: z
.enum(WEB_NETWORK_CAPTURE_FORMATS)
.describe("Producer capture format"),
}),
options: z.object({
record: z
+6 -2
View File
@@ -1,6 +1,6 @@
import { WebNetworkCaptureService } from "../application/WebNetworkCaptureService.js";
import { HistoricalCaptureDecoder } from "../browser/history/HistoricalCaptureDecoder.js";
import { HarCaptureAdapter } from "../browser/history/HarCaptureAdapter.js";
import { NodeCaptureAdapter } from "../browser/history/NodeCaptureAdapter.js";
import { MitmproxyCaptureAdapter } from "../browser/history/MitmproxyCaptureAdapter.js";
/** Compose offline format adapters without import-time reads, installation or process startup. */
@@ -9,7 +9,11 @@ export const createWebNetworkCaptureService = (
): WebNetworkCaptureService =>
new WebNetworkCaptureService(
new HistoricalCaptureDecoder(
[new HarCaptureAdapter(), new MitmproxyCaptureAdapter(environment)],
[
new NodeCaptureAdapter("har"),
new NodeCaptureAdapter("saz"),
new MitmproxyCaptureAdapter(environment),
],
environment,
),
);
+1 -1
View File
@@ -16,7 +16,7 @@ export const ANALYSIS_VIEW_TOOL_CONTRACTS = [
...toolContractMetadata("inspect_analysis_view"),
kind: "application",
description:
'Inspect selected views of completed binary-layout, JavaScript application, native analyze_function, inspect_artifact, inventory_artifact, inspect_wasm_artifact, inspect_evm_interface, inspect_managed_artifact, inspect_android_package or inspect_web_network_capture Evidence without repeating analysis. The api-surface view groups retained HAR requests by method, origin and format-rule path template; templated segments are inferences. It reads no headers or bodies and reports no query values. For a retained record use source: {kind: "retained-evidence", evidence_id: "<parent_evidence_id>"}. Use normalized_result.parent_evidence_id from a summary receipt. Source kind is required. Portable source uses {kind: "inline", evidence: <complete Evidence>}. For layout, JavaScript, native, inventory, WASM, EVM, managed artifact and Android package sources, request a summary with view: {kind: "summary"}; summary is a view kind and has no facet. The WASM summary gives validation, counts, WAT digest/length, tool profile and execution/network states without reading WAT text. Select a layout mitigations/linkage facet, one section/symbol/module, or a stable page. Module pages include assets, bundled and source modules; use node_id for ambiguous or unavailable paths. Inventory uses view: {kind: "inventory", collection: "occurrences", logical_paths: ["package.json"], offset: 0, limit: 64}. Inventory views page nodes, occurrences, edges or integrity contradictions; occurrence logical_paths matches exact recorded strings (omitted means all available, [] means none). source_complete distinguishes complete inventories from partial source records; page exhaustion does not prove absence in a partial source. WASM views use kind wasm and facet sections/imports/exports/candidates/glue/wat/headers/details/tool_profile; text facets use UTF-16 code units and source forms remain exact. EVM summary returns all inferred interface candidates, carrier/decoded identities and explicit unknowns without hex or duplicate stdout. EVM views use kind evm and facet functions/bytecode_hex/stdout/stderr; function pages preserve the original candidate rows, and text offsets/limits count UTF-16 code units (hex has two characters per decoded byte). Exhausting candidate pages does not establish global interface completeness. Managed artifact summaries preserve complete identity/classification, metadata status, module/MVID, frameworks, coverage and limitations without unrelated inventories. Managed pages use kind managed and collection references/resources/attributes, retaining source coverage and build-local token scope; page exhaustion does not establish complete metadata. Android package summaries retain all declarations, engine identity and counts without decoded manifest text. Android manifest pages use kind android, facet manifest, offset and limit in UTF-16 code units; source_status preserves partial decoded coverage even when the retained text page is exhausted. Native views select procedure, pseudocode, assembly, basic blocks, comments, callers/callees, references, unresolved calls, referenced strings/names, native API and high-pcode. Use nonnegative offset and a positive caller-selected limit (defaults 0 and 64); pseudocode uses UTF-16 code units, other native pages use rows. Returns artifact identity, parent Evidence ID, a distinct view digest, coverage, limitations and unknowns. Actual serialized size determines MCP transport admission; choose a smaller page or export the complete retained Evidence if needed.',
'Inspect selected views of completed binary-layout, JavaScript application, native analyze_function, inspect_artifact, inventory_artifact, inspect_wasm_artifact, inspect_evm_interface, inspect_managed_artifact, inspect_android_package or inspect_web_network_capture Evidence without repeating analysis. Historical capture summaries return identity and counts without bodies or archive inventories. Capture views page retained records: view {kind: "capture", detail: "preview" | "complete", offset: 0, limit: 20}; preview selects recorded headline fields and binary descriptors, complete returns exact records with all sidecars. Offsets follow the selected parent record order, preserving original producer ordinals. Paging never reopens the source or reruns the decoder. The api-surface view groups retained HAR requests by method, origin and format-rule path template; templated segments are inferences. It reads no headers or bodies and reports no query values. For a retained record use source: {kind: "retained-evidence", evidence_id: "<parent_evidence_id>"}. Use normalized_result.parent_evidence_id from a summary receipt. Source kind is required. Portable source uses {kind: "inline", evidence: <complete Evidence>}. For layout, JavaScript, native, inventory, WASM, EVM, managed artifact and Android package sources, request a summary with view: {kind: "summary"}; summary is a view kind and has no facet. The WASM summary gives validation, counts, WAT digest/length, tool profile and execution/network states without reading WAT text. Select a layout mitigations/linkage facet, one section/symbol/module, or a stable page. Module pages include assets, bundled and source modules; use node_id for ambiguous or unavailable paths. Inventory uses view: {kind: "inventory", collection: "occurrences", logical_paths: ["package.json"], offset: 0, limit: 64}. Inventory views page nodes, occurrences, edges or integrity contradictions; occurrence logical_paths matches exact recorded strings (omitted means all available, [] means none). source_complete distinguishes complete inventories from partial source records; page exhaustion does not prove absence in a partial source. WASM views use kind wasm and facet sections/imports/exports/candidates/glue/wat/headers/details/tool_profile; text facets use UTF-16 code units and source forms remain exact. EVM summary returns all inferred interface candidates, carrier/decoded identities and explicit unknowns without hex or duplicate stdout. EVM views use kind evm and facet functions/bytecode_hex/stdout/stderr; function pages preserve the original candidate rows, and text offsets/limits count UTF-16 code units (hex has two characters per decoded byte). Exhausting candidate pages does not establish global interface completeness. Managed artifact summaries preserve complete identity/classification, metadata status, module/MVID, frameworks, coverage and limitations without unrelated inventories. Managed pages use kind managed and collection references/resources/attributes, retaining source coverage and build-local token scope; page exhaustion does not establish complete metadata. Android package summaries retain all declarations, engine identity and counts without decoded manifest text. Android manifest pages use kind android, facet manifest, offset and limit in UTF-16 code units; source_status preserves partial decoded coverage even when the retained text page is exhausted. Native views select procedure, pseudocode, assembly, basic blocks, comments, callers/callees, references, unresolved calls, referenced strings/names, native API and high-pcode. Use nonnegative offset and a positive caller-selected limit (defaults 0 and 64); pseudocode uses UTF-16 code units, other native pages use rows. Returns artifact identity, parent Evidence ID, a distinct view digest, coverage, limitations and unknowns. Actual serialized size determines MCP transport admission; choose a smaller page or export the complete retained Evidence if needed.',
inputSchema: inspectAnalysisViewInputSchema,
outputSchema: evidenceResultOf(analysisViewResultSchema),
examples: [
@@ -4,7 +4,11 @@ import {
} from "../domain/webNetworkCapture.js";
import type { ToolContract } from "./toolContractTypes.js";
import { toolContractMetadata } from "./toolEffects.js";
import { evidenceResultOf } from "./toolOutputSchemaPrimitives.js";
import {
analysisDetailSchema,
analysisDetailOutputSchemaOf,
} from "./analysisDetail.js";
import { webNetworkCaptureSummaryViewResultSchema } from "../domain/analysisView/analysisView.js";
/** Historical inspection is distinct from live browser capture and runtime authority. */
export const WEB_NETWORK_CAPTURE_TOOL_CONTRACTS = [
@@ -12,10 +16,15 @@ export const WEB_NETWORK_CAPTURE_TOOL_CONTRACTS = [
name: "inspect_web_network_capture",
...toolContractMetadata("inspect_web_network_capture"),
description:
"Inspect retained local HAR 1.2 or native mitmproxy capture records without contacting recorded URLs. Returns original ordinals, pointers/byte ranges, preserved producer fields, precise numeric representations and exact exposed binary bytes inline. Excludes known transport credentials and explicitly marked values. Decodes every record before optional ordinal selection; malformed or oversized captures return no partial success. Duplicate HAR members are invalid; prototype-named members such as __proto__ are preserved as ordinary own JSON members without prototype mutation. Uses pinned upstream parsers, owned temporary files and bounded offline processes. Native mitmproxy requires caller-supplied mitmdump 12.2.3 on Linux. Browser transaction IDs, runtime attribution and original bytes of HAR Unicode text remain unknown.",
"Request detail summary for capture identity and counts; then page retained records with inspect_analysis_view kind capture (preview or complete), avoiding repeated decoding. Default detail complete preserves existing inline delivery. Inspect retained local HAR 1.2, native mitmproxy, or Fiddler SAZ capture records without contacting recorded URLs. Returns original ordinals, pointers/byte ranges/archive member names, preserved producer fields, precise numeric representations and exact exposed binary bytes inline. Excludes known transport credentials and explicitly marked values. Decodes every record before optional ordinal selection; malformed or oversized captures return no partial success. Duplicate HAR members are invalid; prototype-named members such as __proto__ are preserved as ordinary own JSON members without prototype mutation. Uses pinned upstream parsers, owned temporary files and bounded offline processes. SAZ preserves HTTP fields, XML metadata and encoded body/message bytes without replay or HAR conversion; encrypted archives are unsupported. Native mitmproxy requires caller-supplied mitmdump 12.2.3 on Linux. Browser transaction IDs, runtime attribution and original bytes of HAR Unicode text remain unknown.",
kind: "browser-provider",
inputSchema: inspectWebNetworkCaptureInputSchema,
outputSchema: evidenceResultOf(webNetworkCaptureSchema),
inputSchema: inspectWebNetworkCaptureInputSchema.extend({
detail: analysisDetailSchema,
}),
outputSchema: analysisDetailOutputSchemaOf(
webNetworkCaptureSchema,
webNetworkCaptureSummaryViewResultSchema,
),
examples: [
{
title: "Inspect historical HAR entries",
+33 -5
View File
@@ -55,6 +55,12 @@ import {
wasmViewItemSchema,
wasmSummarySchema,
} from "./wasmArtifactView.js";
import {
projectWebNetworkCaptureView,
captureViewRequestSchema,
captureViewItemsSchema,
captureSummarySchema,
} from "./webNetworkCaptureView.js";
import type { WebNetworkApiSurfaceAccumulator } from "./WebNetworkApiSurfaceAccumulator.js";
import {
projectAndroidPackageView,
@@ -177,6 +183,7 @@ export const analysisViewRequestSchema = z.discriminatedUnion("kind", [
managedViewRequestSchema,
androidViewRequestSchema,
apiSurfaceViewSchema,
captureViewRequestSchema,
]);
// The application owns admission and authenticated Evidence parsing. SDK and
@@ -216,7 +223,7 @@ const artifactSchema = z.strictObject({
path: z
.string()
.describe("Exact recorded artifact path, including an empty string."),
sha256: digestSchema,
sha256: digestSchema.nullable(),
});
const layoutSummarySchema = z.strictObject({
@@ -290,6 +297,7 @@ const viewResultBase = {
"inspect_wasm_artifact",
"inspect_evm_interface",
"inspect_managed_artifact",
"inspect_web_network_capture",
"inspect_android_package",
]),
parent_digest: digestSchema,
@@ -348,6 +356,13 @@ export const managedArtifactSummaryViewResultSchema = z.strictObject({
summary: managedSummarySchema,
});
/** Exact historical capture summary, with no body or member-inventory payload. */
export const webNetworkCaptureSummaryViewResultSchema = z.strictObject({
...summaryViewResultBase,
parent_operation: z.literal("inspect_web_network_capture"),
summary: captureSummarySchema,
});
/** Exact package declarations with retained decoded manifest text. */
export const androidPackageSummaryViewResultSchema = z.strictObject({
...summaryViewResultBase,
@@ -377,9 +392,17 @@ export const analysisViewResultSchema = z.discriminatedUnion("kind", [
wasmSummarySchema,
evmSummarySchema,
managedSummarySchema,
captureSummarySchema,
androidPackageSummarySchema,
]),
}),
z.strictObject({
kind: z.literal("capture"),
view: captureViewRequestSchema,
items: captureViewItemsSchema,
...viewResultBase,
parent_operation: z.literal("inspect_web_network_capture"),
}),
z.strictObject({
kind: z.literal("facet"),
view: facetViewSchema,
@@ -500,6 +523,10 @@ export type UnsignedAnalysisView =
>
| Omit<Extract<AnalysisViewResult, { readonly kind: "wasm" }>, "view_digest">
| Omit<Extract<AnalysisViewResult, { readonly kind: "evm" }>, "view_digest">
| Omit<
Extract<AnalysisViewResult, { readonly kind: "capture" }>,
"view_digest"
>
| Omit<
Extract<AnalysisViewResult, { readonly kind: "managed" }>,
"view_digest"
@@ -603,10 +630,7 @@ export const projectAnalysisView = (
},
]),
);
if (
view.kind === "api-surface" ||
parent.operation === "inspect_web_network_capture"
) {
if (view.kind === "api-surface") {
const projected = projectWebNetworkApiSurfaceView(
parent,
view,
@@ -614,6 +638,10 @@ export const projectAnalysisView = (
);
return projected.ok ? ok(sealAnalysisView(projected.value)) : projected;
}
if (parent.operation === "inspect_web_network_capture") {
const projected = projectWebNetworkCaptureView(parent, view);
return projected.ok ? ok(sealAnalysisView(projected.value)) : projected;
}
if (parent.operation === "inspect_binary_layout") {
const layout = binaryLayoutSchema.safeParse(parent.normalizedResult);
if (!layout.success)
@@ -241,22 +241,14 @@ export const projectJavaScriptApplicationView = (
analysis: JavaScriptApplicationAnalysisResult,
view: Exclude<AnalysisViewRequest, { kind: "api-surface" }>,
): Result<UnsignedAnalysisView, AnalysisError> => {
if (view.kind !== "summary" && view.kind !== "item" && view.kind !== "page")
return err(
jsIncompatible(
"JavaScript application Evidence supports summary, module item or module page views.",
),
);
const shared = parentFields(parent, analysis);
const modules = moduleEntries(analysis.graph.nodes);
if (view.kind === "native")
return err(
jsIncompatible("Native views require analyze_function Evidence."),
);
if (view.kind === "inventory")
return err(jsIncompatible("Inventory views require artifact Evidence."));
if (view.kind === "wasm")
return err(
jsIncompatible("WASM views require inspect_wasm_artifact Evidence."),
);
if (view.kind === "evm")
return err(
jsIncompatible("EVM views require inspect_evm_interface Evidence."),
);
if (view.kind === "summary")
return ok({
kind: "summary",
@@ -291,12 +283,6 @@ export const projectJavaScriptApplicationView = (
coverage: completeCoverage(modules.length, analysis.graph.nodes.length),
...shared,
});
if (view.kind === "facet")
return err(
jsIncompatible(
"facet views apply to inspect_binary_layout Evidence (mitigations or linkage).",
),
);
if (view.kind === "item") {
if (view.collection !== "modules")
return err(
@@ -85,11 +85,13 @@ it("pages groups independently of record completeness and preserves null digests
});
it("rejects native captures and malformed parents with typed errors", () => {
const capture = analysisViewCaptureFixture();
capture.format = "mitmproxy";
expect(project(capture)).toMatchObject({
ok: false,
error: { _tag: "AnalysisUnsupportedTargetError" },
});
for (const format of ["mitmproxy", "saz"] as const) {
capture.format = format;
expect(project(capture)).toMatchObject({
ok: false,
error: { _tag: "AnalysisUnsupportedTargetError" },
});
}
expect(
projectAnalysisView(
{
@@ -0,0 +1,176 @@
import { expect, it } from "vitest";
import { createHash } from "node:crypto";
import { createEvidence } from "../evidence.js";
import { webNetworkCaptureSchema } from "../webNetworkCapture.js";
import { projectAnalysisView } from "./analysisView.js";
const capture = () => {
const body = Buffer.from("original body");
const artifact = {
path: "/recordings/input.saz",
sha256: "b".repeat(64),
bytes: 100,
};
const decoder = { id: "fixture", name: "Fixture", version: "1" };
const report = webNetworkCaptureSchema.parse({
artifact,
decoder,
format: "saz",
runtime_attribution: "unknown",
limitations: [],
container: {
reported: { members: [] },
numeric_literals: [],
redactions: [],
records_pointer: null,
},
total_records: 9,
records: [7, 2].map((ordinal) => ({
ordinal,
location: { kind: "archive-members", members: [`raw/${ordinal}_c.txt`] },
reported: {
session_id: String(ordinal),
time: null,
request: {
method: "POST",
url: "https://example.invalid/a",
body: body.toString(),
},
},
binary_fields: [
{
pointer: "/request/body",
representation: "producer-bytes",
state: "retained",
content_base64: body.toString("base64"),
bytes: body.length,
sha256: createHash("sha256").update(body).digest("hex"),
},
],
numeric_literals: [
{
pointer: "/time",
producer_type: "json-number",
literal: "9007199254740993",
},
],
redactions: [],
limitations: [],
})),
});
const evidence = createEvidence({ ...artifact, format: "file" }, decoder, {
operation: "inspect_web_network_capture",
parameters: { record_ordinals: [7, 2] },
result: report,
confidence: "observed",
authority: "historical-reference",
});
return {
report,
parent: {
evidenceId: evidence.evidence_id,
operation: evidence.operation,
normalizedResult: report,
subject: evidence.subject,
limitations: evidence.limitations,
},
};
};
it("keeps selected-record page coverage distinct from capture coverage and unsafe number absence", () => {
const { parent, report } = capture();
const summary = projectAnalysisView(parent, { kind: "summary" });
expect(summary.ok && summary.value).toMatchObject({
summary: { total_records: 9, retained_records: 2 },
coverage: { examined: 2, total: 2 },
});
const preview = projectAnalysisView(parent, {
kind: "capture",
detail: "preview",
offset: 0,
limit: 1,
});
expect(preview.ok && preview.value).toMatchObject({
items: [
{
ordinal: 7,
reported: { time: null },
numeric_literals: [{ pointer: "/time", literal: "9007199254740993" }],
},
],
coverage: { total: 2, examined: 1, next_offset: 1, exhausted: false },
});
expect(JSON.stringify(preview)).not.toContain("original body");
expect(JSON.stringify(preview)).not.toContain("content_base64");
const complete = projectAnalysisView(parent, {
kind: "capture",
detail: "complete",
offset: 1,
limit: 1,
});
expect(complete.ok && complete.value).toMatchObject({
items: [report.records[1]],
coverage: { total: 2, examined: 1, next_offset: null, exhausted: true },
});
const empty = projectAnalysisView(parent, {
kind: "capture",
detail: "preview",
offset: 50,
limit: 1,
});
expect(empty.ok && empty.value).toMatchObject({
items: [],
coverage: {
status: "empty",
total: 2,
examined: 0,
next_offset: null,
exhausted: true,
},
});
});
it("retains explicitly unknown digests and rejects a different subject path", () => {
const { parent, report } = capture();
const unknown = projectAnalysisView(
{
...parent,
subject: null,
normalizedResult: {
...report,
artifact: { ...report.artifact, sha256: null },
},
},
{ kind: "summary" },
);
expect(unknown.ok && unknown.value).toMatchObject({
artifact: { sha256: null },
});
expect(unknown.ok && unknown.value.unknowns.join(" ")).toContain(
"excluded artifact digest",
);
if (parent.subject === null) throw new Error("Expected fixture subject");
const mismatched = projectAnalysisView(
{
...parent,
subject: { ...parent.subject, local_path: "/different/source.saz" },
},
{ kind: "summary" },
);
expect(mismatched.ok).toBe(false);
});
it("rejects invalid ordinal coverage in a borrowed parent", () => {
const { parent, report } = capture();
const result = projectAnalysisView(
{
...parent,
normalizedResult: {
...report,
records: [report.records[0], report.records[0]],
},
},
{ kind: "summary" },
);
expect(result.ok).toBe(false);
});
@@ -0,0 +1,212 @@
import { z } from "zod";
import type { AnalysisError } from "../analysisErrorBase.js";
import { analysisInputErrorFromIssues } from "../inputIssueProjection.js";
import type { JsonValue } from "../jsonValue.js";
import { err, ok, type Result } from "../result.js";
import {
webNetworkCaptureRecordSchema,
webNetworkCaptureBinaryDescriptorSchema,
webNetworkCaptureSchema,
type WebNetworkCaptureRecord,
} from "../webNetworkCapture.js";
import {
analysisViewInputError,
type AnalysisViewParent,
type AnalysisViewRequest,
type UnsignedAnalysisView,
} from "./analysisView.js";
import {
completeWithinViewCoverage,
pageViewCoverage,
} from "./analysisViewCoverage.js";
/** Offsets page the parent's retained record order, preserving producer ordinals. */
export const captureViewRequestSchema = z.strictObject({
kind: z.literal("capture"),
detail: z.enum(["preview", "complete"]).default("preview"),
offset: z.number().int().nonnegative().default(0),
limit: z.number().int().positive().default(64),
});
const previewSchema = webNetworkCaptureRecordSchema.extend({
binary_fields: z.array(webNetworkCaptureBinaryDescriptorSchema),
});
export const captureViewItemsSchema = z.array(
z.union([webNetworkCaptureRecordSchema, previewSchema]),
);
/** Count/identity overview; bodies and the archive member inventory stay in the parent. */
export const captureSummarySchema = z.strictObject({
artifact: webNetworkCaptureSchema.shape.artifact,
decoder: webNetworkCaptureSchema.shape.decoder,
format: webNetworkCaptureSchema.shape.format,
total_records: z.number().int().nonnegative(),
retained_records: z.number().int().nonnegative(),
runtime_attribution: z.literal("unknown"),
});
const object = (
value: JsonValue,
): Readonly<Record<string, JsonValue>> | null =>
value !== null && typeof value === "object" && !Array.isArray(value)
? value
: null;
/** Select already parsed producer facts without interpreting HTTP or payload bytes again. */
const preview = (record: WebNetworkCaptureRecord) => {
const source = object(record.reported);
const reported: Record<string, JsonValue> = {};
const selectedPointers = new Set<string>();
if (source !== null) {
for (const key of ["session_id", "name", "time", "startedDateTime", "type"])
if (Object.hasOwn(source, key) && source[key] !== undefined) {
reported[key] = source[key];
selectedPointers.add(`/${key}`);
}
for (const key of ["request", "response"]) {
const value = source[key];
if (!Object.hasOwn(source, key) || value === undefined) continue;
const fields = object(value);
if (fields === null) {
// Preserve a reported null/scalar instead of inventing HTTP fields.
reported[key] = value;
continue;
}
const selected: Record<string, JsonValue> = {};
for (const field of [
"method",
"url",
"httpVersion",
"http_version",
"status",
"statusText",
"reason",
])
if (Object.hasOwn(fields, field) && fields[field] !== undefined) {
selected[field] = fields[field];
selectedPointers.add(`/${key}/${field}`);
}
reported[key] = selected;
}
}
return {
...record,
reported: source === null ? record.reported : reported,
binary_fields: record.binary_fields.map(
({ content_base64: _content, ...field }) => field,
),
numeric_literals:
source === null
? record.numeric_literals
: record.numeric_literals.filter(({ pointer }) =>
selectedPointers.has(pointer),
),
};
};
/** Page immutable retained facts; no capture file, provider or network is consulted. */
export const projectWebNetworkCaptureView = (
parent: AnalysisViewParent,
view: AnalysisViewRequest,
): Result<UnsignedAnalysisView, AnalysisError> => {
if (view.kind !== "summary" && view.kind !== "capture")
return err(
analysisViewInputError([
{
path: ["view"],
reason: "invalid_value",
message:
"Historical captures support summary, capture record pages, and admitted api-surface views.",
},
]),
);
const parsed = webNetworkCaptureSchema.safeParse(parent.normalizedResult);
if (!parsed.success)
return err(
analysisInputErrorFromIssues(
"inspect_analysis_view",
parsed.error.issues.map((issue) => ({
...issue,
path: ["source", "normalized_result", ...issue.path],
})),
{ source: { normalized_result: parent.normalizedResult } },
{ cause: parsed.error },
),
);
const report = parsed.data;
if (
report.artifact.sha256 !== null &&
(parent.subject == null ||
parent.subject.local_path !== report.artifact.path ||
parent.subject.digest.sha256 !== report.artifact.sha256)
)
return err(
analysisViewInputError([
{
path: ["source", "subject"],
reason: "invalid_value",
message:
"Capture parent subject must identify the report's exact path and SHA-256.",
},
]),
);
const shared = {
parent_evidence_id: parent.evidenceId,
parent_operation: "inspect_web_network_capture" as const,
parent_digest: parent.evidenceId.slice(3),
artifact: { path: report.artifact.path, sha256: report.artifact.sha256 },
limitations: [
...new Set([
...parent.limitations,
...report.limitations,
"Pages cover the records selected into the parent Evidence, in that selected order; total_records counts the original decoded capture.",
]),
],
unknowns: [
"Historical records do not establish live runtime attribution or deployment authenticity.",
...(report.artifact.sha256 === null
? ["The explicitly excluded artifact digest remains unknown."]
: []),
],
};
if (view.kind === "summary")
return ok({
kind: "summary",
view,
...shared,
summary: {
artifact: report.artifact,
decoder: report.decoder,
format: report.format,
total_records: report.total_records,
retained_records: report.records.length,
runtime_attribution: report.runtime_attribution,
},
coverage: completeWithinViewCoverage(
report.records.length,
report.records.length,
),
});
const records = report.records.slice(view.offset, view.offset + view.limit);
return ok({
kind: "capture",
view,
...shared,
items: captureViewItemsSchema.parse(
view.detail === "complete" ? records : records.map(preview),
),
coverage: pageViewCoverage(
view.offset,
records.length,
report.records.length,
),
limitations: [
...shared.limitations,
...(view.detail === "preview"
? [
"Preview selects recorded headline fields and byte descriptors; headers, XML, bodies, message data and other fields remain in complete record pages. Missing preview fields do not establish their absence from the producer record.",
]
: []),
],
});
};
+43 -17
View File
@@ -10,12 +10,14 @@ export const WEB_NETWORK_CAPTURE_LIMITS = {
depth: 64,
} as const;
export const WEB_NETWORK_CAPTURE_FORMATS = ["har", "mitmproxy", "saz"] as const;
/** Inspect an explicit retained capture without contacting its recorded destinations. */
export const inspectWebNetworkCaptureInputSchema = z.strictObject({
capture_path: localPathStringSchema.describe(
"Absolute local HAR or mitmproxy capture path.",
"Absolute local HAR, mitmproxy, or Fiddler SAZ capture path.",
),
format: z.enum(["har", "mitmproxy"]),
format: z.enum(WEB_NETWORK_CAPTURE_FORMATS),
record_ordinals: z
.array(z.number().int().nonnegative())
.min(1)
@@ -31,6 +33,21 @@ export const inspectWebNetworkCaptureInputSchema = z.strictObject({
),
});
/** Read declarations through their owning schema for service and adapter failures. */
export const captureSensitiveValues = (
rawInput: unknown,
): readonly string[] => {
const parsed =
inspectWebNetworkCaptureInputSchema.shape.sensitive_values.safeParse(
typeof rawInput === "object" &&
rawInput !== null &&
"sensitive_values" in rawInput
? rawInput.sensitive_values
: undefined,
);
return parsed.success ? parsed.data : [];
};
const pointerSchema = z
.string()
.describe("RFC 6901 pointer in the producer's reported object.");
@@ -53,21 +70,26 @@ const binaryBase = {
pointer: pointerSchema,
representation: z.enum(["producer-bytes", "har-base64-content"]),
};
const retainedBinaryDescriptorSchema = z.strictObject({
...binaryBase,
state: z.literal("retained"),
bytes: z.number().int().nonnegative(),
sha256: z.string().regex(/^[0-9a-f]{64}$/),
});
const redactedBinaryDescriptorSchema = z.strictObject({
...binaryBase,
state: z.literal("redacted"),
bytes: z.null(),
sha256: z.null(),
});
/** Exact byte facts shared by complete records and previews that omit content. */
export const webNetworkCaptureBinaryDescriptorSchema = z.discriminatedUnion(
"state",
[retainedBinaryDescriptorSchema, redactedBinaryDescriptorSchema],
);
const binarySchema = z.discriminatedUnion("state", [
z.strictObject({
...binaryBase,
state: z.literal("retained"),
content_base64: z.string(),
bytes: z.number().int().nonnegative(),
sha256: z.string().regex(/^[0-9a-f]{64}$/),
}),
z.strictObject({
...binaryBase,
state: z.literal("redacted"),
content_base64: z.null(),
bytes: z.null(),
sha256: z.null(),
}),
retainedBinaryDescriptorSchema.extend({ content_base64: z.string() }),
redactedBinaryDescriptorSchema.extend({ content_base64: z.null() }),
]);
const numberSchema = z.strictObject({
pointer: pointerSchema,
@@ -84,6 +106,10 @@ export const webNetworkCaptureRecordSchema = z.strictObject({
reason: z.literal("explicit-sensitive-value"),
}),
z.strictObject({ kind: z.literal("json-pointer"), pointer: pointerSchema }),
z.strictObject({
kind: z.literal("archive-members"),
members: z.array(z.string()).min(1),
}),
z.strictObject({
kind: z.literal("byte-range"),
offset: z.number().int().nonnegative(),
@@ -156,7 +182,7 @@ export const webNetworkCaptureSchema =
),
bytes: z.number().int().nonnegative(),
}),
format: z.enum(["har", "mitmproxy"]),
format: z.enum(WEB_NETWORK_CAPTURE_FORMATS),
runtime_attribution: z.literal("unknown"),
limitations: z.array(z.string()),
});
@@ -22,6 +22,8 @@ export const excludeCaptureCoordinates = (
const location =
(record.location.kind === "json-pointer" &&
sensitive(record.location.pointer)) ||
(record.location.kind === "archive-members" &&
record.location.members.some(sensitive)) ||
(record.location.kind === "byte-range" &&
(sensitive(String(record.location.offset)) ||
sensitive(String(record.location.bytes))))
+1 -1
View File
@@ -5,5 +5,5 @@ export const PACKAGE_METADATA = {
serverSdkVersion: "2.3.1",
clientSdkVersion: "2.3.1",
coreSdkVersion: "2.3.1",
skillVersion: "40",
skillVersion: "41",
} as const;
+1
View File
@@ -617,6 +617,7 @@ const registerConfiguredAnalysisTools = (
toolLogger,
recordEvidence,
withAdmittedAnalysis,
evidenceById,
);
registerJavaScriptRecoveryTool(
server,
+35 -1
View File
@@ -1,5 +1,14 @@
import type { EvidenceMcpServer } from "./EvidenceMcpServer.js";
import type { StandardSchemaWithJSON } from "@modelcontextprotocol/server";
import type { EvidenceLookup } from "../application/EvidenceInputResolver.js";
import {
requireAnalysisViewRetention,
summarizeRetainedAnalysis,
} from "../application/analysisView/AnalysisViewService.js";
import { analysisInputErrorFromIssues } from "../domain/inputIssueProjection.js";
import { redactExplicitFailure } from "../domain/explicitSensitiveFailure.js";
import { captureSensitiveValues } from "../domain/webNetworkCapture.js";
import { err } from "../domain/result.js";
import type { WebNetworkCaptureService } from "../application/WebNetworkCaptureService.js";
import type { EvidenceWriter } from "../application/investigation/InvestigationRecordPort.js";
import { toolContract } from "../contracts/toolContracts.js";
@@ -15,6 +24,7 @@ export const registerWebNetworkCaptureTool = (
logger: Logger,
recordEvidence?: EvidenceWriter["recordEvidence"],
withAdmittedAnalysis?: WithAdmittedAnalysis,
evidenceById?: EvidenceLookup,
): void => {
const contract = toolContract("inspect_web_network_capture");
const registration = toolRegistrationOptions(contract);
@@ -30,7 +40,31 @@ export const registerWebNetworkCaptureTool = (
};
const handler = createEvidenceToolHandler(
server,
(input, options) => service.inspect(input, options),
async (rawInput, options) => {
const parsed = contract.inputSchema.safeParse(rawInput);
if (!parsed.success)
return err(
redactExplicitFailure(
analysisInputErrorFromIssues(
contract.name,
parsed.error.issues,
rawInput,
{ cause: parsed.error },
),
captureSensitiveValues(rawInput),
),
);
const { detail, ...input } = parsed.data;
const retention = { recordEvidence, evidenceById };
if (detail === "summary") {
const ready = requireAnalysisViewRetention(contract.name, retention);
if (!ready.ok) return ready;
}
const inspected = await service.inspect(input, options);
return detail === "summary" && inspected.ok
? summarizeRetainedAnalysis(inspected.value, retention)
: inspected;
},
logger,
recordEvidence,
withAdmittedAnalysis,
@@ -5,7 +5,7 @@ import { fileURLToPath } from "node:url";
import { promisify } from "node:util";
import { expect, it } from "vitest";
import { HAR_CAPTURE_PROCESS_MEMORY_LIMIT_BYTES } from "../../../src/browser/history/CaptureRelease.js";
import { NODE_CAPTURE_PROCESS_MEMORY_LIMIT_BYTES } from "../../../src/browser/history/CaptureRelease.js";
import { WEB_NETWORK_CAPTURE_LIMITS } from "../../../src/domain/webNetworkCapture.js";
import { connectLocalToolsMcp } from "../../fixtures/localToolsMcp.js";
import { parseMcpToolError } from "../../fixtures/mcpToolError.js";
@@ -46,7 +46,7 @@ const capture = (count: number) =>
// Node.js enforces the decoder's V8 heap flags. Bun ignores them; on Windows
// the decoder's Job Object commit limit is the enforced bound instead, and
// Bun elsewhere has no bound this capture can reach.
const jobLimit = HAR_CAPTURE_PROCESS_MEMORY_LIMIT_BYTES;
const jobLimit = NODE_CAPTURE_PROCESS_MEMORY_LIMIT_BYTES;
it.skipIf(process.versions.bun !== undefined && jobLimit === null)(
"reports real HAR memory exhaustion through CLI and MCP and preserves server usability",
async () => {
@@ -0,0 +1,499 @@
import { createHash } from "node:crypto";
import { execFile } from "node:child_process";
import { readFile, unlink, writeFile } from "node:fs/promises";
import { join } from "node:path";
import { promisify } from "node:util";
import { fileURLToPath } from "node:url";
import { gzipSync } from "node:zlib";
import { Uint8ArrayReader, Uint8ArrayWriter, ZipWriter } from "@zip.js/zip.js";
import { Ajv2020 } from "ajv/dist/2020.js";
import { expect, it as runtimeTest } from "vitest";
import { z } from "zod";
import { parseEvidence } from "../../../src/domain/evidence.js";
import { webNetworkCaptureSchema } from "../../../src/domain/webNetworkCapture.js";
import { connectLocalToolsMcp } from "../../fixtures/localToolsMcp.js";
import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js";
import {
NODE_CAPTURE_HEAP_ENFORCED,
NODE_CAPTURE_PROCESS_MEMORY_LIMIT_BYTES,
} from "../../../src/browser/history/CaptureRelease.js";
const hasMemoryBound =
NODE_CAPTURE_HEAP_ENFORCED ||
NODE_CAPTURE_PROCESS_MEMORY_LIMIT_BYTES !== null;
const it = runtimeTest.skipIf(!hasMemoryBound);
// Synthetic member fixtures based on Telerik's SAZ layout; not a real-producer golden.
const compressedBody = gzipSync(Buffer.from("stored compressed body"));
const members = (): [string, Buffer][] => [
[
"raw/0042_s.txt",
Buffer.concat([
Buffer.from(
"HTTP/1.1 200 OK\r\nContent-Encoding: gzip\r\nSet-Cookie: server-credential\r\n\r\n",
),
compressedBody,
]),
],
[
"raw/7_c.txt",
Buffer.concat([
Buffer.from("POST /bytes HTTP/2.0\r\n:authority: example.test\r\n\r\n"),
Buffer.from([0xff, 0, 0x80]),
]),
],
[
"raw/0042_c.txt",
Buffer.from(
"GET https://user:password@example.test/a HTTP/1.1\r\nAuthorization: Bearer client-credential\r\nCookie: cookie-credential\r\nX-Duplicate: one\r\nX-Duplicate: two\r\nX-Text: unicode-\u03b2\r\n\r\n",
),
],
[
"raw/0042_m.xml",
Buffer.from(
'<Session SID="0042"><SessionTimers ClientConnected="2026-10-13T00:00:00Z"/><SessionFlags><SessionFlag N="x-processinfo" V="fixture:1"/></SessionFlags><Unknown value="01"><![CDATA[retained metadata]]></Unknown></Session>',
),
],
["raw/0042_w.txt", Buffer.from("opaque websocket message file")],
["raw/7_g.txt", Buffer.from([0, 1, 2])],
["[Content_Types].xml", Buffer.from("<Types/>")],
];
const archive = async (entries = members(), password?: string, level = 6) => {
const writer = new ZipWriter(new Uint8ArrayWriter(), {
useWebWorkers: false,
level,
...(password === undefined ? {} : { password }),
});
for (const [name, bytes] of entries)
await writer.add(name, new Uint8ArrayReader(bytes));
return Buffer.from(await writer.close());
};
const fixture = async (bytes: Buffer) => {
const root = await createTestTempDirectory("rea-saz-capture-");
const path = join(root, "capture.saz");
await writeFile(path, bytes);
return path;
};
const entrypoint = fileURLToPath(
new URL("../../../scripts/rea.mjs", import.meta.url),
);
const cliResponse = async (path: string, extra: string[] = []) => {
try {
const { stdout } = await promisify(execFile)(
process.execPath,
[
entrypoint,
"inspect-web-network-capture",
path,
"saz",
"--json",
...extra,
],
{ timeout: 40_000, maxBuffer: 4 * 1024 * 1024 },
);
return { ok: true, stdout };
} catch (cause: unknown) {
if (
cause instanceof Error &&
"stdout" in cause &&
typeof cause.stdout === "string"
)
return { ok: false, stdout: cause.stdout };
throw cause;
}
};
const cli = async (path: string, extra: string[] = []) => {
const result = await cliResponse(path, extra);
expect(result.ok, result.stdout).toBe(true);
return parseEvidence(JSON.parse(result.stdout));
};
it("inspects native SAZ through the public CLI and MCP with equal evidence, bytes and archive coordinates", async () => {
const bytes = await archive();
const path = await fixture(bytes);
const evidence = await cli(path, ["--record", "1", "--record", "0"]);
const capture = webNetworkCaptureSchema.parse(evidence.normalized_result);
expect(capture).toMatchObject({
format: "saz",
total_records: 2,
runtime_attribution: "unknown",
records: [
{ ordinal: 1, reported: { session_id: "7", response: null } },
{
ordinal: 0,
reported: {
session_id: "0042",
request: { url: "https://example.test/a" },
response: { status: "200" },
},
},
],
});
expect(capture.records[1]?.location).toEqual({
kind: "archive-members",
members: [
"raw/0042_s.txt",
"raw/0042_c.txt",
"raw/0042_m.xml",
"raw/0042_w.txt",
],
});
expect(capture.records[1]?.binary_fields).toContainEqual({
pointer: "/response/body",
representation: "producer-bytes",
state: "retained",
content_base64: compressedBody.toString("base64"),
bytes: compressedBody.length,
sha256: createHash("sha256").update(compressedBody).digest("hex"),
});
expect(capture.records[0]?.binary_fields).toContainEqual(
expect.objectContaining({
pointer: "/request/body",
content_base64: "/wCA",
}),
);
expect(capture.records[1]?.binary_fields).toContainEqual(
expect.objectContaining({
pointer: "/request/body",
bytes: 0,
content_base64: "",
}),
);
expect(capture.records[1]?.reported).toMatchObject({
request: {
headers: [
{ name: "Authorization", value: null },
{ name: "Cookie", value: null },
{ name: "X-Duplicate", value: " one" },
{ name: "X-Duplicate", value: " two" },
{ name: "X-Text" },
],
},
});
expect(JSON.stringify(capture)).toContain("retained metadata");
for (const secret of [
"user:password",
"client-credential",
"server-credential",
"cookie-credential",
])
expect(JSON.stringify(capture)).not.toContain(secret);
expect(capture.artifact.sha256).toBe(
createHash("sha256").update(bytes).digest("hex"),
);
expect(await readFile(path)).toEqual(bytes);
const { client, call } = await connectLocalToolsMcp();
const tool = (await client.listTools()).tools.find(
(item) => item.name === "inspect_web_network_capture",
);
if (tool?.outputSchema === undefined)
throw new Error("Missing capture schema");
const args = { capture_path: path, format: "saz", record_ordinals: [1, 0] };
const ajv = new Ajv2020({ strict: false, validateFormats: false });
expect(ajv.validate(tool.inputSchema, args)).toBe(true);
const response = await call("inspect_web_network_capture", args);
expect(response.isError, JSON.stringify(response)).not.toBe(true);
const fromMcp = parseEvidence(response.structuredContent);
expect(fromMcp.normalized_result).toEqual(evidence.normalized_result);
const bundle = await client.callTool({
name: "get_evidence_bundle",
arguments: { detail: "complete" },
});
const records = z
.object({ result: z.object({ records: z.array(z.unknown()) }) })
.parse(bundle.structuredContent)
.result.records.map(parseEvidence);
expect(
records.find((record) => record.evidence_id === fromMcp.evidence_id),
).toEqual(fromMcp);
}, 60_000);
it("pages retained SAZ Evidence through public MCP and portable CLI after source deletion", async () => {
const path = await fixture(await archive());
const { call } = await connectLocalToolsMcp();
const args = { capture_path: path, format: "saz", record_ordinals: [1, 0] };
const full = await call("inspect_web_network_capture", args);
const fromMcp = parseEvidence(full.structuredContent);
const capture = webNetworkCaptureSchema.parse(fromMcp.normalized_result);
const summaryArgs = { ...args, detail: "summary" };
const summaryResponse = await call(
"inspect_web_network_capture",
summaryArgs,
);
expect(summaryResponse.isError, JSON.stringify(summaryResponse)).not.toBe(
true,
);
const summary = parseEvidence(summaryResponse.structuredContent);
expect(summary.normalized_result).toMatchObject({
kind: "summary",
parent_evidence_id: fromMcp.evidence_id,
summary: { format: "saz", total_records: 2, retained_records: 2 },
});
expect(JSON.stringify(summary)).not.toContain(
compressedBody.toString("base64"),
);
// Retained pages must keep their snapshot semantics after the input disappears.
await unlink(path);
const source = {
kind: "retained-evidence",
evidence_id: fromMcp.evidence_id,
};
const previewArgs = {
source,
view: { kind: "capture", offset: 0, limit: 1 },
};
const previewResponse = await call("inspect_analysis_view", previewArgs);
expect(previewResponse.isError, JSON.stringify(previewResponse)).not.toBe(
true,
);
const preview = parseEvidence(previewResponse.structuredContent);
expect(preview.normalized_result).toMatchObject({
items: [
{
ordinal: 1,
reported: {
session_id: "7",
request: { method: "POST", url: "/bytes" },
},
},
],
coverage: { examined: 1, total: 2, next_offset: 1, exhausted: false },
});
expect(JSON.stringify(preview)).not.toContain("content_base64");
const completeResponse = await call("inspect_analysis_view", {
source,
view: { kind: "capture", detail: "complete", offset: 1, limit: 1 },
});
expect(completeResponse.isError, JSON.stringify(completeResponse)).not.toBe(
true,
);
const complete = parseEvidence(completeResponse.structuredContent);
expect(complete.normalized_result).toMatchObject({
items: [capture.records[1]],
coverage: { next_offset: null, exhausted: true },
});
const workflow = join(path, "..", "summary-view.json");
await writeFile(
workflow,
JSON.stringify({
source: { kind: "inline", evidence: fromMcp },
view: { kind: "summary" },
}),
);
const standalone = await promisify(execFile)(process.execPath, [
entrypoint,
"inspect-analysis-view",
workflow,
"--json",
]);
expect(
parseEvidence(JSON.parse(standalone.stdout)).normalized_result,
).toEqual(summary.normalized_result);
}, 60_000);
it("keeps CRLF separators inside a body after LF-only HTTP headers", async () => {
const body = Buffer.from("\ufefffirst\r\n\r\nsecond\n\nthird");
const bytes = await archive([
[
"raw/1_c.txt",
Buffer.concat([
Buffer.from("POST https://example.test/ HTTP/1.1\nX-Test: request\n\n"),
body,
]),
],
[
"raw/1_s.txt",
Buffer.concat([
Buffer.from("HTTP/1.1 200 OK\nX-Test: response\n\n"),
body,
]),
],
]);
const path = await fixture(bytes);
const capture = webNetworkCaptureSchema.parse(
(await cli(path)).normalized_result,
);
expect(capture.records[0]?.reported).toMatchObject({
request: {
headers: [{ name: "X-Test", value: " request" }],
body: body.toString("utf8"),
},
response: {
headers: [{ name: "X-Test", value: " response" }],
body: body.toString("utf8"),
},
});
for (const pointer of ["/request/body", "/response/body"])
expect(capture.records[0]?.binary_fields).toContainEqual({
pointer,
representation: "producer-bytes",
state: "retained",
content_base64: body.toString("base64"),
bytes: body.length,
sha256: createHash("sha256").update(body).digest("hex"),
});
expect(await readFile(path)).toEqual(bytes);
});
it("excludes explicit literals from XML, HTTP byte views, member coordinates and binary sidecars", async () => {
const path = await fixture(await archive());
const result = await cli(path, [
"--sensitive-value",
"0042",
"--sensitive-value",
"unicode-\u03b2",
"--sensitive-value",
compressedBody.toString("base64"),
"--sensitive-value",
"retained metadata",
]);
const capture = webNetworkCaptureSchema.parse(result.normalized_result);
expect(capture.records[0]?.location).toEqual({
kind: "unknown",
reason: "explicit-sensitive-value",
});
expect(capture.records[0]?.binary_fields).toContainEqual({
pointer: "/response/body",
representation: "producer-bytes",
state: "redacted",
bytes: null,
sha256: null,
content_base64: null,
});
const text = JSON.stringify(result);
for (const secret of [
"0042",
"unicode-",
"retained metadata",
compressedBody.toString("base64"),
createHash("sha256").update(compressedBody).digest("hex"),
])
expect(text).not.toContain(secret);
});
it("retains folded headers, stored chunk framing and BOM-marked UTF-16 metadata", async () => {
const body = "3\r\none\r\n0\r\n\r\n";
const path = await fixture(
await archive([
[
"raw/1_s.txt",
Buffer.from(
`HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\nX-Folded: first\r\n second\r\n\r\n${body}`,
),
],
[
"raw/1_m.xml",
Buffer.from(
'\ufeff<?xml version="1.0" encoding="utf-16"?><Session><Note>\u03b2</Note><Source><![CDATA[<!DOCTYPE html>]]></Source></Session>',
"utf16le",
),
],
[
"raw/2_c.txt",
Buffer.from(
"CONNECT example.test:443 HTTP/1.1\nHost: example.test:443\n\n",
),
],
]),
);
const evidence = await cli(path);
const capture = webNetworkCaptureSchema.parse(evidence.normalized_result);
expect(capture.records[0]?.reported).toMatchObject({
request: null,
response: {
body,
headers: [
{ name: "Transfer-Encoding", value: " chunked" },
{ name: "X-Folded", value: " first\r\n second" },
],
},
});
expect(capture.records[0]?.binary_fields).toContainEqual(
expect.objectContaining({
pointer: "/response/body",
content_base64: Buffer.from(body).toString("base64"),
}),
);
expect(JSON.stringify(capture.records[0]?.reported)).toContain("\u03b2");
expect(capture.records[1]?.reported).toMatchObject({
request: { method: "CONNECT", url: "example.test:443", body: "" },
});
});
it.each([
"bad-http",
"bad-xml",
"dtd",
"unknown-member",
"encrypted",
"duplicate",
"crc",
"expanded-limit",
])("rejects %s before returning a selected subset", async (variant) => {
let entries = members();
if (variant === "bad-http")
entries.push(["raw/8_c.txt", Buffer.from("malformed-private-input")]);
if (variant === "bad-xml")
entries.push(["raw/8_m.xml", Buffer.from("<Session><Broken></Session>")]);
if (variant === "dtd")
entries.push([
"raw/8_m.xml",
Buffer.from(
'<!DOCTYPE Session [<!ENTITY external SYSTEM "file:///private-input">]><Session>&external;</Session>',
),
]);
if (variant === "unknown-member")
entries.push(["raw/8_unknown.txt", Buffer.from("unknown extension")]);
if (variant === "duplicate")
entries = [
["raw/8_c.txt", Buffer.from("GET /a HTTP/1.1\r\n\r\n")],
["raw/9_c.txt", Buffer.from("GET /b HTTP/1.1\r\n\r\n")],
];
let bytes = await archive(
entries,
variant === "encrypted" ? "fixture-password" : undefined,
variant === "crc" ? 0 : 6,
);
if (variant === "duplicate")
bytes = Buffer.from(
bytes.toString("latin1").replaceAll("raw/9_c.txt", "raw/8_c.txt"),
"latin1",
);
if (variant === "crc") {
const offset = bytes.indexOf(Buffer.from("HTTP/1.1 200 OK"));
expect(offset).toBeGreaterThan(0);
bytes[offset + 9] = 0x35;
}
if (variant === "expanded-limit") {
const central = bytes.indexOf(Buffer.from([0x50, 0x4b, 0x01, 0x02]));
bytes.writeUInt32LE(33 * 1024 * 1024, central + 24);
}
const path = await fixture(bytes);
const result = await cliResponse(path, ["--record", "0"]);
expect(result.ok, result.stdout).toBe(false);
expect(JSON.parse(result.stdout).category).toBe(
["encrypted", "unknown-member", "dtd"].includes(variant)
? "unsupported_provider"
: variant === "expanded-limit"
? "execution_failure"
: "invalid_input",
);
if (variant === "expanded-limit")
expect(JSON.parse(result.stdout).code).toBe("unreadable_output");
expect(JSON.stringify(result)).not.toContain("malformed-private-input");
});
runtimeTest.runIf(!hasMemoryBound)(
"reports SAZ unavailable when its runtime cannot enforce decoder memory",
async () => {
const response = await cliResponse(await fixture(await archive()));
expect(response.ok).toBe(false);
expect(JSON.parse(response.stdout)).toMatchObject({
code: "capability_unavailable",
});
},
);
+23 -6
View File
@@ -70,7 +70,7 @@ msgstr "بواسطة N0zoM1z0 * 10 أكتوبر 2026"
#: website/public/faq/index.html:573
#: website/public/first-investigation/index.html:523
#: website/public/get-started/index.html:882
#: website/public/guides/browser/index.html:498
#: website/public/guides/browser/index.html:507
#: website/public/guides/index.html:282
#: website/public/guides/javascript/index.html:503
#: website/public/guides/native/index.html:509 website/public/index.html:1191
@@ -4660,7 +4660,7 @@ msgctxt "page:guides/browser/index.html"
msgid "Copy read the loaded scripts"
msgstr "نسخ قراءة البرامج النصية المحملة"
#: website/public/guides/browser/index.html:498
#: website/public/guides/browser/index.html:507
msgctxt "page:guides/browser/index.html"
msgid "Back to top"
msgstr "العودة للأعلى"
@@ -11469,7 +11469,7 @@ msgstr "الأدلة"
#: website/public/faq/index.html:565
#: website/public/first-investigation/index.html:515
#: website/public/get-started/index.html:874
#: website/public/guides/browser/index.html:490
#: website/public/guides/browser/index.html:499
#: website/public/guides/index.html:274
#: website/public/guides/javascript/index.html:495
#: website/public/guides/native/index.html:501 website/public/index.html:1183
@@ -11489,7 +11489,7 @@ msgstr "الكود المصدري"
#: website/public/faq/index.html:566
#: website/public/first-investigation/index.html:516
#: website/public/get-started/index.html:875
#: website/public/guides/browser/index.html:491
#: website/public/guides/browser/index.html:500
#: website/public/guides/index.html:275
#: website/public/guides/javascript/index.html:496
#: website/public/guides/native/index.html:502 website/public/index.html:1184
@@ -11509,7 +11509,7 @@ msgstr "رخصة MIT"
#: website/public/faq/index.html:567
#: website/public/first-investigation/index.html:517
#: website/public/get-started/index.html:876
#: website/public/guides/browser/index.html:492
#: website/public/guides/browser/index.html:501
#: website/public/guides/index.html:276
#: website/public/guides/javascript/index.html:497
#: website/public/guides/native/index.html:503 website/public/index.html:1185
@@ -11529,7 +11529,7 @@ msgstr "انضم إلى المجتمع"
#: website/public/faq/index.html:568
#: website/public/first-investigation/index.html:518
#: website/public/get-started/index.html:877
#: website/public/guides/browser/index.html:493
#: website/public/guides/browser/index.html:502
#: website/public/guides/index.html:277
#: website/public/guides/javascript/index.html:498
#: website/public/guides/native/index.html:504 website/public/index.html:1186
@@ -12461,6 +12461,23 @@ msgctxt "runtime"
msgid "Switch to dark mode"
msgstr "التبديل إلى الوضع الداكن"
#. {ph0}: <a href="https://github.com/morluto/rea/blob/main/docs/web-network-
#. captures.md" >
#. {ph1}: </a >
#: website/public/guides/browser/index.html:480
#, python-brace-format
msgctxt "page:guides/browser/index.html"
msgid ""
"For HTTP request and response bodies, use {ph0}Fiddler Everywhere: SAZ "
"captures, MCP and public skills{ph1}. Export SAZ or HAR 1.2 for offline REA "
"capture Evidence, or connect Everywhere's MCP server to inspect captured "
"traffic with your agent."
msgstr ""
"لفحص أجسام طلبات HTTP واستجاباتها، استخدم {ph0}Fiddler Everywhere: تسجيلات "
"SAZ وMCP والمهارات العامة{ph1}. صدّر بتنسيق SAZ ?? HAR 1.2 للحصول على أدلة "
"تسجيل في REA دون اتصال، أو اربط خادم MCP في Everywhere لفحص حركة المرور "
"المسجلة مع وكيلك."
#~ msgctxt "page:index.html"
#~ msgid "Setup guide"
#~ msgstr "دليل الإعداد"
+24 -6
View File
@@ -71,7 +71,7 @@ msgstr "Von N0zoM1z0 · 10. Oktober 2026"
#: website/public/faq/index.html:573
#: website/public/first-investigation/index.html:523
#: website/public/get-started/index.html:882
#: website/public/guides/browser/index.html:498
#: website/public/guides/browser/index.html:507
#: website/public/guides/index.html:282
#: website/public/guides/javascript/index.html:503
#: website/public/guides/native/index.html:509 website/public/index.html:1191
@@ -4854,7 +4854,7 @@ msgctxt "page:guides/browser/index.html"
msgid "Copy read the loaded scripts"
msgstr "Kopieren Lesen Sie die geladenen Skripte"
#: website/public/guides/browser/index.html:498
#: website/public/guides/browser/index.html:507
msgctxt "page:guides/browser/index.html"
msgid "Back to top"
msgstr "Nach oben"
@@ -11930,7 +11930,7 @@ msgstr "Anleitungen"
#: website/public/faq/index.html:565
#: website/public/first-investigation/index.html:515
#: website/public/get-started/index.html:874
#: website/public/guides/browser/index.html:490
#: website/public/guides/browser/index.html:499
#: website/public/guides/index.html:274
#: website/public/guides/javascript/index.html:495
#: website/public/guides/native/index.html:501 website/public/index.html:1183
@@ -11950,7 +11950,7 @@ msgstr "Quellcode"
#: website/public/faq/index.html:566
#: website/public/first-investigation/index.html:516
#: website/public/get-started/index.html:875
#: website/public/guides/browser/index.html:491
#: website/public/guides/browser/index.html:500
#: website/public/guides/index.html:275
#: website/public/guides/javascript/index.html:496
#: website/public/guides/native/index.html:502 website/public/index.html:1184
@@ -11970,7 +11970,7 @@ msgstr "MIT-Lizenz"
#: website/public/faq/index.html:567
#: website/public/first-investigation/index.html:517
#: website/public/get-started/index.html:876
#: website/public/guides/browser/index.html:492
#: website/public/guides/browser/index.html:501
#: website/public/guides/index.html:276
#: website/public/guides/javascript/index.html:497
#: website/public/guides/native/index.html:503 website/public/index.html:1185
@@ -11990,7 +11990,7 @@ msgstr "Zur Community"
#: website/public/faq/index.html:568
#: website/public/first-investigation/index.html:518
#: website/public/get-started/index.html:877
#: website/public/guides/browser/index.html:493
#: website/public/guides/browser/index.html:502
#: website/public/guides/index.html:277
#: website/public/guides/javascript/index.html:498
#: website/public/guides/native/index.html:504 website/public/index.html:1186
@@ -12939,6 +12939,24 @@ msgctxt "runtime"
msgid "Switch to dark mode"
msgstr "Zum dunklen Modus wechseln"
#. {ph0}: <a href="https://github.com/morluto/rea/blob/main/docs/web-network-
#. captures.md" >
#. {ph1}: </a >
#: website/public/guides/browser/index.html:480
#, python-brace-format
msgctxt "page:guides/browser/index.html"
msgid ""
"For HTTP request and response bodies, use {ph0}Fiddler Everywhere: SAZ "
"captures, MCP and public skills{ph1}. Export SAZ or HAR 1.2 for offline REA "
"capture Evidence, or connect Everywhere's MCP server to inspect captured "
"traffic with your agent."
msgstr ""
"Für HTTP-Anfrage- und Antwortinhalte verwenden Sie {ph0}Fiddler Everywhere: "
"SAZ-Aufzeichnungen, MCP und öffentliche Skills{ph1}. Exportieren Sie SAZ "
"oder HAR 1.2 für die Offline-Auswertung der Aufzeichnung als REA Evidence, "
"oder verbinden Sie den MCP-Server von Everywhere, um aufgezeichneten "
"Datenverkehr mit Ihrem Agenten zu untersuchen."
#~ msgctxt "page:index.html"
#~ msgid "Setup guide"
#~ msgstr "Einrichtungsanleitung"
+24 -6
View File
@@ -71,7 +71,7 @@ msgstr "Por N0zoM1z0 * 10 octubre 2026"
#: website/public/faq/index.html:573
#: website/public/first-investigation/index.html:523
#: website/public/get-started/index.html:882
#: website/public/guides/browser/index.html:498
#: website/public/guides/browser/index.html:507
#: website/public/guides/index.html:282
#: website/public/guides/javascript/index.html:503
#: website/public/guides/native/index.html:509 website/public/index.html:1191
@@ -4827,7 +4827,7 @@ msgctxt "page:guides/browser/index.html"
msgid "Copy read the loaded scripts"
msgstr "Copiar leer los scripts cargados"
#: website/public/guides/browser/index.html:498
#: website/public/guides/browser/index.html:507
msgctxt "page:guides/browser/index.html"
msgid "Back to top"
msgstr "Volver arriba"
@@ -11849,7 +11849,7 @@ msgstr "Guías"
#: website/public/faq/index.html:565
#: website/public/first-investigation/index.html:515
#: website/public/get-started/index.html:874
#: website/public/guides/browser/index.html:490
#: website/public/guides/browser/index.html:499
#: website/public/guides/index.html:274
#: website/public/guides/javascript/index.html:495
#: website/public/guides/native/index.html:501 website/public/index.html:1183
@@ -11869,7 +11869,7 @@ msgstr "Código fuente"
#: website/public/faq/index.html:566
#: website/public/first-investigation/index.html:516
#: website/public/get-started/index.html:875
#: website/public/guides/browser/index.html:491
#: website/public/guides/browser/index.html:500
#: website/public/guides/index.html:275
#: website/public/guides/javascript/index.html:496
#: website/public/guides/native/index.html:502 website/public/index.html:1184
@@ -11889,7 +11889,7 @@ msgstr "Licencia MIT"
#: website/public/faq/index.html:567
#: website/public/first-investigation/index.html:517
#: website/public/get-started/index.html:876
#: website/public/guides/browser/index.html:492
#: website/public/guides/browser/index.html:501
#: website/public/guides/index.html:276
#: website/public/guides/javascript/index.html:497
#: website/public/guides/native/index.html:503 website/public/index.html:1185
@@ -11909,7 +11909,7 @@ msgstr "Únete a la comunidad"
#: website/public/faq/index.html:568
#: website/public/first-investigation/index.html:518
#: website/public/get-started/index.html:877
#: website/public/guides/browser/index.html:493
#: website/public/guides/browser/index.html:502
#: website/public/guides/index.html:277
#: website/public/guides/javascript/index.html:498
#: website/public/guides/native/index.html:504 website/public/index.html:1186
@@ -12853,6 +12853,24 @@ msgctxt "runtime"
msgid "Switch to dark mode"
msgstr "Cambiar al modo oscuro"
#. {ph0}: <a href="https://github.com/morluto/rea/blob/main/docs/web-network-
#. captures.md" >
#. {ph1}: </a >
#: website/public/guides/browser/index.html:480
#, python-brace-format
msgctxt "page:guides/browser/index.html"
msgid ""
"For HTTP request and response bodies, use {ph0}Fiddler Everywhere: SAZ "
"captures, MCP and public skills{ph1}. Export SAZ or HAR 1.2 for offline REA "
"capture Evidence, or connect Everywhere's MCP server to inspect captured "
"traffic with your agent."
msgstr ""
"Para examinar los cuerpos de las solicitudes y respuestas HTTP, use "
"{ph0}Fiddler Everywhere: capturas SAZ, MCP y habilidades públicas{ph1}. "
"Exporte SAZ o HAR 1.2 para obtener Evidence de capturas en REA sin conexión,"
" o conecte el servidor MCP de Everywhere para inspeccionar el tráfico "
"capturado con su agente."
#~ msgctxt "page:index.html"
#~ msgid "Setup guide"
#~ msgstr "Guía de instalación"
+23 -6
View File
@@ -70,7 +70,7 @@ msgstr "توسط N0zoM1z0 * 10 اکتبر 2026"
#: website/public/faq/index.html:573
#: website/public/first-investigation/index.html:523
#: website/public/get-started/index.html:882
#: website/public/guides/browser/index.html:498
#: website/public/guides/browser/index.html:507
#: website/public/guides/index.html:282
#: website/public/guides/javascript/index.html:503
#: website/public/guides/native/index.html:509 website/public/index.html:1191
@@ -4694,7 +4694,7 @@ msgctxt "page:guides/browser/index.html"
msgid "Copy read the loaded scripts"
msgstr "کپی خواندن اسکریپت های لود شده"
#: website/public/guides/browser/index.html:498
#: website/public/guides/browser/index.html:507
msgctxt "page:guides/browser/index.html"
msgid "Back to top"
msgstr "بازگشت به بالا"
@@ -11564,7 +11564,7 @@ msgstr "راهنماها"
#: website/public/faq/index.html:565
#: website/public/first-investigation/index.html:515
#: website/public/get-started/index.html:874
#: website/public/guides/browser/index.html:490
#: website/public/guides/browser/index.html:499
#: website/public/guides/index.html:274
#: website/public/guides/javascript/index.html:495
#: website/public/guides/native/index.html:501 website/public/index.html:1183
@@ -11584,7 +11584,7 @@ msgstr "کد منبع"
#: website/public/faq/index.html:566
#: website/public/first-investigation/index.html:516
#: website/public/get-started/index.html:875
#: website/public/guides/browser/index.html:491
#: website/public/guides/browser/index.html:500
#: website/public/guides/index.html:275
#: website/public/guides/javascript/index.html:496
#: website/public/guides/native/index.html:502 website/public/index.html:1184
@@ -11604,7 +11604,7 @@ msgstr "مجوز MIT"
#: website/public/faq/index.html:567
#: website/public/first-investigation/index.html:517
#: website/public/get-started/index.html:876
#: website/public/guides/browser/index.html:492
#: website/public/guides/browser/index.html:501
#: website/public/guides/index.html:276
#: website/public/guides/javascript/index.html:497
#: website/public/guides/native/index.html:503 website/public/index.html:1185
@@ -11624,7 +11624,7 @@ msgstr "به جامعه بپیوندید"
#: website/public/faq/index.html:568
#: website/public/first-investigation/index.html:518
#: website/public/get-started/index.html:877
#: website/public/guides/browser/index.html:493
#: website/public/guides/browser/index.html:502
#: website/public/guides/index.html:277
#: website/public/guides/javascript/index.html:498
#: website/public/guides/native/index.html:504 website/public/index.html:1186
@@ -12563,6 +12563,23 @@ msgctxt "runtime"
msgid "Switch to dark mode"
msgstr "تغییر به حالت تیره"
#. {ph0}: <a href="https://github.com/morluto/rea/blob/main/docs/web-network-
#. captures.md" >
#. {ph1}: </a >
#: website/public/guides/browser/index.html:480
#, python-brace-format
msgctxt "page:guides/browser/index.html"
msgid ""
"For HTTP request and response bodies, use {ph0}Fiddler Everywhere: SAZ "
"captures, MCP and public skills{ph1}. Export SAZ or HAR 1.2 for offline REA "
"capture Evidence, or connect Everywhere's MCP server to inspect captured "
"traffic with your agent."
msgstr ""
"برای بررسی بدنهٔ درخواست‌ها و پاسخ‌های HTTP، از {ph0}Fiddler Everywhere: "
"ضبط‌های SAZ، سرور MCP و مهارت‌های عمومی{ph1} استفاده کنید. برای Evidence ضبط"
" در REA به‌صورت آفلاین، خروجی SAZ ?? HAR 1.2 بگیرید، یا سرور MCP نرم‌افزار "
"Everywhere را متصل کنید تا ترافیک ضبط‌شده را با عامل خود بررسی کنید."
#~ msgctxt "page:index.html"
#~ msgid "Setup guide"
#~ msgstr "راهنمای نصب"
+24 -6
View File
@@ -71,7 +71,7 @@ msgstr "Par N0zoM1z0 · 10 octobre 2026"
#: website/public/faq/index.html:573
#: website/public/first-investigation/index.html:523
#: website/public/get-started/index.html:882
#: website/public/guides/browser/index.html:498
#: website/public/guides/browser/index.html:507
#: website/public/guides/index.html:282
#: website/public/guides/javascript/index.html:503
#: website/public/guides/native/index.html:509 website/public/index.html:1191
@@ -4834,7 +4834,7 @@ msgctxt "page:guides/browser/index.html"
msgid "Copy read the loaded scripts"
msgstr "Copier lire les scripts chargés"
#: website/public/guides/browser/index.html:498
#: website/public/guides/browser/index.html:507
msgctxt "page:guides/browser/index.html"
msgid "Back to top"
msgstr "Haut de page"
@@ -11860,7 +11860,7 @@ msgstr "Guides"
#: website/public/faq/index.html:565
#: website/public/first-investigation/index.html:515
#: website/public/get-started/index.html:874
#: website/public/guides/browser/index.html:490
#: website/public/guides/browser/index.html:499
#: website/public/guides/index.html:274
#: website/public/guides/javascript/index.html:495
#: website/public/guides/native/index.html:501 website/public/index.html:1183
@@ -11880,7 +11880,7 @@ msgstr "Code source"
#: website/public/faq/index.html:566
#: website/public/first-investigation/index.html:516
#: website/public/get-started/index.html:875
#: website/public/guides/browser/index.html:491
#: website/public/guides/browser/index.html:500
#: website/public/guides/index.html:275
#: website/public/guides/javascript/index.html:496
#: website/public/guides/native/index.html:502 website/public/index.html:1184
@@ -11900,7 +11900,7 @@ msgstr "Licence MIT"
#: website/public/faq/index.html:567
#: website/public/first-investigation/index.html:517
#: website/public/get-started/index.html:876
#: website/public/guides/browser/index.html:492
#: website/public/guides/browser/index.html:501
#: website/public/guides/index.html:276
#: website/public/guides/javascript/index.html:497
#: website/public/guides/native/index.html:503 website/public/index.html:1185
@@ -11920,7 +11920,7 @@ msgstr "Rejoindre la communauté"
#: website/public/faq/index.html:568
#: website/public/first-investigation/index.html:518
#: website/public/get-started/index.html:877
#: website/public/guides/browser/index.html:493
#: website/public/guides/browser/index.html:502
#: website/public/guides/index.html:277
#: website/public/guides/javascript/index.html:498
#: website/public/guides/native/index.html:504 website/public/index.html:1186
@@ -12867,6 +12867,24 @@ msgctxt "runtime"
msgid "Switch to dark mode"
msgstr "Passer au mode sombre"
#. {ph0}: <a href="https://github.com/morluto/rea/blob/main/docs/web-network-
#. captures.md" >
#. {ph1}: </a >
#: website/public/guides/browser/index.html:480
#, python-brace-format
msgctxt "page:guides/browser/index.html"
msgid ""
"For HTTP request and response bodies, use {ph0}Fiddler Everywhere: SAZ "
"captures, MCP and public skills{ph1}. Export SAZ or HAR 1.2 for offline REA "
"capture Evidence, or connect Everywhere's MCP server to inspect captured "
"traffic with your agent."
msgstr ""
"Pour examiner les corps des requêtes et réponses HTTP, utilisez {ph0}Fiddler"
" Everywhere : captures SAZ, MCP et compétences publiques{ph1}. Exportez en "
"SAZ ou HAR 1.2 pour obtenir des Evidence de capture dans REA hors ligne, ou "
"connectez le serveur MCP d’Everywhere pour inspecter le trafic capturé avec "
"votre agent."
#~ msgctxt "page:index.html"
#~ msgid "Setup guide"
#~ msgstr "Guide d’installation"
+24 -6
View File
@@ -71,7 +71,7 @@ msgstr "Oleh N0zoM1z0 · 10 Oktober 2026"
#: website/public/faq/index.html:573
#: website/public/first-investigation/index.html:523
#: website/public/get-started/index.html:882
#: website/public/guides/browser/index.html:498
#: website/public/guides/browser/index.html:507
#: website/public/guides/index.html:282
#: website/public/guides/javascript/index.html:503
#: website/public/guides/native/index.html:509 website/public/index.html:1191
@@ -4785,7 +4785,7 @@ msgctxt "page:guides/browser/index.html"
msgid "Copy read the loaded scripts"
msgstr "Salin baca skrip yang dimuat"
#: website/public/guides/browser/index.html:498
#: website/public/guides/browser/index.html:507
msgctxt "page:guides/browser/index.html"
msgid "Back to top"
msgstr "Kembali ke atas"
@@ -11739,7 +11739,7 @@ msgstr "Panduan"
#: website/public/faq/index.html:565
#: website/public/first-investigation/index.html:515
#: website/public/get-started/index.html:874
#: website/public/guides/browser/index.html:490
#: website/public/guides/browser/index.html:499
#: website/public/guides/index.html:274
#: website/public/guides/javascript/index.html:495
#: website/public/guides/native/index.html:501 website/public/index.html:1183
@@ -11759,7 +11759,7 @@ msgstr "Kode sumber"
#: website/public/faq/index.html:566
#: website/public/first-investigation/index.html:516
#: website/public/get-started/index.html:875
#: website/public/guides/browser/index.html:491
#: website/public/guides/browser/index.html:500
#: website/public/guides/index.html:275
#: website/public/guides/javascript/index.html:496
#: website/public/guides/native/index.html:502 website/public/index.html:1184
@@ -11779,7 +11779,7 @@ msgstr "Lisensi MIT"
#: website/public/faq/index.html:567
#: website/public/first-investigation/index.html:517
#: website/public/get-started/index.html:876
#: website/public/guides/browser/index.html:492
#: website/public/guides/browser/index.html:501
#: website/public/guides/index.html:276
#: website/public/guides/javascript/index.html:497
#: website/public/guides/native/index.html:503 website/public/index.html:1185
@@ -11799,7 +11799,7 @@ msgstr "Gabung komunitas"
#: website/public/faq/index.html:568
#: website/public/first-investigation/index.html:518
#: website/public/get-started/index.html:877
#: website/public/guides/browser/index.html:493
#: website/public/guides/browser/index.html:502
#: website/public/guides/index.html:277
#: website/public/guides/javascript/index.html:498
#: website/public/guides/native/index.html:504 website/public/index.html:1186
@@ -12740,6 +12740,24 @@ msgctxt "runtime"
msgid "Switch to dark mode"
msgstr "Beralih ke mode gelap"
#. {ph0}: <a href="https://github.com/morluto/rea/blob/main/docs/web-network-
#. captures.md" >
#. {ph1}: </a >
#: website/public/guides/browser/index.html:480
#, python-brace-format
msgctxt "page:guides/browser/index.html"
msgid ""
"For HTTP request and response bodies, use {ph0}Fiddler Everywhere: SAZ "
"captures, MCP and public skills{ph1}. Export SAZ or HAR 1.2 for offline REA "
"capture Evidence, or connect Everywhere's MCP server to inspect captured "
"traffic with your agent."
msgstr ""
"Untuk memeriksa isi permintaan dan respons HTTP, gunakan {ph0}Fiddler "
"Everywhere: rekaman SAZ, MCP, dan skill publik{ph1}. Ekspor SAZ atau HAR 1.2"
" untuk menghasilkan Evidence rekaman di REA secara luring, atau hubungkan "
"server MCP Everywhere untuk memeriksa lalu lintas yang direkam bersama agen "
"Anda."
#~ msgctxt "page:index.html"
#~ msgid "Setup guide"
#~ msgstr "Panduan pemasangan"
+24 -6
View File
@@ -70,7 +70,7 @@ msgstr "Di N0zoM1z0 * 10 ottobre 2026"
#: website/public/faq/index.html:573
#: website/public/first-investigation/index.html:523
#: website/public/get-started/index.html:882
#: website/public/guides/browser/index.html:498
#: website/public/guides/browser/index.html:507
#: website/public/guides/index.html:282
#: website/public/guides/javascript/index.html:503
#: website/public/guides/native/index.html:509 website/public/index.html:1191
@@ -4809,7 +4809,7 @@ msgctxt "page:guides/browser/index.html"
msgid "Copy read the loaded scripts"
msgstr "Copia leggi gli script caricati"
#: website/public/guides/browser/index.html:498
#: website/public/guides/browser/index.html:507
msgctxt "page:guides/browser/index.html"
msgid "Back to top"
msgstr "Torna in alto"
@@ -11797,7 +11797,7 @@ msgstr "Guide"
#: website/public/faq/index.html:565
#: website/public/first-investigation/index.html:515
#: website/public/get-started/index.html:874
#: website/public/guides/browser/index.html:490
#: website/public/guides/browser/index.html:499
#: website/public/guides/index.html:274
#: website/public/guides/javascript/index.html:495
#: website/public/guides/native/index.html:501 website/public/index.html:1183
@@ -11817,7 +11817,7 @@ msgstr "Codice sorgente"
#: website/public/faq/index.html:566
#: website/public/first-investigation/index.html:516
#: website/public/get-started/index.html:875
#: website/public/guides/browser/index.html:491
#: website/public/guides/browser/index.html:500
#: website/public/guides/index.html:275
#: website/public/guides/javascript/index.html:496
#: website/public/guides/native/index.html:502 website/public/index.html:1184
@@ -11837,7 +11837,7 @@ msgstr "Licenza MIT"
#: website/public/faq/index.html:567
#: website/public/first-investigation/index.html:517
#: website/public/get-started/index.html:876
#: website/public/guides/browser/index.html:492
#: website/public/guides/browser/index.html:501
#: website/public/guides/index.html:276
#: website/public/guides/javascript/index.html:497
#: website/public/guides/native/index.html:503 website/public/index.html:1185
@@ -11857,7 +11857,7 @@ msgstr "Unisciti alla community"
#: website/public/faq/index.html:568
#: website/public/first-investigation/index.html:518
#: website/public/get-started/index.html:877
#: website/public/guides/browser/index.html:493
#: website/public/guides/browser/index.html:502
#: website/public/guides/index.html:277
#: website/public/guides/javascript/index.html:498
#: website/public/guides/native/index.html:504 website/public/index.html:1186
@@ -12801,6 +12801,24 @@ msgctxt "runtime"
msgid "Switch to dark mode"
msgstr "Passa alla modalità scura"
#. {ph0}: <a href="https://github.com/morluto/rea/blob/main/docs/web-network-
#. captures.md" >
#. {ph1}: </a >
#: website/public/guides/browser/index.html:480
#, python-brace-format
msgctxt "page:guides/browser/index.html"
msgid ""
"For HTTP request and response bodies, use {ph0}Fiddler Everywhere: SAZ "
"captures, MCP and public skills{ph1}. Export SAZ or HAR 1.2 for offline REA "
"capture Evidence, or connect Everywhere's MCP server to inspect captured "
"traffic with your agent."
msgstr ""
"Per esaminare i corpi delle richieste e delle risposte HTTP, usa "
"{ph0}Fiddler Everywhere: acquisizioni SAZ, MCP e skill pubbliche{ph1}. "
"Esporta SAZ o HAR 1.2 per ottenere Evidence delle acquisizioni in REA "
"offline, oppure collega il server MCP di Everywhere per esaminare il "
"traffico acquisito con il tuo agente."
#~ msgctxt "page:index.html"
#~ msgid "Setup guide"
#~ msgstr "Guida all’installazione"
+22 -6
View File
@@ -69,7 +69,7 @@ msgstr "によってN0zoM1z0•10 10月2026"
#: website/public/faq/index.html:573
#: website/public/first-investigation/index.html:523
#: website/public/get-started/index.html:882
#: website/public/guides/browser/index.html:498
#: website/public/guides/browser/index.html:507
#: website/public/guides/index.html:282
#: website/public/guides/javascript/index.html:503
#: website/public/guides/native/index.html:509 website/public/index.html:1191
@@ -4440,7 +4440,7 @@ msgctxt "page:guides/browser/index.html"
msgid "Copy read the loaded scripts"
msgstr "ロードされたスクリプトをコピーして読みます"
#: website/public/guides/browser/index.html:498
#: website/public/guides/browser/index.html:507
msgctxt "page:guides/browser/index.html"
msgid "Back to top"
msgstr "ページ上部へ"
@@ -10971,7 +10971,7 @@ msgstr "ガイド"
#: website/public/faq/index.html:565
#: website/public/first-investigation/index.html:515
#: website/public/get-started/index.html:874
#: website/public/guides/browser/index.html:490
#: website/public/guides/browser/index.html:499
#: website/public/guides/index.html:274
#: website/public/guides/javascript/index.html:495
#: website/public/guides/native/index.html:501 website/public/index.html:1183
@@ -10991,7 +10991,7 @@ msgstr "ソースコード"
#: website/public/faq/index.html:566
#: website/public/first-investigation/index.html:516
#: website/public/get-started/index.html:875
#: website/public/guides/browser/index.html:491
#: website/public/guides/browser/index.html:500
#: website/public/guides/index.html:275
#: website/public/guides/javascript/index.html:496
#: website/public/guides/native/index.html:502 website/public/index.html:1184
@@ -11011,7 +11011,7 @@ msgstr "MIT ライセンス"
#: website/public/faq/index.html:567
#: website/public/first-investigation/index.html:517
#: website/public/get-started/index.html:876
#: website/public/guides/browser/index.html:492
#: website/public/guides/browser/index.html:501
#: website/public/guides/index.html:276
#: website/public/guides/javascript/index.html:497
#: website/public/guides/native/index.html:503 website/public/index.html:1185
@@ -11031,7 +11031,7 @@ msgstr "コミュニティに参加"
#: website/public/faq/index.html:568
#: website/public/first-investigation/index.html:518
#: website/public/get-started/index.html:877
#: website/public/guides/browser/index.html:493
#: website/public/guides/browser/index.html:502
#: website/public/guides/index.html:277
#: website/public/guides/javascript/index.html:498
#: website/public/guides/native/index.html:504 website/public/index.html:1186
@@ -11947,6 +11947,22 @@ msgctxt "runtime"
msgid "Switch to dark mode"
msgstr "ダークモードに切り替える"
#. {ph0}: <a href="https://github.com/morluto/rea/blob/main/docs/web-network-
#. captures.md" >
#. {ph1}: </a >
#: website/public/guides/browser/index.html:480
#, python-brace-format
msgctxt "page:guides/browser/index.html"
msgid ""
"For HTTP request and response bodies, use {ph0}Fiddler Everywhere: SAZ "
"captures, MCP and public skills{ph1}. Export SAZ or HAR 1.2 for offline REA "
"capture Evidence, or connect Everywhere's MCP server to inspect captured "
"traffic with your agent."
msgstr ""
"HTTP リクエストとレスポンスの本文を調べるには、{ph0}Fiddler Everywhere:SAZ "
"キャプチャ、MCP、公開スキル{ph1}を利用してください。SAZ ??? HAR 1.2 をエクスポートすれば REA でオフラインのキャプチャ "
"Evidence を取得できます。また、Everywhere の MCP サーバーを接続して、エージェントと一緒に記録済みの通信を調べることもできます。"
#~ msgctxt "page:index.html"
#~ msgid "Setup guide"
#~ msgstr "セットアップガイド"
+22 -6
View File
@@ -68,7 +68,7 @@ msgstr "여N0zoM1z0·10 월 2026"
#: website/public/faq/index.html:573
#: website/public/first-investigation/index.html:523
#: website/public/get-started/index.html:882
#: website/public/guides/browser/index.html:498
#: website/public/guides/browser/index.html:507
#: website/public/guides/index.html:282
#: website/public/guides/javascript/index.html:503
#: website/public/guides/native/index.html:509 website/public/index.html:1191
@@ -4422,7 +4422,7 @@ msgctxt "page:guides/browser/index.html"
msgid "Copy read the loaded scripts"
msgstr "로드 된 스크립트 읽기 복사"
#: website/public/guides/browser/index.html:498
#: website/public/guides/browser/index.html:507
msgctxt "page:guides/browser/index.html"
msgid "Back to top"
msgstr "맨 위로"
@@ -10937,7 +10937,7 @@ msgstr "가이드"
#: website/public/faq/index.html:565
#: website/public/first-investigation/index.html:515
#: website/public/get-started/index.html:874
#: website/public/guides/browser/index.html:490
#: website/public/guides/browser/index.html:499
#: website/public/guides/index.html:274
#: website/public/guides/javascript/index.html:495
#: website/public/guides/native/index.html:501 website/public/index.html:1183
@@ -10957,7 +10957,7 @@ msgstr "소스 코드"
#: website/public/faq/index.html:566
#: website/public/first-investigation/index.html:516
#: website/public/get-started/index.html:875
#: website/public/guides/browser/index.html:491
#: website/public/guides/browser/index.html:500
#: website/public/guides/index.html:275
#: website/public/guides/javascript/index.html:496
#: website/public/guides/native/index.html:502 website/public/index.html:1184
@@ -10977,7 +10977,7 @@ msgstr "MIT 라이선스"
#: website/public/faq/index.html:567
#: website/public/first-investigation/index.html:517
#: website/public/get-started/index.html:876
#: website/public/guides/browser/index.html:492
#: website/public/guides/browser/index.html:501
#: website/public/guides/index.html:276
#: website/public/guides/javascript/index.html:497
#: website/public/guides/native/index.html:503 website/public/index.html:1185
@@ -10997,7 +10997,7 @@ msgstr "커뮤니티 참여"
#: website/public/faq/index.html:568
#: website/public/first-investigation/index.html:518
#: website/public/get-started/index.html:877
#: website/public/guides/browser/index.html:493
#: website/public/guides/browser/index.html:502
#: website/public/guides/index.html:277
#: website/public/guides/javascript/index.html:498
#: website/public/guides/native/index.html:504 website/public/index.html:1186
@@ -11911,6 +11911,22 @@ msgctxt "runtime"
msgid "Switch to dark mode"
msgstr "다크 모드로 전환"
#. {ph0}: <a href="https://github.com/morluto/rea/blob/main/docs/web-network-
#. captures.md" >
#. {ph1}: </a >
#: website/public/guides/browser/index.html:480
#, python-brace-format
msgctxt "page:guides/browser/index.html"
msgid ""
"For HTTP request and response bodies, use {ph0}Fiddler Everywhere: SAZ "
"captures, MCP and public skills{ph1}. Export SAZ or HAR 1.2 for offline REA "
"capture Evidence, or connect Everywhere's MCP server to inspect captured "
"traffic with your agent."
msgstr ""
"HTTP 요청과 응답 본문을 살펴보려면 {ph0}Fiddler Everywhere: SAZ 캡처, MCP 및 공개 스킬{ph1}을 "
"사용하세요. SAZ ?? HAR 1.2로 내보내면 REA에서 오프라인 캡처 Evidence를 얻을 수 있으며, Everywhere의 "
"MCP 서버를 연결하여 에이전트와 함께 캡처된 트래픽을 살펴볼 수도 있습니다."
#~ msgctxt "page:index.html"
#~ msgid "Setup guide"
#~ msgstr "설정 가이드"
+18 -6
View File
@@ -131,7 +131,7 @@ msgstr ""
#: website/public/examples/dino-lab/index.html:467 website/public/faq/index.html:565
#: website/public/first-investigation/index.html:515
#: website/public/get-started/index.html:874
#: website/public/guides/browser/index.html:490 website/public/guides/index.html:274
#: website/public/guides/browser/index.html:499 website/public/guides/index.html:274
#: website/public/guides/javascript/index.html:495
#: website/public/guides/native/index.html:501 website/public/index.html:1183
#: website/public/showcase/aegis/index.html:800
@@ -148,7 +148,7 @@ msgstr ""
#: website/public/examples/dino-lab/index.html:468 website/public/faq/index.html:566
#: website/public/first-investigation/index.html:516
#: website/public/get-started/index.html:875
#: website/public/guides/browser/index.html:491 website/public/guides/index.html:275
#: website/public/guides/browser/index.html:500 website/public/guides/index.html:275
#: website/public/guides/javascript/index.html:496
#: website/public/guides/native/index.html:502 website/public/index.html:1184
#: website/public/showcase/aegis/index.html:801
@@ -165,7 +165,7 @@ msgstr ""
#: website/public/examples/dino-lab/index.html:469 website/public/faq/index.html:567
#: website/public/first-investigation/index.html:517
#: website/public/get-started/index.html:876
#: website/public/guides/browser/index.html:492 website/public/guides/index.html:276
#: website/public/guides/browser/index.html:501 website/public/guides/index.html:276
#: website/public/guides/javascript/index.html:497
#: website/public/guides/native/index.html:503 website/public/index.html:1185
#: website/public/showcase/aegis/index.html:802
@@ -182,7 +182,7 @@ msgstr ""
#: website/public/examples/dino-lab/index.html:470 website/public/faq/index.html:568
#: website/public/first-investigation/index.html:518
#: website/public/get-started/index.html:877
#: website/public/guides/browser/index.html:493 website/public/guides/index.html:277
#: website/public/guides/browser/index.html:502 website/public/guides/index.html:277
#: website/public/guides/javascript/index.html:498
#: website/public/guides/native/index.html:504 website/public/index.html:1186
#: website/public/showcase/aegis/index.html:803
@@ -201,7 +201,7 @@ msgstr ""
#: website/public/examples/dino-lab/index.html:475 website/public/faq/index.html:573
#: website/public/first-investigation/index.html:523
#: website/public/get-started/index.html:882
#: website/public/guides/browser/index.html:498 website/public/guides/index.html:282
#: website/public/guides/browser/index.html:507 website/public/guides/index.html:282
#: website/public/guides/javascript/index.html:503
#: website/public/guides/native/index.html:509 website/public/index.html:1191
#: website/public/showcase/aegis/index.html:808
@@ -4299,6 +4299,18 @@ msgid ""
"workflow{ph1} to follow files and process boundaries."
msgstr ""
#. {ph0}: <a href="https://github.com/morluto/rea/blob/main/docs/web-network-
#. captures.md" >
#. {ph1}: </a >
#: website/public/guides/browser/index.html:480
#, python-brace-format
msgctxt "page:guides/browser/index.html"
msgid ""
"For HTTP request and response bodies, use {ph0}Fiddler Everywhere: SAZ captures, MCP "
"and public skills{ph1}. Export SAZ or HAR 1.2 for offline REA capture Evidence, or "
"connect Everywhere's MCP server to inspect captured traffic with your agent."
msgstr ""
#: website/public/guides/browser/index.html:6
msgctxt "page:guides/browser/index.html"
msgid "Browser and Network Analysis — REA"
@@ -4346,7 +4358,7 @@ msgctxt "page:guides/browser/index.html"
msgid "Copy read the loaded scripts"
msgstr ""
#: website/public/guides/browser/index.html:498
#: website/public/guides/browser/index.html:507
msgctxt "page:guides/browser/index.html"
msgid "Back to top"
msgstr ""
+23 -6
View File
@@ -70,7 +70,7 @@ msgstr "Przez N0zoM1z0 * 10 października 2026"
#: website/public/faq/index.html:573
#: website/public/first-investigation/index.html:523
#: website/public/get-started/index.html:882
#: website/public/guides/browser/index.html:498
#: website/public/guides/browser/index.html:507
#: website/public/guides/index.html:282
#: website/public/guides/javascript/index.html:503
#: website/public/guides/native/index.html:509 website/public/index.html:1191
@@ -4754,7 +4754,7 @@ msgctxt "page:guides/browser/index.html"
msgid "Copy read the loaded scripts"
msgstr "Kopiuj przeczytaj załadowane Skrypty"
#: website/public/guides/browser/index.html:498
#: website/public/guides/browser/index.html:507
msgctxt "page:guides/browser/index.html"
msgid "Back to top"
msgstr "Do góry"
@@ -11703,7 +11703,7 @@ msgstr "Poradniki"
#: website/public/faq/index.html:565
#: website/public/first-investigation/index.html:515
#: website/public/get-started/index.html:874
#: website/public/guides/browser/index.html:490
#: website/public/guides/browser/index.html:499
#: website/public/guides/index.html:274
#: website/public/guides/javascript/index.html:495
#: website/public/guides/native/index.html:501 website/public/index.html:1183
@@ -11723,7 +11723,7 @@ msgstr "Kod źródłowy"
#: website/public/faq/index.html:566
#: website/public/first-investigation/index.html:516
#: website/public/get-started/index.html:875
#: website/public/guides/browser/index.html:491
#: website/public/guides/browser/index.html:500
#: website/public/guides/index.html:275
#: website/public/guides/javascript/index.html:496
#: website/public/guides/native/index.html:502 website/public/index.html:1184
@@ -11743,7 +11743,7 @@ msgstr "Licencja MIT"
#: website/public/faq/index.html:567
#: website/public/first-investigation/index.html:517
#: website/public/get-started/index.html:876
#: website/public/guides/browser/index.html:492
#: website/public/guides/browser/index.html:501
#: website/public/guides/index.html:276
#: website/public/guides/javascript/index.html:497
#: website/public/guides/native/index.html:503 website/public/index.html:1185
@@ -11763,7 +11763,7 @@ msgstr "Dołącz do społeczności"
#: website/public/faq/index.html:568
#: website/public/first-investigation/index.html:518
#: website/public/get-started/index.html:877
#: website/public/guides/browser/index.html:493
#: website/public/guides/browser/index.html:502
#: website/public/guides/index.html:277
#: website/public/guides/javascript/index.html:498
#: website/public/guides/native/index.html:504 website/public/index.html:1186
@@ -12706,6 +12706,23 @@ msgctxt "runtime"
msgid "Switch to dark mode"
msgstr "Przełącz na ciemny motyw"
#. {ph0}: <a href="https://github.com/morluto/rea/blob/main/docs/web-network-
#. captures.md" >
#. {ph1}: </a >
#: website/public/guides/browser/index.html:480
#, python-brace-format
msgctxt "page:guides/browser/index.html"
msgid ""
"For HTTP request and response bodies, use {ph0}Fiddler Everywhere: SAZ "
"captures, MCP and public skills{ph1}. Export SAZ or HAR 1.2 for offline REA "
"capture Evidence, or connect Everywhere's MCP server to inspect captured "
"traffic with your agent."
msgstr ""
"Aby zbadać treść żądań i odpowiedzi HTTP, użyj {ph0}Fiddler Everywhere: "
"zapisów SAZ, MCP i publicznych umiejętności{ph1}. Wyeksportuj SAZ lub HAR "
"1.2, aby uzyskać Evidence z zapisu w REA offline, lub połącz serwer MCP "
"Everywhere, aby analizować zapisany ruch z agentem."
#~ msgctxt "page:index.html"
#~ msgid "Setup guide"
#~ msgstr "Instrukcja instalacji"
+24 -6
View File
@@ -70,7 +70,7 @@ msgstr "Por N0zoM1z0 * 10 de outubro de 2026"
#: website/public/faq/index.html:573
#: website/public/first-investigation/index.html:523
#: website/public/get-started/index.html:882
#: website/public/guides/browser/index.html:498
#: website/public/guides/browser/index.html:507
#: website/public/guides/index.html:282
#: website/public/guides/javascript/index.html:503
#: website/public/guides/native/index.html:509 website/public/index.html:1191
@@ -4793,7 +4793,7 @@ msgctxt "page:guides/browser/index.html"
msgid "Copy read the loaded scripts"
msgstr "Copiar ler os scripts carregados"
#: website/public/guides/browser/index.html:498
#: website/public/guides/browser/index.html:507
msgctxt "page:guides/browser/index.html"
msgid "Back to top"
msgstr "Voltar ao topo"
@@ -11780,7 +11780,7 @@ msgstr "Guias"
#: website/public/faq/index.html:565
#: website/public/first-investigation/index.html:515
#: website/public/get-started/index.html:874
#: website/public/guides/browser/index.html:490
#: website/public/guides/browser/index.html:499
#: website/public/guides/index.html:274
#: website/public/guides/javascript/index.html:495
#: website/public/guides/native/index.html:501 website/public/index.html:1183
@@ -11800,7 +11800,7 @@ msgstr "Código-fonte"
#: website/public/faq/index.html:566
#: website/public/first-investigation/index.html:516
#: website/public/get-started/index.html:875
#: website/public/guides/browser/index.html:491
#: website/public/guides/browser/index.html:500
#: website/public/guides/index.html:275
#: website/public/guides/javascript/index.html:496
#: website/public/guides/native/index.html:502 website/public/index.html:1184
@@ -11820,7 +11820,7 @@ msgstr "Licença MIT"
#: website/public/faq/index.html:567
#: website/public/first-investigation/index.html:517
#: website/public/get-started/index.html:876
#: website/public/guides/browser/index.html:492
#: website/public/guides/browser/index.html:501
#: website/public/guides/index.html:276
#: website/public/guides/javascript/index.html:497
#: website/public/guides/native/index.html:503 website/public/index.html:1185
@@ -11840,7 +11840,7 @@ msgstr "Participe da comunidade"
#: website/public/faq/index.html:568
#: website/public/first-investigation/index.html:518
#: website/public/get-started/index.html:877
#: website/public/guides/browser/index.html:493
#: website/public/guides/browser/index.html:502
#: website/public/guides/index.html:277
#: website/public/guides/javascript/index.html:498
#: website/public/guides/native/index.html:504 website/public/index.html:1186
@@ -12780,6 +12780,24 @@ msgctxt "runtime"
msgid "Switch to dark mode"
msgstr "Alternar para o modo escuro"
#. {ph0}: <a href="https://github.com/morluto/rea/blob/main/docs/web-network-
#. captures.md" >
#. {ph1}: </a >
#: website/public/guides/browser/index.html:480
#, python-brace-format
msgctxt "page:guides/browser/index.html"
msgid ""
"For HTTP request and response bodies, use {ph0}Fiddler Everywhere: SAZ "
"captures, MCP and public skills{ph1}. Export SAZ or HAR 1.2 for offline REA "
"capture Evidence, or connect Everywhere's MCP server to inspect captured "
"traffic with your agent."
msgstr ""
"Para examinar os corpos das requisições e respostas HTTP, use {ph0}Fiddler "
"Everywhere: capturas SAZ, MCP e habilidades públicas{ph1}. Exporte SAZ ou "
"HAR 1.2 para obter Evidence das capturas no REA offline, ou conecte o "
"servidor MCP do Everywhere para inspecionar o tráfego capturado com seu "
"agente."
#~ msgctxt "page:index.html"
#~ msgid "Setup guide"
#~ msgstr "Guia de instalação"
+24 -6
View File
@@ -70,7 +70,7 @@ msgstr "Автор: N0zoM1z0 · 10 октября 2026"
#: website/public/faq/index.html:573
#: website/public/first-investigation/index.html:523
#: website/public/get-started/index.html:882
#: website/public/guides/browser/index.html:498
#: website/public/guides/browser/index.html:507
#: website/public/guides/index.html:282
#: website/public/guides/javascript/index.html:503
#: website/public/guides/native/index.html:509 website/public/index.html:1191
@@ -4804,7 +4804,7 @@ msgctxt "page:guides/browser/index.html"
msgid "Copy read the loaded scripts"
msgstr "Копировать читать загруженные скрипты"
#: website/public/guides/browser/index.html:498
#: website/public/guides/browser/index.html:507
msgctxt "page:guides/browser/index.html"
msgid "Back to top"
msgstr "Наверх"
@@ -11817,7 +11817,7 @@ msgstr "Руководства"
#: website/public/faq/index.html:565
#: website/public/first-investigation/index.html:515
#: website/public/get-started/index.html:874
#: website/public/guides/browser/index.html:490
#: website/public/guides/browser/index.html:499
#: website/public/guides/index.html:274
#: website/public/guides/javascript/index.html:495
#: website/public/guides/native/index.html:501 website/public/index.html:1183
@@ -11837,7 +11837,7 @@ msgstr "Исходный код"
#: website/public/faq/index.html:566
#: website/public/first-investigation/index.html:516
#: website/public/get-started/index.html:875
#: website/public/guides/browser/index.html:491
#: website/public/guides/browser/index.html:500
#: website/public/guides/index.html:275
#: website/public/guides/javascript/index.html:496
#: website/public/guides/native/index.html:502 website/public/index.html:1184
@@ -11857,7 +11857,7 @@ msgstr "Лицензия MIT"
#: website/public/faq/index.html:567
#: website/public/first-investigation/index.html:517
#: website/public/get-started/index.html:876
#: website/public/guides/browser/index.html:492
#: website/public/guides/browser/index.html:501
#: website/public/guides/index.html:276
#: website/public/guides/javascript/index.html:497
#: website/public/guides/native/index.html:503 website/public/index.html:1185
@@ -11877,7 +11877,7 @@ msgstr "Вступить в сообщество"
#: website/public/faq/index.html:568
#: website/public/first-investigation/index.html:518
#: website/public/get-started/index.html:877
#: website/public/guides/browser/index.html:493
#: website/public/guides/browser/index.html:502
#: website/public/guides/index.html:277
#: website/public/guides/javascript/index.html:498
#: website/public/guides/native/index.html:504 website/public/index.html:1186
@@ -12822,6 +12822,24 @@ msgctxt "runtime"
msgid "Switch to dark mode"
msgstr "Переключить на тёмную тему"
#. {ph0}: <a href="https://github.com/morluto/rea/blob/main/docs/web-network-
#. captures.md" >
#. {ph1}: </a >
#: website/public/guides/browser/index.html:480
#, python-brace-format
msgctxt "page:guides/browser/index.html"
msgid ""
"For HTTP request and response bodies, use {ph0}Fiddler Everywhere: SAZ "
"captures, MCP and public skills{ph1}. Export SAZ or HAR 1.2 for offline REA "
"capture Evidence, or connect Everywhere's MCP server to inspect captured "
"traffic with your agent."
msgstr ""
"Для изучения тел запросов и ответов HTTP используйте {ph0}Fiddler "
"Everywhere: захваты SAZ, MCP и общедоступные навыки{ph1}. Экспортируйте SAZ "
"??? HAR 1.2 для получения Evidence захвата в REA без подключения к сети или "
"подключите MCP-сервер Everywhere, чтобы исследовать записанный трафик вместе"
" с агентом."
#~ msgctxt "page:index.html"
#~ msgid "Setup guide"
#~ msgstr "Руководство по установке"
+24 -6
View File
@@ -70,7 +70,7 @@ msgstr "โดย N0zoM1z0 ·10 ตุลาคม 2026"
#: website/public/faq/index.html:573
#: website/public/first-investigation/index.html:523
#: website/public/get-started/index.html:882
#: website/public/guides/browser/index.html:498
#: website/public/guides/browser/index.html:507
#: website/public/guides/index.html:282
#: website/public/guides/javascript/index.html:503
#: website/public/guides/native/index.html:509 website/public/index.html:1191
@@ -4674,7 +4674,7 @@ msgctxt "page:guides/browser/index.html"
msgid "Copy read the loaded scripts"
msgstr "คัดลอกอ่านสคริปต์ที่โหลด"
#: website/public/guides/browser/index.html:498
#: website/public/guides/browser/index.html:507
msgctxt "page:guides/browser/index.html"
msgid "Back to top"
msgstr "กลับขึ้นด้านบน"
@@ -11461,7 +11461,7 @@ msgstr "คู่มือ"
#: website/public/faq/index.html:565
#: website/public/first-investigation/index.html:515
#: website/public/get-started/index.html:874
#: website/public/guides/browser/index.html:490
#: website/public/guides/browser/index.html:499
#: website/public/guides/index.html:274
#: website/public/guides/javascript/index.html:495
#: website/public/guides/native/index.html:501 website/public/index.html:1183
@@ -11481,7 +11481,7 @@ msgstr "ซอร์สโค้ด"
#: website/public/faq/index.html:566
#: website/public/first-investigation/index.html:516
#: website/public/get-started/index.html:875
#: website/public/guides/browser/index.html:491
#: website/public/guides/browser/index.html:500
#: website/public/guides/index.html:275
#: website/public/guides/javascript/index.html:496
#: website/public/guides/native/index.html:502 website/public/index.html:1184
@@ -11501,7 +11501,7 @@ msgstr "สัญญาอนุญาต MIT"
#: website/public/faq/index.html:567
#: website/public/first-investigation/index.html:517
#: website/public/get-started/index.html:876
#: website/public/guides/browser/index.html:492
#: website/public/guides/browser/index.html:501
#: website/public/guides/index.html:276
#: website/public/guides/javascript/index.html:497
#: website/public/guides/native/index.html:503 website/public/index.html:1185
@@ -11521,7 +11521,7 @@ msgstr "เข้าร่วมชุมชน"
#: website/public/faq/index.html:568
#: website/public/first-investigation/index.html:518
#: website/public/get-started/index.html:877
#: website/public/guides/browser/index.html:493
#: website/public/guides/browser/index.html:502
#: website/public/guides/index.html:277
#: website/public/guides/javascript/index.html:498
#: website/public/guides/native/index.html:504 website/public/index.html:1186
@@ -12459,6 +12459,24 @@ msgctxt "runtime"
msgid "Switch to dark mode"
msgstr "เปลี่ยนเป็นโหมดมืด"
#. {ph0}: <a href="https://github.com/morluto/rea/blob/main/docs/web-network-
#. captures.md" >
#. {ph1}: </a >
#: website/public/guides/browser/index.html:480
#, python-brace-format
msgctxt "page:guides/browser/index.html"
msgid ""
"For HTTP request and response bodies, use {ph0}Fiddler Everywhere: SAZ "
"captures, MCP and public skills{ph1}. Export SAZ or HAR 1.2 for offline REA "
"capture Evidence, or connect Everywhere's MCP server to inspect captured "
"traffic with your agent."
msgstr ""
"หากต้องการตรวจสอบเนื้อหาคำขอและคำตอบ HTTP ให้ใช้ {ph0}Fiddler Everywhere: "
"ข้อมูลที่บันทึกในรูปแบบ SAZ, MCP และทักษะสาธารณะ{ph1} ส่งออกเป็น SAZ ???? "
"HAR 1.2 เพื่อรับ Evidence จากข้อมูลที่บันทึกใน REA แบบออฟไลน์ "
"หรือเชื่อมต่อเซิร์ฟเวอร์ MCP ของ Everywhere "
"เพื่อตรวจสอบทราฟฟิกที่บันทึกไว้ร่วมกับเอเจนต์ของคุณ"
#~ msgctxt "page:index.html"
#~ msgid "Setup guide"
#~ msgstr "คู่มือการติดตั้ง"
+24 -6
View File
@@ -71,7 +71,7 @@ msgstr "Başlangıç N0zoM1z0 * 10 Ekim 2026"
#: website/public/faq/index.html:573
#: website/public/first-investigation/index.html:523
#: website/public/get-started/index.html:882
#: website/public/guides/browser/index.html:498
#: website/public/guides/browser/index.html:507
#: website/public/guides/index.html:282
#: website/public/guides/javascript/index.html:503
#: website/public/guides/native/index.html:509 website/public/index.html:1191
@@ -4754,7 +4754,7 @@ msgctxt "page:guides/browser/index.html"
msgid "Copy read the loaded scripts"
msgstr "Kopyala yüklenen komut dosyalarını oku"
#: website/public/guides/browser/index.html:498
#: website/public/guides/browser/index.html:507
msgctxt "page:guides/browser/index.html"
msgid "Back to top"
msgstr "Başa dön"
@@ -11690,7 +11690,7 @@ msgstr "Rehberler"
#: website/public/faq/index.html:565
#: website/public/first-investigation/index.html:515
#: website/public/get-started/index.html:874
#: website/public/guides/browser/index.html:490
#: website/public/guides/browser/index.html:499
#: website/public/guides/index.html:274
#: website/public/guides/javascript/index.html:495
#: website/public/guides/native/index.html:501 website/public/index.html:1183
@@ -11710,7 +11710,7 @@ msgstr "Kaynak kod"
#: website/public/faq/index.html:566
#: website/public/first-investigation/index.html:516
#: website/public/get-started/index.html:875
#: website/public/guides/browser/index.html:491
#: website/public/guides/browser/index.html:500
#: website/public/guides/index.html:275
#: website/public/guides/javascript/index.html:496
#: website/public/guides/native/index.html:502 website/public/index.html:1184
@@ -11730,7 +11730,7 @@ msgstr "MIT lisansı"
#: website/public/faq/index.html:567
#: website/public/first-investigation/index.html:517
#: website/public/get-started/index.html:876
#: website/public/guides/browser/index.html:492
#: website/public/guides/browser/index.html:501
#: website/public/guides/index.html:276
#: website/public/guides/javascript/index.html:497
#: website/public/guides/native/index.html:503 website/public/index.html:1185
@@ -11750,7 +11750,7 @@ msgstr "Topluluğa katıl"
#: website/public/faq/index.html:568
#: website/public/first-investigation/index.html:518
#: website/public/get-started/index.html:877
#: website/public/guides/browser/index.html:493
#: website/public/guides/browser/index.html:502
#: website/public/guides/index.html:277
#: website/public/guides/javascript/index.html:498
#: website/public/guides/native/index.html:504 website/public/index.html:1186
@@ -12688,6 +12688,24 @@ msgctxt "runtime"
msgid "Switch to dark mode"
msgstr "Koyu temaya geç"
#. {ph0}: <a href="https://github.com/morluto/rea/blob/main/docs/web-network-
#. captures.md" >
#. {ph1}: </a >
#: website/public/guides/browser/index.html:480
#, python-brace-format
msgctxt "page:guides/browser/index.html"
msgid ""
"For HTTP request and response bodies, use {ph0}Fiddler Everywhere: SAZ "
"captures, MCP and public skills{ph1}. Export SAZ or HAR 1.2 for offline REA "
"capture Evidence, or connect Everywhere's MCP server to inspect captured "
"traffic with your agent."
msgstr ""
"HTTP istek ve yanıt gövdelerini incelemek için {ph0}Fiddler Everywhere: SAZ "
"kayıtları, MCP ve herkese açık beceriler{ph1} rehberini kullanın. REA ile "
"çevrimdışı kayıt Evidence elde etmek için SAZ veya HAR 1.2 dışa aktarın veya"
" kaydedilen trafiği ajanınızla incelemek için Everywhere MCP sunucusunu "
"bağlayın."
#~ msgctxt "page:index.html"
#~ msgid "Setup guide"
#~ msgstr "Kurulum rehberi"
+24 -6
View File
@@ -70,7 +70,7 @@ msgstr "Автор: N0zoM1z0 · 10 жовтня 2026"
#: website/public/faq/index.html:573
#: website/public/first-investigation/index.html:523
#: website/public/get-started/index.html:882
#: website/public/guides/browser/index.html:498
#: website/public/guides/browser/index.html:507
#: website/public/guides/index.html:282
#: website/public/guides/javascript/index.html:503
#: website/public/guides/native/index.html:509 website/public/index.html:1191
@@ -4780,7 +4780,7 @@ msgctxt "page:guides/browser/index.html"
msgid "Copy read the loaded scripts"
msgstr "Копіювати читати завантажені скрипти"
#: website/public/guides/browser/index.html:498
#: website/public/guides/browser/index.html:507
msgctxt "page:guides/browser/index.html"
msgid "Back to top"
msgstr "Нагору"
@@ -11769,7 +11769,7 @@ msgstr "Посібники"
#: website/public/faq/index.html:565
#: website/public/first-investigation/index.html:515
#: website/public/get-started/index.html:874
#: website/public/guides/browser/index.html:490
#: website/public/guides/browser/index.html:499
#: website/public/guides/index.html:274
#: website/public/guides/javascript/index.html:495
#: website/public/guides/native/index.html:501 website/public/index.html:1183
@@ -11789,7 +11789,7 @@ msgstr "Вихідний код"
#: website/public/faq/index.html:566
#: website/public/first-investigation/index.html:516
#: website/public/get-started/index.html:875
#: website/public/guides/browser/index.html:491
#: website/public/guides/browser/index.html:500
#: website/public/guides/index.html:275
#: website/public/guides/javascript/index.html:496
#: website/public/guides/native/index.html:502 website/public/index.html:1184
@@ -11809,7 +11809,7 @@ msgstr "Ліцензія MIT"
#: website/public/faq/index.html:567
#: website/public/first-investigation/index.html:517
#: website/public/get-started/index.html:876
#: website/public/guides/browser/index.html:492
#: website/public/guides/browser/index.html:501
#: website/public/guides/index.html:276
#: website/public/guides/javascript/index.html:497
#: website/public/guides/native/index.html:503 website/public/index.html:1185
@@ -11829,7 +11829,7 @@ msgstr "Приєднатися до спільноти"
#: website/public/faq/index.html:568
#: website/public/first-investigation/index.html:518
#: website/public/get-started/index.html:877
#: website/public/guides/browser/index.html:493
#: website/public/guides/browser/index.html:502
#: website/public/guides/index.html:277
#: website/public/guides/javascript/index.html:498
#: website/public/guides/native/index.html:504 website/public/index.html:1186
@@ -12773,6 +12773,24 @@ msgctxt "runtime"
msgid "Switch to dark mode"
msgstr "Перемкнути на темну тему"
#. {ph0}: <a href="https://github.com/morluto/rea/blob/main/docs/web-network-
#. captures.md" >
#. {ph1}: </a >
#: website/public/guides/browser/index.html:480
#, python-brace-format
msgctxt "page:guides/browser/index.html"
msgid ""
"For HTTP request and response bodies, use {ph0}Fiddler Everywhere: SAZ "
"captures, MCP and public skills{ph1}. Export SAZ or HAR 1.2 for offline REA "
"capture Evidence, or connect Everywhere's MCP server to inspect captured "
"traffic with your agent."
msgstr ""
"Для дослідження тіл запитів і відповідей HTTP використовуйте {ph0}Fiddler "
"Everywhere: захоплення SAZ, MCP і загальнодоступні навички{ph1}. Експортуйте"
" SAZ ??? HAR 1.2 для отримання Evidence захоплення в REA без підключення до "
"мережі або підключіть MCP-сервер Everywhere, щоб досліджувати записаний "
"трафік разом з агентом."
#~ msgctxt "page:index.html"
#~ msgid "Setup guide"
#~ msgstr "Посібник зі встановлення"
+23 -6
View File
@@ -70,7 +70,7 @@ msgstr "Bởi N0zoM1z0 * 10 tháng mười 2026"
#: website/public/faq/index.html:573
#: website/public/first-investigation/index.html:523
#: website/public/get-started/index.html:882
#: website/public/guides/browser/index.html:498
#: website/public/guides/browser/index.html:507
#: website/public/guides/index.html:282
#: website/public/guides/javascript/index.html:503
#: website/public/guides/native/index.html:509 website/public/index.html:1191
@@ -4764,7 +4764,7 @@ msgctxt "page:guides/browser/index.html"
msgid "Copy read the loaded scripts"
msgstr "Sao chép đọc các tập lệnh đã tải"
#: website/public/guides/browser/index.html:498
#: website/public/guides/browser/index.html:507
msgctxt "page:guides/browser/index.html"
msgid "Back to top"
msgstr "Về đầu trang"
@@ -11679,7 +11679,7 @@ msgstr "Hướng dẫn"
#: website/public/faq/index.html:565
#: website/public/first-investigation/index.html:515
#: website/public/get-started/index.html:874
#: website/public/guides/browser/index.html:490
#: website/public/guides/browser/index.html:499
#: website/public/guides/index.html:274
#: website/public/guides/javascript/index.html:495
#: website/public/guides/native/index.html:501 website/public/index.html:1183
@@ -11699,7 +11699,7 @@ msgstr "Mã nguồn"
#: website/public/faq/index.html:566
#: website/public/first-investigation/index.html:516
#: website/public/get-started/index.html:875
#: website/public/guides/browser/index.html:491
#: website/public/guides/browser/index.html:500
#: website/public/guides/index.html:275
#: website/public/guides/javascript/index.html:496
#: website/public/guides/native/index.html:502 website/public/index.html:1184
@@ -11719,7 +11719,7 @@ msgstr "Giấy phép MIT"
#: website/public/faq/index.html:567
#: website/public/first-investigation/index.html:517
#: website/public/get-started/index.html:876
#: website/public/guides/browser/index.html:492
#: website/public/guides/browser/index.html:501
#: website/public/guides/index.html:276
#: website/public/guides/javascript/index.html:497
#: website/public/guides/native/index.html:503 website/public/index.html:1185
@@ -11739,7 +11739,7 @@ msgstr "Tham gia cộng đồng"
#: website/public/faq/index.html:568
#: website/public/first-investigation/index.html:518
#: website/public/get-started/index.html:877
#: website/public/guides/browser/index.html:493
#: website/public/guides/browser/index.html:502
#: website/public/guides/index.html:277
#: website/public/guides/javascript/index.html:498
#: website/public/guides/native/index.html:504 website/public/index.html:1186
@@ -12675,6 +12675,23 @@ msgctxt "runtime"
msgid "Switch to dark mode"
msgstr "Chuyển sang giao diện tối"
#. {ph0}: <a href="https://github.com/morluto/rea/blob/main/docs/web-network-
#. captures.md" >
#. {ph1}: </a >
#: website/public/guides/browser/index.html:480
#, python-brace-format
msgctxt "page:guides/browser/index.html"
msgid ""
"For HTTP request and response bodies, use {ph0}Fiddler Everywhere: SAZ "
"captures, MCP and public skills{ph1}. Export SAZ or HAR 1.2 for offline REA "
"capture Evidence, or connect Everywhere's MCP server to inspect captured "
"traffic with your agent."
msgstr ""
"Để kiểm tra phần thân yêu cầu và phản hồi HTTP, hãy dùng {ph0}Fiddler "
"Everywhere: bản ghi SAZ, MCP và kỹ năng công khai{ph1}. Xuất SAZ ho?c HAR "
"1.2 để lấy Evidence từ bản ghi trong REA ngoại tuyến, hoặc kết nối máy chủ "
"MCP của Everywhere để kiểm tra lưu lượng đã ghi cùng trợ lý của bạn."
#~ msgctxt "page:index.html"
#~ msgid "Setup guide"
#~ msgstr "Hướng dẫn cài đặt"
+21 -6
View File
@@ -68,7 +68,7 @@ msgstr "通过N0zoM1z0*10十月2026"
#: website/public/faq/index.html:573
#: website/public/first-investigation/index.html:523
#: website/public/get-started/index.html:882
#: website/public/guides/browser/index.html:498
#: website/public/guides/browser/index.html:507
#: website/public/guides/index.html:282
#: website/public/guides/javascript/index.html:503
#: website/public/guides/native/index.html:509 website/public/index.html:1191
@@ -4237,7 +4237,7 @@ msgctxt "page:guides/browser/index.html"
msgid "Copy read the loaded scripts"
msgstr "复制读取加载的脚本"
#: website/public/guides/browser/index.html:498
#: website/public/guides/browser/index.html:507
msgctxt "page:guides/browser/index.html"
msgid "Back to top"
msgstr "回到顶部"
@@ -10700,7 +10700,7 @@ msgstr "指南"
#: website/public/faq/index.html:565
#: website/public/first-investigation/index.html:515
#: website/public/get-started/index.html:874
#: website/public/guides/browser/index.html:490
#: website/public/guides/browser/index.html:499
#: website/public/guides/index.html:274
#: website/public/guides/javascript/index.html:495
#: website/public/guides/native/index.html:501 website/public/index.html:1183
@@ -10720,7 +10720,7 @@ msgstr "源代码"
#: website/public/faq/index.html:566
#: website/public/first-investigation/index.html:516
#: website/public/get-started/index.html:875
#: website/public/guides/browser/index.html:491
#: website/public/guides/browser/index.html:500
#: website/public/guides/index.html:275
#: website/public/guides/javascript/index.html:496
#: website/public/guides/native/index.html:502 website/public/index.html:1184
@@ -10740,7 +10740,7 @@ msgstr "MIT 许可证"
#: website/public/faq/index.html:567
#: website/public/first-investigation/index.html:517
#: website/public/get-started/index.html:876
#: website/public/guides/browser/index.html:492
#: website/public/guides/browser/index.html:501
#: website/public/guides/index.html:276
#: website/public/guides/javascript/index.html:497
#: website/public/guides/native/index.html:503 website/public/index.html:1185
@@ -10760,7 +10760,7 @@ msgstr "加入社区"
#: website/public/faq/index.html:568
#: website/public/first-investigation/index.html:518
#: website/public/get-started/index.html:877
#: website/public/guides/browser/index.html:493
#: website/public/guides/browser/index.html:502
#: website/public/guides/index.html:277
#: website/public/guides/javascript/index.html:498
#: website/public/guides/native/index.html:504 website/public/index.html:1186
@@ -11666,6 +11666,21 @@ msgctxt "runtime"
msgid "Switch to dark mode"
msgstr "切换到深色模式"
#. {ph0}: <a href="https://github.com/morluto/rea/blob/main/docs/web-network-
#. captures.md" >
#. {ph1}: </a >
#: website/public/guides/browser/index.html:480
#, python-brace-format
msgctxt "page:guides/browser/index.html"
msgid ""
"For HTTP request and response bodies, use {ph0}Fiddler Everywhere: SAZ "
"captures, MCP and public skills{ph1}. Export SAZ or HAR 1.2 for offline REA "
"capture Evidence, or connect Everywhere's MCP server to inspect captured "
"traffic with your agent."
msgstr ""
"要检查 HTTP 请求和响应正文,请使用{ph0}Fiddler Everywhere:SAZ 捕获、MCP 和公开技能{ph1}。导出 SAZ ? "
"HAR 1.2 可在 REA 中离线获取捕获 Evidence,也可以连接 Everywhere 的 MCP 服务器,让智能体检查已捕获的流量。"
#~ msgctxt "page:index.html"
#~ msgid "Setup guide"
#~ msgstr "安装指南"
+21 -6
View File
@@ -68,7 +68,7 @@ msgstr "透過N0zoM1z0*10十月2026"
#: website/public/faq/index.html:573
#: website/public/first-investigation/index.html:523
#: website/public/get-started/index.html:882
#: website/public/guides/browser/index.html:498
#: website/public/guides/browser/index.html:507
#: website/public/guides/index.html:282
#: website/public/guides/javascript/index.html:503
#: website/public/guides/native/index.html:509 website/public/index.html:1191
@@ -4252,7 +4252,7 @@ msgctxt "page:guides/browser/index.html"
msgid "Copy read the loaded scripts"
msgstr "複製讀取載入的指令碼"
#: website/public/guides/browser/index.html:498
#: website/public/guides/browser/index.html:507
msgctxt "page:guides/browser/index.html"
msgid "Back to top"
msgstr "回到頂部"
@@ -10719,7 +10719,7 @@ msgstr "指南"
#: website/public/faq/index.html:565
#: website/public/first-investigation/index.html:515
#: website/public/get-started/index.html:874
#: website/public/guides/browser/index.html:490
#: website/public/guides/browser/index.html:499
#: website/public/guides/index.html:274
#: website/public/guides/javascript/index.html:495
#: website/public/guides/native/index.html:501 website/public/index.html:1183
@@ -10739,7 +10739,7 @@ msgstr "原始碼"
#: website/public/faq/index.html:566
#: website/public/first-investigation/index.html:516
#: website/public/get-started/index.html:875
#: website/public/guides/browser/index.html:491
#: website/public/guides/browser/index.html:500
#: website/public/guides/index.html:275
#: website/public/guides/javascript/index.html:496
#: website/public/guides/native/index.html:502 website/public/index.html:1184
@@ -10759,7 +10759,7 @@ msgstr "MIT 許可證"
#: website/public/faq/index.html:567
#: website/public/first-investigation/index.html:517
#: website/public/get-started/index.html:876
#: website/public/guides/browser/index.html:492
#: website/public/guides/browser/index.html:501
#: website/public/guides/index.html:276
#: website/public/guides/javascript/index.html:497
#: website/public/guides/native/index.html:503 website/public/index.html:1185
@@ -10779,7 +10779,7 @@ msgstr "加入社群"
#: website/public/faq/index.html:568
#: website/public/first-investigation/index.html:518
#: website/public/get-started/index.html:877
#: website/public/guides/browser/index.html:493
#: website/public/guides/browser/index.html:502
#: website/public/guides/index.html:277
#: website/public/guides/javascript/index.html:498
#: website/public/guides/native/index.html:504 website/public/index.html:1186
@@ -11686,6 +11686,21 @@ msgctxt "runtime"
msgid "Switch to dark mode"
msgstr "切換至深色模式"
#. {ph0}: <a href="https://github.com/morluto/rea/blob/main/docs/web-network-
#. captures.md" >
#. {ph1}: </a >
#: website/public/guides/browser/index.html:480
#, python-brace-format
msgctxt "page:guides/browser/index.html"
msgid ""
"For HTTP request and response bodies, use {ph0}Fiddler Everywhere: SAZ "
"captures, MCP and public skills{ph1}. Export SAZ or HAR 1.2 for offline REA "
"capture Evidence, or connect Everywhere's MCP server to inspect captured "
"traffic with your agent."
msgstr ""
"若要檢查 HTTP 請求與回應本文,請使用{ph0}Fiddler Everywhere:SAZ 擷取、MCP 與公開技能{ph1}。匯出 SAZ ? "
"HAR 1.2 可在 REA 中離線取得擷取 Evidence,也可以連接 Everywhere 的 MCP 伺服器,讓代理檢查已擷取的流量。"
#~ msgctxt "page:index.html"
#~ msgid "Setup guide"
#~ msgstr "安裝指南"
+9
View File
@@ -477,6 +477,15 @@ link.click();</code></pre>
<a href="../javascript/">application workflow</a> to follow files
and process boundaries.
</p>
<p>
For HTTP request and response bodies, use
<a
href="https://github.com/morluto/rea/blob/main/docs/web-network-captures.md"
>Fiddler Everywhere: SAZ captures, MCP and public skills</a
>. Export SAZ or HAR 1.2 for offline REA capture Evidence, or
connect Everywhere's MCP server to inspect captured traffic with
your agent.
</p>
</section>
</article>
</div>