Skip to content

fix: protect unpublished notes and saved page edits#435

Draft
cursor[bot] wants to merge 1 commit into
mainfrom
cursor/critical-bug-investigation-926b
Draft

fix: protect unpublished notes and saved page edits#435
cursor[bot] wants to merge 1 commit into
mainfrom
cursor/critical-bug-investigation-926b

Conversation

@cursor

@cursor cursor Bot commented Jun 12, 2026

Copy link
Copy Markdown

Summary

  • Require isClear = true in note detail lookups so direct /notes/{id} access cannot expose unapproved uploads.
  • Keep PDF page-edit save responses authoritative while server props are still stale, preventing immediate UI reversion and accidental overwrite of saved page fixes.

Validation

  • pnpm exec tsc --noEmit
  • Production build with local PostgreSQL/env: pnpm build
  • Route-level unpublished note check via local dev server: seeded an unapproved note, requested /notes/{id}, and verified the response did not contain the note title or PDF URL.
  • Targeted page-edit state transition simulation using the real page-edit serialization helpers.
Open in Web View Automation 

Co-authored-by: theg1239 <theg1239@users.noreply.github.com>
@vercel

vercel Bot commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
examcooker-dev Error Error Jun 12, 2026 11:15am

@greptile-apps

greptile-apps Bot commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds a missing isClear = true filter to the note-detail query so direct /notes/{id} requests can no longer expose unapproved uploads, and fixes a state-management bug in both the PDF viewer and page-editor where saved edits were immediately overwritten by stale server props.

  • lib/data/note-detail.ts: Adds eq(note.isClear, true) to the Drizzle WHERE clause, consistent with every other query in the codebase (notes.ts, sitemaps, etc.). Cache invalidation is already handled — the moderator approval action calls revalidateTag(\"notes\"), which covers all entries tagged with cacheTag(\"notes\").
  • app/components/pdfviewer.tsx & past-paper-page-editor.tsx: Renames the state field sourceKey → propKey and fixes setSavedPageEdits/replaceSavedPageEdits to store the current prop key instead of a key derived from the saved value. The old code serialized the just-saved data as the key but compared it against the initial-prop key, so the condition always evaluated false and the UI reverted to stale server props on every save.

Confidence Score: 4/5

Safe to merge — all three changes fix real defects with no new logic paths introduced.

Both the note-visibility guard and the page-editor state fix are targeted, well-scoped corrections that match existing patterns in the codebase. The only non-functional observation is a commit-message style note.

No files require special attention; all changed files are straightforward.

Important Files Changed

Filename Overview
lib/data/note-detail.ts Adds isClear = true guard to the note-detail query; consistent with all other note queries in the codebase and cache invalidation is covered by the existing "notes" tag revalidation.
app/components/pdfviewer.tsx Fixes setSavedPageEdits to store propKey = normalizedInitialPageEditsKey instead of the serialised saved value, so the locally-saved state is preserved until authoritative server props arrive.
app/components/moderation/past-paper-page-editor.tsx Applies the same propKey fix to replaceSavedPageEdits, preventing immediate UI reversion after a moderator saves page-edit fixes.

Sequence Diagram

sequenceDiagram
    participant User
    participant NoteDetailPage
    participant getNoteDetail
    participant DB

    User->>NoteDetailPage: "GET /notes/{id}"
    NoteDetailPage->>getNoteDetail: getNoteDetail(id)
    getNoteDetail->>DB: "SELECT ... WHERE id=? AND isClear=true"
    alt note is published
        DB-->>getNoteDetail: row(s)
        getNoteDetail-->>NoteDetailPage: noteDetail
        NoteDetailPage-->>User: Render note
    else note is unpublished / not found
        DB-->>getNoteDetail: []
        getNoteDetail-->>NoteDetailPage: null
        NoteDetailPage-->>User: notFound() → 404
    end

    note over User,DB: Page-editor save flow
    User->>NoteDetailPage: Save page edits
    NoteDetailPage->>NoteDetailPage: "setSavedPageEdits(responseValue) propKey = normalizedInitialPageEditsKey"
    NoteDetailPage-->>User: UI reflects saved value immediately
    NoteDetailPage->>NoteDetailPage: refresh() → server re-render
    alt server props updated (new key)
        NoteDetailPage->>NoteDetailPage: propKey ≠ normalizedInitialPageEditsKey → fall back to new server props
    else server props still stale (same key)
        NoteDetailPage->>NoteDetailPage: "propKey === normalizedInitialPageEditsKey → retain locally saved value"
    end
Loading

Comments Outside Diff (1)

  1. lib/data/note-detail.ts, line 64-71 (link)

    P2 Commit message could be more descriptive

    The commit message fix: protect unpublished notes and saved page edits bundles two unrelated fixes. A good commit message answers why the change was needed and what component was affected — for example: fix(note-detail): exclude isClear=false notes from detail query and a separate fix(page-editor): retain saved page-edit state until server props refresh. This makes it much easier to bisect regressions or understand the git log at a glance.

    Context Used: Encourage people to write better commit messages (source)

    Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

    Fix in Codex Fix in Claude Code Fix in Cursor

Fix All in Codex Fix All in Claude Code Fix All in Cursor

Reviews (1): Last reviewed commit: "fix: protect unpublished notes and saved..." | Re-trigger Greptile

Sign up for free to 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.

1 participant