Skip to content

Replace codetabs with first-party proxy#25

Merged
SirajChokshi merged 8 commits into
mainfrom
cursor/-bc-1df83f69-aca0-4717-9d91-b3109ce612ef-8fcd
Apr 6, 2026
Merged

Replace codetabs with first-party proxy#25
SirajChokshi merged 8 commits into
mainfrom
cursor/-bc-1df83f69-aca0-4717-9d91-b3109ce612ef-8fcd

Conversation

@SirajChokshi
Copy link
Copy Markdown
Owner

@SirajChokshi SirajChokshi commented Apr 6, 2026

Summary

  • switch SvelteKit adapter from auto to Vercel
  • add /api/proxy server route to fetch allowlisted GitHub/GitLab raw content
  • replace client fetches from api.codetabs.com to internal proxy endpoint
  • add proxy policy tests covering target validation and CORS/origin rules
  • add .vercel/ to .gitignore
  • fix SSR runtime safety in logger and ignore .vercel/** in ESLint
  • tighten GitLab raw path validation to reject branch-only /-/raw/<branch> URLs without a file segment (off-by-one fix)

Security hardening

  • only allows HTTPS targets on raw.githubusercontent.com and GitLab /-/raw/ paths
  • rejects query strings and path traversal segments
  • enforces request-origin allowlist (PROXY_ALLOWED_ORIGINS, with sensible defaults)
  • caps upstream payload size to 5 MiB

Verification evidence

  • automated checks: focused proxy tests + lint + svelte-check
  • endpoint behavior checks:
    • /opt/cursor/artifacts/proxy_endpoint_checks.log
    • /opt/cursor/artifacts/gitlab_raw_branch_validation.log
  • GUI walkthrough demo showing loaded preview and network calls via /api/proxy:
    • proxy_route_end_to_end_demo.mp4
    • Preview loaded
    • Network request details

Notes

  • README + AGENTS updated with proxy configuration guidance.
Open in Web Open in Cursor 

Co-authored-by: Siraj Chokshi <SirajChokshi@users.noreply.github.com>
@vercel
Copy link
Copy Markdown

vercel Bot commented Apr 6, 2026

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

Project Deployment Actions Updated (UTC)
static-preview Ready Ready Preview, Comment Apr 6, 2026 10:56pm

Co-authored-by: Siraj Chokshi <SirajChokshi@users.noreply.github.com>
Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Autofix Details

Bugbot Autofix prepared fixes for both issues found in the latest run.

  • ✅ Fixed: GitLab path validation off-by-one allows branch-only URLs
    • Changed the length check from rawPathSegment + 2 to rawPathSegment + 3 to correctly require a file segment after the branch in GitLab raw paths.
  • ✅ Fixed: Proxy follows redirects without validating redirect destination
    • Replaced redirect: 'follow' with redirect: 'manual' and added a loop that validates each redirect target against isAllowedProxyTarget before following it, preventing SSRF via open redirects.

Create PR

Or push these changes by commenting:

@cursor push 26bb8719f4
Preview (26bb8719f4)
diff --git a/src/routes/api/proxy/+server.ts b/src/routes/api/proxy/+server.ts
--- a/src/routes/api/proxy/+server.ts
+++ b/src/routes/api/proxy/+server.ts
@@ -4,6 +4,7 @@
   getAllowedProxyOrigins,
   getRequestOrigin,
   isAllowedOrigin,
+  isAllowedProxyTarget,
   MAX_PROXY_RESPONSE_BYTES,
   parseProxyTarget,
   ProxyRequestError,
@@ -50,15 +51,41 @@
   }
 
   let upstream: Response
+  let currentTarget = target
 
   try {
-    upstream = await fetch(target.toString(), {
-      redirect: 'follow',
-      headers: {
-        Accept: request.headers.get('accept') ?? '*/*',
-      },
-    })
-  } catch {
+    const maxRedirects = 5
+    for (let i = 0; ; i++) {
+      upstream = await fetch(currentTarget.toString(), {
+        redirect: 'manual',
+        headers: {
+          Accept: request.headers.get('accept') ?? '*/*',
+        },
+      })
+
+      if (upstream.status < 300 || upstream.status >= 400) break
+      if (i >= maxRedirects) throw error(502, 'Too many redirects')
+
+      const location = upstream.headers.get('location')
+      if (!location) break
+
+      let redirectTarget: URL
+      try {
+        redirectTarget = new URL(location, currentTarget)
+      } catch {
+        throw error(502, 'Invalid redirect location')
+      }
+
+      redirectTarget.hash = ''
+
+      if (!isAllowedProxyTarget(redirectTarget)) {
+        throw error(403, 'Redirect target is not allowed')
+      }
+
+      currentTarget = redirectTarget
+    }
+  } catch (err) {
+    if (err && typeof err === 'object' && 'status' in err) throw err
     throw error(502, `Could not load ${target.toString()}`)
   }
 

