Skip to content

fix: enforce hourly frequency restriction for free-tier users#250

Merged
bd73-com merged 6 commits intomainfrom
claude/review-github-issues-YIno5
Mar 22, 2026
Merged

fix: enforce hourly frequency restriction for free-tier users#250
bd73-com merged 6 commits intomainfrom
claude/review-github-issues-YIno5

Conversation

@bd73-com
Copy link
Owner

@bd73-com bd73-com commented Mar 22, 2026

Summary

Adds server-side enforcement preventing free-tier users from using hourly check frequency on monitors. Previously, only the client UI restricted this — a direct API call could bypass it entirely. This closes the authorization gap across all four monitor-creation/update surfaces.

Changes

Core logic

  • Add FREQUENCY_TIERS constant to shared/models/auth.ts defining which tiers allow each frequency
  • Add checkFrequencyTier() validation function to server/services/monitorValidation.ts
  • Separate error codes: FREQUENCY_TIER_RESTRICTED (403) for tier violations, INVALID_FREQUENCY (400) for unknown values

Endpoint enforcement

  • POST /api/monitors and PATCH /api/monitors/:id (SPA routes) — frequency tier check added
  • POST /api/extension/monitors (extension route) — frequency tier check added
  • POST /api/v1/monitors and PATCH /api/v1/monitors/:id (public API) — frequency tier check added

Tests

  • 7 unit tests for checkFrequencyTier covering all tier/frequency combos, edge cases, error messages
  • 1 integration test for extension endpoint frequency rejection
  • All 1725 tests pass

Documentation

  • Developer.tsx: Added tier restriction note to POST /monitors docs, added FREQUENCY_TIER_RESTRICTED to error codes table

How to test

  1. Free-tier user creates hourly monitor via API: POST to /api/monitors with frequency: "hourly" — expect 403 with FREQUENCY_TIER_RESTRICTED
  2. Free-tier user updates via v1 API: PATCH to /api/v1/monitors/:id with frequency: "hourly" — expect 403
  3. Pro/Power user creates hourly monitor: Should succeed as before
  4. Run tests: npm run check && npm run test — all 1725 pass

Related issues

https://claude.ai/code/session_01GWwVsyBJt7ZjxS9YJZdYpr

Summary by CodeRabbit

  • New Features

    • Enforced tier-based frequency limits: hourly monitors now require a Pro or Power plan; daily remains available on all plans.
  • Documentation

    • Clarified the frequency field to note the plan requirement for hourly.
    • Added an error reference entry for 403 FREQUENCY_TIER_RESTRICTED.
  • Tests

    • Added tests covering frequency-tier validation, allowed/rejected cases, and error responses.

…sers

Add FREQUENCY_TIERS constant to shared/models/auth.ts and
checkFrequencyTier validation to the monitor create (main + extension)
and update endpoints. Free-tier users sending frequency: "hourly" now
receive a 403 with code FREQUENCY_TIER_RESTRICTED.

Closes #247

https://claude.ai/code/session_01GWwVsyBJt7ZjxS9YJZdYpr
@bd73-com bd73-com added the fix label Mar 22, 2026
@coderabbitai
Copy link

coderabbitai bot commented Mar 22, 2026

Caution

Review failed

Pull request was closed or merged during review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: f92b49f6-be9f-48de-9c87-ad387ac3875d

📥 Commits

Reviewing files that changed from the base of the PR and between 4a6e245 and cb15151.

📒 Files selected for processing (1)
  • server/services/monitorValidation.ts

📝 Walkthrough

Walkthrough

Adds server-side frequency-tier enforcement: a new FREQUENCY_TIERS mapping and exported checkFrequencyTier validator; validator is applied to monitor create/update endpoints (main API, v1, and extension). Docs and tests updated; endpoints short-circuit on disallowed frequencies with structured error responses.

Changes

Cohort / File(s) Summary
Auth models
shared/models/auth.ts
Add exported FREQUENCY_TIERS mapping linking frequencies to allowed tiers (dailyfree, pro, power; hourlypro, power).
Frequency validation service & tests
server/services/monitorValidation.ts, server/services/monitorValidation.test.ts
Introduce exported checkFrequencyTier(frequency, tier) that returns null or structured error (status, code, error); tests cover daily/undefined/hourly/invalid cases and tier behavior.
Route handlers
server/routes.ts, server/routes/extension.ts, server/routes/v1.ts
Integrate checkFrequencyTier into monitor creation and conditional update flows; handlers return the validator's { message, code } with HTTP status when frequency is disallowed, short-circuiting further validation/persistence.
Route tests (extension)
server/routes/extension.test.ts
Add mock for checkFrequencyTier and test asserting free-tier users receive 403 with code: "FREQUENCY_TIER_RESTRICTED" when submitting frequency: "hourly".
Documentation
client/src/pages/Developer.tsx
Update “Create monitor” field docs to note hourly requires Pro/Power and add FREQUENCY_TIER_RESTRICTED (403) to the Error reference table.

Sequence Diagram(s)

mermaid
sequenceDiagram
participant Client as Client
participant API as API Endpoint
participant Auth as Auth/Tier
participant Validator as checkFrequencyTier
participant SSRF as SSRF/Selector Validation
participant DB as Database/Persistence

Client->>API: POST /api/monitors (body with frequency)
API->>Auth: derive user tier
API->>Validator: checkFrequencyTier(frequency, tier)
alt freq rejected
Validator-->>API: {status:403, code:"FREQUENCY_TIER_RESTRICTED", error}
API-->>Client: HTTP 403 + { message: error, code }
else freq allowed
API->>SSRF: validate url/selector
SSRF-->>API: validation OK
API->>DB: persist monitor
DB-->>API: created
API-->>Client: HTTP 201 + monitor

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Possibly related issues

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 75.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'fix: enforce hourly frequency restriction for free-tier users' directly and clearly summarizes the main change—implementing server-side validation to prevent free-tier users from using hourly frequency.
Linked Issues check ✅ Passed All coding requirements from issue #247 are met: FREQUENCY_TIERS constant added [shared/models/auth.ts], checkFrequencyTier validation implemented [server/services/monitorValidation.ts], enforced across all required endpoints (POST/PATCH /api/monitors, POST /api/extension/monitors, POST/PATCH /api/v1/monitors), correct HTTP status codes returned (403 FREQUENCY_TIER_RESTRICTED, 400 INVALID_FREQUENCY), with comprehensive unit and integration tests added.
Out of Scope Changes check ✅ Passed All changes are directly scoped to enforcing hourly frequency restriction: validation logic, tier mappings, route enforcement, tests, and documentation updates. No unrelated modifications detected.

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

✨ 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 claude/review-github-issues-YIno5

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.

claude added 2 commits March 22, 2026 14:08
The security review identified that /api/v1/monitors POST and PATCH
endpoints were missing the checkFrequencyTier guard, allowing free-tier
users to bypass the hourly frequency restriction via the public API.

https://claude.ai/code/session_01GWwVsyBJt7ZjxS9YJZdYpr
Derive the required tier names from FREQUENCY_TIERS constant instead of
hardcoding "pro or power". Return INVALID_FREQUENCY with 400 status for
unrecognized frequency values, separate from FREQUENCY_TIER_RESTRICTED
(403) for known frequencies on insufficient tiers.

https://claude.ai/code/session_01GWwVsyBJt7ZjxS9YJZdYpr
Note that hourly frequency requires Pro or Power plan in the POST
endpoint description, and add FREQUENCY_TIER_RESTRICTED to the error
codes table.

