Skip to content

Potential fix for code scanning alert no. 24: Server-side request forgery - #27

Merged
LCSOGthb merged 2 commits into
mainfrom
alert-autofix-24
Jun 20, 2026
Merged

LCSOGthb merged 2 commits into
mainfrom
alert-autofix-24

Conversation

@LCSOGthb

@LCSOGthb LCSOGthb commented Jun 20, 2026

Copy link
Copy Markdown
Owner

Potential fix for https://github.com/LCSOGthb/AirMerge/security/code-scanning/24

To fix this without changing intended functionality, validate and normalize lat/lon before building the outbound URL:

  • Accept only single query values (not arrays).
  • Parse as finite numbers.
  • Enforce coordinate bounds (lat in [-90,90], lon in [-180,180]).
  • Build the URL via URL/URLSearchParams (not raw string concatenation).
  • Return 400 for invalid input, instead of attempting fetch.

In api/aqicn.ts, update the handler body around lines 8–10 to:

  1. Extract raw query params safely.
  2. Validate/convert.
  3. Construct URL with fixed base and safe path segment using numeric values.
  4. Keep existing response forwarding behavior.

No new dependency is required.

Suggested fixes powered by Copilot Autofix. Review carefully before merging.

Summary by Sourcery

Harden AQICN API proxy request handling by validating latitude/longitude query parameters before forwarding requests.

Bug Fixes:

  • Prevent malformed or out-of-range lat/lon values from being used to construct outbound AQICN API URLs, returning HTTP 400 for invalid input instead of attempting the fetch.

Enhancements:

  • Build the AQICN API request URL using the URL API and query parameters rather than raw string concatenation to reduce SSRF risk.

Summary by cubic

Hardened the AQICN proxy against SSRF by validating lat/lon and building the request URL safely. Invalid coordinates now return 400, addressing code scanning alert #24.

  • Bug Fixes

    • Accept only single lat/lon values, parse as finite numbers, and enforce bounds (lat −90..90, lon −180..180).
    • Construct the outbound URL with URL/URLSearchParams instead of string concatenation.
    • Preserve upstream status/body; no dependency changes.
  • Refactors

    • Formatted api/aqicn.ts with project formatters; no behavior change.

Written for commit 4221863. Summary will update on new commits.

Review in cubic

…gery

Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
@vercel

vercel Bot commented Jun 20, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
airmerge Ready Ready Preview, Comment Jun 20, 2026 1:18am

@semanticdiff-com

semanticdiff-com Bot commented Jun 20, 2026

Copy link
Copy Markdown

Review changes with  SemanticDiff

Changed Files
File Status
  api/aqicn.ts  34% smaller

@cr-gpt

cr-gpt Bot commented Jun 20, 2026

Copy link
Copy Markdown

Seems you are using me but didn't get OPENAI_API_KEY seted in Variables/Secrets for this repo. you could follow readme for more information

@netlify

netlify Bot commented Jun 20, 2026

Copy link
Copy Markdown

Deploy Preview for larme ready!

Name Link
🔨 Latest commit 4221863
🔍 Latest deploy log https://app.netlify.com/projects/larme/deploys/6a35ea000120020008a9d9a8
😎 Deploy Preview https://deploy-preview-27--larme.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@difflens

difflens Bot commented Jun 20, 2026

Copy link
Copy Markdown

View changes in DiffLens

Comment thread api/aqicn.ts
Comment on lines +24 to 27
const apiRes = await fetch(url.toString());
const data = await apiRes.json();

return res.status(apiRes.status).json(data);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

The code does not check if the fetch to the external API was successful (e.g., apiRes.ok) before attempting to parse the response as JSON and returning it. If the external API returns a non-JSON response or an error status, this could result in an unhandled exception or misleading error propagation.

Recommendation:
Add a check for apiRes.ok and handle error responses explicitly:

if (!apiRes.ok) {
  const errorText = await apiRes.text();
  return res.status(502).json({ error: 'Failed to fetch AQICN data', details: errorText });
}

This ensures that only successful responses are parsed as JSON and that errors are handled gracefully.

Comment thread api/aqicn.ts
const apiRes = await fetch(url.toString());
const data = await apiRes.json();

return res.status(apiRes.status).json(data);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Returning the status code from the external API directly (res.status(apiRes.status)) may expose internal details or result in inappropriate status codes for the client. For example, the external API might return a 403 or 429, which may not be meaningful or actionable for your API consumers.

Recommendation:
Map external API errors to appropriate client-facing status codes (e.g., 502 for upstream errors) and avoid leaking internal status codes directly.

@coderabbitai

coderabbitai Bot commented Jun 20, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Caution

Review failed

Pull request was closed or merged during review

