Skip to content

Add Tiingo local websocket test#4712

Draft
cawthorne wants to merge 2 commits intomainfrom
feature/add-tiingo-local-ws-test
Draft

Add Tiingo local websocket test#4712
cawthorne wants to merge 2 commits intomainfrom
feature/add-tiingo-local-ws-test

Conversation

@cawthorne
Copy link
Contributor

Adds a new local websocket test for the Tiiingo Failover behaviour

@changeset-bot
Copy link

changeset-bot bot commented Mar 9, 2026

⚠️ No Changeset found

Latest commit: 4fcc903

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

const id = ++connectionCounter
const path = req.url || ''
const upstreamUrl = `${UPSTREAM_WS_URL}${path}`
console.log(`[proxy][${id}] EA connected, opening upstream: ${upstreamUrl}`)

Check warning

Code scanning / CodeQL

Log injection Medium test

Log entry depends on a
user-provided value
.

Copilot Autofix

AI 3 days ago

In general, to fix log injection when logging user input, sanitize the data before logging by removing or neutralizing characters that can alter the log structure (such as \r, \n, and other control characters). For plain text logs, stripping CR/LF is usually sufficient; for HTML logs, HTML-encoding is needed. It is also good practice to clearly delimit or quote user-provided data in the log message.

For this specific code, the best minimal fix is to ensure that upstreamUrl is sanitized before it is interpolated into the log message on line 39. We can do this by creating a sanitized version of upstreamUrl specifically for logging, e.g. safeUpstreamUrl, which strips carriage return and newline characters (and optionally other control characters) using String.prototype.replace with a regular expression. We should only change the log line (and possibly introduce a one-line local variable) without altering the behavior of the proxy logic: the actual upstreamUrl used to establish the WebSocket connection must remain unchanged. No additional imports are required; we can rely on built-in string methods.

Concretely, inside the wss.on('connection', ...) handler in packages/sources/tiingo/test/local-ws-test/proxy.js, after computing const upstreamUrl = \${UPSTREAM_WS_URL}${path}`, we introduce const safeUpstreamUrl = upstreamUrl.replace(/[\r\n]/g, '')and then change theconsole.logcall to logsafeUpstreamUrlinstead ofupstreamUrl`. This keeps functionality identical (the upstream connection still uses the original URL) while ensuring logs cannot be structurally modified via CR/LF injection.

Suggested changeset 1
packages/sources/tiingo/test/local-ws-test/proxy.js

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/packages/sources/tiingo/test/local-ws-test/proxy.js b/packages/sources/tiingo/test/local-ws-test/proxy.js
--- a/packages/sources/tiingo/test/local-ws-test/proxy.js
+++ b/packages/sources/tiingo/test/local-ws-test/proxy.js
@@ -36,7 +36,8 @@
   const id = ++connectionCounter
   const path = req.url || ''
   const upstreamUrl = `${UPSTREAM_WS_URL}${path}`
-  console.log(`[proxy][${id}] EA connected, opening upstream: ${upstreamUrl}`)
+  const safeUpstreamUrl = upstreamUrl.replace(/[\r\n]/g, '')
+  console.log(`[proxy][${id}] EA connected, opening upstream: ${safeUpstreamUrl}`)
 
   const upstreamWs = new WebSocket(upstreamUrl)
   const pair = { id, clientWs, upstreamWs, path }
EOF
@@ -36,7 +36,8 @@
const id = ++connectionCounter
const path = req.url || ''
const upstreamUrl = `${UPSTREAM_WS_URL}${path}`
console.log(`[proxy][${id}] EA connected, opening upstream: ${upstreamUrl}`)
const safeUpstreamUrl = upstreamUrl.replace(/[\r\n]/g, '')
console.log(`[proxy][${id}] EA connected, opening upstream: ${safeUpstreamUrl}`)

