mirror of
https://github.com/nimbold/Firelink.git
synced 2026-08-17 06:47:48 +00:00
fix(settings): harden network controls
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
import { useDownloadStore } from './useDownloadStore';
|
||||
import { getProxyArgs, normalizeCustomProxy, useDownloadStore } from './useDownloadStore';
|
||||
import { useSettingsStore } from './useSettingsStore';
|
||||
import * as ipc from '../ipc';
|
||||
|
||||
@@ -64,6 +64,32 @@ describe('useDownloadStore', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('normalizes proxy settings for download dispatch', async () => {
|
||||
expect(normalizeCustomProxy('127.0.0.1', 8080)).toBe('http://127.0.0.1:8080');
|
||||
expect(normalizeCustomProxy(' socks5://127.0.0.1 ', 1080)).toBe('socks5://127.0.0.1:1080');
|
||||
expect(normalizeCustomProxy('http://proxy.local:9000', 8080)).toBe('http://proxy.local:9000');
|
||||
expect(normalizeCustomProxy('127.0.0.1', NaN)).toBeNull();
|
||||
|
||||
expect(await getProxyArgs({
|
||||
proxyMode: 'none',
|
||||
proxyHost: '',
|
||||
proxyPort: 8080
|
||||
} as ReturnType<typeof useSettingsStore.getState>)).toBe('none');
|
||||
|
||||
vi.mocked(ipc.invokeCommand).mockResolvedValueOnce(null);
|
||||
expect(await getProxyArgs({
|
||||
proxyMode: 'system',
|
||||
proxyHost: '',
|
||||
proxyPort: 8080
|
||||
} as ReturnType<typeof useSettingsStore.getState>)).toBe('none');
|
||||
|
||||
expect(await getProxyArgs({
|
||||
proxyMode: 'custom',
|
||||
proxyHost: 'socks5://127.0.0.1',
|
||||
proxyPort: 1080
|
||||
} as ReturnType<typeof useSettingsStore.getState>)).toBe('socks5://127.0.0.1:1080');
|
||||
});
|
||||
|
||||
it('Start Queue dispatches exactly once for mixed dispatched/undispatched items', async () => {
|
||||
useDownloadStore.setState({
|
||||
downloads: [
|
||||
|
||||
@@ -54,7 +54,7 @@ export async function dispatchItem(id: string): Promise<boolean> {
|
||||
checksum: item.checksum || null,
|
||||
cookies: item.cookies || null,
|
||||
mirrors: item.mirrors || null,
|
||||
user_agent: settings.customUserAgent || null,
|
||||
user_agent: settings.customUserAgent.trim() || null,
|
||||
max_tries: settings.maxAutomaticRetries,
|
||||
proxy: await getProxyArgs(settings),
|
||||
format_selector: item.mediaFormatSelector || null,
|
||||
@@ -87,7 +87,25 @@ export async function dispatchItem(id: string): Promise<boolean> {
|
||||
return promise;
|
||||
}
|
||||
|
||||
const getProxyArgs = async (settings: ReturnType<typeof useSettingsStore.getState>) => {
|
||||
export const normalizeCustomProxy = (host: string, port: number): string | null => {
|
||||
const trimmedHost = host.trim();
|
||||
const normalizedPort = Number.isFinite(port) ? Math.trunc(port) : NaN;
|
||||
if (!trimmedHost || !Number.isFinite(normalizedPort) || normalizedPort < 1 || normalizedPort > 65535) return null;
|
||||
|
||||
if (/^[a-z][a-z0-9+.-]*:\/\//i.test(trimmedHost)) {
|
||||
try {
|
||||
const parsed = new URL(trimmedHost);
|
||||
if (!parsed.port) parsed.port = String(normalizedPort);
|
||||
return parsed.toString().replace(/\/$/, '');
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return `http://${trimmedHost}:${normalizedPort}`;
|
||||
};
|
||||
|
||||
export const getProxyArgs = async (settings: ReturnType<typeof useSettingsStore.getState>) => {
|
||||
if (settings.proxyMode === 'system') {
|
||||
try {
|
||||
const sysProxy = await invoke('get_system_proxy');
|
||||
@@ -97,8 +115,8 @@ const getProxyArgs = async (settings: ReturnType<typeof useSettingsStore.getStat
|
||||
return "none";
|
||||
}
|
||||
}
|
||||
if (settings.proxyMode === 'custom' && settings.proxyHost) {
|
||||
return `http://${settings.proxyHost}:${settings.proxyPort}`;
|
||||
if (settings.proxyMode === 'custom') {
|
||||
return normalizeCustomProxy(settings.proxyHost, settings.proxyPort) ?? "none";
|
||||
}
|
||||
if (settings.proxyMode === 'none') {
|
||||
return "none";
|
||||
@@ -778,7 +796,7 @@ export const useDownloadStore = create<DownloadState>((set, get) => ({
|
||||
checksum: item.checksum || null,
|
||||
cookies: item.cookies || null,
|
||||
mirrors: item.mirrors || null,
|
||||
user_agent: settings.customUserAgent || null,
|
||||
user_agent: settings.customUserAgent.trim() || null,
|
||||
max_tries: settings.maxAutomaticRetries,
|
||||
proxy: await getProxyArgs(settings),
|
||||
format_selector: item.mediaFormatSelector || null,
|
||||
|
||||
@@ -274,7 +274,11 @@ export const useSettingsStore = create<SettingsState>()(
|
||||
setShowMenuBarIcon: (showMenuBarIcon) => set({ showMenuBarIcon }),
|
||||
setProxyMode: (proxyMode) => set({ proxyMode }),
|
||||
setProxyHost: (proxyHost) => set({ proxyHost }),
|
||||
setProxyPort: (proxyPort) => set({ proxyPort }),
|
||||
setProxyPort: (proxyPort) => set({
|
||||
proxyPort: Number.isFinite(proxyPort)
|
||||
? Math.min(65535, Math.max(1, Math.trunc(proxyPort)))
|
||||
: 8080
|
||||
}),
|
||||
setCustomUserAgent: (customUserAgent) => set({ customUserAgent }),
|
||||
setAskWhereToSaveEachFile: (askWhereToSaveEachFile) => set({ askWhereToSaveEachFile }),
|
||||
setPreventsSleepWhileDownloading: (preventsSleepWhileDownloading) => {
|
||||
|
||||
Reference in New Issue
Block a user