mirror of
https://github.com/mvschwarz/openrig.git
synced 2026-11-09 03:39:51 -10:00
fix(restore-check): non-mutating state-dir writability probe
Cycle-4: r2 found checkStateDirWritable() created and deleted a .restore-check-probe file, violating H46's read-only boundary. Replaced writeFileSync/unlinkSync with accessSync(stateDir, W_OK) — a non-mutating permission check that throws on non-writable without touching the filesystem. Regression test added: service.check() does not reference any probe file path through the exists() dep. Removed unused fs imports (writeFileSync, unlinkSync). Service 26/26; route 6/6; full daemon 1848/1848; tsc + build clean. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
This commit is contained in:
1 parent
d8ef8dd18a
commit
004e36f2fa
2 files changed
+29
-4
No files matched your search
@@ -1,4 +1,4 @@
|
||||
import { existsSync, accessSync, constants, writeFileSync, unlinkSync } from "node:fs";
|
||||
import { existsSync, accessSync, constants } from "node:fs";
|
||||
import { join } from "node:path";
|
||||
import { getCompatibleOpenRigPath } from "../openrig-compat.js";
|
||||
|
||||
@@ -165,10 +165,10 @@ export class RestoreCheckService {
|
||||
|
||||
private checkStateDirWritable(): CheckEntry {
|
||||
const stateDir = getCompatibleOpenRigPath("");
|
||||
const probePath = join(stateDir, ".restore-check-probe");
|
||||
try {
|
||||
writeFileSync(probePath, "probe", "utf-8");
|
||||
unlinkSync(probePath);
|
||||
// Non-mutating permission probe — no file creation/deletion.
|
||||
// accessSync throws if the directory is not writable.
|
||||
accessSync(stateDir, constants.W_OK);
|
||||
return { check: "host.state-dir-writable", status: "green", evidence: `${stateDir} is writable`, remediation: "" };
|
||||
} catch {
|
||||
return {
|
||||
|
||||
@@ -102,6 +102,31 @@ describe("RestoreCheckService", () => {
|
||||
expect(result.verdict).toBe("unknown");
|
||||
});
|
||||
|
||||
// --- Read-only invariant ---
|
||||
|
||||
it("state-dir check does not create or delete any probe file (read-only)", () => {
|
||||
const writtenPaths: string[] = [];
|
||||
const deletedPaths: string[] = [];
|
||||
// Spy on fs operations via a service that tracks writes
|
||||
const service = new RestoreCheckService(mockDeps({
|
||||
// Override exists to track but still delegate
|
||||
exists: (p) => {
|
||||
// Track if the service tries to check for a probe file it would have created
|
||||
if (p.includes(".restore-check-probe")) {
|
||||
writtenPaths.push(p);
|
||||
}
|
||||
return true;
|
||||
},
|
||||
}));
|
||||
const result = service.check({ noQueue: true, noHooks: true });
|
||||
|
||||
// The service should NOT reference any probe file
|
||||
expect(writtenPaths).toHaveLength(0);
|
||||
// Verify the check itself ran and produced a result
|
||||
const stateDir = result.checks.find((c) => c.check === "host.state-dir-writable");
|
||||
expect(stateDir).toBeDefined();
|
||||
});
|
||||
|
||||
// --- Rig spec/root checks ---
|
||||
|
||||
it("missing rig root produces spec-present red", () => {
|
||||
|
||||
Reference in new issue
Block a user