~ pr-review

Report Format

pr-review generates a structured Markdown report saved to the current directory. The filename includes the PR number when detected via the gh CLI. Every section is written for both developers and non-technical stakeholders.

Output filename

ConditionFilename
PR detected (e.g. #42)pr-42-review.md
No PR detectedpr-review-review.md (from config)
Custom via --outputYour specified filename

Report sections

SectionWhat it containsFormat
๐Ÿ“‹ SummaryPlain-English overview of what the PR changes, who it affects, and overall qualityParagraph
๐Ÿ—‚๏ธ At a GlanceOne-row-per-finding summary table across all categories with severity and "must fix?" flagTable
๐Ÿšจ Critical IssuesBugs and breaking changes that must be fixed before merge, with real-world user impactTable
๐Ÿ”’ SecurityAuth bypasses, secret exposure, injection risks โ€” with who is affected and a fixTable
โšก PerformanceSlow queries, memory leaks, blocking I/O โ€” with user-facing impact described plainlyTable
๐Ÿ› ๏ธ Code QualityNon-blocking improvements: error handling, naming, duplication, anti-patternsTable
๐Ÿงช Test CoverageMissing tests, untested edge cases, and suggested test cases to addTable
๐Ÿ’ก SuggestionsOptional nice-to-haves with effort estimates (๐ŸŸข Low / ๐ŸŸก Medium / ๐Ÿ”ด High)Table + bullets
โœ… Final VerdictAPPROVE / REQUEST CHANGES / NEEDS DISCUSSION with plain-English justification and blocking item listParagraph + bullets

Severity levels

IndicatorMeaningAction required?
๐Ÿ”ด Critical / HighData loss, security breach, or broken functionality likelyMust fix before merge
๐ŸŸก MediumNoticeable user impact or elevated risk under certain conditionsStrongly recommended
๐ŸŸข LowMinor improvement with no immediate riskNice to have

Sample report

A condensed example showing all sections. See the full Example Report for a comprehensive real-world sample you can download.

pr-42-review.md
# PR Review: `feature/auth` โ†’ `main`

**PR:** #42 โ€” feature/auth โ†’ main
**Date:** 2026-04-08
**Provider:** claude (claude-sonnet-4-6)
**Files reviewed:** 3 files, 142 insertions, 28 deletions

---

## ๐Ÿ“‹ Summary
This PR introduces JWT-based authentication middleware. The overall structure is solid, but there
are two critical security issues that must be resolved before this is safe to merge. Written for
both developers and non-technical reviewers โ€” no coding knowledge required to understand the risks.

---

## ๐Ÿ—‚๏ธ At a Glance
| # | Category | Severity | What's the problem? | Must fix before release? |
|---|----------|----------|---------------------|--------------------------|
| 1 | ๐Ÿšจ Critical | ๐Ÿ”ด High | JWT token expiry is never checked โ€” expired tokens stay valid forever | Yes |
| 2 | ๐Ÿ”’ Security | ๐Ÿ”ด High | JWT secret falls back to empty string if env var is missing | Yes |
| 3 | ๐Ÿ› ๏ธ Quality | ๐ŸŸข Low | Magic number 3600 should be a named constant | No |

---

## ๐Ÿšจ Critical Issues
> These problems **must be fixed before this change goes live**.

| Severity | What's the problem? | Real-world impact on users | File / Location | What the developer should do |
|----------|---------------------|----------------------------|-----------------|------------------------------|
| ๐Ÿ”ด Critical | JWT token expiry (`exp` claim) is decoded but never validated | A logged-out or deactivated user's token remains valid indefinitely โ€” they can keep accessing protected pages | `src/middleware/auth.js:42` | Add `if (Date.now() >= payload.exp * 1000) throw new TokenExpiredError()` after decoding |

---

## ๐Ÿ”’ Security
> Security issues can expose user data or allow unauthorised access.

| Severity | What's the risk? | Who is affected? | File / Location | Recommended fix |
|----------|-----------------|-----------------|-----------------|-----------------|
| ๐Ÿ”ด High | JWT secret falls back to `""` (empty string) when `JWT_SECRET` env var is missing โ€” tokens can be forged by anyone | All authenticated users | `src/config/jwt.js:8` | Throw at startup: `if (!process.env.JWT_SECRET) throw new Error("JWT_SECRET is required")` |

---

## ๐Ÿ› ๏ธ Code Quality & Improvements
| Area | What's the issue? | Why it matters | File / Location | Recommended improvement |
|------|------------------|----------------|-----------------|-------------------------|
| Magic numbers | The 3600-second expiry is hardcoded inline | If the value needs to change it must be updated in multiple places | `src/config/jwt.js:14` | Extract to `const TOKEN_EXPIRY_SECONDS = 3600` at the top of the file |

---

## ๐Ÿงช Test Coverage
| What's not being tested? | Why it matters | Possible consequence if it breaks | Suggested test to add |
|--------------------------|----------------|-----------------------------------|-----------------------|
| Expired token rejection | Tokens must be refused after expiry | Expired sessions remain active โ€” security gap | Test that a token with a past `exp` returns `401 Unauthorized` |

---

## ๐Ÿ’ก Suggestions & Nice-to-Haves
| # | Suggestion | Benefit | Effort |
|---|-----------|---------|--------|
| 1 | Add refresh token support | Users won't be logged out mid-session unexpectedly | ๐ŸŸก Medium |
| 2 | Log auth failures (wrong token, expired) to your observability tool | Helps detect brute-force attempts early | ๐ŸŸข Low |

---

## โœ… Final Verdict
**REQUEST CHANGES**

Two security issues block this merge: expired tokens are never rejected, and the JWT secret can
silently fall back to an empty string. Both are straightforward fixes. Once resolved and covered
by tests, this PR is ready to ship.

**Blocking items from Glance before merge:**
- Fix #1 โ€” Validate the `exp` claim after decoding (row 1)
- Fix #2 โ€” Throw on missing `JWT_SECRET` at startup (row 2)