📝 Walkthrough

Summary by CodeRabbit

  • Bug Fixes
    • Improved validation of location parameters in air quality API requests. Invalid latitude/longitude coordinates now generate proper error responses instead of being passed to the upstream service, enhancing system reliability.

Walkthrough

The api/aqicn.ts Vercel handler is updated to explicitly parse req.query.lat and req.query.lon (handling both string and array forms), convert them to numbers, and validate coordinate ranges. Invalid inputs now return a 400 JSON error. The upstream URL is constructed using the URL API with searchParams instead of string concatenation.

Changes

AQICN API Proxy Hardening

Layer / File(s) Summary
Typed query parsing, coordinate validation, and URL construction
api/aqicn.ts
Replaces req.query as any destructuring with explicit extraction of lat/lon (handling string | string[]), parseFloat conversion, and range validation (lat: -90..90, lon: -180..180) that returns 400 JSON on invalid input. Upstream URL is now built via new URL() with searchParams.set("token", TOKEN) instead of string concatenation.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~5 minutes

Poem

🐇 A rabbit checks coordinates with care,
No rogue lat or lon shall pass through there!
The URL is built with a proper class,
And bad requests bounce — they simply won't pass.
Valid maps for all, declared with flair! 🗺️

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main fix: addressing a specific security vulnerability (SSRF - code scanning alert #24) in the pull request.
Description check ✅ Passed The description is directly related to the changeset, providing detailed context about the SSRF vulnerability fix and validation logic being implemented.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch alert-autofix-24
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch alert-autofix-24

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@guardrails

guardrails Bot commented Jun 20, 2026

Copy link
Copy Markdown

⚠️ We detected 4 security issues in this pull request:

Vulnerable Libraries (4)
Severity Details
High pkg:npm/ajv@8.18.0 upgrade to: > 8.18.0
Informational pkg:npm/http-proxy-middleware@3.0.5 upgrade to: > 3.0.5
High pkg:npm/react-scripts@5.0.1 upgrade to: > 5.0.1
Medium pkg:npm/axios@1.15.0 (t) upgrade to: 1.15.2

More info on how to fix Vulnerable Libraries in JavaScript.


👉 Go to the dashboard for detailed results.

📥 Happy? Share your feedback with us.

…, Rustfmt, Scalafmt, StandardJS, StandardRB and swift-format

This commit fixes the style issues introduced in 5dc204d according to the output
from ClangFormat, dotnet-format, Prettier, RuboCop, Rustfmt, Scalafmt,
StandardJS, StandardRB and swift-format.

Details: #27
@what-the-diff

what-the-diff Bot commented Jun 20, 2026

Copy link
Copy Markdown

PR Summary

  • Improved Geolocation Data Processing: Changes have been made to better process latitude and longitude received from the request query. This even takes care of situations where these data could be arrays.
  • Data Type Adjustment: The values of latitude and longitude are now converted into numbers for ensuring their accuracy.
  • Quality Assurance Measures: A validation system has been introduced for latitude and longitude data to ensure they are within valid global ranges. Any latitude data should be between -90 and 90 degrees, while longitude should range between -180 and 180 degrees.
  • Effective Error Responses: In case of reception of invalid latitude or longitude data, our system is now equipped to respond with an error message accompanied by a 400 status. This way, the user is informed immediately about the issue.
  • Efficient URL Generation: We've used a URL object to construct the API endpoint. This method allows us to set the token using a safer method (searchParams) instead of just combining strings. This makes our URL creation more reliable and secure.

@difflens

difflens Bot commented Jun 20, 2026

Copy link
Copy Markdown

View changes in DiffLens

1 similar comment
@difflens

difflens Bot commented Jun 20, 2026

Copy link
Copy Markdown

View changes in DiffLens

@cr-gpt

cr-gpt Bot commented Jun 20, 2026

Copy link
Copy Markdown

Seems you are using me but didn't get OPENAI_API_KEY seted in Variables/Secrets for this repo. you could follow readme for more information

@difflens

difflens Bot commented Jun 20, 2026

Copy link
Copy Markdown

View changes in DiffLens

Comment thread api/aqicn.ts
Comment on lines +30 to 31
const apiRes = await fetch(url.toString());
const data = await apiRes.json();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Lack of timeout for external API request

The fetch call to the AQICN API does not specify a timeout. If the external service is slow or unresponsive, this could cause the handler to hang indefinitely, impacting performance and reliability.

Recommendation:
Use a timeout mechanism (e.g., AbortController) to ensure the request fails gracefully after a reasonable period:

const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), 5000);
try {
  const apiRes = await fetch(url.toString(), { signal: controller.signal });
  // ...
} finally {
  clearTimeout(timeout);
}

