Skip to content

audit: security hardening, CodeRabbit tuning, guide expansion#27

Merged
MinitJain merged 1 commit into
mainfrom
audit/security-and-guide-update
Apr 1, 2026
Merged

audit: security hardening, CodeRabbit tuning, guide expansion#27
MinitJain merged 1 commit into
mainfrom
audit/security-and-guide-update

Conversation

@MinitJain

@MinitJain MinitJain commented Mar 31, 2026

Copy link
Copy Markdown
Owner

Summary

  • Security audit completed across all API routes
  • Rate limiting added to all previously unprotected DELETE and collection routes
  • Tag name max-length validation added (50 chars)
  • Silent error swallowing fixed in DELETE /api/bookmarks/[id]
  • `.coderabbit.yaml` updated: assertive profile, path filters, tighter security/Prisma/accessibility rules
  • GUIDE.md expanded locally with 4 missing performance rules (file is gitignored by design)

What was found and fixed

Issue Severity Fixed
No rate limiting on DELETE /api/bookmarks/[id] Medium
No rate limiting on any /api/collections/* route Medium
No max length on tag name input Low
Silent catch in DELETE /api/bookmarks/[id] Low
CodeRabbit profile too lenient for production Low

What was NOT changed

  • Auth is solid across all routes — no issues found
  • npm audit: 0 vulnerabilities
  • No NEXT_PUBLIC_ leaks of secrets
  • TypeScript: 0 errors after changes

Test plan

  • Save a bookmark — confirm it still works
  • Delete a bookmark — confirm it still works and errors now appear in server logs
  • Add a tag longer than 50 chars — should get a 400 response
  • Create and delete a collection — confirm still works
  • Add/remove a bookmark from a collection — confirm still works

Do not merge. Waiting for CodeRabbit review.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Bookmarklet functionality is now live, enabling users to save bookmarks without leaving the page.
    • Added drag-to-save bookmarklet installation to the bookmarks bar for quick access.
    • New "Save without leaving the page" landing page section highlighting web app, browser extension, and bookmarklet saving methods.
  • Improvements

    • Added tag name validation with a 50-character limit.
    • Implemented rate limiting on collections and bookmark operations for improved system stability.
  • Documentation

    • Updated feature status in README: Collections and Bookmarklet now marked as live.

@vercel

vercel Bot commented Mar 31, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
recall Ready Ready Preview, Comment Apr 1, 2026 5:35am

@coderabbitai

coderabbitai Bot commented Mar 31, 2026

Copy link
Copy Markdown

Warning

Rate limit exceeded

@MinitJain has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 4 minutes and 14 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 4 minutes and 14 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 2d587f59-5639-4539-b230-585017624cf7

📥 Commits

Reviewing files that changed from the base of the PR and between 6c3fdd2 and 642c54e.

📒 Files selected for processing (8)
  • .coderabbit.yaml
  • client/src/app/api/bookmarks/[id]/route.ts
  • client/src/app/api/bookmarks/[id]/tags/route.ts
  • client/src/app/api/collections/[id]/bookmarks/[bookmarkId]/route.ts
  • client/src/app/api/collections/[id]/bookmarks/route.ts
  • client/src/app/api/collections/[id]/route.ts
  • client/src/app/api/collections/route.ts
  • client/src/lib/ratelimit.ts

Note

.coderabbit.yaml has unrecognized properties

CodeRabbit is using all valid settings from your configuration. Unrecognized properties (listed below) have been ignored and may indicate typos or deprecated fields that can be removed.

⚠️ Parsing warnings (1)
Validation error: Unrecognized key(s) in object: 'instructions'
⚙️ Configuration instructions
  • Please see the configuration documentation for more information.
  • You can also validate your configuration using the online YAML validator.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json
📝 Walkthrough

Walkthrough

This PR implements a bookmarklet feature enabling URL saving without the browser extension, adds rate limiting (60 ops/hour sliding window) to collection/bookmark API endpoints, introduces tag name length validation (max 50 chars), updates configuration/documentation with stricter review rules, and enhances the landing page with bookmarklet marketing content.

Changes

Cohort / File(s) Summary
Configuration & Documentation
.coderabbit.yaml, CLAUDE.md, README.md
Updated review profile to assertive with new path filters and security/correctness rules; marked P2.6 Bookmarklet complete; reformatted feature/roadmap tables to reflect Bookmarklet and Collections UI as Live.
Bookmarklet Feature
client/src/app/bookmarklet/page.tsx, client/src/app/bookmarklet/BookmarkletSaver.tsx
Introduced new bookmarklet page that validates authentication and URL parameters (http/https only), then renders BookmarkletSaver component. Component manages POST request lifecycle with status tracking (saving/saved/duplicate/error) and auto-closes window on success after 1.5s.
Rate Limiting
client/src/lib/ratelimit.ts, client/src/app/api/collections/route.ts, client/src/app/api/collections/[id]/route.ts, client/src/app/api/collections/[id]/bookmarks/route.ts, client/src/app/api/collections/[id]/bookmarks/[bookmarkId]/route.ts, client/src/app/api/bookmarks/[id]/route.ts
Added collectionRatelimit instance (60 ops/1h sliding window, redis-backed) and applied early-exit rate limit checks to DELETE/POST handlers for collections and bookmarks, returning HTTP 429 when exceeded.
Validation Enhancements
client/src/app/api/bookmarks/[id]/tags/route.ts
Added tag name length validation; rejects names exceeding 50 characters with HTTP 400 error.
Landing Page & UI Updates
client/src/app/page.tsx, client/src/components/SaveUrlForm.tsx
Added "Save without leaving the page" marketing section on landing page with 3-column grid describing Web app, Browser extension, and Bookmarklet saving methods; updated SaveUrlForm with draggable bookmarklet link installation UI.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 36.36% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the three main changes: security hardening (rate limiting, validation, error fixes), CodeRabbit configuration tuning (.coderabbit.yaml updates), and documentation expansion (GUIDE.md).
Description check ✅ Passed The description includes a comprehensive summary, categorized issues table with severity and status, test plan checklist, and verification notes. It covers the core requirements, though the template's type-of-change checklist is not explicitly selected.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch audit/security-and-guide-update

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (1)
client/src/app/bookmarklet/page.tsx (1)

46-57: Use isSafeUrl() for stricter URL validation.

The current validation accepts both http: and https: protocols, but isSafeUrl() from client/src/lib/url-validation.ts enforces HTTPS only and additionally blocks localhost, .local domains, and private IPs. This provides tighter security at the entry point.

Suggested improvement
+import { isSafeUrl } from "@/lib/url-validation";

 // Validate URL param
 let validUrl: string | null = null;
 if (url) {
-  try {
-    const parsed = new URL(url);
-    if (parsed.protocol === "http:" || parsed.protocol === "https:") {
-      validUrl = url;
-    }
-  } catch {
-    // invalid
+  if (isSafeUrl(url)) {
+    validUrl = url;
+  }
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@client/src/app/bookmarklet/page.tsx` around lines 46 - 57, Replace the ad-hoc
URL parsing logic that sets validUrl with a call to isSafeUrl(url): import
isSafeUrl, then set validUrl = url only if isSafeUrl(url) returns true;
otherwise leave validUrl null. Remove the try/catch/URL parsing block and ensure
you reference the same variables (url and validUrl) so downstream code remains
unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@client/src/app/bookmarklet/BookmarkletSaver.tsx`:
- Around line 17-18: The duplicate-detection UI in BookmarkletSaver.tsx expects
a 409 response but api/bookmarks/route.ts currently catches all errors and
always returns 500, so duplicates never surface; update the catch block in the
API route to detect Prisma unique-constraint errors by checking if err is an
instance of Prisma.PrismaClientKnownRequestError and err.code === 'P2002' and
return NextResponse.json({ error: "bookmark already exists" }, { status: 409 })
for that case, otherwise log the error and return the existing 500 response.

In `@client/src/app/bookmarklet/page.tsx`:
- Around line 19-24: The Logo component is currently declared inside the
BookmarkletPage render which triggers an ESLint error and causes component
identity/state to reset on each render; move the Logo function out to module
scope (declare it as a top-level const/function named Logo outside of
BookmarkletPage) and keep using the same <Logo /> JSX in BookmarkletPage,
ensuring the Image import is still in scope and no props change are required.

In `@client/src/app/page.tsx`:
- Around line 399-401: Remove the unnecessary eslint-disable comment before the
anchor in page.tsx: delete the line "/* eslint-disable-next-line no-script-url
*/" that precedes the <a href={`javascript:...`}> so the file no longer contains
an unused eslint directive; keep the existing anchor and its href unchanged
(refer to the anchor/href in page.tsx to locate the spot).

