Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
0ace143
fix(browser): retain inventory changes with unavailable shapes
To3akaRin Oct 14, 2026
4450463
fix(javascript): restore read-site ambient aliases and string config …
kaoru0822-kitauji Oct 14, 2026
d625dfc
fix(browser): preserve unknown request identity in inventory diffs
To3akaRin Oct 14, 2026
c023db3
Merge remote-tracking branch 'origin/main' into fix/main-red-ida-java…
kaoru0822-kitauji Oct 14, 2026
828ccc3
test: cover config value escapes and source-map edge pairing
kaoru0822-kitauji Oct 14, 2026
c0e4aa9
Merge remote-tracking branch 'origin/main' into fix/main-red-ida-java…
kaoru0822-kitauji Oct 14, 2026
b911d21
fix(browser): include dropped events in network inventory coverage
To3akaRin Oct 14, 2026
aff46f5
test(docs): retarget translated readme link drift at current links
kaoru0822-kitauji Oct 14, 2026
53bcc17
chore: merge current main into passive inventory fix
To3akaRin Oct 14, 2026
0fdac09
fix(javascript): admit candidate endpoint interpretation in worker ou…
kaoru0822-kitauji Oct 14, 2026
59c71be
Merge remote-tracking branch 'origin/main' into fix/main-red-ida-java…
kaoru0822-kitauji Oct 14, 2026
7346397
fix(ghidra): keep earlier LaunchSupport warnings when a later helper …
kaoru0822-kitauji Oct 14, 2026
1b75c0d
fix(browser): read DOMSnapshot's -1 string index as an empty value
kaoru0822-kitauji Oct 14, 2026
6681db8
Merge remote-tracking branch 'origin/main' into fix/main-red-ida-java…
kaoru0822-kitauji Oct 14, 2026
136a157
test(browser): expect SAZ expansion limits as caller-correctable input
kaoru0822-kitauji Oct 14, 2026
bb1fbf6
test(artifacts): accept filesystem order for root entries before repl…
kaoru0822-kitauji Oct 14, 2026
181f0f9
test(cli): read the development runner summary without inherited color
kaoru0822-kitauji Oct 14, 2026
e998d4c
chore: integrate reviewed upstream CI repairs from #3080
To3akaRin Oct 14, 2026
dab811b
test(mcp): expect partner fields to be enforced by the canonical pars…
thefolahan Oct 13, 2026
cd47a8f
test(contracts): verify public admission failure boundaries
To3akaRin Oct 14, 2026
fd4af67
test(javascript): preserve candidate endpoints across worker validation
To3akaRin Oct 14, 2026
3d3076a
test(ci): verify scoped caveats and invalid DOM snapshot values
To3akaRin Oct 14, 2026
cb1e920
chore: integrate shared CI recovery into network inventory fix
To3akaRin Oct 14, 2026
66db5f0
chore: integrate reviewed portable CI fixture repairs
To3akaRin Oct 14, 2026
525dd12
test(runtime): align macOS saved database admission expectations
To3akaRin Oct 14, 2026
9653f85
chore: complete portable CI fixture recovery
To3akaRin Oct 14, 2026
2018838
test(process): expose the demo helper fixture through the scanned pro…
kaoru0822-kitauji Oct 14, 2026
40ce9d2
chore: align Linux helper fixture with scoped process inspection
To3akaRin Oct 14, 2026
d5fd6b5
chore: merge main and reconcile boundary regressions
To3akaRin Oct 14, 2026
156b368
fix(browser): keep ambiguous request groups out of content comparisons
To3akaRin Oct 14, 2026
affae68
fix(javascript): retain escapes from uncertain process selections
To3akaRin Oct 14, 2026
79a3963
chore: sync main JSON boundary and portable verification fixes
To3akaRin Oct 14, 2026
9f772cf
fix(domain): abort rejected JSON before enclosing refinements
To3akaRin Oct 14, 2026
90020ec
test(ci): synchronize cancellation phases and preserve exact producer…
To3akaRin Oct 14, 2026
892e481
chore: reconcile main JSON and cleanup recovery fixes
To3akaRin Oct 14, 2026
a1489c5
chore: integrate passive network fixes with current main
N0zoM1z0 Oct 14, 2026
f4c30ba
fix(browser): preserve uncertainty in network metadata and counts
N0zoM1z0 Oct 14, 2026
4121cfa
chore: synchronize passive comparison review with current main
N0zoM1z0 Oct 14, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions docs/browser-observation.md
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,32 @@ REA retains local observation data and removes only URL userinfo credentials, ex
- Accessibility structure and roles are retained by default, while names and descriptions require `include_accessibility_text: true`.
- Network observations retain method, status, MIME type, size, type, initiator stack location, and complete URLs with userinfo credentials removed. Redirects reported by CDP are attached as ordered `redirects` on the one request ID, with the prior request URL, CDP response URL, method/type, response status/MIME/encoded length, request timestamp, and the next `requestWillBeSent` event timestamp. CDP delivers `redirectResponse` with that next request event, so the latter is an event boundary rather than an exact response timestamp. Redirect response headers and bodies are discarded; hops outside the selected origins are discarded with the request chain. Headers are discarded after an allowlisted projection of length/encoding, structured CSP/Link/policy fields, and untrusted agent hints. Cookies and authorization headers are never retained.
- Request/response bodies are not requested or parsed by default. When selected, only allowed-origin JSON media types are read, converted immediately to complete value-free typed paths, types and observation counts, and discarded. Each path segment is either `{ "kind": "property", "name": "raw property name" }` or `{ "kind": "array-element" }`; array-element types include primitives and nested arrays. Literal `*` properties remain distinct from array elements. `Network.getResponseBody` is never sent unless JSON body shape capture is selected. Comparison uses body shapes only when both captures selected them, and compares request/response shapes only where both observations provide those facts. Unavailable or truncated selected coverage makes equivalence unknown; observed differences still report changed.
For normalized passive inputs with complete request inventories on both sides,
request additions and removals remain observable even when selected body shapes,
response metadata, or WebSocket coverage are unavailable. HTTP request and
WebSocket inventory coverage are evaluated separately; complete HTTP coverage
cannot prove a missing WebSocket connection. Limited request inventory still
prevents request-absence claims. This comparison rule does not make live CDP's
attach-limited inventory complete or establish body-shape equality.
An unavailable HTTP resource type also leaves request identity incomplete:
observations with the same method and URL remain possible matches across types,
and identical unknown types cannot establish complete equality. Ambiguous type
groups cannot prove membership or content changes either: a missing type may
redistribute repeated requests between buckets without changing the requests.
Their comparison remains unknown, while independently identified groups retain
proven changes. Different methods/URLs and complete empty peer inventories still
support absence claims.
Relevant dropped-event counts also limit inventory completeness even when no
section is listed as truncated: HTTP uses `network_requests`, while WebSocket
uses `websocket_connections` and `websocket_frames`. Drops in other event kinds
and the aggregate total do not by themselves limit HTTP inventory. Proven
modifications remain reportable despite incomplete inventory.
Repeated identical HTTP content samples establish multiplicity changes only
with complete request inventories; limited inventories retain known content
comparisons without interpreting duplicate sample counts as changes.
WebSocket stream inventories and connection counts likewise require complete
WebSocket coverage. Redirect resource types use only shared known metadata:
an omitted type leaves equality unknown rather than proving a modification.
- Console observations with a stack source retain call type, argument types, timestamp, and source location. When console text capture is selected, already-delivered primitive values are retained verbatim; objects, getters, and remote properties are never expanded.
- WebSocket observations retain direction, opcode, and payload byte length. When shape capture is selected, text frames are classified as text or complete value-free JSON shape; binary bytes, hashes, prefixes, and raw frames are never retained.
- Storage observations always redact values. Key names, IndexedDB names, and cache names require `include_storage_keys`; stable content fingerprints require the additional `include_storage_fingerprints` selection. Cookie fingerprints cover cookies applicable to the current main-frame URL, including its path, rather than every cookie path on that origin. Other storage remains scoped to the origin. Cache bodies above 64 KiB and partial IndexedDB remote objects make fingerprint coverage incomplete, so identical observations remain `unknown`.
Expand Down
5 changes: 5 additions & 0 deletions docs/javascript-application-workflows.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,11 @@ callee bodies and built-ins such as `Object.assign`, `Reflect.set`, and
values. This uncertainty does not prove that a call actually mutated a value.
Interprocedural aliasing beyond the supported call-site references remains
outside this mutation pass.
For process configuration, only known environment value slots and canonical
argument indexes are treated as primitive when passed to calls. Unknown or
inherited selections can expose a mutable prototype, so later reads of that
configuration container retain uncertainty; earlier top-level reads and unrelated
configuration containers remain independent.

Function fingerprints commit normalized syntax, control-flow shape, relation
shape, literal sets, arity, and detected effects without using local names or
Expand Down
19 changes: 12 additions & 7 deletions docs/mcp-contracts.md
Original file line number Diff line number Diff line change
Expand Up @@ -664,10 +664,14 @@ advertised. Every advertised example is a distinct valid canonical input.

Root object unions are presented as
one object without root `anyOf`, `oneOf`, or `allOf` for model API compatibility.
The projection merges properties, retains shared required fields and
group-specific field dependencies, and describes the accepted input groups.
Canonical runtime validation enforces the exact union, including exclusions
that the advertised projection does not express. Tests validate the schemas
The projection merges properties, retains shared required fields, and
describes the accepted input groups. Which fields belong together is guidance
in that description rather than a `dependentRequired` or `dependencies`
keyword, because Gemini function declarations reject both and one unknown
keyword fails the client's whole tool catalog. Canonical runtime validation
enforces the exact union, including partner fields and exclusions that the
advertised projection does not express, so a client must not rely on the
advertised schema to reject an incomplete group. Tests validate the schemas
after SDK conversion and check advertised validation and actual calls.
Individual model APIs can impose additional nesting limits; complete producer
captures can exceed ten structural levels.
Expand Down Expand Up @@ -773,9 +777,10 @@ identities remains unknown.
`normalization` defaults to `{ "rules": [] }`.

