Connection form: STARTTLS with the port left empty dials 993, and the look-back takes any integer #13
Labels
No labels
bug
documentation
enhancement
performance
security
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
Postulo/postulo-imap#13
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Found on 2026-09-29 in a read-only review of postulo-imap at
cc09c6381, reading how the form's optional numbers reach the connection and the search, and running both on the snapshot.Two optional integer fields are used without being checked. The port falls back to 993 whatever security was chosen, so the choice labelled "STARTTLS (port 143)" does not use 143 unless the person types it. The look-back accepts negative and very large numbers: the first makes a connection that looks healthy and never reads anything, the second fails with a raw
OverflowError.1. The port does not follow the security
src/postulo_imap/mailbox.py:266-267,connect():port = int(config.get("port") or 993)is computed beforestarttlsis known.src/postulo_imap/sync.py:47-57: the port isrequired=False, default=993, and the choices are labelled "TLS (port 993)" and "STARTTLS (port 143)".IMAPClient(address, port=993, ssl=False, …)(confirmed with a stand-in client for"",Noneand993): a plain connection to the implicit-TLS port, which waits for a greeting untilTIMEOUT(30 seconds) and then reports "Could not reach the server. Check the address, the port and the security." The message does name the port, but the form has just told the person that STARTTLS means 143. Test runs inside the request, so the page hangs for that half minute.int(config.get("port") or (143 if starttls else 993)), or passNoneand let IMAPClient choose, which it already does (port = ssl and 993 or 143). Invalidate(), refuse a port outside 1–65535.2. The look-back is not bounded
src/postulo_imap/sync.py:109-116declaresdaysas an optional integer;sync.py:149andsync.py:172passint(config.get("days") or mailbox.DEFAULT_SINCE_DAYS);src/postulo_imap/mailbox.py:185computes(dt.datetime.now(dt.UTC) - dt.timedelta(days=since_days)).date().validate()(sync.py:123-129) looks only atfolderandmove_to, and the core builds a plainforms.IntegerFieldwith no minimum or maximum (plugins/forms.py:131-132in postulo), so-30and99999999999999both pass the form.-30, the search isSINCEa date thirty days in the future (run on the snapshot:SINCE 2026-10-31on 2026-10-01). Test answers "Signed in. 0 messages … are waiting to be read." and every pass reports nothing to do, for ever, with no error anywhere.800000the subtraction raisesOverflowError: date value out of range; with1000000000000,dt.timedeltaraisesOverflowError: Python int too large to convert to C int. Neither is aMailboxError, sotest()(sync.py:152) does not catch it; the core's catch-all turns it into "Test failed: OverflowError: date value out of range" (plugins/views.py:402-404) and into the connection's last error on every scheduled pass (plugins/syncing.py:83-85).0is falsy and silently becomes thirty.validate(), require1 <= days <=a sane maximum (3650, say) and put the problem againstdays.Done when
tests/test_imap.pyhas a test in which a configuration withsecurity=starttlsand an empty port reaches the fake client withport == 143; it fails today with 993.tests/test_imap.pyhas a test in whichImapSync().validate()namesdaysfor-30,0and10**12, andportfor0and70000; all return{}today.Fixed by pull request #25.