Skip to content

Harden scheduling against unbounded ranges and invalid input#27

Merged
marianogoldman merged 2 commits into
masterfrom
security-hardening
Jun 13, 2026
Merged

Harden scheduling against unbounded ranges and invalid input#27
marianogoldman merged 2 commits into
masterfrom
security-hardening

Conversation

@marianogoldman

Copy link
Copy Markdown
Contributor

Context

Security review of the library. No injection sinks (eval/exec/unserialize/SQL/file I/O are all absent) and composer audit reports no vulnerable dependencies. The realistic risks were resource exhaustion (DoS) and weak input validation. This PR addresses all four findings from that review.

Changes

🟠 Unbounded date range → memory/CPU exhaustion

WeeklyScheduleAgenda, AgendaSlotter and DaySlotter emit one entry per day (and per slot) across the [from, to] window, so a huge range could exhaust memory. They now take an optional maxDays argument (default 366, 0 disables) and throw Puntodev\Bookables\Exceptions\DateRangeTooLargeException when the window is larger, via a shared DateRangeGuard.

🟡 Non-positive duration/step → degenerate zero-interval loops

AgendaSlotter and DaySlotter constructors now reject duration/step ≤ 0 (and negative timeAfter/timeBefore) with InvalidArgumentException.

🟡 Loose time validation

WeeklySchedule validates times strictly as a time of day (HH:MM or HH:MM:SS, 00:0023:59), rejecting relative expressions like now / +1 day and out-of-range values like 25:00 / 14:60.

🔵 Malformed JSON

WeeklySchedule::fromJson() throws a clear Exception on malformed/non-object JSON instead of leaking a TypeError.

Tests

TDD throughout — each test was watched failing before implementing. 51 tests pass (37 existing + 14 new).

⚠️ Potentially breaking

  • Times must be zero-padded (08:00, not 8:00). Matches the defaults/docs, but stricter than before.
  • Ranges spanning more than maxDays (default 366) now raise instead of silently generating huge result sets. Pass maxDays: 0 to opt out.

🤖 Generated with Claude Code

Security review follow-up. No injection or vulnerable dependencies were
found; the realistic risks were resource exhaustion and weak input
validation. This addresses them:

- Cap requested date ranges (DoS guard). WeeklyScheduleAgenda,
  AgendaSlotter and DaySlotter now take an optional maxDays argument
  (default 366, 0 disables) and throw DateRangeTooLargeException when the
  [from, to] window is larger, via a shared DateRangeGuard.
- Reject non-positive duration/step (and negative timeAfter/timeBefore)
  in the slotters with InvalidArgumentException, preventing degenerate
  zero-interval loops.
- Validate WeeklySchedule times strictly as a time of day (HH:MM or
  HH:MM:SS, 00:00-23:59), rejecting relative expressions such as "now".
- Throw a clear Exception on malformed/non-object JSON in
  WeeklySchedule::fromJson() instead of a TypeError.

Docs (README caveats + schema note) and CHANGELOG updated.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
@marianogoldman marianogoldman marked this pull request as ready for review June 13, 2026 12:27
Rebuild CHANGELOG.md in Keep a Changelog format from every tagged release
(v0.0.1 through v4.1.2) using the GitHub release notes and the merged PRs
between tags. The Unreleased section also captures the changes already on
master without a release yet (#25, #26) alongside the security hardening.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
@marianogoldman marianogoldman merged commit e1a9062 into master Jun 13, 2026
2 checks passed
@marianogoldman marianogoldman deleted the security-hardening branch June 13, 2026 12:41
@marianogoldman marianogoldman added the security Security fix or hardening label Jun 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

security Security fix or hardening

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant