1use tracing::debug;
2
3use crate::sources::IndexSummary;
4
5use super::source::{MaybePackage, Source};
6
7pub struct DependencyConfusionThreatOverlaySource<'gctx> {
15 local: Box<dyn Source + 'gctx>,
18 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 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}