Secure /api/claude endpoint against key-burning abuse#3
Open
samirasadov28-code wants to merge 1 commit into
Open
Secure /api/claude endpoint against key-burning abuse#3samirasadov28-code wants to merge 1 commit into
samirasadov28-code wants to merge 1 commit into
Conversation
- Replace CORS `*` with an origin allowlist and require Origin/Referer to match before accepting a request, blocking cross-site browser abuse. - Add per-IP sliding-window (6/min) + daily (120/day) rate limits and a 4k-char prompt cap so a single abusive caller can't drain the key. - Bump to v2.3.1 (index.html splash + JS header, sw.js cache name).
✅ Deploy Preview for storyroute ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
samirasadov28-code
pushed a commit
that referenced
this pull request
Apr 25, 2026
…(v2.7.0) #2 Walk-to indicator - bearingTo() + bearingArrow() compute compass direction to next landmark - updateWalkIndicator() writes "↗ 180m" alongside the next-stop name in #next-info; called from prefetchStory() when nxtStory is set and from the GPS watchPosition handler so distance updates as the user walks #3 Route summary panel before tour start - After planRoute() fetches landmarks, showRouteSummary() renders an ordered stop list (sorted by routeIndexOf) with walk distance + estimated minutes between each pair; replaces the old immediate setMode('explore') jump - Each stop has a ✕ button → removeRouteStop() adds to routeSkipped Set and re-renders; nearest() now skips routeSkipped landmarks in route mode - "Start Tour · N stops" button calls startRouteTour() which hides the panel and calls startTour(); "← Edit route" goes back to the route input panel - routeSkipped is cleared whenever a new route is planned #4 Polyline-buffered landmark fetching - sampleRouteEvery(intervalM) walks routeCoords accumulating haversine distance and emits a sample point every intervalM metres (plus the last point), replacing the old 8 evenly-indexed fixed samples - planRoute() picks interval = radius/2.5 so fetch circles overlap: 1000m for walking (2500m radius), 3200m for driving (8000m), 8000m for long (20000m); fetchArea's tile-level dedup prevents redundant API calls https://claude.ai/code/session_015rSh7JMd58DyLGhUDcsQWe
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Closes the unauthenticated, wide-open
/api/claudeNetlify Edge Function flagged by the osmlab/awesome-openstreetmap maintainer (issue #183). Anyone could POST to it from any origin and burn the LLM API key with zero friction.Access-Control-Allow-Origin: *header and only accepts requests whoseOriginorRefereris on StoryRoute's own domain(s); everything else gets403.429on exceed (best-effort in-memory, paired with the origin check).messages[], returning413otherwise, so a single abusive call can't drain huge token counts.public/index.html(splash + JS header) andpublic/sw.js(cache name) perCLAUDE.md.Note on the report
The endpoint actually proxies Groq (
GROQ_API_KEY,api.groq.com) — only the path/filename isclaude. The abuse risk is the same, and is now mitigated; the Groq key should be rotated in Netlify env vars since it may have been scraped while the endpoint was open.If StoryRoute uses a custom domain beyond
storyroute.app/storyroute.netlify.app, add it toALLOWED_ORIGIN_SUFFIXESinnetlify/functions/claude.js.Test plan
GROQ_API_KEYstill set, confirm the app (served from the allowlisted origin) still generates stories.curl -X POST https://<site>/api/claude -d '{"messages":[{"role":"user","content":"hi"}]}' -H 'Content-Type: application/json'with noOrigin→ expect403.curlwith-H 'Origin: https://evil.example'→ expect403.429.413 Prompt too large.GROQ_API_KEYin Netlify and verify the app keeps working with the new value.https://claude.ai/code/session_012uCC14gY6NRHz9cowfjiag