Skip to content

Cleanup documentation and enhance security for job alerts and API keys#116

Open
RikepilB wants to merge 6 commits into
masterfrom
main
Open

Cleanup documentation and enhance security for job alerts and API keys#116
RikepilB wants to merge 6 commits into
masterfrom
main

Conversation

@RikepilB

@RikepilB RikepilB commented Jul 8, 2026

Copy link
Copy Markdown
Owner

Summary by CodeRabbit

  • New Features

    • Added per-IP rate limiting for public job alert signups, with clear “too many requests” feedback and retry timing.
  • Bug Fixes

    • Improved job alert handling to reject excessive requests before processing input.
    • Protected integration API key entry by hiding it like a password and disabling browser autofill.
  • Documentation

    • Updated contributor guidance for handoff notes and session tracking.
    • Removed outdated handoff/session report documents.

RikepilB added 6 commits July 1, 2026 19:05
Remove superseded docs/HANDOFF.md and docs/session-reports/*, update
AGENTS.md to point at docs/handoff/HANDOFF.md (the tree father).
POST /api/public/job-alerts had no rate limit, unlike the sibling
application-submit endpoints — flagged as an open P0 in
docs/SECURITY-AUDIT.md ("Public routes" / rate limiting).

Reuses the existing per-IP fixed-window limiter from
src/lib/rate-limit.ts (same 10/min shape as the application submit
routes). Unauthenticated GET /unsubscribe is token-scoped and left
as-is.

Tests: 3 new cases (under limit, 429+Retry-After over limit,
per-IP isolation). 209/209 pass.
The webhook integration form rendered the bearer-token API key as
type="text" — plaintext on screen during a demo, screen-share, or
over someone's shoulder. Flagged in docs/SECURITY-AUDIT.md
("Webhooks / integrations": mask API keys in UI/logs).

Switched to type="password" with autoComplete="off" (avoids the
browser offering to save/fill it as a login password).

Tests: new render test asserts the input is masked. 207/207 pass.
chore(docs): finish handoff-tree migration cleanup
fix(job-alerts): rate-limit the public subscribe endpoint
fix(integrations): mask the API key input field
@vercel

vercel Bot commented Jul 8, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
scoutlane Ready Ready Preview, Comment Jul 8, 2026 1:50pm

@coderabbitai

coderabbitai Bot commented Jul 8, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

AGENTS.md is updated to reference a new docs/handoff structure, replacing legacy docs/HANDOFF.md and docs/session-reports files which are deleted. IntegrationForm's API key input is changed to a password field with autocomplete disabled, with a new test. The job-alerts POST endpoint gains per-IP rate limiting returning 429 with Retry-After, with new tests.

Changes

Documentation Restructuring

Layer / File(s) Summary
Handoff process update and legacy docs removal
AGENTS.md, docs/HANDOFF.md, docs/session-reports/*
AGENTS.md now directs contributors to docs/handoff/HANDOFF.md and per-session subfolders, while the prior docs/HANDOFF.md and all docs/session-reports/* files are deleted.

Integration Form Security

Layer / File(s) Summary
API key field hardening
src/app/(admin)/admin/jobs/[id]/integrations/_components/IntegrationForm.tsx, IntegrationForm.test.tsx
The API key input changes from text to password type with autoComplete="off", verified by a new render/interaction test.

Job-Alerts Rate Limiting

Layer / File(s) Summary
Rate limiter setup and enforcement
src/app/api/public/job-alerts/route.ts, route.test.ts
A per-IP rate limiter (10 requests/60s) is checked before body parsing; exceeding the limit returns 429 with a Retry-After header, validated by new tests covering per-IP isolation and limit enforcement.

Estimated code review effort: 2 (Simple) | ~12 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant Route as POST /api/public/job-alerts
  participant Limiter as RateLimiter
  participant Service as subscribe

  Client->>Route: POST request
  Route->>Limiter: check IP allowance
  alt limit exceeded
    Limiter-->>Route: not allowed + reset time
    Route-->>Client: 429 + Retry-After
  else within limit
    Limiter-->>Route: allowed
    Route->>Route: parse and validate body
    Route->>Service: subscribe(email)
    Service-->>Route: result
    Route-->>Client: 200 response
  end
Loading

Possibly related PRs

  • RikepilB/ScoutLane#82: Introduces the shared rate-limiter utilities used by the per-IP limiting added to the job-alerts route in this PR.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately reflects the two main themes of the diff: documentation cleanup and security hardening for job alerts/API keys.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch main

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.

@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.

🧹 Nitpick comments (1)
src/app/api/public/job-alerts/route.ts (1)

10-12: 🩺 Stability & Availability | 🔵 Trivial

Consider a shared store for production rate limiting and note unbounded Map growth.

Two limitations of the current in-memory limiter to be aware of:

  1. Serverless isolation — each function instance gets its own counters Map, so the effective limit is multiplied by the number of concurrent instances. The comment acknowledges this, but for production you may want a shared store (e.g., Upstash Redis, Vercel KV) to enforce a true global limit.

  2. Unbounded memorycreateRateLimiter never evicts stale entries from its counters Map. On a public endpoint with many unique IPs, memory grows indefinitely within a single instance. Consider periodic cleanup of expired entries or a TTL-based cache.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/app/api/public/job-alerts/route.ts` around lines 10 - 12, The current
job-alerts rate limiter uses an in-memory Map via createRateLimiter, which is
only per-instance and never evicts expired entries. Update the public/job-alerts
route to either switch to a shared production store for true global limiting or
clearly gate the in-memory limiter as non-production, and add cleanup/TTL
eviction in createRateLimiter so the counters Map cannot grow without bound. Use
the jobAlertRateLimiter symbol and the createRateLimiter implementation in
src/lib/rate-limit.ts to locate the changes.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@src/app/api/public/job-alerts/route.ts`:
- Around line 10-12: The current job-alerts rate limiter uses an in-memory Map
via createRateLimiter, which is only per-instance and never evicts expired
entries. Update the public/job-alerts route to either switch to a shared
production store for true global limiting or clearly gate the in-memory limiter
as non-production, and add cleanup/TTL eviction in createRateLimiter so the
counters Map cannot grow without bound. Use the jobAlertRateLimiter symbol and
the createRateLimiter implementation in src/lib/rate-limit.ts to locate the
changes.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: ead827e9-c57f-47c1-9679-555e2d424cb5

📥 Commits

Reviewing files that changed from the base of the PR and between f101a63 and f0e6dfc.

📒 Files selected for processing (17)
  • AGENTS.md
  • docs/HANDOFF.md
  • docs/session-reports/2026-05-19-docs-review.md
  • docs/session-reports/2026-05-20-auth-email-notifications-plan.md
  • docs/session-reports/2026-05-20-handoff-system.md
  • docs/session-reports/2026-05-20-parsing-ai-engineer-plan.md
  • docs/session-reports/2026-05-20-parsing-performance-test.md
  • docs/session-reports/2026-05-20-public-job-readability.md
  • docs/session-reports/2026-05-20-resend-google-email-templates.md
  • docs/session-reports/2026-05-26-codex-fixes-notifications-hardening.md
  • docs/session-reports/2026-05-28-takehome-finish-p0.md
  • docs/session-reports/2026-06-11-resume-pipeline-oauth-emails.md
  • docs/session-reports/TEMPLATE.md
  • src/app/(admin)/admin/jobs/[id]/integrations/_components/IntegrationForm.test.tsx
  • src/app/(admin)/admin/jobs/[id]/integrations/_components/IntegrationForm.tsx
  • src/app/api/public/job-alerts/route.test.ts
  • src/app/api/public/job-alerts/route.ts
💤 Files with no reviewable changes (12)
  • docs/session-reports/2026-05-20-parsing-ai-engineer-plan.md
  • docs/session-reports/2026-05-20-auth-email-notifications-plan.md
  • docs/session-reports/2026-05-26-codex-fixes-notifications-hardening.md
  • docs/session-reports/TEMPLATE.md
  • docs/session-reports/2026-06-11-resume-pipeline-oauth-emails.md
  • docs/session-reports/2026-05-20-resend-google-email-templates.md
  • docs/HANDOFF.md
  • docs/session-reports/2026-05-19-docs-review.md
  • docs/session-reports/2026-05-28-takehome-finish-p0.md
  • docs/session-reports/2026-05-20-parsing-performance-test.md
  • docs/session-reports/2026-05-20-handoff-system.md
  • docs/session-reports/2026-05-20-public-job-readability.md

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