add a pull_request_target audit

Signed-off-by: William Woodruff <william@yossarian.net>
This commit is contained in:
William Woodruff 2024-08-19 15:37:49 -04:00
parent 8dc450b39b
commit 44e6dee80b
No known key found for this signature in database
5 changed files with 40 additions and 13 deletions

View file

@ -7,7 +7,7 @@ use itertools::Itertools;
use crate::finding::{Confidence, Finding, JobIdentity, Severity, StepIdentity};
use crate::models::Workflow;
pub(crate) fn artipacked(workflow: &Workflow) -> Vec<Finding> {
pub(crate) fn audit(workflow: &Workflow) -> Vec<Finding> {
let mut findings = vec![];
for (jobname, job) in workflow.jobs.iter() {
@ -50,7 +50,7 @@ pub(crate) fn artipacked(workflow: &Workflow) -> Vec<Finding> {
}
if vulnerable_uploads.is_empty() {
// If we have no vulnerable uploads, then emit lower-severity
// If we have no vulnerable uploads, then emit lower-confidence
// findings for just the checkout steps.
for checkout in vulnerable_checkouts {
findings.push(Finding {
@ -58,7 +58,7 @@ pub(crate) fn artipacked(workflow: &Workflow) -> Vec<Finding> {
workflow: workflow.filename.clone(),
severity: Severity::Medium,
confidence: Confidence::Low,
job: JobIdentity::new(&jobname, job),
job: Some(JobIdentity::new(&jobname, job)),
steps: vec![checkout],
})
}
@ -76,7 +76,7 @@ pub(crate) fn artipacked(workflow: &Workflow) -> Vec<Finding> {
workflow: workflow.filename.clone(),
severity: Severity::High,
confidence: Confidence::High,
job: JobIdentity::new(&jobname, job),
job: Some(JobIdentity::new(&jobname, job)),
steps: vec![checkout, upload],
})
}

View file

@ -1,3 +1,2 @@
mod artipacked;
pub(crate) use artipacked::artipacked;
pub(crate) mod artipacked;
pub(crate) mod pull_request_target;

View file

@ -0,0 +1,29 @@
use github_actions_models::workflow::event::{BareEvent, OptionalBody};
use github_actions_models::workflow::Trigger;
use crate::finding::{Confidence, Finding, Severity};
use crate::models::Workflow;
pub(crate) fn audit(workflow: &Workflow) -> Vec<Finding> {
let trigger = &workflow.on;
let has_pull_request_target = match trigger {
Trigger::BareEvent(event) => *event == BareEvent::PullRequestTarget,
Trigger::BareEvents(events) => events.contains(&BareEvent::PullRequestTarget),
Trigger::Events(events) => !matches!(events.pull_request_target, OptionalBody::Missing),
};
let mut findings = vec![];
if has_pull_request_target {
findings.push(Finding {
ident: "pull-request-target",
workflow: workflow.filename.clone(),
severity: Severity::High,
confidence: Confidence::Medium,
job: None,
steps: vec![],
})
}
findings
}

View file

@ -56,6 +56,6 @@ pub(crate) struct Finding {
pub(crate) workflow: String,
pub(crate) severity: Severity,
pub(crate) confidence: Confidence,
pub(crate) job: JobIdentity,
pub(crate) job: Option<JobIdentity>,
pub(crate) steps: Vec<StepIdentity>,
}

View file

@ -1,7 +1,4 @@
use std::{
io::stdout,
path::{Path, PathBuf},
};
use std::{io::stdout, path::PathBuf};
use anyhow::{anyhow, Result};
use clap::Parser;
@ -53,7 +50,9 @@ fn main() -> Result<()> {
let mut findings = vec![];
for workflow_path in workflow_paths.iter() {
let workflow = models::Workflow::from_file(workflow_path)?;
findings.extend(audit::artipacked(&workflow));
// TODO: Proper abstraction for multiple audits here.
findings.extend(audit::artipacked::audit(&workflow));
findings.extend(audit::pull_request_target::audit(&workflow));
}
if !findings.is_empty() {