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}