Files
reverse-engineering-anything/src/domain/analysisView/environmentStateDerivedWrites.test.ts
T
Edgar ArakelyanandClaude Opus 5.5 212063ff6c fix(browser): address review of saved values and slug spellings
- Basic credentials and values saved with `secret: true` are redacted however
  short, as declared secrets are; page values keep the 8-character floor
- a literal Basic password other than empty is refused, since Evidence
  records literals as written
- `save_response` takes only a request the step starts, so an earlier
  autosave cannot answer it, matches its path as the browser spells it, and
  stops watching requests when the step ends
- saving reads are bounded: a request step streams its answer up to
  4,000,000 characters; a UI step refuses a larger received size unread
- goto placeholders fill the destination URL before structured query values
  join it
- reading a composed string stops at the reading cap before comparing or
  allocating more
- a slug's `&` spelling follows every create the plan accepted and every held
  entity; direct slug rules and composed strings take it from held entities
  when the create slugs none, and a slug rule they contradict is not learned

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 19:49:33 -07:00

807 lines
25 KiB
TypeScript

import { describe, expect, it } from "vitest";
import {
timedCaptureEvidence,
type TimedCaptureRow,
} from "../../../tests/fixtures/analysisView.js";
import {
BOOKMARK_ROWS,
FIXED_HELD_TAG_ROWS,
FIXED_SIBLING_TAG_ROWS,
FOLDER_ROWS,
goClock,
LATER_FIXED_SIBLING_TAG_ROWS,
MIDDLE_FIXED_TAG_UPDATE_ROWS,
MIXED_WIDTH_TAG_UPDATE_ROWS,
restoredAddressRows,
TAG_ROWS,
TAG_UPDATE_ROWS,
TWO_CLOCK_TAG_UPDATE_ROWS,
UNKNOWN_TAG_UPDATE_ROWS,
} from "../../../tests/fixtures/environmentStateDerivedValues.js";
import {
bookClock,
environmentStateViewOf,
numberedUuid,
} from "../../../tests/fixtures/environmentStateWrites.js";
import type { JsonValue } from "../jsonValue.js";
import { isObject, type JsonObject } from "./environmentStateRules.js";
const planOf = (rows: readonly TimedCaptureRow[], tool: string) => {
const plan = environmentStateViewOf(timedCaptureEvidence(rows)).writes.find(
(candidate) => candidate.tool === tool,
);
if (plan === undefined) throw new Error(`No plan for ${tool}`);
return plan;
};
const TRIMMED = {
kind: "iso",
separator: "T",
fraction: 9,
zone: "Z",
trimmed: true,
} as const;
describe("later creates must retain the learned value rules", () => {
it("withholds a later create whose captured composed address is null", () => {
const snapshot = null;
const rows = BOOKMARK_ROWS.slice(0, 6).map(
(row, index): TimedCaptureRow => {
if (index !== 5) return row;
const [method, path, body, ...rest] = row;
if (!isObject(body)) throw new Error("Expected a bookmark response");
return [
method,
path,
{ ...body, web_archive_snapshot_url: snapshot },
...rest,
];
},
);
expect(planOf(rows, "post_bookmarks")).toMatchObject({
served: false,
reason: "unexplained-change",
});
});
it.each([
{
name: "a string integer becomes a UUID",
exemplar: String,
later: numberedUuid(888),
},
{
name: "a string integer becomes an equal-length token",
exemplar: String,
later: "a656166745726976",
},
{
name: "a UUID becomes an equal-length opaque token",
exemplar: (id: number) => numberedUuid(400 + (id % 1000)),
later: "a".repeat(35) + "1",
},
])("withholds a create when $name", ({ exemplar, later }) => {
const changed = (value: JsonValue): JsonValue =>
Array.isArray(value)
? value.map(changed)
: isObject(value)
? Object.fromEntries(
Object.entries(value).map(([field, child]) => [
field,
field === "id" && typeof child === "number"
? child === 1656166745726976
? later
: exemplar(child)
: changed(child),
]),
)
: value;
const rows = FOLDER_ROWS.map((row): TimedCaptureRow => {
const [method, path, body, ...rest] = row;
return body === undefined ? row : [method, path, changed(body), ...rest];
});
expect(planOf(rows, "post_folders")).toMatchObject({
served: false,
reason: "unexplained-change",
});
});
});
describe("clock spellings an application derives", () => {
it("serves creates whose clocks drop trailing fraction zeros, as Go spells nanoseconds", () => {
expect(planOf(TAG_ROWS, "post_tags")).toMatchObject({
served: true,
rules: expect.arrayContaining([
{ field: "updatedAt", rule: "clock", format: TRIMMED },
]),
});
});
it.each([
["2026-10-01T09:00:00.12345678Z", "2026-10-01T10:00:00.120000000Z"],
["2026-10-01T09:00:00.120000000Z", "2026-10-01T10:00:00.12345678Z"],
["2026-10-01T09:00:00.12345678Z", "2026-10-01T10:00:00.123456780Z"],
["2026-10-01T09:00:00.123456780Z", "2026-10-01T10:00:00.12345678Z"],
])(
"keeps a create's clock at a fixed width when a held value ends its fraction in a zero (%s, %s)",
(first, second) => {
const held = [first, second].map((updatedAt, index) => ({
id: numberedUuid(index + 1),
name: `Held ${index + 1}`,
updatedAt,
}));
const created = {
id: numberedUuid(3),
name: "Heavy",
updatedAt: goClock(3, "374763655"),
};
const rows: TimedCaptureRow[] = [
["GET", "/api/tags", held],
...held.map((tag): TimedCaptureRow => [
"GET",
`/api/tags/${tag.id}`,
tag,
]),
["POST", "/api/tags", created, 201, { name: "Heavy" }],
["GET", `/api/tags/${created.id}`, created],
];
expect(planOf(rows, "post_tags").rules).toContainEqual({
field: "updatedAt",
rule: "clock",
format: { kind: "iso", separator: "T", fraction: 9, zone: "Z" },
});
},
);
it("keeps a create's sibling clock at a fixed width when a later call trims one clock and shows the sibling's trailing zero", () => {
const rules = planOf(FIXED_SIBLING_TAG_ROWS, "post_tags").rules;
expect(rules).toContainEqual({
field: "updatedAt",
rule: "clock",
format: TRIMMED,
});
expect(rules).toContainEqual({
field: "createdAt",
rule: "clock",
format: { kind: "iso", separator: "T", fraction: 9, zone: "Z" },
});
});
it("serves updates whose clock a later call shows trimmed", () => {
expect(planOf(TAG_UPDATE_ROWS, "patch_tags_by_uuid")).toMatchObject({
served: true,
rules: expect.arrayContaining([
{ field: "updatedAt", rule: "clock", format: TRIMMED },
]),
});
});
it("learns no clock spelling from an update to an entity an unmodelled write left unknown", () => {
expect(planOf(UNKNOWN_TAG_UPDATE_ROWS, "patch_tags_by_uuid")).toMatchObject(
{
served: true,
rules: expect.arrayContaining([
{
field: "updatedAt",
rule: "clock",
format: { kind: "iso", separator: "T", fraction: 9, zone: "Z" },
},
]),
},
);
});
it("serves updates whose two clocks a later call shows trimmed for one only", () => {
expect(
planOf(TWO_CLOCK_TAG_UPDATE_ROWS, "patch_tags_by_uuid"),
).toMatchObject({
served: true,
rules: expect.arrayContaining([
{ field: "updatedAt", rule: "clock", format: TRIMMED },
{ field: "syncedAt", rule: "clock", format: TRIMMED },
]),
});
});
});
describe("fixed-width clock evidence", () => {
const FIXED = {
kind: "iso",
separator: "T",
fraction: 9,
zone: "Z",
} as const;
it.each([1, 2])(
"withholds a shorter update clock when held entity %s proves a fixed width",
(heldId) => {
const fixedHeld = (value: JsonValue): JsonValue =>
Array.isArray(value)
? value.map(fixedHeld)
: isObject(value) && value.id === numberedUuid(heldId)
? { ...value, updatedAt: "2026-10-01T09:00:00.120000000Z" }
: value;
const rows = TAG_UPDATE_ROWS.map((row, index): TimedCaptureRow => {
const [method, path, body, ...rest] = row;
return index < 3 && body !== undefined
? [method, path, fixedHeld(body), ...rest]
: row;
});
expect(planOf(rows, "patch_tags_by_uuid")).toMatchObject({
served: false,
reason: "unexplained-change",
rules: expect.arrayContaining([
{ field: "updatedAt", rule: "clock", format: FIXED },
]),
});
},
);
it("serves updates whose later call trims one clock and shows the other's trailing zero", () => {
expect(
planOf(MIXED_WIDTH_TAG_UPDATE_ROWS, "patch_tags_by_uuid"),
).toMatchObject({
served: true,
rules: expect.arrayContaining([
{ field: "updatedAt", rule: "clock", format: TRIMMED },
{ field: "syncedAt", rule: "clock", format: FIXED },
]),
});
});
it("keeps a create's clock at a fixed width an earlier later call showed, whatever a third shows", () => {
expect(
planOf(LATER_FIXED_SIBLING_TAG_ROWS, "post_tags").rules,
).toContainEqual({ field: "createdAt", rule: "clock", format: FIXED });
});
it("keeps a create's clock at a fixed width a held entity showed while its sibling is learned trimmed", () => {
const rules = planOf(FIXED_HELD_TAG_ROWS, "post_tags").rules;
expect(rules).toContainEqual({
field: "updatedAt",
rule: "clock",
format: TRIMMED,
});
expect(rules).toContainEqual({
field: "createdAt",
rule: "clock",
format: FIXED,
});
});
it("withholds updates whose clock a later call trims after an earlier one showed a trailing zero", () => {
expect(
planOf(MIDDLE_FIXED_TAG_UPDATE_ROWS, "patch_tags_by_uuid"),
).toMatchObject({
served: false,
reason: "unexplained-change",
rules: expect.arrayContaining([
{ field: "updatedAt", rule: "clock", format: FIXED },
]),
});
});
});
describe("values an application composes or generates", () => {
it("withholds bookmark creates a later call answers with a snapshot address the composition does not spell", () => {
const special = "https://web.archive.org/web/special";
const rows = BOOKMARK_ROWS.flatMap((row): TimedCaptureRow[] => {
const [method, path, body, ...rest] = row;
if (method === "GET" && path === "/api/bookmarks/4") return [];
return method === "POST" && isObject(body) && body.id === 4
? [
[
method,
path,
{ ...body, web_archive_snapshot_url: special },
...rest,
],
]
: [row];
}).slice(0, -1);
expect(planOf(rows, "post_bookmarks")).toMatchObject({
served: false,
reason: "unexplained-change",
});
});
it("serves creates whose snapshot address joins the creation time and the sent URL, accepting any URL", () => {
expect(planOf(BOOKMARK_ROWS, "post_bookmarks")).toMatchObject({
served: true,
body: { accepted: ["url", "title"], fixed: [] },
rules: expect.arrayContaining([
{
field: "web_archive_snapshot_url",
rule: "composed",
parts: [
{ text: "https://web.archive.org/web/" },
{ from: "date_added", as: "clock", pattern: "YYYYMMDDhhmmss" },
{ text: "/" },
{ from: "url", as: "value" },
],
},
]),
});
});
it("serves folder creates and renames whose address follows the uid and the title's slug, and whose numeric id is generated", () => {
const url = {
field: "url",
rule: "composed",
parts: [
{ text: "/dashboards/f/" },
{ from: "uid", as: "value" },
{ text: "/" },
{ from: "title", as: "slug" },
],
};
expect(planOf(FOLDER_ROWS, "post_folders")).toMatchObject({
served: true,
body: { accepted: ["uid", "title"], fixed: [] },
rules: expect.arrayContaining([{ field: "id", rule: "unique" }, url]),
});
expect(planOf(FOLDER_ROWS, "put_folders_by_uuid")).toMatchObject({
served: true,
rules: expect.arrayContaining([url]),
});
});
it("serves folder creates whose address follows a generated uid and the title's slug", () => {
const generated = FOLDER_ROWS.map((row): TimedCaptureRow => {
const [method, path, body, status, sent] = row;
if (method !== "POST" || !isObject(sent) || body === undefined)
return row;
const { uid: _uid, ...title } = sent;
return [method, path, body, status!, title];
});
expect(planOf(generated, "post_folders")).toMatchObject({
served: true,
body: { accepted: ["title"] },
rules: expect.arrayContaining([
{
field: "url",
rule: "composed",
parts: [
{ text: "/dashboards/f/" },
{ from: "uid", as: "value" },
{ text: "/" },
{ from: "title", as: "slug" },
],
},
]),
});
});
it("withholds renames whose address a later call composes when an earlier call left it otherwise", () => {
expect(
planOf(restoredAddressRows(true), "put_folders_by_uuid"),
).toMatchObject({
served: true,
rules: expect.arrayContaining([
expect.objectContaining({ field: "url", rule: "composed" }),
]),
});
// The first rename left its address as it was, which the composition would rebuild.
expect(
planOf(restoredAddressRows(false), "put_folders_by_uuid"),
).toMatchObject({
served: false,
reason: "unexplained-change",
});
});
it.each([null, { path: "/legacy" }])(
"withholds a later composition whose parts an earlier rename could not spell (%j)",
(base) => {
const first = { id: 1, title: "First Folder", base, url: "/legacy" };
const renamedFirst = { ...first, title: "Renamed First Folder" };
const second = {
id: 2,
title: "Second Folder",
base: "https://example.test/folders",
url: "https://example.test/folders/second-folder",
};
const renamedSecond = {
...second,
title: "Renamed Second Folder",
url: "https://example.test/folders/renamed-second-folder",
};
const rows: TimedCaptureRow[] = [
["GET", "/api/folders", [first, second]],
["GET", "/api/folders/1", first],
["GET", "/api/folders/2", second],
[
"PUT",
"/api/folders/1",
renamedFirst,
200,
{ title: renamedFirst.title },
],
["GET", "/api/folders/1", renamedFirst],
[
"PUT",
"/api/folders/2",
renamedSecond,
200,
{ title: renamedSecond.title },
],
["GET", "/api/folders/2", renamedSecond],
];
expect(planOf(rows, "put_folders_by_id")).toMatchObject({
served: false,
reason: "unexplained-change",
});
},
);
});
/** The folder rows with each folder's numeric id repeated where `mirror` puts it, and in each create's body when `sent`. */
const mirrored = (
mirror: (id: number) => JsonObject,
sent = false,
): TimedCaptureRow[] => {
const repeated = (value: JsonValue): JsonValue =>
Array.isArray(value)
? value.map(repeated)
: isObject(value) && typeof value.id === "number"
? { ...value, ...mirror(value.id) }
: value;
return FOLDER_ROWS.map((row): TimedCaptureRow => {
const [method, path, body, ...rest] = row;
if (body === undefined) return row;
const answered = repeated(body);
const [status, request] = rest;
return sent &&
method === "POST" &&
isObject(answered) &&
status !== undefined &&
isObject(request)
? [
method,
path,
answered,
status,
{ ...request, ...mirror(Number(answered.id)) },
]
: [method, path, answered, ...rest];
});
};
/** A folder's slug with `&` spelled as Grafana's Go slugs spell it. */
const wordSlug = (title: string): string =>
title
.replaceAll("&", " and ")
.toLowerCase()
.replace(/[^a-z0-9]+/gu, "-")
.replace(/^-|-$/gu, "");
/** A folder's slug with `&` dropped, as Django's slugify drops it. */
const droppedSlug = (title: string): string =>
wordSlug(title.replaceAll("&", " "));
/** When the seeded folder was made, well before the capture. */
const SEEDED = "2026-10-01T09:00:00Z";
/** Grafana's time-based folder ids, as the recorded folders hold them. */
const FOLDER_IDS = [1655476044521472, 1656166527623168, 1656166745726976];
interface CreatedFolder {
readonly uid: string;
readonly title: string;
/** Whether the call sent the uid, or the application generated it. */
readonly sent: boolean;
/** The address's last segment, when the application spells it otherwise than the title's slug. */
readonly tail?: string;
}
/** A seeded folder and folder creates, each read back and listed, whose addresses join the uid and `spell` of the title. */
const folderCreates = (
created: readonly CreatedFolder[],
spell: (title: string) => string,
seed: { readonly uid: string; readonly title: string } = {
uid: numberedUuid(101),
title: "Lighthouse Rota",
},
): TimedCaptureRow[] => {
const entity = (
folder: {
readonly uid: string;
readonly title: string;
readonly tail?: string;
},
index: number,
at: number,
): JsonObject => ({
id: FOLDER_IDS[index]!,
uid: folder.uid,
title: folder.title,
url: `/dashboards/f/${folder.uid}/${folder.tail ?? spell(folder.title)}`,
created: bookClock(at),
updated: bookClock(at),
});
const held: JsonObject[] = [
{ ...entity(seed, 0, 0), created: SEEDED, updated: SEEDED },
];
const rows: TimedCaptureRow[] = [
["GET", "/api/folders", [...held]],
["GET", `/api/folders/${seed.uid}`, held[0]!],
];
created.forEach((folder, index) => {
// The application stamps a create with the time of its call, the row's position.
const answered = entity(folder, index + 1, rows.length);
held.push(answered);
rows.push(
[
"POST",
"/api/folders",
answered,
200,
folder.sent
? { uid: folder.uid, title: folder.title }
: { title: folder.title },
],
["GET", `/api/folders/${folder.uid}`, answered],
["GET", "/api/folders", [...held]],
);
});
return rows;
};
const addressParts = (ampersand?: "and") => [
{ text: "/dashboards/f/" },
{ from: "uid", as: "value" },
{ text: "/" },
{
from: "title",
as: "slug",
...(ampersand === undefined ? {} : { ampersand }),
},
];
describe("slug spellings across creates", () => {
it("learns that a slug spells & as the word from the first create that sends one", () => {
const rows = folderCreates(
[
{ uid: numberedUuid(102), title: "Alpha Harbour", sent: false },
{ uid: numberedUuid(103), title: "Bravo Tide & Wind", sent: false },
],
wordSlug,
);
expect(planOf(rows, "post_folders")).toMatchObject({
served: true,
rules: expect.arrayContaining([
{ field: "url", rule: "composed", parts: addressParts("and") },
]),
});
});
it("keeps a slug that drops &, and refuses a later create that spells it as the word", () => {
const dropped = folderCreates(
[
{ uid: numberedUuid(102), title: "Alpha & Harbour", sent: false },
{ uid: numberedUuid(103), title: "Bravo Tide", sent: false },
],
droppedSlug,
);
expect(planOf(dropped, "post_folders")).toMatchObject({
served: true,
rules: expect.arrayContaining([
{ field: "url", rule: "composed", parts: addressParts() },
]),
});
const mixed = folderCreates(
[
{ uid: numberedUuid(102), title: "Alpha & Harbour", sent: false },
{
uid: numberedUuid(103),
title: "Bravo Tide & Wind",
sent: false,
tail: "bravo-tide-and-wind",
},
],
droppedSlug,
);
expect(planOf(mixed, "post_folders")).toMatchObject({ served: false });
});
it("keeps a dropped & that an accepted create showed when a later one spells it as the word", () => {
const tag = (id: number, name: string, slug: string) => ({
id,
name,
slug,
});
const seed = tag(1, "Lamp Oil", "lamp-oil");
const created = [
tag(2, "Alpha Notes", "alpha-notes"),
tag(3, "Bravo & Tide", "bravo-tide"),
tag(4, "Charlie & Wind", "charlie-and-wind"),
];
const rows: TimedCaptureRow[] = [
["GET", "/api/tags", [seed]],
["GET", "/api/tags/1", seed],
];
created.forEach((entity, index) =>
rows.push(
["POST", "/api/tags", entity, 201, { name: entity.name }],
["GET", `/api/tags/${String(entity.id)}`, entity],
["GET", "/api/tags", [seed, ...created.slice(0, index + 1)]],
),
);
const plan = planOf(rows, "post_tags");
expect(plan.rules).toContainEqual({
field: "slug",
rule: "slug",
from: "name",
});
expect(plan.served).toBe(false);
});
it("spells & as the held entities do when the creates slug none", () => {
const rows = folderCreates(
[
{ uid: numberedUuid(102), title: "Alpha Harbour", sent: false },
{ uid: numberedUuid(103), title: "Bravo Tide", sent: false },
],
wordSlug,
{ uid: numberedUuid(101), title: "Lamp & Oil" },
);
expect(planOf(rows, "post_folders")).toMatchObject({
served: true,
rules: expect.arrayContaining([
{ field: "url", rule: "composed", parts: addressParts("and") },
]),
});
});
it("learns a slug field's & spelling from held tags, and no slug rule they contradict", () => {
const tagRows = (held: string, created: string, slug: string) => {
const seed = { id: 1, name: held, slug: wordSlug(held) };
const tag = { id: 2, name: created, slug };
return [
["GET", "/api/tags", [seed]],
["GET", "/api/tags/1", seed],
["POST", "/api/tags", tag, 201, { name: created }],
["GET", "/api/tags/2", tag],
["GET", "/api/tags", [seed, tag]],
] as TimedCaptureRow[];
};
expect(
planOf(
tagRows("Salt & Pepper", "Harbour Notes", "harbour-notes"),
"post_tags",
).rules,
).toContainEqual({
field: "slug",
rule: "slug",
from: "name",
ampersand: "and",
});
expect(
planOf(tagRows("Salt & Pepper", "Tide & Wind", "tide-wind"), "post_tags")
.rules,
).not.toContainEqual(
expect.objectContaining({ field: "slug", rule: "slug" }),
);
});
});
describe("ambiguous addresses and templates across creates", () => {
it("learns an address the first create cannot tell apart, its uid being its title's slug, from the second", () => {
const rows = folderCreates(
[
{ uid: "alpha-harbour", title: "Alpha Harbour", sent: true },
{ uid: "bravo-store", title: "Bravo Tide Notes", sent: true },
],
wordSlug,
{ uid: "lighthouse-rota", title: "Lighthouse Rota" },
);
expect(planOf(rows, "post_folders")).toMatchObject({
served: true,
body: { accepted: ["uid", "title"], fixed: [] },
rules: expect.arrayContaining([
{ field: "url", rule: "composed", parts: addressParts() },
]),
});
});
it("withholds creates whose template a later create answers otherwise", () => {
const rows = folderCreates(
[
{
uid: numberedUuid(102),
title: "Alpha Harbour",
sent: false,
tail: "inbox",
},
{
uid: numberedUuid(103),
title: "Bravo Tide",
sent: false,
tail: "outbox",
},
],
wordSlug,
);
expect(planOf(rows, "post_folders")).toMatchObject({
served: false,
reason: "unexplained-change",
});
});
});
describe("generated integers another field repeats", () => {
it("withholds folder creates a later call answers with a UUID where the exemplar generated an integer", () => {
const later = 1656166745726976;
const answered = numberedUuid(203);
const swap = (value: JsonValue): JsonValue =>
Array.isArray(value)
? value.map(swap)
: isObject(value) && value.id === later
? { ...value, id: answered }
: value;
const rows = FOLDER_ROWS.map((row): TimedCaptureRow => {
const [method, path, body, ...rest] = row;
return body === undefined ? row : [method, path, swap(body), ...rest];
});
expect(planOf(rows, "post_folders")).toMatchObject({
served: false,
reason: "unexplained-change",
});
});
it("withholds folder creates whose generated numeric id another field repeats, which seeding cannot keep", () => {
expect(
planOf(
mirrored((id) => ({ folderId: id })),
"post_folders",
),
).toMatchObject({ served: false, reason: "unexplained-change" });
});
it("withholds folder creates whose generated numeric id a number nested in another field repeats", () => {
expect(
planOf(
mirrored((id) => ({ metadata: { serial: id } })),
"post_folders",
),
).toMatchObject({ served: false, reason: "unexplained-change" });
});
it.each([
[
"an echoed string of its digits",
(id: number) => ({ serialText: String(id) }),
true,
],
[
"a string another rule generates around its digits",
(id: number) => ({ ref: `folder-${id}` }),
false,
],
])(
"withholds folder creates whose generated numeric id %s repeats",
(_, mirror, sent) => {
expect(planOf(mirrored(mirror, sent), "post_folders")).toMatchObject({
served: false,
reason: "unexplained-change",
});
},
);
it("serves folder creates whose string of the id's digits a template rebuilds from it", () => {
expect(
planOf(
mirrored((id) => ({ idText: String(id) })),
"post_folders",
),
).toMatchObject({
served: true,
rules: expect.arrayContaining([
{ field: "id", rule: "unique" },
{ field: "idText", rule: "template", from: "id" },
]),
});
});
});