Files
reverse-engineering-anything/tests/acceptance/webSessionRedirectContext.test.ts
T
morluto 3afd8a16ee feat(evidence)!: give Evidence bundles a format revision owned by one module (#10861)
* feat(evidence)!: give Evidence bundles a format revision owned by one module

`src/domain/evidenceFormat.ts` owns Evidence format evolution. Bundles now
carry `evidence_format: 1`; every bundle reader admits only the current
revision and refuses an unmarked or other-revision bundle once, with the
single recreate-observations guidance, instead of admitting earlier shapes
one optional field at a time.

The 6.7-era artifact inventory summary identities are no longer rebuilt:
compare_artifacts checks the current projection only and reports a mismatch
with the owner's guidance. The pre-6.6 identity diagnosis uses the same
guidance. SQLite `worker_timeout_ms` and Dart AOT `string_pool.policy`,
optional only for historical Evidence, are now required.

BREAKING CHANGE: Evidence bundles written by REA 6.x (no `evidence_format`)
are refused by import, comparison, reconstruction and analysis-snapshot
readers as an integrity mismatch telling the caller to recreate the
observations; re-exporting an earlier bundle does not update its format or
identities. Bundles constructed by hand must include `"evidence_format": 1`.
6.7-era artifact summaries are refused by compare_artifacts, and SQLite and
Dart AOT Evidence without the newly required fields fails validation.

* fix(evidence): let the ledger application admit its bundle's format

build_reconstruction_obligation_ledger registered the full bundle schema with the SDK, so an unmarked or other-revision bundle was refused as invalid_request before the format owner ran. Register it like coverage: the application parses the request envelope and admits the bundle.

* feat(pcap)!: read capture Evidence through the current result schema only (#10879)

Delete the pcap read-compatibility schema that defaulted executable identity for historical capture Evidence, and require query_labels_hex (null when unknown) now that every producer writes it. Evidence from an earlier format is refused at the bundle format owner; a record that omits these fields is reported as malformed instead of defaulted.

BREAKING CHANGE: inspect_pcap_capture results always carry query_labels_hex, and analysis views no longer default missing tshark/capinfos identity fields.

* feat(desktop)!: require the effective view policy in desktop state Evidence (#10886)

inspect_desktop_application_state always records the complete policy it used, including sqlite_view_bytes. Drop the optional historical shape and the task draft's absent-policy branch.

BREAKING CHANGE: desktop application state results without effective_view_policy, or with a policy missing sqlite_view_bytes, are refused as malformed.

* feat(android)!: require component declaration facets and reference aggregates in Android Evidence (#10904)

* feat(android)!: require component declaration facets and reference aggregates in Android Evidence

The JADX producers always write the manifest component projection with every declaration facet, target_definition and direct_invokes_coverage. Require them, delete the duplicate observed component schema, drop the unknown declaration coverage that only described historical omission, and give manifest view results their explicit facet.

BREAKING CHANGE: Android package, component and reference results missing these fields are refused as malformed; component declaration_coverage and its summary counts no longer include unknown; manifest view results always carry facet manifest.

* refactor(android): parse package results with the domain schema directly

* feat(browser)!: require the DOM length unit and redirect-scoped prior-hop facts (#10897)

* feat(browser)!: require the DOM length unit and redirect-scoped prior-hop facts

Both CDP producers always declare node_value_length_unit, and the observation session writes redirect_response on every redirect event and no other. Require the unit, drop the diff's absent-unit branch, and admit redirect_response exactly on redirect rows.

BREAKING CHANGE: DOM snapshots without node_value_length_unit and redirect timeline rows without redirect_response are refused as malformed, including inline inspections passed to compare_web_captures.

* docs(browser): drop the historical redirect_response omission note

* feat(native)!: require native Evidence fields that were optional only for historical Evidence (#10946)

Mach-O encryption, slice malformed cause, symbol runtime names and import
facts; ELF export encoding facts and visibility; value-flow limits; load-image
permissions and relocation symbols; Swift type-reference kind and GPU AMD
target metadata are required. Legacy enum values (unrecorded, unknown
visibility, undecodable, modinfo_field_exceeds_limit) and the retained
schemas, view defaults and reinterpretations that served historical Evidence
are deleted.

BREAKING CHANGE: native records missing these fields are refused as
malformed; recreate the observation.
2026-11-15 17:25:31 +08:00

117 lines
3.9 KiB
TypeScript

import { expect, onTestFinished } from "vitest";
import { z } from "zod";
import { toolContract } from "../../src/contracts/toolContracts.js";
import { webObservationSessionSchema } from "../../src/domain/browserSession.js";
import { startFakeCdpBrowser } from "../fixtures/fakeCdpBrowser.js";
import { connectLocalToolsMcp } from "../fixtures/localToolsMcp.js";
import { cliTest } from "../support/cli/cliFixture.js";
cliTest.for(["CLI", "stdio MCP"] as const)(
"retains admitted redirect response facts exactly on redirect events through %s",
async (surface, { cli }) => {
const browser = await startFakeCdpBrowser({
eventsBeforeReply: ["Page.setLifecycleEventsEnabled"],
commandResult: (command) =>
command.method === "Performance.getMetrics"
? { metrics: [{ name: "Timestamp", value: 20 }] }
: undefined,
commandEvents: (command, origin) =>
command.method === "Page.setLifecycleEventsEnabled"
? [
{
method: "Network.requestWillBeSent",
sessionId: command.sessionId,
params: {
requestId: "redirect-chain",
frameId: "frame-main",
type: "Document",
timestamp: 21,
request: { url: `${origin}/after`, method: "GET" },
redirectResponse: {
url: `${origin}/before?token=visible`,
status: 302,
headers: { Authorization: "discard-redirect-header" },
body: "discard-redirect-body",
},
},
},
]
: undefined,
});
onTestFinished(() => browser.close());
let evidence: unknown;
if (surface === "CLI") {
const reply = await cli.run({
arguments: [
"observe-web-session",
browser.endpoint,
"allowed-page",
"--allowed-origins",
browser.allowedOrigin,
"--observation-ms",
"5",
"--json",
],
});
expect(reply.exitCode, reply.stderr).toBe(0);
evidence = reply.json;
} else {
const { call } = await connectLocalToolsMcp();
const reply = await call("observe_web_session", {
cdp_endpoint: browser.endpoint,
allowed_origins: [browser.allowedOrigin],
target_id: "allowed-page",
observation_ms: 5,
});
expect(reply.isError, JSON.stringify(reply.content)).not.toBe(true);
evidence = reply.structuredContent;
}
const source = toolContract("observe_web_session").outputSchema.parse(
evidence,
);
const result = z
.object({ normalized_result: webObservationSessionSchema })
.parse(source).normalized_result;
expect(result.timeline).toContainEqual(
expect.objectContaining({
type: "redirect",
request_id: "redirect-chain",
timestamp: 21,
url: `${browser.allowedOrigin}/after`,
redirect_response: {
url: `${browser.allowedOrigin}/before?token=visible`,
status: 302,
scope: "approved",
},
}),
);
expect(JSON.stringify(source)).not.toContain("discard-redirect-");
const omitted = {
...result,
timeline: result.timeline.map(({ redirect_response, ...row }) => {
void redirect_response;
return row;
}),
};
const misplaced = {
...result,
timeline: result.timeline.map((row) => ({
...row,
type: "lifecycle" as const,
})),
};
for (const malformed of [omitted, misplaced])
expect(webObservationSessionSchema.safeParse(malformed)).toMatchObject({
success: false,
error: {
issues: expect.arrayContaining([
expect.objectContaining({
message:
"redirect_response is present exactly on redirect events.",
}),
]),
},
});
},
);