SEC2-079: Server-supplied addresses (review_url, listing_url) are opened in the review window without an instance-origin check #36
Labels
No labels
bug
documentation
enhancement
performance
security
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
Postulo/postulo-chromium#36
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
postulo-chromiumatb6c1009, checked by a second, independent pass.Severity: Informational · Category: Input Validation · Reference: CWE-601 (URL Redirection to Untrusted Site); OWASP A01:2021 (Broken Access Control)
Location:
src/review.js:71-80(goToReviewand the click that opens$("review").href)src/captures.js:108-115(the Review link of a sent capture)src/lib/known.js:44-66(sayKnown:open.href = link)src/popup.js:66-86(showReviewList: scheme checked, origin not)src/lib/window.js:32-66(openInReviewWindow:tabs.update/windows.createwith the address as given)src/lib/rendering.js:21-30(the one place that does check the origin)Issue
The capture answer's
review_url, the/captures/knownanswer'slisting_urlandreview_url,and the rows of
GET /capturesare stored and later opened.renderingOffersets the standardfor a server-supplied address: "The address must be on the instance's own origin"
(
src/lib/rendering.js:11, 23-26). The other paths do less:The popup checks only that the row's link starts with
http(s)://(src/popup.js:67, 76-77);review.js,captures.jsandknown.jscheck nothing.openInReviewWindowthen navigates theremembered review window (a popup-type window the person has learnt to treat as Postulo's review
screen, the one that asks them to sign in,
README.md:82) to whatever address was given.Impact
Whoever can answer as the instance decides where the review window goes: the operator of a shared
instance, or anyone on the path of a plain
http://…localinstance. The token is not sent there(every call builds its address from
settings.instance, and the renderingPUTchecks theorigin), so the consequence is a convincing place to ask for the Postulo password inside the
extension's own flow. It needs a hostile or compromised instance, which the threat model does not
treat as an adversary; recorded as Informational.
Fix
One helper beside
renderingOffer, used by every consumer of a server-supplied address:Apply it in
goToReview(hide the button when it returns ""), incaptures.jsitem, insayKnown(say the sentence without the link), and inshowReviewListin place ofweb(); storethe checked value when the capture is marked
sent(src/lib/outbox.js:86), and check a recordkept before the fix when it is drawn.
Done when
test/review.test.mjs/test/captures.test.mjsshow that an answer whosereview_urlnames another origin leaves no link to it, and that a path-only
review_urlresolved against theinstance is kept.
Related
#7 and #8 (open) describe the review list and window and say nothing of checking addresses.