Inspect pasted links, text files and DLC containers before queue insertion and resolve supported hoster metadata without starting downloads. Group multipart files into expandable packages with size, status, availability, timestamps, filtering and stable selection. Add selected or complete transfer to Downloads while retaining entries on failed or partial handoff. Keep collector toolbar spacing consistent and show the full 1Fichier identity with its provider icon in Downloads.
120 lines
5.8 KiB
TypeScript
120 lines
5.8 KiB
TypeScript
import path from "node:path";
|
|
import { pathToFileURL } from "node:url";
|
|
import { describe, expect, it } from "vitest";
|
|
import { assertTrustedIpcSender } from "../src/main/ipc-security";
|
|
import { validateRealDebridLoginRequest } from "../src/shared/preload-api";
|
|
import { validateCollectorContainerInspectionRequest, validateCollectorInspectionRequest } from "../src/shared/collector";
|
|
|
|
function eventFor(url: string) {
|
|
return {
|
|
senderFrame: { url },
|
|
sender: {
|
|
getURL: () => url
|
|
}
|
|
};
|
|
}
|
|
|
|
describe("ipc-security", () => {
|
|
it("accepts only opaque Real-Debrid browser account login requests", () => {
|
|
expect(validateRealDebridLoginRequest({ accountId: "rdw_existing" })).toEqual({ accountId: "rdw_existing", create: false, dailyLimitBytes: 0 });
|
|
expect(validateRealDebridLoginRequest({ accountId: "rdw_reserved", create: true, dailyLimitBytes: 12_345.9 })).toEqual({ accountId: "rdw_reserved", create: true, dailyLimitBytes: 12_345 });
|
|
expect(() => validateRealDebridLoginRequest({ accountId: "../shared", create: true })).toThrow(/Account-Payload/i);
|
|
expect(() => validateRealDebridLoginRequest({ accountId: "rdw_valid", create: "yes" })).toThrow(/Account-Payload/i);
|
|
expect(() => validateRealDebridLoginRequest({ accountId: "rdw_valid", create: false, token: "secret" })).toThrow(/Account-Payload/i);
|
|
expect(() => validateRealDebridLoginRequest({ accountId: "rdw_valid", create: true, dailyLimitBytes: -1 })).toThrow(/Account-Payload/i);
|
|
expect(() => validateRealDebridLoginRequest({ accountId: "rdw_valid", create: true, dailyLimitBytes: Number.MAX_SAFE_INTEGER + 1 })).toThrow(/Account-Payload/i);
|
|
expect(() => validateRealDebridLoginRequest({ accountId: "rdw_existing", dailyLimitBytes: 1 })).toThrow(/Account-Payload/i);
|
|
});
|
|
|
|
it("accepts only bounded collector inspection payloads", () => {
|
|
expect(validateCollectorInspectionRequest({ rawText: "https://1fichier.com/?abc12345", addedAt: 1234 })).toEqual({
|
|
rawText: "https://1fichier.com/?abc12345",
|
|
addedAt: 1234
|
|
});
|
|
expect(() => validateCollectorInspectionRequest({ rawText: "x", addedAt: 1, token: "secret" })).toThrow(/Linksammler-Payload/i);
|
|
expect(() => validateCollectorInspectionRequest({ rawText: "x".repeat(2_000_001), addedAt: 1 })).toThrow(/Linksammler-Payload/i);
|
|
expect(() => validateCollectorInspectionRequest({ rawText: "x", addedAt: -1 })).toThrow(/Linksammler-Payload/i);
|
|
expect(() => validateCollectorInspectionRequest({ rawText: "x", addedAt: Number.MAX_SAFE_INTEGER + 1 })).toThrow(/Linksammler-Payload/i);
|
|
});
|
|
|
|
it("accepts only bounded absolute collector container paths", () => {
|
|
expect(validateCollectorContainerInspectionRequest(["C:\\Imports\\one.dlc", "D:\\two.dlc"], 1234)).toEqual({
|
|
filePaths: ["C:\\Imports\\one.dlc", "D:\\two.dlc"],
|
|
addedAt: 1234
|
|
});
|
|
expect(() => validateCollectorContainerInspectionRequest(["relative.dlc"], 1)).toThrow(/Container-Payload/i);
|
|
expect(() => validateCollectorContainerInspectionRequest(["C:\\Imports\\one.txt"], 1)).toThrow(/Container-Payload/i);
|
|
expect(() => validateCollectorContainerInspectionRequest(Array.from({ length: 101 }, (_unused, index) => `C:\\${index}.dlc`), 1)).toThrow(/Container-Payload/i);
|
|
});
|
|
|
|
it("accepts IPC from the configured Vite development renderer origin", () => {
|
|
expect(() => assertTrustedIpcSender(eventFor("http://localhost:5180/settings"), {
|
|
isPackaged: false,
|
|
devServerUrl: "http://localhost:5180",
|
|
appPath: "C:\\Program Files\\MDD"
|
|
})).not.toThrow();
|
|
});
|
|
|
|
it("rejects IPC from development-origin lookalikes", () => {
|
|
expect(() => assertTrustedIpcSender(eventFor("http://localhost.evil.example:5180/settings"), {
|
|
isPackaged: false,
|
|
devServerUrl: "http://localhost:5180",
|
|
appPath: "C:\\Program Files\\MDD"
|
|
})).toThrow("IPC-Absender ist nicht vertrauenswürdig");
|
|
});
|
|
|
|
it("accepts IPC from the packaged renderer file tree", () => {
|
|
const appPath = path.join("C:", "Program Files", "MDD", "resources", "app.asar");
|
|
const rendererUrl = pathToFileURL(path.join(appPath, "build", "renderer", "index.html")).toString();
|
|
|
|
expect(() => assertTrustedIpcSender(eventFor(rendererUrl), {
|
|
isPackaged: true,
|
|
devServerUrl: "http://localhost:5180",
|
|
appPath
|
|
})).not.toThrow();
|
|
});
|
|
|
|
it("accepts packaged renderer IPC despite Windows path casing differences", () => {
|
|
const platform = Object.getOwnPropertyDescriptor(process, "platform");
|
|
Object.defineProperty(process, "platform", { configurable: true, value: "win32" });
|
|
try {
|
|
const appPath = path.join("C:", "Program Files", "MDD", "resources", "app.asar");
|
|
const rendererUrl = "file:///c:/program%20files/mdd/resources/app.asar/build/renderer/index.html";
|
|
|
|
expect(() => assertTrustedIpcSender(eventFor(rendererUrl), {
|
|
isPackaged: true,
|
|
devServerUrl: "http://localhost:5180",
|
|
appPath
|
|
})).not.toThrow();
|
|
} finally {
|
|
if (platform) {
|
|
Object.defineProperty(process, "platform", platform);
|
|
}
|
|
}
|
|
});
|
|
|
|
it("rejects IPC from local files outside the packaged renderer tree", () => {
|
|
const appPath = path.join("C:", "Program Files", "MDD", "resources", "app.asar");
|
|
const attackerUrl = pathToFileURL(path.join("C:", "Users", "Public", "attacker.html")).toString();
|
|
|
|
expect(() => assertTrustedIpcSender(eventFor(attackerUrl), {
|
|
isPackaged: true,
|
|
devServerUrl: "http://localhost:5180",
|
|
appPath
|
|
})).toThrow("IPC-Absender ist nicht vertrauenswürdig");
|
|
});
|
|
|
|
it("rejects IPC when Electron provides no sender URL", () => {
|
|
expect(() => assertTrustedIpcSender({
|
|
senderFrame: null,
|
|
sender: {
|
|
getURL: () => ""
|
|
}
|
|
}, {
|
|
isPackaged: false,
|
|
devServerUrl: "http://localhost:5180",
|
|
appPath: "C:\\Program Files\\MDD"
|
|
})).toThrow("IPC-Absender ist nicht vertrauenswürdig");
|
|
});
|
|
});
|