Add playbook persistence, nudges, and dashboard#4
Conversation
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| export default async function PlaybookPage({ | ||
| searchParams | ||
| }: { | ||
| searchParams?: { userId?: string }; | ||
| }) { | ||
| const userId = searchParams?.userId ?? fallbackUserId; | ||
|
|
||
| const preference = await prisma.userPreference.findUnique({ | ||
| where: { userId }, | ||
| include: { |
There was a problem hiding this comment.
Derive playbook userId from session instead of query string
The page pulls userId straight from searchParams and uses it to load preferences and saved articles. Because there is no authentication or verification, any visitor can hit /playbook?userId=<someone> and retrieve another user’s saved items and opt‑in state, and the value is forwarded to client components where it is later used for mutations. The user identifier should come from the authenticated session (or the query string should be ignored) to avoid leaking account data.
Useful? React with 👍 / 👎.
| export const updateDeliveryOptIns = async (userId: string, payload: OptInPayload) => { | ||
| await prisma.userPreference.update({ | ||
| where: { userId }, | ||
| data: payload | ||
| }); |
There was a problem hiding this comment.
Server actions allow updating any user by supplying a forged userId
Both server actions accept a userId argument from the client and use it directly in Prisma updates. A malicious caller can invoke these actions with someone else’s ID (e.g. via ?userId= on the dashboard) and change their delivery opt‑ins or saved articles without any ownership check. Instead, derive the user ID from the authenticated request context before running the mutation, or validate that the supplied ID belongs to the current user.
Useful? React with 👍 / 👎.
| export async function GET(request: Request) { | ||
| const { searchParams } = new URL(request.url); | ||
| const userId = searchParams.get('userId'); | ||
|
|
||
| const nudges = await prisma.nudge.findMany({ | ||
| where: userId | ||
| ? { | ||
| preference: { | ||
| userId | ||
| } | ||
| } | ||
| : undefined, | ||
| orderBy: { createdAt: 'desc' }, | ||
| take: 25 | ||
| }); |
There was a problem hiding this comment.
GET /api/nudges leaks all users’ nudges when userId is omitted
The GET handler executes prisma.nudge.findMany with where: undefined whenever no userId query parameter is provided, returning the latest 25 nudges across every user. Because the route has no authentication, any caller can enumerate other users’ notifications. The handler should reject requests without a user identifier and ensure the identifier belongs to the authenticated user before returning data.
Useful? React with 👍 / 👎.
Summary
Testing
https://chatgpt.com/codex/tasks/task_e_690bf5719b10832dad6051a056afa5a2