pub fn validate_index_url(
candidate: &str,
raw_for_log: &str,
ecosystem: &'static str,
gate: PolicyGate<'_>,
) -> Result<Url, IndexUrlError>Expand description
Validates a candidate registry/index URL: https scheme, no userinfo, and — when gate
is PolicyGate::Enforce — a host the live RegistryAccessPolicy allows.
candidate is the string actually parsed (e.g. deps-npm’s already ${VAR}-expanded
value); raw_for_log is what an error payload and the blocked-host tracing::warn!
name instead — the pre-expansion .npmrc value for deps-npm, or the same string as
candidate for deps-cargo/deps-pypi (neither has an expansion step). This split
keeps an environment variable’s expanded value out of any log line or error a caller
might surface in a diagnostic. ecosystem is carried on the blocked-host warning only,
to tell deps-cargo/deps-npm/deps-pypi call sites apart in the logs.
The check order — parse, then https, then userinfo, then the policy gate — is
load-bearing: userinfo is rejected before the policy gate runs, which is what lets a
caller safely log raw_for_log unredacted on a IndexUrlError::BlockedHost warning,
since a userinfo-bearing candidate can never reach that point. Do not reorder.
IndexUrlError::InvalidUrl is the one variant this invariant can’t cover — candidate
failed to parse before any userinfo check could run, so raw_for_log might still carry
one (S1 finding: an otherwise-valid user:pass@host URL can fail to parse for an unrelated
reason, e.g. an invalid port). redact_userinfo is applied to raw_for_log before it is
wrapped in IndexUrlError::InvalidUrl, so every caller — deps-cargo, deps-npm,
deps-pypi — gets this for free, whether or not it separately redacts its own raw before
logging.
§Errors
Returns IndexUrlError if candidate does not parse as a URL, is not https (outside
the cfg(test)/test-util loopback carve-out), carries a userinfo component, or (under
PolicyGate::Enforce) resolves to a host class the current policy blocks.
§Examples
use deps_core::net_policy::{PolicyGate, validate_index_url};
let url = validate_index_url(
"https://index.mycorp.dev",
"https://index.mycorp.dev",
"cargo",
PolicyGate::Skip,
)
.unwrap();
assert_eq!(url.as_str(), "https://index.mycorp.dev/");
assert!(
validate_index_url("http://example.com", "http://example.com", "cargo", PolicyGate::Skip)
.is_err()
);