Skip to content

Commit c46eafd

Browse files
authored
fix: correctly validate protocols in anonymize_proxy (#545)
This allows usage of SOCKS protocols with `anonymizeProxy`.
1 parent a1ac3f2 commit c46eafd

File tree

5 files changed

+32
-20
lines changed

5 files changed

+32
-20
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "proxy-chain",
3-
"version": "2.5.1",
3+
"version": "2.5.2",
44
"description": "Node.js implementation of a proxy server (think Squid) with support for SSL, authentication, upstream proxy chaining, and protocol tunneling.",
55
"main": "dist/index.js",
66
"keywords": [

src/anonymize_proxy.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import net from 'net';
22
import http from 'http';
33
import { Buffer } from 'buffer';
44
import { URL } from 'url';
5-
import { Server } from './server';
5+
import { Server, SOCKS_PROTOCOLS } from './server';
66
import { nodeify } from './utils/nodeify';
77

88
// Dictionary, key is value returned from anonymizeProxy(), value is Server instance.
@@ -38,10 +38,9 @@ export const anonymizeProxy = (
3838
}
3939

4040
const parsedProxyUrl = new URL(proxyUrl);
41-
if (parsedProxyUrl.protocol !== 'http:') {
42-
throw new Error(
43-
'Invalid "proxyUrl" option: only HTTP proxies are currently supported.',
44-
);
41+
if (!['http:', ...SOCKS_PROTOCOLS].includes(parsedProxyUrl.protocol)) {
42+
// eslint-disable-next-line max-len
43+
throw new Error(`Invalid "proxyUrl" provided: URL must have one of the following protocols: "http", ${SOCKS_PROTOCOLS.map((p) => `"${p.replace(':', '')}"`).join(', ')} (was "${parsedProxyUrl}")`);
4544
}
4645

4746
// If upstream proxy requires no password, return it directly

src/server.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import { customConnect } from './custom_connect';
2121
import { forwardSocks } from './forward_socks';
2222
import { chainSocks } from './chain_socks';
2323

24-
const SOCKS_PROTOCOLS = ['socks:', 'socks4:', 'socks4a:', 'socks5:', 'socks5h:'];
24+
export const SOCKS_PROTOCOLS = ['socks:', 'socks4:', 'socks4a:', 'socks5:', 'socks5h:'];
2525

2626
// TODO:
2727
// - Implement this requirement from rfc7230

test/anonymize_proxy.js

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -114,19 +114,11 @@ describe('utils.anonymizeProxy', function () {
114114
assert.throws(() => { closeAnonymizedProxy(null); }, Error);
115115
});
116116

117-
it('throws for unsupported proxy protocols', () => {
118-
assert.throws(() => { anonymizeProxy('socks://whatever.com'); }, Error);
117+
it('throws for unsupported https: protocol', () => {
119118
assert.throws(() => { anonymizeProxy('https://whatever.com'); }, Error);
120-
assert.throws(() => { anonymizeProxy('socks5://whatever.com'); }, Error);
121-
assert.throws(() => {
122-
anonymizeProxy({ url: 'socks://whatever.com' });
123-
}, Error);
124119
assert.throws(() => {
125120
anonymizeProxy({ url: 'https://whatever.com' });
126121
}, Error);
127-
assert.throws(() => {
128-
anonymizeProxy({ url: 'socks5://whatever.com' });
129-
}, Error);
130122
});
131123

132124
it('throws for invalid ports', () => {
@@ -144,16 +136,12 @@ describe('utils.anonymizeProxy', function () {
144136
it('throws for invalid URLs', () => {
145137
assert.throws(() => { anonymizeProxy('://whatever.com'); }, Error);
146138
assert.throws(() => { anonymizeProxy('https://whatever.com'); }, Error);
147-
assert.throws(() => { anonymizeProxy('socks5://whatever.com'); }, Error);
148139
assert.throws(() => {
149140
anonymizeProxy({ url: '://whatever.com' });
150141
}, Error);
151142
assert.throws(() => {
152143
anonymizeProxy({ url: 'https://whatever.com' });
153144
}, Error);
154-
assert.throws(() => {
155-
anonymizeProxy({ url: 'socks5://whatever.com' });
156-
}, Error);
157145
});
158146

159147
it('keeps already anonymous proxies (both with callbacks and promises)', () => {

test/socks.js

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,12 @@ const ProxyChain = require('../src/index');
77
describe('SOCKS protocol', () => {
88
let socksServer;
99
let proxyServer;
10+
let anonymizeProxyUrl;
1011

1112
afterEach(() => {
1213
if (socksServer) socksServer.close();
1314
if (proxyServer) proxyServer.close();
15+
if (anonymizeProxyUrl) ProxyChain.closeAnonymizedProxy(anonymizeProxyUrl, true);
1416
});
1517

1618
it('works without auth', (done) => {
@@ -70,4 +72,27 @@ describe('SOCKS protocol', () => {
7072
.catch(done);
7173
});
7274
}).timeout(5 * 1000);
75+
76+
it('works with anonymizeProxy', (done) => {
77+
portastic.find({ min: 50500, max: 50750 }).then((ports) => {
78+
const [socksPort, proxyPort] = ports;
79+
socksServer = socksv5.createServer((info, accept) => {
80+
accept();
81+
});
82+
socksServer.listen(socksPort, 'localhost');
83+
socksServer.useAuth(socksv5.auth.UserPassword((user, password, cb) => {
84+
cb(user === 'proxy-chain' && password === 'rules!');
85+
}));
86+
87+
ProxyChain.anonymizeProxy({ port: proxyPort, url: `socks://proxy-chain:rules!@localhost:${socksPort}` }).then((anonymizedProxyUrl) => {
88+
anonymizeProxyUrl = anonymizedProxyUrl;
89+
gotScraping.get({ url: 'https://example.com', proxyUrl: anonymizedProxyUrl })
90+
.then((response) => {
91+
expect(response.body).to.contain('Example Domain');
92+
done();
93+
})
94+
.catch(done);
95+
});
96+
});
97+
}).timeout(5 * 1000);
7398
});

0 commit comments

Comments
 (0)