Skip to content

[Data-integrity] /api/daily-focus POST does not validate goal_text length or date format #3164

Description

@vedant7007

Description

while looking at the daily-focus route i noticed the POST accepts goal_text and date from the request body without any length cap or format check, which is inconsistent with how goals and milestones handle input.

The Problem

src/app/api/daily-focus/route.ts:49-56:

const body = await req.json();
const { goal_text, date } = body;

if (!goal_text || !goal_text.trim()) {
  return NextResponse.json({ error: "Goal cannot be empty" }, { status: 400 });
}

const targetDate = date || new Date().toISOString().split("T")[0];

problems:

  1. no length limit on goal_text. an authenticated user can send "a".repeat(5_000_000) and it goes straight to supabase upsert on line 60. compare with src/app/api/goals/route.ts:213-215 (MAX_TITLE_LEN = 100) and src/app/api/milestones/route.ts:63 (stripHtml(title).slice(0, MAX_TITLE_LEN)) that both cap input.
  2. no html strip, so <script> payloads land in the DB and get rendered later.
  3. date from body has zero format validation. sending date: "not-a-date" throws a supabase constraint error (500 to the user instead of a proper 400). sending date: "9999-12-31" lets the user pin a "focus" onto any future or past day of their choice, which breaks the "daily" semantics of the feature.

the GET/DELETE also read date from searchParams without validation (src/app/api/daily-focus/route.ts:16-22, 87-92) so the same fix should cover those.

Proposed Fix

  • add MAX_GOAL_LEN = 280 (match daily-note which does cap at 280 chars in src/app/api/daily-note/route.ts:81) and slice/reject longer input
  • run stripHtml on goal_text before saving, same as goals route
  • validate date (and the query-param variants in GET/DELETE) against /^\d{4}-\d{2}-\d{2}$/ and reject with 400 if bad
  • optionally clamp targetDate so users cannot set a focus for a day other than today in their timezone

I would like to work on this under GSSoC 26. please assign!

for labels i think gssoc + level1 + type:bug fits here.

Metadata

Metadata

Labels

gssoc:assignedGSSoC: Issue assigned to a contributor

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions