-
Notifications
You must be signed in to change notification settings - Fork 67
Expand file tree
/
Copy pathpr_comments.json
More file actions
82 lines (82 loc) · 84.9 KB
/
Copy pathpr_comments.json
File metadata and controls
82 lines (82 loc) · 84.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
[
{
"file": "apps/consumer/src/index.js",
"line": 23,
"body": "_\u26a0\ufe0f Potential issue_ | _\ud83d\udfe0 Major_ | _\u26a1 Quick win_\n\n**Validate port range before binding the server.**\n\nLine 21-Line 23 accepts any truthy number; invalid values like `-1` or `70000` can trigger runtime failure at startup.\n\n \n\n<details>\n<summary>Suggested fix</summary>\n\n```diff\n-const port = process.env.NODE_ENV === 'production'\n- ? (Number(process.env.PORT) || 3000)\n- : (Number(process.env.CONSUMER_PORT) || 1237);\n+const parsePort = (value, fallback) => {\n+ const parsed = Number(value);\n+ return Number.isInteger(parsed) && parsed >= 1 && parsed <= 65535 ? parsed : fallback;\n+};\n+\n+const port = process.env.NODE_ENV === 'production'\n+ ? parsePort(process.env.PORT, 3000)\n+ : parsePort(process.env.CONSUMER_PORT, 1237);\n```\n</details>\n\n<details>\n<summary>\ud83e\udd16 Prompt for AI Agents</summary>\n\n```\nVerify each finding against current code. Fix only still-valid issues, skip the\nrest with a brief reason, keep changes minimal, and validate.\n\nIn `@apps/consumer/src/index.js` around lines 21 - 23, The port assignment logic\n(lines 21-23) does not validate that the port number falls within the valid port\nrange of 1-65535. Add validation after the Number() conversions to check that\nthe resulting port value is within this valid range. If the port is outside the\nvalid range, either use a safe default value or throw an appropriate error to\nprevent runtime failures when the server attempts to bind.\n```\n\n</details>\n\n<!-- fingerprinting:phantom:poseidon:hawk -->\n\n<!-- cr-comment:v1:eca7c08cc51e0dba64685a22 -->\n\n<!-- This is an auto-generated comment by CodeRabbit -->"
},
{
"file": "apps/dashboard-api/src/controllers/auth.controller.js",
"line": 491,
"body": "_\u26a0\ufe0f Potential issue_ | _\ud83d\udd34 Critical_ | _\u26a1 Quick win_\n\n**Do not log raw OTP values in fallback email paths.**\n\nThis branch can still log OTPs in production when `emailErr.message` includes `\"testing emails\"`; raw OTP leakage in logs is high-risk.\n\n<details>\n<summary>Proposed fix</summary>\n\n```diff\n- } catch (emailErr) {\n- if (process.env.NODE_ENV !== 'production' || emailErr.message?.includes('testing emails')) {\n+ } catch (emailErr) {\n+ if (process.env.NODE_ENV !== 'production') {\n console.log(`\\n==================================================`);\n console.log(`[DEV MODE] Failed to send email via Resend sandbox.`);\n- console.log(`OTP code for ${email} is: ${otp}`);\n+ console.log(`OTP generation failed for ${email}.`);\n console.log(`==================================================\\n`);\n } else {\n throw emailErr;\n }\n }\n```\n\nApply the same pattern to the forgot-password block at Line 537-547.\n</details>\n\n\n\n\n\n\n\nAlso applies to: 537-547\n\n<details>\n<summary>\ud83e\udd16 Prompt for AI Agents</summary>\n\n```\nVerify each finding against current code. Fix only still-valid issues, skip the\nrest with a brief reason, keep changes minimal, and validate.\n\nIn `@apps/dashboard-api/src/controllers/auth.controller.js` around lines 481 -\n491, Remove raw OTP value logging from all email error fallback paths to prevent\nproduction credential leakage. In the sendOtp error handler around lines\n481-491, remove the otp variable from the console.log statement that logs \"OTP\ncode for ${email} is: ${otp}\" - log only a generic message without the actual\nOTP. Apply the identical fix to the forgot-password error handler around lines\n537-547 to ensure OTP values are never exposed in logs regardless of error\nconditions or environment flags.\n```\n\n</details>\n\n<!-- fingerprinting:phantom:poseidon:hawk -->\n\n<!-- cr-comment:v1:5b846ec8fb3018882ca49cd8 -->\n\n<!-- This is an auto-generated comment by CodeRabbit -->"
},
{
"file": "apps/dashboard-api/src/controllers/project.controller.js",
"line": 2961,
"body": "_\u26a0\ufe0f Potential issue_ | _\ud83d\udfe1 Minor_ | _\u26a1 Quick win_\n\n**Error responses missing `data: {}` field per coding guidelines.**\n\nThe `revealSecretKey` endpoint error responses are missing the required `data: {}` field. As per coding guidelines, all API endpoints must return format: `{ success: bool, data: {}, message: \"\" }`.\n\n\n\n\n\n<details>\n<summary>Proposed fix</summary>\n\n```diff\n if (!project) {\n- return res.status(404).json({ success: false, message: \"Project not found.\" });\n+ return res.status(404).json({ success: false, data: {}, message: \"Project not found.\" });\n }\n\n if (!req.user.isVerified) {\n- return res.status(403).json({ success: false, message: \"Account verification required to reveal secret key.\" });\n+ return res.status(403).json({ success: false, data: {}, message: \"Account verification required to reveal secret key.\" });\n }\n\n if (project.secretKeyRevealed) {\n- return res.status(400).json({ success: false, message: \"Secret key has already been revealed once.\" });\n+ return res.status(400).json({ success: false, data: {}, message: \"Secret key has already been revealed once.\" });\n }\n\n if (!project.secretKeyEncrypted || !project.secretKeyEncrypted.encrypted) {\n- return res.status(404).json({ success: false, message: \"Secret key cannot be decrypted (it may have been rolled or cleared).\" });\n+ return res.status(404).json({ success: false, data: {}, message: \"Secret key cannot be decrypted (it may have been rolled or cleared).\" });\n }\n // ...\n } catch (err) {\n- res.status(500).json({ success: false, message: err.message });\n+ res.status(500).json({ success: false, data: {}, message: err.message });\n }\n```\n</details>\n\n<!-- suggestion_start -->\n\n<details>\n<summary>\ud83d\udcdd Committable suggestion</summary>\n\n> \u203c\ufe0f **IMPORTANT**\n> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.\n\n```suggestion\nmodule.exports.revealSecretKey = async (req, res) => {\n try {\n const project = await Project.findOne({\n _id: req.params.projectId,\n owner: req.user._id,\n }).select(\"+secretKeyEncrypted secretKeyRevealed\");\n\n if (!project) {\n return res.status(404).json({ success: false, data: {}, message: \"Project not found.\" });\n }\n\n if (!req.user.isVerified) {\n return res.status(403).json({ success: false, data: {}, message: \"Account verification required to reveal secret key.\" });\n }\n\n if (project.secretKeyRevealed) {\n return res.status(400).json({ success: false, data: {}, message: \"Secret key has already been revealed once.\" });\n }\n\n if (!project.secretKeyEncrypted || !project.secretKeyEncrypted.encrypted) {\n return res.status(404).json({ success: false, data: {}, message: \"Secret key cannot be decrypted (it may have been rolled or cleared).\" });\n }\n\n const decryptedKey = decrypt(project.secretKeyEncrypted);\n\n project.secretKeyRevealed = true;\n project.secretKeyEncrypted = null;\n await project.save();\n\n res.json({ success: true, data: { secretKey: decryptedKey }, message: \"Secret key revealed successfully.\" });\n } catch (err) {\n res.status(500).json({ success: false, data: {}, message: err.message });\n }\n};\n```\n\n</details>\n\n<!-- suggestion_end -->\n\n<details>\n<summary>\ud83e\udd16 Prompt for AI Agents</summary>\n\n```\nVerify each finding against current code. Fix only still-valid issues, skip the\nrest with a brief reason, keep changes minimal, and validate.\n\nIn `@apps/dashboard-api/src/controllers/project.controller.js` around lines 2928 -\n2961, The revealSecretKey function's error responses are missing the required\ndata field per API guidelines. All error responses in the function (the 404 for\nproject not found, 403 for unverified account, 400 for already revealed key, 404\nfor decryption failure, and the 500 catch block error) must include an empty\ndata object field alongside success and message to conform to the standard API\nresponse format of { success: bool, data: {}, message: \"\" }. Add data: {} to\neach error response JSON object to match the successful response format already\npresent in the endpoint.\n```\n\n</details>\n\n<!-- fingerprinting:phantom:medusa:ocelot -->\n\n<!-- cr-comment:v1:385c1732b82ea35ac469d5ee -->\n\n_Source: Coding guidelines_\n\n<!-- This is an auto-generated comment by CodeRabbit -->"
},
{
"file": "apps/dashboard-api/src/middlewares/planEnforcement.js",
"line": 86,
"body": "_\u26a0\ufe0f Potential issue_ | _\ud83d\udfe0 Major_ | _\u26a1 Quick win_\n\n**Missing handler for `manageWebhooks` capability causes unverified users to be blocked unconditionally.**\n\nThe `checkDeveloperCapability` middleware only handles `createProject` and `createCollection` capabilities. However, `webhooks.js` routes use `checkDeveloperCapability(\"manageWebhooks\")`. For unverified users, this falls through to line 81, returning a generic 403 error regardless of any limit checks.\n\nIf the intent is to block all unverified users from webhooks, `checkWebhookGate` (lines 163-171) already does this. This makes `checkDeveloperCapability(\"manageWebhooks\")` redundant. If the intent is to allow unverified users limited webhook access, you need to add a handler.\n\n<details>\n<summary>\ud83d\udee0\ufe0f Suggested fix: Either remove the redundant capability check from webhook routes, or add explicit handling</summary>\n\n**Option A: Add explicit handling if webhooks should have unverified limits**\n```diff\n return next();\n }\n\n+ if (capability === 'manageWebhooks') {\n+ // Webhooks require verification - delegate to checkWebhookGate\n+ return next();\n+ }\n+\n return next(new AppError(403, 'Email verification is required for this action.'));\n```\n\n**Option B: Document that unhandled capabilities require verification by design**\n```diff\n+ // Any unrecognized capability requires email verification\n return next(new AppError(403, 'Email verification is required for this action.'));\n```\n</details>\n\n<details>\n<summary>\ud83e\udd16 Prompt for AI Agents</summary>\n\n```\nVerify each finding against current code. Fix only still-valid issues, skip the\nrest with a brief reason, keep changes minimal, and validate.\n\nIn `@apps/dashboard-api/src/middlewares/planEnforcement.js` around lines 41 - 86,\nThe checkDeveloperCapability middleware in planEnforcement.js does not handle\nthe manageWebhooks capability that is called from webhook routes, causing all\nunverified users to fail with a generic 403 error on the final fallback. You\nneed to either add explicit handling for the manageWebhooks capability inside\nthe function (similar to the existing createProject and createCollection\nhandlers) if unverified users should have limited webhook permissions, or remove\nthe redundant checkDeveloperCapability(\"manageWebhooks\") call from webhook\nroutes if the existing checkWebhookGate middleware already enforces the required\nverification. Determine which approach aligns with your webhook access policy\nand implement accordingly.\n```\n\n</details>\n\n<!-- fingerprinting:phantom:medusa:ocelot -->\n\n<!-- cr-comment:v1:a54c9f67164792e2d82e59d6 -->\n\n<!-- This is an auto-generated comment by CodeRabbit -->"
},
{
"file": "apps/public-api/src/__tests__/api_usage.test.js",
"line": 43,
"body": "_\u26a0\ufe0f Potential issue_ | _\ud83d\udfe1 Minor_ | _\u26a1 Quick win_\n\n**Reset `finishCallback` in `beforeEach` to avoid cross-test leakage.**\n\n`finishCallback` is module-scoped; without resetting it, stale listeners can be reused across tests and make failures/non-failures nondeterministic.\n\n<details>\n<summary>Suggested patch</summary>\n\n```diff\n beforeEach(() => {\n jest.clearAllMocks();\n+ finishCallback = undefined;\n mockRedisSet.mockResolvedValue(null);\n mockMarkDeveloperActivated.mockResolvedValue({ activated: false });\n```\n</details>\n\n<!-- suggestion_start -->\n\n<details>\n<summary>\ud83d\udcdd Committable suggestion</summary>\n\n> \u203c\ufe0f **IMPORTANT**\n> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.\n\n```suggestion\n beforeEach(() => {\n jest.clearAllMocks();\n finishCallback = undefined;\n mockRedisSet.mockResolvedValue(null);\n mockMarkDeveloperActivated.mockResolvedValue({ activated: false });\n req = {\n```\n\n</details>\n\n<!-- suggestion_end -->\n\n<details>\n<summary>\ud83e\udd16 Prompt for AI Agents</summary>\n\n```\nVerify each finding against current code. Fix only still-valid issues, skip the\nrest with a brief reason, keep changes minimal, and validate.\n\nIn `@apps/public-api/src/__tests__/api_usage.test.js` around lines 39 - 43, The\nmodule-scoped variable `finishCallback` is not being reset in the `beforeEach`\nhook, causing stale callback listeners from previous tests to persist and\ninterfere with subsequent tests. Add a line in the `beforeEach` function to\nreset `finishCallback` to its initial state (likely null or undefined) alongside\nthe other mock resets like jest.clearAllMocks() and\nmockRedisSet.mockResolvedValue calls, ensuring each test starts with a clean\ncallback state.\n```\n\n</details>\n\n<!-- fingerprinting:phantom:poseidon:hawk -->\n\n<!-- cr-comment:v1:ddec72d1c78e7e7ff7e64cb6 -->\n\n<!-- This is an auto-generated comment by CodeRabbit -->"
},
{
"file": "apps/public-api/src/middlewares/api_usage.js",
"line": 92,
"body": "_\u26a0\ufe0f Potential issue_ | _\ud83d\udd34 Critical_\n\n<details>\n<summary>\ud83e\udde9 Analysis chain</summary>\n\n\ud83c\udfc1 Script executed:\n\n```shell\n#!/bin/bash\n# Verify markDeveloperActivated exists and check its implementation\necho \"=== Searching for markDeveloperActivated definition ===\"\nast-grep --pattern 'function markDeveloperActivated($_) { $$$ }'\nast-grep --pattern 'const markDeveloperActivated = $_'\nast-grep --pattern 'exports.markDeveloperActivated = $_'\n\necho \"=== Checking export from `@urbackend/common` ===\"\nrg -n 'markDeveloperActivated' packages/common/src/\n```\n\nRepository: geturbackend/urBackend\n\nLength of output: 1340\n\n---\n\n\ud83c\udfc1 Script executed:\n\n```shell\n#!/bin/bash\n# Find and examine markDeveloperOnboardingStep implementation\necho \"=== Finding markDeveloperOnboardingStep definition ===\"\nrg -n 'markDeveloperOnboardingStep' packages/common/src/utils/onboarding.js -A 20 | head -60\n\necho \"=== Check if onboarding step can be marked multiple times ===\"\nrg -n 'firstApiCall' packages/common/src/utils/onboarding.js -B 2 -A 2\n```\n\nRepository: geturbackend/urBackend\n\nLength of output: 3108\n\n---\n\n\ud83c\udfc1 Script executed:\n\n```shell\n#!/bin/bash\n# Check how activated is used in api_usage.js\necho \"=== Checking api_usage.js lines 93-104 ===\"\nsed -n '87,115p' apps/public-api/src/middlewares/api_usage.js\n```\n\nRepository: geturbackend/urBackend\n\nLength of output: 1441\n\n---\n\n\n\n</details>\n\n**The `markDeveloperActivated` function does not guarantee idempotency and will create duplicate `first_api_call` events.**\n\nThe function returns `{ activated: true }` whenever the developer's onboarding is in a completed state, not only on the first marking. On the initial API call, it marks the `firstApiCall` step and completes the onboarding, returning `activated: true` and creating a `PlatformEvent`. However, on subsequent API calls, `markDeveloperActivated` will again return `{ activated: true }` because `onboarding.completed` remains true, causing additional duplicate events to be created.\n\nTo fix this, either add a database uniqueness constraint on `(developerId, event)` in the `PlatformEvent` collection, or check if the event already exists before creating it.\n\n<details>\n<summary>\ud83e\udd16 Prompt for AI Agents</summary>\n\n```\nVerify each finding against current code. Fix only still-valid issues, skip the\nrest with a brief reason, keep changes minimal, and validate.\n\nIn `@apps/public-api/src/middlewares/api_usage.js` around lines 87 - 92, The\nmarkDeveloperActivated function returns { activated: true } on every subsequent\ncall (not just the first), causing duplicate first_api_call events to be created\nin the PlatformEvent collection. Fix this by implementing idempotency: either\nadd a database uniqueness constraint on (developerId, event) in the\nPlatformEvent collection to prevent duplicates, or modify the\nmarkDeveloperActivated function logic to check if the event already exists\nbefore creating a new PlatformEvent record, ensuring it only creates the event\nonce per developer.\n```\n\n</details>\n\n<!-- fingerprinting:phantom:medusa:ocelot -->\n\n<!-- cr-comment:v1:94d95fdb104e427c9789eec2 -->\n\n<!-- This is an auto-generated comment by CodeRabbit -->"
},
{
"file": "apps/web-dashboard/src/components/ProtectedRoute.jsx",
"line": 8,
"body": "_\u26a0\ufe0f Potential issue_ | _\ud83d\udfe0 Major_ | _\u26a1 Quick win_\n\n**Wire the new route-gating props and make verification opt-out, not opt-in.**\n\nLine 7 defaults `allowUnverified` to `true`, so verification checks are effectively bypassed by default. Also, Lines 5-8 never consume `allowIncompleteOnboarding`/`onboardingOnly`, so routes in `apps/web-dashboard/src/App.jsx` (Line 64, Line 80, Line 88, Line 96, Line 125, Line 131) pass props that currently do nothing.\n\n \n\n<details>\n<summary>Suggested fix</summary>\n\n```diff\n const ProtectedRoute = ({\n children,\n- allowUnverified = true\n+ allowUnverified = false,\n+ allowIncompleteOnboarding = false,\n+ onboardingOnly = false,\n }) => {\n@@\n- const onboardingCompleted = !!user?.onboarding?.completed;\n+ const onboardingCompleted = !!user?.onboarding?.completed;\n+ const isOnboardingPath = location.pathname.startsWith('/onboarding');\n\n- if (onboardingCompleted) {\n- if (location.pathname.startsWith('/onboarding')) {\n+ if (onboardingCompleted) {\n+ if (isOnboardingPath) {\n return <Navigate to=\"/dashboard\" replace />;\n }\n- } else {\n+ } else if (!allowIncompleteOnboarding || onboardingOnly) {\n const steps = user?.onboarding?.steps || {};\n```\n</details>\n\n\nAlso applies to: 32-70\n\n<details>\n<summary>\ud83e\udd16 Prompt for AI Agents</summary>\n\n```\nVerify each finding against current code. Fix only still-valid issues, skip the\nrest with a brief reason, keep changes minimal, and validate.\n\nIn `@apps/web-dashboard/src/components/ProtectedRoute.jsx` around lines 5 - 8, The\nProtectedRoute component has two issues: the allowUnverified prop defaults to\ntrue, which makes verification optional rather than required by default, and the\ncomponent does not consume the allowIncompleteOnboarding and onboardingOnly\nprops that are being passed to it from App.jsx. Fix this by changing the\nallowUnverified default value from true to false to make verification required\nby default, add allowIncompleteOnboarding and onboardingOnly to the component's\ndestructuring parameters, and implement the logic in the verification/routing\nsection (lines 32-70) to properly check these conditions and gate access\naccordingly based on whether users are verified and have completed onboarding as\nspecified by the passed props.\n```\n\n</details>\n\n<!-- fingerprinting:phantom:poseidon:hawk -->\n\n<!-- cr-comment:v1:7d6f9f8e7a5df8c6889a1673 -->\n\n<!-- This is an auto-generated comment by CodeRabbit -->"
},
{
"file": "apps/web-dashboard/src/pages/Onboarding.jsx",
"line": 106,
"body": "_\u26a0\ufe0f Potential issue_ | _\ud83d\udfe0 Major_\n\n<details>\n<summary>\ud83e\udde9 Analysis chain</summary>\n\n\ud83c\udfc1 Script executed:\n\n```shell\n#!/bin/bash\nset -euo pipefail\n\necho \"== Locate project controller and routes ==\"\nfd -i \"project.controller.js\"\nfd -i \"projects.js\"\n\necho\necho \"== Inspect controller JSON response shapes ==\"\nrg -n -C3 \"res\\\\.(status\\\\([^)]*\\\\)\\\\.)?json\\\\(\" apps/dashboard-api/src/controllers/project.controller.js\n\necho\necho \"== Inspect handlers likely used by onboarding ==\"\nrg -n -C3 \"create|project|collection|api-key|reveal|byod\" apps/dashboard-api/src/controllers/project.controller.js\n\necho\necho \"== Inspect route bindings for onboarding endpoints ==\"\nrg -n -C3 \"api-key|collections|byod|:projectId\" apps/dashboard-api/src/routes/projects.js\n```\n\nRepository: geturbackend/urBackend\n\nLength of output: 50379\n\n---\n\n\ud83c\udfc1 Script executed:\n\n```shell\nsed -n '170,175p; 308,312p' apps/web-dashboard/src/pages/Onboarding.jsx\n```\n\nRepository: geturbackend/urBackend\n\nLength of output: 646\n\n---\n\n\n\n</details>\n\n**Standardize onboarding API response envelopes before merge.**\n\nThe onboarding flow currently reads unwrapped project objects (`res.data._id`, `res.data.collections`), but the `/api/projects` POST and GET endpoints return unwrapped responses, violating the documented coding standard: `{ success: bool, data: {}, message: \"\" }`. \n\nIf these endpoints are ever standardized to the required wrapped format, the onboarding flow will break. Update the frontend to expect wrapped responses, or confirm these endpoints are intentionally exempt from the standard.\n\n<details>\n<summary>Affected API calls (3 locations)</summary>\n\nLines 170-175: `res.data._id` (POST /api/projects) \nLines 308-312: `res.data.collections` (GET /api/projects/:projectId) \nLines 93-106: `res.data` and `res.data.collections` (GET /api/projects/:projectId)\n</details>\n\n<details>\n<summary>\ud83e\udd16 Prompt for AI Agents</summary>\n\n```\nVerify each finding against current code. Fix only still-valid issues, skip the\nrest with a brief reason, keep changes minimal, and validate.\n\nIn `@apps/web-dashboard/src/pages/Onboarding.jsx` around lines 93 - 106, The\nonboarding flow currently expects unwrapped API responses by accessing\nproperties directly on res.data (e.g., res.data._id, res.data.collections), but\nthe documented coding standard requires wrapped responses in the format {\nsuccess: bool, data: {}, message: \"\" }. Update all API response handling in\nOnboarding.jsx at the following locations to expect wrapped responses: lines\n93-106 where GET /api/projects is called and res.data.collections is accessed,\nlines 170-175 where POST /api/projects is called and res.data._id is accessed,\nand lines 308-312 where GET /api/projects/:projectId is called and\nres.data.collections is accessed. Change these to access res.data.data instead\nof res.data when retrieving project and collection properties. Alternatively, if\nthese endpoints are intentionally exempt from the standard wrapper format,\nconfirm this decision and document the exception.\n```\n\n</details>\n\n<!-- fingerprinting:phantom:poseidon:hawk -->\n\n<!-- cr-comment:v1:4c9ff092caa7b4ffc140860d -->\n\n_Source: Coding guidelines_\n\n<!-- This is an auto-generated comment by CodeRabbit -->"
},
{
"file": "apps/web-dashboard/src/pages/Onboarding.jsx",
"line": 122,
"body": "_\u26a0\ufe0f Potential issue_ | _\ud83d\udfe0 Major_ | _\u26a1 Quick win_\n\n**Prevent duplicate key-generation calls after verification.**\n\n`fetchKeys` is invoked from multiple success paths, so OTP verification can trigger redundant key-generation requests (Line 289 and then again via Line 152 effect). Because each call performs two POSTs, this creates avoidable write-side races/churn on credentials.\n\n<details>\n<summary>Suggested fix</summary>\n\n```diff\n-import React, { useState, useEffect } from 'react';\n+import React, { useState, useEffect, useRef } from 'react';\n\n+const fetchedKeysRef = useRef(false);\n\n useEffect(() => {\n if (isApiStep && progress.projectId) {\n if (user?.isVerified) {\n- queueMicrotask(() => fetchKeys(progress.projectId));\n+ if (!fetchedKeysRef.current) {\n+ fetchedKeysRef.current = true;\n+ queueMicrotask(() => fetchKeys(progress.projectId));\n+ }\n } else {\n queueMicrotask(() => setShowVerifyModal(true));\n }\n }\n }, [isApiStep, progress.projectId, user?.isVerified]);\n\n // polling success path\n if (res.data.success && res.data.data.user?.isVerified) {\n login(res.data.data.user);\n queueMicrotask(() => setShowVerifyModal(false));\n toast.success(\"Email verified!\");\n- fetchKeys(progress.projectId);\n }\n\n // manual OTP success path\n if (res.data.success) {\n login(res.data.data.user);\n }\n setShowVerifyModal(false);\n toast.success(\"Email verified!\");\n-fetchKeys(progress.projectId);\n```\n</details>\n\n\n\n\n\n\nAlso applies to: 136-141, 149-157, 289-294\n\n<details>\n<summary>\ud83e\udd16 Prompt for AI Agents</summary>\n\n```\nVerify each finding against current code. Fix only still-valid issues, skip the\nrest with a brief reason, keep changes minimal, and validate.\n\nIn `@apps/web-dashboard/src/pages/Onboarding.jsx` around lines 112 - 122, The\nfetchKeys function in Onboarding.jsx is being called from multiple locations\n(lines 136-141, 149-157, and 289-294), and after OTP verification completes at\nline 289, the useEffect dependency at line 149-157 also triggers fetchKeys,\ncausing redundant API calls that create unnecessary write operations on\ncredentials. Add a guard condition in the fetchKeys function (lines 112-122) to\nprevent it from making API calls if keys have already been successfully fetched,\nor add a flag to track whether a fetch is already in progress and skip\nsubsequent calls. Alternatively, restructure the callsites (specifically the\neffect at lines 149-157 and the verification handler at lines 289-294) to ensure\nfetchKeys is only invoked once after the verification flow completes, removing\nthe duplicate trigger from the effect.\n```\n\n</details>\n\n<!-- fingerprinting:phantom:poseidon:hawk -->\n\n<!-- cr-comment:v1:7110050030e5950efe91b8db -->\n\n<!-- This is an auto-generated comment by CodeRabbit -->"
},
{
"file": "apps/web-dashboard/src/pages/ProjectDetails.jsx",
"line": 120,
"body": "_\u26a0\ufe0f Potential issue_ | _\ud83d\udfe0 Major_ | _\u26a1 Quick win_\n\n**Avoid regenerating live keys from a URL flag.**\n\nLines 100-103 perform non-idempotent key regeneration (`/api-key`) based only on `revealKeys=1`. This can silently roll production keys on navigation and bypasses the dedicated one-time secret reveal flow (`/reveal-secret-key`).\n\n<details>\n<summary>Suggested fix</summary>\n\n```diff\n- const [publishableRes, secretRes] = await Promise.all([\n- api.post(`/api/projects/${projectId}/api-key`, { keyType: 'publishable' }),\n- api.post(`/api/projects/${projectId}/api-key`, { keyType: 'secret' })\n- ]);\n+ const [publishableRes, secretRes] = await Promise.all([\n+ Promise.resolve({ data: { apiKey: project.publishableKey } }),\n+ api.post(`/api/projects/${projectId}/reveal-secret-key`)\n+ ]);\n\n setNewKey({\n type: 'API',\n keys: [\n { label: 'Publishable Key', value: publishableRes.data.apiKey },\n- { label: 'Secret Key', value: secretRes.data.apiKey }\n+ { label: 'Secret Key', value: secretRes.data.data.secretKey }\n ]\n });\n```\n\n</details>\n\n<!-- suggestion_start -->\n\n<details>\n<summary>\ud83d\udcdd Committable suggestion</summary>\n\n> \u203c\ufe0f **IMPORTANT**\n> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.\n\n```suggestion\n useEffect(() => {\n if (revealAttemptedRef.current || loading || !project || !user?.isVerified || searchParams.get('revealKeys') !== '1') {\n return;\n }\n\n revealAttemptedRef.current = true;\n const revealKeys = async () => {\n try {\n const [publishableRes, secretRes] = await Promise.all([\n Promise.resolve({ data: { apiKey: project.publishableKey } }),\n api.post(`/api/projects/${projectId}/reveal-secret-key`)\n ]);\n setNewKey({\n type: 'API',\n keys: [\n { label: 'Publishable Key', value: publishableRes.data.apiKey },\n { label: 'Secret Key', value: secretRes.data.data.secretKey }\n ]\n });\n completeStep('get_api_key');\n setSearchParams({}, { replace: true });\n toast.success('API keys revealed. Copy them now.');\n } catch (err) {\n toast.error(err.response?.data?.message || err.response?.data?.error || 'Failed to reveal API keys');\n }\n };\n\n revealKeys();\n }, [completeStep, loading, project, projectId, searchParams, setSearchParams, user?.isVerified]);\n```\n\n</details>\n\n<!-- suggestion_end -->\n\n<details>\n<summary>\ud83e\udd16 Prompt for AI Agents</summary>\n\n```\nVerify each finding against current code. Fix only still-valid issues, skip the\nrest with a brief reason, keep changes minimal, and validate.\n\nIn `@apps/web-dashboard/src/pages/ProjectDetails.jsx` around lines 92 - 120, The\nrevealKeys async function within the useEffect is regenerating live API keys\nusing the /api-key endpoint based on a URL parameter, which is insecure and\nnon-idempotent. Replace the two POST calls to /api/projects/{projectId}/api-key\nwith calls to the proper /reveal-secret-key endpoint that is designed for safely\nrevealing keys in a one-time manner, and adjust the response parsing accordingly\nto match the structure returned by that endpoint instead of the key generation\nendpoint.\n```\n\n</details>\n\n<!-- fingerprinting:phantom:poseidon:hawk -->\n\n<!-- cr-comment:v1:09e05e3b65fb366f2b81c635 -->\n\n<!-- This is an auto-generated comment by CodeRabbit -->"
},
{
"file": "apps/web-dashboard/src/pages/Storage.jsx",
"line": 200,
"body": "_\u26a0\ufe0f Potential issue_ | _\ud83d\udfe1 Minor_ | _\u26a1 Quick win_\n\n**Do not show a success toast before any storage action succeeds.**\n\nLine 198 emits `\"Storage bucket initialized!\"` before upload starts; canceling the picker still shows success, which is misleading.\n\n<details>\n<summary>Suggested fix</summary>\n\n```diff\n <button \n onClick={() => {\n- toast.success(\"Storage bucket initialized!\");\n- fileInputRef.current.click();\n+ fileInputRef.current?.click();\n }} \n className=\"btn btn-primary\" \n```\n</details>\n\n<!-- suggestion_start -->\n\n<details>\n<summary>\ud83d\udcdd Committable suggestion</summary>\n\n> \u203c\ufe0f **IMPORTANT**\n> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.\n\n```suggestion\n onClick={() => {\n fileInputRef.current?.click();\n }} \n```\n\n</details>\n\n<!-- suggestion_end -->\n\n<details>\n<summary>\ud83e\udd16 Prompt for AI Agents</summary>\n\n```\nVerify each finding against current code. Fix only still-valid issues, skip the\nrest with a brief reason, keep changes minimal, and validate.\n\nIn `@apps/web-dashboard/src/pages/Storage.jsx` around lines 197 - 200, The success\ntoast \"Storage bucket initialized!\" in the onClick handler is being shown\nimmediately when the button is clicked, before any actual storage operation\ncompletes. This is misleading because users can cancel the file picker and still\nsee the success message. Remove the toast.success call from the onClick handler\nin the button that triggers fileInputRef.current.click(), and instead ensure the\nsuccess toast only appears after the file upload or storage operation actually\ncompletes successfully (likely in the file input change handler or upload\ncompletion callback).\n```\n\n</details>\n\n<!-- fingerprinting:phantom:poseidon:hawk -->\n\n<!-- cr-comment:v1:9f8dd57ae6c77519539e9094 -->\n\n<!-- This is an auto-generated comment by CodeRabbit -->"
},
{
"file": "apps/web-dashboard/src/utils/quota.js",
"line": 8,
"body": "_\u26a0\ufe0f Potential issue_ | _\ud83d\udfe1 Minor_ | _\u26a1 Quick win_\n\n**Clamp percentage to a lower bound of 0 as well.**\n\nLine 8 can return negative values when `used < 0`, which can flow into negative CSS widths in progress bars.\n\n \n\n<details>\n<summary>Suggested fix</summary>\n\n```diff\n- return Math.min(Math.round((numericUsed / numericLimit) * 100), 100);\n+ return Math.max(0, Math.min(Math.round((numericUsed / numericLimit) * 100), 100));\n```\n</details>\n\n<!-- suggestion_start -->\n\n<details>\n<summary>\ud83d\udcdd Committable suggestion</summary>\n\n> \u203c\ufe0f **IMPORTANT**\n> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.\n\n```suggestion\n return Math.max(0, Math.min(Math.round((numericUsed / numericLimit) * 100), 100));\n```\n\n</details>\n\n<!-- suggestion_end -->\n\n<details>\n<summary>\ud83e\udd16 Prompt for AI Agents</summary>\n\n```\nVerify each finding against current code. Fix only still-valid issues, skip the\nrest with a brief reason, keep changes minimal, and validate.\n\nIn `@apps/web-dashboard/src/utils/quota.js` at line 8, The return statement that\ncalculates the percentage using Math.min currently only clamps the value to a\nmaximum of 100, but allows negative values to be returned when numericUsed is\nnegative. This causes negative CSS widths in progress bars. Fix this by adding a\nlower bound clamp using Math.max to ensure the percentage is never less than 0,\nwhile keeping the existing upper bound clamp of 100. Wrap the Math.min\nexpression inside a Math.max call that establishes 0 as the minimum value.\n```\n\n</details>\n\n<!-- fingerprinting:phantom:poseidon:hawk -->\n\n<!-- cr-comment:v1:6e90152e68bca8f36ff7b570 -->\n\n<!-- This is an auto-generated comment by CodeRabbit -->"
},
{
"file": "packages/common/src/utils/emailService.js",
"line": 11,
"body": "_\u26a0\ufe0f Potential issue_ | _\ud83d\udd34 Critical_ | _\u26a1 Quick win_\n\n**Remove raw API key logging from startup path.**\n\nLine 10 and Line 11 print live secret values to logs, which is a direct credential exposure path.\n\n<details>\n<summary>Proposed fix</summary>\n\n```diff\n dotenv.config();\n-console.log(\"cwd =\", process.cwd());\n-console.log(\"__dirname =\", __dirname);\n-console.log(\"RESEND_API_KEY =\", process.env.RESEND_API_KEY);\n-console.log(\"RESEND_API_KEY_2 =\", process.env.RESEND_API_KEY_2);\n+if (process.env.NODE_ENV !== 'production' && process.env.DEBUG_EMAIL_SERVICE === 'true') {\n+ console.debug(\"cwd =\", process.cwd());\n+ console.debug(\"__dirname =\", __dirname);\n+ console.debug(\"RESEND_API_KEY set =\", Boolean(process.env.RESEND_API_KEY));\n+ console.debug(\"RESEND_API_KEY_2 set =\", Boolean(process.env.RESEND_API_KEY_2));\n+}\n```\n</details>\n\n<details>\n<summary>\ud83e\udd16 Prompt for AI Agents</summary>\n\n```\nVerify each finding against current code. Fix only still-valid issues, skip the\nrest with a brief reason, keep changes minimal, and validate.\n\nIn `@packages/common/src/utils/emailService.js` around lines 8 - 11, Remove the\nconsole.log statements that print the RESEND_API_KEY and RESEND_API_KEY_2\nenvironment variables directly to logs, as this exposes sensitive credentials.\nDelete lines 10 and 11 that log process.env.RESEND_API_KEY and\nprocess.env.RESEND_API_KEY_2, or replace them with masked versions that only log\npartial key information (e.g., first 4 and last 4 characters) if debugging is\nnecessary.\n```\n\n</details>\n\n<!-- fingerprinting:phantom:poseidon:hawk -->\n\n<!-- cr-comment:v1:e8aa795550b66783b4a22856 -->\n\n<!-- This is an auto-generated comment by CodeRabbit -->"
},
{
"file": "packages/common/src/utils/emailService.js",
"line": 80,
"body": "_\u26a0\ufe0f Potential issue_ | _\ud83d\udfe0 Major_ | _\u26a1 Quick win_\n\n**Use 5xx for upstream email provider failures, not 400.**\n\nThese are provider/infrastructure failures, not client input errors. Returning 400 misclassifies incidents and can break retry/error handling behavior.\n\n<details>\n<summary>Proposed fix</summary>\n\n```diff\n-throw new AppError(400, error.message || \"Failed to send email\");\n+throw new AppError(502, error.message || \"Failed to send email\");\n```\n\nApply similarly at Line 157, Line 251, Line 302, and Line 379.\n</details>\n\n\n\n\n\n\n\nAlso applies to: 157-157, 251-251, 302-302, 379-379\n\n<details>\n<summary>\ud83e\udd16 Prompt for AI Agents</summary>\n\n```\nVerify each finding against current code. Fix only still-valid issues, skip the\nrest with a brief reason, keep changes minimal, and validate.\n\nIn `@packages/common/src/utils/emailService.js` at line 80, The AppError\nexceptions being thrown for email provider failures are using status code 400\n(client error), but these are upstream infrastructure failures that should\nreturn 5xx (server error) status codes instead. Change the status code from 400\nto 502 (Bad Gateway) in the AppError constructor calls at all affected locations\nin the emailService.js file: line 80, line 157, line 251, line 302, and line\n379. This ensures that failures from the external email provider are correctly\nclassified as server-side issues rather than client input errors, allowing\nproper retry logic and error handling downstream.\n```\n\n</details>\n\n<!-- fingerprinting:phantom:poseidon:hawk -->\n\n<!-- cr-comment:v1:07e74fe256a6cefff00443b2 -->\n\n<!-- This is an auto-generated comment by CodeRabbit -->"
},
{
"file": "scratch/list_developers.js",
"line": 15,
"body": "_\u26a0\ufe0f Potential issue_ | _\ud83d\udfe1 Minor_ | _\u26a1 Quick win_\n\n**Ensure DB connection is always closed on query errors.**\n\nIf `Developer.find({})` throws, `mongoose.connection.close()` is skipped and the script can hang with an open connection.\n \n<details>\n<summary>Suggested change</summary>\n\n```diff\n async function list() {\n- await connectDB();\n- const developers = await Developer.find({});\n- console.log(\"All developers in DB:\");\n- developers.forEach(d => {\n- console.log(`- Email: ${d.email}, isVerified: ${d.isVerified}`);\n- });\n- await mongoose.connection.close();\n+ await connectDB();\n+ try {\n+ const developers = await Developer.find({});\n+ console.log(\"All developers in DB:\");\n+ developers.forEach(d => {\n+ console.log(`- Email: ${d.email}, isVerified: ${d.isVerified}`);\n+ });\n+ } finally {\n+ await mongoose.connection.close();\n+ }\n }\n \n list().catch(console.error);\n```\n</details>\n\n\nAlso applies to: 17-17\n\n<details>\n<summary>\ud83e\udd16 Prompt for AI Agents</summary>\n\n```\nVerify each finding against current code. Fix only still-valid issues, skip the\nrest with a brief reason, keep changes minimal, and validate.\n\nIn `@scratch/list_developers.js` around lines 7 - 15, The list function does not\nguarantee that the database connection is closed if an error occurs during the\nDeveloper.find({}) operation or when processing the results. Wrap the database\noperations (the find call and forEach loop) in a try-finally block, moving the\nmongoose.connection.close() call to the finally block to ensure it executes\nregardless of whether an error is thrown. This same pattern should be applied to\nany other similar database query functions in the file that have the same\nvulnerability.\n```\n\n</details>\n\n<!-- fingerprinting:phantom:poseidon:hawk -->\n\n<!-- cr-comment:v1:0539565cfc259e5bef3a34aa -->\n\n<!-- This is an auto-generated comment by CodeRabbit -->"
},
{
"file": "scratch/test_signup.js",
"line": 34,
"body": "_\u26a0\ufe0f Potential issue_ | _\ud83d\udfe0 Major_ | _\u26a1 Quick win_\n\n**Sensitive data is being logged directly in scratch scripts.** The shared root cause is unredacted diagnostic logging of PII/secrets that can leak in local/CI logs.\n- `scratch/test_signup.js#L25-L34`: redact cookie values and any token-like fields in JSON output before logging.\n- `scratch/list_developers.js#L10-L13`: avoid raw email output; mask addresses or log only aggregate counts.\n\n<details>\n<summary>\ud83d\udccd Affects 2 files</summary>\n\n- `scratch/test_signup.js#L25-L34` (this comment)\n- `scratch/list_developers.js#L10-L13`\n\n</details>\n\n<details>\n<summary>\ud83e\udd16 Prompt for AI Agents</summary>\n\n```\nVerify each finding against current code. Fix only still-valid issues, skip the\nrest with a brief reason, keep changes minimal, and validate.\n\nIn `@scratch/test_signup.js` around lines 25 - 34, This scratch script contains\nmultiple sites leaking sensitive data in logs. At scratch/test_signup.js lines\n25-34, the cookie function logs raw cookie values (which may contain session\ntokens) and the json function logs full response data (which may contain\nPII/tokens). Redact these values before logging by extracting and masking\nsensitive fields like tokens, sessions, or passwords, or replace actual values\nwith placeholders like [REDACTED]. At scratch/list_developers.js lines 10-13,\nraw email addresses are being logged directly. Replace direct email output with\nmasked email addresses (e.g., user***`@example.com`) or log only aggregate counts\nof developers instead of individual identifiable information.\n```\n\n</details>\n\n<!-- consolidated_sites_start -->\n<!--\n<consolidated_sites>\n<site>\n<role>anchor</role>\n<file>scratch/test_signup.js</file>\n<line_range>25-34</line_range>\n</site>\n<site>\n<role>sibling</role>\n<file>scratch/list_developers.js</file>\n<line_range>10-13</line_range>\n</site>\n</consolidated_sites>\n-->\n<!-- consolidated_sites_end -->\n\n<!-- fingerprinting:phantom:poseidon:hawk -->\n\n<!-- cr-comment:v1:4ba130817d8a215c4bc014ce -->\n\n<!-- This is an auto-generated comment by CodeRabbit -->"
}
]