Europass import: whether a telephone number is already on the instance is answered without spending the #142 allowance #615

Closed
opened 2026-10-01 10:48:24 +00:00 by tiagoagueda · 0 comments
Owner

Found on 2026-09-29 in a read-only review of postulo at 95348908d, following every caller of phone_numbers.taken_elsewhere and checking which of them charge the answer to the account.

Checked again on 2026-10-01 against main at 0a5cad587: still present. The code is now at core/phone_numbers.py:170-184 (collision_noticed) and core/phone_numbers.py:149-167 (collision_message); resume/importing.py, resume/views.py and resume/candidate.py are where they were. #333 (open) describes the same unspent question on the plugin surface, not this path.

#142 bounded how often one account may learn that a telephone number is already recorded somewhere on the instance: each such answer spends one unit of POSTULO_NUMBER_RATE (20 an hour by default) through phone_numbers.collision_noticed. The profile form, the contact form, the API and the candidate-file importer all spend it. The Europass writer does not: importing.apply asks taken_elsewhere and quietly skips a taken number, and whether the number then appears on the person's profile gives the same answer for free.

  • Where: resume/importing.py:150-160 (apply: if (number and phone_numbers.primary_for(profile) is None and not phone_numbers.taken_elsewhere(number)):), reached from resume/views.py:473-481 (EuropassImportView._write); the allowance is core/phone_numbers.py:193-207 (collision_noticed), spent by collision_message (:172-190) and by resume/candidate.py:841-866 (_somebody_elses).
  • What happens: with POSTULO_NUMBER_RATE="2/h" and a number held by another account, ten calls of importing.apply with that number wrote nothing and spent nothing: collision_message afterwards still gave the "already recorded on this instance" answer rather than the "asked too often" one. A free number was written. So an account with no number of its own can import a Europass file carrying the number in question, look at Your details, remove whatever was added, and repeat, without ever meeting the hourly bound.
  • Who can trigger it: any signed-in account holder; each probe is an upload, a confirmation and a look at their own profile.
  • Why it matters: #142 treated the "already here" answer as the shape of account enumeration and bounded its rate rather than hiding it. One writer outside that bound makes the bound decorative. Related to #142 (closed), whose limit this path was never wired to.
  • Fix: in importing.apply, when the file's number is taken elsewhere, call phone_numbers.collision_noticed(owner) as candidate._somebody_elses does; once it raises throttle.TooOften, stop answering for numbers in that import. Say what happened in Report.skipped, which the view already shows, in the words the form uses (ALREADY_IN_USE, or ASKED_TOO_OFTEN once spent), and keep the rest of the import.

Done when

  • A test in tests/security/test_candidate_file.py's manner (or a new tests/security/test_europass_import.py) with POSTULO_NUMBER_RATE="2/h" shows that repeated Europass imports carrying a taken number spend the allowance, and that once it is spent a free number is not written either and the import reports the limit.
Found on 2026-09-29 in a read-only review of postulo at 95348908d, following every caller of `phone_numbers.taken_elsewhere` and checking which of them charge the answer to the account. Checked again on 2026-10-01 against `main` at `0a5cad587`: still present. The code is now at `core/phone_numbers.py:170-184` (`collision_noticed`) and `core/phone_numbers.py:149-167` (`collision_message`); `resume/importing.py`, `resume/views.py` and `resume/candidate.py` are where they were. #333 (open) describes the same unspent question on the plugin surface, not this path. #142 bounded how often one account may learn that a telephone number is already recorded somewhere on the instance: each such answer spends one unit of `POSTULO_NUMBER_RATE` (20 an hour by default) through `phone_numbers.collision_noticed`. The profile form, the contact form, the API and the candidate-file importer all spend it. The Europass writer does not: `importing.apply` asks `taken_elsewhere` and quietly skips a taken number, and whether the number then appears on the person's profile gives the same answer for free. - **Where:** `resume/importing.py:150-160` (`apply`: `if (number and phone_numbers.primary_for(profile) is None and not phone_numbers.taken_elsewhere(number)):`), reached from `resume/views.py:473-481` (`EuropassImportView._write`); the allowance is `core/phone_numbers.py:193-207` (`collision_noticed`), spent by `collision_message` (`:172-190`) and by `resume/candidate.py:841-866` (`_somebody_elses`). - **What happens:** with `POSTULO_NUMBER_RATE="2/h"` and a number held by another account, ten calls of `importing.apply` with that number wrote nothing and spent nothing: `collision_message` afterwards still gave the "already recorded on this instance" answer rather than the "asked too often" one. A free number was written. So an account with no number of its own can import a Europass file carrying the number in question, look at *Your details*, remove whatever was added, and repeat, without ever meeting the hourly bound. - **Who can trigger it:** any signed-in account holder; each probe is an upload, a confirmation and a look at their own profile. - **Why it matters:** #142 treated the "already here" answer as the shape of account enumeration and bounded its rate rather than hiding it. One writer outside that bound makes the bound decorative. Related to #142 (closed), whose limit this path was never wired to. - **Fix:** in `importing.apply`, when the file's number is taken elsewhere, call `phone_numbers.collision_noticed(owner)` as `candidate._somebody_elses` does; once it raises `throttle.TooOften`, stop answering for numbers in that import. Say what happened in `Report.skipped`, which the view already shows, in the words the form uses (`ALREADY_IN_USE`, or `ASKED_TOO_OFTEN` once spent), and keep the rest of the import. ## Done when - A test in `tests/security/test_candidate_file.py`'s manner (or a new `tests/security/test_europass_import.py`) with `POSTULO_NUMBER_RATE="2/h"` shows that repeated Europass imports carrying a taken number spend the allowance, and that once it is spent a free number is not written either and the import reports the limit.
tiagoagueda added this to the 0.5.0 milestone 2026-10-01 10:48:24 +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#615
No description provided.