Spreadsheet import: a file name over about 106 characters overflows the timeline's actor column #463

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

Found on 2026-09-29 in a read-only review of postulo at 95348908d, reading core/csv_import.perform and importing a file with a long name on an in-memory database.

Checked again on 2026-10-01 against main at 0a5cad587: still present. The code is now at core/csv_import.py:939 (provenance), passed as actor= at :1017, :1021, :1023, :1025, :1037 and :1045, and as summary= at :1042; applications/services.py, applications/models.py and jobs/history.py have not moved.

The text "Imported from " is written as the actor (at most 120 characters) of every timeline entry the spreadsheet import makes, and as the summary (at most 250) of one of them. Neither is shortened. Django keeps an uploaded file's name up to 255 characters, so any name longer than about 106 characters overflows the actor column. SQLite stores the long value silently. PostgreSQL, which docker/compose.postgres.yml sets up, refuses it with a DataError, and the whole import, which runs in one transaction, fails with a server error.

  • Where: core/csv_import.py:944 (provenance = str(_("Imported from %(file)s") % {"file": sheet.filename})), passed as actor= at :1022, :1026, :1028, :1030, :1042 and :1050, and as summary= at :1047; applications/services.py:40-67 (record_event), which stores both as given; applications/models.py:563 (actor = models.CharField(..., max_length=120)).
  • What happens: importing a sheet named with 200 characters on SQLite stored events with a 218-character actor. On PostgreSQL, a file such as "Job applications tracker 2025-2026 - export from Google Sheets (final, cleaned up for Postulo) (2).csv" makes the import page answer 500, and nothing is imported. A translation of "Imported from" that is longer than the English lowers the threshold.
  • Why it matters: a long, descriptive export name is common, and the failure appears only on the production database, not in the tests.
  • Fix: shorten the file name when building the provenance (for example to 80 characters, keeping the extension), and make record_event clip actor and summary to their columns, as jobs/history.py already does for listing events (ACTOR_MAX_CHARS, SUMMARY_MAX_CHARS).

Done when

  • A test in tests/test_csv_import.py imports a sheet with a 250-character file name and asserts every event's actor is at most 120 characters and summary at most 250.
Found on 2026-09-29 in a read-only review of postulo at 95348908d, reading `core/csv_import.perform` and importing a file with a long name on an in-memory database. Checked again on 2026-10-01 against `main` at `0a5cad587`: still present. The code is now at `core/csv_import.py:939` (`provenance`), passed as `actor=` at `:1017`, `:1021`, `:1023`, `:1025`, `:1037` and `:1045`, and as `summary=` at `:1042`; `applications/services.py`, `applications/models.py` and `jobs/history.py` have not moved. The text "Imported from <file name>" is written as the `actor` (at most 120 characters) of every timeline entry the spreadsheet import makes, and as the `summary` (at most 250) of one of them. Neither is shortened. Django keeps an uploaded file's name up to 255 characters, so any name longer than about 106 characters overflows the actor column. SQLite stores the long value silently. PostgreSQL, which `docker/compose.postgres.yml` sets up, refuses it with a `DataError`, and the whole import, which runs in one transaction, fails with a server error. - **Where:** `core/csv_import.py:944` (`provenance = str(_("Imported from %(file)s") % {"file": sheet.filename})`), passed as `actor=` at `:1022`, `:1026`, `:1028`, `:1030`, `:1042` and `:1050`, and as `summary=` at `:1047`; `applications/services.py:40-67` (`record_event`), which stores both as given; `applications/models.py:563` (`actor = models.CharField(..., max_length=120)`). - **What happens:** importing a sheet named with 200 characters on SQLite stored events with a 218-character actor. On PostgreSQL, a file such as "Job applications tracker 2025-2026 - export from Google Sheets (final, cleaned up for Postulo) (2).csv" makes the import page answer 500, and nothing is imported. A translation of "Imported from" that is longer than the English lowers the threshold. - **Why it matters:** a long, descriptive export name is common, and the failure appears only on the production database, not in the tests. - **Fix:** shorten the file name when building the provenance (for example to 80 characters, keeping the extension), and make `record_event` clip `actor` and `summary` to their columns, as `jobs/history.py` already does for listing events (`ACTOR_MAX_CHARS`, `SUMMARY_MAX_CHARS`). ## Done when - A test in `tests/test_csv_import.py` imports a sheet with a 250-character file name and asserts every event's `actor` is at most 120 characters and `summary` at most 250.
tiagoagueda added this to the 0.6.0 milestone 2026-10-01 10:46:53 +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#463
No description provided.