Comment thread api/aqicn.ts
Comment on lines +35 to 36
console.error("AQICN handler error:", err);
res.status(500).json({ error: err.message });

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Error handling exposes internal error details

The error handler returns err.message directly to the client, which may leak internal error details or sensitive information.

Recommendation:
Return a generic error message to clients and log the detailed error internally:

console.error("AQICN handler error:", err);
res.status(500).json({ error: "Internal server error" });

@sourcery-ai

sourcery-ai Bot commented Jun 20, 2026

Copy link
Copy Markdown

Reviewer's Guide

Hardened the AQICN proxy handler against SSRF by validating/normalizing lat/lon query parameters and constructing the upstream URL with the URL API, while preserving the existing response forwarding behavior and aligning string quotes with project style.

File-Level Changes

Change Details Files
Validate and normalize lat/lon query parameters before performing the upstream AQICN request, and construct the outbound URL safely using the URL API instead of string concatenation.
  • Extract lat and lon from req.query, normalizing potential array values to a single raw string for each parameter.
  • Convert raw lat/lon strings to numbers and ensure they are finite and within geographic bounds (lat −90..90, lon −180..180).
  • Return a 400 JSON response with an error message when lat/lon are missing, non-numeric, or out of range, avoiding the upstream fetch in those cases.
  • Build the AQICN request URL with new URL(...) and url.searchParams.set('token', TOKEN) rather than interpolating the URL string directly.
  • Forward the upstream AQICN response status and JSON body unchanged, and keep 500 error handling with console.error logging intact, updating only string quote style.
api/aqicn.ts

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@difflens

difflens Bot commented Jun 20, 2026

Copy link
Copy Markdown

View changes in DiffLens

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code Review

This pull request introduces validation for the lat and lon query parameters in the AQICN API handler, ensuring they represent valid coordinates, and refactors the URL construction to use the URL object. The review feedback correctly identifies an edge case where empty or whitespace-only query parameters evaluate to 0 when converted to a number, thereby bypassing validation, and provides a code suggestion to handle this scenario.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread api/aqicn.ts
Comment on lines +8 to +12
const rawLat = Array.isArray(req.query.lat)
? req.query.lat[0]
: req.query.lat;
const rawLon = Array.isArray(req.query.lon)
? req.query.lon[0]

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

In JavaScript/TypeScript, Number(''), Number(' '), or Number(null) evaluates to 0. If a client sends empty or whitespace-only values for lat or lon (e.g., ?lat=&lon=), they will be parsed as 0, bypassing the intended validation and querying the coordinates 0,0 instead of returning a 400 Bad Request error.\n\nTo prevent this, ensure that rawLat and rawLon are non-empty, non-whitespace strings before converting them to numbers.

    const rawLat = Array.isArray(req.query.lat) ? req.query.lat[0] : req.query.lat;\n    const rawLon = Array.isArray(req.query.lon) ? req.query.lon[0] : req.query.lon;\n\n    const lat = rawLat && rawLat.trim() !== '' ? Number(rawLat) : NaN;\n    const lon = rawLon && rawLon.trim() !== '' ? Number(rawLon) : NaN;

@sonarqubecloud

Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
0.0% Coverage on New Code (required ≥ 80%)

See analysis details on SonarQube Cloud

@deepsource-io

deepsource-io Bot commented Jun 20, 2026

Copy link
Copy Markdown

DeepSource Code Review

We reviewed changes in 0288a10...4221863 on this pull request. Below is the summary for the review, and you can see the individual issues we found as inline review comments.

See full review on DeepSource ↗

PR Report Card

Overall Grade   Security  

Reliability  

Complexity  

Hygiene  

Code Review Summary

Analyzer Status Updated (UTC) Details
Ruby Jun 20, 2026 1:17a.m. Review ↗
Rust Jun 20, 2026 1:17a.m. Review ↗
JavaScript Jun 20, 2026 1:17a.m. Review ↗
Scala Jun 20, 2026 1:17a.m. Review ↗
Shell Jun 20, 2026 1:17a.m. Review ↗
Secrets Jun 20, 2026 1:17a.m. Review ↗
Terraform Jun 20, 2026 1:17a.m. Review ↗
Swift Jun 20, 2026 1:17a.m. Review ↗
SQL Jun 20, 2026 1:17a.m. Review ↗
Code coverage Jun 20, 2026 1:17a.m. Review ↗
C & C++ Jun 20, 2026 1:17a.m. Review ↗
C# Jun 20, 2026 1:17a.m. Review ↗
Ansible Jun 20, 2026 1:17a.m. Review ↗

Important

AI Review is run only on demand for your team. We're only showing results of static analysis review right now. To trigger AI Review, comment @deepsourcebot review on this thread.

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jun 20, 2026

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Updated (UTC)
❌ Deployment failed
View logs
airmerge 4221863 Jun 20 2026, 01:19 AM

@LCSOGthb
LCSOGthb marked this pull request as ready for review June 20, 2026 01:19

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

1 issue found and verified against the latest diff

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="api/aqicn.ts">

<violation number="1" location="api/aqicn.ts:15">
P1: Custom agent: **Require API Auth, Validation, and Tenant Scoping**

Empty string lat/lon query parameters bypass validation because `Number('')` returns `0`, which passes the bounds check. Empty values should be rejected with a 400 error instead of silently defaulting to `0,0`.</violation>
</file>

Shadow auto-approve: would not auto-approve because issues were found.

Re-trigger cubic

Comment thread api/aqicn.ts
? req.query.lon[0]
: req.query.lon;

const lat = Number(rawLat);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1: Custom agent: Require API Auth, Validation, and Tenant Scoping

Empty string lat/lon query parameters bypass validation because Number('') returns 0, which passes the bounds check. Empty values should be rejected with a 400 error instead of silently defaulting to 0,0.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At api/aqicn.ts, line 11:

<comment>Empty string lat/lon query parameters bypass validation because `Number('')` returns `0`, which passes the bounds check. Empty values should be rejected with a 400 error instead of silently defaulting to `0,0`.</comment>

<file context>
@@ -5,9 +5,23 @@ if (!TOKEN) throw new Error('Missing environment variable AQICN_TOKEN');
+    const rawLat = Array.isArray(req.query.lat) ? req.query.lat[0] : req.query.lat;
+    const rawLon = Array.isArray(req.query.lon) ? req.query.lon[0] : req.query.lon;
+
+    const lat = Number(rawLat);
+    const lon = Number(rawLon);
+
</file context>

@codacy-production

Copy link
Copy Markdown

Not up to standards ⛔

🔴 Issues 1 critical · 1 high · 5 medium · 3 minor

Alerts:
⚠ 10 issues (≤ 0 issues of at least minor severity)

Results:
10 new issues

Category Results
BestPractice 4 medium
2 minor
ErrorProne 1 high
Security 1 critical
CodeStyle 1 minor
Complexity 1 medium

View in Codacy

🟢 Metrics 9 complexity · -1 duplication

Metric Results
Complexity 9
Duplication -1

View in Codacy

NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.

@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Harden AQICN proxy against SSRF via lat/lon validation and safe URL construction
🐞 Bug fix 🕐 10-20 Minutes

Grey Divider

Description

• Validate lat/lon query params as finite numbers within geographic bounds
• Build upstream AQICN URL using URL/searchParams instead of string concatenation
• Return HTTP 400 for invalid coordinates before issuing the upstream request
Diagram

flowchart TD
  A["Client"] --> B["Vercel handler"] --> C{Validate coords} -->|"invalid"| D["400 JSON error"]
  C -->|"valid"| E["Build URL token"] --> F["Fetch upstream"] --> G["Return JSON"]

  subgraph Legend
    direction LR
    _cli["Caller"] ~~~ _svc["Handler logic"] ~~~ _dec{Decision}
  end
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Reject array query params explicitly
  • ➕ Matches the stated goal of accepting only single values
  • ➕ Avoids surprising behavior where lat=1&amp;lat=2 silently takes the first value
  • ➖ Slightly stricter behavior; could break clients relying on repeated params (unlikely)
2. Use a schema validator (e.g., Zod) for query parsing
  • ➕ Centralizes validation and yields clearer error reporting
  • ➕ Scales better if more query params are added later
  • ➖ Introduces a new dependency and runtime overhead
  • ➖ Overkill for two numeric parameters

Recommendation: The current approach (bounds-check + URL/URLSearchParams) is the right minimal fix for the SSRF alert without adding dependencies. Consider tightening behavior by returning 400 when lat or lon are arrays (instead of taking the first element) to fully align with the stated intent and reduce ambiguity.

Files changed (1) +27 / -7

Bug fix (1) +27 / -7
aqicn.tsValidate coordinates and safely construct AQICN upstream URL +27/-7

Validate coordinates and safely construct AQICN upstream URL

• Replaces direct query interpolation with parsing/coercion of 'lat'/'lon' to finite numbers and enforcing valid geographic bounds. Returns HTTP 400 for invalid inputs and builds the upstream request using 'URL' and 'searchParams' before proxying upstream status/body.

api/aqicn.ts

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Hey - I've found 2 issues

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location path="api/aqicn.ts" line_range="8-25" />
<code_context>
+      ? req.query.lon[0]
+      : req.query.lon;
+
+    const lat = Number(rawLat);
+    const lon = Number(rawLon);
+
+    const isValidLat = Number.isFinite(lat) && lat >= -90 && lat <= 90;
</code_context>
<issue_to_address>
**suggestion (bug_risk):** Handle empty or missing lat/lon strings before numeric conversion

`Number(rawLat)` / `Number(rawLon)` interpret `''` as `0`, so `?lat=&lon=` would incorrectly pass the range check as `(0, 0)`. Consider explicitly rejecting empty or whitespace-only strings before conversion and returning the existing 400 response in those cases.

```suggestion
    const rawLat = Array.isArray(req.query.lat)
      ? req.query.lat[0]
      : req.query.lat;
    const rawLon = Array.isArray(req.query.lon)
      ? req.query.lon[0]
      : req.query.lon;

    const hasValidRawLat =
      typeof rawLat === "string" && rawLat.trim().length > 0;
    const hasValidRawLon =
      typeof rawLon === "string" && rawLon.trim().length > 0;

    if (!hasValidRawLat || !hasValidRawLon) {
      return res
        .status(400)
        .json({ error: "Invalid lat/lon query parameters" });
    }

    const lat = Number(rawLat);
    const lon = Number(rawLon);

    const isValidLat = Number.isFinite(lat) && lat >= -90 && lat <= 90;
    const isValidLon = Number.isFinite(lon) && lon >= -180 && lon <= 180;

    if (!isValidLat || !isValidLon) {
      return res
        .status(400)
        .json({ error: "Invalid lat/lon query parameters" });
    }
```
</issue_to_address>

### Comment 2
<location path="api/aqicn.ts" line_range="34-37" />
<code_context>
     const data = await apiRes.json();

     return res.status(apiRes.status).json(data);
   } catch (err: any) {
-    console.error('AQICN handler error:', err);
+    console.error("AQICN handler error:", err);
     res.status(500).json({ error: err.message });
   }
-}
</code_context>
<issue_to_address>
**suggestion:** Avoid relying on `err.message` for all error types in the catch handler

Since `err` is `any`, non-Error values (like strings or plain objects) won’t have a `message` property, so the response could be `{ error: undefined }`. Normalize the error first (e.g., check `err instanceof Error` and otherwise use a generic fallback message) so the client always receives a usable error string.

```suggestion
  } catch (err: unknown) {
    console.error("AQICN handler error:", err);

    let message = "Internal server error";

    if (err instanceof Error && err.message) {
      message = err.message;
    } else if (typeof err === "string") {
      message = err;
    }

    res.status(500).json({ error: message });
  }
```
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread api/aqicn.ts
Comment on lines +8 to +25
const rawLat = Array.isArray(req.query.lat)
? req.query.lat[0]
: req.query.lat;
const rawLon = Array.isArray(req.query.lon)
? req.query.lon[0]
: req.query.lon;

const lat = Number(rawLat);
const lon = Number(rawLon);

const isValidLat = Number.isFinite(lat) && lat >= -90 && lat <= 90;
const isValidLon = Number.isFinite(lon) && lon >= -180 && lon <= 180;

if (!isValidLat || !isValidLon) {
return res
.status(400)
.json({ error: "Invalid lat/lon query parameters" });
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

suggestion (bug_risk): Handle empty or missing lat/lon strings before numeric conversion

Number(rawLat) / Number(rawLon) interpret '' as 0, so ?lat=&lon= would incorrectly pass the range check as (0, 0). Consider explicitly rejecting empty or whitespace-only strings before conversion and returning the existing 400 response in those cases.

Suggested change
const rawLat = Array.isArray(req.query.lat)
? req.query.lat[0]
: req.query.lat;
const rawLon = Array.isArray(req.query.lon)
? req.query.lon[0]
: req.query.lon;
const lat = Number(rawLat);
const lon = Number(rawLon);
const isValidLat = Number.isFinite(lat) && lat >= -90 && lat <= 90;
const isValidLon = Number.isFinite(lon) && lon >= -180 && lon <= 180;
if (!isValidLat || !isValidLon) {
return res
.status(400)
.json({ error: "Invalid lat/lon query parameters" });
}
const rawLat = Array.isArray(req.query.lat)
? req.query.lat[0]
: req.query.lat;
const rawLon = Array.isArray(req.query.lon)
? req.query.lon[0]
: req.query.lon;
const hasValidRawLat =
typeof rawLat === "string" && rawLat.trim().length > 0;
const hasValidRawLon =
typeof rawLon === "string" && rawLon.trim().length > 0;
if (!hasValidRawLat || !hasValidRawLon) {
return res
.status(400)
.json({ error: "Invalid lat/lon query parameters" });
}
const lat = Number(rawLat);
const lon = Number(rawLon);
const isValidLat = Number.isFinite(lat) && lat >= -90 && lat <= 90;
const isValidLon = Number.isFinite(lon) && lon >= -180 && lon <= 180;
if (!isValidLat || !isValidLon) {
return res
.status(400)
.json({ error: "Invalid lat/lon query parameters" });
}

Comment thread api/aqicn.ts
Comment on lines 34 to 37
} catch (err: any) {
console.error('AQICN handler error:', err);
console.error("AQICN handler error:", err);
res.status(500).json({ error: err.message });
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

suggestion: Avoid relying on err.message for all error types in the catch handler

Since err is any, non-Error values (like strings or plain objects) won’t have a message property, so the response could be { error: undefined }. Normalize the error first (e.g., check err instanceof Error and otherwise use a generic fallback message) so the client always receives a usable error string.

Suggested change
} catch (err: any) {
console.error('AQICN handler error:', err);
console.error("AQICN handler error:", err);
res.status(500).json({ error: err.message });
}
} catch (err: unknown) {
console.error("AQICN handler error:", err);
let message = "Internal server error";
if (err instanceof Error && err.message) {
message = err.message;
} else if (typeof err === "string") {
message = err;
}
res.status(500).json({ error: message });
}

