mirror of
https://github.com/Termix-SSH/Termix.git
synced 2026-10-28 06:05:24 +00:00
fix(electron): validate external login URL scheme and callback port (#1621)
This commit is contained in:
1 parent
d791e52d82
commit
8e5a50623c
3 files changed
+68
No files matched your search
@@ -0,0 +1,20 @@
|
|||||||
|
/**
|
||||||
|
* The login URL a server hands the desktop app goes to shell.openExternal,
|
||||||
|
* so only web pages pass: any other scheme (file:, smb:, ms-msdt:, ...)
|
||||||
|
* would launch whatever OS handler is registered for it.
|
||||||
|
*/
|
||||||
|
function isExternalLoginUrl(value) {
|
||||||
|
try {
|
||||||
|
const { protocol } = new URL(value);
|
||||||
|
return protocol === "https:" || protocol === "http:";
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** A non-privileged port for the loopback callback listener. */
|
||||||
|
function isCallbackPort(value) {
|
||||||
|
return Number.isInteger(value) && value >= 1024 && value <= 65535;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { isExternalLoginUrl, isCallbackPort };
|
||||||
@@ -36,6 +36,7 @@ const { quitApp } = require("./app-quit.cjs");
|
|||||||
const { selectLinuxPasswordStore } = require("./linux-password-store.cjs");
|
const { selectLinuxPasswordStore } = require("./linux-password-store.cjs");
|
||||||
const { resolveLocalShell, repairSpawnHelpers } = require("./local-shell.cjs");
|
const { resolveLocalShell, repairSpawnHelpers } = require("./local-shell.cjs");
|
||||||
const { registerLocalFileHandlers } = require("./local-files.cjs");
|
const { registerLocalFileHandlers } = require("./local-files.cjs");
|
||||||
|
const { isExternalLoginUrl, isCallbackPort } = require("./external-login.cjs");
|
||||||
|
|
||||||
const localTerminalSessions = new Map();
|
const localTerminalSessions = new Map();
|
||||||
|
|
||||||
@@ -1663,6 +1664,12 @@ ipcMain.handle("get-embedded-server-status", () => {
|
|||||||
ipcMain.handle(
|
ipcMain.handle(
|
||||||
"external-browser-login",
|
"external-browser-login",
|
||||||
async (_event, authUrl, callbackPort) => {
|
async (_event, authUrl, callbackPort) => {
|
||||||
|
if (!isExternalLoginUrl(authUrl)) {
|
||||||
|
return { success: false, error: "Invalid authentication URL" };
|
||||||
|
}
|
||||||
|
if (!isCallbackPort(callbackPort)) {
|
||||||
|
return { success: false, error: "Invalid callback port" };
|
||||||
|
}
|
||||||
const http = require("http");
|
const http = require("http");
|
||||||
|
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
import { createRequire } from "node:module";
|
||||||
|
import { describe, expect, it } from "vitest";
|
||||||
|
|
||||||
|
const require = createRequire(import.meta.url);
|
||||||
|
const { isExternalLoginUrl, isCallbackPort } =
|
||||||
|
require("../electron/external-login.cjs") as {
|
||||||
|
isExternalLoginUrl: (value: unknown) => boolean;
|
||||||
|
isCallbackPort: (value: unknown) => boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
describe("Electron external login", () => {
|
||||||
|
it.each([
|
||||||
|
"https://idp.example.com/authorize?client_id=termix",
|
||||||
|
"http://keycloak.lan:8080/realms/home/protocol/openid-connect/auth",
|
||||||
|
])("opens web login URLs: %s", (url) => {
|
||||||
|
expect(isExternalLoginUrl(url)).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it.each([
|
||||||
|
"file:///C:/Windows/System32/calc.exe",
|
||||||
|
"smb://attacker/share",
|
||||||
|
"ms-msdt:/id PCWDiagnostic",
|
||||||
|
"javascript:alert(1)",
|
||||||
|
"not a url",
|
||||||
|
undefined,
|
||||||
|
])("refuses other schemes: %s", (url) => {
|
||||||
|
expect(isExternalLoginUrl(url)).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("accepts the ports the login screen picks", () => {
|
||||||
|
expect(isCallbackPort(17832)).toBe(true);
|
||||||
|
expect(isCallbackPort(17931)).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it.each([0, 80, 65536, 17832.5, "17832", NaN, undefined])(
|
||||||
|
"refuses callback port %s",
|
||||||
|
(port) => {
|
||||||
|
expect(isCallbackPort(port)).toBe(false);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
Reference in new issue
Block a user