diff --git a/apps/server/src/policy/ManagedPolicy.ts b/apps/server/src/policy/ManagedPolicy.ts index 15167dedd38c2..01651051893c0 100644 --- a/apps/server/src/policy/ManagedPolicy.ts +++ b/apps/server/src/policy/ManagedPolicy.ts @@ -185,7 +185,10 @@ const make = Effect.gen(function* () { const policy = yield* Option.match(devPath, { onSome: load, onNone: () => - readManagedPolicy(platform).pipe( + readManagedPolicy( + platform, + ManagedProviders.knownDriverLabels().map(({ driver }) => driver), + ).pipe( Effect.flatMap((found) => fromDocument(found?.source ?? managedPolicyPath(platform), found?.document), ), diff --git a/packages/shared/src/managedPolicyFile.test.ts b/packages/shared/src/managedPolicyFile.test.ts index 1ea2c24386388..3d20710a4b8be 100644 --- a/packages/shared/src/managedPolicyFile.test.ts +++ b/packages/shared/src/managedPolicyFile.test.ts @@ -208,7 +208,7 @@ describe("readManagedPolicy", () => { Effect.gen(function* () { const organization = text("Organization", "Société 日本 ✓"); const document = (registry: RegistryAnswer) => - readManagedPolicy("win32").pipe( + readManagedPolicy("win32", ["codex", "claudeAgent"]).pipe( Effect.map((found): unknown => found?.document), Effect.catchTags({ ManagedPolicyFileError: (error) => Effect.succeed(`error: ${error.reason}`), @@ -233,7 +233,10 @@ describe("readManagedPolicy", () => { dword("AllowedDrivers", 1), ], // Registry names ignore case, so a subkey written in other case still counts. - { clients: { desktop: 1, remote: 0 }, AllowedDrivers: { codex: 1, claudeAgent: 1 } }, + { + clients: { Desktop: 1, "LOCAL-BROWSER": 1, REMOTE: 1 }, + AllowedDrivers: { Codex: 1, CLAUDEAGENT: 1, UnknownDriver: 1 }, + }, ), ), { @@ -244,8 +247,8 @@ describe("readManagedPolicy", () => { maxRuntimeMode: "auto-accept-edits", telemetry: false, updates: { mode: "off", allowNightly: true }, - clients: ["desktop"], - providers: { allowedDrivers: ["codex", "claudeAgent"] }, + clients: ["desktop", "local-browser", "remote"], + providers: { allowedDrivers: ["codex", "claudeAgent", "UnknownDriver"] }, }, }, ); @@ -295,6 +298,10 @@ describe("readManagedPolicy", () => { ], [policyKey([organization, text("Settings", "{")]), "not valid JSON"], [policyKey([organization, dword("Clients", 2)]), "DWORD of 0 or 1"], + [ + policyKey([organization, dword("Clients", 1)], { Clients: { UnknownClient: 1 } }), + "does not match the policy schema", + ], [ policyKey([organization, dword("Clients", 1)], { Clients: { desktop: 0 } }), "does not match the policy schema", diff --git a/packages/shared/src/managedPolicyFile.ts b/packages/shared/src/managedPolicyFile.ts index 24f34381937b9..c11169e6107b6 100644 --- a/packages/shared/src/managedPolicyFile.ts +++ b/packages/shared/src/managedPolicyFile.ts @@ -1,4 +1,4 @@ -import { ManagedPolicyDocument } from "@t3tools/contracts"; +import { ManagedPolicyClient, ManagedPolicyDocument } from "@t3tools/contracts"; import * as Cause from "effect/Cause"; import * as Context from "effect/Context"; import * as Duration from "effect/Duration"; @@ -369,6 +369,7 @@ const dwordBoolean = (value: RegistryValue) => */ function windowsPolicyDocument( values: ReadonlyArray, + knownDrivers: ReadonlyArray, ): { readonly document: unknown } | { readonly error: string } { // Registry names ignore case; a hand-written `organization` is still Organization. const canonical = new Map( @@ -407,7 +408,12 @@ function windowsPolicyDocument( if (checked === undefined) return { error: `${where} must be a DWORD of 0 or 1` }; const list = lists.get(value.key) ?? []; lists.set(value.key, list); - if (checked) list.push(value.name); + if (checked) { + const names = value.key === "Clients" ? ManagedPolicyClient.literals : knownDrivers; + list.push( + names.find((name) => name.toLowerCase() === value.name.toLowerCase()) ?? value.name, + ); + } continue; } if (Object.hasOwn(WINDOWS_POLICY_GROUPS, value.name)) continue; @@ -476,7 +482,10 @@ function unwrapForced(json: string): unknown { } /** Read the policy a configuration profile or Group Policy set; absent means no such policy. */ -const readPlatformPolicy = Effect.fnUntraced(function* (platform: NodeJS.Platform) { +const readPlatformPolicy = Effect.fnUntraced(function* ( + platform: NodeJS.Platform, + knownDrivers: ReadonlyArray, +) { if (platform === "darwin") { const path = MACOS_MANAGED_PREFERENCES_PATH; const exists = yield* (yield* FileSystem.FileSystem) @@ -543,7 +552,7 @@ const readPlatformPolicy = Effect.fnUntraced(function* (platform: NodeJS.Platfor } values.push(...checkboxes.values.map((value) => ({ ...value, key: list }))); } - const built = windowsPolicyDocument(values); + const built = windowsPolicyDocument(values, knownDrivers); if ("error" in built) return yield* refuse(built.error); // An empty key, or one holding only policies set to Disabled, sets nothing. if (built.document === undefined) return undefined; @@ -559,10 +568,11 @@ const readPlatformPolicy = Effect.fnUntraced(function* (platform: NodeJS.Platfor */ export const readManagedPolicy = Effect.fn("readManagedPolicy")(function* ( platform: NodeJS.Platform, + knownDrivers: ReadonlyArray = [], ) { const filePath = managedPolicyPath(platform); const [platformPolicy, file] = yield* Effect.all([ - readPlatformPolicy(platform), + readPlatformPolicy(platform, knownDrivers), readManagedPolicyFile(filePath), ]); if (platformPolicy !== undefined && file !== undefined) {