serenakeyitan/tdoc
81/100
🤖 Prompt-and-Pray Merchant
serenakeyitan/tdoc preview

🔥 Roast

Not a developer. An LLM’s copy-paste button with commit access.

Be honest — you didn’t write this, you typed “make me secure auth” into a chatbot and shipped whatever fell out. Textbook slop: a shiny timing-safe compare made you feel like an engineer while auth sat wide open. Some kid loops curl over it, posts “startup that’s never heard of rate limiting,” 900 upvotes, your name in the thread.

Critical: 0High: 2Medium: 5
Scanned by 🌳 First Tree · first-tree.ai · 2026-06-30

Oh, brilliant. Timing-safe token compare, CodeQL in CI, 23 try/catch pairs, a real offline test suite — you clearly know how to lock a door. Which makes it all the more impressive that you fitted that gorgeous lock to a house with two side doors taken clean off the hinges.

Score Breakdown

Six cells in the green, two dragging the whole score down — you’re the honor student who forgot to wear pants to graduation.

ErrTestSecAuthObsDataDepPerf
Input & Data Safety 48/100
The door’s open, but you swept the floor first
Deploy Config 48/100
Shipped to prod with no headers, very brave
Secrets & Credentials 88/100
Clean today — a git add . from the news
Authentication & Access 88/100
Timing-safe now! The 4 routes you recall
Observability 88/100
You’ll hear it broke. Eventually. From a user
Performance 88/100
Edge runtime carries you — no obvious N+1 or blocking hot path
Error Handling 100/100
23 try/catch pairs — genuinely, show-off
Tests & CI 100/100
CodeQL and an offline suite. Teacher’s pet
Must-Fix Before Launch

Two things stand between you and a launch-ready score. Here’s where the joking stops and the exact fix starts.

HighCongrats — you built a free brute-force playground
That’s not an auth endpoint — it’s an open bar: free, unlimited, 24/7, serving every stranger who fancies grinding your GitHub Device Flow quota to dust with one while(true).
Evidence
worker/worker.js:1631 — POST /api/auth/device/start
worker/worker.js:1650 — POST /api/auth/device/poll
proxy GitHub Device Flow with no rate limiter wired
Root causeUnthrottled auth endpoints let an attacker brute-force device codes or burn your Device Flow quota; any public auth surface needs a per-IP + global cap.
The fix
const ip = request.headers.get("CF-Connecting-IP");
const n = Number(await env.RL.get(`rl:device:${ip}`)) || 0;
if (n > 10) return new Response("rate limited", { status: 429 });
await env.RL.put(`rl:device:${ip}`, String(n + 1), { expirationTtl: 60 });
Verify
for i in $(seq 1 15); do curl -s -o /dev/null -w "%{http_code}\n" \
  https://<worker>/api/auth/device/start; done   # expect 429 after the cap
HighYou serve other people’s HTML on your own origin and call it a day
Your doc-serve responses set Content-Type and stop there — no CSP, no X-Frame-Options, no nosniff. You built a beautiful printing press and skipped the part where the ink doesn’t set the building on fire.
Evidence
worker/worker.js:1616 — doc-serve Response sets Content-Type only;
no CSP / X-Frame-Options / X-Content-Type-Options / Referrer-Policy / HSTS
Root causeAuthor HTML is served from the worker’s own origin with no framing or MIME-sniff protection — a real clickjacking / sniff risk for an HTML publisher.
The fix
const headers = {
  ’Content-Type’: ’text/html; charset=utf-8’,
+   ’X-Content-Type-Options’: ’nosniff’,
+   ’Content-Security-Policy’: "frame-ancestors ’self’",
+   ’Strict-Transport-Security’: ’max-age=31536000; includeSubDomains’,
};
Verify
curl -sI https://<worker>/d/<slug>/v/1 | grep -iE \
  ’x-content-type|content-security|strict-transport’
MediumYour .gitignore is one line short of a public apology tour
Right now a stray git add . is all that stands between your secrets and a very public commit history.
Evidence
.gitignore:0 — .env / .env* not listed; a secret-bearing dotenv could be committed
Root causeAn untracked .env is one `git add .` from leaking every key in it — once it hits history, rotating is the only fix.
The fix
# append to .gitignore
.env
.env.*
!.env.example
Verify
git check-ignore .env   # should print .env
MediumAuthor HTML served verbatim — your diary on your own kitchen table
It’s “fine” the way leaving your diary open on the kitchen table is fine — because it’s your kitchen, for now.
Evidence
worker/worker.js:1610 — author HTML returned verbatim from R2 (reader comments/logins ARE escaped)
Root causeSelf-XSS on the author’s own tenant, not cross-user — acceptable on a single-owner worker where the author is the only writer.
The fix
if multi-author publishing is added, isolate each doc to a sandboxed per-author origin.
Verify
confirm no shared-origin multi-tenant publishing before scaling.
MediumWildcard CORS — door wide open, nothing worth stealing behind it yet
Access-Control-Allow-Origin: * is fine while nothing sensitive is on the other side — the moment something is, it’s a liability.
Evidence
worker/worker.js:19 — Access-Control-Allow-Origin: * WITHOUT Allow-Credentials
Root causePublic non-credentialed API shape; fine while nothing sensitive is served, dangerous the instant a response carries user data.
The fix
if any response becomes sensitive, replace * with an explicit allowlist — never pair * with Allow-Credentials.
Verify
curl -sI <worker>/api/... | grep -i access-control
Medium4 mutating routes guarded — great until route #5 ships without it
Every write route is guarded today; the trouble is the sixth one nobody remembers to guard.
Evidence
worker/worker.js:1774,1859,1907,1981 — requireUploadAuth guards the 4 mutating routes found
Root causeA 5th mutating route could ship without the guard and nobody notices until it’s abused.
The fix
add a default-deny test so any unguarded mutating route fails CI.
Verify
add a test asserting every POST/DELETE route calls requireUploadAuth.
MediumYour errors are confessing to an empty room
console.error plus a terminal tail means you find out things broke from users, not from alerts.
Evidence
worker/worker.js:88 — errors only via console.error + wrangler tail; no error-tracking provider
Root causeYou find out about breakage from users, not alerts — uncaught errors vanish the moment you stop tailing.
The fix
wire a Workers error tracker (Sentry / Logpush) so uncaught errors surface without tailing.
Verify
trigger a handled error and confirm it lands in the tracker.
First Tree fixes every blocker for you — all at once, free. Fix it in First Tree →