Skip to content

fix(assets): set a timeout on every outbound request - #259

Merged
LeadcodeDev merged 1 commit into
chantier/audit-2026-09from
fix/http-timeout
Sep 22, 2026
Merged

LeadcodeDev merged 1 commit into
chantier/audit-2026-09from
fix/http-timeout

Conversation

@LeadcodeDev

Copy link
Copy Markdown
Owner

Severity Low, category security. Location: crates/rustmotion-core/src/engine/renderer/assets.rs:141

Impact

I read ureq 3.2.0's impl Default for Timeouts (config.rs:898-911): global, per_call, resolve, connect, send_request, send_body, recv_response and recv_body are all None; only await_100 is set (1 s). None of the four call sites configures one — assets.rs:141 (Iconify), google_fonts.rs:189 and google_fonts.rs:217 (Google Fonts CSS + TTF), rustmotion/src/include.rs:221 (arbitrary remote include). A host that accepts the TCP connection and then stalls, or trickles one byte per minute, blocks indefinitely; ureq's 10 MB body cap bounds bytes, not time. The icon path is the worst case: icon.rs:55 can reach fetch_icon_svg from inside paint_content, i.e. on a render worker thread, so one stalled connection wedges a render that has no user at the keyboard (CI, --frames a-b distributed segments). Combined with the remote-include finding, a scenario chooses the host that stalls.

Fix

Build one shared ureq::Agent with Config::builder().timeout_global(Some(Duration::from_secs(20))).timeout_connect(Some(Duration::from_secs(5))) (plus https_only(true)) and route all four call sites through it, instead of the bare ureq::get(...) free function which always uses the default (untimed) agent.

Evidence the audit read

let response = ureq::get(&url)
        .call()
        .map_err(|e| RustmotionError::IconFetch {
            icon: icon.to_string(),
            reason: e.to_string(),
        })?;

Based directly on the chantier branch.

Part of the September 2026 audit remediation chantier. Refs #220 (RM-41).

@LeadcodeDev LeadcodeDev added the bug Something isn't working label Sep 21, 2026
@LeadcodeDev LeadcodeDev self-assigned this Sep 21, 2026
@LeadcodeDev
LeadcodeDev force-pushed the fix/http-timeout branch 2 times, most recently from 268592c to fa7b242 Compare September 22, 2026 08:36
I read ureq 3.2.0's impl Default for Timeouts (config.rs:898-911): global,
per_call, resolve, connect, send_request, send_body, recv_response and
recv_body are all None; only await_100 is set (1 s). None of the four call
sites configures one — assets.rs:141 (Iconify), google_fonts.rs:189 and
google_fonts.rs:217 (Google Fonts CSS + TTF), rustmotion/src/include.rs:221
(arbitrary remote include). A host that accepts the TCP connection and then
stalls, or trickles one byte per minute, blocks indefinitely; ureq's 10 MB
body cap bounds bytes, not time. The icon path is the worst case: icon.rs:55
can reach fetch_icon_svg from inside paint_content, i.e. on a render worker
thread, so one stalled connection wedges a render that has no user at the
keyboard (CI, --frames a-b distributed segments). Combined with the remote-
include finding, a scenario chooses the host that stalls.

Fix: Build one shared ureq::Agent with Config::builder().timeout_global(Some
(Duration::from_secs(20))).timeout_connect(Some(Duration::from_secs(5)))
(plus https_only(true)) and route all four call sites through it, instead of
the bare ureq::get(...) free function which always uses the default
(untimed) agent.

Refs #220
@LeadcodeDev
LeadcodeDev merged commit b01fb1b into chantier/audit-2026-09 Sep 22, 2026
0 of 3 checks passed
@LeadcodeDev
LeadcodeDev deleted the fix/http-timeout branch September 22, 2026 08:54
LeadcodeDev added a commit that referenced this pull request Sep 22, 2026
I read ureq 3.2.0's impl Default for Timeouts (config.rs:898-911): global,
per_call, resolve, connect, send_request, send_body, recv_response and
recv_body are all None; only await_100 is set (1 s). None of the four call
sites configures one — assets.rs:141 (Iconify), google_fonts.rs:189 and
google_fonts.rs:217 (Google Fonts CSS + TTF), rustmotion/src/include.rs:221
(arbitrary remote include). A host that accepts the TCP connection and then
stalls, or trickles one byte per minute, blocks indefinitely; ureq's 10 MB
body cap bounds bytes, not time. The icon path is the worst case: icon.rs:55
can reach fetch_icon_svg from inside paint_content, i.e. on a render worker
thread, so one stalled connection wedges a render that has no user at the
keyboard (CI, --frames a-b distributed segments). Combined with the remote-
include finding, a scenario chooses the host that stalls.

Fix: Build one shared ureq::Agent with Config::builder().timeout_global(Some
(Duration::from_secs(20))).timeout_connect(Some(Duration::from_secs(5)))
(plus https_only(true)) and route all four call sites through it, instead of
the bare ureq::get(...) free function which always uses the default
(untimed) agent.

Refs #220
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant