Skip to content

Commit

Permalink
TTY rendering (#18)
Browse files Browse the repository at this point in the history
  • Loading branch information
woodruffw authored Sep 21, 2024
1 parent 5a17e36 commit a54a754
Show file tree
Hide file tree
Showing 17 changed files with 239 additions and 66 deletions.
22 changes: 20 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ repository = "https://github.com/woodruffw/zizmor"
homepage = "https://github.com/woodruffw/zizmor"

[dependencies]
annotate-snippets = "0.11.4"
anstream = "0.6.15"
anyhow = "1.0.86"
clap = { version = "4.5.16", features = ["derive", "env"] }
clap-verbosity-flag = "2.2.1"
Expand All @@ -19,4 +21,4 @@ serde = { version = "1.0.208", features = ["derive"] }
serde-sarif = "0.6.5"
serde_json = "1.0.125"
serde_yaml = "0.9.34"
yamlpath = "0.5.1"
yamlpath = "0.8.0"
7 changes: 7 additions & 0 deletions src/audit/artipacked.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,13 @@ impl<'a> WorkflowAudit<'a> for Artipacked<'a> {
"artipacked"
}

fn desc() -> &'static str
where
Self: Sized,
{
"credential persistence through GitHub Actions artifacts"
}

fn new(config: AuditConfig<'a>) -> Result<Self> {
Ok(Self { config })
}
Expand Down
25 changes: 11 additions & 14 deletions src/audit/excessive_permissions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,13 @@ impl<'a> WorkflowAudit<'a> for ExcessivePermissions<'a> {
"excessive-permissions"
}

fn desc() -> &'static str
where
Self: Sized,
{
"overly broad workflow or job-level permissions"
}

fn new(config: AuditConfig<'a>) -> anyhow::Result<Self>
where
Self: Sized,
Expand Down Expand Up @@ -106,16 +113,12 @@ impl<'a> ExcessivePermissions<'a> {
BasePermission::ReadAll => vec![(
Severity::Medium,
Confidence::High,
"uses read-all permissions, which may grant read access to more resources \
than necessary"
.into(),
"uses read-all permissions".into(),
)],
BasePermission::WriteAll => vec![(
Severity::High,
Confidence::High,
"uses write-all permissions, which grants destructive access to repository \
resources"
.into(),
"uses write-all permissions".into(),
)],
},
Permissions::Explicit(perms) => match parent {
Expand All @@ -136,21 +139,15 @@ 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"),
)),
None => {
log::debug!("unknown permission: {name}");

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"),
))
}
}
Expand Down
7 changes: 7 additions & 0 deletions src/audit/hardcoded_container_credentials.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,13 @@ impl<'a> WorkflowAudit<'a> for HardcodedContainerCredentials<'a> {
"hardcoded-container-credentials"
}

fn desc() -> &'static str
where
Self: Sized,
{
"hardcoded credential in GitHub Actions container configurations"
}

fn new(config: AuditConfig<'a>) -> anyhow::Result<Self>
where
Self: Sized,
Expand Down
7 changes: 7 additions & 0 deletions src/audit/impostor_commit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,13 @@ impl<'a> WorkflowAudit<'a> for ImpostorCommit<'a> {
"impostor-commit"
}

fn desc() -> &'static str
where
Self: Sized,
{
"commit with no history in referenced repository"
}

fn new(config: AuditConfig<'a>) -> Result<Self> {
if config.offline {
return Err(anyhow!("offline audits only requested"));
Expand Down
6 changes: 5 additions & 1 deletion src/audit/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,17 @@ pub(crate) trait WorkflowAudit<'a> {
where
Self: Sized,
{
FindingBuilder::new(Self::ident())
FindingBuilder::new(Self::ident(), Self::desc())
}

fn ident() -> &'static str
where
Self: Sized;

fn desc() -> &'static str
where
Self: Sized;

fn new(config: AuditConfig<'a>) -> Result<Self>
where
Self: Sized;
Expand Down
7 changes: 7 additions & 0 deletions src/audit/pull_request_target.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,13 @@ impl<'a> WorkflowAudit<'a> for PullRequestTarget<'a> {
"pull-request-target"
}

fn desc() -> &'static str
where
Self: Sized,
{
"use of fundamentally insecure workflow trigger"
}

fn new(config: AuditConfig<'a>) -> Result<Self> {
Ok(Self { _config: config })
}
Expand Down
7 changes: 7 additions & 0 deletions src/audit/ref_confusion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,13 @@ impl<'a> WorkflowAudit<'a> for RefConfusion<'a> {
"ref-confusion"
}

fn desc() -> &'static str
where
Self: Sized,
{
"git ref for action with ambiguous ref type"
}

fn new(config: AuditConfig<'a>) -> anyhow::Result<Self>
where
Self: Sized,
Expand Down
9 changes: 8 additions & 1 deletion src/audit/template_injection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,13 @@ impl<'a> WorkflowAudit<'a> for TemplateInjection<'a> {
"template-injection"
}

fn desc() -> &'static str
where
Self: Sized,
{
"code injection via template expansion"
}

fn new(config: AuditConfig<'a>) -> anyhow::Result<Self>
where
Self: Sized,
Expand Down Expand Up @@ -90,7 +97,7 @@ impl<'a> WorkflowAudit<'a> for TemplateInjection<'a> {
.severity(severity)
.confidence(confidence)
.add_location(step.location().annotated(format!(
"template may expand into attacker-controllable code: {expr}"
"{expr} may expand into attacker-controllable code"
)))
.build(workflow)?,
)
Expand Down
7 changes: 7 additions & 0 deletions src/audit/use_trusted_publishing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,13 @@ impl<'a> WorkflowAudit<'a> for UseTrustedPublishing<'a> {
"use-trusted-publishing"
}

fn desc() -> &'static str
where
Self: Sized,
{
"perfer trusted publishing for authentication"
}

fn new(config: AuditConfig<'a>) -> anyhow::Result<Self> {
Ok(Self { _config: config })
}
Expand Down
17 changes: 11 additions & 6 deletions src/finding/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ pub(crate) mod locate;

// TODO: Traits + more flexible models here.

#[derive(Copy, Clone, Default, Serialize)]
#[derive(Copy, Clone, Debug, Default, Serialize)]
pub(crate) enum Confidence {
#[default]
Unknown,
Expand All @@ -17,7 +17,7 @@ pub(crate) enum Confidence {
High,
}

#[derive(Copy, Clone, Default, Serialize)]
#[derive(Copy, Clone, Debug, Default, Serialize)]
pub(crate) enum Severity {
#[default]
Unknown,
Expand Down Expand Up @@ -100,8 +100,8 @@ pub(crate) struct WorkflowLocation<'w> {
/// The name of the workflow.
pub(crate) name: &'w str,

/// An optional annotation for this location.
pub(crate) annotation: Option<String>,
/// An annotation for this location.
pub(crate) annotation: String,

/// The job or non-job key within this workflow.
pub(crate) job_or_key: Option<JobOrKeys<'w>>,
Expand Down Expand Up @@ -158,7 +158,7 @@ impl<'w> WorkflowLocation<'w> {

/// Adds a human-readable annotation to the current `WorkflowLocation`.
pub(crate) fn annotated(mut self, annotation: impl Into<String>) -> WorkflowLocation<'w> {
self.annotation = Some(annotation.into());
self.annotation = annotation.into();
self
}
}
Expand Down Expand Up @@ -203,6 +203,7 @@ impl From<&yamlpath::Location> for ConcreteLocation {
pub(crate) struct Feature<'w> {
/// The feature's concrete location, as both an offset range and point span.
pub(crate) location: ConcreteLocation,

/// The feature's textual content.
pub(crate) feature: &'w str,
}
Expand All @@ -226,21 +227,24 @@ pub(crate) struct Determinations {
#[derive(Serialize)]
pub(crate) struct Finding<'w> {
pub(crate) ident: &'static str,
pub(crate) desc: &'static str,
pub(crate) determinations: Determinations,
pub(crate) locations: Vec<Location<'w>>,
}

pub(crate) struct FindingBuilder<'w> {
ident: &'static str,
desc: &'static str,
severity: Severity,
confidence: Confidence,
locations: Vec<WorkflowLocation<'w>>,
}

impl<'w> FindingBuilder<'w> {
pub(crate) fn new(ident: &'static str) -> Self {
pub(crate) fn new(ident: &'static str, desc: &'static str) -> Self {
Self {
ident,
desc,
severity: Default::default(),
confidence: Default::default(),
locations: vec![],
Expand All @@ -265,6 +269,7 @@ impl<'w> FindingBuilder<'w> {
pub(crate) fn build(self, workflow: &'w Workflow) -> Result<Finding<'w>> {
Ok(Finding {
ident: self.ident,
desc: self.desc,
determinations: Determinations {
confidence: self.confidence,
severity: self.severity,
Expand Down
Loading

0 comments on commit a54a754

Please sign in to comment.