Skip to content

fix(core): validate job names and unique keys at the API boundary#269

Merged
ueberBrot merged 2 commits intomainfrom
fix/job-identifier-validation
Mar 17, 2026
Merged

fix(core): validate job names and unique keys at the API boundary#269
ueberBrot merged 2 commits intomainfrom
fix/job-identifier-validation

Conversation

@ueberBrot
Copy link
Copy Markdown
Owner

@ueberBrot ueberBrot commented Mar 17, 2026

Reject empty, whitespace-only, and control-character identifiers before worker registration or scheduling reaches MongoDB operations.

Preserve existing valid printable job names used by schedule() tests while failing fast on malformed public identifiers.

Summary by CodeRabbit

  • New Features

    • Added validation for job names and unique keys at the API boundary, ensuring invalid identifiers fail fast before database operations.
  • Bug Fixes

    • Job scheduling now prevents malformed identifiers from reaching MongoDB.
  • Documentation

    • Expanded public API with InvalidJobIdentifierError, validateJobName, and validateUniqueKey utilities.

@changeset-bot
Copy link
Copy Markdown

changeset-bot bot commented Mar 17, 2026

🦋 Changeset detected

Latest commit: 326aacb

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@monque/core Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@ueberBrot
Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 17, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Reject empty, whitespace-only, and control-character identifiers before
worker registration or scheduling reaches MongoDB operations.

Preserve existing valid printable job names used by schedule() tests while
failing fast on malformed public identifiers.
@ueberBrot ueberBrot force-pushed the fix/job-identifier-validation branch from 8c4cdb3 to d5d6a6a Compare March 17, 2026 10:02
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 17, 2026

📝 Walkthrough

Walkthrough

This PR adds input validation for job identifiers (names and unique keys) at the public API boundary. New validators check identifiers for empty values, length constraints, and invalid characters. A new error type InvalidJobIdentifierError is introduced. The validators are integrated into scheduling and registration methods to fail fast before database operations.

Changes

Cohort / File(s) Summary
Changeset & Manifest
.changeset/blue-rats-train.md
Documents patch release with note about identifier validation at public API boundary.
Error Type Definition
packages/core/src/shared/errors.ts
Introduces InvalidJobIdentifierError class extending MonqueError with field and value properties to report which identifier failed validation and why.
Validators Implementation
packages/core/src/shared/utils/job-identifiers.ts
Adds validateJobName and validateUniqueKey functions with regex patterns and length constraints (max 255 and 1024 chars respectively); throws InvalidJobIdentifierError on violations.
Public API Exports
packages/core/src/index.ts, packages/core/src/shared/index.ts, packages/core/src/shared/utils/index.ts
Re-exports InvalidJobIdentifierError, validateJobName, and validateUniqueKey to expand public API surface.
Scheduler Integration
packages/core/src/scheduler/monque.ts, packages/core/src/scheduler/services/job-scheduler.ts
Integrates validators into enqueue, schedule, register methods via new private helper methods; updates docblocks with throws annotations; changes deduplication checks to explicit undefined comparisons.
Test Coverage
packages/core/tests/unit/errors.test.ts, packages/core/tests/unit/job-identifiers.test.ts, packages/core/tests/unit/monque.test.ts, packages/core/tests/unit/services/job-scheduler.test.ts
Adds comprehensive tests for new error class, validators, and integration points; verifies invalid identifiers are rejected before scheduler delegation.

Possibly Related PRs

  • Management Apis v.1.1.0 #75 — Modifies scheduler surface (monque.ts, job-scheduler.ts) and shared exports/errors; identifier validation integrates into same enqueue/schedule/register flows.
  • Monque job scheduler core package v0.1.0 #1 — Directly modifies core modules (scheduler/monque.ts, shared/errors.ts, shared utils) that this PR extends with new validation logic.

Estimated Code Review Effort

🎯 3 (Moderate) | ⏱️ ~20 minutes


🐰 Hop-hop, validators come!
No more bad names reaching the database,
Job identifiers checked with care,
Errors caught before they dare
Fast fail, happy trails!

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically describes the main change: adding validation for job names and unique keys at the API boundary, which matches the primary objective across multiple files in the changeset.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

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

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/job-identifier-validation
📝 Coding Plan
  • Generate coding plan for human review comments

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.

@codecov
Copy link
Copy Markdown

codecov bot commented Mar 17, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 97.35%. Comparing base (4429734) to head (326aacb).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #269      +/-   ##
==========================================
+ Coverage   97.27%   97.35%   +0.07%     
==========================================
  Files          26       27       +1     
  Lines        1249     1286      +37     
  Branches      315      332      +17     
==========================================
+ Hits         1215     1252      +37     
  Misses         34       34              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@ueberBrot
Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 17, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown

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

🧹 Nitpick comments (4)
packages/core/tests/unit/services/job-scheduler.test.ts (1)

161-166: Use an invalid cron here to prove validation precedence.

This only proves the name check happens before MongoDB calls. With a valid cron string, the test still passes if schedule() parses cron before validating name. Make the cron invalid too and assert that InvalidJobIdentifierError wins.

🧪 Tighten the ordering assertion
 		it('should reject invalid scheduled job names before parsing cron', async () => {
-			await expect(scheduler.schedule('0 * * * *', 'bad name', {})).rejects.toThrow(
+			await expect(scheduler.schedule('invalid cron', 'bad name', {})).rejects.toThrow(
 				InvalidJobIdentifierError,
 			);
 			expect(ctx.mockCollection.insertOne).not.toHaveBeenCalled();
 		});
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/tests/unit/services/job-scheduler.test.ts` around lines 161 -
166, The test currently only uses a valid cron which doesn't prove
name-validation runs before cron parsing; update the test in
job-scheduler.test.ts to pass an invalid cron string (e.g., malformed cron)
along with the invalid job name to scheduler.schedule so the call would also
fail cron parsing if order were reversed, then assert that
InvalidJobIdentifierError is still thrown and that ctx.mockCollection.insertOne
was not called; reference the schedule(...) call and InvalidJobIdentifierError
to locate and modify the test.
packages/core/tests/unit/job-identifiers.test.ts (1)

15-25: Cover the two job-name rejection branches more directly.

The suite never hits the whitespace-only branch, and the current control-character case uses \n, which would still fail if the control-character check regressed because newlines are also whitespace. Add a ' ' case and use a non-whitespace control character such as \u0000.

🧪 Suggested test additions
 		it('rejects empty job names', () => {
 			expect(() => validateJobName('')).toThrow(InvalidJobIdentifierError);
 		});
+
+		it('rejects whitespace-only job names', () => {
+			expect(() => validateJobName('   ')).toThrow(InvalidJobIdentifierError);
+		});
 
 		it('rejects names with whitespace', () => {
 			expect(() => validateJobName('email send')).toThrow(InvalidJobIdentifierError);
 		});
 
 		it('rejects names with control characters', () => {
-			expect(() => validateJobName('email\nsend')).toThrow(InvalidJobIdentifierError);
+			expect(() => validateJobName('email\u0000send')).toThrow(InvalidJobIdentifierError);
 		});
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/tests/unit/job-identifiers.test.ts` around lines 15 - 25, Add
two more unit tests for validateJobName to directly exercise the whitespace-only
and non-whitespace control-character branches: add a case that calls
validateJobName('   ') and asserts it throws InvalidJobIdentifierError (covers
whitespace-only rejection), and add a case that calls validateJobName('\u0000')
and asserts it throws InvalidJobIdentifierError (covers control-character
rejection using a non-whitespace control char). Ensure both tests use the same
pattern as existing tests (expect(() =>
validateJobName(...)).toThrow(InvalidJobIdentifierError)) and place them
alongside the other job-name rejection tests.
packages/core/src/shared/errors.ts (1)

205-213: Don't log error.value verbatim in the example.

This field can contain control characters or sensitive deduplication keys, so the current example normalizes a logging pattern that's easy to misuse. Prefer field + message, or a sanitized preview.

🛡️ Safer example
 * } catch (error) {
 *   if (error instanceof InvalidJobIdentifierError) {
- *     console.error(`Invalid ${error.field}:`, error.value);
+ *     console.error(`Invalid ${error.field}: ${error.message}`);
 *   }
 * }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/src/shared/errors.ts` around lines 205 - 213, The example in
the InvalidJobIdentifierError usage logs error.value verbatim which may expose
control characters or sensitive deduplication keys; update the example that
references InvalidJobIdentifierError (and the variables error.field /
error.value) to avoid printing error.value directly—log a descriptive message
using error.field and a sanitized preview or message instead (e.g., include
field and a redacted/trimmed snippet or a constant placeholder) so the example
demonstrates safe logging practices without exposing raw values.
packages/core/tests/unit/monque.test.ts (1)

193-199: Consider adding one explicit control-character worker-name case.

This test covers whitespace-in-name well. Adding a case like '\n' or '\u0000' would lock the control-character requirement directly at register() boundary too.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/tests/unit/monque.test.ts` around lines 193 - 199, Add an
explicit control-character case to the invalid-name test: call monque.register
with a control-only name such as '\n' or '\u0000' (using the same handler used
in the test) and assert it throws InvalidJobIdentifierError, so the register()
boundary is verified for control characters as well as whitespace.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@packages/core/src/shared/errors.ts`:
- Around line 205-213: The example in the InvalidJobIdentifierError usage logs
error.value verbatim which may expose control characters or sensitive
deduplication keys; update the example that references InvalidJobIdentifierError
(and the variables error.field / error.value) to avoid printing error.value
directly—log a descriptive message using error.field and a sanitized preview or
message instead (e.g., include field and a redacted/trimmed snippet or a
constant placeholder) so the example demonstrates safe logging practices without
exposing raw values.

In `@packages/core/tests/unit/job-identifiers.test.ts`:
- Around line 15-25: Add two more unit tests for validateJobName to directly
exercise the whitespace-only and non-whitespace control-character branches: add
a case that calls validateJobName('   ') and asserts it throws
InvalidJobIdentifierError (covers whitespace-only rejection), and add a case
that calls validateJobName('\u0000') and asserts it throws
InvalidJobIdentifierError (covers control-character rejection using a
non-whitespace control char). Ensure both tests use the same pattern as existing
tests (expect(() => validateJobName(...)).toThrow(InvalidJobIdentifierError))
and place them alongside the other job-name rejection tests.

In `@packages/core/tests/unit/monque.test.ts`:
- Around line 193-199: Add an explicit control-character case to the
invalid-name test: call monque.register with a control-only name such as '\n' or
'\u0000' (using the same handler used in the test) and assert it throws
InvalidJobIdentifierError, so the register() boundary is verified for control
characters as well as whitespace.

In `@packages/core/tests/unit/services/job-scheduler.test.ts`:
- Around line 161-166: The test currently only uses a valid cron which doesn't
prove name-validation runs before cron parsing; update the test in
job-scheduler.test.ts to pass an invalid cron string (e.g., malformed cron)
along with the invalid job name to scheduler.schedule so the call would also
fail cron parsing if order were reversed, then assert that
InvalidJobIdentifierError is still thrown and that ctx.mockCollection.insertOne
was not called; reference the schedule(...) call and InvalidJobIdentifierError
to locate and modify the test.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 5325cc0b-bd77-459b-8012-9333349a4b75

📥 Commits

Reviewing files that changed from the base of the PR and between 4429734 and d5d6a6a.

📒 Files selected for processing (12)
  • .changeset/blue-rats-train.md
  • packages/core/src/index.ts
  • packages/core/src/scheduler/monque.ts
  • packages/core/src/scheduler/services/job-scheduler.ts
  • packages/core/src/shared/errors.ts
  • packages/core/src/shared/index.ts
  • packages/core/src/shared/utils/index.ts
  • packages/core/src/shared/utils/job-identifiers.ts
  • packages/core/tests/unit/errors.test.ts
  • packages/core/tests/unit/job-identifiers.test.ts
  • packages/core/tests/unit/monque.test.ts
  • packages/core/tests/unit/services/job-scheduler.test.ts

@ueberBrot ueberBrot enabled auto-merge (squash) March 17, 2026 10:22
@ueberBrot ueberBrot merged commit cb707b9 into main Mar 17, 2026
8 checks passed
@ueberBrot ueberBrot mentioned this pull request Mar 17, 2026
@ueberBrot ueberBrot deleted the fix/job-identifier-validation branch March 17, 2026 10:28
@ueberBrot ueberBrot mentioned this pull request Mar 19, 2026
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