@difflens

difflens Bot commented Jun 20, 2026

Copy link
Copy Markdown

View changes in DiffLens

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

0 issues found across 1 file (changes from recent commits).

Shadow auto-approve: would not auto-approve. Auto-approval blocked by 1 unresolved issue from previous reviews.

Re-trigger cubic

@kilo-code-bot

kilo-code-bot Bot commented Jun 20, 2026

Copy link
Copy Markdown

Code Review Summary

Status: No New Issues Found | Recommendation: Review existing comments before merge

All issues in this PR have been previously identified by other review bots. The key concerns are:

Already-Flagged Issues Summary

File Line Issue Commenter
api/aqicn.ts 33 No check for apiRes.ok before parsing JSON codereviewbot-ai[bot]
api/aqicn.ts 33 Exposing external API status codes directly codereviewbot-ai[bot]
api/aqicn.ts 31 No timeout on external API fetch codereviewbot-ai[bot]
api/aqicn.ts 36 Exposing internal error details in response codereviewbot-ai[bot]
api/aqicn.ts 12 Empty string values bypass validation (Number('') = 0) gemini-code-assist[bot], cubic-dev-ai[bot]

Files Reviewed

  • api/aqicn.ts

Reviewed by laguna-m.1-20260312:free · Input: 233.3K · Output: 3.4K · Cached: 37.9K

@devin-ai-integration devin-ai-integration Bot 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.

Devin Review found 2 potential issues.

Open in Devin Review

Comment thread api/aqicn.ts
Comment on lines +8 to +25
const rawLat = Array.isArray(req.query.lat)
? req.query.lat[0]
: req.query.lat;
const rawLon = Array.isArray(req.query.lon)
? req.query.lon[0]
: req.query.lon;

const lat = Number(rawLat);
const lon = Number(rawLon);

const isValidLat = Number.isFinite(lat) && lat >= -90 && lat <= 90;
const isValidLon = Number.isFinite(lon) && lon >= -180 && lon <= 180;

if (!isValidLat || !isValidLon) {
return res
.status(400)
.json({ error: "Invalid lat/lon query parameters" });
}

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.

🚩 Inconsistent validation patterns between API handlers

The api/aqicn.ts handler now validates lat/lon query parameters and uses new URL for safe URL construction, but api/owm/[type].ts:9-16 still uses the old pattern (req.query as any with raw string interpolation into URLs). This creates an inconsistency: one handler is hardened against malformed input while the other is not. Consider applying the same validation pattern to the OWM handler.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment thread api/aqicn.ts
Comment on lines +15 to +16
const lat = Number(rawLat);
const lon = Number(rawLon);

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.

📝 Info: Number('') coerces to 0, which passes lat/lon validation

If lat or lon is passed as an empty string (e.g., ?lat=&lon=1), Number('') evaluates to 0, which is a valid latitude and longitude. This means an empty query parameter would be silently treated as 0 rather than rejected. In practice this is unlikely to be a real problem since 0,0 is a valid coordinate (Gulf of Guinea), but it's a subtle behavioral difference from what a user might expect when omitting a value.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

@LCSOGthb
LCSOGthb merged commit e7b28ee into main Jun 20, 2026
52 of 61 checks passed
@LCSOGthb
LCSOGthb deleted the alert-autofix-24 branch June 20, 2026 01:21
@qodo-code-review

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (2) 📘 Rule violations (0) 📜 Skill insights (0)

