Skip to main content

cargo/sources/
overlay.rs

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