Skip to content

fix: prevent query param override of URL-defined connection options#4162

Merged
wellwelwel merged 1 commit intosidorares:masterfrom
wellwelwel:url-params
Mar 8, 2026
Merged

fix: prevent query param override of URL-defined connection options#4162
wellwelwel merged 1 commit intosidorares:masterfrom
wellwelwel:url-params

Conversation

@wellwelwel
Copy link
Collaborator

@wellwelwel wellwelwel commented Mar 8, 2026

Query parameters in parseUrl could override connection options already extracted from the URL structure (e.g., host, port, user, password, and database):

const options = {
host: decodeURIComponent(parsedUrl.hostname),
port: parseInt(parsedUrl.port, 10),
database: decodeURIComponent(parsedUrl.pathname.slice(1)),
user: decodeURIComponent(parsedUrl.username),
password: decodeURIComponent(parsedUrl.password),
};

Now, keys already present in the parsed options object are skipped during query parameter processing.

Dynamic options like ssl, multipleStatements, etc. remain configurable via query parameters since they are not part of the URL structure and can/are commonly used in DSN strings, it also prevent a potential breaking change to this fix, since whoever controls the DSN, usually already controls the host and credentials.

cc @peaktwilight, please see also 3123b4e 🤝

@codecov
Copy link

codecov bot commented Mar 8, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 90.47%. Comparing base (91c5229) to head (9611c65).
⚠️ Report is 1 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #4162      +/-   ##
==========================================
+ Coverage   90.41%   90.47%   +0.05%     
==========================================
  Files          86       86              
  Lines       13977    13980       +3     
  Branches     1728     1732       +4     
==========================================
+ Hits        12638    12648      +10     
+ Misses       1339     1332       -7     
Flag Coverage Δ
compression-0 89.71% <100.00%> (+0.05%) ⬆️
compression-1 90.45% <100.00%> (+0.05%) ⬆️
static-parser-0 88.11% <100.00%> (+0.05%) ⬆️
static-parser-1 88.85% <100.00%> (+0.05%) ⬆️
tls-0 89.89% <100.00%> (+0.05%) ⬆️
tls-1 90.25% <100.00%> (+0.05%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@wellwelwel wellwelwel marked this pull request as ready for review March 8, 2026 20:23
@wellwelwel wellwelwel merged commit 3123b4e into sidorares:master Mar 8, 2026
88 checks passed
@wellwelwel wellwelwel deleted the url-params branch March 8, 2026 20:26
@wellwelwel
Copy link
Collaborator Author

This PR also indirectly fixes the possibility of prototype pollution (manipulating the __proto__ key). @peaktwilight, I believe it would be interesting to complement the report/advisory with this detail.

@peaktwilight
Copy link

@wellwelwel perfect, will add it to the advisory and writeup when published, thanks for the heads up!

In the meantime, there'a slight edge case that might also be worth looking at while we're already at it:

The bounds checks before each read prevent the RangeError crash, but the loop count itself (num/numPoints/numRings) is still taken at face value from the buffer. For the multi-geometry types (cases 4–7), a payload with numGeometries = 0xFFFFFFFF and no actual geometry data causes a bunch of no-op iterations of parseGeometry() > each call hits the bounds check and returns null, but the loop still spins.

I tested this against the canary build:

Count Time (ms)
1,000 0.18ms
100,000 1.88ms
1,000,000 10ms
10,000,000 91ms
100,000,000 734ms
0xFFFFFFFF (extrapolated) ~37s

A single malformed geometry response could freeze the event loop for more than half a minute.

Same approach would work for numPoints in WKBLineString and numRings in WKBPolygon. Something like:

// after reading num/numPoints/numRings:
if (num > (bufferLength - offset) / 9) return null;

9 bytes = minimum sub-geometry size (1 byte order + 4 wkbType + 4 count). For points it'd be /16 (two doubles), and for rings /4 (minimum ring header).

Obviously minor compared to the other fixes, but same preconditions as the original finding (attacker-controlled server data). Just figured I'd flag it while the code is fresh :D

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants