mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-02 08:11:34 +00:00
4705 lines
220 KiB
TypeScript
4705 lines
220 KiB
TypeScript
// Database-first legacy-store guard tests cover runtime state-file regressions.
|
|
import { promises as fs } from "node:fs";
|
|
import os from "node:os";
|
|
import path from "node:path";
|
|
import { describe, expect, it } from "vitest";
|
|
import {
|
|
collectDatabaseFirstNativeLegacyStoreViolations,
|
|
collectDatabaseFirstLegacyStoreSourceFiles,
|
|
collectDatabaseFirstLegacyStoreViolations,
|
|
} from "../../scripts/check-database-first-legacy-stores.mjs";
|
|
|
|
type LegacyStoreViolations = ReturnType<typeof collectDatabaseFirstLegacyStoreViolations>;
|
|
type UnnamedViolationCase = {
|
|
source: string;
|
|
filename: string;
|
|
expected: LegacyStoreViolations;
|
|
};
|
|
|
|
function filesystemWriteViolations(...lines: number[]): LegacyStoreViolations {
|
|
return lines.map((line) => ({ kind: "legacy store filesystem write", line }));
|
|
}
|
|
|
|
function createSourceCase(source: string) {
|
|
return (filename: string, expected: LegacyStoreViolations): UnnamedViolationCase => ({
|
|
source,
|
|
filename: filename.includes("/") ? filename : `src/runtime/${filename}`,
|
|
expected,
|
|
});
|
|
}
|
|
|
|
function sourceCase(source: TemplateStringsArray) {
|
|
return createSourceCase(source.join(""));
|
|
}
|
|
|
|
function importedSourceCase(
|
|
importStatement: string,
|
|
prefix: readonly string[] = [],
|
|
suffix: readonly string[] = [],
|
|
) {
|
|
return (source: TemplateStringsArray) => {
|
|
const body = source.join("");
|
|
const closingIndent = /\n([\t ]*)$/.exec(body);
|
|
if (!closingIndent) {
|
|
throw new Error("Source fixtures must end with an indented closing line.");
|
|
}
|
|
|
|
// Restore each envelope at its original indentation so reported source lines stay identical.
|
|
const indent = `${closingIndent[1]} `;
|
|
const renderLines = (lines: readonly string[]) =>
|
|
lines.map((line) => `\n${indent}${line}`).join("");
|
|
const restored = `${renderLines([importStatement, ...prefix])}${body.slice(
|
|
0,
|
|
closingIndent.index,
|
|
)}${renderLines(suffix)}${body.slice(closingIndent.index)}`;
|
|
return createSourceCase(restored);
|
|
};
|
|
}
|
|
|
|
const filesystemImport = 'import { promises as fs } from "node:fs";';
|
|
const atomicImport = 'import { writeTextAtomic } from "../infra/json-files.js";';
|
|
const promisesImport = 'import fs from "node:fs/promises";';
|
|
const writeFileImport = 'import { writeFile } from "node:fs/promises";';
|
|
const requireImport = 'import { createRequire } from "node:module";';
|
|
const privateStoreImport =
|
|
'import { privateFileStore } from "openclaw/plugin-sdk/security-runtime";';
|
|
const jsonImport = 'import { writeJson } from "../infra/json-files.js";';
|
|
|
|
const fsCase = importedSourceCase(filesystemImport);
|
|
const atomicCase = importedSourceCase(atomicImport);
|
|
const fsPromisesCase = importedSourceCase(promisesImport);
|
|
const writeFileCase = importedSourceCase(writeFileImport);
|
|
const requireCase = importedSourceCase(requireImport);
|
|
const privateStoreCase = importedSourceCase(privateStoreImport);
|
|
const fsPathCase = importedSourceCase(filesystemImport, ['import path from "node:path";']);
|
|
const fsPersistCase = importedSourceCase(
|
|
filesystemImport,
|
|
["function persist(filePath: string) {"],
|
|
["}", 'await persist("sessions.json");'],
|
|
);
|
|
const atomicPersistCase = importedSourceCase(
|
|
atomicImport,
|
|
["function persist(params: { filePath: string }) {"],
|
|
["}", 'await persist({ filePath: "sessions.json" });'],
|
|
);
|
|
const fsPromisesPersistCase = importedSourceCase(
|
|
promisesImport,
|
|
["function persist(params: { filePath: string }) {"],
|
|
["}", 'await persist({ filePath: "sessions.json" });'],
|
|
);
|
|
const requirePersistCase = importedSourceCase(
|
|
requireImport,
|
|
["function persist(filePath: string) {"],
|
|
["}", 'persist("sessions.json");'],
|
|
);
|
|
const jsonPersistCase = importedSourceCase(jsonImport, [
|
|
"function persist(options: { store: string }) {",
|
|
" return writeJson(options.store, {});",
|
|
"}",
|
|
]);
|
|
|
|
function namedCases(cases: Record<string, UnnamedViolationCase>) {
|
|
return Object.entries(cases).map(([name, { source, filename, expected }]) => ({
|
|
name,
|
|
source,
|
|
filename,
|
|
expected,
|
|
}));
|
|
}
|
|
|
|
describe("check-database-first-legacy-stores", () => {
|
|
it("collects JavaScript runtime source files", async () => {
|
|
const root = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-db-first-guard-"));
|
|
try {
|
|
await fs.mkdir(path.join(root, "src"), { recursive: true });
|
|
await fs.writeFile(path.join(root, "src", "runtime.js"), "export {};\n");
|
|
await fs.writeFile(path.join(root, "src", "worker.mjs"), "export {};\n");
|
|
await fs.writeFile(path.join(root, "src", "types.ts"), "export {};\n");
|
|
await fs.writeFile(path.join(root, "src", "runtime.test.js"), "export {};\n");
|
|
await fs.writeFile(path.join(root, "src", "test-helpers.ts"), "export {};\n");
|
|
await fs.writeFile(path.join(root, "src", "test-support.ts"), "export {};\n");
|
|
await fs.writeFile(path.join(root, "src", "worker.test-helpers.ts"), "export {};\n");
|
|
|
|
const files = await collectDatabaseFirstLegacyStoreSourceFiles([path.join(root, "src")]);
|
|
const relativeFiles = files
|
|
.map((file) => path.relative(root, file).replaceAll(path.sep, "/"))
|
|
.toSorted();
|
|
|
|
expect(relativeFiles).toEqual(["src/runtime.js", "src/types.ts", "src/worker.mjs"]);
|
|
} finally {
|
|
await fs.rm(root, { force: true, recursive: true });
|
|
}
|
|
});
|
|
|
|
it("skips generated extension asset and dist bundles", async () => {
|
|
const root = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-db-first-guard-"));
|
|
try {
|
|
await fs.mkdir(path.join(root, "extensions", "diffs", "assets"), { recursive: true });
|
|
await fs.mkdir(path.join(root, "extensions", "diffs", "dist", "assets"), {
|
|
recursive: true,
|
|
});
|
|
await fs.mkdir(path.join(root, "extensions", "diffs", "src"), { recursive: true });
|
|
await fs.mkdir(path.join(root, "packages", "plugin-sdk", "dist"), { recursive: true });
|
|
await fs.mkdir(path.join(root, "packages", "plugin-sdk", "src"), { recursive: true });
|
|
await fs.writeFile(
|
|
path.join(root, "extensions", "diffs", "assets", "viewer-runtime.js"),
|
|
"export const bundled = true;\n",
|
|
);
|
|
await fs.writeFile(
|
|
path.join(root, "extensions", "diffs", "dist", "assets", "viewer-runtime.js"),
|
|
"export const bundled = true;\n",
|
|
);
|
|
await fs.writeFile(
|
|
path.join(root, "extensions", "diffs", "src", "runtime.js"),
|
|
"export const runtime = true;\n",
|
|
);
|
|
await fs.writeFile(
|
|
path.join(root, "packages", "plugin-sdk", "dist", "index.js"),
|
|
"export const bundled = true;\n",
|
|
);
|
|
await fs.writeFile(
|
|
path.join(root, "packages", "plugin-sdk", "src", "index.js"),
|
|
"export const runtime = true;\n",
|
|
);
|
|
|
|
const files = await collectDatabaseFirstLegacyStoreSourceFiles([
|
|
path.join(root, "extensions"),
|
|
path.join(root, "packages"),
|
|
]);
|
|
const relativeFiles = files
|
|
.map((file) => path.relative(root, file).replaceAll(path.sep, "/"))
|
|
.toSorted();
|
|
|
|
expect(relativeFiles).toEqual([
|
|
"extensions/diffs/src/runtime.js",
|
|
"packages/plugin-sdk/src/index.js",
|
|
]);
|
|
} finally {
|
|
await fs.rm(root, { force: true, recursive: true });
|
|
}
|
|
});
|
|
|
|
it("ignores deeply nested type-only syntax", () => {
|
|
const nestedType = Array.from({ length: 600 }).reduce<string>(
|
|
(type) => `Readonly<${type}>`,
|
|
"string",
|
|
);
|
|
const violations = collectDatabaseFirstLegacyStoreViolations(
|
|
`
|
|
type DeepRuntimeSchema = ${nestedType};
|
|
export const ok: DeepRuntimeSchema | null = null;
|
|
`,
|
|
"src/runtime/deep-type-only-schema.ts",
|
|
);
|
|
|
|
expect(violations).toEqual([]);
|
|
});
|
|
|
|
// Core source analysis and initial legacy-store detection.
|
|
it.each(
|
|
namedCases({
|
|
"terminates analysis for self-recursive helper wrappers": sourceCase`
|
|
function normalize(value: unknown): unknown {
|
|
if (Array.isArray(value)) {
|
|
return value.map((entry) => normalize(entry));
|
|
}
|
|
return value;
|
|
}
|
|
normalize([]);
|
|
`("self-recursive-helper.ts", []),
|
|
"flags runtime writes to legacy sessions.json stores": fsPathCase`
|
|
export async function save(dir: string) {
|
|
await fs.writeFile(path.join(dir, "sessions.json"), "{}\\n", "utf8");
|
|
}
|
|
`("session-writer.ts", filesystemWriteViolations(5)),
|
|
}),
|
|
)("$name", ({ source, filename, expected }) => {
|
|
const violations = collectDatabaseFirstLegacyStoreViolations(source, filename);
|
|
|
|
expect(violations).toEqual(expected);
|
|
});
|
|
|
|
it("keeps legacy restart sentinel filesystem access in its sole migration owner", () => {
|
|
const runtimeViolations = collectDatabaseFirstLegacyStoreViolations(
|
|
`
|
|
import { readFile } from "node:fs/promises";
|
|
import path from "node:path";
|
|
const legacyFilename = "restart-sentinel.json";
|
|
`,
|
|
"src/infra/restart-sentinel.ts",
|
|
);
|
|
const migrationViolations = collectDatabaseFirstLegacyStoreViolations(
|
|
`
|
|
import { readFile } from "node:fs/promises";
|
|
import path from "node:path";
|
|
const legacyFilename = "restart-sentinel.json";
|
|
`,
|
|
"src/infra/state-migrations.restart-sentinel.ts",
|
|
);
|
|
|
|
expect(runtimeViolations).toEqual([
|
|
{ kind: "legacy restart sentinel filesystem import", line: 2 },
|
|
{ kind: "legacy restart sentinel filesystem import", line: 3 },
|
|
{ kind: "legacy restart sentinel reference", line: 4 },
|
|
]);
|
|
expect(migrationViolations).toEqual([]);
|
|
});
|
|
|
|
it("keeps exec approvals legacy paths and stable URI identity in their exact owners", () => {
|
|
const runtimeViolations = collectDatabaseFirstLegacyStoreViolations(
|
|
`
|
|
import fs from "node:fs";
|
|
const legacyFilename = "exec-approvals.json";
|
|
`,
|
|
"src/infra/exec-approvals-store.ts",
|
|
);
|
|
const migrationViolations = collectDatabaseFirstLegacyStoreViolations(
|
|
`
|
|
import fs from "node:fs";
|
|
const legacyFilename = "exec-approvals.json";
|
|
`,
|
|
"src/infra/state-migrations.exec-approvals.ts",
|
|
);
|
|
const configViolations = collectDatabaseFirstLegacyStoreViolations(
|
|
'const EXEC_APPROVALS_FILE = "exec-approvals.json";',
|
|
"src/infra/exec-approvals-config.ts",
|
|
);
|
|
const stableUriViolations = collectDatabaseFirstLegacyStoreViolations(
|
|
'export const EXEC_APPROVALS_POLICY_URI = "oc://exec-approvals.json";',
|
|
"extensions/policy/src/exec-approvals-uri.ts",
|
|
);
|
|
const copiedUriViolations = collectDatabaseFirstLegacyStoreViolations(
|
|
'const copied = "oc://exec-approvals.json";',
|
|
"extensions/policy/src/doctor/copied-uri.ts",
|
|
);
|
|
|
|
expect(runtimeViolations).toEqual([
|
|
{ kind: "legacy exec approvals filesystem import", line: 2 },
|
|
{ kind: "legacy exec approvals reference", line: 3 },
|
|
]);
|
|
expect(migrationViolations).toEqual([]);
|
|
expect(configViolations).toEqual([]);
|
|
expect(stableUriViolations).toEqual([]);
|
|
expect(copiedUriViolations).toEqual([{ kind: "legacy exec approvals reference", line: 1 }]);
|
|
});
|
|
|
|
// Legacy paths and literal propagation.
|
|
it.each(
|
|
namedCases({
|
|
"flags legacy restart sentinel references outside the migration owner": sourceCase`
|
|
const legacyPath = path.join(stateDir, "restart-sentinel.json");
|
|
await readFile(legacyPath, "utf8");
|
|
`("src/commands/doctor/state-migrations.ts", [
|
|
{ kind: "legacy restart sentinel reference", line: 2 },
|
|
]),
|
|
"allows the CLI preflight to detect exact legacy restart sentinel inputs": sourceCase`
|
|
[
|
|
path.join(stateDir, "restart-sentinel.json"),
|
|
path.join(stateDir, "restart-sentinel.json.doctor-importing"),
|
|
].some(fileOrDirExists);
|
|
`("src/cli/program/config-guard.ts", []),
|
|
"flags direct legacy restart sentinel reads from the CLI preflight": sourceCase`
|
|
await readFile(path.join(stateDir, "restart-sentinel.json"), "utf8");
|
|
await readFile(path.join(stateDir, "restart-sentinel.json.doctor-importing"), "utf8");
|
|
`("src/cli/program/config-guard.ts", [
|
|
{ kind: "legacy restart sentinel reference", line: 2 },
|
|
{ kind: "legacy restart sentinel reference", line: 3 },
|
|
]),
|
|
"flags nested restart sentinel paths disguised as CLI preflight detection": sourceCase`
|
|
[path.join(stateDir, "archive/restart-sentinel.json")].some(fileOrDirExists);
|
|
`("src/cli/program/config-guard.ts", [
|
|
{ kind: "legacy restart sentinel reference", line: 2 },
|
|
]),
|
|
"flags retired Diffs viewer sidecar writes": fsPathCase`
|
|
await fs.writeFile(path.join(root, id, "viewer.html"), html);
|
|
await fs.writeFile(path.join(root, id, "meta.json"), metadata);
|
|
await fs.writeFile(path.join(root, id, "file-meta.json"), metadata);
|
|
`("extensions/diffs/src/legacy-store.ts", filesystemWriteViolations(4, 5, 6)),
|
|
"flags retired QMD file-lock sidecars": sourceCase`
|
|
import { withFileLock } from "openclaw/plugin-sdk/file-lock";
|
|
import path from "node:path";
|
|
await withFileLock(path.join(stateDir, "qmd", "embed.lock"), options, task);
|
|
await withFileLock(path.join(agentDir, "qmd-write.lock"), options, task);
|
|
`("extensions/memory-core/src/memory/qmd-locks.ts", filesystemWriteViolations(4, 5)),
|
|
"flags writes through local variables initialized from legacy store paths": fsPathCase`
|
|
export async function save(dir: string) {
|
|
const storePath = path.join(dir, "sessions.json");
|
|
await fs.writeFile(storePath, "{}\\n", "utf8");
|
|
}
|
|
`("session-writer.ts", filesystemWriteViolations(6)),
|
|
"flags writes through property access on legacy path variables": atomicCase`
|
|
const storePath = "sessions.json";
|
|
await writeTextAtomic(storePath.toString(), "{}\\n");
|
|
`("legacy-path-property-access.ts", filesystemWriteViolations(4)),
|
|
"flags legacy paths split across path.join segments": fsPathCase`
|
|
await fs.writeFile(path.join(stateDir, "cron", "jobs.json"), "{}\\n", "utf8");
|
|
const sidecarPath = path.join(root, "plugin-state", "state.sqlite");
|
|
await fs.writeFile(sidecarPath, "");
|
|
`("legacy-state.ts", filesystemWriteViolations(4, 6)),
|
|
"flags runtime writes to the retired TUI last-session store": fsPathCase`
|
|
await fs.writeFile(path.join(stateDir, "tui", "last-session.json"), "{}\\n");
|
|
`("src/tui/last-session-writer.ts", filesystemWriteViolations(4)),
|
|
"flags runtime writes to the retired commitments JSON store": fsPathCase`
|
|
await fs.writeFile(path.join(stateDir, "commitments", "commitments.json"), "{}\\n");
|
|
`("src/commitments/file-store.ts", filesystemWriteViolations(4)),
|
|
"flags runtime writes to retired core audit JSONL stores": fsPathCase`
|
|
await fs.appendFile(path.join(stateDir, "logs", "config-audit.jsonl"), "{}\\n");
|
|
await fs.appendFile(path.join(stateDir, "audit", "system-agent.jsonl"), "{}\\n");
|
|
`("src/infra/audit-writer.ts", filesystemWriteViolations(4, 5)),
|
|
"flags runtime writes to retired managed-image record JSON": fsPathCase`
|
|
await fs.writeFile(path.join(stateDir, "media", "outgoing", "records", \`\${id}.json\`), "{}\n");
|
|
`("src/gateway/managed-image-file-store.ts", filesystemWriteViolations(4)),
|
|
"flags runtime writes to retired Web Push JSON stores": fsPathCase`
|
|
await fs.writeFile(path.join(stateDir, "push", "web-push-subscriptions.json"), "{}\n");
|
|
await fs.writeFile(path.join(stateDir, "push", "vapid-keys.json"), "{}\n");
|
|
`("src/infra/push-web-file-store.ts", filesystemWriteViolations(4, 6)),
|
|
"flags runtime writes to the retired APNs registration store": fsPathCase`
|
|
await fs.writeFile(path.join(stateDir, "push", "apns-registrations.json"), "{}\n");
|
|
`("src/infra/push-apns-file-store.ts", filesystemWriteViolations(4)),
|
|
"flags runtime writes to the retired node-host JSON config": fsPathCase`
|
|
await fs.writeFile(path.join(stateDir, "node.json"), "{}\n");
|
|
`("src/node-host/config-file-store.ts", filesystemWriteViolations(4)),
|
|
"flags runtime writes to retired workspace setup and attestation sidecars": fsPathCase`
|
|
await fs.writeFile(path.join(workspaceDir, "openclaw-workspace-state.json"), "{}\\n");
|
|
await fs.writeFile(path.join(workspaceDir, ".openclaw", "workspace-state.json"), "{}\\n");
|
|
await fs.writeFile(path.join(stateDir, "workspace-attestations", \`\${workspaceKey}.attested\`), "ok\\n");
|
|
await fs.writeFile(\`\${workspaceDir}.attested\`, "ok\\n");
|
|
`("src/agents/workspace-sidecar-store.ts", filesystemWriteViolations(4, 5, 6, 7)),
|
|
"flags runtime writes to the retired native hook relay JSON registry": fsPathCase`
|
|
await fs.writeFile(path.join("/tmp", "openclaw-native-hook-relays-501", "relay.json"), "{}\n");
|
|
`("src/agents/harness/native-hook-relay-file-store.ts", filesystemWriteViolations(4)),
|
|
"flags runtime writes to the retired subagent JSON registry": fsPathCase`
|
|
await fs.writeFile(path.join(stateDir, "subagents", "runs.json"), "{}\n");
|
|
`("src/agents/subagent-registry-file-store.ts", filesystemWriteViolations(4)),
|
|
"flags runtime writes to retired skill-upload staging": fsPathCase`
|
|
await fs.writeFile(path.join(stateDir, "tmp", "skill-uploads", uploadId, "metadata.json"), "{}\n");
|
|
`("src/skills/lifecycle/upload-file-store.ts", filesystemWriteViolations(4)),
|
|
"flags runtime writes to retired system-agent rescue approval stores": fsPathCase`
|
|
await fs.writeFile(path.join(stateDir, "openclaw", "rescue-pending", \`\${key}.json\`), "{}\\n");
|
|
await fs.writeFile(path.join(stateDir, "crestodian", "rescue-pending", "old.json"), "{}\\n");
|
|
`("src/system-agent/rescue-writer.ts", filesystemWriteViolations(4, 5)),
|
|
"flags legacy paths with dynamic agent id segments": fsPathCase`
|
|
await fs.writeFile(path.join(stateDir, "agents", agentId, "agent", "auth.json"), "{}\\n");
|
|
`("dynamic-agent-auth.ts", filesystemWriteViolations(4)),
|
|
"flags legacy paths with dynamic segments and constant filenames": fsPathCase`
|
|
const AUTH_FILE = "auth.json";
|
|
await fs.writeFile(path.join(stateDir, "agents", agentId, "agent", AUTH_FILE), "{}\\n");
|
|
`("dynamic-agent-auth-constant.ts", filesystemWriteViolations(5)),
|
|
"flags legacy JSONL paths with dynamic template filenames": fsPathCase`
|
|
await fs.appendFile(path.join(stateDir, "cron", "runs", \`\${runId}.jsonl\`), "{}\\n");
|
|
`("dynamic-cron-run.ts", filesystemWriteViolations(4)),
|
|
"flags legacy paths assembled from filename constants": fsPathCase`
|
|
const STORE_FILE = "sessions.json";
|
|
const JOBS_FILE = "jobs.json";
|
|
const SQLITE_FILE = "state.sqlite";
|
|
const storePath = path.join(dir, STORE_FILE);
|
|
await fs.writeFile(storePath, "{}\\n", "utf8");
|
|
await fs.writeFile(path.join(stateDir, "cron", JOBS_FILE), "{}\\n");
|
|
await fs.writeFile(path.join(stateDir, "plugin-state", SQLITE_FILE), "");
|
|
`("constant-session-store.ts", filesystemWriteViolations(8, 9, 10)),
|
|
"flags legacy paths assembled from template literal constants": fsCase`
|
|
const sessionBase = "sessions";
|
|
const cronRuns = "cron/runs";
|
|
await fs.writeFile(\`\${sessionBase}.json\`, "{}\\n");
|
|
await fs.appendFile(\`\${cronRuns}/job.jsonl\`, "{}\\n");
|
|
`("template-constant-legacy-store.ts", filesystemWriteViolations(5, 6)),
|
|
"does not leak conditional literal path constants": fsPathCase`
|
|
const JOBS_FILE = "current.json";
|
|
if (debug) {
|
|
const JOBS_FILE = "jobs.json";
|
|
console.log(JOBS_FILE);
|
|
}
|
|
await fs.writeFile(path.join(stateDir, "cron", JOBS_FILE), "{}\\n");
|
|
`("conditional-literal-shadow.ts", []),
|
|
"keeps conditional literal reassignment candidates": fsPathCase`
|
|
let JOBS_FILE = "current.json";
|
|
if (debug) {
|
|
JOBS_FILE = "jobs.json";
|
|
}
|
|
await fs.writeFile(path.join(stateDir, "cron", JOBS_FILE), "{}\\n");
|
|
`("conditional-literal-reassignment.ts", filesystemWriteViolations(8)),
|
|
"keeps known literal candidates when conditional reassignment is dynamic": fsPathCase`
|
|
let JOBS_FILE = "jobs.json";
|
|
if (debug) {
|
|
JOBS_FILE = getJobsFile();
|
|
}
|
|
await fs.writeFile(path.join(stateDir, "cron", JOBS_FILE), "{}\\n");
|
|
`("conditional-dynamic-literal-reassignment.ts", filesystemWriteViolations(8)),
|
|
"drops stale literal candidates after exhaustive branch reassignment": fsPathCase`
|
|
let JOBS_FILE = "jobs.json";
|
|
if (debug) {
|
|
JOBS_FILE = "current.json";
|
|
} else {
|
|
JOBS_FILE = "active.json";
|
|
}
|
|
await fs.writeFile(path.join(stateDir, "cron", JOBS_FILE), "{}\\n");
|
|
`("exhaustive-literal-reassignment.ts", []),
|
|
"keeps known literal candidates after exhaustive dynamic branch reassignment": fsPathCase`
|
|
let JOBS_FILE = "current.json";
|
|
if (debug) {
|
|
JOBS_FILE = "jobs.json";
|
|
} else {
|
|
JOBS_FILE = getJobsFile();
|
|
}
|
|
await fs.writeFile(path.join(stateDir, "cron", JOBS_FILE), "{}\\n");
|
|
`("exhaustive-dynamic-literal-reassignment.ts", filesystemWriteViolations(10)),
|
|
"drops stale literal candidates after exhaustive dynamic branch reassignment": fsPathCase`
|
|
let JOBS_FILE = "jobs.json";
|
|
if (debug) {
|
|
JOBS_FILE = "current.json";
|
|
} else {
|
|
JOBS_FILE = getJobsFile();
|
|
}
|
|
await fs.writeFile(path.join(stateDir, "cron", JOBS_FILE), "{}\\n");
|
|
`("exhaustive-dynamic-stale-literal-reassignment.ts", []),
|
|
|
|
// Filesystem bindings, aliases, and helper writes.
|
|
"flags imported and destructured fs write aliases": sourceCase`
|
|
import fs, { writeFile as persist } from "node:fs/promises";
|
|
const { appendFile: append } = fs;
|
|
await persist("sessions.json", "{}\\n", "utf8");
|
|
await append("cron/runs/job.jsonl", "{}\\n");
|
|
`("aliased-fs.ts", filesystemWriteViolations(4, 5)),
|
|
"flags helper writes through namespace imports": sourceCase`
|
|
import * as jsonFiles from "../infra/json-files.js";
|
|
await jsonFiles.writeJson("sessions.json", {});
|
|
`("helper-namespace-write.ts", filesystemWriteViolations(3)),
|
|
"flags private file store writes to legacy paths": privateStoreCase`
|
|
await privateFileStore(stateDir).writeJson("thread-bindings.json", {});
|
|
`("private-file-store-write.ts", filesystemWriteViolations(3)),
|
|
"flags fs-safe factory aliases writing legacy paths": privateStoreCase`
|
|
import * as fsSafe from "openclaw/plugin-sdk/security-runtime";
|
|
const makePrivateStore = privateFileStore;
|
|
const makeRoot = fsSafe.root;
|
|
const { privateFileStore: makeFromNamespace } = fsSafe;
|
|
await makePrivateStore(stateDir).writeJson("thread-bindings.json", {});
|
|
await (await makeRoot(stateDir)).writeJson("plugin-binding-approvals.json", {});
|
|
await makeFromNamespace(stateDir).writeJson("gateway-restart-intent.json", {});
|
|
`("fs-safe-factory-alias-write.ts", filesystemWriteViolations(7, 8, 9)),
|
|
"flags fs-safe root writes to legacy paths": sourceCase`
|
|
import { root } from "openclaw/plugin-sdk/security-runtime";
|
|
const state = await root(stateDir);
|
|
await state.writeJson("plugin-binding-approvals.json", {});
|
|
await (await root(stateDir)).writeJson("thread-bindings.json", {});
|
|
`("fs-safe-root-write.ts", filesystemWriteViolations(4, 5)),
|
|
"flags bare fs-safe package root writes to legacy paths": sourceCase`
|
|
import { root } from "@openclaw/fs-safe";
|
|
const state = await root(stateDir);
|
|
await state.writeJson("thread-bindings.json", {});
|
|
`("bare-fs-safe-root-write.ts", filesystemWriteViolations(4)),
|
|
"flags file access runtime root writes to legacy paths": sourceCase`
|
|
import { root } from "openclaw/plugin-sdk/file-access-runtime";
|
|
const state = await root(stateDir);
|
|
await state.writeJson("thread-bindings.json", {});
|
|
`("extensions/example/src/runtime/file-access-root-write.ts", filesystemWriteViolations(4)),
|
|
"flags fs-safe store root writes to legacy paths": privateStoreCase`
|
|
const state = await privateFileStore(stateDir).root();
|
|
await state.writeJson("thread-bindings.json", {});
|
|
await (await privateFileStore(stateDir).root()).writeJson("plugin-binding-approvals.json", {});
|
|
`("fs-safe-store-root-write.ts", filesystemWriteViolations(4, 5)),
|
|
"allows fs-safe store reads from legacy paths": privateStoreCase`
|
|
const store = privateFileStore(stateDir);
|
|
await store.readJson("thread-bindings.json");
|
|
`("private-file-store-read.ts", []),
|
|
"flags fs-safe JSON store writes to legacy paths": privateStoreCase`
|
|
await privateFileStore(stateDir).json("thread-bindings.json").write({});
|
|
const bindings = privateFileStore(stateDir).json("plugin-binding-approvals.json");
|
|
await bindings.update((current) => current ?? {});
|
|
await privateFileStore(stateDir).json("gateway-restart-intent.json").updateOr({}, (current) => current);
|
|
`("private-file-json-store-write.ts", filesystemWriteViolations(3, 5, 6)),
|
|
"flags direct fs-safe package store writes to legacy paths": sourceCase`
|
|
import { fileStore, jsonStore } from "@openclaw/fs-safe/store";
|
|
await fileStore({ rootDir: stateDir }).writeJson("thread-bindings.json", {});
|
|
const options = { filePath: "plugin-binding-approvals.json" };
|
|
await jsonStore(options).write({});
|
|
await jsonStore({ filePath: "gateway-restart-intent.json" }).update((current) => current ?? {});
|
|
`("direct-fs-safe-store-write.ts", filesystemWriteViolations(3, 5, 6)),
|
|
"flags fs-safe store object aliases writing legacy paths": privateStoreCase`
|
|
const jsonBindings = privateFileStore(stateDir).json("plugin-binding-approvals.json");
|
|
const stores = {
|
|
state: privateFileStore(stateDir),
|
|
bindings: jsonBindings,
|
|
};
|
|
await stores.state.writeJson("thread-bindings.json", {});
|
|
await stores.bindings.write({});
|
|
stores.state = customStore;
|
|
stores.bindings = privateFileStore(stateDir).json("gateway-restart-intent.json");
|
|
await stores.state.writeJson("thread-bindings.json", {});
|
|
await stores.bindings.update((current) => current ?? {});
|
|
`("fs-safe-store-object-alias-write.ts", filesystemWriteViolations(8, 9, 13)),
|
|
"flags fs-safe store object aliases copied through spreads and nested objects":
|
|
privateStoreCase`
|
|
const base = { state: privateFileStore(stateDir) };
|
|
const stores = { ...base };
|
|
const nested = { inner: { bindings: privateFileStore(stateDir).json("plugin-binding-approvals.json") } };
|
|
await stores.state.writeJson("thread-bindings.json", {});
|
|
await nested.inner.bindings.write({});
|
|
`("fs-safe-store-spread-object-alias-write.ts", filesystemWriteViolations(6, 7)),
|
|
"flags fs-safe store object aliases assigned through nested object properties":
|
|
privateStoreCase`
|
|
const stores = {};
|
|
stores.inner = { bindings: privateFileStore(stateDir).json("thread-bindings.json") };
|
|
await stores.inner.bindings.write({});
|
|
`("assigned-nested-fs-safe-store-object-alias.ts", filesystemWriteViolations(5)),
|
|
"flags fs-safe store object aliases copied through destructuring": privateStoreCase`
|
|
const stores = { state: privateFileStore(stateDir) };
|
|
const nested = { inner: { bindings: privateFileStore(stateDir).json("plugin-binding-approvals.json") } };
|
|
const { state } = stores;
|
|
const { inner: { bindings } } = nested;
|
|
await state.writeJson("thread-bindings.json", {});
|
|
await bindings.write({});
|
|
`("fs-safe-store-destructured-object-alias-write.ts", filesystemWriteViolations(7, 8)),
|
|
"clears fs-safe store object aliases after exhaustive property reassignment":
|
|
privateStoreCase`
|
|
const stores = { state: privateFileStore(stateDir) };
|
|
if (flag) {
|
|
stores.state = customA;
|
|
} else {
|
|
stores.state = customB;
|
|
}
|
|
await stores.state.writeJson("thread-bindings.json", {});
|
|
`("exhaustive-fs-safe-store-property-reassignment.ts", []),
|
|
"clears nested fs-safe store object aliases after exhaustive property reassignment":
|
|
privateStoreCase`
|
|
const stores = { inner: { bindings: privateFileStore(stateDir).json("thread-bindings.json") } };
|
|
if (flag) {
|
|
stores.inner = { bindings: customA };
|
|
} else {
|
|
stores.inner = { bindings: customB };
|
|
}
|
|
await stores.inner.bindings.write({});
|
|
`("exhaustive-nested-fs-safe-store-property-reassignment.ts", []),
|
|
"keeps fs-safe store object aliases when one exhaustive property branch remains a store":
|
|
privateStoreCase`
|
|
const stores = { state: customStore };
|
|
if (flag) {
|
|
stores.state = customA;
|
|
} else {
|
|
stores.state = privateFileStore(stateDir);
|
|
}
|
|
await stores.state.writeJson("thread-bindings.json", {});
|
|
`("exhaustive-fs-safe-store-property-partial-reassignment.ts", filesystemWriteViolations(9)),
|
|
"flags direct fs-safe package namespace store writes to legacy paths": sourceCase`
|
|
import * as fsSafeStore from "@openclaw/fs-safe/store";
|
|
const store = fsSafeStore.fileStoreSync({ rootDir: stateDir });
|
|
store.writeJson("thread-bindings.json", {});
|
|
const bindings = fsSafeStore.jsonStore({ filePath: "plugin-binding-approvals.json" });
|
|
await bindings.write({});
|
|
`("direct-fs-safe-store-namespace-write.ts", filesystemWriteViolations(4, 6)),
|
|
"allows fs-safe JSON store reads from legacy paths": privateStoreCase`
|
|
const bindings = privateFileStore(stateDir).json("thread-bindings.json");
|
|
await bindings.read();
|
|
await privateFileStore(stateDir).json("plugin-binding-approvals.json").readOr({});
|
|
`("private-file-json-store-read.ts", []),
|
|
"clears fs-safe store aliases after exhaustive non-store reassignment": privateStoreCase`
|
|
let store = privateFileStore(stateDir);
|
|
if (flag) {
|
|
store = customA;
|
|
} else {
|
|
store = customB;
|
|
}
|
|
await store.writeJson("thread-bindings.json", {});
|
|
`("exhaustive-fs-safe-store-reassignment.ts", []),
|
|
"keeps fs-safe store aliases when one exhaustive branch remains a store": privateStoreCase`
|
|
let store = customStore;
|
|
if (flag) {
|
|
store = customA;
|
|
} else {
|
|
store = privateFileStore(stateDir);
|
|
}
|
|
await store.writeJson("thread-bindings.json", {});
|
|
`("exhaustive-fs-safe-store-partial-reassignment.ts", filesystemWriteViolations(9)),
|
|
"clears fs-safe namespace factory aliases after shadowing": sourceCase`
|
|
import * as fsSafe from "openclaw/plugin-sdk/security-runtime";
|
|
async function save(fsSafe: { root(dir: string): Promise<{ writeJson(path: string): void }> }) {
|
|
await (await fsSafe.root(stateDir)).writeJson("thread-bindings.json");
|
|
}
|
|
`("fs-safe-namespace-shadow.ts", []),
|
|
"ignores helper-like namespace imports from unrelated modules": sourceCase`
|
|
import * as runtime from "../runtime/json-output.js";
|
|
runtime.writeJson("sessions.json", {});
|
|
`("unrelated-helper-namespace.ts", []),
|
|
"ignores helper-like named imports from unrelated modules": sourceCase`
|
|
import { writeJson } from "../runtime/json-output.js";
|
|
writeJson("sessions.json", {});
|
|
`("unrelated-helper-named-import.ts", []),
|
|
"clears namespace helper aliases after shadowing": sourceCase`
|
|
import * as jsonFiles from "../infra/json-files.js";
|
|
function save(jsonFiles: { writeJson(path: string, value: unknown): void }) {
|
|
jsonFiles.writeJson("sessions.json", {});
|
|
}
|
|
save(customJsonFiles);
|
|
`("helper-namespace-shadow.ts", []),
|
|
"allows read-only fs open calls and flags write modes": fsPromisesCase`
|
|
await fs.open("sessions.json");
|
|
await fs.open("sessions.json", "r");
|
|
await fs.open("sessions.json", "r+");
|
|
await fs.open("sessions.json", "w");
|
|
`("open-flags.ts", filesystemWriteViolations(5, 6)),
|
|
"flags fs copy calls writing legacy store paths": fsPromisesCase`
|
|
import syncFs from "node:fs";
|
|
await fs.cp("source.json", "sessions.json");
|
|
syncFs.cpSync("source.json", "cron/jobs.json");
|
|
`("fs-copy-legacy-store.ts", filesystemWriteViolations(4, 5)),
|
|
"allows fs copy calls reading from legacy store paths": fsPromisesCase`
|
|
import syncFs from "node:fs";
|
|
await fs.copyFile("sessions.json", "state/openclaw.sqlite.import");
|
|
await fs.cp("cron/jobs.json", "state/openclaw.sqlite.import");
|
|
syncFs.copyFileSync("auth-profiles.json", "state/openclaw.sqlite.import");
|
|
syncFs.cpSync("cache/models.json", "state/openclaw.sqlite.import");
|
|
`("fs-copy-legacy-store-source.ts", []),
|
|
"flags fs removal calls targeting legacy store paths": fsPromisesCase`
|
|
import syncFs from "node:fs";
|
|
await fs.rm("sessions.json", { force: true });
|
|
syncFs.unlinkSync("cron/jobs.json");
|
|
`("fs-remove-legacy-store.ts", filesystemWriteViolations(4, 5)),
|
|
"flags legacy paths destructured from for-of tuple entries": sourceCase`
|
|
import path from "node:path";
|
|
import { root as fsRoot } from "openclaw/plugin-sdk/security-runtime";
|
|
const CLAIMS_DIGEST_PATH = ".openclaw-wiki/cache/claims.jsonl";
|
|
const claimsDigestPath = path.join(rootDir, CLAIMS_DIGEST_PATH);
|
|
for (const [filePath, content] of [[claimsDigestPath, claimsDigest]]) {
|
|
const relativePath = path.relative(rootDir, filePath);
|
|
const root = await fsRoot(rootDir);
|
|
await root.write(relativePath, content);
|
|
}
|
|
`("for-of-destructured-legacy-path.ts", filesystemWriteViolations(9)),
|
|
"applies open write-mode checks inside wrappers": fsPromisesCase`
|
|
function read(path: string) {
|
|
return fs.open(path, "r");
|
|
}
|
|
function write(path: string) {
|
|
return fs.open(path, "w");
|
|
}
|
|
await read("sessions.json");
|
|
await write("sessions.json");
|
|
`("open-wrapper-flags.ts", filesystemWriteViolations(10)),
|
|
"flags string-literal fs write aliases from destructuring": fsPromisesCase`
|
|
const { "writeFile": persist } = fs;
|
|
await persist("sessions.json", "{}\\n", "utf8");
|
|
`("string-literal-fs-alias.ts", filesystemWriteViolations(4)),
|
|
"flags CommonJS fs write aliases": sourceCase`
|
|
const fs = require("node:fs");
|
|
const { appendFileSync } = require("node:fs");
|
|
fs.writeFileSync("sessions.json", "{}\\n");
|
|
appendFileSync("cron/runs/job.jsonl", "{}\\n");
|
|
`("commonjs-fs-aliases.ts", filesystemWriteViolations(4, 5)),
|
|
"does not treat local require bindings as CommonJS fs": sourceCase`
|
|
function save(require: (specifier: string) => { writeFileSync(path: string, value: string): void }) {
|
|
const fs = require("node:fs");
|
|
fs.writeFileSync("sessions.json", "");
|
|
}
|
|
save(customRequire);
|
|
`("local-require-binding.ts", []),
|
|
"flags createRequire-backed CommonJS fs writes": requireCase`
|
|
const require = createRequire(import.meta.url);
|
|
const fs = require("node:fs");
|
|
fs.writeFileSync("sessions.json", "{}\\n");
|
|
`("create-require-fs.ts", filesystemWriteViolations(5)),
|
|
"flags createRequire alias-backed CommonJS fs writes": requireCase`
|
|
const req = createRequire(import.meta.url);
|
|
const fs = req("node:fs");
|
|
fs.writeFileSync("sessions.json", "{}\\n");
|
|
`("create-require-alias-fs.ts", filesystemWriteViolations(5)),
|
|
"flags copied createRequire alias-backed CommonJS fs writes": requireCase`
|
|
const req = createRequire(import.meta.url);
|
|
const req2 = req;
|
|
const fs = req2("node:fs");
|
|
fs.writeFileSync("sessions.json", "{}\\n");
|
|
`("copied-create-require-alias-fs.ts", filesystemWriteViolations(6)),
|
|
"flags reassigned createRequire alias-backed CommonJS fs writes": requireCase`
|
|
let req;
|
|
req = createRequire(import.meta.url);
|
|
const fs = req("node:fs");
|
|
fs.writeFileSync("sessions.json", "{}\\n");
|
|
`("reassigned-create-require-alias-fs.ts", filesystemWriteViolations(6)),
|
|
"flags reassigned createRequire aliases named require": requireCase`
|
|
let require;
|
|
require = createRequire(import.meta.url);
|
|
const fs = require("node:fs");
|
|
fs.writeFileSync("sessions.json", "{}\\n");
|
|
`("reassigned-create-require-name.ts", filesystemWriteViolations(6)),
|
|
"refreshes hoisted wrappers after createRequire alias reassignment": requireCase`
|
|
let req;
|
|
function persist(filePath: string) {
|
|
const fs = req("node:fs");
|
|
fs.writeFileSync(filePath, "{}\\n");
|
|
}
|
|
req = createRequire(import.meta.url);
|
|
persist("sessions.json");
|
|
`("hoisted-wrapper-reassigned-create-require-alias.ts", filesystemWriteViolations(9)),
|
|
"refreshes hoisted wrappers after nested createRequire alias reassignment": requireCase`
|
|
let req;
|
|
function persist(filePath: string) {
|
|
const fs = req("node:fs");
|
|
fs.writeFileSync(filePath, "{}\\n");
|
|
}
|
|
{
|
|
req = createRequire(import.meta.url);
|
|
}
|
|
persist("sessions.json");
|
|
`("hoisted-wrapper-nested-create-require-alias.ts", filesystemWriteViolations(11)),
|
|
"refreshes block-scoped wrappers after nested outer createRequire alias reassignment":
|
|
requireCase`
|
|
let req;
|
|
{
|
|
function persist(filePath: string) {
|
|
const fs = req("node:fs");
|
|
fs.writeFileSync(filePath, "{}\\n");
|
|
}
|
|
{
|
|
req = createRequire(import.meta.url);
|
|
}
|
|
persist("sessions.json");
|
|
}
|
|
`("block-wrapper-nested-create-require-alias.ts", filesystemWriteViolations(12)),
|
|
"refreshes escaped wrappers after outer createRequire alias reassignment": requireCase`
|
|
let req;
|
|
let persist;
|
|
{
|
|
function inner(filePath: string) {
|
|
const fs = req("node:fs");
|
|
fs.writeFileSync(filePath, "{}\\n");
|
|
}
|
|
persist = inner;
|
|
}
|
|
req = createRequire(import.meta.url);
|
|
persist("sessions.json");
|
|
`("escaped-wrapper-create-require-alias.ts", filesystemWriteViolations(13)),
|
|
"keeps escaped wrapper local require shadows after outer createRequire alias reassignment":
|
|
requireCase`
|
|
let req;
|
|
let persist;
|
|
{
|
|
let req;
|
|
function inner(filePath: string) {
|
|
const fs = req("node:fs");
|
|
fs.writeFileSync(filePath, "{}\\n");
|
|
}
|
|
persist = inner;
|
|
}
|
|
req = createRequire(import.meta.url);
|
|
persist("sessions.json");
|
|
`("escaped-wrapper-local-require-shadow.ts", []),
|
|
"does not treat parameter shadows as createRequire aliases": requireCase`
|
|
const req = createRequire(import.meta.url);
|
|
function save(req: (specifier: string) => { writeFileSync(path: string, value: string): void }) {
|
|
const fs = req("node:fs");
|
|
fs.writeFileSync("sessions.json", "");
|
|
}
|
|
save(customRequire);
|
|
`("create-require-parameter-shadow.ts", []),
|
|
"does not treat shadowed createRequire bindings as Node require": requireCase`
|
|
function save(createRequire: (url: string) => (specifier: string) => { writeFileSync(path: string, value: string): void }) {
|
|
const require = createRequire("custom");
|
|
const fs = require("node:fs");
|
|
fs.writeFileSync("sessions.json", "");
|
|
}
|
|
save(customCreateRequire);
|
|
`("shadowed-create-require.ts", []),
|
|
"does not treat hoisted function createRequire shadows as Node require": requireCase`
|
|
function run() {
|
|
function createRequire(url: string) {
|
|
return customRequire(url);
|
|
}
|
|
const req = createRequire(import.meta.url);
|
|
const fs = req("node:fs");
|
|
fs.writeFileSync("sessions.json", "{}\\n");
|
|
}
|
|
run();
|
|
`("hoisted-create-require-shadow.ts", []),
|
|
"flags CommonJS fs promises aliases": sourceCase`
|
|
const { promises: fs } = require("node:fs");
|
|
const { promises } = require("node:fs");
|
|
await fs.writeFile("sessions.json", "{}\\n");
|
|
await promises.appendFile("cron/runs/job.jsonl", "{}\\n");
|
|
`("commonjs-fs-promises-aliases.ts", filesystemWriteViolations(4, 5)),
|
|
"flags nested CommonJS fs promises write aliases": sourceCase`
|
|
const { promises: { writeFile } } = require("node:fs");
|
|
await writeFile("sessions.json", "{}\\n");
|
|
`("nested-commonjs-fs-promises-alias.ts", filesystemWriteViolations(3)),
|
|
"flags inline CommonJS fs writes": sourceCase`
|
|
require("node:fs").writeFileSync("sessions.json", "{}\\n");
|
|
require("node:fs").promises.writeFile("cron/jobs.json", "{}\\n");
|
|
`("inline-commonjs-fs-write.ts", filesystemWriteViolations(2, 3)),
|
|
"flags bracketed fs writes": sourceCase`
|
|
import fs from "node:fs";
|
|
await fs["writeFile"]("sessions.json", "{}\\n");
|
|
await fs.promises["writeFile"]("cron/runs/job.jsonl", "{}\\n");
|
|
require("node:fs")["writeFileSync"]("sessions.json", "{}\\n");
|
|
`("bracketed-fs-writes.ts", filesystemWriteViolations(3, 4, 5)),
|
|
"flags dynamic fs import writes": sourceCase`
|
|
const fs = await import("node:fs/promises");
|
|
const nodeFs = await import("node:fs");
|
|
await fs.writeFile("sessions.json", "{}\\n");
|
|
await nodeFs.promises.appendFile("cron/runs/job.jsonl", "{}\\n");
|
|
`("dynamic-fs-import-write.ts", filesystemWriteViolations(4, 5)),
|
|
"flags dynamic fs import write aliases": sourceCase`
|
|
const { writeFile } = await import("node:fs/promises");
|
|
const { promises } = await import("node:fs");
|
|
await writeFile("sessions.json", "{}\\n");
|
|
await promises.appendFile("cron/runs/job.jsonl", "{}\\n");
|
|
`("dynamic-fs-import-aliases.ts", filesystemWriteViolations(4, 5)),
|
|
"flags dynamic fs import promise callback writes": sourceCase`
|
|
await import("node:fs/promises").then((fs) =>
|
|
fs.writeFile("sessions.json", "{}\\n"),
|
|
);
|
|
`("dynamic-fs-import-promise-callback.ts", filesystemWriteViolations(3)),
|
|
"flags destructured dynamic fs import promise callback writes": sourceCase`
|
|
await import("node:fs/promises").then(({ writeFile }) =>
|
|
writeFile("sessions.json", "{}\\n"),
|
|
);
|
|
await import("node:fs").then(({ promises }) =>
|
|
promises.appendFile("cron/runs/job.jsonl", "{}\\n"),
|
|
);
|
|
await import("node:fs").then(({ promises: { writeFile: persist } }) =>
|
|
persist("sessions.json", "{}\\n"),
|
|
);
|
|
`("destructured-dynamic-fs-import-promise-callback.ts", filesystemWriteViolations(3, 6, 9)),
|
|
"flags write aliases destructured from fs.promises": sourceCase`
|
|
import * as fs from "node:fs";
|
|
const { writeFile: persist } = fs.promises;
|
|
const fsp = fs.promises;
|
|
const { appendFile } = fsp;
|
|
await persist("sessions.json", "{}\\n", "utf8");
|
|
await appendFile("cron/runs/job.jsonl", "{}\\n");
|
|
`("fs-promises-aliases.ts", filesystemWriteViolations(6, 7)),
|
|
"flags fs write method aliases": fsPromisesCase`
|
|
const persist = fs.writeFile;
|
|
await persist("sessions.json", "{}\\n");
|
|
`("fs-write-method-alias.ts", filesystemWriteViolations(4)),
|
|
"flags write aliases destructured from local fs module aliases": fsPromisesCase`
|
|
{
|
|
const storage = fs;
|
|
const { writeFile } = storage;
|
|
await writeFile("sessions.json", "{}\\n");
|
|
}
|
|
`("local-fs-module-alias.ts", filesystemWriteViolations(6)),
|
|
"flags nested write aliases destructured from local fs module aliases": sourceCase`
|
|
const nodeFs = require("node:fs");
|
|
const { promises: { writeFile } } = nodeFs;
|
|
await writeFile("sessions.json", "{}\\n");
|
|
`("nested-local-fs-module-alias.ts", filesystemWriteViolations(4)),
|
|
"clears fs module aliases after reassignment": fsPromisesCase`
|
|
let writer = fs;
|
|
writer = customWriter;
|
|
await writer.writeFile("sessions.json", "{}\\n");
|
|
`("reassigned-fs-module-alias.ts", []),
|
|
"uses branch-local fs module aliases after conditional assignment": fsPromisesCase`
|
|
let writer;
|
|
if (ready) {
|
|
writer = fs;
|
|
await writer.writeFile("sessions.json", "{}\\n");
|
|
}
|
|
`("conditional-fs-module-alias.ts", filesystemWriteViolations(6)),
|
|
"keeps fs module aliases after conditional assignment": fsPromisesCase`
|
|
let writer;
|
|
if (ready) {
|
|
writer = fs;
|
|
}
|
|
await writer.writeFile("sessions.json", "{}\\n");
|
|
`("conditional-retained-fs-module-alias.ts", filesystemWriteViolations(7)),
|
|
"keeps fs write aliases after conditional assignment": fsPromisesCase`
|
|
let persist;
|
|
if (ready) {
|
|
persist = fs.writeFile;
|
|
}
|
|
await persist("sessions.json", "{}\\n");
|
|
`("conditional-retained-fs-write-alias.ts", filesystemWriteViolations(7)),
|
|
"clears fs module aliases after exhaustive conditional reassignment": fsPromisesCase`
|
|
let writer = fs;
|
|
if (ready) {
|
|
writer = customWriter;
|
|
} else {
|
|
writer = otherWriter;
|
|
}
|
|
await writer.writeFile("sessions.json", "{}\\n");
|
|
`("exhaustive-reassigned-fs-module-alias.ts", []),
|
|
"keeps uninitialized fs aliases assigned from nested blocks": fsPromisesCase`
|
|
let writer;
|
|
let persist;
|
|
{
|
|
writer = fs;
|
|
persist = fs.writeFile;
|
|
}
|
|
await writer.writeFile("sessions.json", "{}\\n");
|
|
await persist("cron/jobs.json", "{}\\n");
|
|
`("nested-assigned-uninitialized-fs-aliases.ts", filesystemWriteViolations(9, 10)),
|
|
"flags fs write aliases stored on object properties": fsPromisesCase`
|
|
const writer = { writeFile: fs.writeFile };
|
|
await writer.writeFile("sessions.json", "{}\\n");
|
|
`("object-fs-write-alias.ts", filesystemWriteViolations(4)),
|
|
"flags fs module handles stored on object properties": fsPromisesCase`
|
|
const deps = { fs };
|
|
const io = { storage: fs };
|
|
await deps.fs.writeFile("sessions.json", "{}\\n");
|
|
await io.storage.appendFile("cron/runs/job.jsonl", "{}\\n");
|
|
`("object-fs-module-alias.ts", filesystemWriteViolations(5, 6)),
|
|
"clears fs write object aliases after object reassignment": fsPromisesCase`
|
|
let writer = { writeFile: fs.writeFile };
|
|
writer = customWriter;
|
|
await writer.writeFile("sessions.json", "{}\\n");
|
|
`("reassigned-object-fs-write-alias.ts", []),
|
|
"clears fs module object aliases after object reassignment": fsPromisesCase`
|
|
let deps = { fs };
|
|
deps = customDeps;
|
|
await deps.fs.writeFile("sessions.json", "{}\\n");
|
|
`("reassigned-object-fs-module-alias.ts", []),
|
|
"flags fs write aliases assigned to object properties": fsPromisesCase`
|
|
const writer: any = {};
|
|
writer.writeFile = fs.writeFile;
|
|
await writer.writeFile("sessions.json", "{}\\n");
|
|
`("assigned-object-fs-write-alias.ts", filesystemWriteViolations(5)),
|
|
"flags fs module handles assigned to object properties": fsPromisesCase`
|
|
const deps: any = {};
|
|
deps.fs = fs;
|
|
await deps.fs.writeFile("sessions.json", "{}\\n");
|
|
`("assigned-object-fs-module-alias.ts", filesystemWriteViolations(5)),
|
|
"uses branch-local fs object aliases after conditional reassignment": fsPromisesCase`
|
|
const writer = { writeFile: fs.writeFile };
|
|
if (ready) {
|
|
writer.writeFile = customSink;
|
|
await writer.writeFile("sessions.json", "{}\\n");
|
|
}
|
|
`("conditional-object-fs-write-alias-reassignment.ts", []),
|
|
"does not leak local fs module aliases outside their scope": fsPromisesCase`
|
|
{
|
|
const storage = fs;
|
|
const { writeFile } = storage;
|
|
await writeFile(currentSqlitePath, "{}\\n");
|
|
}
|
|
{
|
|
const storage = customWriter;
|
|
const { writeFile } = storage;
|
|
await writeFile("sessions.json", "{}\\n");
|
|
}
|
|
`("local-fs-module-alias-scope.ts", []),
|
|
"flags legacy paths written through regular-file helpers": sourceCase`
|
|
import { appendRegularFile as appendSafe } from "openclaw/plugin-sdk/security-runtime";
|
|
const filePath = "session.trajectory.jsonl";
|
|
await appendSafe({ filePath, content: "{}\\n" });
|
|
`("regular-file-helper.ts", filesystemWriteViolations(4)),
|
|
"flags legacy paths written through JSON and atomic helpers": sourceCase`
|
|
import { writeJson, writeTextAtomic } from "../infra/json-files.js";
|
|
import { replaceFileAtomicSync } from "../infra/replace-file.js";
|
|
import { saveJsonFile, writeJsonFileAtomically } from "openclaw/plugin-sdk/json-store";
|
|
await writeJson("restart-sentinel.json", {});
|
|
await writeTextAtomic("gateway-restart-intent.json", "{}\\n");
|
|
replaceFileAtomicSync({ filePath: "plugin-state/state.sqlite", content: "" });
|
|
await writeJsonFileAtomically("thread-bindings.json", {});
|
|
saveJsonFile("plugin-binding-approvals.json", {});
|
|
`("write-helper-regressions.ts", [
|
|
{ kind: "legacy restart sentinel reference", line: 5 },
|
|
{ kind: "legacy store filesystem write", line: 5 },
|
|
{ kind: "legacy store filesystem write", line: 6 },
|
|
{ kind: "legacy store filesystem write", line: 7 },
|
|
{ kind: "legacy store filesystem write", line: 8 },
|
|
{ kind: "legacy store filesystem write", line: 9 },
|
|
]),
|
|
"flags legacy paths passed through wrapper object properties": sourceCase`
|
|
import path from "node:path";
|
|
import { writeTextAtomic } from "../infra/json-files.js";
|
|
function persist(params: { filePath: string }) {
|
|
return writeTextAtomic(params.filePath, "{}\\n");
|
|
}
|
|
const ledgerPath = path.join(stateDir, "acp", "event-ledger.json");
|
|
await persist({ filePath: ledgerPath });
|
|
`("object-property-wrapper.ts", filesystemWriteViolations(8)),
|
|
"flags wrapper paths written through createRequire aliases": requireCase`
|
|
const req = createRequire(import.meta.url);
|
|
function persist(filePath: string) {
|
|
const fs = req("node:fs");
|
|
fs.writeFileSync(filePath, "{}\\n");
|
|
}
|
|
persist("sessions.json");
|
|
`("create-require-alias-wrapper.ts", filesystemWriteViolations(8)),
|
|
"flags wrapper-local createRequire alias writes": requirePersistCase`
|
|
const req = createRequire(import.meta.url);
|
|
const fs = req("node:fs");
|
|
fs.writeFileSync(filePath, "{}\\n");
|
|
`("wrapper-local-create-require-alias.ts", filesystemWriteViolations(8)),
|
|
"flags wrapper-local copied createRequire aliases": requirePersistCase`
|
|
const req = createRequire(import.meta.url);
|
|
const req2 = req;
|
|
const fs = req2("node:fs");
|
|
fs.writeFileSync(filePath, "{}\\n");
|
|
`("wrapper-copied-create-require-alias.ts", filesystemWriteViolations(9)),
|
|
"flags wrapper-local createRequire aliases after local shadow reassignment": requireCase`
|
|
const req = createRequire(import.meta.url);
|
|
function persist(filePath: string) {
|
|
let req;
|
|
req = createRequire(import.meta.url);
|
|
const fs = req("node:fs");
|
|
fs.writeFileSync(filePath, "{}\\n");
|
|
}
|
|
persist("sessions.json");
|
|
`("wrapper-shadowed-reassigned-create-require-alias.ts", filesystemWriteViolations(10)),
|
|
"flags wrapper-local reassigned createRequire aliases named require": requirePersistCase`
|
|
let require;
|
|
require = createRequire(import.meta.url);
|
|
const fs = require("node:fs");
|
|
fs.writeFileSync(filePath, "{}\\n");
|
|
`("wrapper-reassigned-create-require-name.ts", filesystemWriteViolations(9)),
|
|
"flags wrapper-local createRequire alias assignments inside blocks": requirePersistCase`
|
|
let req;
|
|
{
|
|
req = createRequire(import.meta.url);
|
|
}
|
|
const fs = req("node:fs");
|
|
fs.writeFileSync(filePath, "{}\\n");
|
|
`("wrapper-block-create-require-assignment.ts", filesystemWriteViolations(11)),
|
|
"does not treat wrapper-shadowed createRequire parameters as Node createRequire": sourceCase`
|
|
function persist(
|
|
filePath: string,
|
|
createRequire: (url: string) => (specifier: string) => { writeFileSync(path: string, value: string): void },
|
|
) {
|
|
const req = createRequire(import.meta.url);
|
|
const fs = req("node:fs");
|
|
fs.writeFileSync(filePath, "{}\\n");
|
|
}
|
|
persist("sessions.json", customCreateRequire);
|
|
`("wrapper-shadowed-create-require.ts", []),
|
|
"does not treat wrapper hoisted function createRequire shadows as Node createRequire":
|
|
requirePersistCase`
|
|
function createRequire(url: string) {
|
|
return customRequire(url);
|
|
}
|
|
const req = createRequire(import.meta.url);
|
|
const fs = req("node:fs");
|
|
fs.writeFileSync(filePath, "{}\\n");
|
|
`("wrapper-hoisted-create-require-shadow.ts", []),
|
|
"keeps wrapper lexical createRequire aliases when call sites shadow them": requireCase`
|
|
const req = createRequire(import.meta.url);
|
|
function persist(filePath: string) {
|
|
const fs = req("node:fs");
|
|
fs.writeFileSync(filePath, "{}\\n");
|
|
}
|
|
{
|
|
const req = customRequire;
|
|
persist("sessions.json");
|
|
}
|
|
`("wrapper-lexical-create-require-alias.ts", filesystemWriteViolations(10)),
|
|
"keeps wrapper-local createRequire calls when call sites shadow createRequire": requireCase`
|
|
function persist(filePath: string) {
|
|
const req = createRequire(import.meta.url);
|
|
const fs = req("node:fs");
|
|
fs.writeFileSync(filePath, "{}\\n");
|
|
}
|
|
{
|
|
const createRequire = customCreateRequire;
|
|
persist("sessions.json");
|
|
}
|
|
`("wrapper-create-require-call-site-shadow.ts", filesystemWriteViolations(10)),
|
|
"flags exhaustive conditional createRequire alias assignments": requireCase`
|
|
let req;
|
|
if (condition) {
|
|
req = createRequire(import.meta.url);
|
|
} else {
|
|
req = createRequire(import.meta.url);
|
|
}
|
|
const fs = req("node:fs");
|
|
fs.writeFileSync("sessions.json", "{}\\n");
|
|
`("conditional-create-require-alias.ts", filesystemWriteViolations(10)),
|
|
"refreshes hoisted wrappers after exhaustive createRequire alias branches": requireCase`
|
|
let req;
|
|
function persist(filePath: string) {
|
|
const fs = req("node:fs");
|
|
fs.writeFileSync(filePath, "{}\\n");
|
|
}
|
|
if (condition) {
|
|
req = createRequire(import.meta.url);
|
|
} else {
|
|
req = createRequire(import.meta.url);
|
|
}
|
|
persist("sessions.json");
|
|
`("hoisted-wrapper-conditional-create-require-alias.ts", filesystemWriteViolations(13)),
|
|
"keeps wrapper conditional createRequire alias branches": requirePersistCase`
|
|
let req;
|
|
if (condition) {
|
|
req = createRequire(import.meta.url);
|
|
} else {
|
|
req = customRequire;
|
|
}
|
|
const fs = req("node:fs");
|
|
fs.writeFileSync(filePath, "{}\\n");
|
|
`("wrapper-conditional-create-require-alias.ts", filesystemWriteViolations(13)),
|
|
|
|
// Wrapper argument and default-value propagation.
|
|
"flags legacy paths passed through named wrapper options": jsonPersistCase`
|
|
const store = "sessions.json";
|
|
const params = { store };
|
|
await persist(params);
|
|
`("named-wrapper-options.ts", filesystemWriteViolations(8)),
|
|
"flags legacy paths read through chained wrapper option properties": atomicCase`
|
|
function persist(params: { filePath: string }) {
|
|
return writeTextAtomic(params.filePath.toString(), "{}\\n");
|
|
}
|
|
const options = { filePath: "sessions.json" };
|
|
await persist(options);
|
|
`("chained-wrapper-option-path.ts", filesystemWriteViolations(7)),
|
|
"flags legacy paths passed through destructured wrapper options": atomicCase`
|
|
function persist({ filePath }: { filePath: string }) {
|
|
return writeTextAtomic(filePath, "{}\\n");
|
|
}
|
|
await persist({ filePath: "sessions.json" });
|
|
`("destructured-wrapper-options.ts", filesystemWriteViolations(6)),
|
|
"flags legacy paths passed through nested destructured wrapper options": atomicCase`
|
|
function persist({ paths: { filePath } }: { paths: { filePath: string } }) {
|
|
return writeTextAtomic(filePath, "{}\\n");
|
|
}
|
|
await persist({ paths: { filePath: "sessions.json" } });
|
|
`("nested-destructured-wrapper-options.ts", filesystemWriteViolations(6)),
|
|
"flags legacy paths from nested destructured wrapper option defaults": atomicCase`
|
|
function persist({ paths: { filePath = "sessions.json" } }: { paths: { filePath?: string } }) {
|
|
return writeTextAtomic(filePath, "{}\\n");
|
|
}
|
|
await persist({ paths: {} });
|
|
`("nested-destructured-wrapper-option-default.ts", filesystemWriteViolations(6)),
|
|
"flags nested parameter defaults from identifier-valued intermediate objects": atomicCase`
|
|
const paths = { filePath: "sessions.json" };
|
|
function persist({ paths: { filePath } }: { paths: { filePath: string } } = { paths }) {
|
|
return writeTextAtomic(filePath, "{}\\n");
|
|
}
|
|
await persist();
|
|
`("nested-parameter-default-identifier-intermediate.ts", filesystemWriteViolations(7)),
|
|
"flags nested destructuring defaults from identifier-valued intermediate objects": atomicCase`
|
|
const paths = {};
|
|
function persist({ paths: { filePath = "sessions.json" } }: { paths: { filePath?: string } } = { paths }) {
|
|
return writeTextAtomic(filePath, "{}\\n");
|
|
}
|
|
await persist();
|
|
`("nested-destructuring-default-identifier-intermediate.ts", filesystemWriteViolations(7)),
|
|
"flags nested destructuring defaults from aliased known object literals": atomicCase`
|
|
function persist({ paths: { filePath = "sessions.json" } }: { paths: { filePath?: string } }) {
|
|
return writeTextAtomic(filePath, "{}\\n");
|
|
}
|
|
const source = { paths: {} };
|
|
const options = source;
|
|
await persist(options);
|
|
`("nested-destructuring-default-aliased-known-object.ts", filesystemWriteViolations(8)),
|
|
"flags nested destructuring defaults from parent binding defaults": fsCase`
|
|
function persist({ paths: { filePath } = { filePath: "sessions.json" } }) {
|
|
return fs.writeFile(filePath, "{}\\n");
|
|
}
|
|
await persist({});
|
|
`("nested-destructuring-parent-binding-default.ts", filesystemWriteViolations(6)),
|
|
"does not force nested destructured defaults for unknown intermediate properties": fsCase`
|
|
declare function loadPaths(): { filePath?: string };
|
|
function persist({ paths: { filePath = "sessions.json" } }: { paths: { filePath?: string } }) {
|
|
return fs.writeFile(filePath, "{}\\n");
|
|
}
|
|
const options = { paths: loadPaths() };
|
|
await persist(options);
|
|
`("nested-destructured-wrapper-option-unknown-intermediate.ts", []),
|
|
"flags defaults referencing earlier nested destructured identifier parameters": fsCase`
|
|
function writePath({ paths: { filePath } }: { paths: { filePath: string } }, path = filePath) {
|
|
return fs.writeFile(path, "{}\\n");
|
|
}
|
|
const options = { paths: { filePath: "sessions.json" } };
|
|
await writePath(options);
|
|
`("nested-destructured-wrapper-earlier-identifier-default.ts", filesystemWriteViolations(7)),
|
|
"flags defaults referencing earlier nested object parameter properties": fsCase`
|
|
function writePath(options: { paths: { filePath: string } }, path = options.paths.filePath) {
|
|
return fs.writeFile(path, "{}\\n");
|
|
}
|
|
const options = { paths: { filePath: "sessions.json" } };
|
|
await writePath(options);
|
|
`("nested-object-wrapper-earlier-property-default.ts", filesystemWriteViolations(7)),
|
|
"flags legacy paths passed through positional wrapper parameters": atomicCase`
|
|
function persist(filePath: string) {
|
|
return writeTextAtomic(filePath, "{}\\n");
|
|
}
|
|
await persist("sessions.json");
|
|
`("positional-wrapper-path.ts", filesystemWriteViolations(6)),
|
|
"flags defaulted wrapper parameters after optional safe assignments": fsCase`
|
|
let filePath;
|
|
if (useDb) filePath = currentSqlitePath;
|
|
function persist(path = "sessions.json") {
|
|
return fs.writeFile(path, "{}\\n");
|
|
}
|
|
await persist(filePath);
|
|
`("conditional-undefined-defaulted-wrapper-parameter.ts", filesystemWriteViolations(8)),
|
|
"flags legacy paths forwarded through nested wrapper helpers": fsPersistCase`
|
|
function inner(nextPath: string) {
|
|
return fs.writeFile(nextPath, "{}\\n");
|
|
}
|
|
return inner(filePath);
|
|
`("nested-wrapper-path.ts", filesystemWriteViolations(9)),
|
|
"flags legacy paths captured by nested wrapper helpers": fsPersistCase`
|
|
function inner() {
|
|
return fs.writeFile(filePath, "{}\\n");
|
|
}
|
|
return inner();
|
|
`("nested-wrapper-closed-over-path.ts", filesystemWriteViolations(9)),
|
|
"flags legacy paths captured by nested helpers and forwarded to outer wrappers": fsCase`
|
|
function writePath(path: string) {
|
|
return fs.writeFile(path, "{}\\n");
|
|
}
|
|
function persist(filePath: string) {
|
|
function inner() {
|
|
return writePath(filePath);
|
|
}
|
|
return inner();
|
|
}
|
|
await persist("sessions.json");
|
|
`("nested-helper-forwarded-to-outer-wrapper.ts", filesystemWriteViolations(12)),
|
|
"keeps closed-over write aliases after loop-scoped shadows": fsPersistCase`
|
|
const write = fs.writeFile;
|
|
function inner() {
|
|
for (const write of [async () => {}]) {}
|
|
return write(filePath, "{}\\n");
|
|
}
|
|
return inner();
|
|
`("nested-wrapper-loop-shadowed-write-alias.ts", filesystemWriteViolations(11)),
|
|
"flags legacy paths forwarded through nested helper parameter defaults": fsPersistCase`
|
|
function inner(nextPath = filePath) {
|
|
return fs.writeFile(nextPath, "{}\\n");
|
|
}
|
|
return inner();
|
|
`("nested-helper-parameter-default.ts", filesystemWriteViolations(9)),
|
|
"flags legacy paths written by callable nested helper parameter defaults": fsPersistCase`
|
|
function inner(save = () => fs.writeFile(filePath, "{}\\n")) {
|
|
return save();
|
|
}
|
|
return inner();
|
|
`("nested-helper-callable-parameter-default.ts", filesystemWriteViolations(9)),
|
|
"does not use callable nested helper parameter defaults when callbacks are provided":
|
|
fsPersistCase`
|
|
function inner(save = () => fs.writeFile(filePath, "{}\\n")) {
|
|
return save();
|
|
}
|
|
return inner(async () => {});
|
|
`("nested-helper-callable-parameter-default-provided.ts", []),
|
|
"flags legacy paths forwarded through undefined nested helper arguments": fsPersistCase`
|
|
function inner(nextPath = filePath) {
|
|
return fs.writeFile(nextPath, "{}\\n");
|
|
}
|
|
return inner(undefined);
|
|
`("nested-helper-undefined-parameter-default.ts", filesystemWriteViolations(9)),
|
|
"flags legacy paths forwarded through void nested helper arguments": fsPersistCase`
|
|
function inner(nextPath = filePath) {
|
|
return fs.writeFile(nextPath, "{}\\n");
|
|
}
|
|
return inner(void 0);
|
|
`("nested-helper-void-parameter-default.ts", filesystemWriteViolations(9)),
|
|
"resolves nested helper parameter defaults in the helper scope": fsPersistCase`
|
|
function inner(filePath: string, nextPath = filePath) {
|
|
return fs.writeFile(nextPath, "{}\\n");
|
|
}
|
|
return inner(currentSqlitePath);
|
|
`("nested-helper-default-parameter-shadow.ts", []),
|
|
"does not resolve top-level helper parameter defaults in the caller scope": fsCase`
|
|
const defaultPath = "current-state.json";
|
|
function writePath(path = defaultPath) {
|
|
return fs.writeFile(path, "{}\\n");
|
|
}
|
|
function persist(defaultPath: string) {
|
|
return writePath();
|
|
}
|
|
await persist("sessions.json");
|
|
`("top-level-helper-default-caller-shadow.ts", []),
|
|
"does not resolve top-level helper object binding defaults in the caller scope": fsCase`
|
|
const defaultPath = "current-state.json";
|
|
function writePath({ path = defaultPath } = {}) {
|
|
return fs.writeFile(path, "{}\\n");
|
|
}
|
|
function persist(defaultPath: string) {
|
|
return writePath({});
|
|
}
|
|
await persist("sessions.json");
|
|
`("top-level-helper-object-binding-default-caller-shadow.ts", []),
|
|
"flags forwarded top-level helper object binding literal defaults": fsCase`
|
|
function writePath({ path = "sessions.json" } = {}) {
|
|
return fs.writeFile(path, "{}\\n");
|
|
}
|
|
function persist() {
|
|
return writePath({});
|
|
}
|
|
await persist();
|
|
`("top-level-helper-object-binding-literal-default.ts", filesystemWriteViolations(7)),
|
|
"does not resolve top-level helper expression defaults in the caller scope": fsCase`
|
|
const fallback = "current-state.json";
|
|
function writePath(path = filePath ?? fallback) {
|
|
return fs.writeFile(path, "{}\\n");
|
|
}
|
|
function persist(filePath: string) {
|
|
return writePath();
|
|
}
|
|
await persist("sessions.json");
|
|
`("top-level-helper-expression-default-caller-shadow.ts", []),
|
|
"flags top-level helper expression defaults derived from earlier parameters": fsCase`
|
|
const fallback = "current-state.json";
|
|
function writePath(filePath: string, path = filePath ?? fallback) {
|
|
return fs.writeFile(path, "{}\\n");
|
|
}
|
|
function persist(filePath: string) {
|
|
return writePath(filePath);
|
|
}
|
|
await persist("sessions.json");
|
|
`("top-level-helper-earlier-parameter-expression-default.ts", filesystemWriteViolations(10)),
|
|
"flags direct top-level helper calls with defaults derived from earlier arguments": fsCase`
|
|
const fallback = "current-state.json";
|
|
function writePath(filePath: string, path = filePath ?? fallback) {
|
|
return fs.writeFile(path, "{}\\n");
|
|
}
|
|
await writePath("sessions.json");
|
|
`("top-level-helper-direct-earlier-parameter-default.ts", filesystemWriteViolations(7)),
|
|
"flags direct top-level helper calls with defaults from earlier destructured arguments":
|
|
fsCase`
|
|
function writePath({ filePath }: { filePath: string }, path = filePath) {
|
|
return fs.writeFile(path, "{}\\n");
|
|
}
|
|
await writePath({ filePath: "sessions.json" });
|
|
`(
|
|
"top-level-helper-direct-destructured-earlier-parameter-default.ts",
|
|
filesystemWriteViolations(6),
|
|
),
|
|
"flags direct top-level helper calls with defaults from nested destructured arguments":
|
|
fsCase`
|
|
function writePath(
|
|
{ paths: { filePath } }: { paths: { filePath: string } },
|
|
path = filePath,
|
|
) {
|
|
return fs.writeFile(path, "{}\\n");
|
|
}
|
|
await writePath({ paths: { filePath: "sessions.json" } });
|
|
`(
|
|
"top-level-helper-direct-nested-destructured-earlier-parameter-default.ts",
|
|
filesystemWriteViolations(9),
|
|
),
|
|
"does not flag safe defaults that only inspect earlier legacy arguments": fsCase`
|
|
function writePath(
|
|
filePath: string,
|
|
path = filePath ? "current-state.json" : "current-state.json",
|
|
) {
|
|
return fs.writeFile(path, "{}\\n");
|
|
}
|
|
await writePath("sessions.json");
|
|
`("top-level-helper-safe-conditional-default.ts", []),
|
|
"flags direct top-level helper calls with method defaults from earlier arguments": fsCase`
|
|
function writePath(filePath: string, path = filePath.replace(/\\.json$/, ".json")) {
|
|
return fs.writeFile(path, "{}\\n");
|
|
}
|
|
await writePath("sessions.json");
|
|
`(
|
|
"top-level-helper-direct-method-earlier-parameter-default.ts",
|
|
filesystemWriteViolations(6),
|
|
),
|
|
"flags direct top-level helper calls with comma defaults from earlier arguments": fsCase`
|
|
const safePath = "current-state.json";
|
|
function writePath(filePath: string, path = (safePath, filePath)) {
|
|
return fs.writeFile(path, "{}\\n");
|
|
}
|
|
await writePath("sessions.json");
|
|
`("top-level-helper-direct-comma-earlier-parameter-default.ts", filesystemWriteViolations(7)),
|
|
"flags direct top-level helper calls with assignment defaults from earlier arguments": fsCase`
|
|
let cached = "current-state.json";
|
|
function writePath(filePath: string, path = (cached = filePath)) {
|
|
return fs.writeFile(path, "{}\\n");
|
|
}
|
|
await writePath("sessions.json");
|
|
`(
|
|
"top-level-helper-direct-assignment-earlier-parameter-default.ts",
|
|
filesystemWriteViolations(7),
|
|
),
|
|
"flags top-level helper object binding expression defaults derived from earlier parameters":
|
|
fsCase`
|
|
const fallback = "current-state.json";
|
|
function writePath(filePath: string, { path = filePath ?? fallback } = {}) {
|
|
return fs.writeFile(path, "{}\\n");
|
|
}
|
|
function persist(filePath: string) {
|
|
return writePath(filePath, {});
|
|
}
|
|
await persist("sessions.json");
|
|
`(
|
|
"top-level-helper-object-binding-earlier-parameter-expression-default.ts",
|
|
filesystemWriteViolations(10),
|
|
),
|
|
"flags direct top-level helper calls with object binding defaults from earlier arguments":
|
|
fsCase`
|
|
const fallback = "current-state.json";
|
|
function writePath(filePath: string, { path = filePath ?? fallback } = {}) {
|
|
return fs.writeFile(path, "{}\\n");
|
|
}
|
|
await writePath("sessions.json");
|
|
`(
|
|
"top-level-helper-direct-object-binding-earlier-parameter-default.ts",
|
|
filesystemWriteViolations(7),
|
|
),
|
|
"flags object binding defaults from missing properties on identifier arguments": fsCase`
|
|
const options = {};
|
|
function writePath(filePath: string, { path = filePath } = {}) {
|
|
return fs.writeFile(path, "{}\\n");
|
|
}
|
|
await writePath("sessions.json", options);
|
|
`(
|
|
"top-level-helper-object-binding-missing-identifier-default.ts",
|
|
filesystemWriteViolations(7),
|
|
),
|
|
"flags object binding defaults from undefined properties on identifier arguments": fsCase`
|
|
const options = { path: undefined };
|
|
function writePath(filePath: string, { path = filePath } = {}) {
|
|
return fs.writeFile(path, "{}\\n");
|
|
}
|
|
await writePath("sessions.json", options);
|
|
`(
|
|
"top-level-helper-object-binding-undefined-identifier-default.ts",
|
|
filesystemWriteViolations(7),
|
|
),
|
|
"flags object binding defaults from undefined properties in parameter defaults": fsCase`
|
|
function writePath({ path = "sessions.json" } = { path: undefined }) {
|
|
return fs.writeFile(path, "{}\\n");
|
|
}
|
|
await writePath();
|
|
`(
|
|
"top-level-helper-object-binding-undefined-parameter-default.ts",
|
|
filesystemWriteViolations(6),
|
|
),
|
|
"uses explicit safe properties on identifier arguments before object binding defaults":
|
|
fsCase`
|
|
const options = { path: "current-state.json" };
|
|
function writePath(filePath: string, { path = filePath } = {}) {
|
|
return fs.writeFile(path, "{}\\n");
|
|
}
|
|
await writePath("sessions.json", options);
|
|
`("top-level-helper-object-binding-safe-identifier-property.ts", []),
|
|
"does not force object binding defaults for unknown identifier arguments": fsCase`
|
|
declare function loadOptions(): { path?: string };
|
|
const options = loadOptions();
|
|
function writePath(filePath: string, { path = filePath } = {}) {
|
|
return fs.writeFile(path, "{}\\n");
|
|
}
|
|
await writePath("sessions.json", options);
|
|
`("top-level-helper-object-binding-unknown-identifier-default.ts", []),
|
|
"does not force object binding defaults for identifier arguments with unknown spreads":
|
|
fsCase`
|
|
declare const defaults: { path?: string };
|
|
const options = { ...defaults };
|
|
function writePath(filePath: string, { path = filePath } = {}) {
|
|
return fs.writeFile(path, "{}\\n");
|
|
}
|
|
await writePath("sessions.json", options);
|
|
`("top-level-helper-object-binding-unknown-spread-default.ts", []),
|
|
"keeps explicit undefined object properties after exhaustive branch merges": fsCase`
|
|
function writePath(filePath: string, { path = filePath } = {}) {
|
|
return fs.writeFile(path, "{}\\n");
|
|
}
|
|
const options = { path: "current-state.json" };
|
|
if (Math.random() > 0.5) {
|
|
options.path = undefined;
|
|
} else {
|
|
options.path = undefined;
|
|
}
|
|
await writePath("sessions.json", options);
|
|
`(
|
|
"top-level-helper-object-binding-branch-undefined-default.ts",
|
|
filesystemWriteViolations(12),
|
|
),
|
|
"keeps maybe undefined object properties after exhaustive branch merges": fsCase`
|
|
function writePath(filePath: string, { path = filePath } = {}) {
|
|
return fs.writeFile(path, "{}\\n");
|
|
}
|
|
const options = { path: "current-state.json" };
|
|
if (Math.random() > 0.5) {
|
|
options.path = undefined;
|
|
} else {
|
|
options.path = "current-state.json";
|
|
}
|
|
await writePath("sessions.json", options);
|
|
`(
|
|
"top-level-helper-object-binding-branch-maybe-undefined-default.ts",
|
|
filesystemWriteViolations(12),
|
|
),
|
|
"keeps known nested object literals after exhaustive branch merges": fsCase`
|
|
function writePath({ paths: { filePath = "sessions.json" } = {} }) {
|
|
return fs.writeFile(filePath, "{}\\n");
|
|
}
|
|
let options = { paths: { filePath: "current-state.json" } };
|
|
if (Math.random() > 0.5) {
|
|
options = { paths: {} };
|
|
} else {
|
|
options = { paths: {} };
|
|
}
|
|
await writePath(options);
|
|
`(
|
|
"top-level-helper-object-binding-branch-known-nested-object.ts",
|
|
filesystemWriteViolations(12),
|
|
),
|
|
"does not force object binding defaults after exhaustive unknown object branch merges":
|
|
fsCase`
|
|
declare function loadOptions(): { path?: string };
|
|
function writePath(filePath: string, { path = filePath } = {}) {
|
|
return fs.writeFile(path, "{}\\n");
|
|
}
|
|
let options;
|
|
if (Math.random() > 0.5) {
|
|
options = { path: "current-state.json" };
|
|
} else {
|
|
options = loadOptions();
|
|
}
|
|
await writePath("sessions.json", options);
|
|
`("top-level-helper-object-binding-branch-unknown-default.ts", []),
|
|
"does not force object binding defaults after optional unknown object rewrites": fsCase`
|
|
declare function loadOptions(): { path?: string };
|
|
function writePath(filePath: string, { path = filePath } = {}) {
|
|
return fs.writeFile(path, "{}\\n");
|
|
}
|
|
let options = loadOptions();
|
|
if (Math.random() > 0.5) {
|
|
options = {};
|
|
}
|
|
await writePath("sessions.json", options);
|
|
`("top-level-helper-object-binding-optional-unknown-rewrite-default.ts", []),
|
|
"keeps known-missing object properties after exhaustive branch merges": fsCase`
|
|
function writePath(filePath: string, { path = filePath } = {}) {
|
|
return fs.writeFile(path, "{}\\n");
|
|
}
|
|
let options;
|
|
if (Math.random() > 0.5) {
|
|
options = { path: "current-state.json" };
|
|
} else {
|
|
options = {};
|
|
}
|
|
await writePath("sessions.json", options);
|
|
`(
|
|
"top-level-helper-object-binding-branch-known-missing-default.ts",
|
|
filesystemWriteViolations(12),
|
|
),
|
|
"flags object binding defaults from earlier destructured arguments": fsCase`
|
|
function writePath({ filePath }: { filePath: string }, { path = filePath } = {}) {
|
|
return fs.writeFile(path, "{}\\n");
|
|
}
|
|
await writePath({ filePath: "sessions.json" }, {});
|
|
`(
|
|
"top-level-helper-object-binding-destructured-earlier-parameter-default.ts",
|
|
filesystemWriteViolations(6),
|
|
),
|
|
"flags object binding defaults from nested destructured arguments": fsCase`
|
|
function writePath(
|
|
{ paths: { filePath } }: { paths: { filePath: string } },
|
|
{ path = filePath } = {},
|
|
) {
|
|
return fs.writeFile(path, "{}\\n");
|
|
}
|
|
await writePath({ paths: { filePath: "sessions.json" } }, {});
|
|
`(
|
|
"top-level-helper-object-binding-nested-destructured-earlier-parameter-default.ts",
|
|
filesystemWriteViolations(9),
|
|
),
|
|
"does not scan unrelated object properties for earlier property defaults": fsCase`
|
|
function writePath(
|
|
options: { currentPath: string; legacyPath: string },
|
|
path = options.currentPath,
|
|
) {
|
|
return fs.writeFile(path, "{}\\n");
|
|
}
|
|
await writePath({
|
|
currentPath: "state/openclaw.sqlite",
|
|
legacyPath: "sessions.json",
|
|
});
|
|
`("top-level-helper-property-default-unrelated-legacy-property.ts", []),
|
|
"does not scan unrelated object properties for bracket defaults": fsCase`
|
|
function writePath(
|
|
options: { currentPath: string; legacyPath: string },
|
|
path = options["currentPath"],
|
|
) {
|
|
return fs.writeFile(path, "{}\\n");
|
|
}
|
|
await writePath({
|
|
currentPath: "state/openclaw.sqlite",
|
|
legacyPath: "sessions.json",
|
|
});
|
|
`("top-level-helper-bracket-default-unrelated-legacy-property.ts", []),
|
|
"flags direct top-level helper calls with nested property defaults": fsCase`
|
|
function writePath(
|
|
options: { paths: { filePath: string } },
|
|
path = options.paths.filePath,
|
|
) {
|
|
return fs.writeFile(path, "{}\\n");
|
|
}
|
|
await writePath({
|
|
paths: { filePath: "sessions.json" },
|
|
});
|
|
`("top-level-helper-nested-property-default.ts", filesystemWriteViolations(9)),
|
|
"flags direct top-level helper calls with nested bracket property defaults": fsCase`
|
|
function writePath(
|
|
options: { paths: { filePath: string } },
|
|
path = options.paths["filePath"],
|
|
) {
|
|
return fs.writeFile(path, "{}\\n");
|
|
}
|
|
await writePath({
|
|
paths: { filePath: "sessions.json" },
|
|
});
|
|
`("top-level-helper-nested-bracket-property-default.ts", filesystemWriteViolations(9)),
|
|
"does not crash on unknown spreads in nested property defaults": fsCase`
|
|
declare const defaults: { paths?: { filePath: string } };
|
|
function writePath(
|
|
options: { paths?: { filePath: string } },
|
|
path = options.paths.filePath,
|
|
) {
|
|
return fs.writeFile(path, "{}\\n");
|
|
}
|
|
await writePath({ ...defaults });
|
|
`("top-level-helper-nested-property-default-unknown-spread.ts", []),
|
|
"keeps nested legacy paths before unknown outer spreads": fsCase`
|
|
declare const options: { paths?: { filePath: string } };
|
|
function persist({ paths: { filePath } }: { paths: { filePath: string } }) {
|
|
return fs.writeFile(filePath, "{}\\n");
|
|
}
|
|
await persist({ paths: { filePath: "sessions.json" }, ...options });
|
|
`("nested-wrapper-path-before-unknown-spread.ts", filesystemWriteViolations(7)),
|
|
"flags nested legacy paths passed through shorthand options": fsCase`
|
|
const paths = { filePath: "sessions.json" };
|
|
function writePath(options: { paths: { filePath: string } }, path = options.paths.filePath) {
|
|
return fs.writeFile(path, "{}\\n");
|
|
}
|
|
await writePath({ paths });
|
|
`("top-level-helper-nested-shorthand-options.ts", filesystemWriteViolations(7)),
|
|
"flags nested legacy paths forwarded through identifier-valued object properties": fsCase`
|
|
function persist({ paths: { filePath } }: { paths: { filePath: string } }) {
|
|
return fs.writeFile(filePath, "{}\\n");
|
|
}
|
|
const paths = { filePath: "sessions.json" };
|
|
const options = { paths };
|
|
await persist(options);
|
|
`("nested-wrapper-identifier-valued-object-property.ts", filesystemWriteViolations(8)),
|
|
"flags nested legacy paths hidden in intermediate option expressions": fsCase`
|
|
declare function makePaths(filePath: string): { filePath: string };
|
|
function persist({ paths: { filePath } }: { paths: { filePath: string } }) {
|
|
return fs.writeFile(filePath, "{}\\n");
|
|
}
|
|
await persist({ paths: makePaths("sessions.json") });
|
|
`("nested-wrapper-path-intermediate-expression.ts", filesystemWriteViolations(7)),
|
|
"flags direct top-level helper calls with chained literal parameter defaults": fsCase`
|
|
function writePath(filePath = "sessions.json", path = filePath) {
|
|
return fs.writeFile(path, "{}\\n");
|
|
}
|
|
await writePath();
|
|
`("top-level-helper-chained-literal-parameter-default.ts", filesystemWriteViolations(6)),
|
|
"flags direct top-level helper calls with nested object literal parameter defaults": fsCase`
|
|
function writePath(
|
|
options = { paths: { filePath: "sessions.json" } },
|
|
path = options.paths.filePath,
|
|
) {
|
|
return fs.writeFile(path, "{}\\n");
|
|
}
|
|
await writePath();
|
|
`(
|
|
"top-level-helper-nested-object-literal-parameter-default.ts",
|
|
filesystemWriteViolations(9),
|
|
),
|
|
"flags direct top-level helper calls with nested spread parameter defaults": fsCase`
|
|
const defaults = { paths: { filePath: "sessions.json" } };
|
|
function writePath(
|
|
options = { ...defaults },
|
|
path = options.paths.filePath,
|
|
) {
|
|
return fs.writeFile(path, "{}\\n");
|
|
}
|
|
await writePath();
|
|
`("top-level-helper-nested-spread-parameter-default.ts", filesystemWriteViolations(10)),
|
|
"does not resolve top-level helper nested spread defaults in the caller scope": fsCase`
|
|
const defaults = { paths: { filePath: "current-state.json" } };
|
|
function writePath(
|
|
options = { ...defaults },
|
|
path = options.paths.filePath,
|
|
) {
|
|
return fs.writeFile(path, "{}\\n");
|
|
}
|
|
function persist() {
|
|
const defaults = { paths: { filePath: "sessions.json" } };
|
|
return writePath();
|
|
}
|
|
await persist();
|
|
`("top-level-helper-nested-spread-default-caller-shadow.ts", []),
|
|
"does not resolve omitted earlier helper parameters in the caller scope": fsCase`
|
|
function writePath(filePath?: string, path = filePath) {
|
|
return fs.writeFile(path, "{}\\n");
|
|
}
|
|
function persist(filePath: string) {
|
|
return writePath();
|
|
}
|
|
await persist("sessions.json");
|
|
`("top-level-helper-omitted-earlier-default.ts", []),
|
|
"does not resolve omitted earlier helper parameters in object binding defaults": fsCase`
|
|
function writePath(filePath?: string, { path = filePath } = {}) {
|
|
return fs.writeFile(path, "{}\\n");
|
|
}
|
|
function persist(filePath: string) {
|
|
return writePath(undefined, {});
|
|
}
|
|
await persist("sessions.json");
|
|
`("top-level-helper-object-binding-omitted-earlier-default.ts", []),
|
|
"flags default expressions that combine multiple earlier parameters": fsCase`
|
|
function writePath(prefix: string, filePath: string, path = prefix + filePath) {
|
|
return fs.writeFile(path, "{}\\n");
|
|
}
|
|
function persist(filePath: string) {
|
|
return writePath("state/", filePath);
|
|
}
|
|
await persist("sessions.json");
|
|
`("top-level-helper-multiple-earlier-parameter-default.ts", filesystemWriteViolations(9)),
|
|
"does not resolve top-level helper defaults in closed-over caller scope": fsCase`
|
|
const defaultPath = "current-state.json";
|
|
function writePath(path = defaultPath) {
|
|
return fs.writeFile(path, "{}\\n");
|
|
}
|
|
function persist(defaultPath: string) {
|
|
function inner() {
|
|
return writePath();
|
|
}
|
|
return inner();
|
|
}
|
|
await persist("sessions.json");
|
|
`("top-level-helper-default-closed-over-caller-shadow.ts", []),
|
|
"does not treat top-level helper aliases as closed over nested helpers": fsCase`
|
|
const filePath = "current-state.json";
|
|
function writeCurrent() {
|
|
return fs.writeFile(filePath, "{}\\n");
|
|
}
|
|
function persist(filePath: string) {
|
|
function inner() {
|
|
const save = writeCurrent;
|
|
return save();
|
|
}
|
|
return inner();
|
|
}
|
|
await persist("sessions.json");
|
|
`("top-level-helper-alias-module-path-shadow.ts", []),
|
|
"does not resolve aliased top-level helper defaults in the caller scope": fsCase`
|
|
const defaultPath = "current-state.json";
|
|
function writePath(path = defaultPath) {
|
|
return fs.writeFile(path, "{}\\n");
|
|
}
|
|
function persist(defaultPath: string) {
|
|
const save = writePath;
|
|
return save();
|
|
}
|
|
await persist("sessions.json");
|
|
`("top-level-helper-alias-default-caller-shadow.ts", []),
|
|
|
|
// Nested helper capture and wrapper propagation.
|
|
"flags legacy paths forwarded through nested helper object binding defaults": fsPersistCase`
|
|
function inner({ path = filePath } = {}) {
|
|
return fs.writeFile(path, "{}\\n");
|
|
}
|
|
return inner();
|
|
`("nested-helper-object-binding-default.ts", filesystemWriteViolations(9)),
|
|
"does not use nested helper object binding defaults when a spread may provide the property":
|
|
fsPersistCase`
|
|
const options = getOptions();
|
|
function inner({ path = filePath } = {}) {
|
|
return fs.writeFile(path, "{}\\n");
|
|
}
|
|
return inner({ ...options });
|
|
`("nested-helper-object-binding-default-unknown-spread.ts", []),
|
|
"flags nested helper object binding defaults after known-empty object spreads": fsPersistCase`
|
|
const defaults = {};
|
|
function inner({ path = filePath } = {}) {
|
|
return fs.writeFile(path, "{}\\n");
|
|
}
|
|
return inner({ ...defaults, ...{} });
|
|
`(
|
|
"nested-helper-object-binding-default-known-empty-spread.ts",
|
|
filesystemWriteViolations(10),
|
|
),
|
|
"resolves nested helper object binding defaults in the helper scope": fsPersistCase`
|
|
function inner(filePath: string, { path = filePath } = {}) {
|
|
return fs.writeFile(path, "{}\\n");
|
|
}
|
|
return inner(currentSqlitePath, {});
|
|
`("nested-helper-object-binding-default-shadow.ts", []),
|
|
"flags legacy paths forwarded through undefined nested helper object arguments":
|
|
fsPersistCase`
|
|
function inner({ path = filePath } = {}) {
|
|
return fs.writeFile(path, "{}\\n");
|
|
}
|
|
return inner(undefined);
|
|
`("nested-helper-undefined-object-binding-default.ts", filesystemWriteViolations(9)),
|
|
"flags legacy paths forwarded through explicit undefined nested helper object properties":
|
|
fsPersistCase`
|
|
function inner({ path = filePath } = {}) {
|
|
return fs.writeFile(path, "{}\\n");
|
|
}
|
|
return inner({ path: undefined });
|
|
`("nested-helper-undefined-object-property-default.ts", filesystemWriteViolations(9)),
|
|
"flags legacy paths captured by nested helpers with local fs aliases": sourceCase`
|
|
function persist(filePath: string) {
|
|
function inner() {
|
|
const fs = require("node:fs");
|
|
return fs.writeFileSync(filePath, "{}\\n");
|
|
}
|
|
return inner();
|
|
}
|
|
persist("sessions.json");
|
|
`("nested-wrapper-closed-over-local-fs.ts", filesystemWriteViolations(9)),
|
|
"does not treat named function expression self-bindings as captured write aliases":
|
|
fsPersistCase`
|
|
const writeFile = fs.writeFile;
|
|
const inner = function writeFile() {
|
|
return writeFile(filePath);
|
|
};
|
|
return inner();
|
|
`("named-function-expression-write-alias-shadow.ts", []),
|
|
"flags legacy paths captured by defaulted destructured nested helpers": fsPersistCase`
|
|
const writer = {};
|
|
function inner() {
|
|
const { save = () => fs.writeFile(filePath, "{}\\n") } = writer;
|
|
return save();
|
|
}
|
|
return inner();
|
|
`("defaulted-destructured-nested-helper.ts", filesystemWriteViolations(11)),
|
|
"does not use nested helper destructuring defaults when safe callbacks are present": fsCase`
|
|
const noopParam = async (_path: string) => {};
|
|
function persist(filePath: string) {
|
|
function inner() {
|
|
const writer = { save: noopParam };
|
|
const { save = () => fs.writeFile(filePath, "{}\\n") } = writer;
|
|
return save(filePath);
|
|
}
|
|
return inner();
|
|
}
|
|
await persist("sessions.json");
|
|
`("present-safe-callback-nested-default.ts", []),
|
|
"uses nested helper destructuring defaults when properties are explicitly undefined":
|
|
fsPersistCase`
|
|
function inner() {
|
|
const writer = { save: undefined };
|
|
const { save = () => fs.writeFile(filePath, "{}\\n") } = writer;
|
|
return save();
|
|
}
|
|
return inner();
|
|
`("undefined-callback-nested-default.ts", filesystemWriteViolations(11)),
|
|
"does not resolve outer object methods through local object shadows": fsPersistCase`
|
|
const writer = {
|
|
save() {
|
|
return fs.writeFile(filePath, "{}\\n");
|
|
},
|
|
};
|
|
function inner() {
|
|
const writer = getWriter();
|
|
const { save } = writer;
|
|
return save();
|
|
}
|
|
return inner();
|
|
`("local-object-shadow-nested-method.ts", []),
|
|
"keeps branch-only object methods inside closed-over nested helpers": fsCase`
|
|
function persist(filePath: string, enabled: boolean) {
|
|
function inner() {
|
|
let writer = {};
|
|
if (enabled) {
|
|
writer = {
|
|
save() {
|
|
return fs.writeFile(filePath, "{}\\n");
|
|
},
|
|
};
|
|
} else {
|
|
writer = {};
|
|
}
|
|
const { save } = writer;
|
|
return save();
|
|
}
|
|
return inner();
|
|
}
|
|
await persist("sessions.json", true);
|
|
`("branch-only-closed-over-object-method.ts", filesystemWriteViolations(20)),
|
|
"keeps branch-only property assigned methods inside closed-over nested helpers": fsCase`
|
|
function persist(filePath: string, enabled: boolean) {
|
|
function inner() {
|
|
let writer = {};
|
|
if (enabled) {
|
|
writer.save = () => fs.writeFile(filePath, "{}\\n");
|
|
} else {
|
|
writer = {};
|
|
}
|
|
return writer.save?.();
|
|
}
|
|
return inner();
|
|
}
|
|
await persist("sessions.json", true);
|
|
`("branch-only-property-closed-over-object-method.ts", filesystemWriteViolations(15)),
|
|
"flags legacy paths captured by nested helpers with branch-assigned write aliases": fsCase`
|
|
function persist(filePath: string, json: boolean) {
|
|
function inner() {
|
|
let write: typeof fs.writeFile;
|
|
if (json) {
|
|
write = fs.writeFile;
|
|
} else {
|
|
write = fs.writeFile;
|
|
}
|
|
return write(filePath, "{}\\n");
|
|
}
|
|
return inner();
|
|
}
|
|
await persist("sessions.json", true);
|
|
`("nested-wrapper-branch-assigned-write-alias.ts", filesystemWriteViolations(15)),
|
|
"flags legacy paths captured by conditionally assigned nested helpers": fsCase`
|
|
function persist(filePath: string, json: boolean) {
|
|
function inner() {
|
|
let save;
|
|
if (json) {
|
|
save = () => fs.writeFile(filePath, "{}\\n");
|
|
} else {
|
|
save = () => fs.writeFile(filePath, "{}\\n");
|
|
}
|
|
return save();
|
|
}
|
|
return inner();
|
|
}
|
|
await persist("sessions.json", true);
|
|
`("conditionally-assigned-nested-helper.ts", filesystemWriteViolations(15)),
|
|
"keeps legacy nested helpers after braceless optional reassignment": fsCase`
|
|
function persist(filePath: string, disabled: boolean) {
|
|
function inner() {
|
|
let save = () => fs.writeFile(filePath, "{}\\n");
|
|
if (disabled) save = async () => {};
|
|
return save();
|
|
}
|
|
return inner();
|
|
}
|
|
await persist("sessions.json", false);
|
|
`("nested-wrapper-braceless-optional-reassignment.ts", filesystemWriteViolations(11)),
|
|
"flags legacy paths captured by nested helpers with destructured fs aliases": fsPersistCase`
|
|
function inner() {
|
|
const { writeFile } = fs;
|
|
return writeFile(filePath, "{}\\n");
|
|
}
|
|
return inner();
|
|
`("nested-wrapper-closed-over-destructured-fs.ts", filesystemWriteViolations(10)),
|
|
"ignores nested helpers with shadowed local require aliases": sourceCase`
|
|
function persist(filePath: string, customRequire: NodeRequire) {
|
|
function inner() {
|
|
const require = customRequire;
|
|
const fs = require("node:fs");
|
|
return fs.writeFileSync(filePath, "{}\\n");
|
|
}
|
|
return inner();
|
|
}
|
|
persist("sessions.json", customRequire);
|
|
`("nested-wrapper-shadowed-require.ts", []),
|
|
"uses nested helper createRequire shadows from the helper definition": requireCase`
|
|
function persist(filePath: string, customCreateRequire: typeof createRequire) {
|
|
function inner() {
|
|
const req = createRequire(import.meta.url);
|
|
const fs = req("node:fs");
|
|
return fs.writeFileSync(filePath, "{}\\n");
|
|
}
|
|
{
|
|
const createRequire = customCreateRequire;
|
|
return inner();
|
|
}
|
|
}
|
|
persist("sessions.json", customCreateRequire);
|
|
`("nested-wrapper-create-require-definition-scope.ts", filesystemWriteViolations(14)),
|
|
"does not treat named nested function expressions as closed-over path parameters":
|
|
fsPersistCase`
|
|
const inner = function filePath() {
|
|
return fs.writeFile(filePath, "{}\\n");
|
|
};
|
|
return inner();
|
|
`("nested-wrapper-named-function-expression.ts", []),
|
|
"does not resolve locally shadowed nested helper calls to outer wrappers": fsCase`
|
|
function helper(filePath: string) {
|
|
return fs.writeFile(filePath, "{}\\n");
|
|
}
|
|
function persist(filePath: string) {
|
|
function inner(helper: (path: string) => Promise<void>) {
|
|
return helper(filePath);
|
|
}
|
|
return inner(async () => {});
|
|
}
|
|
await persist("sessions.json");
|
|
`("nested-wrapper-shadowed-helper-call.ts", []),
|
|
"flags legacy paths captured by branch-assigned nested helpers": fsCase`
|
|
function persist(filePath: string, useJson: boolean) {
|
|
let inner;
|
|
if (useJson) {
|
|
inner = () => fs.writeFile(filePath, "{}\\n");
|
|
}
|
|
return inner();
|
|
}
|
|
await persist("sessions.json", true);
|
|
`("nested-wrapper-branch-assigned-closed-over-path.ts", filesystemWriteViolations(10)),
|
|
"flags legacy paths captured through nested helper chains": fsPersistCase`
|
|
function inner() {
|
|
function deeper() {
|
|
return fs.writeFile(filePath, "{}\\n");
|
|
}
|
|
return deeper();
|
|
}
|
|
return inner();
|
|
`("nested-wrapper-helper-chain-closed-over-path.ts", filesystemWriteViolations(12)),
|
|
"flags legacy paths captured through hoisted nested helper chains": fsPersistCase`
|
|
function inner() {
|
|
return deeper();
|
|
function deeper() {
|
|
return fs.writeFile(filePath, "{}\\n");
|
|
}
|
|
}
|
|
return inner();
|
|
`("nested-wrapper-hoisted-helper-chain-closed-over-path.ts", filesystemWriteViolations(12)),
|
|
"flags hoisted nested helpers that use write aliases declared later": fsPersistCase`
|
|
function inner() {
|
|
function deeper() {
|
|
return write(filePath, "{}\\n");
|
|
}
|
|
const write = fs.writeFile;
|
|
return deeper();
|
|
}
|
|
return inner();
|
|
`("nested-wrapper-hoisted-helper-late-alias.ts", filesystemWriteViolations(13)),
|
|
"flags escaped nested helpers that use write aliases declared later": fsPersistCase`
|
|
let save;
|
|
function configure() {
|
|
save = () => write(filePath, "{}\\n");
|
|
const write = fs.writeFile;
|
|
}
|
|
configure();
|
|
return save?.();
|
|
`("nested-wrapper-escaped-late-write-alias.ts", filesystemWriteViolations(12)),
|
|
"flags block-escaped nested helpers that use block write aliases declared later":
|
|
fsPersistCase`
|
|
function inner() {
|
|
let save;
|
|
{
|
|
save = () => write(filePath, "{}\\n");
|
|
const write = fs.writeFile;
|
|
}
|
|
return save?.();
|
|
}
|
|
return inner();
|
|
`("nested-wrapper-block-escaped-late-write-alias.ts", filesystemWriteViolations(14)),
|
|
"flags var nested helpers declared in blocks": fsPersistCase`
|
|
function inner() {
|
|
{
|
|
var save = () => fs.writeFile(filePath, "{}\\n");
|
|
}
|
|
return save();
|
|
}
|
|
return inner();
|
|
`("nested-wrapper-var-block-helper.ts", filesystemWriteViolations(12)),
|
|
"flags var nested helper object methods declared in blocks": fsPersistCase`
|
|
function inner() {
|
|
{
|
|
var writer = {
|
|
save() {
|
|
return fs.writeFile(filePath, "{}\\n");
|
|
},
|
|
};
|
|
}
|
|
return writer.save();
|
|
}
|
|
return inner();
|
|
`("nested-wrapper-var-block-helper-object-method.ts", filesystemWriteViolations(16)),
|
|
"flags nested helper defaults from object literal destructuring": fsPersistCase`
|
|
const { save = () => fs.writeFile(filePath, "{}\\n") } = {};
|
|
return save();
|
|
`("nested-wrapper-object-literal-destructuring-default.ts", filesystemWriteViolations(7)),
|
|
"uses the last object literal property before nested helper destructuring defaults":
|
|
fsPersistCase`
|
|
const safe = () => undefined;
|
|
const { save = () => fs.writeFile(filePath, "{}\\n") } = {
|
|
save: safe,
|
|
save: undefined,
|
|
};
|
|
return save?.();
|
|
`("nested-wrapper-object-literal-duplicate-default.ts", filesystemWriteViolations(11)),
|
|
"does not use nested helper destructuring defaults when the last duplicate is safe":
|
|
fsPersistCase`
|
|
const safe = () => undefined;
|
|
const { save = () => fs.writeFile(filePath, "{}\\n") } = {
|
|
save: undefined,
|
|
save: safe,
|
|
};
|
|
return save?.();
|
|
`("nested-wrapper-object-literal-duplicate-safe.ts", []),
|
|
"does not use nested helper destructuring defaults when a spread may provide the property":
|
|
fsPersistCase`
|
|
const safe = async () => {};
|
|
const { save = () => fs.writeFile(filePath, "{}\\n") } = {
|
|
...{ save: safe },
|
|
};
|
|
return save();
|
|
`("nested-wrapper-object-literal-spread-safe.ts", []),
|
|
"does not use nested helper destructuring defaults for untracked identifier spreads":
|
|
fsPersistCase`
|
|
const defaults = getWriter();
|
|
const { save = () => fs.writeFile(filePath, "{}\\n") } = {
|
|
...defaults,
|
|
};
|
|
return save?.();
|
|
`("nested-wrapper-object-literal-unknown-spread-default.ts", []),
|
|
"keeps earlier wrapper properties through known-missing object spreads": fsPersistCase`
|
|
const save = () => fs.writeFile(filePath, "{}\\n");
|
|
const defaults = {};
|
|
const { save: inner = async () => {} } = {
|
|
save,
|
|
...defaults,
|
|
};
|
|
return inner();
|
|
`("nested-wrapper-object-literal-known-missing-spread.ts", filesystemWriteViolations(12)),
|
|
"uses nested helper destructuring defaults after known undefined object spreads":
|
|
fsPersistCase`
|
|
const defaults = { save: undefined };
|
|
const { save = () => fs.writeFile(filePath, "{}\\n") } = {
|
|
...defaults,
|
|
};
|
|
return save();
|
|
`("nested-wrapper-object-literal-undefined-spread-default.ts", filesystemWriteViolations(10)),
|
|
"flags var nested wrappers declared in blocks": fsPersistCase`
|
|
{
|
|
var inner = (path: string) => fs.writeFile(path, "{}\\n");
|
|
}
|
|
return inner(filePath);
|
|
`("nested-wrapper-var-block-declaration.ts", filesystemWriteViolations(9)),
|
|
"merges var nested wrapper declarations inside exhaustive branches": fsCase`
|
|
function persist(filePath: string, enabled: boolean) {
|
|
if (enabled) {
|
|
var inner = (path: string) => fs.writeFile(path, "{}\\n");
|
|
} else {
|
|
var inner = async (_path: string) => {};
|
|
}
|
|
return inner(filePath);
|
|
}
|
|
await persist("sessions.json", true);
|
|
`("nested-wrapper-var-branch-declaration.ts", filesystemWriteViolations(11)),
|
|
"keeps prior var nested wrapper declarations after optional branch redeclarations": fsCase`
|
|
function persist(filePath: string, disabled: boolean) {
|
|
var inner = (path: string) => fs.writeFile(path, "{}\\n");
|
|
if (disabled) {
|
|
var inner = async (_path: string) => {};
|
|
}
|
|
return inner(filePath);
|
|
}
|
|
await persist("sessions.json", false);
|
|
`("nested-wrapper-var-optional-branch-declaration.ts", filesystemWriteViolations(10)),
|
|
"flags var nested wrapper destructuring defaults declared in blocks": fsPersistCase`
|
|
{
|
|
var { save = (path: string) => fs.writeFile(path, "{}\\n") } = {};
|
|
}
|
|
return save(filePath);
|
|
`("nested-wrapper-var-block-destructuring-default.ts", filesystemWriteViolations(9)),
|
|
"flags legacy paths captured through sibling nested helper calls": fsPersistCase`
|
|
function save() {
|
|
return fs.writeFile(filePath, "{}\\n");
|
|
}
|
|
function inner() {
|
|
return save();
|
|
}
|
|
return inner();
|
|
`("nested-wrapper-sibling-helper-call-closed-over-path.ts", filesystemWriteViolations(12)),
|
|
"flags legacy paths forwarded through sibling nested helper parameters": fsPersistCase`
|
|
function inner(nextPath: string) {
|
|
return deeper(nextPath);
|
|
}
|
|
function deeper(nextPath: string) {
|
|
return fs.writeFile(nextPath, "{}\\n");
|
|
}
|
|
return inner(filePath);
|
|
`("nested-wrapper-sibling-helper-forwarded-path.ts", filesystemWriteViolations(12)),
|
|
"flags legacy paths captured through nested arrow helper chains": fsPersistCase`
|
|
const inner = () => {
|
|
const deeper = () => fs.writeFile(filePath, "{}\\n");
|
|
return deeper();
|
|
};
|
|
return inner();
|
|
`("nested-wrapper-arrow-helper-chain-closed-over-path.ts", filesystemWriteViolations(10)),
|
|
"flags legacy paths captured through nested helper aliases": fsPersistCase`
|
|
function inner() {
|
|
const deeper = () => fs.writeFile(filePath, "{}\\n");
|
|
const save = deeper;
|
|
return save();
|
|
}
|
|
return inner();
|
|
`("nested-wrapper-helper-alias-closed-over-path.ts", filesystemWriteViolations(11)),
|
|
"flags legacy paths captured through nested object helper methods": fsPersistCase`
|
|
function inner() {
|
|
const writer = {
|
|
save() {
|
|
return fs.writeFile(filePath, "{}\\n");
|
|
},
|
|
};
|
|
return writer.save();
|
|
}
|
|
return inner();
|
|
`("nested-wrapper-object-helper-closed-over-path.ts", filesystemWriteViolations(14)),
|
|
"flags legacy paths captured through nested object helper aliases": fsPersistCase`
|
|
function inner() {
|
|
const save = () => fs.writeFile(filePath, "{}\\n");
|
|
const writer = { save };
|
|
return writer.save();
|
|
}
|
|
return inner();
|
|
`("nested-wrapper-object-helper-alias-closed-over-path.ts", filesystemWriteViolations(11)),
|
|
"does not treat nested function declaration shadows as captured write aliases": fsPersistCase`
|
|
const write = fs.writeFile;
|
|
function inner() {
|
|
function write() {}
|
|
return write(filePath);
|
|
}
|
|
return inner();
|
|
`("nested-wrapper-function-declaration-write-shadow.ts", []),
|
|
"does not treat nested helper parameters as captured write aliases": fsCase`
|
|
function persist(filePath: string, customWrite: (value: string) => void) {
|
|
const writeFile = fs.writeFile;
|
|
function inner(writeFile: (value: string) => void) {
|
|
return writeFile(filePath);
|
|
}
|
|
return inner(customWrite);
|
|
}
|
|
await persist("sessions.json", customWrite);
|
|
`("nested-wrapper-parameter-write-alias-shadow.ts", []),
|
|
"flags legacy paths forwarded through nested arrow helpers": fsPersistCase`
|
|
const inner = (nextPath: string) => fs.writeFile(nextPath, "{}\\n");
|
|
return inner(filePath);
|
|
`("nested-arrow-wrapper-path.ts", filesystemWriteViolations(7)),
|
|
"flags legacy paths forwarded through nested object helper methods": fsPersistCase`
|
|
const writer = {
|
|
inner(nextPath: string) {
|
|
return fs.writeFile(nextPath, "{}\\n");
|
|
},
|
|
};
|
|
return writer.inner(filePath);
|
|
`("nested-object-wrapper-path.ts", filesystemWriteViolations(11)),
|
|
"flags legacy paths forwarded through nested helper aliases": fsPersistCase`
|
|
function inner(nextPath: string) {
|
|
return fs.writeFile(nextPath, "{}\\n");
|
|
}
|
|
const save = inner;
|
|
return save(filePath);
|
|
`("nested-wrapper-alias-path.ts", filesystemWriteViolations(10)),
|
|
"flags legacy paths forwarded through assignment-defined nested helper aliases":
|
|
fsPersistCase`
|
|
function inner() {
|
|
let save: () => Promise<void>;
|
|
save = () => fs.writeFile(filePath, "{}\\n");
|
|
return save();
|
|
}
|
|
return inner();
|
|
`("nested-wrapper-assigned-alias-closed-over-path.ts", filesystemWriteViolations(11)),
|
|
"flags enclosing helper assignments made inside nested helpers": fsPersistCase`
|
|
let save;
|
|
function configure() {
|
|
save = () => fs.writeFile(filePath, "{}\\n");
|
|
}
|
|
configure();
|
|
return save?.();
|
|
`("nested-wrapper-enclosing-assigned-helper.ts", filesystemWriteViolations(11)),
|
|
"flags legacy paths forwarded through extracted nested object helper methods": fsPersistCase`
|
|
const writer = {
|
|
inner(nextPath: string) {
|
|
return fs.writeFile(nextPath, "{}\\n");
|
|
},
|
|
};
|
|
const save = writer.inner;
|
|
return save(filePath);
|
|
`("nested-object-wrapper-alias-path.ts", filesystemWriteViolations(12)),
|
|
"flags legacy paths forwarded through assignment-defined nested object helpers":
|
|
fsPersistCase`
|
|
function inner() {
|
|
const writer: { save?: () => Promise<void> } = {};
|
|
writer.save = () => fs.writeFile(filePath, "{}\\n");
|
|
return writer.save?.();
|
|
}
|
|
return inner();
|
|
`("nested-object-wrapper-assigned-alias-closed-over-path.ts", filesystemWriteViolations(11)),
|
|
"merges closed-over var nested wrapper declarations inside exhaustive branches": fsCase`
|
|
function persist(filePath: string, enabled: boolean) {
|
|
function inner() {
|
|
if (enabled) {
|
|
var save = () => fs.writeFile(filePath, "{}\\n");
|
|
} else {
|
|
var save = async () => {};
|
|
}
|
|
return save();
|
|
}
|
|
return inner();
|
|
}
|
|
await persist("sessions.json", true);
|
|
`("nested-wrapper-var-branch-declaration-closed-over-path.ts", filesystemWriteViolations(14)),
|
|
"merges closed-over var fs aliases declared inside optional branches": fsCase`
|
|
function persist(filePath: string, enabled: boolean) {
|
|
function inner() {
|
|
if (enabled) {
|
|
var write = fs.writeFile;
|
|
}
|
|
return write(filePath, "{}\\n");
|
|
}
|
|
return inner();
|
|
}
|
|
await persist("sessions.json", true);
|
|
`("nested-wrapper-var-fs-alias-branch-closed-over-path.ts", filesystemWriteViolations(12)),
|
|
"flags enclosing object helper assignments made inside nested helpers": fsPersistCase`
|
|
const writer: { save?: () => Promise<void> } = {};
|
|
function configure() {
|
|
writer.save = () => fs.writeFile(filePath, "{}\\n");
|
|
}
|
|
function inner() {
|
|
configure();
|
|
return writer.save?.();
|
|
}
|
|
return inner();
|
|
`("nested-object-wrapper-enclosing-assigned-helper.ts", filesystemWriteViolations(14)),
|
|
"flags legacy paths forwarded through local nested object helper aliases": fsPersistCase`
|
|
function inner() {
|
|
const writer = {
|
|
save() {
|
|
return fs.writeFile(filePath, "{}\\n");
|
|
},
|
|
};
|
|
const save = writer.save;
|
|
return save();
|
|
}
|
|
return inner();
|
|
`(
|
|
"nested-object-wrapper-local-method-alias-closed-over-path.ts",
|
|
filesystemWriteViolations(15),
|
|
),
|
|
"flags closed-over nested object helper methods copied through destructuring aliases":
|
|
fsPersistCase`
|
|
function inner() {
|
|
const writer = {
|
|
nested: {
|
|
save() {
|
|
return fs.writeFile(filePath, "{}\\n");
|
|
},
|
|
},
|
|
};
|
|
const { nested } = writer;
|
|
return nested.save();
|
|
}
|
|
return inner();
|
|
`(
|
|
"nested-object-wrapper-destructured-alias-closed-over-path.ts",
|
|
filesystemWriteViolations(17),
|
|
),
|
|
"clears closed-over nested object helpers after exhaustive reassignment": fsCase`
|
|
function persist(filePath: string, enabled: boolean) {
|
|
function inner() {
|
|
let writer = {
|
|
save() {
|
|
return fs.writeFile(filePath, "{}\\n");
|
|
},
|
|
};
|
|
if (enabled) {
|
|
writer = {};
|
|
} else {
|
|
writer = {};
|
|
}
|
|
return writer.save?.();
|
|
}
|
|
return inner();
|
|
}
|
|
await persist("sessions.json", true);
|
|
`("nested-object-wrapper-exhaustive-reassigned-safe.ts", []),
|
|
"keeps closed-over nested helpers after optional branch assignment": fsCase`
|
|
function persist(filePath: string, enabled: boolean) {
|
|
function inner() {
|
|
let save;
|
|
if (enabled) {
|
|
save = () => fs.writeFile(filePath, "{}\\n");
|
|
}
|
|
return save?.();
|
|
}
|
|
return inner();
|
|
}
|
|
await persist("sessions.json", true);
|
|
`("nested-wrapper-optional-branch-assigned-helper.ts", filesystemWriteViolations(13)),
|
|
"keeps closed-over nested helpers after loop assignment": fsCase`
|
|
function persist(filePath: string, values: string[]) {
|
|
function inner() {
|
|
let save;
|
|
for (const value of values) {
|
|
save = () => fs.writeFile(filePath, value);
|
|
}
|
|
return save?.();
|
|
}
|
|
return inner();
|
|
}
|
|
await persist("sessions.json", ["{}\\n"]);
|
|
`("nested-wrapper-loop-assigned-helper.ts", filesystemWriteViolations(13)),
|
|
"keeps closed-over nested helpers after optional while reassignment": fsCase`
|
|
function persist(filePath: string, disabled: boolean) {
|
|
function inner() {
|
|
let save = () => fs.writeFile(filePath, "{}\\n");
|
|
while (disabled) {
|
|
save = async () => {};
|
|
}
|
|
return save();
|
|
}
|
|
return inner();
|
|
}
|
|
await persist("sessions.json", false);
|
|
`("nested-wrapper-while-reassigned-helper.ts", filesystemWriteViolations(13)),
|
|
"keeps switch case closed-over nested helper shadows scoped": fsCase`
|
|
function persist(filePath: string, mode: string) {
|
|
function inner() {
|
|
const save = () => fs.writeFile(filePath, "{}\\n");
|
|
switch (mode) {
|
|
case "off":
|
|
const save = async () => {};
|
|
break;
|
|
}
|
|
return save();
|
|
}
|
|
return inner();
|
|
}
|
|
await persist("sessions.json", "on");
|
|
`("nested-wrapper-switch-case-shadow.ts", filesystemWriteViolations(15)),
|
|
"flags nested helper declarations inside switch cases": fsCase`
|
|
function persist(filePath: string, mode: string) {
|
|
switch (mode) {
|
|
case "legacy":
|
|
function inner(nextPath: string) {
|
|
return fs.writeFile(nextPath, "{}\\n");
|
|
}
|
|
return inner(filePath);
|
|
}
|
|
}
|
|
await persist("sessions.json", "legacy");
|
|
`("nested-wrapper-switch-case-helper.ts", filesystemWriteViolations(12)),
|
|
"keeps closed-over nested helpers after try assignment": fsPersistCase`
|
|
function inner() {
|
|
let save;
|
|
try {
|
|
save = () => fs.writeFile(filePath, "{}\\n");
|
|
} catch {}
|
|
return save?.();
|
|
}
|
|
return inner();
|
|
`("nested-wrapper-try-assigned-helper.ts", filesystemWriteViolations(13)),
|
|
"flags closed-over nested helper aliases to outer wrappers": fsCase`
|
|
function writePath(path: string) {
|
|
return fs.writeFile(path, "{}\\n");
|
|
}
|
|
function persist(filePath: string) {
|
|
function inner() {
|
|
const save = writePath;
|
|
return save(filePath);
|
|
}
|
|
return inner();
|
|
}
|
|
await persist("sessions.json");
|
|
`("nested-wrapper-outer-wrapper-alias.ts", filesystemWriteViolations(13)),
|
|
"flags closed-over nested helper parameter default writes": fsPersistCase`
|
|
function inner(_ = fs.writeFile(filePath, "{}\\n")) {
|
|
return _;
|
|
}
|
|
return inner();
|
|
`("nested-wrapper-parameter-default-write.ts", filesystemWriteViolations(9)),
|
|
"does not use outer write aliases shadowed later in closed-over helpers": fsPersistCase`
|
|
const write = fs.writeFile;
|
|
function inner() {
|
|
write(filePath, "{}\\n");
|
|
const write = async () => {};
|
|
return write;
|
|
}
|
|
return inner();
|
|
`("nested-wrapper-later-write-alias-shadow.ts", []),
|
|
"flags closed-over aliases to top-level wrappers with local metadata": fsCase`
|
|
function writePath(path: string) {
|
|
let writer = {};
|
|
writer.save = () => fs.writeFile(path, "{}\\n");
|
|
return writer.save();
|
|
}
|
|
function persist(filePath: string) {
|
|
function inner() {
|
|
const save = writePath;
|
|
return save(filePath);
|
|
}
|
|
return inner();
|
|
}
|
|
await persist("sessions.json");
|
|
`("nested-wrapper-top-level-wrapper-alias.ts", filesystemWriteViolations(15)),
|
|
"flags closed-over aliases to top-level wrappers with module object metadata": fsCase`
|
|
const writer = {};
|
|
function writePath(path: string) {
|
|
writer.save = () => fs.writeFile(path, "{}\\n");
|
|
return writer.save();
|
|
}
|
|
function persist(filePath: string) {
|
|
function inner() {
|
|
const save = writePath;
|
|
return save(filePath);
|
|
}
|
|
return inner();
|
|
}
|
|
await persist("sessions.json");
|
|
`("nested-wrapper-top-level-object-wrapper-alias.ts", filesystemWriteViolations(15)),
|
|
"flags legacy paths forwarded through destructured local nested object helpers":
|
|
fsPersistCase`
|
|
function inner() {
|
|
const writer = {
|
|
save() {
|
|
return fs.writeFile(filePath, "{}\\n");
|
|
},
|
|
};
|
|
const { save } = writer;
|
|
return save();
|
|
}
|
|
return inner();
|
|
`(
|
|
"nested-object-wrapper-local-method-destructure-closed-over-path.ts",
|
|
filesystemWriteViolations(15),
|
|
),
|
|
"flags legacy paths forwarded through destructured nested object helper methods":
|
|
fsPersistCase`
|
|
const writer = {
|
|
inner(nextPath: string) {
|
|
return fs.writeFile(nextPath, "{}\\n");
|
|
},
|
|
};
|
|
const { inner } = writer;
|
|
return inner(filePath);
|
|
`("nested-object-wrapper-destructure-path.ts", filesystemWriteViolations(12)),
|
|
"flags legacy paths forwarded through renamed nested object helper methods": fsPersistCase`
|
|
const writer = {
|
|
inner(nextPath: string) {
|
|
return fs.writeFile(nextPath, "{}\\n");
|
|
},
|
|
};
|
|
const { inner: save } = writer;
|
|
return save(filePath);
|
|
`("nested-object-wrapper-renamed-destructure-path.ts", filesystemWriteViolations(12)),
|
|
"keeps nested wrapper assignments inside optional branches": fsCase`
|
|
function persist(filePath: string, useJson: boolean) {
|
|
let inner: (nextPath: string) => Promise<void>;
|
|
if (useJson) {
|
|
inner = (nextPath: string) => fs.writeFile(nextPath, "{}\\n");
|
|
}
|
|
return inner(filePath);
|
|
}
|
|
await persist("sessions.json", true);
|
|
`("nested-wrapper-optional-branch-assignment.ts", filesystemWriteViolations(10)),
|
|
"keeps previous nested wrappers after optional safe reassignment": fsCase`
|
|
function persist(filePath: string, disabled: boolean) {
|
|
let inner = (nextPath: string) => fs.writeFile(nextPath, "{}\\n");
|
|
if (disabled) {
|
|
inner = async () => {};
|
|
}
|
|
return inner(filePath);
|
|
}
|
|
await persist("sessions.json", false);
|
|
`("nested-wrapper-optional-safe-reassignment.ts", filesystemWriteViolations(10)),
|
|
"keeps nested object wrapper assignments inside optional branches": fsCase`
|
|
function persist(filePath: string, useJson: boolean) {
|
|
const writer: { inner?: (nextPath: string) => Promise<void> } = {};
|
|
if (useJson) {
|
|
writer.inner = (nextPath: string) => fs.writeFile(nextPath, "{}\\n");
|
|
}
|
|
return writer.inner?.(filePath);
|
|
}
|
|
await persist("sessions.json", true);
|
|
`("nested-object-wrapper-optional-branch-assignment.ts", filesystemWriteViolations(10)),
|
|
"keeps nested object wrapper methods from optional whole-object assignments": fsCase`
|
|
function persist(filePath: string, useJson: boolean) {
|
|
let writer: { inner?: (nextPath: string) => Promise<void> } = {};
|
|
if (useJson) {
|
|
writer = {
|
|
inner(nextPath: string) {
|
|
return fs.writeFile(nextPath, "{}\\n");
|
|
},
|
|
};
|
|
}
|
|
return writer.inner?.(filePath);
|
|
}
|
|
await persist("sessions.json", true);
|
|
`("nested-object-wrapper-optional-object-assignment.ts", filesystemWriteViolations(14)),
|
|
"flags legacy paths after exhaustive nested wrapper assignments": fsCase`
|
|
function persist(filePath: string, useJson: boolean) {
|
|
let inner: (nextPath: string) => Promise<void>;
|
|
if (useJson) {
|
|
inner = (nextPath: string) => fs.writeFile(nextPath, "{}\\n");
|
|
} else {
|
|
inner = (nextPath: string) => fs.writeFile(nextPath, "[]\\n");
|
|
}
|
|
return inner(filePath);
|
|
}
|
|
await persist("sessions.json", true);
|
|
`("nested-wrapper-branch-assignment.ts", filesystemWriteViolations(12)),
|
|
"flags legacy paths after nested wrapper assignments inside plain blocks": fsPersistCase`
|
|
let inner: (nextPath: string) => Promise<void>;
|
|
{
|
|
inner = (nextPath: string) => fs.writeFile(nextPath, "{}\\n");
|
|
}
|
|
return inner(filePath);
|
|
`("nested-wrapper-block-assignment.ts", filesystemWriteViolations(10)),
|
|
"keeps exhaustive nested wrapper assignments inside plain blocks": fsCase`
|
|
function persist(filePath: string, useJson: boolean) {
|
|
let inner: (nextPath: string) => Promise<void>;
|
|
{
|
|
if (useJson) {
|
|
inner = (nextPath: string) => fs.writeFile(nextPath, "{}\\n");
|
|
} else {
|
|
inner = (nextPath: string) => fs.writeFile(nextPath, "[]\\n");
|
|
}
|
|
}
|
|
return inner(filePath);
|
|
}
|
|
await persist("sessions.json", true);
|
|
`("nested-wrapper-block-branch-assignment.ts", filesystemWriteViolations(14)),
|
|
"does not leak branch-local nested wrapper shadows after exhaustive branches": fsCase`
|
|
function persist(filePath: string, useJson: boolean) {
|
|
let inner = (_nextPath: string) => Promise.resolve();
|
|
if (useJson) {
|
|
{
|
|
let inner: (nextPath: string) => Promise<void>;
|
|
inner = (nextPath: string) => fs.writeFile(nextPath, "{}\\n");
|
|
}
|
|
} else {
|
|
{
|
|
let inner: (nextPath: string) => Promise<void>;
|
|
inner = (nextPath: string) => fs.writeFile(nextPath, "[]\\n");
|
|
}
|
|
}
|
|
return inner(filePath);
|
|
}
|
|
await persist("sessions.json", true);
|
|
`("nested-wrapper-branch-local-shadow.ts", []),
|
|
"refreshes block-local aliases for nested wrappers assigned to outer bindings": fsPersistCase`
|
|
let inner: (nextPath: string) => Promise<void>;
|
|
{
|
|
inner = (nextPath: string) => write(nextPath, "{}\\n");
|
|
const write = fs.writeFile;
|
|
}
|
|
return inner(filePath);
|
|
`("nested-wrapper-block-local-late-alias.ts", filesystemWriteViolations(11)),
|
|
"keeps escaped nested wrapper aliases isolated from sibling blocks": fsPersistCase`
|
|
let inner: (nextPath: string) => Promise<void>;
|
|
{
|
|
const write = fs.writeFile;
|
|
inner = (nextPath: string) => write(nextPath, "{}\\n");
|
|
}
|
|
{
|
|
const write = async () => {};
|
|
}
|
|
return inner(filePath);
|
|
`("nested-wrapper-sibling-block-alias-shadow.ts", filesystemWriteViolations(14)),
|
|
"refreshes merged nested wrapper assignments after later aliases": fsCase`
|
|
function persist(filePath: string, useJson: boolean) {
|
|
let inner: (nextPath: string) => Promise<void>;
|
|
if (useJson) {
|
|
inner = (nextPath: string) => write(nextPath, "{}\\n");
|
|
} else {
|
|
inner = (nextPath: string) => write(nextPath, "[]\\n");
|
|
}
|
|
const write = fs.writeFile;
|
|
return inner(filePath);
|
|
}
|
|
await persist("sessions.json", true);
|
|
`("nested-wrapper-branch-late-alias.ts", filesystemWriteViolations(13)),
|
|
"refreshes branch-local aliases before merging nested wrapper assignments": fsCase`
|
|
function persist(filePath: string, useJson: boolean) {
|
|
let inner: (nextPath: string) => Promise<void>;
|
|
if (useJson) {
|
|
inner = (nextPath: string) => write(nextPath, "{}\\n");
|
|
const write = fs.writeFile;
|
|
} else {
|
|
inner = (nextPath: string) => write(nextPath, "[]\\n");
|
|
const write = fs.writeFile;
|
|
}
|
|
return inner(filePath);
|
|
}
|
|
await persist("sessions.json", true);
|
|
`("nested-wrapper-branch-local-late-alias.ts", filesystemWriteViolations(14)),
|
|
"flags legacy paths after exhaustive nested object wrapper assignments": fsCase`
|
|
function persist(filePath: string, useJson: boolean) {
|
|
let writer: { inner(nextPath: string): Promise<void> };
|
|
if (useJson) {
|
|
writer = {
|
|
inner(nextPath: string) {
|
|
return fs.writeFile(nextPath, "{}\\n");
|
|
},
|
|
};
|
|
} else {
|
|
writer = {
|
|
inner(nextPath: string) {
|
|
return fs.writeFile(nextPath, "[]\\n");
|
|
},
|
|
};
|
|
}
|
|
return writer.inner(filePath);
|
|
}
|
|
await persist("sessions.json", true);
|
|
`("nested-object-wrapper-branch-assignment.ts", filesystemWriteViolations(20)),
|
|
"clears stale nested object wrapper methods after exhaustive object reassignment": fsCase`
|
|
function persist(filePath: string, useJson: boolean) {
|
|
let writer = {
|
|
inner(nextPath: string) {
|
|
return fs.writeFile(nextPath, "{}\\n");
|
|
},
|
|
};
|
|
if (useJson) {
|
|
writer = {};
|
|
} else {
|
|
writer = {};
|
|
}
|
|
return writer.inner?.(filePath);
|
|
}
|
|
await persist("sessions.json", true);
|
|
`("nested-object-wrapper-exhaustive-object-reassignment.ts", []),
|
|
"flags legacy paths after exhaustive nested object wrapper parameter assignments": fsCase`
|
|
function persist(
|
|
filePath: string,
|
|
writer: { inner?: (nextPath: string) => Promise<void> },
|
|
useJson: boolean,
|
|
) {
|
|
if (useJson) {
|
|
writer.inner = (nextPath: string) => fs.writeFile(nextPath, "{}\\n");
|
|
} else {
|
|
writer.inner = (nextPath: string) => fs.writeFile(nextPath, "[]\\n");
|
|
}
|
|
return writer.inner?.(filePath);
|
|
}
|
|
await persist("sessions.json", {}, true);
|
|
`("nested-object-wrapper-parameter-branch-assignment.ts", filesystemWriteViolations(15)),
|
|
"keeps block-local nested wrapper shadows scoped to their block": fsPersistCase`
|
|
function inner(nextPath: string) {
|
|
return fs.writeFile(nextPath, "{}\\n");
|
|
}
|
|
{
|
|
function inner(_: string) {}
|
|
}
|
|
return inner(filePath);
|
|
`("nested-wrapper-block-shadow.ts", filesystemWriteViolations(12)),
|
|
"does not use outer nested wrappers for destructured parameter shadows": fsCase`
|
|
function helper(nextPath: string) {
|
|
return fs.writeFile(nextPath, "{}\\n");
|
|
}
|
|
function persist({ helper }: { helper: (nextPath: string) => Promise<void> }, filePath: string) {
|
|
return helper(filePath);
|
|
}
|
|
await persist({ helper: async () => {} }, "sessions.json");
|
|
`("nested-wrapper-destructured-parameter-shadow.ts", []),
|
|
"uses enclosing aliases when nested wrapper helpers are called": fsPersistCase`
|
|
const write = fs.writeFile;
|
|
function inner(nextPath: string) {
|
|
return write(nextPath, "{}\\n");
|
|
}
|
|
return inner(filePath);
|
|
`("nested-wrapper-alias.ts", filesystemWriteViolations(10)),
|
|
"does not use caller block shadows for nested wrapper helper aliases": requireCase`
|
|
const req = createRequire(import.meta.url);
|
|
function persist(filePath: string, customRequire: NodeRequire) {
|
|
function inner(nextPath: string) {
|
|
const fs = req("node:fs");
|
|
return fs.writeFileSync(nextPath, "{}\\n");
|
|
}
|
|
{
|
|
const req = customRequire;
|
|
return inner(filePath);
|
|
}
|
|
}
|
|
await persist("sessions.json", require);
|
|
`("nested-wrapper-call-shadow.ts", filesystemWriteViolations(14)),
|
|
|
|
// Wrapper parameter and body alias state.
|
|
"flags wrapper object binding defaults for explicit undefined forwarded properties": fsCase`
|
|
function inner({ path }: { path: string }) {
|
|
return fs.writeFile(path, "{}\\n");
|
|
}
|
|
function persist(filePath: string) {
|
|
function forward({ path = filePath } = {}) {
|
|
return inner({ path });
|
|
}
|
|
return forward({ path: undefined });
|
|
}
|
|
await persist("sessions.json");
|
|
`("wrapper-forwarded-undefined-object-property-default.ts", filesystemWriteViolations(12)),
|
|
"flags legacy paths from defaulted wrapper parameters": atomicCase`
|
|
function persistPath(filePath = "sessions.json") {
|
|
return writeTextAtomic(filePath, "{}\\n");
|
|
}
|
|
function persistOptions(options: { filePath?: string } = { filePath: "cron/jobs.json" }) {
|
|
return writeTextAtomic(options.filePath ?? currentSqlitePath, "{}\\n");
|
|
}
|
|
function persistDestructured({ filePath = "cron/runs/job.jsonl" }: { filePath?: string } = {}) {
|
|
return writeTextAtomic(filePath, "{}\\n");
|
|
}
|
|
persistPath();
|
|
persistPath(undefined);
|
|
persistOptions();
|
|
persistDestructured({});
|
|
persistDestructured({ filePath: undefined });
|
|
persistDestructured({ filePath: currentSqlitePath });
|
|
`("defaulted-wrapper-paths.ts", filesystemWriteViolations(12, 13, 14, 15, 16)),
|
|
"does not treat ambient declarations as undefined wrapper arguments": fsPromisesCase`
|
|
declare const provided: string;
|
|
function persist(filePath = "sessions.json") {
|
|
return fs.writeFile(filePath, "{}\\n");
|
|
}
|
|
await persist(provided);
|
|
`("ambient-defaulted-wrapper-path.ts", []),
|
|
"clears wrapper object parameter paths after reassignment": atomicPersistCase`
|
|
params = { filePath: currentSqlitePath };
|
|
return writeTextAtomic(params.filePath, "{}\\n");
|
|
`("reassigned-wrapper-object-options.ts", []),
|
|
"clears wrapper object parameter paths after nested block reassignment": atomicPersistCase`
|
|
{
|
|
params = { filePath: currentSqlitePath };
|
|
}
|
|
return writeTextAtomic(params.filePath, "{}\\n");
|
|
`("nested-reassigned-wrapper-object-options.ts", []),
|
|
"does not let block-local wrapper parameter shadows clear outer paths": atomicPersistCase`
|
|
{
|
|
const params = { filePath: currentSqlitePath };
|
|
await use(params);
|
|
}
|
|
return writeTextAtomic(params.filePath, "{}\\n");
|
|
`("block-local-wrapper-object-options-shadow.ts", filesystemWriteViolations(10)),
|
|
"clears destructured wrapper option paths after reassignment": atomicCase`
|
|
function persist({ filePath }: { filePath: string }) {
|
|
filePath = currentSqlitePath;
|
|
return writeTextAtomic(filePath, "{}\\n");
|
|
}
|
|
await persist({ filePath: "sessions.json" });
|
|
`("reassigned-destructured-wrapper-options.ts", []),
|
|
"clears wrapper object property paths after reassignment": atomicPersistCase`
|
|
params.filePath = currentSqlitePath;
|
|
return writeTextAtomic(params.filePath, "{}\\n");
|
|
`("reassigned-wrapper-property-options.ts", []),
|
|
"clears nested wrapper object property paths after reassignment": fsCase`
|
|
function persist(params: { paths: { filePath: string } }) {
|
|
params.paths.filePath = currentSqlitePath;
|
|
return fs.writeFile(params.paths.filePath, "{}\\n");
|
|
}
|
|
await persist({ paths: { filePath: "sessions.json" } });
|
|
`("reassigned-nested-wrapper-property-options.ts", []),
|
|
"updates wrapper object property paths after reassignment from another parameter": atomicCase`
|
|
function persist(params: { filePath: string }, legacy: { filePath: string }) {
|
|
params.filePath = legacy.filePath;
|
|
return writeTextAtomic(params.filePath, "{}\\n");
|
|
}
|
|
await persist({ filePath: currentSqlitePath }, { filePath: "sessions.json" });
|
|
`("reassigned-wrapper-property-from-parameter.ts", filesystemWriteViolations(7)),
|
|
"keeps wrapper object property paths after conditional reassignment": atomicPersistCase`
|
|
if (ready) params.filePath = currentSqlitePath;
|
|
return writeTextAtomic(params.filePath, "{}\\n");
|
|
`("conditional-reassigned-wrapper-property-options.ts", filesystemWriteViolations(7)),
|
|
"keeps wrapper object parameter paths after conditional reassignment": atomicPersistCase`
|
|
if (ready) params = { filePath: currentSqlitePath };
|
|
return writeTextAtomic(params.filePath, "{}\\n");
|
|
`("conditional-reassigned-wrapper-object-options.ts", filesystemWriteViolations(7)),
|
|
"keeps wrapper object property paths after for-of reassignment": atomicPersistCase`
|
|
for (const item of items) {
|
|
params.filePath = currentSqlitePath;
|
|
}
|
|
return writeTextAtomic(params.filePath, "{}\\n");
|
|
`("for-of-reassigned-wrapper-property-options.ts", filesystemWriteViolations(9)),
|
|
"keeps wrapper object property paths after try-block reassignment": atomicPersistCase`
|
|
try {
|
|
maybeThrow();
|
|
params.filePath = currentSqlitePath;
|
|
} catch {}
|
|
return writeTextAtomic(params.filePath, "{}\\n");
|
|
`("try-reassigned-wrapper-property-options.ts", filesystemWriteViolations(10)),
|
|
"clears wrapper object property paths after exhaustive current-path assignments":
|
|
atomicPersistCase`
|
|
if (ready) params.filePath = currentSqlitePath;
|
|
else params.filePath = currentSqlitePath;
|
|
return writeTextAtomic(params.filePath, "{}\\n");
|
|
`("exhaustive-current-wrapper-property-options.ts", []),
|
|
"clears wrapper object parameter paths after exhaustive current-object assignments":
|
|
atomicPersistCase`
|
|
if (ready) params = { filePath: currentSqlitePath };
|
|
else params = { filePath: currentSqlitePath };
|
|
return writeTextAtomic(params.filePath, "{}\\n");
|
|
`("exhaustive-current-wrapper-object-options.ts", []),
|
|
"flags wrapper object property paths after mixed exhaustive assignments": atomicCase`
|
|
function persist(params: { filePath: string }, legacy: { filePath: string }) {
|
|
if (ready) params.filePath = currentSqlitePath;
|
|
else params.filePath = legacy.filePath;
|
|
return writeTextAtomic(params.filePath, "{}\\n");
|
|
}
|
|
await persist({ filePath: currentSqlitePath }, { filePath: "sessions.json" });
|
|
`("exhaustive-mixed-wrapper-property-options.ts", filesystemWriteViolations(8)),
|
|
"flags wrapper object property paths after conditional reassignment from another parameter":
|
|
atomicCase`
|
|
function persist(params: { filePath: string }, legacy: { filePath: string }) {
|
|
if (ready) params.filePath = legacy.filePath;
|
|
return writeTextAtomic(params.filePath, "{}\\n");
|
|
}
|
|
await persist({ filePath: currentSqlitePath }, { filePath: "sessions.json" });
|
|
`("conditional-reassigned-wrapper-property-from-parameter.ts", filesystemWriteViolations(7)),
|
|
"flags wrapper object paths after conditional reassignment from another parameter":
|
|
atomicCase`
|
|
function persist(params: { filePath: string }, legacy: { filePath: string }) {
|
|
if (ready) params = legacy;
|
|
return writeTextAtomic(params.filePath, "{}\\n");
|
|
}
|
|
await persist({ filePath: currentSqlitePath }, { filePath: "sessions.json" });
|
|
`("conditional-reassigned-wrapper-object-from-parameter.ts", filesystemWriteViolations(7)),
|
|
"flags destructured wrapper paths after conditional reassignment from another parameter":
|
|
atomicCase`
|
|
function persist({ filePath }: { filePath: string }, legacy: { filePath: string }) {
|
|
if (ready) filePath = legacy.filePath;
|
|
return writeTextAtomic(filePath, "{}\\n");
|
|
}
|
|
await persist({ filePath: currentSqlitePath }, { filePath: "sessions.json" });
|
|
`("conditional-reassigned-destructured-wrapper-options.ts", filesystemWriteViolations(7)),
|
|
"flags legacy paths passed through locally destructured wrapper options": atomicPersistCase`
|
|
const { filePath } = params;
|
|
return writeTextAtomic(filePath, "{}\\n");
|
|
`("local-destructured-wrapper-options.ts", filesystemWriteViolations(7)),
|
|
"flags legacy paths passed through local wrapper property aliases": atomicPersistCase`
|
|
const filePath = params.filePath;
|
|
return writeTextAtomic(filePath, "{}\\n");
|
|
`("local-property-alias-wrapper-options.ts", filesystemWriteViolations(7)),
|
|
"flags legacy paths passed through local wrapper object aliases": atomicPersistCase`
|
|
const target = params;
|
|
return writeTextAtomic(target.filePath, "{}\\n");
|
|
`("local-object-alias-wrapper-options.ts", filesystemWriteViolations(7)),
|
|
"flags wrapper object paths after reassignment from another parameter": atomicCase`
|
|
function persist(params: { filePath: string }, legacy: { filePath: string }) {
|
|
params = legacy;
|
|
return writeTextAtomic(params.filePath, "{}\\n");
|
|
}
|
|
await persist({ filePath: currentSqlitePath }, { filePath: "sessions.json" });
|
|
`("reassigned-wrapper-object-from-parameter.ts", filesystemWriteViolations(7)),
|
|
"flags wrapper object property paths after nested block reassignment from another parameter":
|
|
atomicCase`
|
|
function persist(params: { filePath: string }, legacy: { filePath: string }) {
|
|
{
|
|
params.filePath = legacy.filePath;
|
|
}
|
|
return writeTextAtomic(params.filePath, "{}\\n");
|
|
}
|
|
await persist({ filePath: currentSqlitePath }, { filePath: "sessions.json" });
|
|
`("nested-block-reassigned-wrapper-property.ts", filesystemWriteViolations(9)),
|
|
"flags wrapper destructured paths after nested block reassignment from another parameter":
|
|
atomicCase`
|
|
function persist({ filePath }: { filePath: string }, legacy: { filePath: string }) {
|
|
{
|
|
filePath = legacy.filePath;
|
|
}
|
|
return writeTextAtomic(filePath, "{}\\n");
|
|
}
|
|
await persist({ filePath: currentSqlitePath }, { filePath: "sessions.json" });
|
|
`("nested-block-reassigned-wrapper-destructured.ts", filesystemWriteViolations(9)),
|
|
"does not leak block-local wrapper path aliases into the parent block": atomicPersistCase`
|
|
const filePath = currentSqlitePath;
|
|
{
|
|
const filePath = params.filePath;
|
|
}
|
|
return writeTextAtomic(filePath, "{}\\n");
|
|
`("block-local-wrapper-path-alias.ts", []),
|
|
"flags wrapper option paths written through body-local fs aliases": fsPromisesPersistCase`
|
|
const { writeFile } = fs;
|
|
return writeFile(params.filePath, "{}\\n");
|
|
`("body-local-fs-alias-wrapper.ts", filesystemWriteViolations(7)),
|
|
"flags wrapper option paths written through body-local fs method aliases":
|
|
fsPromisesPersistCase`
|
|
const save = fs.writeFile;
|
|
return save(params.filePath, "{}\\n");
|
|
`("wrapper-body-local-fs-method-alias.ts", filesystemWriteViolations(7)),
|
|
"flags wrapper option paths written through branch-assigned body-local fs aliases":
|
|
fsPromisesCase`
|
|
function persist(params: { filePath: string }, ready: boolean) {
|
|
let write;
|
|
if (ready) {
|
|
write = fs.writeFile;
|
|
} else {
|
|
write = fs.writeFile;
|
|
}
|
|
return write(params.filePath, "{}\\n");
|
|
}
|
|
await persist({ filePath: "sessions.json" }, true);
|
|
`("branch-assigned-body-local-fs-alias-wrapper.ts", filesystemWriteViolations(12)),
|
|
"flags nested wrapper helpers capturing branch-assigned body-local fs aliases":
|
|
fsPromisesCase`
|
|
function persist(params: { filePath: string }, ready: boolean) {
|
|
let write;
|
|
if (ready) {
|
|
write = fs.writeFile;
|
|
} else {
|
|
write = fs.writeFile;
|
|
}
|
|
function inner() {
|
|
return write(params.filePath, "{}\\n");
|
|
}
|
|
return inner();
|
|
}
|
|
await persist({ filePath: "sessions.json" }, true);
|
|
`("nested-wrapper-branch-assigned-body-local-fs-alias.ts", filesystemWriteViolations(15)),
|
|
"flags wrapper option paths written through body-local fs object aliases":
|
|
fsPromisesPersistCase`
|
|
const writer = { writeFile: fs.writeFile };
|
|
return writer.writeFile(params.filePath, "{}\\n");
|
|
`("body-local-fs-object-alias-wrapper.ts", filesystemWriteViolations(7)),
|
|
"flags wrapper option paths written through bracketed body-local fs object aliases":
|
|
fsPromisesPersistCase`
|
|
const writer = { writeFile: fs.writeFile };
|
|
return writer["writeFile"](params.filePath, "{}\\n");
|
|
`("body-local-bracket-fs-object-alias-wrapper.ts", filesystemWriteViolations(7)),
|
|
"clears wrapper body fs object aliases after object reassignment": fsPromisesPersistCase`
|
|
let writer = { writeFile: fs.writeFile };
|
|
writer = customWriter;
|
|
return writer.writeFile(params.filePath, "{}\\n");
|
|
`("reassigned-body-local-fs-object-alias-wrapper.ts", []),
|
|
"does not let block-local wrapper aliases mutate outer wrapper metadata": fsPromisesCase`
|
|
import { writeFile } from "../infra/custom-writer.js";
|
|
function persist(params: { filePath: string }) {
|
|
return writeFile(params.filePath, "{}\\n");
|
|
}
|
|
{
|
|
const save = persist;
|
|
const { writeFile } = fs;
|
|
await save({ filePath: "sessions.json" });
|
|
}
|
|
await persist({ filePath: "cron/jobs.json" });
|
|
`("block-local-wrapper-alias-metadata.ts", []),
|
|
"flags wrapper option paths written through fs.promises": sourceCase`
|
|
import fs from "node:fs";
|
|
function persist(params: { filePath: string }) {
|
|
return fs.promises.writeFile(params.filePath, "{}\\n");
|
|
}
|
|
await persist({ filePath: "sessions.json" });
|
|
`("wrapper-fs-promises-write.ts", filesystemWriteViolations(6)),
|
|
"flags wrapper option paths written through outer fs module object aliases": fsPromisesCase`
|
|
const deps = { fs };
|
|
function persist(params: { filePath: string }) {
|
|
return deps.fs.writeFile(params.filePath, "{}\\n");
|
|
}
|
|
persist({ filePath: "sessions.json" });
|
|
`("wrapper-outer-fs-module-object-alias.ts", filesystemWriteViolations(7)),
|
|
"flags wrapper option paths written through injected fs handles": sourceCase`
|
|
function persist(deps: { fs: typeof import("node:fs") }, params: { filePath: string }) {
|
|
return deps.fs.promises.writeFile(params.filePath, "{}\\n");
|
|
}
|
|
await persist(deps, { filePath: "sessions.json" });
|
|
`("wrapper-injected-fs-write.ts", filesystemWriteViolations(5)),
|
|
"does not treat untyped wrapper fs properties as filesystem writes": sourceCase`
|
|
function persist(deps: { fs: { promises: { writeFile: Function } } }, params: { filePath: string }) {
|
|
return deps.fs.promises.writeFile(params.filePath, "{}\\n");
|
|
}
|
|
await persist(deps, { filePath: "sessions.json" });
|
|
`("wrapper-custom-fs-property.ts", []),
|
|
"flags wrapper option paths written through CommonJS fs": sourceCase`
|
|
function persist(params: { filePath: string }) {
|
|
const fs = require("node:fs");
|
|
return fs.promises.writeFile(params.filePath, "{}\\n");
|
|
}
|
|
await persist({ filePath: "sessions.json" });
|
|
`("wrapper-commonjs-fs-write.ts", filesystemWriteViolations(6)),
|
|
"flags wrapper options forwarded to filePath helper objects": sourceCase`
|
|
import { appendRegularFile, replaceFileAtomic } from "../infra/fs-safe.js";
|
|
function append(options: { filePath: string; content: string }) {
|
|
return appendRegularFile(options);
|
|
}
|
|
function replace(options: { filePath: string; content: string }) {
|
|
return replaceFileAtomic(options);
|
|
}
|
|
append({ filePath: "sessions.json", content: "{}\\n" });
|
|
replace({ filePath: "plugin-state/state.sqlite", content: "" });
|
|
`("forwarded-filepath-helper-options.ts", filesystemWriteViolations(9, 10)),
|
|
"flags wrapper options forwarded through another wrapper": atomicCase`
|
|
function persist(params: { filePath: string }) {
|
|
return writeTextAtomic(params.filePath, "{}\\n");
|
|
}
|
|
function save(params: { filePath: string }) {
|
|
return persist(params);
|
|
}
|
|
save({ filePath: "sessions.json" });
|
|
`("transitive-wrapper-forwarding.ts", filesystemWriteViolations(9)),
|
|
"flags wrapper options spread through another wrapper": atomicCase`
|
|
function persist(params: { filePath: string }) {
|
|
return writeTextAtomic(params.filePath, "{}\\n");
|
|
}
|
|
function save(params: { filePath: string }) {
|
|
return persist({ ...params });
|
|
}
|
|
save({ filePath: "sessions.json" });
|
|
`("transitive-wrapper-spread-forwarding.ts", filesystemWriteViolations(9)),
|
|
"allows wrapper spread forwarding when a later property overrides the path": atomicCase`
|
|
function persist(params: { filePath: string }) {
|
|
return writeTextAtomic(params.filePath, "{}\\n");
|
|
}
|
|
function save(params: { filePath: string }) {
|
|
return persist({ ...params, filePath: currentSqlitePath });
|
|
}
|
|
save({ filePath: "sessions.json" });
|
|
`("transitive-wrapper-spread-overridden-forwarding.ts", []),
|
|
"flags wrapper spread forwarding when a later spread restores the path": atomicCase`
|
|
function persist(params: { filePath: string }) {
|
|
return writeTextAtomic(params.filePath, "{}\\n");
|
|
}
|
|
function save(params: { filePath: string }) {
|
|
return persist({ filePath: currentSqlitePath, ...params });
|
|
}
|
|
save({ filePath: "sessions.json" });
|
|
`("transitive-wrapper-spread-restored-forwarding.ts", filesystemWriteViolations(9)),
|
|
"flags wrapper options renamed through another wrapper": atomicCase`
|
|
function persist(params: { filePath: string }) {
|
|
return writeTextAtomic(params.filePath, "{}\\n");
|
|
}
|
|
function save(params: { storePath: string }) {
|
|
return persist({ filePath: params.storePath });
|
|
}
|
|
save({ storePath: "sessions.json" });
|
|
`("transitive-wrapper-renamed-forwarding.ts", filesystemWriteViolations(9)),
|
|
"flags hoisted wrappers that use write aliases declared later": fsPromisesCase`
|
|
function persist(params: { filePath: string }) {
|
|
return writeFile(params.filePath, "{}\\n");
|
|
}
|
|
const { writeFile } = fs;
|
|
await persist({ filePath: "sessions.json" });
|
|
`("late-alias-hoisted-wrapper.ts", filesystemWriteViolations(7)),
|
|
"flags hoisted wrappers that use renamed write aliases declared later": fsPromisesCase`
|
|
function persist(params: { filePath: string }) {
|
|
return write(params.filePath, "{}\\n");
|
|
}
|
|
const { writeFile: write } = fs;
|
|
await persist({ filePath: "sessions.json" });
|
|
`("late-renamed-alias-hoisted-wrapper.ts", filesystemWriteViolations(7)),
|
|
"flags reassigned wrapper variables": atomicCase`
|
|
let persist;
|
|
persist = (params: { filePath: string }) => writeTextAtomic(params.filePath, "{}\\n");
|
|
persist({ filePath: "sessions.json" });
|
|
`("reassigned-wrapper-variable.ts", filesystemWriteViolations(5)),
|
|
"flags aliased wrapper variables": atomicCase`
|
|
function persist(params: { filePath: string }) {
|
|
return writeTextAtomic(params.filePath, "{}\\n");
|
|
}
|
|
const save = persist;
|
|
save({ filePath: "sessions.json" });
|
|
`("aliased-wrapper-variable.ts", filesystemWriteViolations(7)),
|
|
"does not treat aliased top-level helpers as closing over wrapper parameters": atomicCase`
|
|
const filePath = "not-openclaw-state.txt";
|
|
function helper() {
|
|
return writeTextAtomic(filePath, "{}\\n");
|
|
}
|
|
function persist(filePath: string) {
|
|
const inner = helper;
|
|
return inner();
|
|
}
|
|
persist("sessions.json");
|
|
`("aliased-top-level-wrapper-closed-over-module-var.ts", []),
|
|
|
|
// Object-backed wrapper discovery and alias tracking.
|
|
"flags object method wrappers": atomicCase`
|
|
const writer = {
|
|
persist(params: { filePath: string }) {
|
|
return writeTextAtomic(params.filePath, "{}\\n");
|
|
},
|
|
};
|
|
writer.persist({ filePath: "sessions.json" });
|
|
`("object-method-wrapper.ts", filesystemWriteViolations(8)),
|
|
"flags object property wrapper functions": atomicCase`
|
|
const writer = {
|
|
persist: (params: { filePath: string }) => writeTextAtomic(params.filePath, "{}\\n"),
|
|
};
|
|
writer["persist"]({ filePath: "sessions.json" });
|
|
`("object-property-wrapper-function.ts", filesystemWriteViolations(6)),
|
|
"flags object wrapper shorthand aliases": atomicCase`
|
|
function persist(params: { filePath: string }) {
|
|
return writeTextAtomic(params.filePath, "{}\\n");
|
|
}
|
|
const writer = { persist };
|
|
await writer.persist({ filePath: "sessions.json" });
|
|
`("object-wrapper-shorthand-alias.ts", filesystemWriteViolations(7)),
|
|
"flags object wrapper property aliases": atomicCase`
|
|
function persist(params: { filePath: string }) {
|
|
return writeTextAtomic(params.filePath, "{}\\n");
|
|
}
|
|
const writer = { save: persist };
|
|
await writer.save({ filePath: "sessions.json" });
|
|
`("object-wrapper-property-alias.ts", filesystemWriteViolations(7)),
|
|
"flags object wrapper methods copied through property access aliases": fsPersistCase`
|
|
const writer = {
|
|
save(nextPath: string) {
|
|
return fs.writeFile(nextPath, "{}\\n");
|
|
},
|
|
};
|
|
const proxy = { save: writer.save };
|
|
return proxy.save(filePath);
|
|
`("object-wrapper-property-access-alias.ts", filesystemWriteViolations(12)),
|
|
"flags object wrapper methods copied through whole-object aliases": fsPersistCase`
|
|
const writer = {
|
|
save(nextPath: string) {
|
|
return fs.writeFile(nextPath, "{}\\n");
|
|
},
|
|
};
|
|
const proxy = writer;
|
|
return proxy.save(filePath);
|
|
`("object-wrapper-whole-object-alias.ts", filesystemWriteViolations(12)),
|
|
"flags nested object wrapper methods copied through property access aliases": fsPersistCase`
|
|
const writer = {
|
|
nested: {
|
|
save(nextPath: string) {
|
|
return fs.writeFile(nextPath, "{}\\n");
|
|
},
|
|
},
|
|
};
|
|
const nested = writer.nested;
|
|
return nested.save(filePath);
|
|
`("nested-object-wrapper-property-access-alias.ts", filesystemWriteViolations(14)),
|
|
"flags destructured object wrapper methods from deep property paths": fsPersistCase`
|
|
const holder = {
|
|
inner: {
|
|
writer: {
|
|
save() {
|
|
return fs.writeFile(filePath, "{}\\n");
|
|
},
|
|
},
|
|
},
|
|
};
|
|
const { save } = holder.inner.writer;
|
|
return save();
|
|
`("deep-object-wrapper-destructured-method.ts", filesystemWriteViolations(16)),
|
|
"flags nested object wrapper methods copied through destructuring aliases": fsPersistCase`
|
|
const writer = {
|
|
nested: {
|
|
save() {
|
|
return fs.writeFile(filePath, "{}\\n");
|
|
},
|
|
},
|
|
};
|
|
const { nested } = writer;
|
|
return nested.save();
|
|
`("nested-object-wrapper-destructured-alias.ts", filesystemWriteViolations(14)),
|
|
"flags nested object wrapper methods copied through identifier-valued properties":
|
|
fsPersistCase`
|
|
const nested = {
|
|
save() {
|
|
return fs.writeFile(filePath, "{}\\n");
|
|
},
|
|
};
|
|
const writer = { nested };
|
|
return writer.nested.save();
|
|
`("nested-object-wrapper-identifier-property-alias.ts", filesystemWriteViolations(12)),
|
|
"clears stale nested object wrapper methods after object literal overwrites": fsPersistCase`
|
|
const writer = {
|
|
nested: {
|
|
save(nextPath: string) {
|
|
return fs.writeFile(nextPath, "{}\\n");
|
|
},
|
|
},
|
|
nested: {},
|
|
};
|
|
return writer.nested.save(filePath);
|
|
`("nested-object-wrapper-literal-overwrite.ts", []),
|
|
"flags object wrapper methods copied through object spreads": fsPersistCase`
|
|
const writer = {
|
|
save(nextPath: string) {
|
|
return fs.writeFile(nextPath, "{}\\n");
|
|
},
|
|
};
|
|
const proxy = { ...writer };
|
|
return proxy.save(filePath);
|
|
`("object-wrapper-object-spread-alias.ts", filesystemWriteViolations(12)),
|
|
"flags nested object wrapper methods": fsPersistCase`
|
|
const writer = {
|
|
nested: {
|
|
save() {
|
|
return fs.writeFile(filePath, "{}\\n");
|
|
},
|
|
},
|
|
};
|
|
return writer.nested.save();
|
|
`("nested-object-wrapper-method.ts", filesystemWriteViolations(13)),
|
|
"flags top-level nested object wrapper methods": fsCase`
|
|
const writer = {
|
|
nested: {
|
|
save(filePath: string) {
|
|
return fs.writeFile(filePath, "{}\\n");
|
|
},
|
|
},
|
|
};
|
|
await writer.nested.save("sessions.json");
|
|
`("top-level-nested-object-wrapper-method.ts", filesystemWriteViolations(10)),
|
|
"flags top-level nested object wrapper methods copied through shorthand properties": fsCase`
|
|
const nested = {
|
|
save(filePath: string) {
|
|
return fs.writeFile(filePath, "{}\\n");
|
|
},
|
|
};
|
|
const writer = { nested };
|
|
await writer.nested.save("sessions.json");
|
|
`("top-level-nested-object-wrapper-shorthand.ts", filesystemWriteViolations(9)),
|
|
"flags top-level nested object wrapper methods copied through identifier properties": fsCase`
|
|
const nested = {
|
|
save(filePath: string) {
|
|
return fs.writeFile(filePath, "{}\\n");
|
|
},
|
|
};
|
|
const writer = { child: nested };
|
|
await writer.child.save("sessions.json");
|
|
`("top-level-nested-object-wrapper-identifier-property.ts", filesystemWriteViolations(9)),
|
|
"flags top-level nested object wrapper methods copied through property access aliases":
|
|
fsCase`
|
|
const writer = {
|
|
nested: {
|
|
save(filePath: string) {
|
|
return fs.writeFile(filePath, "{}\\n");
|
|
},
|
|
},
|
|
};
|
|
const child = writer.nested;
|
|
await child.save("sessions.json");
|
|
`("top-level-nested-object-wrapper-property-access-alias.ts", filesystemWriteViolations(11)),
|
|
"clears top-level object wrapper methods overwritten with undefined": fsCase`
|
|
function save(filePath: string) {
|
|
return fs.writeFile(filePath, "{}\\n");
|
|
}
|
|
const writer = { save, save: undefined };
|
|
await writer.save?.("sessions.json");
|
|
`("top-level-object-wrapper-undefined-overwrite.ts", []),
|
|
"clears top-level nested object wrapper methods overwritten with undefined": fsCase`
|
|
const writer = {
|
|
nested: {
|
|
save(filePath: string) {
|
|
return fs.writeFile(filePath, "{}\\n");
|
|
},
|
|
},
|
|
nested: undefined,
|
|
};
|
|
await writer.nested?.save?.("sessions.json");
|
|
`("top-level-nested-object-wrapper-undefined-overwrite.ts", []),
|
|
"does not copy object wrapper methods from shadowed objects": fsCase`
|
|
const writer = {
|
|
save(filePath: string) {
|
|
return fs.writeFile(filePath, "{}\\n");
|
|
},
|
|
};
|
|
{
|
|
const writer = {};
|
|
const alias = writer;
|
|
await alias.save?.("sessions.json");
|
|
}
|
|
`("shadowed-object-wrapper-method-alias.ts", []),
|
|
"flags nested object wrapper methods assigned through object properties": fsPersistCase`
|
|
const writer: any = {};
|
|
writer.nested = {
|
|
save() {
|
|
return fs.writeFile(filePath, "{}\\n");
|
|
},
|
|
};
|
|
return writer.nested.save();
|
|
`("nested-object-wrapper-property-object-assignment.ts", filesystemWriteViolations(12)),
|
|
"flags top-level nested object wrapper methods assigned through object properties": fsCase`
|
|
const writer: any = {};
|
|
writer.nested = {
|
|
save(filePath: string) {
|
|
return fs.writeFile(filePath, "{}\\n");
|
|
},
|
|
};
|
|
await writer.nested.save("sessions.json");
|
|
`(
|
|
"top-level-nested-object-wrapper-property-object-assignment.ts",
|
|
filesystemWriteViolations(9),
|
|
),
|
|
"flags nested object wrapper methods assigned through deep object properties": fsPersistCase`
|
|
const writer: any = { nested: {} };
|
|
writer.nested.save = () => fs.writeFile(filePath, "{}\\n");
|
|
return writer.nested.save();
|
|
`("nested-object-wrapper-deep-property-assignment.ts", filesystemWriteViolations(8)),
|
|
"clears stale nested object wrapper methods after property reassignment": fsPersistCase`
|
|
const writer: any = {
|
|
nested: {
|
|
save() {
|
|
return fs.writeFile(filePath, "{}\\n");
|
|
},
|
|
},
|
|
};
|
|
writer.nested = {};
|
|
return writer.nested.save?.();
|
|
`("nested-object-wrapper-property-object-reassignment.ts", []),
|
|
"clears object wrapper property aliases overwritten with undefined": fsPersistCase`
|
|
const save = () => fs.writeFile(filePath, "{}\\n");
|
|
const writer = { save, save: undefined };
|
|
return writer.save?.();
|
|
`("object-wrapper-property-undefined-overwrite.ts", []),
|
|
"flags object wrapper methods assigned after declaration": atomicCase`
|
|
let writer: any = {};
|
|
writer = {
|
|
persist(params: { filePath: string }) {
|
|
return writeTextAtomic(params.filePath, "{}\\n");
|
|
},
|
|
};
|
|
await writer.persist({ filePath: "sessions.json" });
|
|
`("assigned-object-wrapper-method.ts", filesystemWriteViolations(9)),
|
|
"clears object wrapper metadata after object reassignment": atomicCase`
|
|
let writer: any = {
|
|
persist(params: { filePath: string }) {
|
|
return writeTextAtomic(params.filePath, "{}\\n");
|
|
},
|
|
};
|
|
writer = customWriter;
|
|
await writer.persist({ filePath: "sessions.json" });
|
|
`("reassigned-object-wrapper-method.ts", []),
|
|
"uses branch-local object wrapper metadata after conditional reassignment": atomicCase`
|
|
const writer: any = {
|
|
persist(params: { filePath: string }) {
|
|
return writeTextAtomic(params.filePath, "{}\\n");
|
|
},
|
|
};
|
|
if (ready) {
|
|
writer.persist = customPersist;
|
|
await writer.persist({ filePath: "sessions.json" });
|
|
}
|
|
`("conditional-object-wrapper-reassignment.ts", []),
|
|
"flags object wrapper property assignments": atomicCase`
|
|
const writer: any = {};
|
|
writer.persist = (params: { filePath: string }) => writeTextAtomic(params.filePath, "{}\\n");
|
|
await writer.persist({ filePath: "sessions.json" });
|
|
`("object-wrapper-property-assignment.ts", filesystemWriteViolations(5)),
|
|
"flags nested object wrapper methods copied through property access spreads": atomicCase`
|
|
const writer: any = {
|
|
nested: {
|
|
save(params: { filePath: string }) {
|
|
return writeTextAtomic(params.filePath, "{}\\n");
|
|
},
|
|
},
|
|
};
|
|
const copy = { ...writer.nested };
|
|
await copy.save({ filePath: "sessions.json" });
|
|
`("nested-object-wrapper-property-access-spread.ts", filesystemWriteViolations(11)),
|
|
"flags extracted object wrapper methods": atomicCase`
|
|
const writer = {
|
|
persist(params: { filePath: string }) {
|
|
return writeTextAtomic(params.filePath, "{}\\n");
|
|
},
|
|
};
|
|
const save = writer.persist;
|
|
await save({ filePath: "sessions.json" });
|
|
`("extracted-object-wrapper-method.ts", filesystemWriteViolations(9)),
|
|
"flags extracted bracket object wrapper methods": atomicCase`
|
|
const writer = {
|
|
persist(params: { filePath: string }) {
|
|
return writeTextAtomic(params.filePath, "{}\\n");
|
|
},
|
|
};
|
|
const save = writer["persist"];
|
|
await save({ filePath: "sessions.json" });
|
|
`("extracted-bracket-object-wrapper-method.ts", filesystemWriteViolations(9)),
|
|
"flags reassigned aliases from object wrapper methods": atomicCase`
|
|
const writer = {
|
|
persist(params: { filePath: string }) {
|
|
return writeTextAtomic(params.filePath, "{}\\n");
|
|
},
|
|
};
|
|
let save;
|
|
save = writer.persist;
|
|
await save({ filePath: "sessions.json" });
|
|
`("reassigned-object-wrapper-method-alias.ts", filesystemWriteViolations(10)),
|
|
"flags destructured object wrapper methods": atomicCase`
|
|
const writer = {
|
|
persist(params: { filePath: string }) {
|
|
return writeTextAtomic(params.filePath, "{}\\n");
|
|
},
|
|
};
|
|
const { persist } = writer;
|
|
await persist({ filePath: "sessions.json" });
|
|
`("destructured-object-wrapper-method.ts", filesystemWriteViolations(9)),
|
|
"flags renamed destructured object wrapper methods": atomicCase`
|
|
const writer = {
|
|
persist(params: { filePath: string }) {
|
|
return writeTextAtomic(params.filePath, "{}\\n");
|
|
},
|
|
};
|
|
const { persist: save } = writer;
|
|
await save({ filePath: "sessions.json" });
|
|
`("renamed-destructured-object-wrapper-method.ts", filesystemWriteViolations(9)),
|
|
"flags defaulted destructured object wrapper methods": fsPersistCase`
|
|
const writer = {};
|
|
const { save = () => fs.writeFile(filePath, "{}\\n") } = writer;
|
|
return save();
|
|
`("defaulted-destructured-object-wrapper-method.ts", filesystemWriteViolations(8)),
|
|
"does not use destructured wrapper defaults when safe callbacks are present": fsCase`
|
|
const noopParam = async (_path: string) => {};
|
|
function persist(filePath: string) {
|
|
const writer = { save: noopParam };
|
|
const { save = () => fs.writeFile(filePath, "{}\\n") } = writer;
|
|
return save(filePath);
|
|
}
|
|
await persist("sessions.json");
|
|
`("present-safe-callback-destructured-default.ts", []),
|
|
"uses destructured wrapper defaults when properties are explicitly undefined": fsPersistCase`
|
|
const writer = { save: undefined };
|
|
const { save = () => fs.writeFile(filePath, "{}\\n") } = writer;
|
|
return save();
|
|
`("undefined-callback-destructured-default.ts", filesystemWriteViolations(8)),
|
|
"uses destructured wrapper defaults when properties are aliased undefined": fsCase`
|
|
const absent = undefined;
|
|
function persist(filePath: string) {
|
|
const writer = { save: absent };
|
|
const { save = () => fs.writeFile(filePath, "{}\\n") } = writer;
|
|
return save();
|
|
}
|
|
await persist("sessions.json");
|
|
`("aliased-undefined-callback-destructured-default.ts", filesystemWriteViolations(9)),
|
|
"does not use destructured wrapper defaults after unknown spreads": fsCase`
|
|
function persist(filePath: string, options: { save?: () => Promise<void> }) {
|
|
const writer = { save: undefined, ...options };
|
|
const { save = () => fs.writeFile(filePath, "{}\\n") } = writer;
|
|
return save();
|
|
}
|
|
await persist("sessions.json", { save: async () => {} });
|
|
`("object-wrapper-destructuring-default-unknown-spread.ts", []),
|
|
"does not force destructured wrapper defaults from unknown spread objects": fsCase`
|
|
function persist(filePath: string, defaults: { save?: () => Promise<void> }) {
|
|
const writer = { ...defaults };
|
|
const { save = () => fs.writeFile(filePath, "{}\\n") } = writer;
|
|
return save();
|
|
}
|
|
await persist("sessions.json", { save: async () => {} });
|
|
`("object-wrapper-destructuring-default-unknown-spread-object.ts", []),
|
|
"does not force closed-over destructured wrapper defaults from unknown spread objects":
|
|
fsCase`
|
|
function persist(filePath: string, defaults: { save?: () => Promise<void> }) {
|
|
function inner() {
|
|
const writer = { ...defaults };
|
|
const { save = () => fs.writeFile(filePath, "{}\\n") } = writer;
|
|
return save();
|
|
}
|
|
return inner();
|
|
}
|
|
await persist("sessions.json", { save: async () => {} });
|
|
`("object-wrapper-destructuring-default-unknown-spread-object-closed-over.ts", []),
|
|
"keeps branch-only object wrapper methods after exhaustive merge": fsCase`
|
|
function persist(filePath: string, enabled: boolean) {
|
|
let writer = {};
|
|
if (enabled) {
|
|
writer = {
|
|
save() {
|
|
return fs.writeFile(filePath, "{}\\n");
|
|
},
|
|
};
|
|
} else {
|
|
writer = {};
|
|
}
|
|
const { save } = writer;
|
|
return save();
|
|
}
|
|
await persist("sessions.json", true);
|
|
`("branch-only-object-wrapper-method.ts", filesystemWriteViolations(17)),
|
|
"keeps branch-only property assigned object wrapper methods after exhaustive merge": fsCase`
|
|
function persist(filePath: string, enabled: boolean) {
|
|
let writer = {};
|
|
if (enabled) {
|
|
writer.save = () => fs.writeFile(filePath, "{}\\n");
|
|
} else {
|
|
writer = {};
|
|
}
|
|
return writer.save?.();
|
|
}
|
|
await persist("sessions.json", true);
|
|
`("branch-only-property-object-wrapper-method.ts", filesystemWriteViolations(12)),
|
|
"keeps prior nested wrapper values when only one branch assigns": fsCase`
|
|
function persist(filePath: string, disabled: boolean) {
|
|
let save = (nextPath: string) => fs.writeFile(nextPath, "{}\\n");
|
|
if (disabled) {
|
|
save = async () => {};
|
|
} else {
|
|
}
|
|
return save(filePath);
|
|
}
|
|
await persist("sessions.json", false);
|
|
`("prior-wrapper-value-branch-assignment.ts", filesystemWriteViolations(11)),
|
|
"clears wrapper metadata after non-wrapper reassignment": atomicCase`
|
|
let persist = (params: { filePath: string }) => writeTextAtomic(params.filePath, "{}\\n");
|
|
persist = customSink;
|
|
persist({ filePath: "sessions.json" });
|
|
`("cleared-wrapper-variable.ts", []),
|
|
"keeps wrapper metadata after conditional non-wrapper reassignment": atomicCase`
|
|
let persist = (params: { filePath: string }) => writeTextAtomic(params.filePath, "{}\\n");
|
|
if (ready) persist = customSink;
|
|
persist({ filePath: "sessions.json" });
|
|
`("conditional-cleared-wrapper-variable.ts", filesystemWriteViolations(5)),
|
|
"clears wrapper metadata after exhaustive non-wrapper reassignments": atomicCase`
|
|
let persist = (params: { filePath: string }) => writeTextAtomic(params.filePath, "{}\\n");
|
|
if (ready) persist = customSink;
|
|
else persist = customSink;
|
|
persist({ filePath: "sessions.json" });
|
|
`("exhaustive-cleared-wrapper-variable.ts", []),
|
|
"keeps wrapper metadata after try-block non-wrapper reassignment": atomicCase`
|
|
let persist = (params: { filePath: string }) => writeTextAtomic(params.filePath, "{}\\n");
|
|
try {
|
|
maybeThrow();
|
|
persist = customSink;
|
|
} catch {}
|
|
persist({ filePath: "sessions.json" });
|
|
`("try-cleared-wrapper-variable.ts", filesystemWriteViolations(8)),
|
|
|
|
// Tracked object paths, destructuring, and shadowing.
|
|
"flags wrapper option paths read through bracket property access": atomicCase`
|
|
function persist(params: { filePath: string }) {
|
|
return writeTextAtomic(params["filePath"], "{}\\n");
|
|
}
|
|
persist({ filePath: "sessions.json" });
|
|
`("bracket-wrapper-property.ts", filesystemWriteViolations(6)),
|
|
"flags wrapper option paths read through computed const property access": writeFileCase`
|
|
const key = "filePath";
|
|
function persist(params: { filePath: string }) {
|
|
return writeFile(params[key], "{}\\n");
|
|
}
|
|
persist({ [key]: "sessions.json" });
|
|
`("computed-const-wrapper-property.ts", filesystemWriteViolations(7)),
|
|
"tracks computed property keys through const aliases": writeFileCase`
|
|
const key = "filePath";
|
|
const alias = key;
|
|
function persist(params: { filePath: string }) {
|
|
return writeFile(params[alias], "{}\\n");
|
|
}
|
|
persist({ [key]: "sessions.json" });
|
|
`("computed-aliased-wrapper-property.ts", filesystemWriteViolations(8)),
|
|
"updates computed property keys after reassignment": writeFileCase`
|
|
let key = "currentPath";
|
|
key = "filePath";
|
|
function persist(params: { filePath: string }) {
|
|
return writeFile(params[key], "{}\\n");
|
|
}
|
|
persist({ [key]: "sessions.json" });
|
|
`("computed-reassigned-wrapper-property.ts", filesystemWriteViolations(8)),
|
|
"tracks computed property keys through nested wrapper closures": writeFileCase`
|
|
const key = "filePath";
|
|
function persist(params: { filePath: string }) {
|
|
function nested() {
|
|
return writeFile(params[key], "{}\\n");
|
|
}
|
|
return nested();
|
|
}
|
|
persist({ [key]: "sessions.json" });
|
|
`("computed-nested-wrapper-property.ts", filesystemWriteViolations(10)),
|
|
"conservatively scans options for unknown computed property keys": writeFileCase`
|
|
declare const key: string;
|
|
function persist(params: { filePath: string }) {
|
|
return writeFile(params[key], "{}\\n");
|
|
}
|
|
persist({ filePath: "sessions.json" });
|
|
`("unknown-computed-wrapper-property.ts", filesystemWriteViolations(7)),
|
|
"does not flag safe options for unknown computed property keys": writeFileCase`
|
|
declare const key: string;
|
|
function persist(params: { currentPath: string }) {
|
|
return writeFile(params[key], "{}\\n");
|
|
}
|
|
persist({ currentPath: "state/openclaw.sqlite" });
|
|
`("unknown-computed-safe-wrapper-property.ts", []),
|
|
"tracks unknown computed property definitions": writeFileCase`
|
|
declare const key: string;
|
|
function persist(params: Record<string, string>) {
|
|
return writeFile(params[key], "{}\\n");
|
|
}
|
|
persist({ [key]: "sessions.json" });
|
|
`("unknown-computed-property-definition.ts", filesystemWriteViolations(7)),
|
|
"tracks distinct unknown computed definition and read keys": writeFileCase`
|
|
declare const readKey: string;
|
|
declare const writeKey: string;
|
|
function persist(params: Record<string, string>) {
|
|
return writeFile(params[readKey], "{}\\n");
|
|
}
|
|
persist({ [writeKey]: "sessions.json" });
|
|
`("distinct-unknown-computed-property-keys.ts", filesystemWriteViolations(8)),
|
|
"copies unknown computed property facts through object spreads": writeFileCase`
|
|
declare const key: string;
|
|
const base = { [key]: "sessions.json" };
|
|
function persist(params: Record<string, string>) {
|
|
return writeFile(params[key], "{}\\n");
|
|
}
|
|
persist({ ...base });
|
|
`("spread-unknown-computed-property.ts", filesystemWriteViolations(8)),
|
|
"tracks nested unknown computed property definitions": writeFileCase`
|
|
declare const key: string;
|
|
function persist(params: { paths: Record<string, string> }) {
|
|
return writeFile(params.paths[key], "{}\\n");
|
|
}
|
|
persist({ paths: { [key]: "sessions.json" } });
|
|
`("nested-unknown-computed-property.ts", filesystemWriteViolations(7)),
|
|
"keeps exact safe siblings safe beside unknown computed properties": writeFileCase`
|
|
declare const key: string;
|
|
function persist(params: Record<string, string>) {
|
|
return writeFile(params.currentPath, "{}\\n");
|
|
}
|
|
persist({ [key]: "sessions.json", currentPath: "state/openclaw.sqlite" });
|
|
`("unknown-computed-property-safe-sibling.ts", []),
|
|
"retains a conservative fallback when computed key candidates exceed the cap": {
|
|
source: [
|
|
'import { writeFile } from "node:fs/promises";',
|
|
`let key; ${Array.from(
|
|
{ length: 32 },
|
|
(_, index) =>
|
|
`${index === 0 ? "" : "else "}if (condition${index}) key = "key${index}";`,
|
|
).join(" ")} else key = "filePath";`,
|
|
'function persist(params) { return writeFile(params[key], "{}\\n"); }',
|
|
'persist({ filePath: "sessions.json" });',
|
|
].join("\n"),
|
|
filename: "src/runtime/computed-key-candidate-cap.ts",
|
|
expected: filesystemWriteViolations(4),
|
|
},
|
|
"retains a conservative fallback for computed key cross products": {
|
|
source: [
|
|
'import { writeFile } from "node:fs/promises";',
|
|
`let outerKey; ${Array.from(
|
|
{ length: 5 },
|
|
(_, index) =>
|
|
`${index === 0 ? "" : "else "}if (outer${index}) outerKey = "outer${index}";`,
|
|
).join(" ")} else outerKey = "paths";`,
|
|
`let innerKey; ${Array.from(
|
|
{ length: 5 },
|
|
(_, index) =>
|
|
`${index === 0 ? "" : "else "}if (inner${index}) innerKey = "inner${index}";`,
|
|
).join(" ")} else innerKey = "filePath";`,
|
|
'function persist(params) { return writeFile(params[outerKey][innerKey], "{}\\n"); }',
|
|
'persist({ paths: { filePath: "sessions.json" } });',
|
|
].join("\n"),
|
|
filename: "src/runtime/computed-key-cross-product-cap.ts",
|
|
expected: filesystemWriteViolations(5),
|
|
},
|
|
"keeps outer computed-key facts visible through conditional property overlays": writeFileCase`
|
|
declare const key: string;
|
|
function persist(params: { filePath: string }, key: string) {
|
|
if (ready) {
|
|
params.currentPath = "state/openclaw.sqlite";
|
|
return writeFile(params[key], "{}\\n");
|
|
}
|
|
}
|
|
persist({ filePath: "sessions.json" }, key);
|
|
`("conditional-computed-key-overlay.ts", filesystemWriteViolations(10)),
|
|
"keeps outer computed-key facts visible through loop property overlays": writeFileCase`
|
|
declare const key: string;
|
|
function persist(params: { filePath: string }, key: string) {
|
|
while (ready) {
|
|
params.currentPath = "state/openclaw.sqlite";
|
|
return writeFile(params[key], "{}\\n");
|
|
}
|
|
}
|
|
persist({ filePath: "sessions.json" }, key);
|
|
`("loop-computed-key-overlay.ts", filesystemWriteViolations(10)),
|
|
"keeps outer computed-key facts visible through try property overlays": writeFileCase`
|
|
declare const key: string;
|
|
function persist(params: { filePath: string }, key: string) {
|
|
try {
|
|
params.currentPath = "state/openclaw.sqlite";
|
|
return writeFile(params[key], "{}\\n");
|
|
} catch {}
|
|
}
|
|
persist({ filePath: "sessions.json" }, key);
|
|
`("try-computed-key-overlay.ts", filesystemWriteViolations(10)),
|
|
"keeps nested computed-key facts visible through property overlays": writeFileCase`
|
|
declare const key: string;
|
|
function persist(params: { paths: { filePath: string } }, key: string) {
|
|
if (ready) {
|
|
params.paths.currentPath = "state/openclaw.sqlite";
|
|
return writeFile(params.paths[key], "{}\\n");
|
|
}
|
|
}
|
|
persist({ paths: { filePath: "sessions.json" } }, key);
|
|
`("nested-computed-key-overlay.ts", filesystemWriteViolations(10)),
|
|
"flags inline wrapper paths before chained method calls": writeFileCase`
|
|
function persist(params: { filePath: string }) {
|
|
return writeFile(params.filePath.toString(), "{}\\n");
|
|
}
|
|
persist({ filePath: "sessions.json" });
|
|
`("inline-chained-wrapper-property.ts", filesystemWriteViolations(6)),
|
|
"flags nested inline wrapper paths before chained method calls": writeFileCase`
|
|
function persist(params: { paths: { filePath: string } }) {
|
|
return writeFile(params.paths.filePath.toString(), "{}\\n");
|
|
}
|
|
persist({ paths: { filePath: "sessions.json" } });
|
|
`("nested-inline-chained-wrapper-property.ts", filesystemWriteViolations(6)),
|
|
"flags inline wrapper paths passed through chained normalize methods": writeFileCase`
|
|
function persist(params: { path: string }) {
|
|
return writeFile(params.path.normalize(), "{}\\n");
|
|
}
|
|
persist({ path: "sessions.json" });
|
|
`("inline-normalized-wrapper-property.ts", filesystemWriteViolations(6)),
|
|
"expands wrapper spread arguments from inline arrays": writeFileCase`
|
|
function persist(params: { filePath: string }) {
|
|
return writeFile(params.filePath, "{}\\n");
|
|
}
|
|
const params = { filePath: "sessions.json" };
|
|
persist(...[params]);
|
|
`("inline-array-spread-wrapper-argument.ts", filesystemWriteViolations(7)),
|
|
"expands wrapper spread arguments from tuple bindings": writeFileCase`
|
|
function persist(params: { filePath: string }) {
|
|
return writeFile(params.filePath, "{}\\n");
|
|
}
|
|
const args = [{ filePath: "sessions.json" }] as const;
|
|
persist(...args);
|
|
`("tuple-spread-wrapper-argument.ts", filesystemWriteViolations(7)),
|
|
"preserves wrapper positions through prefixed nested spreads": writeFileCase`
|
|
function persist(label: string, params: { filePath: string }) {
|
|
return writeFile(params.filePath, "{}\\n");
|
|
}
|
|
const params = { filePath: "sessions.json" };
|
|
persist("state", ...[...[params]]);
|
|
`("prefixed-nested-spread-wrapper-argument.ts", filesystemWriteViolations(7)),
|
|
"merges object facts across conditional wrapper arguments": writeFileCase`
|
|
function persist(params: { filePath: string }) {
|
|
return writeFile(params.filePath, "{}\\n");
|
|
}
|
|
persist(ready ? { filePath: "sessions.json" } : { filePath: currentPath });
|
|
`("conditional-wrapper-argument.ts", filesystemWriteViolations(6)),
|
|
"forwards comma-expression wrapper arguments": writeFileCase`
|
|
function persist(params: { filePath: string }) {
|
|
return writeFile(params.filePath, "{}\\n");
|
|
}
|
|
persist((0, { filePath: "sessions.json" }));
|
|
`("comma-wrapper-argument.ts", filesystemWriteViolations(6)),
|
|
"forwards satisfies wrapper arguments": writeFileCase`
|
|
function persist(params: { filePath: string }) {
|
|
return writeFile(params.filePath, "{}\\n");
|
|
}
|
|
const params = { filePath: "sessions.json" };
|
|
persist(params satisfies { filePath: string });
|
|
`("satisfies-wrapper-argument.ts", filesystemWriteViolations(7)),
|
|
"forwards awaited resolved wrapper arguments": writeFileCase`
|
|
function persist(params: { filePath: string }) {
|
|
return writeFile(params.filePath, "{}\\n");
|
|
}
|
|
const params = { filePath: "sessions.json" };
|
|
await persist(await Promise.resolve(params));
|
|
`("awaited-wrapper-argument.ts", filesystemWriteViolations(7)),
|
|
"forwards proxied wrapper arguments": writeFileCase`
|
|
function persist(params: { filePath: string }) {
|
|
return writeFile(params.filePath, "{}\\n");
|
|
}
|
|
const params = { filePath: "sessions.json" };
|
|
persist(new Proxy(params, {}));
|
|
`("proxied-wrapper-argument.ts", filesystemWriteViolations(7)),
|
|
"keeps safe proxied wrapper arguments safe": writeFileCase`
|
|
function persist(params: { filePath: string }) {
|
|
return writeFile(params.filePath, "{}\\n");
|
|
}
|
|
const params = { filePath: currentPath };
|
|
persist(new Proxy(params, {}));
|
|
`("safe-proxied-wrapper-argument.ts", []),
|
|
"forwards scalar paths through satisfies expressions": writeFileCase`
|
|
function persist(filePath: string) {
|
|
return writeFile(filePath, "{}\\n");
|
|
}
|
|
const filePath = "sessions.json";
|
|
persist(filePath satisfies string);
|
|
`("satisfies-scalar-wrapper-argument.ts", filesystemWriteViolations(7)),
|
|
"forwards scalar paths through spread arguments": writeFileCase`
|
|
function persist(filePath: string) {
|
|
return writeFile(filePath, "{}\\n");
|
|
}
|
|
const filePath = "sessions.json";
|
|
persist(...[filePath]);
|
|
`("spread-scalar-wrapper-argument.ts", filesystemWriteViolations(7)),
|
|
"keeps safe conditional wrapper arguments safe": writeFileCase`
|
|
function persist(params: { filePath: string }) {
|
|
return writeFile(params.filePath, "{}\\n");
|
|
}
|
|
persist(ready ? { filePath: currentPath } : { filePath: sqlitePath });
|
|
`("safe-conditional-wrapper-argument.ts", []),
|
|
"widens truncated conditional argument facts conservatively": {
|
|
source: [
|
|
'import { writeFile } from "node:fs/promises";',
|
|
'function persist(params) { return writeFile(params.filePath, "{}\\n"); }',
|
|
`persist(${Array.from(
|
|
{ length: 32 },
|
|
(_, index) => `condition${index} ? { filePath: currentPath } : `,
|
|
).join("")}{ filePath: "sessions.json" });`,
|
|
].join("\n"),
|
|
filename: "src/runtime/truncated-conditional-wrapper-argument.ts",
|
|
expected: filesystemWriteViolations(3),
|
|
},
|
|
"preserves filesystem writer aliases beyond the fact alternative cap": {
|
|
source: [
|
|
'import { writeFile } from "node:fs/promises";',
|
|
'function invoke(callback, filePath) { return callback(filePath, "{}\\n"); }',
|
|
`invoke(${Array.from(
|
|
{ length: 32 },
|
|
(_, index) => `condition${index} ? safe${index} : `,
|
|
).join("")}writeFile, "sessions.json");`,
|
|
].join("\n"),
|
|
filename: "src/runtime/truncated-filesystem-writer-fact.ts",
|
|
expected: filesystemWriteViolations(3),
|
|
},
|
|
"preserves wrapper records beyond the fact alternative cap": {
|
|
source: [
|
|
'import { writeFile } from "node:fs/promises";',
|
|
'function dangerous(params) { return writeFile(params.filePath, "{}\\n"); }',
|
|
"function invoke(callback, params) { return callback(params); }",
|
|
`invoke(${Array.from(
|
|
{ length: 32 },
|
|
(_, index) => `condition${index} ? safe${index} : `,
|
|
).join("")}dangerous, { filePath: "sessions.json" });`,
|
|
].join("\n"),
|
|
filename: "src/runtime/truncated-wrapper-record-fact.ts",
|
|
expected: filesystemWriteViolations(4),
|
|
},
|
|
"keeps safe callbacks beyond the fact alternative cap safe": {
|
|
source: [
|
|
'import { writeFile } from "node:fs/promises";',
|
|
'function invoke(callback, filePath) { return callback(filePath, "{}\\n"); }',
|
|
`invoke(${Array.from(
|
|
{ length: 32 },
|
|
(_, index) => `condition${index} ? safe${index} : `,
|
|
).join("")}safe32, "sessions.json");`,
|
|
].join("\n"),
|
|
filename: "src/runtime/truncated-safe-callback-fact.ts",
|
|
expected: [],
|
|
},
|
|
"preserves nested callback maps beyond the fact alternative cap": {
|
|
source: [
|
|
'import { writeFile } from "node:fs/promises";',
|
|
'function dangerous(filePath) { return writeFile(filePath, "{}\\n"); }',
|
|
'const dangerousObject = { callback: dangerous, filePath: "sessions.json" };',
|
|
"function invoke(entry) { return entry.callback(entry.filePath); }",
|
|
`invoke(${Array.from(
|
|
{ length: 32 },
|
|
(_, index) => `condition${index} ? safe${index} : `,
|
|
).join("")}dangerousObject);`,
|
|
].join("\n"),
|
|
filename: "src/runtime/truncated-nested-callback-map.ts",
|
|
expected: filesystemWriteViolations(5),
|
|
},
|
|
"keeps safe nested callback maps beyond the fact alternative cap safe": {
|
|
source: [
|
|
'import { writeFile } from "node:fs/promises";',
|
|
"function invoke(entry) { return entry.callback(entry.filePath); }",
|
|
`invoke(${Array.from(
|
|
{ length: 32 },
|
|
(_, index) => `condition${index} ? safe${index} : `,
|
|
).join("")}safe32);`,
|
|
].join("\n"),
|
|
filename: "src/runtime/truncated-safe-nested-callback-map.ts",
|
|
expected: [],
|
|
},
|
|
"does not merge branch-local computed property effects after their scope exits": sourceCase`
|
|
function update(groupId: string) {
|
|
if (named) {
|
|
const groups = {};
|
|
groups[groupId] = {};
|
|
} else {
|
|
const groups = {};
|
|
groups[groupId] = {};
|
|
}
|
|
}
|
|
update(event.groupId ?? "");
|
|
`("branch-local-computed-property-effects.ts", []),
|
|
"retains defined branches when applying object parameter defaults": writeFileCase`
|
|
function persist(params = { filePath: currentPath }) {
|
|
return writeFile(params.filePath, "{}\\n");
|
|
}
|
|
const params = { filePath: "sessions.json" };
|
|
persist(ready ? undefined : params);
|
|
`("conditional-object-parameter-default.ts", filesystemWriteViolations(7)),
|
|
"retains defined branches when applying scalar parameter defaults": writeFileCase`
|
|
function persist(filePath = currentPath) {
|
|
return writeFile(filePath, "{}\\n");
|
|
}
|
|
const filePath = "sessions.json";
|
|
persist(ready ? void 0 : filePath);
|
|
`("conditional-scalar-parameter-default.ts", filesystemWriteViolations(7)),
|
|
"applies legacy object defaults to possibly undefined arguments": writeFileCase`
|
|
function persist(params = { filePath: "sessions.json" }) {
|
|
return writeFile(params.filePath, "{}\\n");
|
|
}
|
|
const params = { filePath: currentPath };
|
|
persist(ready ? undefined : params);
|
|
`("legacy-object-parameter-default.ts", filesystemWriteViolations(7)),
|
|
"applies legacy destructured defaults to possibly missing properties": writeFileCase`
|
|
function persist({ filePath = "sessions.json" }) {
|
|
return writeFile(filePath, "{}\\n");
|
|
}
|
|
persist(ready ? {} : { filePath: currentPath });
|
|
`("legacy-destructured-parameter-default.ts", filesystemWriteViolations(6)),
|
|
"keeps safe defaults and defined branches safe": writeFileCase`
|
|
function persist(params = { filePath: currentPath }) {
|
|
return writeFile(params.filePath, "{}\\n");
|
|
}
|
|
const params = { filePath: sqlitePath };
|
|
persist(ready ? undefined : params);
|
|
`("safe-conditional-parameter-default.ts", []),
|
|
"does not treat custom writeFile methods as wrapper filesystem writes": sourceCase`
|
|
function persist(writer: { writeFile: (path: string, content: string) => void }, params: { filePath: string }) {
|
|
return writer.writeFile(params.filePath, "{}\\n");
|
|
}
|
|
persist(customWriter, { filePath: "sessions.json" });
|
|
`("custom-writer-method-wrapper.ts", []),
|
|
"does not use outer wrapper metadata for shadowed wrapper names": jsonPersistCase`
|
|
{
|
|
function persist(_options: { store: string }) {
|
|
return "current";
|
|
}
|
|
await persist({ store: "sessions.json" });
|
|
}
|
|
`("shadowed-wrapper-options.ts", []),
|
|
"does not let loop-scoped wrapper names shadow outer wrappers": jsonPersistCase`
|
|
for (const persist of handlers) {
|
|
await persist(currentOptions);
|
|
}
|
|
await persist({ store: "sessions.json" });
|
|
`("loop-scoped-wrapper-name.ts", filesystemWriteViolations(9)),
|
|
"does not use outer wrapper metadata for destructured parameter wrapper names":
|
|
jsonPersistCase`
|
|
function caller({ persist }: { persist: (options: { store: string }) => void }) {
|
|
persist({ store: "sessions.json" });
|
|
}
|
|
`("destructured-wrapper-name-parameter.ts", []),
|
|
"does not treat sibling object metadata as the wrapper path property": atomicCase`
|
|
function persist(params: { filePath: string }) {
|
|
return writeTextAtomic(params.filePath, "{}\\n");
|
|
}
|
|
const params = { label: "sessions.json", filePath: currentSqlitePath };
|
|
await persist(params);
|
|
`("current-path-sibling-metadata.ts", []),
|
|
"does not treat custom writeFile methods as direct filesystem writes": sourceCase`
|
|
const params = { filePath: "sessions.json" };
|
|
await customWriter.writeFile(params.filePath, "{}\\n");
|
|
`("custom-writer-method.ts", []),
|
|
"flags legacy paths written through injected fs handles": sourceCase`
|
|
const storePath = "sessions.json";
|
|
const params: { deps: { fs: typeof import("node:fs") } } = { deps };
|
|
await params.deps.fs.promises.writeFile(storePath, "{}\\n");
|
|
`("injected-fs-write.ts", filesystemWriteViolations(4)),
|
|
"does not treat custom fs properties as direct filesystem writes": sourceCase`
|
|
const storePath = "sessions.json";
|
|
await client.fs.promises.writeFile(storePath, "{}\\n");
|
|
`("custom-fs-property.ts", []),
|
|
"updates object path metadata after property assignment": atomicCase`
|
|
const params = { filePath: currentSqlitePath };
|
|
params.filePath = "sessions.json";
|
|
writeTextAtomic(params.filePath, "{}\\n");
|
|
`("assigned-object-path.ts", filesystemWriteViolations(5)),
|
|
"updates object path metadata after bracket property assignment": atomicCase`
|
|
const params = { filePath: currentSqlitePath };
|
|
params["filePath"] = "sessions.json";
|
|
writeTextAtomic(params["filePath"], "{}\\n");
|
|
`("bracket-assigned-object-path.ts", filesystemWriteViolations(5)),
|
|
"updates outer object path metadata after nested property assignment": atomicCase`
|
|
const params = { filePath: currentSqlitePath };
|
|
{
|
|
params.filePath = "sessions.json";
|
|
}
|
|
writeTextAtomic(params.filePath, "{}\\n");
|
|
`("nested-assigned-object-path.ts", filesystemWriteViolations(7)),
|
|
"flags nested property assignments forwarded through option objects": fsCase`
|
|
function persist({ paths: { filePath } }: { paths: { filePath: string } }) {
|
|
return fs.writeFile(filePath, "{}\\n");
|
|
}
|
|
const options = { paths: {} };
|
|
options.paths.filePath = "sessions.json";
|
|
await persist(options);
|
|
`("nested-wrapper-option-property-assignment.ts", filesystemWriteViolations(8)),
|
|
"flags nested property assignments read directly by filesystem writes": fsCase`
|
|
const options = { paths: {} };
|
|
options.paths.filePath = "sessions.json";
|
|
await fs.writeFile(options.paths.filePath, "{}\\n");
|
|
`("nested-direct-property-assignment.ts", filesystemWriteViolations(5)),
|
|
"flags nested parent object assignments forwarded through option objects": fsCase`
|
|
function persist({ paths: { filePath } }: { paths: { filePath: string } }) {
|
|
return fs.writeFile(filePath, "{}\\n");
|
|
}
|
|
const options = { paths: {} };
|
|
options.paths = { filePath: "sessions.json" };
|
|
await persist(options);
|
|
`("nested-wrapper-option-parent-assignment.ts", filesystemWriteViolations(8)),
|
|
"flags nested defaults after parent object property assignments": fsCase`
|
|
function persist({ paths: { filePath = "sessions.json" } }: { paths: { filePath?: string } }) {
|
|
return fs.writeFile(filePath, "{}\\n");
|
|
}
|
|
const options = {};
|
|
options.paths = {};
|
|
await persist(options);
|
|
`("nested-wrapper-option-parent-known-empty-assignment.ts", filesystemWriteViolations(8)),
|
|
"clears nested path metadata after parent object assignments": fsCase`
|
|
function persist({ paths: { filePath } }: { paths: { filePath: string } }) {
|
|
return fs.writeFile(filePath, "{}\\n");
|
|
}
|
|
const options = { paths: { filePath: "sessions.json" } };
|
|
options.paths = { filePath: "current-state.json" };
|
|
await persist(options);
|
|
`("nested-wrapper-option-parent-current-assignment.ts", []),
|
|
"keeps maybe missing nested properties after conditional parent object assignments": fsCase`
|
|
function persist({ paths: { filePath = "sessions.json" } }: { paths: { filePath?: string } }) {
|
|
return fs.writeFile(filePath, "{}\\n");
|
|
}
|
|
const options = { paths: { filePath: "state/openclaw.sqlite" } };
|
|
if (Math.random() > 0.5) {
|
|
options.paths = {};
|
|
}
|
|
await persist(options);
|
|
`("conditional-nested-parent-missing-property.ts", filesystemWriteViolations(10)),
|
|
"clears object path metadata after current-path assignment": atomicCase`
|
|
const params = { filePath: "sessions.json" };
|
|
params.filePath = currentSqlitePath;
|
|
writeTextAtomic(params.filePath, "{}\\n");
|
|
`("reassigned-object-path.ts", []),
|
|
"keeps legacy object metadata after conditional current-path assignment": atomicCase`
|
|
const params = { filePath: "sessions.json" };
|
|
if (ready) params.filePath = currentSqlitePath;
|
|
writeTextAtomic(params.filePath, "{}\\n");
|
|
`("conditional-current-object-path.ts", filesystemWriteViolations(5)),
|
|
"keeps maybe missing properties after conditional safe property assignments": fsCase`
|
|
const options = {};
|
|
if (ready) options.path = currentSqlitePath;
|
|
function writePath(filePath: string, { path = filePath } = {}) {
|
|
return fs.writeFile(path, "{}\\n");
|
|
}
|
|
await writePath("sessions.json", options);
|
|
`("conditional-safe-property-missing-default.ts", filesystemWriteViolations(8)),
|
|
"keeps legacy object metadata after loop current-path assignment": atomicCase`
|
|
const params = { filePath: "sessions.json" };
|
|
while (ready) params.filePath = currentSqlitePath;
|
|
writeTextAtomic(params.filePath, "{}\\n");
|
|
`("loop-current-object-path.ts", filesystemWriteViolations(5)),
|
|
"does not let for-loop object bindings clear outer object metadata": atomicCase`
|
|
const params = { filePath: "sessions.json" };
|
|
for (const params = { filePath: currentSqlitePath }; ready; advance()) {
|
|
await use(params);
|
|
}
|
|
writeTextAtomic(params.filePath, "{}\\n");
|
|
`("for-loop-object-shadow.ts", filesystemWriteViolations(7)),
|
|
"does not leak for-loop legacy object bindings after the loop": atomicCase`
|
|
const params = { filePath: currentSqlitePath };
|
|
for (const params = { filePath: "sessions.json" }; ready; advance()) {
|
|
await use(params);
|
|
}
|
|
writeTextAtomic(params.filePath, "{}\\n");
|
|
`("for-loop-legacy-object-shadow.ts", []),
|
|
"keeps legacy object metadata after conditional current-object assignment": atomicCase`
|
|
let params = { filePath: "sessions.json" };
|
|
if (ready) {
|
|
params = { filePath: currentSqlitePath };
|
|
}
|
|
writeTextAtomic(params.filePath, "{}\\n");
|
|
`("conditional-current-object.ts", filesystemWriteViolations(7)),
|
|
"clears object metadata after exhaustive current-object assignments": atomicCase`
|
|
let params = { filePath: "sessions.json" };
|
|
if (ready) {
|
|
params = { filePath: currentSqlitePath };
|
|
} else {
|
|
params = { filePath: currentSqlitePath };
|
|
}
|
|
writeTextAtomic(params.filePath, "{}\\n");
|
|
`("exhaustive-current-object.ts", []),
|
|
"keeps outer object metadata after optional exhaustive current-object assignments":
|
|
atomicCase`
|
|
let params = { filePath: "sessions.json" };
|
|
if (ready) {
|
|
if (mode === "a") {
|
|
params = { filePath: currentSqlitePath };
|
|
} else {
|
|
params = { filePath: currentSqlitePath };
|
|
}
|
|
}
|
|
writeTextAtomic(params.filePath, "{}\\n");
|
|
`("optional-exhaustive-current-object.ts", filesystemWriteViolations(11)),
|
|
"allows branch-local writes after nested exhaustive current-object assignments": atomicCase`
|
|
let params = { filePath: "sessions.json" };
|
|
if (ready) {
|
|
if (mode === "a") {
|
|
params = { filePath: currentSqlitePath };
|
|
} else {
|
|
params = { filePath: currentSqlitePath };
|
|
}
|
|
writeTextAtomic(params.filePath, "{}\\n");
|
|
}
|
|
`("branch-local-exhaustive-current-object.ts", []),
|
|
"keeps object metadata when one exhaustive branch keeps a legacy object": atomicCase`
|
|
let params = { filePath: "sessions.json" };
|
|
if (ready) {
|
|
params = { filePath: currentSqlitePath };
|
|
} else {
|
|
params = { filePath: "sessions.json" };
|
|
}
|
|
writeTextAtomic(params.filePath, "{}\\n");
|
|
`("exhaustive-mixed-object.ts", filesystemWriteViolations(9)),
|
|
"clears object property metadata after exhaustive current-path assignments": atomicCase`
|
|
const params = { filePath: "sessions.json" };
|
|
if (ready) {
|
|
params.filePath = currentSqlitePath;
|
|
} else {
|
|
params.filePath = currentSqlitePath;
|
|
}
|
|
writeTextAtomic(params.filePath, "{}\\n");
|
|
`("exhaustive-current-object-property.ts", []),
|
|
"keeps legacy object metadata after try-block current-object assignment": atomicCase`
|
|
let params = { filePath: "sessions.json" };
|
|
try {
|
|
maybeThrow();
|
|
params = { filePath: currentSqlitePath };
|
|
} catch {}
|
|
writeTextAtomic(params.filePath, "{}\\n");
|
|
`("try-current-object.ts", filesystemWriteViolations(8)),
|
|
"clears outer object path metadata after nested current-path assignment": atomicCase`
|
|
const params = { filePath: "sessions.json" };
|
|
{
|
|
params.filePath = currentSqlitePath;
|
|
}
|
|
writeTextAtomic(params.filePath, "{}\\n");
|
|
`("nested-reassigned-object-path.ts", []),
|
|
"allows in-branch writes after object property reassignment to the current path": atomicCase`
|
|
const params = { filePath: "sessions.json" };
|
|
if (ready) {
|
|
params.filePath = currentSqlitePath;
|
|
writeTextAtomic(params.filePath, "{}\\n");
|
|
}
|
|
`("branch-reassigned-object-property.ts", []),
|
|
"updates object path metadata after whole-object assignment": atomicCase`
|
|
let params = { filePath: currentSqlitePath };
|
|
params = { filePath: "sessions.json" };
|
|
writeTextAtomic(params.filePath, "{}\\n");
|
|
`("reassigned-object.ts", filesystemWriteViolations(5)),
|
|
"clears object path metadata after whole-object current-path assignment": atomicCase`
|
|
let params = { filePath: "sessions.json" };
|
|
params = { filePath: currentSqlitePath };
|
|
writeTextAtomic(params.filePath, "{}\\n");
|
|
`("reassigned-current-object.ts", []),
|
|
"flags legacy paths destructured from tracked object properties": atomicCase`
|
|
const params = { filePath: "sessions.json" };
|
|
const { filePath } = params;
|
|
writeTextAtomic(filePath, "{}\\n");
|
|
`("destructured-tracked-object-path.ts", filesystemWriteViolations(5)),
|
|
"flags legacy paths from nested destructured object properties": atomicCase`
|
|
const params = { nested: { filePath: "sessions.json" } };
|
|
const { nested: { filePath } } = params;
|
|
writeTextAtomic(filePath, "{}\\n");
|
|
`("nested-destructured-tracked-object-path.ts", filesystemWriteViolations(5)),
|
|
"uses tracked nested current paths before destructured defaults": atomicCase`
|
|
const params = { nested: { filePath: currentSqlitePath } };
|
|
const { nested: { filePath = "sessions.json" } } = params;
|
|
writeTextAtomic(filePath, "{}\\n");
|
|
`("nested-destructured-current-object-path.ts", []),
|
|
"flags nested defaults after conditional whole-object rewrites omit safe properties":
|
|
atomicCase`
|
|
function persist({ paths: { filePath = "sessions.json" } }: { paths: { filePath?: string } }) {
|
|
return writeTextAtomic(filePath, "{}\\n");
|
|
}
|
|
let options = { paths: { filePath: currentSqlitePath } };
|
|
if (ready) {
|
|
options = { paths: {} };
|
|
}
|
|
await persist(options);
|
|
`("conditional-whole-object-rewrite-nested-default.ts", filesystemWriteViolations(10)),
|
|
"flags nested defaults after conditional whole-object rewrites from known aliases":
|
|
atomicCase`
|
|
function persist({ paths: { filePath = "sessions.json" } }: { paths: { filePath?: string } }) {
|
|
return writeTextAtomic(filePath, "{}\\n");
|
|
}
|
|
const source = { paths: {} };
|
|
let options = { paths: { filePath: currentSqlitePath } };
|
|
if (ready) {
|
|
options = source;
|
|
}
|
|
await persist(options);
|
|
`("conditional-whole-object-rewrite-alias-nested-default.ts", filesystemWriteViolations(11)),
|
|
"flags legacy paths from destructured default values": atomicCase`
|
|
const params = {};
|
|
const { filePath = "sessions.json" } = params;
|
|
writeTextAtomic(filePath, "{}\\n");
|
|
`("destructured-default-object-path.ts", filesystemWriteViolations(5)),
|
|
"flags legacy paths from inline object destructured default values": atomicCase`
|
|
const { filePath = "sessions.json" } = {};
|
|
writeTextAtomic(filePath, "{}\\n");
|
|
`("inline-destructured-default-object-path.ts", filesystemWriteViolations(4)),
|
|
"flags legacy paths from inline destructured object descendants": atomicCase`
|
|
const { paths } = { paths: { filePath: "sessions.json" } };
|
|
writeTextAtomic(paths.filePath, "{}\\n");
|
|
`("inline-destructured-object-descendant-path.ts", filesystemWriteViolations(4)),
|
|
"flags legacy paths destructured from tracked nested properties": atomicCase`
|
|
const options = { paths: { filePath: "sessions.json" } };
|
|
const { filePath } = options.paths;
|
|
writeTextAtomic(filePath, "{}\\n");
|
|
`("destructured-tracked-nested-property-path.ts", filesystemWriteViolations(5)),
|
|
"does not force inline object destructured defaults after unknown spreads": atomicCase`
|
|
declare const defaults: { filePath?: string };
|
|
const { filePath = "sessions.json" } = { ...defaults };
|
|
writeTextAtomic(filePath, "{}\\n");
|
|
`("inline-destructured-default-unknown-spread.ts", []),
|
|
"flags destructured defaults from explicitly undefined tracked properties": atomicCase`
|
|
const params = { filePath: undefined };
|
|
const { filePath = "sessions.json" } = params;
|
|
writeTextAtomic(filePath, "{}\\n");
|
|
`("destructured-default-explicit-undefined-object-path.ts", filesystemWriteViolations(5)),
|
|
"flags wrapper defaults from destructured missing tracked properties": fsCase`
|
|
const params = {};
|
|
function persist({ path = "sessions.json" }: { path?: string }) {
|
|
return fs.writeFile(path, "{}\\n");
|
|
}
|
|
const { filePath } = params;
|
|
await persist({ path: filePath });
|
|
`("destructured-missing-property-wrapper-default.ts", filesystemWriteViolations(8)),
|
|
"flags wrapper defaults from inline destructured missing properties": fsCase`
|
|
function persist({ path = "sessions.json" }: { path?: string }) {
|
|
return fs.writeFile(path, "{}\\n");
|
|
}
|
|
const { filePath } = {};
|
|
await persist({ path: filePath });
|
|
`("inline-destructured-missing-property-wrapper-default.ts", filesystemWriteViolations(7)),
|
|
"flags destructured defaults from aliased undefined tracked properties": atomicCase`
|
|
const absent = undefined;
|
|
const params = { filePath: absent };
|
|
const { filePath = "sessions.json" } = params;
|
|
writeTextAtomic(filePath, "{}\\n");
|
|
`("destructured-default-aliased-undefined-object-path.ts", filesystemWriteViolations(6)),
|
|
"uses tracked object properties before destructured defaults": atomicCase`
|
|
const params = { filePath: currentSqlitePath };
|
|
const { filePath = "sessions.json" } = params;
|
|
writeTextAtomic(filePath, "{}\\n");
|
|
`("destructured-default-current-object-path.ts", []),
|
|
"flags wrapper shorthand options destructured from tracked object properties": atomicCase`
|
|
function persist(params: { filePath: string }) {
|
|
return writeTextAtomic(params.filePath, "{}\\n");
|
|
}
|
|
const params = { filePath: "sessions.json" };
|
|
const { filePath } = params;
|
|
persist({ filePath });
|
|
`("destructured-shorthand-wrapper-path.ts", filesystemWriteViolations(8)),
|
|
"does not treat unrelated property names as destructured wrapper paths": atomicCase`
|
|
function persist({ filePath }: { filePath: string }) {
|
|
return writeTextAtomic(current.filePath, "{}\\n");
|
|
}
|
|
await persist({ filePath: "sessions.json" });
|
|
`("unrelated-property-name-wrapper.ts", []),
|
|
"flags wrapper option paths forwarded through object aliases": jsonPersistCase`
|
|
const params = { store: "sessions.json" };
|
|
const forwarded = params;
|
|
await persist(forwarded);
|
|
`("forwarded-wrapper-options.ts", filesystemWriteViolations(8)),
|
|
"flags wrapper option paths forwarded through destructured object aliases": atomicCase`
|
|
function persist(params: { paths: { filePath: string } }) {
|
|
return writeTextAtomic(params.paths.filePath, "{}\\n");
|
|
}
|
|
const options = { paths: { filePath: "sessions.json" } };
|
|
const { paths } = options;
|
|
await persist({ paths });
|
|
`("wrapper-option-path-destructured-object-alias.ts", filesystemWriteViolations(8)),
|
|
"does not treat sibling nested properties as wrapper option paths": fsCase`
|
|
function persist(params: { paths: { filePath: string; legacyPath: string } }) {
|
|
return fs.writeFile(params.paths.filePath, "{}\\n");
|
|
}
|
|
const options = {
|
|
paths: {
|
|
filePath: "state/openclaw.sqlite",
|
|
legacyPath: "sessions.json",
|
|
},
|
|
};
|
|
await persist(options);
|
|
`("wrapper-option-path-nested-sibling-property.ts", []),
|
|
"clears nested wrapper option paths after object literal spread overwrites": fsCase`
|
|
function persist({ paths: { filePath = currentSqlitePath } = {} }) {
|
|
return fs.writeFile(filePath, "{}\\n");
|
|
}
|
|
const params = {
|
|
paths: { filePath: "sessions.json" },
|
|
...{ paths: {} },
|
|
};
|
|
await persist(params);
|
|
`("wrapper-option-path-object-spread-overwrite.ts", []),
|
|
"clears known nested wrapper option paths after parent rewrites": fsCase`
|
|
declare function loadNested(): { filePath?: string };
|
|
function persist({ paths: { nested: { filePath = "sessions.json" } = {} } }) {
|
|
return fs.writeFile(filePath, "{}\\n");
|
|
}
|
|
const options = { paths: { nested: {} } };
|
|
options.paths = { nested: loadNested() };
|
|
await persist(options);
|
|
`("wrapper-option-path-parent-rewrite-unknown-nested.ts", []),
|
|
"flags wrapper option paths forwarded through object spreads": jsonPersistCase`
|
|
const params = { store: "sessions.json" };
|
|
const forwarded = { ...params };
|
|
await persist(forwarded);
|
|
`("spread-wrapper-options.ts", filesystemWriteViolations(8)),
|
|
"flags wrapper option paths passed through inline object spreads": jsonPersistCase`
|
|
const params = { store: "sessions.json" };
|
|
await persist({ ...params });
|
|
await persist({ store: currentSqlitePath, ...params });
|
|
`("inline-spread-wrapper-options.ts", filesystemWriteViolations(7, 8)),
|
|
"flags wrapper option paths passed through inline object literal spreads": jsonPersistCase`
|
|
await persist({ ...{ store: "sessions.json" } });
|
|
const params = { ...{ store: "sessions.json" } };
|
|
await persist(params);
|
|
`("inline-object-literal-spread-wrapper-options.ts", filesystemWriteViolations(6, 8)),
|
|
"allows inline object spreads when a later property overrides the legacy path":
|
|
jsonPersistCase`
|
|
const params = { store: "sessions.json" };
|
|
await persist({ ...params, store: currentSqlitePath });
|
|
`("inline-spread-wrapper-options.ts", []),
|
|
"allows inline object literal spreads when a later property overrides the legacy path":
|
|
jsonPersistCase`
|
|
await persist({ ...{ store: "sessions.json" }, store: currentSqlitePath });
|
|
await persist({ store: "sessions.json", ...{ store: currentSqlitePath } });
|
|
`("inline-object-literal-spread-current-wrapper-options.ts", []),
|
|
"allows inline object spreads when a later spread overrides the legacy path": jsonPersistCase`
|
|
const currentOptions = { store: currentSqlitePath };
|
|
await persist({ store: "sessions.json", ...currentOptions });
|
|
`("inline-current-spread-wrapper-options.ts", []),
|
|
"allows nested inline object spreads when a later spread overrides the legacy path":
|
|
sourceCase`
|
|
import { writeJson } from "../infra/json-files.js";
|
|
function persist({ paths: { filePath } }: { paths: { filePath: string } }) {
|
|
return writeJson(filePath, {});
|
|
}
|
|
await persist({
|
|
paths: { filePath: "sessions.json" },
|
|
...{ paths: { filePath: currentSqlitePath } },
|
|
});
|
|
`("nested-inline-current-spread-wrapper-options.ts", []),
|
|
"does not copy wrapper option metadata from shadowed source objects": jsonPersistCase`
|
|
const params = { store: "sessions.json" };
|
|
{
|
|
const params = currentSqlitePath;
|
|
const forwarded = params;
|
|
await persist(forwarded);
|
|
}
|
|
`("shadowed-forwarded-wrapper-options.ts", []),
|
|
"does not treat shadowed fs alias names as wrapper filesystem writes": writeFileCase`
|
|
function persist(writeFile: (path: string, value: string) => void, params: { filePath: string }) {
|
|
return writeFile(params.filePath, "{}\\n");
|
|
}
|
|
await persist(customSink, { filePath: "sessions.json" });
|
|
`("shadowed-fs-alias-wrapper.ts", []),
|
|
"does not treat block-shadowed fs alias names as wrapper filesystem writes": writeFileCase`
|
|
{
|
|
const writeFile = customSink;
|
|
function persist(params: { filePath: string }) {
|
|
return writeFile(params.filePath, "{}\\n");
|
|
}
|
|
persist({ filePath: "sessions.json" });
|
|
}
|
|
`("block-shadowed-fs-alias-wrapper.ts", []),
|
|
"does not treat destructures from shadowed fs module names as wrapper filesystem writes":
|
|
fsPromisesCase`
|
|
function persist(params: { filePath: string }) {
|
|
const fs = customFs;
|
|
const { writeFile } = fs;
|
|
return writeFile(params.filePath, "{}\\n");
|
|
}
|
|
persist({ filePath: "sessions.json" });
|
|
`("shadowed-fs-module-wrapper.ts", []),
|
|
"does not treat shadowed wrapper parameter objects as argument paths": atomicPersistCase`
|
|
{
|
|
const params = { filePath: currentSqlitePath };
|
|
writeTextAtomic(params.filePath, "{}\\n");
|
|
}
|
|
`("shadowed-wrapper-parameter-object.ts", []),
|
|
"does not keep object metadata for uninitialized local shadows": atomicCase`
|
|
const params = { filePath: "sessions.json" };
|
|
{
|
|
let params;
|
|
writeTextAtomic(params.filePath, "{}\\n");
|
|
}
|
|
`("uninitialized-object-shadow.ts", []),
|
|
"keeps catch binding shadows scoped to the catch block": atomicCase`
|
|
const params = { filePath: "sessions.json" };
|
|
try {
|
|
await load();
|
|
} catch (params) {
|
|
writeTextAtomic(params.filePath, "{}\\n");
|
|
}
|
|
writeTextAtomic(params.filePath, "{}\\n");
|
|
`("catch-object-shadow.ts", filesystemWriteViolations(9)),
|
|
"keeps closed-over catch binding shadows scoped to the catch block": fsPromisesCase`
|
|
function persist(filePath: string) {
|
|
function inner() {
|
|
try {
|
|
await load();
|
|
} catch (filePath) {
|
|
await recover(filePath);
|
|
}
|
|
return fs.writeFile(filePath, "{}\\n");
|
|
}
|
|
return inner();
|
|
}
|
|
await persist("sessions.json");
|
|
`("nested-wrapper-catch-shadow.ts", filesystemWriteViolations(14)),
|
|
"keeps wrapper catch binding shadows scoped to the catch block": atomicCase`
|
|
function persist(params: { filePath: string }) {
|
|
try {
|
|
await load();
|
|
} catch (params) {
|
|
await recover(params);
|
|
}
|
|
writeTextAtomic(params.filePath, "{}\\n");
|
|
}
|
|
persist({ filePath: "sessions.json" });
|
|
`("wrapper-catch-object-shadow.ts", filesystemWriteViolations(11)),
|
|
"does not keep object metadata for destructured local shadows": atomicCase`
|
|
const params = { filePath: "sessions.json" };
|
|
{
|
|
const { params } = source;
|
|
writeTextAtomic(params.filePath, "{}\\n");
|
|
}
|
|
`("destructured-object-shadow.ts", []),
|
|
"does not let unrelated nested fs aliases mark custom writes": fsPromisesCase`
|
|
import { writeFile } from "./custom-writer.js";
|
|
await writeFile("sessions.json", "{}\\n");
|
|
function later() {
|
|
const { writeFile } = fs;
|
|
return writeFile(currentSqlitePath, "{}\\n");
|
|
}
|
|
`("custom-writer-shadow.ts", []),
|
|
"does not use caller block fs aliases for outer wrapper bodies": fsPromisesCase`
|
|
import { writeFile } from "./custom-writer.js";
|
|
function persist(params: { filePath: string }) {
|
|
return writeFile(params.filePath, "{}\\n");
|
|
}
|
|
{
|
|
const { writeFile } = fs;
|
|
await persist({ filePath: "sessions.json" });
|
|
}
|
|
`("caller-block-alias-wrapper.ts", []),
|
|
"does not leak block-scoped fs aliases across wrapper body scopes": fsPromisesPersistCase`
|
|
{
|
|
const { writeFile } = fs;
|
|
writeFile(currentSqlitePath, "{}\\n");
|
|
}
|
|
return writeFile(params.filePath, "{}\\n");
|
|
`("block-scoped-fs-alias-wrapper.ts", []),
|
|
"ignores shadowed destructured wrapper option names": atomicCase`
|
|
function persist({ filePath }: { filePath: string }) {
|
|
{
|
|
const filePath = currentSqlitePath;
|
|
writeTextAtomic(filePath, "{}\\n");
|
|
}
|
|
}
|
|
await persist({ filePath: "sessions.json" });
|
|
`("shadowed-destructured-wrapper-options.ts", []),
|
|
"keeps earlier destructured wrapper option uses before later shadowing": atomicCase`
|
|
function persist({ filePath }: { filePath: string }) {
|
|
writeTextAtomic(filePath, "{}\\n");
|
|
{
|
|
const filePath = currentSqlitePath;
|
|
}
|
|
}
|
|
await persist({ filePath: "sessions.json" });
|
|
`("late-shadowed-destructured-wrapper-options.ts", filesystemWriteViolations(9)),
|
|
"does not leak legacy path variable names across lexical scopes": fsCase`
|
|
{
|
|
const storePath = "sessions.json";
|
|
}
|
|
export async function save(storePath: string) {
|
|
await fs.writeFile(storePath, "{}\\n", "utf8");
|
|
}
|
|
`("current-store-writer.ts", []),
|
|
"lets inner bindings shadow outer legacy path variables": fsCase`
|
|
const storePath = "sessions.json";
|
|
{
|
|
const storePath = currentSqlitePath;
|
|
await fs.writeFile(storePath, "{}\\n", "utf8");
|
|
}
|
|
`("current-store-writer.ts", []),
|
|
"lets inner object properties shadow outer legacy path properties": atomicCase`
|
|
const params = { filePath: "sessions.json" };
|
|
{
|
|
const params = { filePath: currentSqlitePath };
|
|
await writeTextAtomic(params.filePath, "{}\\n");
|
|
}
|
|
`("current-store-writer.ts", []),
|
|
"ignores legacy filenames in write payloads": fsCase`
|
|
await fs.writeFile(reportPath, "sessions.json\\n", "utf8");
|
|
await fs.appendFile(currentLogPath, "cron/runs/job.jsonl\\n", "utf8");
|
|
`("report-writer.ts", []),
|
|
|
|
// Current-debt and migration-owner policy.
|
|
"flags runtime writes to sidecar SQLite and JSONL stores": sourceCase`
|
|
import fs from "node:fs";
|
|
fs.appendFileSync("cron/runs/job.jsonl", "{}\\n");
|
|
fs.writeFileSync("plugin-state/state.sqlite", "");
|
|
`("extensions/example/src/store.ts", filesystemWriteViolations(3, 4)),
|
|
"flags new writes in current legacy-debt files": sourceCase`
|
|
import fs from "node:fs";
|
|
fs.writeFileSync("sessions.json", "{}\\n");
|
|
`("extensions/memory-wiki/src/compile.ts", filesystemWriteViolations(3)),
|
|
}),
|
|
)("$name", ({ source, filename, expected }) => {
|
|
const violations = collectDatabaseFirstLegacyStoreViolations(source, filename);
|
|
|
|
expect(violations).toEqual(expected);
|
|
});
|
|
|
|
it("flags changed writes on current legacy-debt lines", () => {
|
|
const content = `import fs from "node:fs";${"\n".repeat(667)}fs.writeFileSync("sessions.json", "{}\\n");`;
|
|
const violations = collectDatabaseFirstLegacyStoreViolations(
|
|
content,
|
|
"extensions/memory-wiki/src/compile.ts",
|
|
);
|
|
|
|
expect(violations).toEqual(filesystemWriteViolations(668));
|
|
});
|
|
|
|
it("flags duplicate copies when one current legacy-debt write is allowed", () => {
|
|
const relativePath = "extensions/memory-wiki/src/compile.ts";
|
|
const allowedWrite = `fs.writeFileSync("sessions.json", "{}\\n")`;
|
|
const currentLegacyWriteAllowances = new Map([
|
|
[`${relativePath}:legacy store filesystem write:${allowedWrite}`, 1],
|
|
]);
|
|
const violations = collectDatabaseFirstLegacyStoreViolations(
|
|
[`import fs from "node:fs";`, `${allowedWrite};`, `${allowedWrite};`].join("\n"),
|
|
relativePath,
|
|
{ currentLegacyWriteAllowances },
|
|
);
|
|
|
|
expect(violations).toEqual(filesystemWriteViolations(3));
|
|
});
|
|
|
|
// Migration-owner allowlists and runtime exclusions.
|
|
it.each(
|
|
namedCases({
|
|
"allows doctor and migration owners to import or archive legacy files": fsCase`
|
|
await fs.rename("cron/jobs.json", "cron/jobs.json.migrated");
|
|
await fs.writeFile("sessions.json", "{}\\n", "utf8");
|
|
`("src/commands/doctor/cron/legacy-store-migration.ts", []),
|
|
"blocks runtime writes to the retired device identity file": sourceCase`
|
|
import fs from "node:fs";
|
|
fs.writeFileSync(path.join(stateDir, "identity/device.json"), "{}\\n");
|
|
`("src/infra/device-identity.ts", filesystemWriteViolations(3)),
|
|
"allows only the device identity migration owner to retire its legacy source": sourceCase`
|
|
import fs from "node:fs";
|
|
fs.renameSync(
|
|
path.join(stateDir, "identity/device.json"),
|
|
path.join(stateDir, "identity/device.json.doctor-importing"),
|
|
);
|
|
`("src/infra/state-migrations.device-identity.ts", []),
|
|
}),
|
|
)("$name", ({ source, filename, expected }) => {
|
|
const violations = collectDatabaseFirstLegacyStoreViolations(source, filename);
|
|
|
|
expect(violations).toEqual(expected);
|
|
});
|
|
|
|
it("keeps legacy PortGuardian filenames inside the native migration owner", () => {
|
|
const runtimeViolations = collectDatabaseFirstNativeLegacyStoreViolations(
|
|
'let path = root.appendingPathComponent("port-guard.json")\n',
|
|
"apps/macos/Sources/OpenClaw/PortGuardian.swift",
|
|
);
|
|
const migrationViolations = collectDatabaseFirstNativeLegacyStoreViolations(
|
|
'let path = root.appendingPathComponent("port-guard.json")\n',
|
|
"apps/macos/Sources/OpenClaw/PortGuardianRecordStore.swift",
|
|
);
|
|
|
|
expect(runtimeViolations).toEqual([{ kind: "legacy PortGuardian file reference", line: 1 }]);
|
|
expect(migrationViolations).toEqual([]);
|
|
});
|
|
|
|
// Doctor, plugin, QA, and transcript owner boundaries.
|
|
it.each(
|
|
namedCases({
|
|
"allows the workspace Doctor migration owner to claim legacy sidecars": fsCase`
|
|
await fs.rename("openclaw-workspace-state.json", "openclaw-workspace-state.json.doctor-importing");
|
|
await fs.rename("workspace.attested", "workspace.attested.doctor-importing");
|
|
`("src/infra/state-migrations.workspace-setup.ts", []),
|
|
"allows plugin doctor migration owners to archive legacy files": fsCase`
|
|
const statePath = "plugin-state/state.sqlite";
|
|
await fs.rename(statePath, "plugin-state/state.sqlite.migrated");
|
|
`("extensions/example/doctor-contract-api.ts", []),
|
|
"flags extension runtime writes under migration-like directories": fsCase`
|
|
await fs.writeFile("sessions.json", "{}\\n", "utf8");
|
|
`("extensions/example/src/migrations/runtime.ts", filesystemWriteViolations(3)),
|
|
"allows exact QA fixture owners to materialize legacy files": fsCase`
|
|
const authStorePath = "auth-profiles.json";
|
|
await fs.writeFile(authStorePath, "{}\\n", "utf8");
|
|
`("extensions/qa-lab/src/providers/shared/auth-store.ts", []),
|
|
"flags legacy transcript bridge markers in runtime source": sourceCase`
|
|
export const transcriptLocator = "sqlite-transcript://session";
|
|
export const dynamicLocator = \`sqlite-transcript://\${sessionId}\`;
|
|
`("transcript-bridge.ts", [
|
|
{ kind: "legacy transcript bridge marker", line: 2 },
|
|
{ kind: "legacy transcript bridge marker", line: 3 },
|
|
]),
|
|
}),
|
|
)("$name", ({ source, filename, expected }) => {
|
|
const violations = collectDatabaseFirstLegacyStoreViolations(source, filename);
|
|
|
|
expect(violations).toEqual(expected);
|
|
});
|
|
});
|