Skip to content

Commit 574a994

Browse files
authored
WAF Bypass && getUser max redirect (#310)
2 parents 1474bbe + de1d866 commit 574a994

File tree

3 files changed

+66
-3
lines changed

3 files changed

+66
-3
lines changed

src/client.js

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,7 @@ module.exports = class Client {
218218
* @prop {boolean} [DEBUG] Enable debug mode
219219
* @prop {'data' | 'prodata' | 'widgetdata'} [server] Server type
220220
* @prop {string} [location] Auth page location (For france: https://fr.tradingview.com/)
221+
* @prop {Object<string, string>} [headers] Custom WebSocket headers
221222
*/
222223

223224
/**
@@ -228,8 +229,17 @@ module.exports = class Client {
228229
if (clientOptions.DEBUG) global.TW_DEBUG = clientOptions.DEBUG;
229230

230231
const server = clientOptions.server || 'data';
231-
this.#ws = new WebSocket(`wss://${server}.tradingview.com/socket.io/websocket?type=chart`, {
232+
const defaultHeaders = {
233+
// eslint-disable-next-line max-len
234+
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36',
235+
'Accept-Language': 'en-US,en;q=0.9',
236+
'Cache-Control': 'no-cache',
237+
Pragma: 'no-cache',
238+
};
239+
240+
this.#ws = new WebSocket(`wss://${server}.tradingview.com/socket.io/websocket?from=chart&type=chart`, {
232241
origin: 'https://www.tradingview.com',
242+
headers: { ...defaultHeaders, ...clientOptions.headers },
233243
});
234244

235245
if (clientOptions.token) {

src/miscRequests.js

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -429,7 +429,11 @@ module.exports = {
429429
* @param {string} [location] Auth page location (For france: https://fr.tradingview.com/)
430430
* @returns {Promise<User>} Token
431431
*/
432-
async getUser(session, signature = '', location = 'https://www.tradingview.com/') {
432+
async getUser(session, signature = '', location = 'https://www.tradingview.com/', redirectCount = 0) {
433+
if (redirectCount > 5) {
434+
throw new Error('Too many redirects - possible WAF or geo-restriction');
435+
}
436+
433437
const { data, headers } = await axios.get(location, {
434438
headers: {
435439
cookie: genAuthCookies(session, signature),
@@ -461,7 +465,7 @@ module.exports = {
461465
}
462466

463467
if (headers.location !== location) {
464-
return this.getUser(session, signature, headers.location);
468+
return this.getUser(session, signature, headers.location, redirectCount + 1);
465469
}
466470

467471
throw new Error('Wrong or expired sessionid/signature');

tests/getUser-redirect.test.ts

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import { describe, it, expect } from 'vitest';
2+
3+
describe('getUser redirect protection', () => {
4+
it('should not loop infinitely on repeated redirects', async () => {
5+
// Monkey-patch axios in the require cache to simulate redirect loop
6+
let callCount = 0;
7+
const axiosMock = {
8+
get: async (url: string) => {
9+
callCount += 1;
10+
const isA = url === 'https://www.tradingview.com/';
11+
return {
12+
data: '<html>no auth here</html>',
13+
headers: {
14+
location: isA
15+
? 'https://www.tradingview.com/accounts/signin/'
16+
: 'https://www.tradingview.com/',
17+
},
18+
};
19+
},
20+
};
21+
22+
const axiosPath = require.resolve('axios');
23+
const originalModule = require.cache[axiosPath];
24+
require.cache[axiosPath] = {
25+
id: axiosPath,
26+
filename: axiosPath,
27+
loaded: true,
28+
exports: axiosMock,
29+
} as any;
30+
31+
const miscPath = require.resolve('../src/miscRequests');
32+
delete require.cache[miscPath];
33+
34+
try {
35+
// eslint-disable-next-line global-require
36+
const misc = require('../src/miscRequests');
37+
await expect(
38+
misc.getUser('fake_session', 'fake_signature'),
39+
).rejects.toThrow('Too many redirects');
40+
41+
expect(callCount).toBeGreaterThan(0);
42+
expect(callCount).toBeLessThanOrEqual(10);
43+
} finally {
44+
if (originalModule) require.cache[axiosPath] = originalModule;
45+
else delete require.cache[axiosPath];
46+
delete require.cache[miscPath];
47+
}
48+
}, 5000);
49+
});

0 commit comments

Comments
 (0)