mirror of
https://github.com/johannesjo/super-productivity.git
synced 2026-07-17 16:37:43 +00:00
Co-authored-by: cocojojo5213 <cocojojo5213@users.noreply.github.com>
This commit is contained in:
parent
d844559c6f
commit
1c53656889
3 changed files with 241 additions and 3 deletions
|
|
@ -18,7 +18,7 @@ export const sendJiraRequest = ({
|
|||
jiraCfg: JiraCfg;
|
||||
}): void => {
|
||||
const mainWin = getWin();
|
||||
const agent = createProxyAwareAgent(jiraCfg?.isAllowSelfSignedCertificate);
|
||||
const agent = createProxyAwareAgent(url, jiraCfg?.isAllowSelfSignedCertificate);
|
||||
|
||||
fetch(url, {
|
||||
...requestInit,
|
||||
|
|
|
|||
168
electron/proxy-agent.test.cjs
Normal file
168
electron/proxy-agent.test.cjs
Normal file
|
|
@ -0,0 +1,168 @@
|
|||
const test = require('node:test');
|
||||
const assert = require('node:assert/strict');
|
||||
const path = require('node:path');
|
||||
const Module = require('node:module');
|
||||
const { Agent: HttpsAgent } = require('node:https');
|
||||
const { HttpsProxyAgent } = require('https-proxy-agent');
|
||||
|
||||
require('ts-node/register/transpile-only');
|
||||
|
||||
const originalModuleLoad = Module._load;
|
||||
const originalEnv = { ...process.env };
|
||||
const proxyAgentModulePath = path.resolve(__dirname, 'proxy-agent.ts');
|
||||
|
||||
const resetModule = () => {
|
||||
delete require.cache[proxyAgentModulePath];
|
||||
};
|
||||
|
||||
const clearProxyEnv = () => {
|
||||
delete process.env.HTTPS_PROXY;
|
||||
delete process.env.https_proxy;
|
||||
delete process.env.HTTP_PROXY;
|
||||
delete process.env.http_proxy;
|
||||
delete process.env.NO_PROXY;
|
||||
delete process.env.no_proxy;
|
||||
};
|
||||
|
||||
const installMocks = () => {
|
||||
Module._load = function patchedLoad(request, parent, isMain) {
|
||||
if (request === 'electron-log/main') {
|
||||
return {
|
||||
log: () => {},
|
||||
};
|
||||
}
|
||||
|
||||
return originalModuleLoad.call(this, request, parent, isMain);
|
||||
};
|
||||
};
|
||||
|
||||
const loadProxyAgentModule = () => {
|
||||
resetModule();
|
||||
return require(proxyAgentModulePath);
|
||||
};
|
||||
|
||||
test.beforeEach(() => {
|
||||
clearProxyEnv();
|
||||
installMocks();
|
||||
resetModule();
|
||||
});
|
||||
|
||||
test.afterEach(() => {
|
||||
Module._load = originalModuleLoad;
|
||||
process.env = { ...originalEnv };
|
||||
resetModule();
|
||||
});
|
||||
|
||||
test('returns undefined when no proxy or self-signed handling is configured', () => {
|
||||
const { createProxyAwareAgent } = loadProxyAgentModule();
|
||||
|
||||
assert.equal(
|
||||
createProxyAwareAgent('https://jira.internal.example/rest/api'),
|
||||
undefined,
|
||||
);
|
||||
});
|
||||
|
||||
test('creates a proxy agent from HTTPS_PROXY', () => {
|
||||
process.env.HTTPS_PROXY = 'http://proxy.example:8080';
|
||||
const { createProxyAwareAgent } = loadProxyAgentModule();
|
||||
|
||||
const agent = createProxyAwareAgent('https://jira.external.example/rest/api');
|
||||
|
||||
assert.ok(agent instanceof HttpsProxyAgent);
|
||||
assert.equal(agent.proxy.href, 'http://proxy.example:8080/');
|
||||
});
|
||||
|
||||
test('bypasses proxy for an exact NO_PROXY host match', () => {
|
||||
process.env.HTTPS_PROXY = 'http://proxy.example:8080';
|
||||
process.env.NO_PROXY = 'jira.internal.example';
|
||||
const { createProxyAwareAgent } = loadProxyAgentModule();
|
||||
|
||||
assert.equal(
|
||||
createProxyAwareAgent('https://jira.internal.example/rest/api'),
|
||||
undefined,
|
||||
);
|
||||
});
|
||||
|
||||
test('bypasses proxy for subdomains of a bare NO_PROXY domain', () => {
|
||||
process.env.HTTPS_PROXY = 'http://proxy.example:8080';
|
||||
process.env.NO_PROXY = 'internal.example';
|
||||
const { createProxyAwareAgent } = loadProxyAgentModule();
|
||||
|
||||
assert.equal(
|
||||
createProxyAwareAgent('https://jira.internal.example/rest/api'),
|
||||
undefined,
|
||||
);
|
||||
assert.ok(
|
||||
createProxyAwareAgent('https://notinternal.example/rest/api') instanceof
|
||||
HttpsProxyAgent,
|
||||
);
|
||||
});
|
||||
|
||||
test('honors lowercase no_proxy and leading-dot suffix matches', () => {
|
||||
process.env.HTTPS_PROXY = 'http://proxy.example:8080';
|
||||
process.env.no_proxy = '.internal.example';
|
||||
const { createProxyAwareAgent } = loadProxyAgentModule();
|
||||
|
||||
assert.equal(
|
||||
createProxyAwareAgent('https://jira.internal.example/rest/api'),
|
||||
undefined,
|
||||
);
|
||||
assert.equal(createProxyAwareAgent('https://internal.example/rest/api'), undefined);
|
||||
});
|
||||
|
||||
test('honors wildcard NO_PROXY entries', () => {
|
||||
process.env.HTTPS_PROXY = 'http://proxy.example:8080';
|
||||
process.env.NO_PROXY = '*';
|
||||
const { createProxyAwareAgent } = loadProxyAgentModule();
|
||||
|
||||
assert.equal(
|
||||
createProxyAwareAgent('https://jira.external.example/rest/api'),
|
||||
undefined,
|
||||
);
|
||||
});
|
||||
|
||||
test('honors wildcard suffix NO_PROXY entries', () => {
|
||||
process.env.HTTPS_PROXY = 'http://proxy.example:8080';
|
||||
process.env.NO_PROXY = '*.corp.example';
|
||||
const { createProxyAwareAgent } = loadProxyAgentModule();
|
||||
|
||||
assert.equal(createProxyAwareAgent('https://jira.corp.example/rest/api'), undefined);
|
||||
});
|
||||
|
||||
test('requires NO_PROXY port entries to match the request port', () => {
|
||||
process.env.HTTPS_PROXY = 'http://proxy.example:8080';
|
||||
process.env.NO_PROXY = 'jira.internal.example:8443';
|
||||
const { createProxyAwareAgent } = loadProxyAgentModule();
|
||||
|
||||
assert.equal(
|
||||
createProxyAwareAgent('https://jira.internal.example:8443/rest/api'),
|
||||
undefined,
|
||||
);
|
||||
assert.ok(
|
||||
createProxyAwareAgent('https://jira.internal.example:443/rest/api') instanceof
|
||||
HttpsProxyAgent,
|
||||
);
|
||||
});
|
||||
|
||||
test('keeps self-signed handling when NO_PROXY bypasses the proxy', () => {
|
||||
process.env.HTTPS_PROXY = 'http://proxy.example:8080';
|
||||
process.env.NO_PROXY = 'jira.internal.example';
|
||||
const { createProxyAwareAgent } = loadProxyAgentModule();
|
||||
|
||||
const agent = createProxyAwareAgent('https://jira.internal.example/rest/api', true);
|
||||
|
||||
assert.ok(agent instanceof HttpsAgent);
|
||||
assert.equal(agent.options.rejectUnauthorized, false);
|
||||
});
|
||||
|
||||
test('keeps proxy handling for non-matching NO_PROXY entries', () => {
|
||||
process.env.HTTPS_PROXY = 'http://proxy.example:8080';
|
||||
process.env.NO_PROXY = 'other.internal.example,.corp.example';
|
||||
const { createProxyAwareAgent } = loadProxyAgentModule();
|
||||
|
||||
const agent = createProxyAwareAgent('https://jira.internal.example/rest/api', true);
|
||||
|
||||
assert.ok(agent instanceof HttpsProxyAgent);
|
||||
assert.equal(agent.proxy.href, 'http://proxy.example:8080/');
|
||||
assert.equal(agent.options.rejectUnauthorized, false);
|
||||
});
|
||||
|
|
@ -13,11 +13,80 @@ export const getProxyUrl = (): string | undefined =>
|
|||
process.env.http_proxy ||
|
||||
undefined;
|
||||
|
||||
const getNoProxy = (): string | undefined =>
|
||||
process.env.NO_PROXY || process.env.no_proxy || undefined;
|
||||
|
||||
const getUrlPort = (url: URL): string =>
|
||||
url.port || (url.protocol === 'http:' ? '80' : url.protocol === 'https:' ? '443' : '');
|
||||
|
||||
const parseNoProxyEntry = (
|
||||
rawEntry: string,
|
||||
): { host: string; port?: string } | undefined => {
|
||||
const entry = rawEntry.trim().toLowerCase();
|
||||
if (!entry) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const withoutProtocol = entry.replace(/^[a-z][a-z\d+.-]*:\/\//, '');
|
||||
const withoutPath = withoutProtocol.split('/')[0];
|
||||
const portMatch = withoutPath.match(/:(\d+)$/);
|
||||
const port = portMatch?.[1];
|
||||
const host = (port ? withoutPath.slice(0, -port.length - 1) : withoutPath).replace(
|
||||
/^\[(.*)]$/,
|
||||
'$1',
|
||||
);
|
||||
|
||||
return host ? { host, port } : undefined;
|
||||
};
|
||||
|
||||
export const isNoProxyMatch = (requestUrl: string, noProxy = getNoProxy()): boolean => {
|
||||
if (!noProxy) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let url: URL;
|
||||
try {
|
||||
url = new URL(requestUrl);
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
|
||||
const targetHost = url.hostname.toLowerCase();
|
||||
const targetPort = getUrlPort(url);
|
||||
|
||||
return noProxy
|
||||
.split(/[,\s]+/)
|
||||
.map(parseNoProxyEntry)
|
||||
.filter((entry): entry is { host: string; port?: string } => !!entry)
|
||||
.some(({ host, port }) => {
|
||||
if (host === '*') {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (port && port !== targetPort) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (host.startsWith('*.')) {
|
||||
const suffix = host.slice(1);
|
||||
return targetHost.endsWith(suffix);
|
||||
}
|
||||
|
||||
if (host.startsWith('.')) {
|
||||
return targetHost === host.slice(1) || targetHost.endsWith(host);
|
||||
}
|
||||
|
||||
return targetHost === host || targetHost.endsWith(`.${host}`);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Builds a node-fetch–compatible HTTPS agent that respects:
|
||||
* 1. Proxy environment variables (HTTPS_PROXY / HTTP_PROXY)
|
||||
* 2. An opt-in flag to accept self-signed certificates on the **target** server
|
||||
* 2. NO_PROXY / no_proxy bypasses for the current request URL
|
||||
* 3. An opt-in flag to accept self-signed certificates on the **target** server
|
||||
*
|
||||
* @param requestUrl The URL about to be requested.
|
||||
* @param allowSelfSigned When `true`, TLS certificate errors on both the
|
||||
* proxy **and** the target connection are ignored.
|
||||
* This is intentional – the user opted in via the
|
||||
|
|
@ -26,11 +95,12 @@ export const getProxyUrl = (): string | undefined =>
|
|||
* a proxy nor self-signed handling is needed.
|
||||
*/
|
||||
export const createProxyAwareAgent = (
|
||||
requestUrl: string,
|
||||
allowSelfSigned = false,
|
||||
): HttpsProxyAgent<string> | HttpsAgent | undefined => {
|
||||
const proxyUrl = getProxyUrl();
|
||||
|
||||
if (proxyUrl) {
|
||||
if (proxyUrl && !isNoProxyMatch(requestUrl)) {
|
||||
log(`Using proxy.${allowSelfSigned ? ' (self-signed certs allowed)' : ''}`);
|
||||
|
||||
const agent = new HttpsProxyAgent(proxyUrl, {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue