Enclosure downloads send the feed's basic-auth password to whatever host the feed names, unlike feed fetches #328
Reference in New Issue
Block a user
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 by the security-audit skill (run-1, 2026-10-20). Source-read only: nothing was run, so this is a lead that still needs its local check, not a confirmed finding. Two independent verifiers did not disprove it.
download::download (crates/ipx/src/download.rs:135-139) attaches the feed's configured username and password to the first request for every enclosure URL whenever feed_cfg.username is set, with no comparison of the enclosure's host to the feed's host. Enclosure URLs come from the feed XML (publisher-controlled), and podcast enclosures commonly go through third-party measurement or ad-insertion prefixes and CDNs, so those hosts receive the paid feed's credential in an Authorization header. feed::fetch (crates/feed/src/lib.rs:153-157) deliberately limits the same credential to the feed's own host, and Feed::auth() is documented as what is sent 'the feed's host', so the download path is the weaker sibling. Exposure is bounded: credentials are set only by the operator in the catalogue (web add_feed sets none), reqwest's default redirect policy on ctx.client (main.rs:358-365, no custom policy) strips Authorization on a cross-origin redirect so only the first, publisher-named host receives it, and that host is chosen by the same publisher who issued the credential or by a third party the publisher placed in the URL. Derived OPML children that inherit a parent's credential (separate candidate ipx:main:derived-feed-inherits-parent-basic-auth) reach this path too.
Claimed root cause. download.rs:136-137 calls req.basic_auth(user, feed_cfg.password()) unconditionally when feed_cfg.username is present, without the host binding feed::fetch applies at crates/feed/src/lib.rs:154-157; download() is not even given the feed URL to compare against.
Local check. {'local': 'In a sandbox, add a test in crates/ipx/src/download.rs: start a loopback listener bound to 'localhost' that records request headers and returns a small non-HTML body; call download() with a FeedCfg whose url is 'http://127.0.0.1:
/rss', username Some("u"), password Some("p") and enclosure URL 'http://localhost:
Owner check (no probing). Owner lists catalogue feeds with a username set and checks, read only, whether their enclosure URLs (enclosures table) name any host other than the feed URL's host; any such host has been receiving the feed's password.
Full detail:
/config/security-audit-skill/ipx/run-1/NEEDS-VALIDATION.md(fingerprintipx:download:enclosure-basic-auth-any-host).Fixed in
80f12cd: download::download sends basic auth only when the enclosure is on the feed's own host (config::Feed::auth_for). Deployed.