The advertised schema includes complete nested capture fields and rejects
incomplete pairs and structurally malformed captures. Mixed comparison families
can pass advertised validation; the SDK rejects them against the canonical
input schema before invoking the handler. Domain validation additionally checks
incomplete pairs and structurally malformed captures. Mixed comparison families,
and `normalization` without both scenario captures, can pass advertised
validation; the SDK rejects them against the canonical input schema before
invoking the handler. Domain validation additionally checks
relationships such as event sequence references and retained counts; JSON
Schema does not express those cross-field invariants.

Expand Down
6 changes: 5 additions & 1 deletion docs/web-network-captures.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,11 @@ 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
limited to 32 MiB in addition to the input/output budgets below. Exceeding this
expanded-byte budget returns `invalid_request` / `invalid_input` through both
CLI and MCP, with an `out_of_range` issue at
`capture_path` and `maximum_expanded_capture_bytes`. Selecting records does not
bypass the complete-archive admission check or return partial Evidence. 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
Expand Down
2 changes: 2 additions & 0 deletions src/browser/CdpCaptureDocuments.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,10 +130,12 @@ describe("CDP DOM string and namespace validity", () => {
{ field: "nodeName", index: -1 },
{ field: "nodeName", index: 20 },
{ field: "nodeName", index: 2, malformedString: true },
{ field: "nodeValue", index: -2 },
{ field: "nodeValue", index: 20 },
{ field: "nodeValue", index: 5, malformedString: true },
{ field: "attributeName", index: -1 },
{ field: "attributeName", index: 20 },
{ field: "attributeValue", index: -2 },
{ field: "attributeValue", index: 20 },
] as const)(
// -1 is DOMSnapshot's empty string for values, never for names.
Expand Down
4 changes: 2 additions & 2 deletions src/contracts/inputSchemaPresentation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -172,8 +172,8 @@ const flattenRootObjectUnion = (
variants.set(name, known);
}
const required = sharedRequiredProperties(branches);
// A field unique to a group still needs that group's required partners.
// Counting properties alone would let optional fields pad an incomplete pair.
// A coarse property minimum rejects underspecified objects. Partner fields
// and exact group membership remain enforced by the canonical runtime parser.
const fewestRequired = Math.min(
...branches.map((branch) => requiredProperties(branch).length),
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,29 @@ describe("JavaScript semantic analysis: configuration value escapes", () => {
expect(configurationReads(body)).toEqual([[kind, key]]);
});

it.each([
"patch(process.argv[key]); return process.argv[3];",
"patch(process.argv.__proto__); return process.argv[3];",
"const args = process.argv; patch(args[key]); return args[3];",
])(
"retains uncertainty after an object-valued argv selection escapes: %s",
(body) => {
expect(configurationReads(body)).not.toContainEqual(["argv", "3"]);
},
);

it("preserves an earlier top-level read and unrelated configuration after an argv escape", () => {
const captured = analyzeJavaScriptSemantics(
"const saved = process.argv[3]; patch(process.argv[key]); globalThis.saved = saved;",
);
expect(
captured.configurationOperations.map(({ kind, key }) => [kind, key]),
).toContainEqual(["argv", "3"]);
expect(
configurationReads("patch(process.argv[key]); return process.env.PORT;"),
).toContainEqual(["environment", "PORT"]);
});

it.each([
[
"a direct argv entry",
Expand Down
27 changes: 18 additions & 9 deletions src/domain/javascript/javascriptSemanticMemberMutations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,29 +69,38 @@ export function* collectSemanticMemberMutationsSteps(
node: t.Node | undefined,
): void => {
const identity = semanticRequireExportsIdentity(specifier, path);
// Node stores process.env values and process.argv entries as strings, so
// passing one to a call exposes no object that the callee could mutate.
// Replacing an entry records its own write or receiver escape.
let effectPath = identity.path;
// Only known value slots are primitive. Unknown or inherited selections
// can expose the container prototype and affect any subsequently read slot.
if (
effect === "escape" &&
identity.specifier === "process" &&
identity.path.length === 2 &&
(identity.path[0] === "env" || identity.path[0] === "argv")
)
return;
effectPath.length === 2 &&
(effectPath[0] === "env" || effectPath[0] === "argv")
) {
const selected = effectPath[1];
if (
(typeof selected === "string" || typeof selected === "number") &&
(effectPath[0] === "env"
? !Object.hasOwn(Object.prototype, selected)
: selected === "length" || semanticArrayIndex(selected) !== null)
)
return;
effectPath = [effectPath[0], null];
}
const key = JSON.stringify([
"required",
identity.specifier,
effect,
identity.path,
effectPath,
node?.start,
node?.end,
]);
if (recordedRequiredEffects.has(key)) return;
recordedRequiredEffects.add(key);
recordedEffectCount += 1;
const effects = state.requiredModuleEffects.get(identity.specifier) ?? [];
effects.push({ path: identity.path, node, kind: effect });
effects.push({ path: effectPath, node, kind: effect });
state.requiredModuleEffects.set(identity.specifier, effects);
};
// Ambient indexing owns aliases of global objects. Consume its canonical
Expand Down
12 changes: 12 additions & 0 deletions src/domain/jsonValue.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,18 @@ describe("jsonValueSchema depth bound", () => {
},
);

it("stops enclosing refinements before rejected JSON reaches consumers", () => {
let consumed = 0;
const envelope = z.object({ value: jsonValueSchema }).superRefine(() => {
consumed += 1;
});
const result = envelope.safeParse({ value: deepObject(10_000) });
expect(result.success).toBe(false);
if (result.success) return;
expect(result.error.issues[0]?.message).toContain("maximum nesting depth");
expect(consumed).toBe(0);
});

it("rejects cyclic values instead of recursing forever", () => {
const cyclic: Record<string, unknown> = {};
cyclic.self = cyclic;
Expand Down
Loading
Loading