Skip to content

Commit 320814a

Browse files
committed
fix(win): record the fsync attempt and degrade coordination-path ACL stalls
Real Windows exposed two seams the win32 test override never reached: the directory-fsync recorder ran only after a directory handle opened, which Windows refuses, so the ordering seam now records the best-effort attempt before the platform layer decides; and a genuine icacls stall on the config-mutation coordination directory crashed server startup through the new fail-closed path, so coordination directories now warn once and degrade while secret-bearing files keep failing closed.
1 parent 675eb6a commit 320814a

4 files changed

Lines changed: 25 additions & 5 deletions

File tree

src/config.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import {
1818
hardenSecretDir,
1919
hardenSecretPath,
2020
hardenSecretPathAsync,
21+
windowsSecretAclApplies,
2122
} from "./lib/windows-secret-acl";
2223
import { recordOwnedConfigPath } from "./lib/config-ownership";
2324
import { assertNotRealHomeUnderTest } from "./lib/test-home-guard";
@@ -1506,6 +1507,7 @@ export function readConfigDiagnostics(): ConfigDiagnostics {
15061507

15071508
const CONFIG_MUTATION_DB_FILENAME = "config-mutation.sqlite";
15081509
const CONFIG_MUTATION_DB_SIDECARS = ["-journal", "-wal", "-shm"] as const;
1510+
let warnedConfigMutationDirectoryAcl = false;
15091511

15101512
export class ConfigMutationLockError extends Error {
15111513
readonly code = "CONFIG_MUTATION_LOCK_UNAVAILABLE";
@@ -1526,8 +1528,18 @@ function configMutationDatabasePath(): string {
15261528
} else {
15271529
try { chmodSync(dir, 0o700); } catch { /* best-effort on existing dir */ }
15281530
}
1529-
if (process.platform === "win32") {
1530-
hardenSecretDir(dir, { required: true });
1531+
if (windowsSecretAclApplies()) {
1532+
try {
1533+
hardenSecretDir(dir, { required: true });
1534+
} catch (error) {
1535+
if (!warnedConfigMutationDirectoryAcl) {
1536+
warnedConfigMutationDirectoryAcl = true;
1537+
const diagnostics = error instanceof Error ? error.message : "ACL hardening failed";
1538+
console.warn(
1539+
`[opencodex] Config mutation coordination directory ACL hardening did not complete; continuing without it. ${diagnostics}`,
1540+
);
1541+
}
1542+
}
15311543
}
15321544
const path = join(dir, CONFIG_MUTATION_DB_FILENAME);
15331545
recordOwnedConfigPath(dir, path);

src/lib/windows-secret-acl.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,11 @@ function effectivePlatform(): string {
182182
return platformOverride ?? platform;
183183
}
184184

185+
/** Test-aware platform predicate for callers that must avoid even a no-op ACL call. */
186+
export function windowsSecretAclApplies(): boolean {
187+
return (platformOverride ?? process.platform) === "win32";
188+
}
189+
185190
/** Error carrying an honest code: ETIMEDOUT only for real timeouts, EICACLS otherwise. */
186191
function icaclsError(step: string, result: IcaclsResult): NodeJS.ErrnoException {
187192
const err = new Error(

src/responses/spill-store.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,11 +82,13 @@ function record(event: "write" | "fsync" | "close" | "harden" | "publish" | "dir
8282

8383
function fsyncDirectoryBestEffort(dir: string): void {
8484
let fd: number | null = null;
85+
// Record the durability attempt before opening the directory: Windows cannot
86+
// open/fsync directory handles this way, but callers still cross this seam.
87+
record("dir-fsync");
8588
try {
8689
fd = openSync(dir, "r");
8790
if (spillIoForTest?.fsync) spillIoForTest.fsync(fd);
8891
else fsyncSync(fd);
89-
record("dir-fsync");
9092
} catch {
9193
// Windows and some filesystems do not support fsync on directory handles.
9294
} finally {

tests/config.test.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1619,15 +1619,16 @@ describe("config.ts – Windows ACL hardening integration", () => {
16191619
}
16201620
});
16211621

1622-
test("saveConfig throws when hardenSecretDir fails in required mode on win32", () => {
1622+
test("saveConfig degrades when config-mutation directory hardening fails on win32", () => {
16231623
const origPlatform = process.platform;
16241624
Object.defineProperty(process, "platform", { value: "win32", configurable: true });
16251625
try {
16261626
const spy = spyOn(windowsAcl, "hardenSecretDir").mockImplementation((_path, opts) => {
16271627
if (opts?.required) throw new Error("ACL hardening failed: access denied");
16281628
return { ok: true };
16291629
});
1630-
expect(() => saveConfig(getDefaultConfig())).toThrow(/ACL/i);
1630+
expect(() => saveConfig(getDefaultConfig())).not.toThrow();
1631+
expect(existsSync(getConfigPath())).toBe(true);
16311632
spy.mockRestore();
16321633
} finally {
16331634
Object.defineProperty(process, "platform", { value: origPlatform, configurable: true });

0 commit comments

Comments
 (0)