# Overpass API Request Sanitization in God's Eye View's Vite Middleware: 6 Security Layers Explained

> Discover 6 security layers protecting Overpass API requests in God's Eye View's Vite middleware. Learn how POST data is validated, noise stripped, bounds enforced, and queries secured.

- Repository: [Bilawal Sidhu/gods-eye-view](https://github.com/bilawalsidhu/gods-eye-view)
- Tags: deep-dive
- Published: 2026-09-11

---

**God's Eye View implements a multi-stage sanitization pipeline in [`vite.config.js`](https://github.com/bilawalsidhu/gods-eye-view/blob/main/vite.config.js) that validates POST request bodies, strips Overpass QL noise via a custom lexer, enforces strict spatial bounds on `around:` and bbox selectors, blocks dangerous control-flow constructs, clamps query timeouts to 25 seconds, and URL-encodes the final payload before proxying to the Overpass API.**

The open-source mapping application **bilawalsidhu/gods-eye-view** proxies Overpass API requests through its Vite development server middleware to prevent abusive or malformed queries. This deep-dive examines the specific sanitization techniques applied to incoming requests, referencing the exact implementation lines and constants that enforce security bounds in the [`vite.config.js`](https://github.com/bilawalsidhu/gods-eye-view/blob/main/vite.config.js) file.

## How the Vite Middleware Sanitizes Overpass Requests

The sanitization logic runs inside the Vite dev server's proxy middleware, specifically within the `sanitizeOverpassBody` function. Each request undergoes six distinct validation stages before reaching the Overpass API backend.

### 1. Request Body Validation

The middleware strictly enforces POST-only access with a single `data` parameter containing the raw Overpass QL query. Any request missing this parameter or containing additional fields receives an immediate `400` response with the error message `"Malformed query body"` or `"Exactly one data query is required"`. This prevents injection attacks through unexpected request shapes and ensures predictable payload structure.

### 2. Noise Stripping via `stripOverpassNoise`

Before parsing query logic, the raw input passes through `stripOverpassNoise`, a single-pass lexer implemented at **lines 588-618** in [`vite.config.js`](https://github.com/bilawalsidhu/gods-eye-view/blob/main/vite.config.js). This function:

- **Collapses quoted string literals** to empty quotes to prevent string-based evasion
- **Removes block comments** (`/* … */`) and **line comments** (`// …`) that could hide malicious constructs
- **Guarantees comment markers inside strings** cannot terminate the string early and hide malicious code

By normalizing the query text, the lexer ensures subsequent validation stages operate on the actual query semantics rather than presentation formatting.

### 3. Spatial Bounding Enforcement

After noise removal, the sanitizer enforces strict geographic limits at **lines 636-702** to prevent planet-scale data extraction:

- **Radius limits**: Any `around:` clause exceeding **`OVERPASS_MAX_AROUND_M`** (approximately 5 kilometers) triggers rejection with the error `"Overpass around radius too large"`
- **Bounding-box limits**: Bbox tuples larger than **`OVERPASS_MAX_BBOX_DEG`** degrees are rejected to prevent excessive geographic coverage
- **Unbounded selector detection**: Every selector must be bounded either directly via `around:`, bbox, `is_in()`, or `area()`, or through a previously validated set. Queries containing unbounded selectors return `"Overpass query has an unbounded selector"`

These checks ensure the proxy only forwards queries with deterministic, limited result sets.

### 4. Construct Deny-Listing

The middleware maintains a deny-list of Overpass QL constructs that could generate unbounded results or consume excessive resources. Implemented at **lines 552-562**, this filter blocks:

- **Control-flow constructs**: `foreach`, `complete`, `retro`, `compare`, `convert`, and `make`
- **Complex filters**: The `poly:` filter

These constructs are rejected to prevent queries that might enumerate massive object collections or perform expensive geometric operations.

### 5. Timeout Clamping

To prevent long-running queries from tying up server resources, the sanitizer clamps any `[timeout:N]` directive to **`OVERPASS_MAX_QL_TIMEOUT`** (default 25 seconds) at **lines 708-712**. Even if a client requests a longer timeout, the middleware overrides it with the safe maximum before forwarding.

### 6. Final Payload Preparation

At **lines 724-733**, after passing all validation stages, the sanitized query is URL-encoded and formatted as `data=...` for the final POST body. If any check fails, the middleware returns a `400` status with a descriptive JSON error object, preventing invalid requests from ever reaching the Overpass backend.

## Client-Side Integration Example

The following JavaScript demonstrates a compliant client request that respects the middleware's sanitization requirements:

```javascript
// Correct: Bounded query with proper encoding
async function getRoads(lat, lon, radius = 500) {
  const query = `
    [out:json][timeout:25];
    way(around:${radius},${lat},${lon})["highway"]["name"];
    out geom;
  `;

  const resp = await fetch('/api/overpass', {
    method: 'POST',
    headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
    body: `data=${encodeURIComponent(query)}`,
  });

  if (!resp.ok) throw new Error(`Overpass error: ${await resp.text()}`);
  return resp.json();
}

```

Conversely, this query would be rejected by the sanitization pipeline due to excessive radius:

```javascript
// Incorrect: 60km radius exceeds OVERPASS_MAX_AROUND_M (5km)
const badQuery = `
  [out:json];
  node(around:60000,51.5,-0.1);
  out;
`;

await fetch('/api/overpass', {
  method: 'POST',
  headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
  body: `data=${encodeURIComponent(badQuery)}`,
});
// Returns: 400 Bad Request with "Overpass around radius too large"

```

## Summary

- **Request validation** enforces POST-only single-parameter bodies to prevent injection
- **`stripOverpassNoise`** (lines 588-618) normalizes queries by removing comments and handling string literals safely
- **Spatial bounds** (lines 636-702) enforce 5km radius limits, bbox degree caps, and prohibit unbounded selectors
- **Construct blocking** (lines 552-562) bans resource-intensive keywords like `foreach` and `poly:`
- **Timeout clamping** (lines 708-712) hard-limits execution to 25 seconds regardless of client requests
- **Safe encoding** (lines 724-733) prepares the validated payload for upstream transmission

## Frequently Asked Questions

### What happens if a query exceeds the 5km radius limit?

The middleware returns a `400 Bad Request` with the error message `"Overpass around radius too large"` and prevents the query from reaching the Overpass API. According to the source code at lines 636-702, any `around:` clause parsing to a radius greater than `OVERPASS_MAX_AROUND_M` triggers immediate rejection during the bounding check stage.

### Why does the middleware strip comments from Overpass QL queries?

Comments are stripped via the `stripOverpassNoise` lexer (lines 588-618) to prevent **comment-based evasion attacks** where malicious code hides inside comment markers that appear to be inside string literals. By collapsing strings to empty quotes and removing both block (`/* */`) and line (`//`) comments before validation, the sanitizer ensures the subsequent security checks analyze the actual query semantics.

### Which specific Overpass API constructs are blocked by the deny-list?

The middleware explicitly blocks control-flow constructs including `foreach`, `complete`, `retro`, `compare`, `convert`, and `make`, plus the `poly:` filter (lines 552-562). These constructs can generate large, unbounded result sets or perform computationally expensive geometric operations that would compromise server stability.

### How does request sanitization improve caching behavior?

By enforcing deterministic bounds—hard-limiting timeouts to 25 seconds, capping geographic areas to 5km radii, and rejecting unbounded selectors—the middleware ensures that cached responses remain reasonably sized and fast to retrieve. According to the [`vite.config.js`](https://github.com/bilawalsidhu/gods-eye-view/blob/main/vite.config.js) implementation, this prevents cache pollution from planet-scale queries that would otherwise evict useful, smaller result sets.