diff --git a/src/audit/artipacked.rs b/src/audit/artipacked.rs index fd390ab..bc3c627 100644 --- a/src/audit/artipacked.rs +++ b/src/audit/artipacked.rs @@ -48,8 +48,6 @@ impl<'a> WorkflowAudit<'a> for Artipacked<'a> { } fn audit<'w>(&mut self, workflow: &'w Workflow) -> Result>> { - log::debug!("audit: {} evaluating {}", Self::ident(), &workflow.filename); - let mut findings = vec![]; for job in workflow.jobs() { @@ -143,8 +141,6 @@ impl<'a> WorkflowAudit<'a> for Artipacked<'a> { } } - log::debug!("audit: {} completed {}", Self::ident(), &workflow.filename); - Ok(findings) } } diff --git a/src/audit/excessive_permissions.rs b/src/audit/excessive_permissions.rs index 1324f57..91b834a 100644 --- a/src/audit/excessive_permissions.rs +++ b/src/audit/excessive_permissions.rs @@ -136,7 +136,10 @@ impl<'a> ExcessivePermissions<'a> { Some(sev) => results.push(( *sev, Confidence::High, - format!("{name}: write is overly broad at the workflow level; move to the job level"), + format!( + "{name}: write is overly broad at the workflow level; move to \ + the job level" + ), )), None => { log::debug!("unknown permission: {name}"); @@ -144,9 +147,12 @@ impl<'a> ExcessivePermissions<'a> { results.push(( Severity::Unknown, Confidence::High, - format!("{name}: write is overly broad at the workflow level; move to the job level") + format!( + "{name}: write is overly broad at the workflow level; \ + move to the job level" + ), )) - }, + } } } diff --git a/src/audit/impostor_commit.rs b/src/audit/impostor_commit.rs index f3437dc..e8ead30 100644 --- a/src/audit/impostor_commit.rs +++ b/src/audit/impostor_commit.rs @@ -150,8 +150,6 @@ impl<'a> WorkflowAudit<'a> for ImpostorCommit<'a> { } fn audit<'w>(&mut self, workflow: &'w Workflow) -> Result>> { - log::debug!("audit: {} evaluating {}", Self::ident(), &workflow.filename); - let mut findings = vec![]; for job in workflow.jobs() { @@ -197,8 +195,6 @@ impl<'a> WorkflowAudit<'a> for ImpostorCommit<'a> { } } - log::debug!("audit: {} completed {}", Self::ident(), &workflow.filename); - Ok(findings) } } diff --git a/src/audit/pull_request_target.rs b/src/audit/pull_request_target.rs index dd792ae..6aa433f 100644 --- a/src/audit/pull_request_target.rs +++ b/src/audit/pull_request_target.rs @@ -20,8 +20,6 @@ impl<'a> WorkflowAudit<'a> for PullRequestTarget<'a> { } fn audit<'w>(&mut self, workflow: &'w Workflow) -> Result>> { - log::debug!("audit: {} evaluating {}", Self::ident(), &workflow.filename); - let trigger = &workflow.on; let has_pull_request_target = match trigger { @@ -44,8 +42,6 @@ impl<'a> WorkflowAudit<'a> for PullRequestTarget<'a> { ); } - log::debug!("audit: {} completed {}", Self::ident(), &workflow.filename); - Ok(findings) } } diff --git a/src/audit/ref_confusion.rs b/src/audit/ref_confusion.rs index 42b2743..bd01c42 100644 --- a/src/audit/ref_confusion.rs +++ b/src/audit/ref_confusion.rs @@ -72,8 +72,6 @@ impl<'a> WorkflowAudit<'a> for RefConfusion<'a> { &mut self, workflow: &'w crate::models::Workflow, ) -> anyhow::Result>> { - log::debug!("audit: {} evaluating {}", Self::ident(), &workflow.filename); - let mut findings = vec![]; for job in workflow.jobs() { @@ -119,8 +117,6 @@ impl<'a> WorkflowAudit<'a> for RefConfusion<'a> { } } - log::debug!("audit: {} completed {}", Self::ident(), &workflow.filename); - Ok(findings) } } diff --git a/src/audit/use_trusted_publishing.rs b/src/audit/use_trusted_publishing.rs index 88a9dde..e9ce25d 100644 --- a/src/audit/use_trusted_publishing.rs +++ b/src/audit/use_trusted_publishing.rs @@ -73,8 +73,6 @@ impl<'a> WorkflowAudit<'a> for UseTrustedPublishing<'a> { &mut self, workflow: &'w crate::models::Workflow, ) -> anyhow::Result>> { - log::debug!("audit: {} evaluating {}", Self::ident(), &workflow.filename); - let mut findings = vec![]; for job in workflow.jobs() { @@ -121,7 +119,6 @@ impl<'a> WorkflowAudit<'a> for UseTrustedPublishing<'a> { } } - log::debug!("audit: {} completed {}", Self::ident(), &workflow.filename); Ok(findings) } } diff --git a/src/main.rs b/src/main.rs index 25eec39..41cdb9e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,11 +4,13 @@ use anyhow::{anyhow, Result}; use audit::WorkflowAudit; use clap::{Parser, ValueEnum}; use models::AuditConfig; +use registry::Registry; mod audit; mod finding; mod github_api; mod models; +mod registry; mod sarif; mod utils; @@ -91,20 +93,40 @@ fn main() -> Result<()> { workflows.push(models::Workflow::from_file(workflow_path)?); } + let mut registry = Registry::new(); + + macro_rules! register_audit { + ($rule:path) => {{ + // HACK: https://github.com/rust-lang/rust/issues/48067 + use $rule as base; + match base::new(config) { + Ok(audit) => registry.register_workflow_audit(base::ident(), Box::new(audit)), + Err(e) => log::warn!("{audit} is being skipped: {e}", audit = base::ident()), + } + }}; + } + + register_audit!(audit::artipacked::Artipacked); + register_audit!(audit::excessive_permissions::ExcessivePermissions); + register_audit!(audit::pull_request_target::PullRequestTarget); + register_audit!(audit::impostor_commit::ImpostorCommit); + register_audit!(audit::ref_confusion::RefConfusion); + register_audit!(audit::use_trusted_publishing::UseTrustedPublishing); + register_audit!(audit::template_injection::TemplateInjection); + register_audit!(audit::hardcoded_container_credentials::HardcodedContainerCredentials); + let mut results = vec![]; - let audits: &mut [&mut dyn WorkflowAudit] = &mut [ - &mut audit::artipacked::Artipacked::new(config)?, - &mut audit::excessive_permissions::ExcessivePermissions::new(config)?, - &mut audit::pull_request_target::PullRequestTarget::new(config)?, - &mut audit::impostor_commit::ImpostorCommit::new(config)?, - &mut audit::ref_confusion::RefConfusion::new(config)?, - &mut audit::use_trusted_publishing::UseTrustedPublishing::new(config)?, - &mut audit::template_injection::TemplateInjection::new(config)?, - &mut audit::hardcoded_container_credentials::HardcodedContainerCredentials::new(config)?, - ]; for workflow in workflows.iter() { - for audit in audits.iter_mut() { + for (name, audit) in registry.iter_workflow_audits() { + log::info!( + "performing {name} on {workflow}", + workflow = &workflow.filename + ); results.extend(audit.audit(workflow)?); + log::info!( + "completed {name} on {workflow}", + workflow = &workflow.filename + ); } } diff --git a/src/registry.rs b/src/registry.rs new file mode 100644 index 0000000..fce3392 --- /dev/null +++ b/src/registry.rs @@ -0,0 +1,33 @@ +//! Functionality for registering and managing the lifecycles of +//! audits. + +use std::collections::HashMap; + +use crate::audit::WorkflowAudit; + +pub(crate) struct Registry<'config> { + pub(crate) workflow_audits: HashMap<&'static str, Box + 'config>>, +} + +impl<'config> Registry<'config> { + pub(crate) fn new() -> Self { + Self { + workflow_audits: Default::default(), + } + } + + pub(crate) fn register_workflow_audit( + &mut self, + ident: &'static str, + audit: Box + 'config>, + ) { + self.workflow_audits.insert(ident, audit); + } + + pub(crate) fn iter_workflow_audits( + &mut self, + ) -> std::collections::hash_map::IterMut<'_, &str, Box + 'config>> + { + self.workflow_audits.iter_mut() + } +}