Reject ambiguous Seoul Bike integer input

Tighten the public Seoul Bike query boundary so malformed integer strings cannot be partially parsed into valid requests.

Constraint: PR #277 review found parseInt accepted partially numeric query values on Seoul Bike routes.\nRejected: Keep parseInt with bounds checks | bounds still allow misleading values like 10abc and 1.5.\nConfidence: high\nScope-risk: narrow\nDirective: Keep Seoul Bike public query aliases strict; do not reintroduce partial numeric parsing.\nTested: node --test packages/k-skill-proxy/test/server.test.js --test-name-pattern 'seoul bike'; PYTHONPATH=.:scripts python3 -m unittest scripts.test_seoul_bike; explicit app.inject invalid-query smoke; PATH="/Users/jeffrey/.pyenv/versions/3.11.9/bin:/Users/jeffrey/.codex/tmp/arg0/codex-arg0uv50Mt:/opt/homebrew/lib/node_modules/@openai/codex/node_modules/@openai/codex-darwin-arm64/vendor/aarch64-apple-darwin/path:/Users/jeffrey/.cmuxterm/omo-bin:/opt/homebrew/share/android-commandlinetools/platform-tools:/opt/homebrew/share/android-commandlinetools/emulator:/opt/homebrew/share/android-commandlinetools/cmdline-tools/latest/bin:/Users/jeffrey/.local/bin:/Users/jeffrey/.bun/bin:/opt/homebrew/opt/node@22/bin:/opt/homebrew/opt/openjdk@21/bin:/opt/homebrew/opt/postgresql@18/bin:/Users/jeffrey/.jenv/shims:/Users/jeffrey/.jenv/bin:/opt/homebrew/opt/imagemagick/bin:/opt/homebrew/Cellar/pyenv-virtualenv/1.4.0/shims:/Users/jeffrey/.pyenv/shims:/opt/homebrew/opt/openssl@3/bin:/Users/jeffrey/.rbenv/shims:/Users/jeffrey/.rbenv/bin:/Users/jeffrey/google-cloud-sdk/bin:/Applications/cmux.app/Contents/Resources/bin:/Users/jeffrey/Library/pnpm:/Users/jeffrey/.nvm/versions/node/v24.13.0/bin:/Users/jeffrey/.cops/bin:/opt/homebrew/bin:/opt/homebrew/sbin:/usr/local/bin:/System/Cryptexes/App/usr/bin:/usr/bin:/bin:/usr/sbin:/sbin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/local/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/appleinternal/bin:/opt/pmk/env/global/bin:/Library/Apple/usr/bin:/Library/TeX/texbin:/Users/jeffrey/.cargo/bin:/Users/jeffrey/Library/Application Support/JetBrains/Toolbox/scripts:/Library/Java/JavaVirtualMachines/zulu-17.jdk/Contents/Home/bin:/Users/jeffrey/xcode-projects/marshroom/cli" npm run ci\nNot-tested: live hosted Seoul Open API traffic
This commit is contained in:
Jeffrey (Dongkyu) Kim 2026-05-21 15:49:35 +09:00
commit 3965cd7396
2 changed files with 25 additions and 1 deletions

View file

@ -508,7 +508,13 @@ function parseBoundedIntegerAlias(query, keys, { defaultValue, min, max, label }
break;
}
}
const value = raw === undefined ? defaultValue : Number.parseInt(raw, 10);
let value = defaultValue;
if (raw !== undefined) {
if (typeof raw !== "string" || !/^[+-]?\d+$/.test(raw)) {
throw new Error(`Provide valid ${label}.`);
}
value = Number(raw);
}
if (!Number.isInteger(value) || value < min || value > max) {
throw new Error(`Provide valid ${label}.`);
}

View file

@ -2084,6 +2084,24 @@ test("seoul bike nearby endpoint validates coordinates", async (t) => {
assert.equal(response.json().error, "bad_request");
});
test("seoul bike endpoints reject partially numeric integer query params", async (t) => {
const app = buildServer({ env: { SEOUL_OPEN_API_KEY: "seoul-key" } });
t.after(async () => {
await app.close();
});
for (const url of [
"/v1/seoul-bike/realtime?startIndex=10abc&endIndex=20",
"/v1/seoul-bike/stations?startIndex=1&endIndex=1.5",
"/v1/seoul-bike/nearby?lat=37.5717&lon=126.9763&radius_m=120m",
"/v1/seoul-bike/nearby?lat=37.5717&lon=126.9763&limit=5.5"
]) {
const response = await app.inject({ method: "GET", url });
assert.equal(response.statusCode, 400, url);
assert.equal(response.json().error, "bad_request", url);
}
});
test("korea weather endpoint caches successful upstream responses for normalized coordinate queries", async (t) => {
const originalFetch = global.fetch;
let fetchCalls = 0;