const upstreamWs = new WebSocket(upstreamUrl)
const pair = { id, clientWs, upstreamWs, path }
Copilot is powered by AI and may make mistakes. Always verify output.
Comment on lines +126 to +127
`[control] Closing ${targets.length} connection(s) with code=${code}` +
(pathFilter ? ` path=${pathFilter}` : ''),

Check warning

Code scanning / CodeQL

Log injection Medium test

Log entry depends on a
user-provided value
.

Copilot Autofix

AI 3 days ago

To fix the problem, any user-controlled data included in log messages should be sanitized before logging. For plain text logs, the key step is to remove or neutralize newline characters (\r, \n) so an attacker cannot inject additional log lines. It also helps to clearly delimit user-supplied values.

In this file, the only problematic user-controlled value in the highlighted log call is pathFilter. The best minimal fix is to derive a sanitized version of pathFilter (e.g., safePathFilter) by stripping carriage returns and newlines using String.prototype.replace, then log only that sanitized value. This keeps the existing functionality (filtering by path and closing connections) unchanged while ensuring logs cannot be broken by crafted input.

Concretely:

  • After determining pathFilter on line 121, compute safePathFilter as either null (if no filter) or String(pathFilter).replace(/\r|\n/g, '').
  • Use safePathFilter instead of pathFilter in the console.log message on line 125–128.
  • Optionally, also use safePathFilter in the JSON response on line 141 to keep logged/returned values consistent and sanitized, though CodeQL only cares about the log sink.

No new external libraries are necessary; String.prototype.replace with a simple regex is sufficient.

Suggested changeset 1
packages/sources/tiingo/test/local-ws-test/proxy.js

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/packages/sources/tiingo/test/local-ws-test/proxy.js b/packages/sources/tiingo/test/local-ws-test/proxy.js
--- a/packages/sources/tiingo/test/local-ws-test/proxy.js
+++ b/packages/sources/tiingo/test/local-ws-test/proxy.js
@@ -119,12 +119,16 @@
     const code = parseInt(parsed.query.code || '1005', 10)
     const reason = parsed.query.reason || ''
     const pathFilter = parsed.query.path || null
+    const safePathFilter =
+      pathFilter === null || pathFilter === undefined
+        ? null
+        : String(pathFilter).replace(/\r|\n/g, '')
 
     const targets = [...activePairs].filter((p) => !pathFilter || p.path === pathFilter)
 
     console.log(
       `[control] Closing ${targets.length} connection(s) with code=${code}` +
-        (pathFilter ? ` path=${pathFilter}` : ''),
+        (safePathFilter ? ` path=${safePathFilter}` : ''),
     )
 
     for (const pair of targets) {
@@ -138,7 +137,7 @@
     }
 
     res.writeHead(200, { 'Content-Type': 'application/json' })
-    res.end(JSON.stringify({ closed: targets.length, code, path: pathFilter }))
+    res.end(JSON.stringify({ closed: targets.length, code, path: safePathFilter }))
     return
   }
 
EOF
@@ -119,12 +119,16 @@
const code = parseInt(parsed.query.code || '1005', 10)
const reason = parsed.query.reason || ''
const pathFilter = parsed.query.path || null
const safePathFilter =
pathFilter === null || pathFilter === undefined
? null
: String(pathFilter).replace(/\r|\n/g, '')

const targets = [...activePairs].filter((p) => !pathFilter || p.path === pathFilter)

console.log(
`[control] Closing ${targets.length} connection(s) with code=${code}` +
(pathFilter ? ` path=${pathFilter}` : ''),
(safePathFilter ? ` path=${safePathFilter}` : ''),
)

for (const pair of targets) {
@@ -138,7 +137,7 @@
}

res.writeHead(200, { 'Content-Type': 'application/json' })
res.end(JSON.stringify({ closed: targets.length, code, path: pathFilter }))
res.end(JSON.stringify({ closed: targets.length, code, path: safePathFilter }))
return
}

Copilot is powered by AI and may make mistakes. Always verify output.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant