Skip to content

Indexer API key leaks into tracing logs via error-message URLs #433

Description

@forkwright

Finding

build_search_url and build_caps_url append the indexer API key as a plain query parameter (&apikey=…) onto the URL string. That URL is then embedded verbatim into the SearchIndexerError::HttpRequest { url } and SearchIndexerError::ParseResponse { url, … } variants, both of which surface the URL through their Display impl. The caller logs these errors at warn! with error = %e, invoking Display. Every network or parse failure therefore writes the full URL — API key included — to the logs in plaintext.

Evidence

crates/zetesis/src/client/mod.rs:135 embeds the key into the URL string:

url.push_str(&format!("&apikey={key}"));

crates/zetesis/src/client/torznab.rs:52 constructs the error from that same URL:

result.context(error::HttpRequestSnafu { url })?

crates/zetesis/src/search.rs:102 logs the error via Display, which includes the URL and key:

warn!(
    indexer_id = indexer.id,
    error = %e,
    "search failed for indexer"
);

Why this matters

Anyone with read access to application logs — operators, monitoring agents, log aggregators, a SIEM — obtains a working API key for every indexer that hits a network or parse error. On a self-hosted LAN deployment these logs are frequently world-readable on the server, and the keys persist long after the transient failure that exposed them. A single network hiccup permanently discloses a credential.

Desired correction

Strip the apikey parameter from URLs before they enter any error struct. Add a redact_api_key(url: &str) -> String helper that rewrites apikey=<value> to apikey=[REDACTED], and apply it at the error-construction sites; alternatively, keep the API key out of the URL field stored in the error entirely. Done when: a test confirms that the Display output of an HttpRequest or ParseResponse error does not contain the literal API key value.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions