Trivy: scan images from the local containerd store before the registry
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
@ -10,11 +10,21 @@ use crate::host::CommandRunner;
|
||||
|
||||
pub struct TrivyScanner {
|
||||
runner: Arc<dyn CommandRunner>,
|
||||
containerd: Option<String>,
|
||||
}
|
||||
|
||||
impl TrivyScanner {
|
||||
pub fn new(runner: Arc<dyn CommandRunner>) -> Self {
|
||||
Self { runner }
|
||||
Self {
|
||||
runner,
|
||||
containerd: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Scan images from this containerd socket first, falling back to the registry.
|
||||
pub fn with_containerd(mut self, socket: &str) -> Self {
|
||||
self.containerd = Some(socket.to_string());
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
@ -88,7 +98,12 @@ impl TrivyScanner {
|
||||
out: &dyn LineSink,
|
||||
) -> Result<Vec<RawFinding>, DomainError> {
|
||||
out.line(&format!("$ trivy {}", args.join(" ")));
|
||||
let res = self.runner.run("trivy", args).await?;
|
||||
let env: Vec<(&str, &str)> = self
|
||||
.containerd
|
||||
.iter()
|
||||
.map(|s| ("CONTAINERD_ADDRESS", s.as_str()))
|
||||
.collect();
|
||||
let res = self.runner.run_env("trivy", args, &env).await?;
|
||||
if !res.success {
|
||||
let tail: Vec<&str> = res.stderr.lines().rev().take(5).collect();
|
||||
for l in tail.into_iter().rev() {
|
||||
@ -148,6 +163,9 @@ impl VulnerabilityScanner for TrivyScanner {
|
||||
) -> Result<Vec<RawFinding>, DomainError> {
|
||||
let mut args = vec!["image"];
|
||||
args.extend(COMMON);
|
||||
if self.containerd.is_some() {
|
||||
args.extend(["--image-src", "containerd,remote"]);
|
||||
}
|
||||
args.push(image);
|
||||
self.scan(&args, out).await
|
||||
}
|
||||
@ -351,6 +369,76 @@ mod tests {
|
||||
fn line(&self, _: &str) {}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn image_scan_prefers_local_containerd_when_configured() {
|
||||
use std::sync::Mutex;
|
||||
#[derive(Default)]
|
||||
struct Env(Mutex<Vec<(Vec<String>, Vec<(String, String)>)>>);
|
||||
#[async_trait]
|
||||
impl CommandRunner for Env {
|
||||
async fn run(&self, p: &str, a: &[&str]) -> Result<crate::host::Output, DomainError> {
|
||||
self.run_env(p, a, &[]).await
|
||||
}
|
||||
async fn run_env(
|
||||
&self,
|
||||
_: &str,
|
||||
a: &[&str],
|
||||
env: &[(&str, &str)],
|
||||
) -> Result<crate::host::Output, DomainError> {
|
||||
self.0.lock().unwrap().push((
|
||||
a.iter().map(|s| s.to_string()).collect(),
|
||||
env.iter()
|
||||
.map(|(k, v)| (k.to_string(), v.to_string()))
|
||||
.collect(),
|
||||
));
|
||||
Ok(crate::host::Output {
|
||||
stdout: "{}".into(),
|
||||
success: true,
|
||||
..Default::default()
|
||||
})
|
||||
}
|
||||
async fn run_to_file(
|
||||
&self,
|
||||
_: &str,
|
||||
_: &[&str],
|
||||
_: &std::path::Path,
|
||||
) -> Result<crate::host::Output, DomainError> {
|
||||
unreachable!()
|
||||
}
|
||||
async fn read_file(&self, _: &str) -> Result<Option<String>, DomainError> {
|
||||
Ok(None)
|
||||
}
|
||||
async fn run_streaming(
|
||||
&self,
|
||||
_: &str,
|
||||
_: &[&str],
|
||||
_: &dyn LineSink,
|
||||
) -> Result<bool, DomainError> {
|
||||
Ok(true)
|
||||
}
|
||||
}
|
||||
let r = Arc::new(Env::default());
|
||||
let s = TrivyScanner::new(r.clone())
|
||||
.with_containerd("/var/snap/microk8s/common/run/containerd.sock");
|
||||
s.scan_image("x/y:1", &Sink).await.unwrap();
|
||||
s.scan_os(&Sink).await.unwrap();
|
||||
let calls = r.0.lock().unwrap();
|
||||
let (args, env) = &calls[0];
|
||||
let i = args.iter().position(|a| a == "--image-src").unwrap();
|
||||
assert_eq!(args[i + 1], "containerd,remote");
|
||||
assert_eq!(
|
||||
env[0],
|
||||
(
|
||||
"CONTAINERD_ADDRESS".to_string(),
|
||||
"/var/snap/microk8s/common/run/containerd.sock".to_string()
|
||||
)
|
||||
);
|
||||
assert!(
|
||||
!calls[1].0.contains(&"--image-src".to_string()),
|
||||
"rootfs scan has no image source"
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn scanner_invokes_trivy_and_parses() {
|
||||
let s = TrivyScanner::new(Arc::new(Canned));
|
||||
|
||||
Reference in New Issue
Block a user