Loading report…
← nittim
[](https://nittim.com)Critical issues block production use. Do not ship as-is.Serious problems block using this for real users. Don't make it live as-is.Fix these serious problems before shipping.
app/api/todos/comments/route.tsPOST validates only the request body shape (commentCreateSchema) and inserts a comment with the caller's session.user.id and the attacker-supplied todoId, with NO check that the todo belongs to the caller or a shared workspace. Contrast app/api/[transport]/route.ts add_todo/add_comment which DO verify todo ownership via db.query.todos.findFirst(and(eq(todos.id,todoId),eq(todos.userId,session.user.id))).app/api/parse-todo/route.tshas no auth check. It reads userId and workspaceId from the request body and uses them: getModelForUser(userId) selects the premium 'gpt-4.1' model for 'pro' users, and createSystemPrompt(userId, workspaceId) runs db.select(...).from(todosTable).where(eq(todosTable.workspaceId, workspaceId)) to inject 'recent todos in this workspace' into the prompt.Full coverage — every eligible source and configuration file was audited.
20 other files aren’t code this audit reads — images, lockfiles, generated output, and similar non-source content.
agenda.dev is a public Next.js 15 / TypeScript todo application (Neon Postgres + Drizzle, Better-Auth/NextAuth, Stripe, OpenAI, PostHog, Resend) with a companion Raycast extension and an experimental desktop build. The application is functional and reasonably organized around shadcn/ui, but it carries several real security defects that are as serious in this app as in any enterprise product because they touch real user data: a broken access-control (IDOR) write path on comments, two OpenAI-backed API routes that are effectively unauthenticated and trust client-supplied identity, and an arbitrary-workspace data read in the todo-parsing endpoint. The deterministic scanner reports 54 known-vulnerable dependency versions, including a CRITICAL Auth.js fail-open advisory and a Drizzle ORM SQL-injection advisory (reachability not verified). The build is configured to ignore all TypeScript and ESLint errors, and there is no rate limiting on cost-bearing AI endpoints. Privacy posture is thin for an app that stores email/IP and forwards todo content to OpenAI: analytics fire without consent and there is no data-subject deletion path in the audited snapshot. IP exposure is low — the code is largely conventional and the public-facing copy stays at benefit level. Net verdict: high risk, driven by access-control and dependency issues that should be fixed before production.
app/api/convert-date/route.tsretrieves a session (auth.api.getSession with cookies) under a comment 'Authentication check' but never checks the result; execution proceeds to generateText({ model: openai('gpt-4.1-mini'), ... }) for any caller.components/PostHogProvider.tsxcalls posthog.init(...) unconditionally on mount with capture_pageview/pageleave, and app/layout.tsx renders <Analytics /> (@vercel/analytics) for all users; there is no cookie/consent banner in the audited snapshot. The privacy policy (app/privacy/page.tsx) states cookies/analytics are used but tracking does not wait for consent.next.config.mjssets `eslint.ignoreDuringBuilds: true` and `typescript.ignoreBuildErrors: true`.app/api/convert-date/route.tsand app/api/reminders/route.ts delegate relative-date parsing to gpt-4.1-mini using prompts whose worked examples are pinned to 'April 2025' dates; the app also bundles chrono-node (package.json) which is not used for this parsing.app/api/parse-todo/route.tscontains the full conversational todo-extraction design: tag-based extraction (<title>/<date>/<urgency>/<follow_up>/<still_needed>), loop detection with field-attempt counting (checkForLoop/createLoopFallbackMessage), keyword-based time suggestions (generateTimeSuggestions) and subscription-gated model selection; the !remindme reminder-generation prompt lives in app/api/reminders/route.ts. The repository is PUBLIC.Weighted blend of 4 dimensions → ~45/100. Dimensions are weighted by importance (Security and Privacy weigh heaviest). Published figures are approximate; nittim's exact weighting and precise scoring are proprietary, while the verdict is computed on exact internal values.4 categories combine into ~45/100. Some categories count for more than others (Security and Privacy count the most). The published numbers are approximate; nittim's exact math is kept private, while the verdict itself is worked out from the exact internal numbers.Weighted blend of 4 dimensions → ~45/100 (Security and Privacy weigh heaviest). Published figures are approximate; nittim's exact weighting is proprietary. The verdict runs on exact internal values.
IP Protection is an independent IP-defensibility lens and is excluded from this blend.IP Protection (checking whether your ideas are exposed) is judged separately and isn’t
part of this combined number.IP Protection is judged separately, excluded from this blend.
Scores are a judgement, not a measurement: a re-audit of the same commit can land a few
points either side of this figure. The evidence underneath it is deterministic — the same
commit always yields the same committed-secret and vulnerable-dependency findings.Scores are a judgment call, not an exact measurement: checking the same saved snapshot of
your code twice can land a few points either side of this number. The evidence underneath
it is not — the same snapshot always turns up the same passwords-in-code and known-security-bug
findings.Scores are a judgment, not a measurement — a re-audit can land a few points either side. The evidence underneath is deterministic: the same commit always yields the same secret and vulnerable-dependency findings.
Each score is the model's overall assessment of the dimensions it covers, across everything it reviewed — not a tally of the findings in this report. Findings are the subset worth acting on, so a score can sit above or below what the listed findings alone would suggest.Each score is the AI's overall judgment of everything it reviewed in that category — not a
count of the problems listed in this report. The listed problems are the ones worth acting
on, so a score can sit above or below what they alone would suggest.Each score is the model's overall assessment across everything it reviewed — not a tally of this report's findings. A score can sit above or below what the findings alone suggest.
nittim guardrail — nittim's safety check — nittim guardrail — The model proposed “high_risk”; nittim's deterministic layer set the final verdict to “not_safe” because of a critical vulnerability in an installed runtime dependency (reachability from your own code was not verified). The written rationale below reflects the model's proposal — the verdict above is the one that governs.
Dependency manifests: 2 package roots found, 2 scanned — package list truncated at the vulnerability-lookup limit.
Verdict rationale — Why this verdict — Verdict rationale — The app is close to production but not there yet. Three access-control/identity defects are directly reachable by ordinary users — an IDOR write on comments, two AI endpoints that don't enforce authentication and trust client-supplied userId/workspaceId (enabling cost abuse, premium-model theft, and cross-workspace data disclosure) — and these are graded on what they protect regardless of project size. Layered on top are 54 vulnerable dependencies (including a CRITICAL Auth.js fail-open and a Drizzle SQL-injection advisory, reachability unverified), a build that ignores all TS/ESLint errors, no rate limiting on paid endpoints, and gaps in consent/data-subject rights for an app that stores PII and forwards user content to OpenAI. None of these are architecturally fatal and all have small-to-medium fixes, so this is high risk rather than not-safe: resolve the authorization checks, enforce auth + rate limiting on AI routes, patch the critical dependencies, and add consent/deletion handling, then re-audit. IP protection and AI-authorship likelihood are reported independently and did not influence this verdict.