#
cargo/sources/
overlay.rs

    1use std::task::ready;
    2
    3use tracing::debug;
    4
    5use crate::sources::IndexSummary;
    6
    7use super::source::{MaybePackage, Source};
    8
    9/// A `Source` that overlays one source over another, pretending that the packages
    10/// available in the overlay are actually available in the other one.
    11///
    12/// This is a massive footgun and a terrible idea, so we do not (and never will)
    13/// expose this publicly. However, it is useful for some very specific private
    14/// things, like locally verifying a bunch of packages at a time before any of
    15/// them have been published.
    16pub struct DependencyConfusionThreatOverlaySource<'gctx> {
    17    // The overlay source. The naming here comes from the main application of this,
    18    // where there is a remote registry that we overlay some local packages on.
    19    local: Box<dyn Source + 'gctx>,
    20    // The source we're impersonating.
    21    remote: Box<dyn Source + 'gctx>,
    22}
    23
    24impl<'gctx> DependencyConfusionThreatOverlaySource<'gctx> {
    25    pub fn new(local: Box<dyn Source + 'gctx>, remote: Box<dyn Source + 'gctx>) -> Self {
    26        debug!(
    27            "overlaying {} on {}",
    28            local.source_id().as_url(),
    29            remote.source_id().as_url()
    30        );
    31        Self { local, remote }
    32    }
    33}
    34
    35impl<'gctx> Source for DependencyConfusionThreatOverlaySource<'gctx> {
    36    fn source_id(&self) -> crate::core::SourceId {
    37        self.remote.source_id()
    38    }
    39
    40    fn supports_checksums(&self) -> bool {
    41        self.local.supports_checksums() && self.remote.supports_checksums()
    42    }
    43
    44    fn requires_precise(&self) -> bool {
    45        self.local.requires_precise() || self.remote.requires_precise()
    46    }
    47
    48    fn query(
    49        &mut self,
    50        dep: &crate::core::Dependency,
    51        kind: super::source::QueryKind,
    52        f: &mut dyn FnMut(super::IndexSummary),
    53    ) -> std::task::Poll<crate::CargoResult<()>> {
    54        let local_source = self.local.source_id();
    55        let remote_source = self.remote.source_id();
    56
    57        let local_dep = dep.clone().map_source(remote_source, local_source);
    58        let mut local_packages = std::collections::HashSet::new();
    59        let mut local_callback = |index: IndexSummary| {
    60            let index = index.map_summary(|s| s.map_source(local_source, remote_source));
    61            local_packages.insert(index.as_summary().clone());
    62            f(index)
    63        };
    64        ready!(self.local.query(&local_dep, kind, &mut local_callback))?;
    65
    66        let mut remote_callback = |index: IndexSummary| {
    67            if local_packages.contains(index.as_summary()) {
    68                tracing::debug!(?local_source, ?remote_source, ?index, "package collision");
    69            } else {
    70                f(index)
    71            }
    72        };
    73        ready!(self.remote.query(dep, kind, &mut remote_callback))?;
    74
    75        std::task::Poll::Ready(Ok(()))
    76    }
    77
    78    fn invalidate_cache(&mut self) {
    79        self.local.invalidate_cache();
    80        self.remote.invalidate_cache();
    81    }
    82
    83    fn set_quiet(&mut self, quiet: bool) {
    84        self.local.set_quiet(quiet);
    85        self.remote.set_quiet(quiet);
    86    }
    87
    88    fn download(
    89        &mut self,
    90        package: crate::core::PackageId,
    91    ) -> crate::CargoResult<super::source::MaybePackage> {
    92        let local_source = self.local.source_id();
    93        let remote_source = self.remote.source_id();
    94
    95        self.local
    96            .download(package.map_source(remote_source, local_source))
    97            .map(|maybe_pkg| match maybe_pkg {
    98                MaybePackage::Ready(pkg) => {
    99                    MaybePackage::Ready(pkg.map_source(local_source, remote_source))
    100                }
    101                x => x,
    102            })
    103            .or_else(|_| self.remote.download(package))
    104    }
    105
    106    fn finish_download(
    107        &mut self,
    108        pkg_id: crate::core::PackageId,
    109        contents: Vec<u8>,
    110    ) -> crate::CargoResult<crate::core::Package> {
    111        // The local registry should never return MaybePackage::Download from `download`, so any
    112        // downloads that need to be finished come from the remote registry.
    113        self.remote.finish_download(pkg_id, contents)
    114    }
    115
    116    fn fingerprint(&self, pkg: &crate::core::Package) -> crate::CargoResult<String> {
    117        Ok(pkg.package_id().version().to_string())
    118    }
    119
    120    fn describe(&self) -> String {
    121        self.remote.describe()
    122    }
    123
    124    fn add_to_yanked_whitelist(&mut self, pkgs: &[crate::core::PackageId]) {
    125        self.local.add_to_yanked_whitelist(pkgs);
    126        self.remote.add_to_yanked_whitelist(pkgs);
    127    }
    128
    129    fn is_yanked(
    130        &mut self,
    131        pkg: crate::core::PackageId,
    132    ) -> std::task::Poll<crate::CargoResult<bool>> {
    133        self.remote.is_yanked(pkg)
    134    }
    135
    136    fn block_until_ready(&mut self) -> crate::CargoResult<()> {
    137        self.local.block_until_ready()?;
    138        self.remote.block_until_ready()
    139    }
    140}