blob: 8b837a36166af68138ff808948fbd0d7bb89ecf8 [file] [edit]
use tracing::debug;
use super::source::{MaybePackage, Source};
use crate::sources::IndexSummary;
use crate::util::data_structures::HashSet;
/// A `Source` that overlays one source over another, pretending that the packages
/// available in the overlay are actually available in the other one.
///
/// This is a massive footgun and a terrible idea, so we do not (and never will)
/// expose this publicly. However, it is useful for some very specific private
/// things, like locally verifying a bunch of packages at a time before any of
/// them have been published.
pub struct DependencyConfusionThreatOverlaySource<'gctx> {
// The overlay source. The naming here comes from the main application of this,
// where there is a remote registry that we overlay some local packages on.
local: Box<dyn Source + 'gctx>,
// The source we're impersonating.
remote: Box<dyn Source + 'gctx>,
}
impl<'gctx> DependencyConfusionThreatOverlaySource<'gctx> {
pub fn new(local: Box<dyn Source + 'gctx>, remote: Box<dyn Source + 'gctx>) -> Self {
debug!(
"overlaying {} on {}",
local.source_id().as_url(),
remote.source_id().as_url()
);
Self { local, remote }
}
}
#[async_trait::async_trait(?Send)]
impl<'gctx> Source for DependencyConfusionThreatOverlaySource<'gctx> {
fn source_id(&self) -> crate::workspace::SourceId {
self.remote.source_id()
}
fn supports_checksums(&self) -> bool {
self.local.supports_checksums() && self.remote.supports_checksums()
}
fn requires_precise(&self) -> bool {
self.local.requires_precise() || self.remote.requires_precise()
}
async fn query(
&self,
dep: &crate::workspace::Dependency,
kind: super::source::QueryKind,
f: &mut dyn FnMut(IndexSummary),
) -> crate::CargoResult<()> {
let local_source = self.local.source_id();
let remote_source = self.remote.source_id();
let local_dep = dep.clone().map_source(remote_source, local_source);
let mut local_packages = HashSet::default();
let mut local_callback = |index: IndexSummary| {
let index = index.map_summary(|s| s.map_source(local_source, remote_source));
local_packages.insert(index.clone());
f(index)
};
self.local
.query(&local_dep, kind, &mut local_callback)
.await?;
let mut remote_callback = |index: IndexSummary| {
if local_packages.contains(&index) {
tracing::debug!(?local_source, ?remote_source, ?index, "package collision");
} else {
f(index)
}
};
self.remote.query(dep, kind, &mut remote_callback).await?;
Ok(())
}
fn invalidate_cache(&self) {
self.local.invalidate_cache();
self.remote.invalidate_cache();
}
fn set_quiet(&mut self, quiet: bool) {
self.local.set_quiet(quiet);
self.remote.set_quiet(quiet);
}
async fn download(
&self,
package: crate::workspace::PackageId,
) -> crate::CargoResult<super::source::MaybePackage> {
let local_source = self.local.source_id();
let remote_source = self.remote.source_id();
if let Ok(pkg) = self
.local
.download(package.map_source(remote_source, local_source))
.await
.map(|maybe_pkg| match maybe_pkg {
MaybePackage::Ready(pkg) => {
MaybePackage::Ready(pkg.map_source(local_source, remote_source))
}
x => x,
})
{
return Ok(pkg);
}
self.remote.download(package).await
}
async fn finish_download(
&self,
pkg_id: crate::workspace::PackageId,
contents: Vec<u8>,
) -> crate::CargoResult<crate::workspace::Package> {
// The local registry should never return MaybePackage::Download from `download`, so any
// downloads that need to be finished come from the remote registry.
self.remote.finish_download(pkg_id, contents).await
}
fn fingerprint(&self, pkg: &crate::workspace::Package) -> crate::CargoResult<String> {
Ok(pkg.package_id().version().to_string())
}
fn describe(&self) -> String {
self.remote.describe()
}
}