PST-057: Sync now and Send everything have no per-account rate limit, while Test is throttled #786
Labels
No labels
accessibility
authentication
breaking change
bug
documentation
enhancement
interface
internationalisation
observability
performance
security
tier
1
tier
2
tier
3
tier/4
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
Postulo/postulo#786
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 in the security audit of 2026-10-10 (
SECURITY-AUDIT-2026-10-10.md), run againstmainatc30851bfrom 8 to 10 October 2026, as PST-057. Severity: Low.The text below is the audit's entry, unchanged apart from links to the other issues it names. The finding was produced by automated review and checked by a second automated pass; re-check it before acting on it.
src/postulo/plugins/views.py(492-515)Who can do it, and what they need. Any signed-in account. Needs nothing beyond its own sync connections, which it may create without limit.
How it works.
syncconnections (each a distinct pk, each with the configured remote host; the guarded client restricts destinations to public hosts unless the operator allowed private ones).connections:sync_nowfor each in a loop. Each request runssync_connectioninline: the plugin'ssync()makes real CalDAV/CardDAV/HTTP requests to the configured server inside the web worker. The cache lease (cache.add(lease_key)) only stops two runs of the same connection overlapping; it does not bound how many connections, nor how often a connection is re-run once its lease is released at the end of each run.connections:backfilllikewise has no throttle; each press re-walks every document of the account and issuesget_or_createper document per connection.Impact. The instance can be driven to generate sustained outbound traffic against a third-party host chosen by the account holder (within the destination policy), and worker time is consumed inline.
ConnectionTestViewwas giventhrottle.consume('connection-test', ...)under #232 because 'a test is a real request to somebody's server at a press of a button'; Sync now is the same shape and heavier. Narrow path and largely self-service, and DoS on the instance itself is out of scope, hence low.Evidence.
Verification (verdict: confirmed; severity after verification: Low). Only the Sync now half is a meaningful gap, at views.py:492-515. Sync now lets an account holder bypass both the Test throttle and the 15-minute minimum interval the scheduler enforces. Backfill (views.py:454-487) makes no outbound call and is idempotent, so it should be dropped from the finding. Precondition: the administrator has installed a third-party sync plugin, because none ships in this repository. Remediation: spend a
throttle.consume('sync-now', request.user, throttle.rate_for(...))allowance inConnectionSyncNowView.post, asConnectionTestViewdoes.Suggested remediation. Apply
throttle.consumewith a setting such asPOSTULO_CONNECTION_SYNC_RATE(and a looser one for backfill) keyed per account in both views, mirroring ConnectionTestView, and reuse its 'try again in N seconds' message; add the two addresses to tests/security/test_rate_limits.py.Fixed in
8c0e600b1on main.