Grey Divider


Action required

1. Empty coords become zero 🐞 Bug ≡ Correctness
Description
The handler uses Number(rawLat/rawLon) without rejecting empty strings, so requests like
?lat=&lon= get coerced to 0 and pass the finite/range checks, resulting in an upstream request
for geo:0;0 instead of a 400. This returns data for the wrong location and masks malformed client
requests.
Code

api/aqicn.ts[R15-25]

+    const lat = Number(rawLat);
+    const lon = Number(rawLon);
+
+    const isValidLat = Number.isFinite(lat) && lat >= -90 && lat <= 90;
+    const isValidLon = Number.isFinite(lon) && lon >= -180 && lon <= 180;
+
+    if (!isValidLat || !isValidLon) {
+      return res
+        .status(400)
+        .json({ error: "Invalid lat/lon query parameters" });
+    }
Evidence
lat/lon are coerced via Number(...) and only checked for finiteness + bounds; this allows
empty-string inputs to become 0 and be considered valid. The frontend calls this endpoint with
required numeric lat/lon, so accepting empty values is not an expected request shape and indicates
malformed traffic that should be rejected.

api/aqicn.ts[8-29]
src/api.ts[7-8]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`Number("")` (or whitespace) coerces to `0`, which is finite and in-bounds, so the handler treats empty query params as valid coordinates and fetches data for `(0,0)`.

### Issue Context
The current validation only checks `Number.isFinite()` and bounds after coercion.

### Fix Focus Areas
- api/aqicn.ts[8-25]

### Suggested fix
1. Reject arrays (optional; see separate note) and require `rawLat`/`rawLon` to be non-empty strings.
2. Trim before parsing and reject `""` / whitespace.
3. Then parse and bounds-check as you do today.

Example approach:
- if (typeof rawLat !== 'string' || rawLat.trim() === '') -> 400
- same for rawLon
- const lat = Number(rawLat.trim())
- const lon = Number(rawLon.trim())

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Informational

2. Multi-value params normalized 🐞 Bug ⚙ Maintainability
Description
When lat/lon are provided multiple times, the handler silently uses only the first value
(req.query.lat[0] / lon[0]) instead of rejecting the ambiguous input. This makes requests
non-obvious to debug and can hide client/request construction bugs.
Code

api/aqicn.ts[R8-13]

+    const rawLat = Array.isArray(req.query.lat)
+      ? req.query.lat[0]
+      : req.query.lat;
+    const rawLon = Array.isArray(req.query.lon)
+      ? req.query.lon[0]
+      : req.query.lon;
Evidence
The handler explicitly branches on Array.isArray(...) and uses index 0, meaning multi-valued
inputs are accepted and silently normalized. The in-repo caller uses axios params with single
numeric lat/lon, so arrays aren’t part of the normal request contract.

api/aqicn.ts[8-13]
src/api.ts[7-8]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
The handler currently accepts repeated query params by taking only the first value. This is ambiguous behavior and makes troubleshooting harder.

### Issue Context
`req.query.lat` / `req.query.lon` can be `string | string[] | undefined`.

### Fix Focus Areas
- api/aqicn.ts[8-13]

### Suggested fix
If `Array.isArray(req.query.lat)` or `Array.isArray(req.query.lon)`, return `400` with an error indicating only single values are accepted. Then treat the remaining values as `string | undefined` and validate normally.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Qodo Logo

Comment thread api/aqicn.ts
Comment on lines +15 to +25
const lat = Number(rawLat);
const lon = Number(rawLon);

const isValidLat = Number.isFinite(lat) && lat >= -90 && lat <= 90;
const isValidLon = Number.isFinite(lon) && lon >= -180 && lon <= 180;

if (!isValidLat || !isValidLon) {
return res
.status(400)
.json({ error: "Invalid lat/lon query parameters" });
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Action required

1. Empty coords become zero 🐞 Bug ≡ Correctness

The handler uses Number(rawLat/rawLon) without rejecting empty strings, so requests like
?lat=&lon= get coerced to 0 and pass the finite/range checks, resulting in an upstream request
for geo:0;0 instead of a 400. This returns data for the wrong location and masks malformed client
requests.
Agent Prompt
### Issue description
`Number("")` (or whitespace) coerces to `0`, which is finite and in-bounds, so the handler treats empty query params as valid coordinates and fetches data for `(0,0)`.

### Issue Context
The current validation only checks `Number.isFinite()` and bounds after coercion.

### Fix Focus Areas
- api/aqicn.ts[8-25]

### Suggested fix
1. Reject arrays (optional; see separate note) and require `rawLat`/`rawLon` to be non-empty strings.
2. Trim before parsing and reject `""` / whitespace.
3. Then parse and bounds-check as you do today.

Example approach:
- if (typeof rawLat !== 'string' || rawLat.trim() === '') -> 400
- same for rawLon
- const lat = Number(rawLat.trim())
- const lon = Number(rawLon.trim())

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

@llamapreview llamapreview Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

AI Code Review by LlamaPReview

🎯 TL;DR & Recommendation

Recommendation: Approve with suggestions

This PR hardens the AQICN proxy against SSRF by validating lat/lon and constructing URLs safely. The fix is correct and addresses the alert. However, the same vulnerability exists in another endpoint, and the new validation logic lacks test coverage.

📄 Documentation Diagram

This diagram documents the refactored AQICN proxy handler with input validation and safe URL construction.

sequenceDiagram
    participant Client
    participant Handler as Vercel Handler (api/aqicn.ts)
    participant AQICN as AQICN API
    Client->>Handler: GET /api/aqicn?lat=...&lon=...
    note over Handler: Extract & validate lat/lon<br/>(single value, finite, bounds)
    alt invalid lat/lon
        Handler-->>Client: 400 { error: "Invalid lat/lon query parameters" }
    else valid
        Handler->>Handler: Build URL using URL API + token
        Handler->>AQICN: fetch(URL)
        AQICN-->>Handler: Response
        Handler-->>Client: Proxy status & body
    end
Loading

🌟 Strengths

  • Proper input validation (type, bounds) and secure URL construction via URL/URLSearchParams.
  • Error response (400) clearly communicates invalid input.
Priority File Category Impact Summary (≤12 words) Anchors
P2 api/aqicn.ts Bug Error contract change may break callers symbol:fetchAqicn, path:src/api.ts
P2 api/aqicn.ts Architecture SSRF fix not applied to owm endpoint path:api/owm/[type].ts
P2 api/aqicn.ts Testing No tests for validation logic -

🔍 Notable Themes

  • Incomplete hardening: The same SSRF pattern exists in api/owm/[type].ts (unaddressed) and validation logic lacks unit tests, increasing regression risk.

💡 Have feedback? We'd love to hear it in our GitHub Discussions.
✨ This review was generated by LlamaPReview Advanced, which is free for all open-source projects. Learn more.

Comment thread api/aqicn.ts
Comment on lines +27 to +28
const url = new URL(`https://api.waqi.info/feed/geo:${lat};${lon}/`);
url.searchParams.set("token", TOKEN);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 | Confidence: High

Speculative: This PR hardens api/aqicn.ts against SSRF by validating and constructing URLs safely. However, an identical vulnerable pattern exists in api/owm/[type].ts (lines 7–24), which directly interpolates unsanitized query parameters (lat, lon, start, end, type) into a fetch URL. This file is not modified in this PR but presents the same class of vulnerability (code‑scanning alert #24 likely only flagged aqicn.ts). To achieve consistent security posture, the same validation and safe‑URL construction should be applied to the OWM endpoint and any other endpoints that forward user‑supplied parameters to external APIs.

Comment thread api/aqicn.ts
Comment on lines +21 to +25
if (!isValidLat || !isValidLon) {
return res
.status(400)
.json({ error: "Invalid lat/lon query parameters" });
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 | Confidence: Medium

Speculative: The PR changes the error contract for invalid lat/lon input from an external API error (or fetch failure) to a locally generated HTTP 400 response with a fixed JSON body. The external caller fetchAqicn (in src/api.ts) uses axios.get which treats non-2xx responses as promise rejections. If the calling code in Dashboard.tsx (or other consumers) does not have a catch handler or does not expect a 400, this change could cause unhandled promise rejections or misleading error displays. The exact impact depends on the error‑handling logic of consumers, which is not visible in the provided context. To verify, review the consumer's error handling and ensure it can gracefully handle HTTP 400 with the new error shape, or wrap the call in a try‑catch on the consumer side.

Comment thread api/aqicn.ts
Comment on lines +18 to +19
const isValidLat = Number.isFinite(lat) && lat >= -90 && lat <= 90;
const isValidLon = Number.isFinite(lon) && lon >= -180 && lon <= 180;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 | Confidence: Medium

Speculative: The validation logic for lat/lon (array handling, Number conversion, finite check, and range bounds) has no corresponding unit tests. Given this change is a security fix intended to prevent SSRF, the lack of tests creates risk that edge cases (e.g., empty string ""0, nullNaN, whitespace, non‑decimal numbers, scientific notation) are not correctly handled. Adding a small test suite (e.g., Vitest or Jest) covering valid, invalid, and boundary inputs would improve long‑term maintainability and reduce regression risk.

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.

1 participant