Summary
The FastAPI endpoint POST /api/query invokes Gemini 2.5 Pro with zero authentication. The application also uses wildcard CORS (allow_origins=["*"]) on all routes. The service is deployed live at sentinel.osintnet.uk. Any unauthenticated request from any origin can trigger an AI inference call at the repository owner's Gemini billing account.
Evidence
main.py line 82 — wildcard CORS on all routes:
app.add_middleware(CORSMiddleware, allow_origins=["*"], allow_methods=["*"], allow_headers=["*"])
main.py lines 387-395 — unauthenticated Gemini endpoint:
@app.post("/api/query")
async def query_agent(req: QueryRequest):
if not GEMINI_API_KEY:
return {"answer": "GEMINI_API_KEY not configured.", "engine": "none"}
# No auth dependency, no rate limiting on this endpoint
# ... proceeds to call Gemini 2.5 Pro
.env.example confirms GEMINI_API_KEY is a real credential, not a placeholder. The README references the live deployment at sentinel.osintnet.uk.
No authentication dependency (Depends(...)) is applied to /api/query, /api/contracts, /api/stats, or any other route. There is no API key header check, no session validation, and no per-IP rate limit on the /api/query endpoint specifically.
Why this matters
Any anonymous user can POST arbitrary questions to /api/query and consume Gemini API tokens at the owner's expense. Depending on the Gemini tier and query complexity, this can generate significant billing charges within minutes of discovery. Wildcard CORS additionally allows any malicious website to trigger cross-origin requests from a visitor's browser.
Attack or failure scenario
- Attacker finds the live deployment at
sentinel.osintnet.uk
- Scripts a loop:
while true; do curl -X POST https://sentinel.osintnet.uk/api/query -d '{"question": "<long prompt>"}'; done
- Each request invokes Gemini 2.5 Pro with the attacker's prompt — billing accumulates per token
- No rate limiting stops the loop; the endpoint continues serving until the API quota is exhausted or the billing limit is hit
- Alternatively, any webpage can iframe or fetch the endpoint cross-origin (wildcard CORS allows it)
Root cause
The endpoint was built for hackathon demonstration without adding authentication. Rate limiting exists on other routes but was not applied to /api/query. The wildcard CORS is a common copy-paste default that was never restricted to the expected frontend origin.
Recommended fix
- Add a
Depends check on /api/query — at minimum verify an X-API-Key header against a secret stored in the environment:
async def require_key(x_api_key: str = Header(...)):
if x_api_key != os.environ["API_SECRET"]:
raise HTTPException(status_code=401)
@app.post("/api/query", dependencies=[Depends(require_key)])
- Add per-IP rate limiting on
/api/query (e.g., slowapi or a Redis-backed counter)
- Replace
allow_origins=["*"] with an explicit allow-list of production frontend origins
- Set a Gemini API quota limit in the Google Cloud console to cap maximum spend
Acceptance criteria
Suggested labels
security, bug
Priority
P0
Severity
Critical — live unauthenticated endpoint invoking a paid AI API with no rate limiting or origin restriction; immediately exploitable for billing abuse.
Confidence
Confirmed — main.py read directly from committed source; live deployment documented in README.
Summary
The FastAPI endpoint
POST /api/queryinvokes Gemini 2.5 Pro with zero authentication. The application also uses wildcard CORS (allow_origins=["*"]) on all routes. The service is deployed live atsentinel.osintnet.uk. Any unauthenticated request from any origin can trigger an AI inference call at the repository owner's Gemini billing account.Evidence
main.pyline 82 — wildcard CORS on all routes:main.pylines 387-395 — unauthenticated Gemini endpoint:.env.exampleconfirmsGEMINI_API_KEYis a real credential, not a placeholder. The README references the live deployment atsentinel.osintnet.uk.No authentication dependency (
Depends(...)) is applied to/api/query,/api/contracts,/api/stats, or any other route. There is no API key header check, no session validation, and no per-IP rate limit on the/api/queryendpoint specifically.Why this matters
Any anonymous user can POST arbitrary questions to
/api/queryand consume Gemini API tokens at the owner's expense. Depending on the Gemini tier and query complexity, this can generate significant billing charges within minutes of discovery. Wildcard CORS additionally allows any malicious website to trigger cross-origin requests from a visitor's browser.Attack or failure scenario
sentinel.osintnet.ukwhile true; do curl -X POST https://sentinel.osintnet.uk/api/query -d '{"question": "<long prompt>"}'; doneRoot cause
The endpoint was built for hackathon demonstration without adding authentication. Rate limiting exists on other routes but was not applied to
/api/query. The wildcard CORS is a common copy-paste default that was never restricted to the expected frontend origin.Recommended fix
Dependscheck on/api/query— at minimum verify anX-API-Keyheader against a secret stored in the environment:/api/query(e.g.,slowapior a Redis-backed counter)allow_origins=["*"]with an explicit allow-list of production frontend originsAcceptance criteria
POST /api/queryreturns 401 without a valid credential/api/query(e.g., max 10 req/min)allow_originsrestricted to the production frontend domainSuggested labels
security, bug
Priority
P0
Severity
Critical — live unauthenticated endpoint invoking a paid AI API with no rate limiting or origin restriction; immediately exploitable for billing abuse.
Confidence
Confirmed —
main.pyread directly from committed source; live deployment documented in README.