In `@client/src/components/SaveUrlForm.tsx`:
- Around line 71-73: The eslint-disable-next-line no-script-url directive above
the anchor in SaveUrlForm.tsx is unused; remove the comment so the file no
longer contains an unnecessary eslint suppression. Locate the anchor element
setting href to the bookmarklet string in the SaveUrlForm component and delete
the line containing "eslint-disable-next-line no-script-url" (leave the anchor
and its href intact), then run the linter to confirm no warnings remain.

---

Nitpick comments:
In `@client/src/app/bookmarklet/page.tsx`:
- Around line 46-57: Replace the ad-hoc URL parsing logic that sets validUrl
with a call to isSafeUrl(url): import isSafeUrl, then set validUrl = url only if
isSafeUrl(url) returns true; otherwise leave validUrl null. Remove the
try/catch/URL parsing block and ensure you reference the same variables (url and
validUrl) so downstream code remains unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 4a7aa8a5-fa55-47bb-8c96-4d2bd3e3fc43

📥 Commits

Reviewing files that changed from the base of the PR and between ffe15de and 6c3fdd2.

📒 Files selected for processing (14)
  • .coderabbit.yaml
  • CLAUDE.md
  • README.md
  • client/src/app/api/bookmarks/[id]/route.ts
  • client/src/app/api/bookmarks/[id]/tags/route.ts
  • client/src/app/api/collections/[id]/bookmarks/[bookmarkId]/route.ts
  • client/src/app/api/collections/[id]/bookmarks/route.ts
  • client/src/app/api/collections/[id]/route.ts
  • client/src/app/api/collections/route.ts
  • client/src/app/bookmarklet/BookmarkletSaver.tsx
  • client/src/app/bookmarklet/page.tsx
  • client/src/app/page.tsx
  • client/src/components/SaveUrlForm.tsx
  • client/src/lib/ratelimit.ts