https://claude.ai/code/session_01GWwVsyBJt7ZjxS9YJZdYpr
coderabbitai[bot]
coderabbitai bot previously requested changes Mar 22, 2026
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@server/services/monitorValidation.ts`:
- Line 50: The check using "tier as any" loses type safety; replace it with a
proper type guard or a safer assertion: ensure the runtime check uses a
predicate like isAllowedTier(tier) or cast from unknown to the specific union
type (e.g., treat tier as unknown then assert to the Tier union) before calling
allowedTiers.includes, and/or implement a function (isTier(value): value is
Tier) and call that instead of "tier as any" so that allowedTiers.includes(tier)
is type-safe; update references to allowedTiers and the tier parameter in
monitorValidation.ts accordingly.
🪄 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: ASSERTIVE

Plan: Pro

Run ID: b64d4b0b-6df4-4f61-a56f-a73e510fe915

📥 Commits

Reviewing files that changed from the base of the PR and between 74c91ee and 654a26e.

📒 Files selected for processing (8)
  • client/src/pages/Developer.tsx
  • server/routes.ts
  • server/routes/extension.test.ts
  • server/routes/extension.ts
  • server/routes/v1.ts
  • server/services/monitorValidation.test.ts
  • server/services/monitorValidation.ts
  • shared/models/auth.ts

code: "INVALID_FREQUENCY",
};
}
if (!allowedTiers.includes(tier as any)) {
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Minor: Type assertion could be improved.

The as any cast works but loses type safety. Consider using a type guard or a more precise assertion:

♻️ Optional refactor for type safety
-  if (!allowedTiers.includes(tier as any)) {
+  if (!(allowedTiers as readonly string[]).includes(tier)) {
📝 Committable suggestion

‼️ IMPORTANT
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.

Suggested change
if (!allowedTiers.includes(tier as any)) {
if (!(allowedTiers as readonly string[]).includes(tier)) {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@server/services/monitorValidation.ts` at line 50, The check using "tier as
any" loses type safety; replace it with a proper type guard or a safer
assertion: ensure the runtime check uses a predicate like isAllowedTier(tier) or
cast from unknown to the specific union type (e.g., treat tier as unknown then
assert to the Tier union) before calling allowedTiers.includes, and/or implement
a function (isTier(value): value is Tier) and call that instead of "tier as any"
so that allowedTiers.includes(tier) is type-safe; update references to
allowedTiers and the tier parameter in monitorValidation.ts accordingly.

Replace `tier as any` with `allowedTiers as readonly string[]` cast
to preserve type safety while satisfying TypeScript's includes() check.

Addresses CodeRabbit review feedback on PR #250.

https://claude.ai/code/session_01GWwVsyBJt7ZjxS9YJZdYpr
coderabbitai[bot]
coderabbitai bot previously requested changes Mar 22, 2026
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@server/services/monitorValidation.ts`:
- Around line 41-43: The current validation treats falsy values (so "" passes)
and does an unsafe dynamic lookup on FREQUENCY_TIERS, enabling prototype
pollution; change the initial check to only allow undefined as "no frequency"
(use frequency === undefined) and reject empty string inputs with a 400 error,
and before indexing FREQUENCY_TIERS guard the lookup with
Object.prototype.hasOwnProperty.call(FREQUENCY_TIERS, frequency) so inherited
keys (e.g. "__proto__") are not accepted; if the hasOwnProperty check fails
return a structured validation error, otherwise safely read allowedTiers =
FREQUENCY_TIERS[frequency as keyof typeof FREQUENCY_TIERS] and proceed with the
existing .includes() logic.
🪄 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: ASSERTIVE

Plan: Pro

Run ID: c76239eb-62cc-4c3f-a3eb-51415fd45996

📥 Commits

Reviewing files that changed from the base of the PR and between 654a26e and 4a6e245.

📒 Files selected for processing (1)
  • server/services/monitorValidation.ts

@bd73-com bd73-com dismissed coderabbitai[bot]’s stale review March 22, 2026 15:42

Fixed: replaced 'tier as any' with safer cast in commit 4a6e245.

…string

Use hasOwnProperty guard before indexing FREQUENCY_TIERS to prevent
__proto__ key access. Use strict undefined check instead of falsy check
to reject empty-string frequency values with a proper 400 error.

https://claude.ai/code/session_01GWwVsyBJt7ZjxS9YJZdYpr
@bd73-com bd73-com dismissed coderabbitai[bot]’s stale review March 22, 2026 15:44

Fixed: added hasOwnProperty guard and strict undefined check in commit cb15151.

@bd73-com bd73-com merged commit cedfb02 into main Mar 22, 2026
2 of 3 checks passed
@bd73-com bd73-com deleted the claude/review-github-issues-YIno5 branch March 22, 2026 15:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bug: Missing server-side enforcement of hourly frequency restriction for free-tier users

2 participants