SEC2-028: The webhook notifier accepts a plain http:// receiver, so notification text leaves in clear #970
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#970
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-10-16 in a read-only security review of
postuloat56fc9d531, checked by a second, independent pass.Severity: Low · Category: Data Exposure · Reference: CWE-319 (Cleartext Transmission of Sensitive Information); OWASP A02:2021 (Cryptographic Failures)
Location:
src/postulo/plugins/webhook/__init__.py:185-192(validate:url.startswith(("https://", "http://")), and the message "An address starting with https:// or http://.")src/postulo/plugins/webhook/__init__.py:164-167(the help text says "HTTPS")src/postulo/plugins/webhook/__init__.py:101-121(payload_for: title, body, URL anddataof every event)Issue
The field's help promises "HTTPS, and a public address unless the operator has allowed private ones", and the module docstring describes the signature as what lets a receiver tell Postulo from "anyone who found the address".
validatenevertheless acceptshttp://, and its error message contradicts the help outright by asking for "https:// or http://". The HMAC authenticates the body to the receiver; it does not hide it, and the body is the notification: the role, the company, an interview's time and place, an offer's terms, a reminder's words. The reference plugins and the wiki teach the HTTPS form, and_links()inplugins/views.pyalready refuses a non-httpsaddress for a connection card, so the rule exists elsewhere.Impact
A person who types an
http://receiver (a home automation box, a webhook relay) sends their job-search events over the network in clear to anyone on the path between the instance and the receiver. Their own choice, against the field's stated rule.Fix
Refuse
http://unless the operator allowed private destinations and the address is in fact private (a LAN receiver is the one honest case;check_destinationalready resolves the address, so a plainhttp://to a public host is refused regardless):and make the help text and the error message say the same thing. Done when:
tests/test_webhooks.pyassertsvalidate({"url": "http://example.org/hook", ...})returns a problem forurlon a default instance, and still does with private destinations allowed, since the host is public.Related
#365 (open) - the delivery's deadline and size cap, a different weakness of the same plugin. #779 (closed) - the private-address switch the exception rests on. Nothing on the scheme.