Skip to content

Commit 1930d73

Browse files
committed
Merge pull request #544 from ndycode/claude/audit-26-isrecord-fix
fix(runtime): reject array helper-status files; dedupe isRecord
2 parents 4cb9624 + 63f8b75 commit 1930d73

3 files changed

Lines changed: 7 additions & 22 deletions

File tree

lib/runtime/runtime-current-account.ts

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import type { AppBindRouterStatus } from "./app-bind.js";
66
import { APP_RUNTIME_HELPER_STATUS_FILE } from "../runtime-constants.js";
77
import { getCodexMultiAuthDir } from "../runtime-paths.js";
88
import type { AccountStorageV3 } from "../storage.js";
9+
import { isRecord } from "../utils.js";
910

1011
export type RuntimeCurrentAccountSource =
1112
| "runtime-observability"
@@ -97,10 +98,6 @@ function normalizeTimestamp(signal: RuntimeAccountSignal): number | null {
9798
return timestamps.length > 0 ? Math.max(...timestamps) : null;
9899
}
99100

100-
function isRecord(value: unknown): value is Record<string, unknown> {
101-
return typeof value === "object" && value !== null;
102-
}
103-
104101
function readOptionalNumber(record: Record<string, unknown>, key: string): number | null {
105102
const value = record[key];
106103
return typeof value === "number" && Number.isFinite(value) ? value : null;

test/runtime-current-account.test.ts

Lines changed: 4 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -535,23 +535,10 @@ describe("readAppRuntimeHelperStatus", () => {
535535
});
536536
});
537537

538-
it("treats a JSON array as a record and yields an all-null status", async () => {
539-
// Pins current behavior: isRecord() accepts arrays, so `[]` produces a
540-
// fully-null status object instead of null. Downstream
541-
// appRuntimeHelperStatusToSignal still rejects it via the kind check.
538+
it("rejects a JSON array status file as not-a-record", async () => {
539+
// isRecord() excludes arrays: an `[]` helper-status file is malformed
540+
// content, not an all-null status object.
542541
await writeStatusFile("[]");
543-
const status = readAppRuntimeHelperStatus();
544-
expect(status).toEqual({
545-
kind: null,
546-
state: null,
547-
pid: null,
548-
lastAccountIndex: null,
549-
lastAccountLabel: null,
550-
lastAccountEmail: null,
551-
lastAccountId: null,
552-
lastAccountUpdatedAt: null,
553-
updatedAt: null,
554-
});
555-
expect(appRuntimeHelperStatusToSignal(status)).toBeNull();
542+
expect(readAppRuntimeHelperStatus()).toBeNull();
556543
});
557544
});

test/snapshot-inspectors.test.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { describe, expect, it, vi } from "vitest";
22
import { describeAccountsWalSnapshot } from "../lib/storage/snapshot-inspectors.js";
33
import type { SnapshotStats } from "../lib/storage/backup-metadata.js";
4+
import { isRecord } from "../lib/utils.js";
45

56
type WalDeps = Parameters<typeof describeAccountsWalSnapshot>[1];
67

@@ -36,7 +37,7 @@ function createDeps(overrides: Partial<WalDeps> = {}): WalDeps {
3637
readFile: vi.fn(async () =>
3738
makeJournalEntry(makeInnerStorage()),
3839
) as unknown as WalDeps["readFile"],
39-
isRecord: (value: unknown) => typeof value === "object" && value !== null,
40+
isRecord,
4041
computeSha256: fakeSha256,
4142
parseAndNormalizeStorage: vi.fn((data: unknown) => ({
4243
normalized: { accounts: (data as { accounts: unknown[] }).accounts },

0 commit comments

Comments
 (0)