From 144479b89cefbabc838aac02d449be7c4ce2c828 Mon Sep 17 00:00:00 2001 From: Oguzhan Date: Mon, 27 Oct 2025 02:17:45 +0300 Subject: [PATCH 1/8] add: max redirect 5 limit for infinity loops --- src/miscRequests.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/miscRequests.js b/src/miscRequests.js index cfb83340..147ecd9d 100644 --- a/src/miscRequests.js +++ b/src/miscRequests.js @@ -427,7 +427,11 @@ module.exports = { * @param {string} [location] Auth page location (For france: https://fr.tradingview.com/) * @returns {Promise} Token */ - async getUser(session, signature = '', location = 'https://www.tradingview.com/') { + async getUser(session, signature = '', location = 'https://www.tradingview.com/', redirectCount = 0) { + if (redirectCount > 5) { + throw new Error('Too many redirects - invalid session/signature'); + } + const { data, headers } = await axios.get(location, { headers: { cookie: genAuthCookies(session, signature), @@ -459,7 +463,7 @@ module.exports = { } if (headers.location !== location) { - return this.getUser(session, signature, headers.location); + return this.getUser(session, signature, headers.location, redirectCount + 1); } throw new Error('Wrong or expired sessionid/signature'); From 8215fb90c9e764a4d8f9e29a1cdf740bd45ef994 Mon Sep 17 00:00:00 2001 From: Oguzhan Date: Mon, 27 Oct 2025 02:18:39 +0300 Subject: [PATCH 2/8] add: more headers for waf bypass --- src/client.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/client.js b/src/client.js index c38aa6a3..d32ccee8 100644 --- a/src/client.js +++ b/src/client.js @@ -158,6 +158,7 @@ module.exports = class Client { if (!this.isOpen) return; protocol.parseWSPacket(str).forEach((packet) => { + console.dir(packet, { depth: null }); if (global.TW_DEBUG) console.log('§90§30§107 CLIENT §0 PACKET', packet); if (typeof packet === 'number') { // Ping this.#ws.send(protocol.formatWSPacket(`~h~${packet}`)); @@ -228,8 +229,14 @@ module.exports = class Client { if (clientOptions.DEBUG) global.TW_DEBUG = clientOptions.DEBUG; const server = clientOptions.server || 'data'; - this.#ws = new WebSocket(`wss://${server}.tradingview.com/socket.io/websocket?type=chart`, { + this.#ws = new WebSocket(`wss://${server}.tradingview.com/socket.io/websocket?from=chart&type=chart`, { origin: 'https://www.tradingview.com', + headers: { + 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36', + 'Accept-Language': 'en-US,en;q=0.9', + 'Cache-Control': 'no-cache', + Pragma: 'no-cache', + }, }); if (clientOptions.token) { From 9205c6548fbc293874786aeac15ebc3f41357a89 Mon Sep 17 00:00:00 2001 From: Oguzhan Date: Mon, 27 Oct 2025 02:20:58 +0300 Subject: [PATCH 3/8] remove: console dir --- src/client.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/client.js b/src/client.js index d32ccee8..feb41e20 100644 --- a/src/client.js +++ b/src/client.js @@ -158,7 +158,6 @@ module.exports = class Client { if (!this.isOpen) return; protocol.parseWSPacket(str).forEach((packet) => { - console.dir(packet, { depth: null }); if (global.TW_DEBUG) console.log('§90§30§107 CLIENT §0 PACKET', packet); if (typeof packet === 'number') { // Ping this.#ws.send(protocol.formatWSPacket(`~h~${packet}`)); From 6f1e0762f24cdab1270c0c03fbfb4d28c21caf7f Mon Sep 17 00:00:00 2001 From: Caio Lins Date: Sat, 11 Apr 2026 18:34:50 +0900 Subject: [PATCH 4/8] Update redirect error message and add unit test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Change error message to "possible WAF or geo-restriction" since redirect loops are caused by WAF/geo-blocking, not invalid credentials - Add unit test verifying redirect loop is capped at ≤10 calls Co-Authored-By: Claude Opus 4.6 (1M context) --- src/miscRequests.js | 2 +- tests/getUser-redirect.test.ts | 48 ++++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 1 deletion(-) create mode 100644 tests/getUser-redirect.test.ts diff --git a/src/miscRequests.js b/src/miscRequests.js index ea2c5baa..a9707f2d 100644 --- a/src/miscRequests.js +++ b/src/miscRequests.js @@ -431,7 +431,7 @@ module.exports = { */ async getUser(session, signature = '', location = 'https://www.tradingview.com/', redirectCount = 0) { if (redirectCount > 5) { - throw new Error('Too many redirects - invalid session/signature'); + throw new Error('Too many redirects - possible WAF or geo-restriction'); } const { data, headers } = await axios.get(location, { diff --git a/tests/getUser-redirect.test.ts b/tests/getUser-redirect.test.ts new file mode 100644 index 00000000..4d8faa5c --- /dev/null +++ b/tests/getUser-redirect.test.ts @@ -0,0 +1,48 @@ +import { describe, it, expect } from 'vitest'; + +describe('getUser redirect protection', () => { + it('should not loop infinitely on repeated redirects', async () => { + // Monkey-patch axios in the require cache to simulate redirect loop + let callCount = 0; + const axiosMock = { + get: async (url: string) => { + callCount++; + const isA = url === 'https://www.tradingview.com/'; + return { + data: 'no auth here', + headers: { + location: isA + ? 'https://www.tradingview.com/accounts/signin/' + : 'https://www.tradingview.com/', + }, + }; + }, + }; + + const axiosPath = require.resolve('axios'); + const originalModule = require.cache[axiosPath]; + require.cache[axiosPath] = { + id: axiosPath, + filename: axiosPath, + loaded: true, + exports: axiosMock, + } as any; + + const miscPath = require.resolve('../src/miscRequests'); + delete require.cache[miscPath]; + + try { + const misc = require('../src/miscRequests'); + await expect( + misc.getUser('fake_session', 'fake_signature'), + ).rejects.toThrow('Too many redirects'); + + expect(callCount).toBeGreaterThan(0); + expect(callCount).toBeLessThanOrEqual(10); + } finally { + if (originalModule) require.cache[axiosPath] = originalModule; + else delete require.cache[axiosPath]; + delete require.cache[miscPath]; + } + }, 5000); +}); From 2b94d348e6ed909fa07c87dedf3727a85e6360dc Mon Sep 17 00:00:00 2001 From: Caio Lins Date: Sat, 11 Apr 2026 18:36:22 +0900 Subject: [PATCH 5/8] Fix CodeFactor lint warnings in redirect test - Replace ++ with += 1 (no-plusplus) - Add eslint-disable for global-require (needed for cache patching) Co-Authored-By: Claude Opus 4.6 (1M context) --- tests/getUser-redirect.test.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/getUser-redirect.test.ts b/tests/getUser-redirect.test.ts index 4d8faa5c..505b2cbe 100644 --- a/tests/getUser-redirect.test.ts +++ b/tests/getUser-redirect.test.ts @@ -6,7 +6,7 @@ describe('getUser redirect protection', () => { let callCount = 0; const axiosMock = { get: async (url: string) => { - callCount++; + callCount += 1; const isA = url === 'https://www.tradingview.com/'; return { data: 'no auth here', @@ -32,6 +32,7 @@ describe('getUser redirect protection', () => { delete require.cache[miscPath]; try { + // eslint-disable-next-line global-require const misc = require('../src/miscRequests'); await expect( misc.getUser('fake_session', 'fake_signature'), From a074b0ca09adf61d31bdcadeefb8b8d741ec3203 Mon Sep 17 00:00:00 2001 From: Caio Lins Date: Sat, 11 Apr 2026 19:07:17 +0900 Subject: [PATCH 6/8] Make WebSocket headers configurable with sensible defaults - Provide default browser-like headers (required by TradingView WAF) - Allow users to override via clientOptions.headers - Update User-Agent to Windows/Chrome 131 (more common, more recent) Co-Authored-By: Claude Opus 4.6 (1M context) --- src/client.js | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/client.js b/src/client.js index feb41e20..8eb014f0 100644 --- a/src/client.js +++ b/src/client.js @@ -218,6 +218,7 @@ module.exports = class Client { * @prop {boolean} [DEBUG] Enable debug mode * @prop {'data' | 'prodata' | 'widgetdata'} [server] Server type * @prop {string} [location] Auth page location (For france: https://fr.tradingview.com/) + * @prop {Object} [headers] Custom headers for the WebSocket connection (e.g. for WAF bypass) */ /** @@ -228,14 +229,16 @@ module.exports = class Client { if (clientOptions.DEBUG) global.TW_DEBUG = clientOptions.DEBUG; const server = clientOptions.server || 'data'; + const defaultHeaders = { + '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', + 'Accept-Language': 'en-US,en;q=0.9', + 'Cache-Control': 'no-cache', + Pragma: 'no-cache', + }; + this.#ws = new WebSocket(`wss://${server}.tradingview.com/socket.io/websocket?from=chart&type=chart`, { origin: 'https://www.tradingview.com', - headers: { - 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36', - 'Accept-Language': 'en-US,en;q=0.9', - 'Cache-Control': 'no-cache', - Pragma: 'no-cache', - }, + headers: { ...defaultHeaders, ...clientOptions.headers }, }); if (clientOptions.token) { From 9c353d621247de4efec7580417e7e2720e418d8e Mon Sep 17 00:00:00 2001 From: Caio Lins Date: Sat, 11 Apr 2026 19:08:55 +0900 Subject: [PATCH 7/8] Fix max-len lint warning in JSDoc Co-Authored-By: Claude Opus 4.6 (1M context) --- src/client.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/client.js b/src/client.js index 8eb014f0..7cb70a40 100644 --- a/src/client.js +++ b/src/client.js @@ -218,7 +218,7 @@ module.exports = class Client { * @prop {boolean} [DEBUG] Enable debug mode * @prop {'data' | 'prodata' | 'widgetdata'} [server] Server type * @prop {string} [location] Auth page location (For france: https://fr.tradingview.com/) - * @prop {Object} [headers] Custom headers for the WebSocket connection (e.g. for WAF bypass) + * @prop {Object} [headers] Custom WebSocket headers */ /** From de1d8669b5e82c348da01619d2169faf0e551789 Mon Sep 17 00:00:00 2001 From: Caio Lins Date: Sat, 11 Apr 2026 19:10:32 +0900 Subject: [PATCH 8/8] Disable max-len for User-Agent string Co-Authored-By: Claude Opus 4.6 (1M context) --- src/client.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/client.js b/src/client.js index 7cb70a40..718d15c7 100644 --- a/src/client.js +++ b/src/client.js @@ -230,6 +230,7 @@ module.exports = class Client { const server = clientOptions.server || 'data'; const defaultHeaders = { + // eslint-disable-next-line max-len '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', 'Accept-Language': 'en-US,en;q=0.9', 'Cache-Control': 'no-cache',