diff --git a/src/utils/proxy.ts b/src/utils/proxy.ts
--- a/src/utils/proxy.ts
+++ b/src/utils/proxy.ts
@@ -70,7 +70,7 @@
 
   // /:owner/(optional subgroups...)/:repo/-/raw/:branch/:file...
   if (rawPathSegment < 2) return false
-  if (pathSegments.length <= rawPathSegment + 2) return false
+  if (pathSegments.length <= rawPathSegment + 3) return false
 
   return true
 }

You can send follow-ups to the cloud agent here.

Comment thread src/utils/proxy.ts Outdated
Comment thread src/routes/api/proxy/+server.ts
Co-authored-by: Siraj Chokshi <SirajChokshi@users.noreply.github.com>
@SirajChokshi SirajChokshi changed the title Replace codetabs with first-party SvelteKit proxy Replace codetabs with first-party proxy Apr 6, 2026
Co-authored-by: Siraj Chokshi <SirajChokshi@users.noreply.github.com>
Co-authored-by: Siraj Chokshi <SirajChokshi@users.noreply.github.com>
@SirajChokshi SirajChokshi marked this pull request as ready for review April 6, 2026 21:12
Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Autofix Details

Bugbot Autofix prepared a fix for the issue found in the latest run.

  • ✅ Fixed: Invalid configured origins silently fall back to permissive defaults
    • Changed getAllowedProxyOrigins to return only valid parsed origins (possibly empty) whenever configuredOrigins is defined, reserving the permissive default fallback for when the env var is truly undefined.

Create PR

Or push these changes by commenting:

@cursor push 22d9a116f6
Preview (22d9a116f6)
diff --git a/src/utils/proxy.ts b/src/utils/proxy.ts
--- a/src/utils/proxy.ts
+++ b/src/utils/proxy.ts
@@ -120,14 +120,14 @@
   configuredOrigins: string | undefined,
   requestOrigin: string,
 ): string[] {
-  const allowedOrigins = configuredOrigins
-    ?.split(',')
-    .map((origin) => origin.trim())
-    .filter(Boolean)
-    .map((origin) => normalizeOrigin(origin))
-    .filter((origin): origin is string => Boolean(origin))
+  if (configuredOrigins !== undefined) {
+    const allowedOrigins = configuredOrigins
+      .split(',')
+      .map((origin) => origin.trim())
+      .filter(Boolean)
+      .map((origin) => normalizeOrigin(origin))
+      .filter((origin): origin is string => Boolean(origin))
 
-  if (allowedOrigins && allowedOrigins.length > 0) {
     return [...new Set(allowedOrigins)]
   }

You can send follow-ups to the cloud agent here.

Comment thread src/utils/proxy.ts
…ns are invalid (#29)

When PROXY_ALLOWED_ORIGINS is set but all entries fail normalizeOrigin
(e.g. missing protocol), the function now returns the empty valid set
instead of silently falling back to DEFAULT_ALLOWED_ORIGINS which
includes localhost. Only fall back to defaults when the env var is
truly undefined.

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

Bugbot Autofix prepared a fix for the issue found in the latest run.

  • ✅ Fixed: Proxy serves arbitrary HTML at app origin enabling XSS
    • Added Content-Security-Policy: sandbox and Content-Disposition: attachment headers to proxy responses, preventing browsers from executing scripts or rendering HTML at the app's origin.

Create PR

Or push these changes by commenting:

@cursor push 42265fb190
Preview (42265fb190)
diff --git a/src/routes/api/proxy/+server.ts b/src/routes/api/proxy/+server.ts
--- a/src/routes/api/proxy/+server.ts
+++ b/src/routes/api/proxy/+server.ts
@@ -81,6 +81,8 @@
     upstream.headers.get('content-type') ?? 'text/plain; charset=utf-8',
   )
   headers.set('X-Content-Type-Options', 'nosniff')
+  headers.set('Content-Security-Policy', 'sandbox')
+  headers.set('Content-Disposition', 'attachment')
   headers.set(
     'Cache-Control',
     upstream.headers.get('cache-control') ?? 'public, max-age=300',

You can send follow-ups to the cloud agent here.

Reviewed by Cursor Bugbot for commit aa0bc52. Configure here.

Comment thread src/routes/api/proxy/+server.ts
cursoragent and others added 2 commits April 6, 2026 22:54
Co-authored-by: Siraj Chokshi <SirajChokshi@users.noreply.github.com>
…https://github.com/SirajChokshi/static-preview into cursor/-bc-1df83f69-aca0-4717-9d91-b3109ce612ef-8fcd

Co-authored-by: Siraj Chokshi <SirajChokshi@users.noreply.github.com>
@SirajChokshi SirajChokshi merged commit eb0d76c into main Apr 6, 2026
6 checks passed
@SirajChokshi SirajChokshi deleted the cursor/-bc-1df83f69-aca0-4717-9d91-b3109ce612ef-8fcd branch April 6, 2026 23:19
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.

2 participants