Comment on lines +17 to +18
if (res.status === 409) {
setStatus("duplicate");

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify if the API route handles duplicates with 409 status
rg -n -A 10 'prisma.bookmark.create' client/src/app/api/bookmarks/route.ts

Repository: MinitJain/recall

Length of output: 407


🏁 Script executed:

# Get the complete catch block including return statement(s)
cat -n client/src/app/api/bookmarks/route.ts | sed -n '70,85p'

Repository: MinitJain/recall

Length of output: 657


Duplicate detection will not work — API returns 500, not 409.

The component expects HTTP 409 for duplicate URLs (line 17-18), but the API route (lines 70-76) catches all errors and returns HTTP 500. It does not detect Prisma's unique constraint error (P2002) to return 409 for duplicates. The duplicate status will never be triggered.

Fix the API route to detect unique constraint violations:

Proposed fix in client/src/app/api/bookmarks/route.ts
} catch (err) {
  if (err instanceof Prisma.PrismaClientKnownRequestError && err.code === 'P2002') {
    return NextResponse.json({ error: "bookmark already exists" }, { status: 409 });
  }
  console.error("Failed to create bookmark:", err);
  return NextResponse.json({ error: "failed to save bookmark" }, { status: 500 });
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@client/src/app/bookmarklet/BookmarkletSaver.tsx` around lines 17 - 18, The
duplicate-detection UI in BookmarkletSaver.tsx expects a 409 response but
api/bookmarks/route.ts currently catches all errors and always returns 500, so
duplicates never surface; update the catch block in the API route to detect
Prisma unique-constraint errors by checking if err is an instance of
Prisma.PrismaClientKnownRequestError and err.code === 'P2002' and return
NextResponse.json({ error: "bookmark already exists" }, { status: 409 }) for
that case, otherwise log the error and return the existing 500 response.

Comment thread client/src/app/bookmarklet/page.tsx Outdated
Comment thread client/src/app/page.tsx Outdated
Comment thread client/src/components/SaveUrlForm.tsx Outdated
…ogging

- Add collectionRatelimit (60/hr) to all collection POST/DELETE routes
- Add bookmarkRatelimit to DELETE /api/bookmarks/[id] (was unprotected)
- Add max-length (50 chars) validation on tag name in POST /api/bookmarks/[id]/tags
- Add console.error to silent catch in DELETE /api/bookmarks/[id]
- Update .coderabbit.yaml: assertive profile, request_changes_workflow,
  path filters for generated files, and tighter security/Prisma/a11y rules

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@MinitJain MinitJain force-pushed the audit/security-and-guide-update branch from 6c3fdd2 to 642c54e Compare April 1, 2026 05:34
@MinitJain MinitJain merged commit 3530010 into main Apr 1, 2026
4 checks passed
@MinitJain MinitJain deleted the audit/security-and-guide-update branch April 1, 2026 05:38
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