fix(security): gate /api/gemini-test behind require_admin (#198) - #219

Merged
Jose-Gael-Cruz-Lopez merged 1 commit into
mainfrom
security/198-gate-gemini-test
Jun 14, 2026
Merged

fix(security): gate /api/gemini-test behind require_admin (#198)#219
Jose-Gael-Cruz-Lopez merged 1 commit into
mainfrom
security/198-gate-gemini-test

Conversation

@Jose-Gael-Cruz-Lopez

@Jose-Gael-Cruz-LopezJose-Gael-Cruz-Lopez commented Jun 13, 2026

Copy link
Copy Markdown
Member

Closes#198.

Problem

GET /api/gemini-test had no auth and made a real, billable call_gemini(...) round-trip on every hit. Anonymous callers could burn Gemini quota at will and use the {"ok": ...} response as an oracle for whether the API key is configured/valid — a debug endpoint left wired into the production app.

Fix

Gate the endpoint behind require_admin (the existing guard in services/auth_guard.py). Anonymous → 401, non-admin → 403, admin → reaches the connectivity check. Kept (rather than removed) so operators retain a deliberate, auth-gated Gemini probe; /api/health remains the unauthenticated liveness check.

Test (vuln-closing)

tests/test_gemini_test_auth.py:

  • test_unauthenticated_returns_401_and_makes_no_llm_call — restores the real auth guard (undoing conftest's autouse bypass) and asserts an anonymous GET returns 401 and call_gemini is never called. Fails on pre-fix code (returned 200 + a live LLM call); passes post-fix.
  • test_admin_reaches_handler — admin-equivalent request reaches the handler (LLM mocked).

Scope

One file + one test. No behavior change for authenticated admins.

⚠️ Per Wave-1 convention: do not merge — opened for review.

Summary by CodeRabbit

  • Bug Fixes

    • Secured admin endpoint with authentication enforcement to prevent unauthorized access and unintended API quota usage.
  • Tests

    • Added tests to validate authentication requirements for the admin endpoint.

The endpoint made a real, billable call_gemini round-trip on every hit with
no auth, so anonymous callers could burn Gemini quota and use the {"ok": ...}
response as an oracle for whether the API key is configured. Gate it behind
require_admin so only admins can trigger LLM spend.
Adds a regression test: an unauthenticated GET returns 401 and never reaches
call_gemini (fails on pre-fix code, which returned 200).
@cloudflare-workers-and-pages

cloudflare-workers-and-pagesBot commented Jun 13, 2026

Copy link
Copy Markdown

Deploying with Cloudflare Workers Cloudflare Workers

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

StatusNameLatest CommitPreview URLUpdated (UTC)
✅ Deployment successful!
View logs
frontendf4344b9Commit Preview URL

Branch Preview URL
Jun 13 2026, 03:40 AM

@coderabbitai

coderabbitaiBot commented Jun 13, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 9f044721-b330-4a1b-9795-fc6c359f6f64

📥 Commits

Reviewing files that changed from the base of the PR and between a27b688 and f4344b9.

📒 Files selected for processing (2)
  • backend/main.py
  • backend/tests/test_gemini_test_auth.py

📝 Walkthrough

Walkthrough

The /api/gemini-test endpoint is updated to require admin authentication via require_admin(request), preventing unauthenticated quota burn. Unauthenticated requests return 401 and do not trigger Gemini calls. Admin-authenticated requests proceed to execute the endpoint's existing billable Gemini connectivity check.

Changes

Admin-gated Gemini endpoint

Layer / File(s)Summary
Admin authorization in endpoint
backend/main.py
Route handler accepts Request, calls require_admin(request) to enforce admin-only access, and rewritten docstring explains the billable, admin-gated Gemini round-trip. Existing success and error response paths remain unchanged.
Authentication regression tests
backend/tests/test_gemini_test_auth.py
New test class with two test cases: unauthenticated request restored real auth guards and asserts 401 without Gemini invocation; admin-path test mocks Gemini and asserts 200 with expected JSON payload.

Sequence Diagram

sequenceDiagram
participant AnonymousClient
participant AdminClient
participant Handler as /api/gemini-test Handler
participant Auth as require_admin
participant Gemini as call_gemini
AnonymousClient->>Handler: GET /api/gemini-test
Handler->>Auth: require_admin(request)
Auth-->>Handler: 401 Unauthorized
Handler-->>AnonymousClient: {"error": "Unauthorized"}
AdminClient->>Handler: GET /api/gemini-test (authenticated)
Handler->>Auth: require_admin(request)
Auth-->>Handler: allowed
Handler->>Gemini: call_gemini()
Gemini-->>Handler: "Gemini response"
Handler-->>AdminClient: {"ok": true, "reply": "Gemini response"}
Loading

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Poem

🐰 The test endpoint once ran wild and free,
Burning quota for all to see—
Now it bows to the admin gate,
Gemini calls keep their proper fate!
No more oracle for the spree,
Just billable checks, safe as can be! 🔐

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 33.33% 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 nameStatusExplanation
Title check✅ PassedThe title accurately and concisely describes the main security fix: adding admin-level authentication to the /api/gemini-test endpoint, which directly addresses the vulnerability in issue #198.
Description check✅ PassedThe description comprehensively covers the problem statement, the implemented fix, the test strategy (including both vulnerability-closing tests), scope, and notes for reviewers. It aligns well with the provided template structure.
Linked Issues check✅ PassedThe PR successfully implements the acceptance criteria from #198: the /api/gemini-test endpoint is now gated behind require_admin, preventing anonymous callers from triggering LLM spend. Code changes and tests directly validate this requirement.
Out of Scope Changes check✅ PassedAll changes are directly scoped to addressing issue #198: one file modified (backend/main.py) and one test file added (backend/tests/test_gemini_test_auth.py). No extraneous changes detected.

✏️ 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 security/198-gate-gemini-test

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.

@Jose-Gael-Cruz-Lopez
Jose-Gael-Cruz-Lopez merged commit f4b9174 into mainJun 14, 2026
6 checks passed
@Jose-Gael-Cruz-Lopez
Jose-Gael-Cruz-Lopez deleted the security/198-gate-gemini-test branch June 14, 2026 02:17
Sign up for freeto 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.

/api/gemini-test is an unauthenticated live-LLM cost/key oracle

1 participant

@Jose-Gael-Cruz-Lopez
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

fix(security): gate /api/gemini-test behind require_admin (#198) - #219

Merged
Jose-Gael-Cruz-Lopez merged 1 commit into
mainfrom
security/198-gate-gemini-test
Jun 14, 2026
Merged

fix(security): gate /api/gemini-test behind require_admin (#198)#219
Jose-Gael-Cruz-Lopez merged 1 commit into
mainfrom
security/198-gate-gemini-test

Conversation

@Jose-Gael-Cruz-Lopez

@Jose-Gael-Cruz-LopezJose-Gael-Cruz-Lopez commented Jun 13, 2026

Copy link
Copy Markdown
Member

Closes#198.

Problem

GET /api/gemini-test had no auth and made a real, billable call_gemini(...) round-trip on every hit. Anonymous callers could burn Gemini quota at will and use the {"ok": ...} response as an oracle for whether the API key is configured/valid — a debug endpoint left wired into the production app.

Fix

Gate the endpoint behind require_admin (the existing guard in services/auth_guard.py). Anonymous → 401, non-admin → 403, admin → reaches the connectivity check. Kept (rather than removed) so operators retain a deliberate, auth-gated Gemini probe; /api/health remains the unauthenticated liveness check.

Test (vuln-closing)

tests/test_gemini_test_auth.py:

  • test_unauthenticated_returns_401_and_makes_no_llm_call — restores the real auth guard (undoing conftest's autouse bypass) and asserts an anonymous GET returns 401 and call_gemini is never called. Fails on pre-fix code (returned 200 + a live LLM call); passes post-fix.
  • test_admin_reaches_handler — admin-equivalent request reaches the handler (LLM mocked).

Scope

One file + one test. No behavior change for authenticated admins.

⚠️ Per Wave-1 convention: do not merge — opened for review.

Summary by CodeRabbit

  • Bug Fixes

    • Secured admin endpoint with authentication enforcement to prevent unauthorized access and unintended API quota usage.
  • Tests

    • Added tests to validate authentication requirements for the admin endpoint.

The endpoint made a real, billable call_gemini round-trip on every hit with
no auth, so anonymous callers could burn Gemini quota and use the {"ok": ...}
response as an oracle for whether the API key is configured. Gate it behind
require_admin so only admins can trigger LLM spend.
Adds a regression test: an unauthenticated GET returns 401 and never reaches
call_gemini (fails on pre-fix code, which returned 200).
@cloudflare-workers-and-pages

cloudflare-workers-and-pagesBot commented Jun 13, 2026

Copy link
Copy Markdown

Deploying with Cloudflare Workers Cloudflare Workers

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

StatusNameLatest CommitPreview URLUpdated (UTC)
✅ Deployment successful!
View logs
frontendf4344b9Commit Preview URL

Branch Preview URL
Jun 13 2026, 03:40 AM

@coderabbitai

coderabbitaiBot commented Jun 13, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 9f044721-b330-4a1b-9795-fc6c359f6f64

📥 Commits

Reviewing files that changed from the base of the PR and between a27b688 and f4344b9.

📒 Files selected for processing (2)
  • backend/main.py
  • backend/tests/test_gemini_test_auth.py

📝 Walkthrough

Walkthrough

The /api/gemini-test endpoint is updated to require admin authentication via require_admin(request), preventing unauthenticated quota burn. Unauthenticated requests return 401 and do not trigger Gemini calls. Admin-authenticated requests proceed to execute the endpoint's existing billable Gemini connectivity check.

Changes

Admin-gated Gemini endpoint

Layer / File(s)Summary
Admin authorization in endpoint
backend/main.py
Route handler accepts Request, calls require_admin(request) to enforce admin-only access, and rewritten docstring explains the billable, admin-gated Gemini round-trip. Existing success and error response paths remain unchanged.
Authentication regression tests
backend/tests/test_gemini_test_auth.py
New test class with two test cases: unauthenticated request restored real auth guards and asserts 401 without Gemini invocation; admin-path test mocks Gemini and asserts 200 with expected JSON payload.

Sequence Diagram

sequenceDiagram
participant AnonymousClient
participant AdminClient
participant Handler as /api/gemini-test Handler
participant Auth as require_admin
participant Gemini as call_gemini
AnonymousClient->>Handler: GET /api/gemini-test
Handler->>Auth: require_admin(request)
Auth-->>Handler: 401 Unauthorized
Handler-->>AnonymousClient: {"error": "Unauthorized"}
AdminClient->>Handler: GET /api/gemini-test (authenticated)
Handler->>Auth: require_admin(request)
Auth-->>Handler: allowed
Handler->>Gemini: call_gemini()
Gemini-->>Handler: "Gemini response"
Handler-->>AdminClient: {"ok": true, "reply": "Gemini response"}
Loading

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Poem

🐰 The test endpoint once ran wild and free,
Burning quota for all to see—
Now it bows to the admin gate,
Gemini calls keep their proper fate!
No more oracle for the spree,
Just billable checks, safe as can be! 🔐

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 33.33% 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 nameStatusExplanation
Title check✅ PassedThe title accurately and concisely describes the main security fix: adding admin-level authentication to the /api/gemini-test endpoint, which directly addresses the vulnerability in issue #198.
Description check✅ PassedThe description comprehensively covers the problem statement, the implemented fix, the test strategy (including both vulnerability-closing tests), scope, and notes for reviewers. It aligns well with the provided template structure.
Linked Issues check✅ PassedThe PR successfully implements the acceptance criteria from #198: the /api/gemini-test endpoint is now gated behind require_admin, preventing anonymous callers from triggering LLM spend. Code changes and tests directly validate this requirement.
Out of Scope Changes check✅ PassedAll changes are directly scoped to addressing issue #198: one file modified (backend/main.py) and one test file added (backend/tests/test_gemini_test_auth.py). No extraneous changes detected.

✏️ 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 security/198-gate-gemini-test

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.

@Jose-Gael-Cruz-Lopez
Jose-Gael-Cruz-Lopez merged commit f4b9174 into mainJun 14, 2026
6 checks passed
@Jose-Gael-Cruz-Lopez
Jose-Gael-Cruz-Lopez deleted the security/198-gate-gemini-test branch June 14, 2026 02:17
Sign up for freeto 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.

/api/gemini-test is an unauthenticated live-LLM cost/key oracle

1 participant

@Jose-Gael-Cruz-Lopez
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(security): gate /api/gemini-test behind require_admin (#198) - #219

Merged
Jose-Gael-Cruz-Lopez merged 1 commit into
mainfrom
security/198-gate-gemini-test
Jun 14, 2026
Merged

fix(security): gate /api/gemini-test behind require_admin (#198)#219
Jose-Gael-Cruz-Lopez merged 1 commit into
mainfrom
security/198-gate-gemini-test

Conversation

@Jose-Gael-Cruz-Lopez

@Jose-Gael-Cruz-LopezJose-Gael-Cruz-Lopez commented Jun 13, 2026

Copy link
Copy Markdown
Member

Closes#198.

Problem

GET /api/gemini-test had no auth and made a real, billable call_gemini(...) round-trip on every hit. Anonymous callers could burn Gemini quota at will and use the {"ok": ...} response as an oracle for whether the API key is configured/valid — a debug endpoint left wired into the production app.

Fix

Gate the endpoint behind require_admin (the existing guard in services/auth_guard.py). Anonymous → 401, non-admin → 403, admin → reaches the connectivity check. Kept (rather than removed) so operators retain a deliberate, auth-gated Gemini probe; /api/health remains the unauthenticated liveness check.

Test (vuln-closing)

tests/test_gemini_test_auth.py:

  • test_unauthenticated_returns_401_and_makes_no_llm_call — restores the real auth guard (undoing conftest's autouse bypass) and asserts an anonymous GET returns 401 and call_gemini is never called. Fails on pre-fix code (returned 200 + a live LLM call); passes post-fix.
  • test_admin_reaches_handler — admin-equivalent request reaches the handler (LLM mocked).

Scope

One file + one test. No behavior change for authenticated admins.

⚠️ Per Wave-1 convention: do not merge — opened for review.

Summary by CodeRabbit

  • Bug Fixes

    • Secured admin endpoint with authentication enforcement to prevent unauthorized access and unintended API quota usage.
  • Tests

    • Added tests to validate authentication requirements for the admin endpoint.

The endpoint made a real, billable call_gemini round-trip on every hit with
no auth, so anonymous callers could burn Gemini quota and use the {"ok": ...}
response as an oracle for whether the API key is configured. Gate it behind
require_admin so only admins can trigger LLM spend.
Adds a regression test: an unauthenticated GET returns 401 and never reaches
call_gemini (fails on pre-fix code, which returned 200).
@cloudflare-workers-and-pages

cloudflare-workers-and-pagesBot commented Jun 13, 2026

Copy link
Copy Markdown

Deploying with Cloudflare Workers Cloudflare Workers

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

StatusNameLatest CommitPreview URLUpdated (UTC)
✅ Deployment successful!
View logs
frontendf4344b9Commit Preview URL

Branch Preview URL
Jun 13 2026, 03:40 AM

@coderabbitai

coderabbitaiBot commented Jun 13, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 9f044721-b330-4a1b-9795-fc6c359f6f64

📥 Commits

Reviewing files that changed from the base of the PR and between a27b688 and f4344b9.

📒 Files selected for processing (2)
  • backend/main.py
  • backend/tests/test_gemini_test_auth.py

📝 Walkthrough

Walkthrough

The /api/gemini-test endpoint is updated to require admin authentication via require_admin(request), preventing unauthenticated quota burn. Unauthenticated requests return 401 and do not trigger Gemini calls. Admin-authenticated requests proceed to execute the endpoint's existing billable Gemini connectivity check.

Changes

Admin-gated Gemini endpoint

Layer / File(s)Summary
Admin authorization in endpoint
backend/main.py
Route handler accepts Request, calls require_admin(request) to enforce admin-only access, and rewritten docstring explains the billable, admin-gated Gemini round-trip. Existing success and error response paths remain unchanged.
Authentication regression tests
backend/tests/test_gemini_test_auth.py
New test class with two test cases: unauthenticated request restored real auth guards and asserts 401 without Gemini invocation; admin-path test mocks Gemini and asserts 200 with expected JSON payload.

Sequence Diagram

sequenceDiagram
participant AnonymousClient
participant AdminClient
participant Handler as /api/gemini-test Handler
participant Auth as require_admin
participant Gemini as call_gemini
AnonymousClient->>Handler: GET /api/gemini-test
Handler->>Auth: require_admin(request)
Auth-->>Handler: 401 Unauthorized
Handler-->>AnonymousClient: {"error": "Unauthorized"}
AdminClient->>Handler: GET /api/gemini-test (authenticated)
Handler->>Auth: require_admin(request)
Auth-->>Handler: allowed
Handler->>Gemini: call_gemini()
Gemini-->>Handler: "Gemini response"
Handler-->>AdminClient: {"ok": true, "reply": "Gemini response"}
Loading

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Poem

🐰 The test endpoint once ran wild and free,
Burning quota for all to see—
Now it bows to the admin gate,
Gemini calls keep their proper fate!
No more oracle for the spree,
Just billable checks, safe as can be! 🔐

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 33.33% 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 nameStatusExplanation
Title check✅ PassedThe title accurately and concisely describes the main security fix: adding admin-level authentication to the /api/gemini-test endpoint, which directly addresses the vulnerability in issue #198.
Description check✅ PassedThe description comprehensively covers the problem statement, the implemented fix, the test strategy (including both vulnerability-closing tests), scope, and notes for reviewers. It aligns well with the provided template structure.
Linked Issues check✅ PassedThe PR successfully implements the acceptance criteria from #198: the /api/gemini-test endpoint is now gated behind require_admin, preventing anonymous callers from triggering LLM spend. Code changes and tests directly validate this requirement.
Out of Scope Changes check✅ PassedAll changes are directly scoped to addressing issue #198: one file modified (backend/main.py) and one test file added (backend/tests/test_gemini_test_auth.py). No extraneous changes detected.

✏️ 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 security/198-gate-gemini-test

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.

@Jose-Gael-Cruz-Lopez
Jose-Gael-Cruz-Lopez merged commit f4b9174 into mainJun 14, 2026
6 checks passed
@Jose-Gael-Cruz-Lopez
Jose-Gael-Cruz-Lopez deleted the security/198-gate-gemini-test branch June 14, 2026 02:17
Sign up for freeto 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.

/api/gemini-test is an unauthenticated live-LLM cost/key oracle

1 participant

@Jose-Gael-Cruz-Lopez
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(security): gate /api/gemini-test behind require_admin (#198) - #219

Merged
Jose-Gael-Cruz-Lopez merged 1 commit into
mainfrom
security/198-gate-gemini-test
Jun 14, 2026
Merged

fix(security): gate /api/gemini-test behind require_admin (#198)#219
Jose-Gael-Cruz-Lopez merged 1 commit into
mainfrom
security/198-gate-gemini-test

Conversation

@Jose-Gael-Cruz-Lopez

@Jose-Gael-Cruz-LopezJose-Gael-Cruz-Lopez commented Jun 13, 2026

Copy link
Copy Markdown
Member

Closes#198.

Problem

GET /api/gemini-test had no auth and made a real, billable call_gemini(...) round-trip on every hit. Anonymous callers could burn Gemini quota at will and use the {"ok": ...} response as an oracle for whether the API key is configured/valid — a debug endpoint left wired into the production app.

Fix

Gate the endpoint behind require_admin (the existing guard in services/auth_guard.py). Anonymous → 401, non-admin → 403, admin → reaches the connectivity check. Kept (rather than removed) so operators retain a deliberate, auth-gated Gemini probe; /api/health remains the unauthenticated liveness check.

Test (vuln-closing)

tests/test_gemini_test_auth.py:

  • test_unauthenticated_returns_401_and_makes_no_llm_call — restores the real auth guard (undoing conftest's autouse bypass) and asserts an anonymous GET returns 401 and call_gemini is never called. Fails on pre-fix code (returned 200 + a live LLM call); passes post-fix.
  • test_admin_reaches_handler — admin-equivalent request reaches the handler (LLM mocked).

Scope

One file + one test. No behavior change for authenticated admins.

⚠️ Per Wave-1 convention: do not merge — opened for review.

Summary by CodeRabbit

  • Bug Fixes

    • Secured admin endpoint with authentication enforcement to prevent unauthorized access and unintended API quota usage.
  • Tests

    • Added tests to validate authentication requirements for the admin endpoint.

The endpoint made a real, billable call_gemini round-trip on every hit with
no auth, so anonymous callers could burn Gemini quota and use the {"ok": ...}
response as an oracle for whether the API key is configured. Gate it behind
require_admin so only admins can trigger LLM spend.
Adds a regression test: an unauthenticated GET returns 401 and never reaches
call_gemini (fails on pre-fix code, which returned 200).
@cloudflare-workers-and-pages

cloudflare-workers-and-pagesBot commented Jun 13, 2026

Copy link
Copy Markdown

Deploying with Cloudflare Workers Cloudflare Workers

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

StatusNameLatest CommitPreview URLUpdated (UTC)
✅ Deployment successful!
View logs
frontendf4344b9Commit Preview URL

Branch Preview URL
Jun 13 2026, 03:40 AM

@coderabbitai

coderabbitaiBot commented Jun 13, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 9f044721-b330-4a1b-9795-fc6c359f6f64

📥 Commits

Reviewing files that changed from the base of the PR and between a27b688 and f4344b9.

📒 Files selected for processing (2)
  • backend/main.py
  • backend/tests/test_gemini_test_auth.py

📝 Walkthrough

Walkthrough

The /api/gemini-test endpoint is updated to require admin authentication via require_admin(request), preventing unauthenticated quota burn. Unauthenticated requests return 401 and do not trigger Gemini calls. Admin-authenticated requests proceed to execute the endpoint's existing billable Gemini connectivity check.

Changes

Admin-gated Gemini endpoint

Layer / File(s)Summary
Admin authorization in endpoint
backend/main.py
Route handler accepts Request, calls require_admin(request) to enforce admin-only access, and rewritten docstring explains the billable, admin-gated Gemini round-trip. Existing success and error response paths remain unchanged.
Authentication regression tests
backend/tests/test_gemini_test_auth.py
New test class with two test cases: unauthenticated request restored real auth guards and asserts 401 without Gemini invocation; admin-path test mocks Gemini and asserts 200 with expected JSON payload.

Sequence Diagram

sequenceDiagram
participant AnonymousClient
participant AdminClient
participant Handler as /api/gemini-test Handler
participant Auth as require_admin
participant Gemini as call_gemini
AnonymousClient->>Handler: GET /api/gemini-test
Handler->>Auth: require_admin(request)
Auth-->>Handler: 401 Unauthorized
Handler-->>AnonymousClient: {"error": "Unauthorized"}
AdminClient->>Handler: GET /api/gemini-test (authenticated)
Handler->>Auth: require_admin(request)
Auth-->>Handler: allowed
Handler->>Gemini: call_gemini()
Gemini-->>Handler: "Gemini response"
Handler-->>AdminClient: {"ok": true, "reply": "Gemini response"}
Loading

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Poem

🐰 The test endpoint once ran wild and free,
Burning quota for all to see—
Now it bows to the admin gate,
Gemini calls keep their proper fate!
No more oracle for the spree,
Just billable checks, safe as can be! 🔐

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 33.33% 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 nameStatusExplanation
Title check✅ PassedThe title accurately and concisely describes the main security fix: adding admin-level authentication to the /api/gemini-test endpoint, which directly addresses the vulnerability in issue #198.
Description check✅ PassedThe description comprehensively covers the problem statement, the implemented fix, the test strategy (including both vulnerability-closing tests), scope, and notes for reviewers. It aligns well with the provided template structure.
Linked Issues check✅ PassedThe PR successfully implements the acceptance criteria from #198: the /api/gemini-test endpoint is now gated behind require_admin, preventing anonymous callers from triggering LLM spend. Code changes and tests directly validate this requirement.
Out of Scope Changes check✅ PassedAll changes are directly scoped to addressing issue #198: one file modified (backend/main.py) and one test file added (backend/tests/test_gemini_test_auth.py). No extraneous changes detected.

✏️ 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 security/198-gate-gemini-test

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.

@Jose-Gael-Cruz-Lopez
Jose-Gael-Cruz-Lopez merged commit f4b9174 into mainJun 14, 2026
6 checks passed
@Jose-Gael-Cruz-Lopez
Jose-Gael-Cruz-Lopez deleted the security/198-gate-gemini-test branch June 14, 2026 02:17
Sign up for freeto 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.

/api/gemini-test is an unauthenticated live-LLM cost/key oracle

1 participant

@Jose-Gael-Cruz-Lopez
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

fix(security): gate /api/gemini-test behind require_admin (#198) - #219

Merged
Jose-Gael-Cruz-Lopez merged 1 commit into
mainfrom
security/198-gate-gemini-test
Jun 14, 2026
Merged

fix(security): gate /api/gemini-test behind require_admin (#198)#219
Jose-Gael-Cruz-Lopez merged 1 commit into
mainfrom
security/198-gate-gemini-test

Conversation

@Jose-Gael-Cruz-Lopez

@Jose-Gael-Cruz-LopezJose-Gael-Cruz-Lopez commented Jun 13, 2026

Copy link
Copy Markdown
Member

Closes#198.

Problem

GET /api/gemini-test had no auth and made a real, billable call_gemini(...) round-trip on every hit. Anonymous callers could burn Gemini quota at will and use the {"ok": ...} response as an oracle for whether the API key is configured/valid — a debug endpoint left wired into the production app.

Fix

Gate the endpoint behind require_admin (the existing guard in services/auth_guard.py). Anonymous → 401, non-admin → 403, admin → reaches the connectivity check. Kept (rather than removed) so operators retain a deliberate, auth-gated Gemini probe; /api/health remains the unauthenticated liveness check.

Test (vuln-closing)

tests/test_gemini_test_auth.py:

  • test_unauthenticated_returns_401_and_makes_no_llm_call — restores the real auth guard (undoing conftest's autouse bypass) and asserts an anonymous GET returns 401 and call_gemini is never called. Fails on pre-fix code (returned 200 + a live LLM call); passes post-fix.
  • test_admin_reaches_handler — admin-equivalent request reaches the handler (LLM mocked).

Scope

One file + one test. No behavior change for authenticated admins.

⚠️ Per Wave-1 convention: do not merge — opened for review.

Summary by CodeRabbit

  • Bug Fixes

    • Secured admin endpoint with authentication enforcement to prevent unauthorized access and unintended API quota usage.
  • Tests

    • Added tests to validate authentication requirements for the admin endpoint.

The endpoint made a real, billable call_gemini round-trip on every hit with
no auth, so anonymous callers could burn Gemini quota and use the {"ok": ...}
response as an oracle for whether the API key is configured. Gate it behind
require_admin so only admins can trigger LLM spend.
Adds a regression test: an unauthenticated GET returns 401 and never reaches
call_gemini (fails on pre-fix code, which returned 200).
@cloudflare-workers-and-pages

cloudflare-workers-and-pagesBot commented Jun 13, 2026

Copy link
Copy Markdown

Deploying with Cloudflare Workers Cloudflare Workers

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

StatusNameLatest CommitPreview URLUpdated (UTC)
✅ Deployment successful!
View logs
frontendf4344b9Commit Preview URL

Branch Preview URL
Jun 13 2026, 03:40 AM

@coderabbitai

coderabbitaiBot commented Jun 13, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 9f044721-b330-4a1b-9795-fc6c359f6f64

📥 Commits

Reviewing files that changed from the base of the PR and between a27b688 and f4344b9.

📒 Files selected for processing (2)
  • backend/main.py
  • backend/tests/test_gemini_test_auth.py

📝 Walkthrough

Walkthrough

The /api/gemini-test endpoint is updated to require admin authentication via require_admin(request), preventing unauthenticated quota burn. Unauthenticated requests return 401 and do not trigger Gemini calls. Admin-authenticated requests proceed to execute the endpoint's existing billable Gemini connectivity check.

Changes

Admin-gated Gemini endpoint

Layer / File(s)Summary
Admin authorization in endpoint
backend/main.py
Route handler accepts Request, calls require_admin(request) to enforce admin-only access, and rewritten docstring explains the billable, admin-gated Gemini round-trip. Existing success and error response paths remain unchanged.
Authentication regression tests
backend/tests/test_gemini_test_auth.py
New test class with two test cases: unauthenticated request restored real auth guards and asserts 401 without Gemini invocation; admin-path test mocks Gemini and asserts 200 with expected JSON payload.

Sequence Diagram

sequenceDiagram
participant AnonymousClient
participant AdminClient
participant Handler as /api/gemini-test Handler
participant Auth as require_admin
participant Gemini as call_gemini
AnonymousClient->>Handler: GET /api/gemini-test
Handler->>Auth: require_admin(request)
Auth-->>Handler: 401 Unauthorized
Handler-->>AnonymousClient: {"error": "Unauthorized"}
AdminClient->>Handler: GET /api/gemini-test (authenticated)
Handler->>Auth: require_admin(request)
Auth-->>Handler: allowed
Handler->>Gemini: call_gemini()
Gemini-->>Handler: "Gemini response"
Handler-->>AdminClient: {"ok": true, "reply": "Gemini response"}
Loading

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Poem

🐰 The test endpoint once ran wild and free,
Burning quota for all to see—
Now it bows to the admin gate,
Gemini calls keep their proper fate!
No more oracle for the spree,
Just billable checks, safe as can be! 🔐

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 33.33% 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 nameStatusExplanation
Title check✅ PassedThe title accurately and concisely describes the main security fix: adding admin-level authentication to the /api/gemini-test endpoint, which directly addresses the vulnerability in issue #198.
Description check✅ PassedThe description comprehensively covers the problem statement, the implemented fix, the test strategy (including both vulnerability-closing tests), scope, and notes for reviewers. It aligns well with the provided template structure.
Linked Issues check✅ PassedThe PR successfully implements the acceptance criteria from #198: the /api/gemini-test endpoint is now gated behind require_admin, preventing anonymous callers from triggering LLM spend. Code changes and tests directly validate this requirement.
Out of Scope Changes check✅ PassedAll changes are directly scoped to addressing issue #198: one file modified (backend/main.py) and one test file added (backend/tests/test_gemini_test_auth.py). No extraneous changes detected.

✏️ 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 security/198-gate-gemini-test

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.

@Jose-Gael-Cruz-Lopez
Jose-Gael-Cruz-Lopez merged commit f4b9174 into mainJun 14, 2026
6 checks passed
@Jose-Gael-Cruz-Lopez
Jose-Gael-Cruz-Lopez deleted the security/198-gate-gemini-test branch June 14, 2026 02:17
Sign up for freeto 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.

/api/gemini-test is an unauthenticated live-LLM cost/key oracle

1 participant

@Jose-Gael-Cruz-Lopez
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(security): gate /api/gemini-test behind require_admin (#198) - #219

Merged
Jose-Gael-Cruz-Lopez merged 1 commit into
mainfrom
security/198-gate-gemini-test
Jun 14, 2026
Merged

fix(security): gate /api/gemini-test behind require_admin (#198)#219
Jose-Gael-Cruz-Lopez merged 1 commit into
mainfrom
security/198-gate-gemini-test

Conversation

@Jose-Gael-Cruz-Lopez

@Jose-Gael-Cruz-LopezJose-Gael-Cruz-Lopez commented Jun 13, 2026

Copy link
Copy Markdown
Member

Closes#198.

Problem

GET /api/gemini-test had no auth and made a real, billable call_gemini(...) round-trip on every hit. Anonymous callers could burn Gemini quota at will and use the {"ok": ...} response as an oracle for whether the API key is configured/valid — a debug endpoint left wired into the production app.

Fix

Gate the endpoint behind require_admin (the existing guard in services/auth_guard.py). Anonymous → 401, non-admin → 403, admin → reaches the connectivity check. Kept (rather than removed) so operators retain a deliberate, auth-gated Gemini probe; /api/health remains the unauthenticated liveness check.

Test (vuln-closing)

tests/test_gemini_test_auth.py:

  • test_unauthenticated_returns_401_and_makes_no_llm_call — restores the real auth guard (undoing conftest's autouse bypass) and asserts an anonymous GET returns 401 and call_gemini is never called. Fails on pre-fix code (returned 200 + a live LLM call); passes post-fix.
  • test_admin_reaches_handler — admin-equivalent request reaches the handler (LLM mocked).

Scope

One file + one test. No behavior change for authenticated admins.

⚠️ Per Wave-1 convention: do not merge — opened for review.

Summary by CodeRabbit

  • Bug Fixes

    • Secured admin endpoint with authentication enforcement to prevent unauthorized access and unintended API quota usage.
  • Tests

    • Added tests to validate authentication requirements for the admin endpoint.

The endpoint made a real, billable call_gemini round-trip on every hit with
no auth, so anonymous callers could burn Gemini quota and use the {"ok": ...}
response as an oracle for whether the API key is configured. Gate it behind
require_admin so only admins can trigger LLM spend.
Adds a regression test: an unauthenticated GET returns 401 and never reaches
call_gemini (fails on pre-fix code, which returned 200).
@cloudflare-workers-and-pages

cloudflare-workers-and-pagesBot commented Jun 13, 2026

Copy link
Copy Markdown

Deploying with Cloudflare Workers Cloudflare Workers

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

StatusNameLatest CommitPreview URLUpdated (UTC)
✅ Deployment successful!
View logs
frontendf4344b9Commit Preview URL

Branch Preview URL
Jun 13 2026, 03:40 AM

@coderabbitai

coderabbitaiBot commented Jun 13, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 9f044721-b330-4a1b-9795-fc6c359f6f64

📥 Commits

Reviewing files that changed from the base of the PR and between a27b688 and f4344b9.

📒 Files selected for processing (2)
  • backend/main.py
  • backend/tests/test_gemini_test_auth.py

📝 Walkthrough

Walkthrough

The /api/gemini-test endpoint is updated to require admin authentication via require_admin(request), preventing unauthenticated quota burn. Unauthenticated requests return 401 and do not trigger Gemini calls. Admin-authenticated requests proceed to execute the endpoint's existing billable Gemini connectivity check.

Changes

Admin-gated Gemini endpoint

Layer / File(s)Summary
Admin authorization in endpoint
backend/main.py
Route handler accepts Request, calls require_admin(request) to enforce admin-only access, and rewritten docstring explains the billable, admin-gated Gemini round-trip. Existing success and error response paths remain unchanged.
Authentication regression tests
backend/tests/test_gemini_test_auth.py
New test class with two test cases: unauthenticated request restored real auth guards and asserts 401 without Gemini invocation; admin-path test mocks Gemini and asserts 200 with expected JSON payload.

Sequence Diagram

sequenceDiagram
participant AnonymousClient
participant AdminClient
participant Handler as /api/gemini-test Handler
participant Auth as require_admin
participant Gemini as call_gemini
AnonymousClient->>Handler: GET /api/gemini-test
Handler->>Auth: require_admin(request)
Auth-->>Handler: 401 Unauthorized
Handler-->>AnonymousClient: {"error": "Unauthorized"}
AdminClient->>Handler: GET /api/gemini-test (authenticated)
Handler->>Auth: require_admin(request)
Auth-->>Handler: allowed
Handler->>Gemini: call_gemini()
Gemini-->>Handler: "Gemini response"
Handler-->>AdminClient: {"ok": true, "reply": "Gemini response"}
Loading

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Poem

🐰 The test endpoint once ran wild and free,
Burning quota for all to see—
Now it bows to the admin gate,
Gemini calls keep their proper fate!
No more oracle for the spree,
Just billable checks, safe as can be! 🔐

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 33.33% 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 nameStatusExplanation
Title check✅ PassedThe title accurately and concisely describes the main security fix: adding admin-level authentication to the /api/gemini-test endpoint, which directly addresses the vulnerability in issue #198.
Description check✅ PassedThe description comprehensively covers the problem statement, the implemented fix, the test strategy (including both vulnerability-closing tests), scope, and notes for reviewers. It aligns well with the provided template structure.
Linked Issues check✅ PassedThe PR successfully implements the acceptance criteria from #198: the /api/gemini-test endpoint is now gated behind require_admin, preventing anonymous callers from triggering LLM spend. Code changes and tests directly validate this requirement.
Out of Scope Changes check✅ PassedAll changes are directly scoped to addressing issue #198: one file modified (backend/main.py) and one test file added (backend/tests/test_gemini_test_auth.py). No extraneous changes detected.

✏️ 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 security/198-gate-gemini-test

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.

@Jose-Gael-Cruz-Lopez
Jose-Gael-Cruz-Lopez merged commit f4b9174 into mainJun 14, 2026
6 checks passed
@Jose-Gael-Cruz-Lopez
Jose-Gael-Cruz-Lopez deleted the security/198-gate-gemini-test branch June 14, 2026 02:17
Sign up for freeto 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.

/api/gemini-test is an unauthenticated live-LLM cost/key oracle

1 participant

@Jose-Gael-Cruz-Lopez
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(security): gate /api/gemini-test behind require_admin (#198) - #219

Merged
Jose-Gael-Cruz-Lopez merged 1 commit into
mainfrom
security/198-gate-gemini-test
Jun 14, 2026
Merged

fix(security): gate /api/gemini-test behind require_admin (#198)#219
Jose-Gael-Cruz-Lopez merged 1 commit into
mainfrom
security/198-gate-gemini-test

Conversation

@Jose-Gael-Cruz-Lopez

@Jose-Gael-Cruz-LopezJose-Gael-Cruz-Lopez commented Jun 13, 2026

Copy link
Copy Markdown
Member

Closes#198.

Problem

GET /api/gemini-test had no auth and made a real, billable call_gemini(...) round-trip on every hit. Anonymous callers could burn Gemini quota at will and use the {"ok": ...} response as an oracle for whether the API key is configured/valid — a debug endpoint left wired into the production app.

Fix

Gate the endpoint behind require_admin (the existing guard in services/auth_guard.py). Anonymous → 401, non-admin → 403, admin → reaches the connectivity check. Kept (rather than removed) so operators retain a deliberate, auth-gated Gemini probe; /api/health remains the unauthenticated liveness check.

Test (vuln-closing)

tests/test_gemini_test_auth.py:

  • test_unauthenticated_returns_401_and_makes_no_llm_call — restores the real auth guard (undoing conftest's autouse bypass) and asserts an anonymous GET returns 401 and call_gemini is never called. Fails on pre-fix code (returned 200 + a live LLM call); passes post-fix.
  • test_admin_reaches_handler — admin-equivalent request reaches the handler (LLM mocked).

Scope

One file + one test. No behavior change for authenticated admins.

⚠️ Per Wave-1 convention: do not merge — opened for review.

Summary by CodeRabbit

  • Bug Fixes

    • Secured admin endpoint with authentication enforcement to prevent unauthorized access and unintended API quota usage.
  • Tests

    • Added tests to validate authentication requirements for the admin endpoint.

The endpoint made a real, billable call_gemini round-trip on every hit with
no auth, so anonymous callers could burn Gemini quota and use the {"ok": ...}
response as an oracle for whether the API key is configured. Gate it behind
require_admin so only admins can trigger LLM spend.
Adds a regression test: an unauthenticated GET returns 401 and never reaches
call_gemini (fails on pre-fix code, which returned 200).
@cloudflare-workers-and-pages

cloudflare-workers-and-pagesBot commented Jun 13, 2026

Copy link
Copy Markdown

Deploying with Cloudflare Workers Cloudflare Workers

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

StatusNameLatest CommitPreview URLUpdated (UTC)
✅ Deployment successful!
View logs
frontendf4344b9Commit Preview URL

Branch Preview URL
Jun 13 2026, 03:40 AM

@coderabbitai

coderabbitaiBot commented Jun 13, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 9f044721-b330-4a1b-9795-fc6c359f6f64

📥 Commits

Reviewing files that changed from the base of the PR and between a27b688 and f4344b9.

📒 Files selected for processing (2)
  • backend/main.py
  • backend/tests/test_gemini_test_auth.py

📝 Walkthrough

Walkthrough

The /api/gemini-test endpoint is updated to require admin authentication via require_admin(request), preventing unauthenticated quota burn. Unauthenticated requests return 401 and do not trigger Gemini calls. Admin-authenticated requests proceed to execute the endpoint's existing billable Gemini connectivity check.

Changes

Admin-gated Gemini endpoint

Layer / File(s)Summary
Admin authorization in endpoint
backend/main.py
Route handler accepts Request, calls require_admin(request) to enforce admin-only access, and rewritten docstring explains the billable, admin-gated Gemini round-trip. Existing success and error response paths remain unchanged.
Authentication regression tests
backend/tests/test_gemini_test_auth.py
New test class with two test cases: unauthenticated request restored real auth guards and asserts 401 without Gemini invocation; admin-path test mocks Gemini and asserts 200 with expected JSON payload.

Sequence Diagram

sequenceDiagram
participant AnonymousClient
participant AdminClient
participant Handler as /api/gemini-test Handler
participant Auth as require_admin
participant Gemini as call_gemini
AnonymousClient->>Handler: GET /api/gemini-test
Handler->>Auth: require_admin(request)
Auth-->>Handler: 401 Unauthorized
Handler-->>AnonymousClient: {"error": "Unauthorized"}
AdminClient->>Handler: GET /api/gemini-test (authenticated)
Handler->>Auth: require_admin(request)
Auth-->>Handler: allowed
Handler->>Gemini: call_gemini()
Gemini-->>Handler: "Gemini response"
Handler-->>AdminClient: {"ok": true, "reply": "Gemini response"}
Loading

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Poem

🐰 The test endpoint once ran wild and free,
Burning quota for all to see—
Now it bows to the admin gate,
Gemini calls keep their proper fate!
No more oracle for the spree,
Just billable checks, safe as can be! 🔐

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 33.33% 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 nameStatusExplanation
Title check✅ PassedThe title accurately and concisely describes the main security fix: adding admin-level authentication to the /api/gemini-test endpoint, which directly addresses the vulnerability in issue #198.
Description check✅ PassedThe description comprehensively covers the problem statement, the implemented fix, the test strategy (including both vulnerability-closing tests), scope, and notes for reviewers. It aligns well with the provided template structure.
Linked Issues check✅ PassedThe PR successfully implements the acceptance criteria from #198: the /api/gemini-test endpoint is now gated behind require_admin, preventing anonymous callers from triggering LLM spend. Code changes and tests directly validate this requirement.
Out of Scope Changes check✅ PassedAll changes are directly scoped to addressing issue #198: one file modified (backend/main.py) and one test file added (backend/tests/test_gemini_test_auth.py). No extraneous changes detected.

✏️ 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 security/198-gate-gemini-test

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.

@Jose-Gael-Cruz-Lopez
Jose-Gael-Cruz-Lopez merged commit f4b9174 into mainJun 14, 2026
6 checks passed
@Jose-Gael-Cruz-Lopez
Jose-Gael-Cruz-Lopez deleted the security/198-gate-gemini-test branch June 14, 2026 02:17
Sign up for freeto 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.

/api/gemini-test is an unauthenticated live-LLM cost/key oracle

1 participant

@Jose-Gael-Cruz-Lopez
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

fix(security): gate /api/gemini-test behind require_admin (#198) - #219

Merged
Jose-Gael-Cruz-Lopez merged 1 commit into
mainfrom
security/198-gate-gemini-test
Jun 14, 2026
Merged

fix(security): gate /api/gemini-test behind require_admin (#198)#219
Jose-Gael-Cruz-Lopez merged 1 commit into
mainfrom
security/198-gate-gemini-test

Conversation

@Jose-Gael-Cruz-Lopez

@Jose-Gael-Cruz-LopezJose-Gael-Cruz-Lopez commented Jun 13, 2026

Copy link
Copy Markdown
Member

Closes#198.

Problem

GET /api/gemini-test had no auth and made a real, billable call_gemini(...) round-trip on every hit. Anonymous callers could burn Gemini quota at will and use the {"ok": ...} response as an oracle for whether the API key is configured/valid — a debug endpoint left wired into the production app.

Fix

Gate the endpoint behind require_admin (the existing guard in services/auth_guard.py). Anonymous → 401, non-admin → 403, admin → reaches the connectivity check. Kept (rather than removed) so operators retain a deliberate, auth-gated Gemini probe; /api/health remains the unauthenticated liveness check.

Test (vuln-closing)

tests/test_gemini_test_auth.py:

  • test_unauthenticated_returns_401_and_makes_no_llm_call — restores the real auth guard (undoing conftest's autouse bypass) and asserts an anonymous GET returns 401 and call_gemini is never called. Fails on pre-fix code (returned 200 + a live LLM call); passes post-fix.
  • test_admin_reaches_handler — admin-equivalent request reaches the handler (LLM mocked).

Scope

One file + one test. No behavior change for authenticated admins.

⚠️ Per Wave-1 convention: do not merge — opened for review.

Summary by CodeRabbit

  • Bug Fixes

    • Secured admin endpoint with authentication enforcement to prevent unauthorized access and unintended API quota usage.
  • Tests

    • Added tests to validate authentication requirements for the admin endpoint.

The endpoint made a real, billable call_gemini round-trip on every hit with
no auth, so anonymous callers could burn Gemini quota and use the {"ok": ...}
response as an oracle for whether the API key is configured. Gate it behind
require_admin so only admins can trigger LLM spend.
Adds a regression test: an unauthenticated GET returns 401 and never reaches
call_gemini (fails on pre-fix code, which returned 200).
@cloudflare-workers-and-pages

cloudflare-workers-and-pagesBot commented Jun 13, 2026

Copy link
Copy Markdown

Deploying with Cloudflare Workers Cloudflare Workers

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

StatusNameLatest CommitPreview URLUpdated (UTC)
✅ Deployment successful!
View logs
frontendf4344b9Commit Preview URL

Branch Preview URL
Jun 13 2026, 03:40 AM

@coderabbitai

coderabbitaiBot commented Jun 13, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 9f044721-b330-4a1b-9795-fc6c359f6f64

📥 Commits

Reviewing files that changed from the base of the PR and between a27b688 and f4344b9.

📒 Files selected for processing (2)
  • backend/main.py
  • backend/tests/test_gemini_test_auth.py

📝 Walkthrough

Walkthrough

The /api/gemini-test endpoint is updated to require admin authentication via require_admin(request), preventing unauthenticated quota burn. Unauthenticated requests return 401 and do not trigger Gemini calls. Admin-authenticated requests proceed to execute the endpoint's existing billable Gemini connectivity check.

Changes

Admin-gated Gemini endpoint

Layer / File(s)Summary
Admin authorization in endpoint
backend/main.py
Route handler accepts Request, calls require_admin(request) to enforce admin-only access, and rewritten docstring explains the billable, admin-gated Gemini round-trip. Existing success and error response paths remain unchanged.
Authentication regression tests
backend/tests/test_gemini_test_auth.py
New test class with two test cases: unauthenticated request restored real auth guards and asserts 401 without Gemini invocation; admin-path test mocks Gemini and asserts 200 with expected JSON payload.

Sequence Diagram

sequenceDiagram
participant AnonymousClient
participant AdminClient
participant Handler as /api/gemini-test Handler
participant Auth as require_admin
participant Gemini as call_gemini
AnonymousClient->>Handler: GET /api/gemini-test
Handler->>Auth: require_admin(request)
Auth-->>Handler: 401 Unauthorized
Handler-->>AnonymousClient: {"error": "Unauthorized"}
AdminClient->>Handler: GET /api/gemini-test (authenticated)
Handler->>Auth: require_admin(request)
Auth-->>Handler: allowed
Handler->>Gemini: call_gemini()
Gemini-->>Handler: "Gemini response"
Handler-->>AdminClient: {"ok": true, "reply": "Gemini response"}
Loading

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Poem

🐰 The test endpoint once ran wild and free,
Burning quota for all to see—
Now it bows to the admin gate,
Gemini calls keep their proper fate!
No more oracle for the spree,
Just billable checks, safe as can be! 🔐

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 33.33% 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 nameStatusExplanation
Title check✅ PassedThe title accurately and concisely describes the main security fix: adding admin-level authentication to the /api/gemini-test endpoint, which directly addresses the vulnerability in issue #198.
Description check✅ PassedThe description comprehensively covers the problem statement, the implemented fix, the test strategy (including both vulnerability-closing tests), scope, and notes for reviewers. It aligns well with the provided template structure.
Linked Issues check✅ PassedThe PR successfully implements the acceptance criteria from #198: the /api/gemini-test endpoint is now gated behind require_admin, preventing anonymous callers from triggering LLM spend. Code changes and tests directly validate this requirement.
Out of Scope Changes check✅ PassedAll changes are directly scoped to addressing issue #198: one file modified (backend/main.py) and one test file added (backend/tests/test_gemini_test_auth.py). No extraneous changes detected.

✏️ 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 security/198-gate-gemini-test

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.

@Jose-Gael-Cruz-Lopez
Jose-Gael-Cruz-Lopez merged commit f4b9174 into mainJun 14, 2026
6 checks passed
@Jose-Gael-Cruz-Lopez
Jose-Gael-Cruz-Lopez deleted the security/198-gate-gemini-test branch June 14, 2026 02:17
Sign up for freeto 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.

/api/gemini-test is an unauthenticated live-LLM cost/key oracle

1 participant

@Jose-Gael-Cruz-Lopez