Skip to content

fix(geoloc): reject non-numeric bounding box params with a 400 (#2000) - #2003

Merged
tristan0x merged 2 commits into
developfrom
fix/22p02-geoloc-non-numeric-bounding-box-params
Nov 11, 2026
Merged

tristan0x merged 2 commits into
developfrom
fix/22p02-geoloc-non-numeric-bounding-box-params

Conversation

@tristan0x

@tristan0x tristan0x commented Nov 11, 2026 •

Copy link
Copy Markdown
Contributor

🤔 What

  • GeoLocService.checkAndGetCoordinatesParams now checks that each of sw_lat, sw_lng, ne_lat and ne_lng is a finite number before range-checking it, and answers 400 naming every non-numeric parameter, e.g. South west latitude value must be a number (value found: {t[0]}).
  • It returns the parsed numbers rather than the raw strings, so the queries only ever bind numbers.
  • The fix covers the eight geoloc endpoints that share this function, for GET and HEAD.
  • assets/swaggerV1.yaml: the 400 of GET /geoloc/entrances now mentions non-numeric coordinates.

Closes #2000

🤷‍♂️ Why

GET and HEAD /api/v1/geoloc/entrances?sw_lat={t[0]}&… returned 500. The validation only range-checked the values, and a comparison between a non-numeric string and a number is always false, so the value passed and reached Postgres, which rejected it with 22P02 (invalid input syntax for type double precision). A single 5xx fires the Azure Http5xx >= 1 alert, and the right answer for bad input is a 400.

🔍 How

A new parseCoordinate helper returns the number to bind, or null when the value is not a coordinate. It accepts a finite number, or a string that, once trimmed, matches decimal or exponent notation (^[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:e[+-]?\d+)?$, case-insensitive) and converts to a finite number. Number() alone is not enough: it reads '' and ' ' as 0 and accepts 0x10, which Postgres rejects. The finiteness check also rejects NaN, Infinity and 1e400, which overflows in Postgres too. A repeated parameter (an array) is rejected as well. The fractional digits require the decimal point, so no two digit runs compete for the same characters and a long rejected value is matched in linear time.

checkAndGetCoordinatesParams returns the parsed numbers in southWestBound and northEastBound. Binding the raw string would still fail on values Number() reads but Postgres does not: 1e-400 underflows (22003), and a value padded with a non-breaking space is invalid input (22P02). They are now bound as 0 and as the number itself.

A non-numeric value skips the range check, so it does not also get a misleading range error. The comment in validateBoundingBoxArea that said such values "fail later in Postgres" is updated: they are now rejected earlier.

🧪 Testing

  • New service tests in test/integration/1_services/GeoLocService.test.js cover rejected values ({t[0]}, abc, '', ' ', 0x10, 1,2, NaN, Infinity, 1e400, a repeated parameter), accepted values (45, -45.5, +3, .5, 5., 1e1, ' 12 ', numbers), the parsed numbers returned for 1e-400, a non-breaking space, padding and a leading +, a 15000-digit value rejected in under 50 ms, naming of every non-numeric parameter, and the range check on numeric strings.
  • New route tests in test/integration/4_routes/Geoloc.test.js send the URL from the issue with GET (400, with the parameter named) and HEAD (400), and check that sw_lat=1e-400 and a value padded with a non-breaking space answer 200. The existing ?sw_lat=abc test now also expects a 400.
  • The new tests fail without the fix and pass with it.
  • npm run test: 6161 passing, 0 failing. npm run lint: clean.

Manual check against a dev server:

curl -s -w "\n[%{http_code}]\n" "localhost:1337/api/v1/geoloc/entrances?sw_lat=%7Bt%5B0%5D%7D&sw_lng=%7Bt%5B1%5D%7D&ne_lat=%7Bt%5B0%5D+1%7D&ne_lng=%7Bt%5B1%5D+1%7D"

should answer [400] with a message naming all four parameters.

📸 Previews

No UI change. Example response body:

South west latitude value must be a number (value found: {t[0]}), South west longitude value must be a number (value found: {t[1]}), North east latitude value must be a number (value found: {t[0] 1}), North east longitude value must be a number (value found: {t[1] 1}).

- checkAndGetCoordinatesParams now checks that each coordinate is a finite
  decimal or exponent number before the range check; a comparison between a
  non-numeric string and a number is always false, so such values reached
  Postgres and failed with 22P02, sent back as a 500
- reject empty strings, hexadecimal, NaN, Infinity, overflowing exponents
  and repeated parameters, naming every offending parameter
- covers all eight geoloc endpoints, GET and HEAD alike
- document non-numeric coordinates in the 400 of GET /geoloc/entrances
@tristan0x tristan0x self-assigned this Nov 11, 2026
@tristan0x
tristan0x requested a review from ClemRz November 11, 2026 05:23
@tristan0x
tristan0x requested a review from Paul-AUB November 11, 2026 05:23

@Paul-AUB Paul-AUB left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changes requested; two Must Fix findings are attached inline.

Validation: 20 direct service validation assertions passed, the OpenAPI YAML parsed, and the changed JavaScript files passed ESLint with checkout CRLF normalized in memory. The targeted integration run could not start because the local test database refused connections. Additional isolated checks used PostgreSQL 18.6 through PGlite and a temporary Node HTTP server.

Comment thread api/services/GeoLocService.js Resolved
Comment thread api/services/GeoLocService.js Resolved
…2000)

- Return the parsed numbers from checkAndGetCoordinatesParams so the queries
  never bind a raw string Postgres rejects ('1e-400' underflows, a value padded
  with a non-breaking space is invalid input)
- Require the decimal point before the fractional digits, so a long rejected
  value no longer backtracks quadratically
- Cover underflow, Unicode whitespace and a 15000-digit value

@Paul-AUB Paul-AUB left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved on commit 7bdb7a1. The two previous Must Fix findings are addressed, and I found no additional actionable issues in the updated diff or its callers.

Validation: 25 isolated service validation cases passed, including normalized numeric bounds; a 15,001-character rejected input completed in 0.09 ms; the OpenAPI YAML parsed. The targeted integration suite could not start because the local PostgreSQL connection was refused. All current PR CI checks pass.

@tristan0x
tristan0x merged commit 0f362c4 into develop Nov 11, 2026
5 checks passed
@tristan0x
tristan0x deleted the fix/22p02-geoloc-non-numeric-bounding-box-params branch November 11, 2026 13:28

This branch was successfully deployed

1 active deployment
build — 7bdb7a1d Deployed Nov 11, 2026 by tristan0x via build-test #4234
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.

bug(api): [22P02] geoloc - non-numeric bounding box params return 500 on GET /geoloc/entrances

2 participants