Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions apps/desktop/src/app/DesktopApp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,11 @@ const startup = Effect.gen(function* () {
const updates = yield* DesktopUpdates.DesktopUpdates;
const environment = yield* DesktopEnvironment.DesktopEnvironment;

yield* shellEnvironment.installIntoProcess;
const startupSettings = yield* desktopSettings.load;
yield* shellEnvironment.installIntoProcess({
mode: startupSettings.shellEnvironmentMode,
names: startupSettings.shellEnvironmentNames,
});
const hasCommandLinePasswordStore =
preReadyElectronOptions.linuxPasswordStoreCommandLine !== null;
const linuxElectronOptions =
Expand All @@ -269,7 +273,6 @@ const startup = Effect.gen(function* () {
const userDataPath = yield* appIdentity.resolveUserDataPath;
yield* electronApp.setPath("userData", userDataPath);
yield* logStartupInfo("runtime logging configured", { logDir: environment.logDir });
yield* desktopSettings.load;

if (linuxElectronOptions !== null) {
yield* logStartupInfo("linux password store configured", {
Expand Down
61 changes: 61 additions & 0 deletions apps/desktop/src/settings/DesktopAppSettings.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ const DesktopSettingsPatch = Schema.Struct({
),
mainWindowMaximized: Schema.optionalKey(Schema.Boolean),
serverExposureMode: Schema.optionalKey(Schema.Literals(["local-only", "network-accessible"])),
shellEnvironmentMode: Schema.optionalKey(Schema.String),
shellEnvironmentNames: Schema.optionalKey(Schema.Array(Schema.Unknown)),
tailscaleServeEnabled: Schema.optionalKey(Schema.Boolean),
tailscaleServePort: Schema.optionalKey(Schema.Number),
updateChannel: Schema.optionalKey(Schema.Literals(["latest", "nightly"])),
Expand Down Expand Up @@ -109,6 +111,8 @@ describe("DesktopSettings", () => {
mainWindowBounds: null,
mainWindowMaximized: false,
serverExposureMode: "local-only",
shellEnvironmentMode: "allowlist",
shellEnvironmentNames: [],
tailscaleServeEnabled: false,
tailscaleServePort: 443,
updateChannel: "nightly",
Expand All @@ -127,6 +131,8 @@ describe("DesktopSettings", () => {
yield* writeSettingsPatch({
linuxPasswordStore: "gnome-libsecret",
serverExposureMode: "network-accessible",
shellEnvironmentMode: "allowlist",
shellEnvironmentNames: [],
tailscaleServeEnabled: true,
tailscaleServePort: 8443,
updateChannel: "latest",
Expand All @@ -138,6 +144,8 @@ describe("DesktopSettings", () => {
mainWindowBounds: null,
mainWindowMaximized: false,
serverExposureMode: "network-accessible",
shellEnvironmentMode: "allowlist",
shellEnvironmentNames: [],
tailscaleServeEnabled: true,
tailscaleServePort: 8443,
updateChannel: "latest",
Expand Down Expand Up @@ -245,6 +253,8 @@ describe("DesktopSettings", () => {
mainWindowBounds: { x: 120, y: 80, width: 1280, height: 900 },
mainWindowMaximized: false,
serverExposureMode: "network-accessible",
shellEnvironmentMode: "allowlist",
shellEnvironmentNames: [],
tailscaleServeEnabled: true,
tailscaleServePort: 8443,
updateChannel: "latest",
Expand Down Expand Up @@ -301,6 +311,8 @@ describe("DesktopSettings", () => {
mainWindowBounds: null,
mainWindowMaximized: false,
serverExposureMode: "network-accessible",
shellEnvironmentMode: "allowlist",
shellEnvironmentNames: [],
tailscaleServeEnabled: true,
tailscaleServePort: 8443,
updateChannel: "nightly",
Expand Down Expand Up @@ -349,6 +361,8 @@ describe("DesktopSettings", () => {
mainWindowBounds: null,
mainWindowMaximized: false,
serverExposureMode: "local-only",
shellEnvironmentMode: "allowlist",
shellEnvironmentNames: [],
tailscaleServeEnabled: false,
tailscaleServePort: 443,
updateChannel: "nightly",
Expand Down Expand Up @@ -377,6 +391,8 @@ describe("DesktopSettings", () => {
mainWindowBounds: null,
mainWindowMaximized: false,
serverExposureMode: "local-only",
shellEnvironmentMode: "allowlist",
shellEnvironmentNames: [],
tailscaleServeEnabled: false,
tailscaleServePort: 443,
updateChannel: "latest",
Expand Down Expand Up @@ -404,6 +420,8 @@ describe("DesktopSettings", () => {
mainWindowBounds: null,
mainWindowMaximized: false,
serverExposureMode: "local-only",
shellEnvironmentMode: "allowlist",
shellEnvironmentNames: [],
tailscaleServeEnabled: true,
tailscaleServePort: 443,
updateChannel: "latest",
Expand Down Expand Up @@ -491,6 +509,49 @@ describe("DesktopSettings", () => {
),
);

it.effect("defaults to the fixed login-shell allowlist", () =>
withSettings(
Effect.gen(function* () {
const settings = yield* DesktopAppSettings.DesktopAppSettings;
const loaded = yield* settings.load;
assert.equal(loaded.shellEnvironmentMode, "allowlist");
assert.deepEqual(loaded.shellEnvironmentNames, []);
}),
),
);

it.effect("loads the shell environment harvest and drops unusable names", () =>
withSettings(
Effect.gen(function* () {
const settings = yield* DesktopAppSettings.DesktopAppSettings;
yield* writeSettingsPatch({
shellEnvironmentMode: "all",
shellEnvironmentNames: ["OPENAI_API_KEY", "FOO; rm -rf /", "HOME", "OPENAI_API_KEY"],
});
const loaded = yield* settings.load;
assert.equal(loaded.shellEnvironmentMode, "all");
assert.deepEqual(loaded.shellEnvironmentNames, ["OPENAI_API_KEY"]);
}),
),
);

it.effect("keeps unrelated settings when the harvest values are invalid", () =>
withSettings(
Effect.gen(function* () {
const settings = yield* DesktopAppSettings.DesktopAppSettings;
yield* writeSettingsPatch({
serverExposureMode: "network-accessible",
shellEnvironmentMode: "everything",
shellEnvironmentNames: [42, "OPENAI_API_KEY"],
});
const loaded = yield* settings.load;
assert.equal(loaded.shellEnvironmentMode, "allowlist");
assert.deepEqual(loaded.shellEnvironmentNames, ["OPENAI_API_KEY"]);
assert.equal(loaded.serverExposureMode, "network-accessible");
}),
),
);

it.effect("drops invalid persisted wsl distro values on load", () =>
withSettings(
Effect.gen(function* () {
Expand Down
25 changes: 24 additions & 1 deletion apps/desktop/src/settings/DesktopAppSettings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,12 @@ import {
normalizeLinuxPasswordStorePreference,
type LinuxPasswordStorePreference,
} from "../linuxSecretStorage.ts";
import {
DEFAULT_SHELL_ENVIRONMENT_HARVEST,
normalizeShellEnvironmentMode,
normalizeShellEnvironmentNames,
type ShellEnvironmentMode,
} from "../shell/shellEnvironmentHarvest.ts";
import { resolveDefaultDesktopUpdateChannel } from "../updates/updateChannels.ts";
import { isValidDistroName } from "../wsl/wslPathParsing.ts";

Expand All @@ -29,6 +35,8 @@ export interface DesktopSettings {
readonly mainWindowBounds: DesktopWindowBounds | null;
readonly mainWindowMaximized: boolean;
readonly serverExposureMode: DesktopServerExposureMode;
readonly shellEnvironmentMode: ShellEnvironmentMode;
readonly shellEnvironmentNames: ReadonlyArray<string>;
readonly tailscaleServeEnabled: boolean;
readonly tailscaleServePort: number;
readonly updateChannel: DesktopUpdateChannel;
Expand Down Expand Up @@ -77,6 +85,8 @@ export const DEFAULT_DESKTOP_SETTINGS: DesktopSettings = {
mainWindowBounds: null,
mainWindowMaximized: false,
serverExposureMode: "local-only",
shellEnvironmentMode: DEFAULT_SHELL_ENVIRONMENT_HARVEST.mode,
shellEnvironmentNames: DEFAULT_SHELL_ENVIRONMENT_HARVEST.names,
tailscaleServeEnabled: false,
tailscaleServePort: DEFAULT_TAILSCALE_SERVE_PORT,
updateChannel: "latest",
Expand All @@ -98,6 +108,8 @@ const DesktopSettingsDocument = Schema.Struct({
mainWindowBounds: Schema.optionalKey(Schema.NullOr(DesktopWindowBoundsDocument)),
mainWindowMaximized: Schema.optionalKey(Schema.Boolean),
serverExposureMode: Schema.optionalKey(DesktopServerExposureModeSchema),
shellEnvironmentMode: Schema.optionalKey(Schema.Unknown),
shellEnvironmentNames: Schema.optionalKey(Schema.Unknown),
tailscaleServeEnabled: Schema.optionalKey(Schema.Boolean),
tailscaleServePort: Schema.optionalKey(Schema.Number),
updateChannel: Schema.optionalKey(DesktopUpdateChannelSchema),
Expand Down Expand Up @@ -207,6 +219,7 @@ export function normalizeMainWindowBounds(value: unknown): DesktopWindowBounds |
function normalizeDesktopSettingsDocument(
parsed: DesktopSettingsDocument,
appVersion: string,
platform: NodeJS.Platform,
): DesktopSettings {
const defaultSettings = resolveDefaultDesktopSettings(appVersion);
const mainWindowBounds = normalizeMainWindowBounds(parsed.mainWindowBounds);
Expand All @@ -229,6 +242,8 @@ function normalizeDesktopSettingsDocument(
mainWindowMaximized: mainWindowBounds !== null && parsed.mainWindowMaximized === true,
serverExposureMode:
parsed.serverExposureMode === "network-accessible" ? "network-accessible" : "local-only",
shellEnvironmentMode: normalizeShellEnvironmentMode(parsed.shellEnvironmentMode),
shellEnvironmentNames: normalizeShellEnvironmentNames(parsed.shellEnvironmentNames, platform),
tailscaleServeEnabled: parsed.tailscaleServeEnabled === true,
tailscaleServePort: normalizeTailscaleServePort(parsed.tailscaleServePort),
updateChannel: updateChannelConfiguredByUser
Expand Down Expand Up @@ -259,6 +274,12 @@ function toDesktopSettingsDocument(
if (settings.serverExposureMode !== defaults.serverExposureMode) {
document.serverExposureMode = settings.serverExposureMode;
}
if (settings.shellEnvironmentMode !== defaults.shellEnvironmentMode) {
document.shellEnvironmentMode = settings.shellEnvironmentMode;
}
if (settings.shellEnvironmentNames.length > 0) {
document.shellEnvironmentNames = settings.shellEnvironmentNames;
}
if (settings.tailscaleServeEnabled !== defaults.tailscaleServeEnabled) {
document.tailscaleServeEnabled = settings.tailscaleServeEnabled;
}
Expand Down Expand Up @@ -378,6 +399,7 @@ function readSettings(
fileSystem: FileSystem.FileSystem,
settingsPath: string,
appVersion: string,
platform: NodeJS.Platform,
): Effect.Effect<DesktopSettings> {
const defaultSettings = resolveDefaultDesktopSettings(appVersion);

Expand All @@ -388,7 +410,7 @@ function readSettings(
onNone: () => Effect.succeed(defaultSettings),
onSome: (raw) =>
decodeDesktopSettingsJson(raw).pipe(
Effect.map((parsed) => normalizeDesktopSettingsDocument(parsed, appVersion)),
Effect.map((parsed) => normalizeDesktopSettingsDocument(parsed, appVersion, platform)),
Effect.orElseSucceed(() => defaultSettings),
),
}),
Expand Down Expand Up @@ -504,6 +526,7 @@ export const make = Effect.gen(function* () {
fileSystem,
environment.desktopSettingsPath,
environment.appVersion,
environment.platform,
);
return yield* SynchronizedRef.setAndGet(settingsRef, settings);
}).pipe(Effect.withSpan("desktop.settings.load")),
Expand Down
Loading
Loading