PST-073: API validation gaps: capture corrections echo the submitted value, and enumerated fields are stored unvalidated #802
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#802
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-073. Severity: Informational.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/api/api.py(456-463);src/postulo/api/schemas.py(ListingIn.remote_type / employment_type / salary_period)Who can do it, and what they need. The token holder, against their own request only.
How it works.
POST /captureswithdata: {"url": "javascript:alert(1)", ...}-> 422 whoseerrors[0].inputis the submitted string (verified). With a 40kdescriptioncorrection that fails validation, the whole text is echoed back.Impact. No cross-account data; only the caller's own input is reflected, in JSON. Inconsistent with django-ninja's builder, which deletes
inputfrom every other validation error, and a larger-than-needed response body. No attack path.Verification (verdict: confirmed; severity after verification: Informational). Line numbers: src/postulo/api/api.py:476-482 (the except block in _capture), not 456-463. Only the failing field's own value is echoed, not 'the whole 40k description'; description is truncated, not rejected. Remediation: pass include_input=False to exc.errors() for consistency with the rest of the API.
Suggested remediation. Pass
include_input=Falsetoexc.errors(...)so the corrections path matches the rest of the API.Also covered by this issue.
src/postulo/api/schemas.py(ListingIn.remote_type / employment_type / salary_period); Informational). No cross-account effect; values outside the model's choices reachget_*_display(), filters and analytics that assume the enumeration, and an invalid email reaches vCard export and mailto links. …Fixed in
5d588a865on main.