Calendar and report: a date at the edge of the calendar, or a five-digit quarter year, answers 500 #389

Closed
opened 2026-10-01 10:46:20 +00:00 by tiagoagueda · 0 comments
Owner

Found on 2026-09-29 in a read-only review of postulo at 95348908d, reading how the calendar and the report read a period from the address, and running the period helpers with extreme values.

Checked again on 2026-10-01 against main at 0a5cad587: still present. The code is now at applications/agenda.py:513-524 (month_from and day_from), :436-442 (next_month and previous_month), :630 (week view), :647 (day view) and :662 (agenda view), and the report views are at applications/views.py:1147-1150, :1178 (CSV) and :1207 (PDF); applications/reports.py and applications/slow.py are unchanged. Run again on that code, every address and helper call listed below still raised.

Both pages promise that an address somebody edited by hand gets a sensible page rather than an error: page_for's docstring says a mistyped address "still gets a calendar rather than an error about dates", and period_from's says "Nothing here refuses". The parsers do fall back on text they cannot read, but they accept any year datetime accepts, and the code then steps one period past it or builds a date from an unchecked year. OverflowError or ValueError escapes and the page answers 500.

  • Where:
    • applications/agenda.py:440-451, month_from and day_from: accept 0001 to 9999.
    • applications/agenda.py:364-371, next_month and previous_month: step past 9999-12 and 0001-01; :552 (week view), :569 (day view) and :584 (agenda view) add days past 9999-12-31.
    • applications/reports.py:188-196, _int and _date, and period_from at :211-217: for period=quarter&on=<year>-Q<n>, the year is any integer, and quarter_period (:156) calls dt.date(year, …) with it.
    • applications/reports.py:134-148, Period.shifted: the view (applications/views.py:1107-1110) always asks for the period before and after, which leaves the range for 0001-01 and 9999-12.
    • applications/reports.py:304 (_cadence) and :439 (build, via Period.bounds) overflow for a custom range in December 9999.
  • What happens: run against the snapshot, period_from followed by shifted(-1), shifted(1) and bounds() raised for every one of period=month&on=9999-12, period=month&on=0001-01, period=quarter&on=9999-Q4, period=quarter&on=99999-Q1, period=quarter&on=0-Q1 and period=custom&from=9999-12-01&to=9999-12-31; agenda.month_range(date(9999, 12, 1)) and agenda.previous_month(date(1, 1, 1)) raised OverflowError. The quarter case is the likeliest to be met: a year typed with one digit too many (20266-Q3) reaches dt.date(20266, 7, 1) inside period_from itself. The same period_from runs in the report_pdf errand (applications/slow.py:32), and in the CSV and PDF views (applications/views.py:1138 and :1167).
  • Why it matters: a 500 where the code documents a fallback; the report's CSV and PDF downloads fail the same way from a bookmarked or hand-edited address.
  • Fix: clamp what an address may ask for in one place: have month_from, day_from and reports._date return None outside a sane range (for example years 1900 to 2200), and have the quarter branch of period_from treat a year outside that range as unreadable and fall back to today.year. The pages then fall back to today as their docstrings say.

Done when

  • tests/test_calendar.py requests ?month=0001-01, ?month=9999-12 and ?view=day&on=9999-12-31 and gets 200.
  • tests/test_reports.py requests period=month&on=9999-12, period=quarter&on=20266-Q3 and period=custom&from=9999-12-01&to=9999-12-31, and each answers 200 with the fallback period.
Found on 2026-09-29 in a read-only review of postulo at 95348908d, reading how the calendar and the report read a period from the address, and running the period helpers with extreme values. Checked again on 2026-10-01 against `main` at `0a5cad587`: still present. The code is now at `applications/agenda.py:513-524` (`month_from` and `day_from`), `:436-442` (`next_month` and `previous_month`), `:630` (week view), `:647` (day view) and `:662` (agenda view), and the report views are at `applications/views.py:1147-1150`, `:1178` (CSV) and `:1207` (PDF); `applications/reports.py` and `applications/slow.py` are unchanged. Run again on that code, every address and helper call listed below still raised. Both pages promise that an address somebody edited by hand gets a sensible page rather than an error: `page_for`'s docstring says a mistyped address "still gets a calendar rather than an error about dates", and `period_from`'s says "Nothing here refuses". The parsers do fall back on text they cannot read, but they accept any year `datetime` accepts, and the code then steps one period past it or builds a date from an unchecked year. `OverflowError` or `ValueError` escapes and the page answers 500. - **Where:** - `applications/agenda.py:440-451`, `month_from` and `day_from`: accept 0001 to 9999. - `applications/agenda.py:364-371`, `next_month` and `previous_month`: step past 9999-12 and 0001-01; `:552` (week view), `:569` (day view) and `:584` (agenda view) add days past 9999-12-31. - `applications/reports.py:188-196`, `_int` and `_date`, and `period_from` at `:211-217`: for `period=quarter&on=<year>-Q<n>`, the year is any integer, and `quarter_period` (`:156`) calls `dt.date(year, …)` with it. - `applications/reports.py:134-148`, `Period.shifted`: the view (`applications/views.py:1107-1110`) always asks for the period before and after, which leaves the range for 0001-01 and 9999-12. - `applications/reports.py:304` (`_cadence`) and `:439` (`build`, via `Period.bounds`) overflow for a custom range in December 9999. - **What happens:** run against the snapshot, `period_from` followed by `shifted(-1)`, `shifted(1)` and `bounds()` raised for every one of `period=month&on=9999-12`, `period=month&on=0001-01`, `period=quarter&on=9999-Q4`, `period=quarter&on=99999-Q1`, `period=quarter&on=0-Q1` and `period=custom&from=9999-12-01&to=9999-12-31`; `agenda.month_range(date(9999, 12, 1))` and `agenda.previous_month(date(1, 1, 1))` raised `OverflowError`. The quarter case is the likeliest to be met: a year typed with one digit too many (`20266-Q3`) reaches `dt.date(20266, 7, 1)` inside `period_from` itself. The same `period_from` runs in the `report_pdf` errand (`applications/slow.py:32`), and in the CSV and PDF views (`applications/views.py:1138` and `:1167`). - **Why it matters:** a 500 where the code documents a fallback; the report's CSV and PDF downloads fail the same way from a bookmarked or hand-edited address. - **Fix:** clamp what an address may ask for in one place: have `month_from`, `day_from` and `reports._date` return `None` outside a sane range (for example years 1900 to 2200), and have the quarter branch of `period_from` treat a year outside that range as unreadable and fall back to `today.year`. The pages then fall back to today as their docstrings say. ## Done when - `tests/test_calendar.py` requests `?month=0001-01`, `?month=9999-12` and `?view=day&on=9999-12-31` and gets 200. - `tests/test_reports.py` requests `period=month&on=9999-12`, `period=quarter&on=20266-Q3` and `period=custom&from=9999-12-01&to=9999-12-31`, and each answers 200 with the fallback period.
tiagoagueda added this to the 0.6.0 milestone 2026-10-01 10:46:20 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
Postulo/postulo#389
No description provided.