From 470d7ffb61bbeb09203d67010dab188f15e8b939 Mon Sep 17 00:00:00 2001 From: Christof Marti Date: Thu, 9 Apr 2026 09:40:31 +0200 Subject: [PATCH] Omit embedded credentials in error message --- src/agent.ts | 13 ++++- .../sanitizeProxyResultCredentials.test.ts | 57 +++++++++++++++++++ 2 files changed, 69 insertions(+), 1 deletion(-) create mode 100644 tests/src/sanitizeProxyResultCredentials.test.ts diff --git a/src/agent.ts b/src/agent.ts index 37da0ab..4bcfb63 100644 --- a/src/agent.ts +++ b/src/agent.ts @@ -128,10 +128,21 @@ export class PacProxyAgent extends Agent { req.emit('proxy', { proxy, error: err }); } - throw new Error(`Failed to establish a socket connection to proxies: ${result}`); + throw new Error(`Failed to establish a socket connection to proxies: ${sanitizeProxyResultCredentials(result)}`); } } +/** + * Replaces credentials (userinfo) in proxy resolver result strings with a placeholder. + * E.g., "PROXY user:pass@host:8080" → "PROXY @host:8080" + */ +export function sanitizeProxyResultCredentials(result: string | undefined): string { + if (!result) { + return ''; + } + return String(result).replace(/(\b(?:PROXY|HTTPS?|SOCKS[45]?)\s+)[^\s@]+@/gi, '$1@'); +} + export function getProxyURLFromResolverResult(result: string | undefined) { // Default to "DIRECT" if a falsey value was returned (or nothing) if (!result) { diff --git a/tests/src/sanitizeProxyResultCredentials.test.ts b/tests/src/sanitizeProxyResultCredentials.test.ts new file mode 100644 index 0000000..717c4db --- /dev/null +++ b/tests/src/sanitizeProxyResultCredentials.test.ts @@ -0,0 +1,57 @@ +import * as assert from 'assert'; +import { sanitizeProxyResultCredentials } from '../../src/agent'; + +describe('sanitizeProxyResultCredentials', function () { + it('should replace user:pass with placeholder in PROXY target', function () { + assert.strictEqual( + sanitizeProxyResultCredentials('PROXY testuser:testpass@host:8080'), + 'PROXY @host:8080' + ); + }); + + it('should replace user:pass with special chars', function () { + assert.strictEqual( + sanitizeProxyResultCredentials('PROXY jane.fictional%40corp.example.com:fictional123@proxy.fictional.example.com:8080'), + 'PROXY @proxy.fictional.example.com:8080' + ); + }); + + it('should replace user-only (no password) with placeholder', function () { + assert.strictEqual( + sanitizeProxyResultCredentials('PROXY fictional-user@proxy.fictional.example.com:3128'), + 'PROXY @proxy.fictional.example.com:3128' + ); + }); + + it('should replace credentials from HTTPS proxy', function () { + assert.strictEqual( + sanitizeProxyResultCredentials('HTTPS fictional-user:fictional-pass@proxy.fictional.example.com:8443'), + 'HTTPS @proxy.fictional.example.com:8443' + ); + }); + + it('should not modify PROXY without credentials', function () { + assert.strictEqual( + sanitizeProxyResultCredentials('PROXY proxy.fictional.example.com:8080'), + 'PROXY proxy.fictional.example.com:8080' + ); + }); + + it('should not modify DIRECT', function () { + assert.strictEqual( + sanitizeProxyResultCredentials('DIRECT'), + 'DIRECT' + ); + }); + + it('should handle undefined', function () { + assert.strictEqual(sanitizeProxyResultCredentials(undefined), ''); + }); + + it('should handle multiple proxies with semicolons', function () { + assert.strictEqual( + sanitizeProxyResultCredentials('PROXY testuser:testpass@host1:8080; PROXY host2:8080; DIRECT'), + 'PROXY @host1:8080; PROXY host2:8080; DIRECT' + ); + }); +});