mirror of
https://github.com/Termix-SSH/Termix.git
synced 2026-10-29 06:35:24 +00:00
fix(desktop): harden external editor and session cookie IPC (#1627)
* fix(desktop): stop the renderer choosing the external editor binary or reading the session cookie open-external-editor now launches only the editor saved from a file dialog and refuses to hand executable file types to the OS opener. The unused get-session-cookie and wait-session-cookie channels, which returned the httpOnly jwt, are removed. * fix: validate setting keys before editor authorization
This commit is contained in:
1 parent
7d13b2a9fe
commit
25e58a6425
5 files changed
+200
-113
No files matched your search
@@ -32,9 +32,62 @@ describe("Electron security boundary", () => {
|
||||
}
|
||||
});
|
||||
|
||||
it("never hands the session cookie to the renderer over IPC", () => {
|
||||
for (const channel of ["get-session-cookie", "wait-session-cookie"]) {
|
||||
expect(main).not.toContain(`"${channel}"`);
|
||||
expect(preload).not.toContain(`"${channel}"`);
|
||||
}
|
||||
});
|
||||
|
||||
it("launches only the editor saved from a file dialog", () => {
|
||||
expect(main).not.toContain("fileData?.editorPath");
|
||||
expect(main).toContain("readSetting(EXTERNAL_EDITOR_SETTING)");
|
||||
expect(main).toContain("dialogChosenPaths.has(value)");
|
||||
});
|
||||
|
||||
it("scopes proxy headers and basic auth to the linked origin", () => {
|
||||
expect(main).toContain('"sync-proxy-config"');
|
||||
expect(main).toContain("linkedServer.applyLinkHeaders(");
|
||||
expect(main).toContain("linkedServer.answerLogin(");
|
||||
});
|
||||
});
|
||||
|
||||
it("rejects setting keys that coerce to the protected editor key", async () => {
|
||||
const { runInNewContext } = await import("node:vm");
|
||||
const { vi } = await import("vitest");
|
||||
const writeFileSync = vi.fn();
|
||||
let handler: (event: unknown, key: unknown, value: unknown) => unknown;
|
||||
runInNewContext(
|
||||
main.slice(
|
||||
main.indexOf('ipcMain.handle("set-setting"'),
|
||||
main.indexOf('ipcMain.handle("clear-session-cookies"'),
|
||||
),
|
||||
{
|
||||
ipcMain: {
|
||||
handle: (_: string, callback: typeof handler) => {
|
||||
handler = callback;
|
||||
},
|
||||
},
|
||||
EXTERNAL_EDITOR_SETTING: "fileManager.externalEditorPath",
|
||||
dialogChosenPaths: new Set(["/chosen/editor"]),
|
||||
app: { getPath: () => "/settings" },
|
||||
path: { join: (...parts: string[]) => parts.join("/") },
|
||||
fs: { existsSync: () => false, mkdirSync: vi.fn(), writeFileSync },
|
||||
console,
|
||||
},
|
||||
);
|
||||
for (const key of [["fileManager.externalEditorPath"], null, {}, 42]) {
|
||||
expect(handler!({}, key, "/unapproved/editor")).toEqual({
|
||||
success: false,
|
||||
error: "Invalid setting key",
|
||||
});
|
||||
}
|
||||
expect(writeFileSync).not.toHaveBeenCalled();
|
||||
expect(
|
||||
handler!({}, "fileManager.externalEditorPath", "/unapproved/editor"),
|
||||
).toEqual({ success: false, error: "Choose the editor in the file dialog" });
|
||||
expect(
|
||||
handler!({}, "fileManager.externalEditorPath", "/chosen/editor"),
|
||||
).toEqual({ success: true });
|
||||
expect(writeFileSync).toHaveBeenCalledOnce();
|
||||
});
|
||||
Reference in new issue
Block a user