1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
//! Shared `ureq::Agent` with sane timeouts and a cached User-Agent header.
//!
//! Previous code constructed a fresh `ureq::Agent::new_with_defaults()` per
//! HTTP call (`remote::fetch_remote_config`, `update::installer::download_*`,
//! `update::release::*`, `team::*`). Each fresh agent does its own TLS
//! handshake; for `jarvy update` that means three handshakes against
//! `objects.githubusercontent.com` for archive + checksums + signature
//! companions — 100–400 ms of avoidable latency per update.
//!
//! Sharing a single agent also lets us pin a sensible global timeout. The
//! previous default had no overall budget, so a slow-loris attacker on an
//! allowlisted host (compromised raw.githubusercontent.com mirror, MitM
//! through a corp proxy) could hold `jarvy update` open indefinitely while
//! the staging file existed in `~/.jarvy/staging/` un-verified.
//!
//! Use `agent()` for any GET that should respect Jarvy's network policy.
use LazyLock;
use Duration;
/// Shared agent for all outbound HTTP. Built lazily on first use.
///
/// `max_redirects(0)` disables ureq's default 10-hop auto-follow.
/// Without this disable, `remote::validated_get` and
/// `fetch_remote_config` only check the URL host once — a 302 to a
/// non-allowlisted host is followed silently, bypassing the entire
/// allowlist. Round-2 security P1: callers that need to follow a
/// redirect must call `validated_get` again with the resolved URL,
/// re-running the policy check on the new host.
static SHARED_AGENT: = new;
/// Dedicated agent for the GitHub Releases API client (`update::release`).
///
/// Differs from `SHARED_AGENT` in exactly one knob: `max_redirects(3)`.
/// GitHub's REST API permanently redirects every endpoint of a renamed
/// repository to `api.github.com/repositories/<id>/...` (same host). The
/// shared agent's `max_redirects(0)` policy — correct for user-supplied
/// remote-config fetches — turns those 301s into a JSON parse failure,
/// which the install path swallows as "up to date" under sandbox
/// auto-disable. Effect: every binary built before a future repo rename
/// silently stops self-updating until the user runs `cargo install
/// jarvy --force` (or equivalent) out-of-band.
///
/// Limited to 3 hops so a misconfigured loop on api.github.com still
/// terminates quickly. The host allowlist policy that `SHARED_AGENT`
/// protects does not apply here: `update::release` only ever calls the
/// hardcoded `api.github.com/repos/{owner}/{repo}/...` paths.
static GITHUB_API_AGENT: = new;
/// Returns the process-wide `ureq::Agent`. Callers should attach their own
/// `User-Agent` header per request.
/// Returns the dedicated agent for GitHub Releases API calls.
///
/// Use this *only* for hardcoded `api.github.com/repos/{owner}/{repo}/...`
/// URLs in `update::release`. Following redirects there is safe and
/// future-proofs against another `bearbinary/jarvy` → `Cliftonz/jarvy`
/// style rename bricking the auto-updater for already-installed binaries.
/// Standard `User-Agent` string for jarvy outbound requests.
///
/// `pub const &str` so callers don't pay an allocation per request.
pub const USER_AGENT: &str = concat!;
/// Standard `User-Agent` string for jarvy outbound requests. Kept as a
/// function for backward compatibility with sites that take `&str`.
// Callers can prefer `USER_AGENT` const.