Skip to content

Commit e50f954

Browse files
authored
fix: handle non-static env: in job steps (#246)
1 parent eb95b08 commit e50f954

File tree

7 files changed

+103
-14
lines changed

7 files changed

+103
-14
lines changed

Cargo.lock

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ camino = { version = "1.1.9", features = ["serde1"] }
2020
clap = { version = "4.5.21", features = ["derive", "env"] }
2121
clap-verbosity-flag = "3.0.0"
2222
env_logger = "0.11.5"
23-
github-actions-models = "0.12.0"
23+
github-actions-models = "0.13.0"
2424
human-panic = "2.0.1"
2525
indexmap = "2.7.0"
2626
indicatif = "0.17.9"

src/audit/insecure_commands.rs

Lines changed: 35 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
use crate::audit::WorkflowAudit;
2-
use crate::finding::{Confidence, Finding, Severity, SymbolicLocation};
2+
use crate::finding::{Confidence, Finding, Persona, Severity, SymbolicLocation};
33
use crate::models::{Steps, Workflow};
44
use crate::state::AuditState;
5+
use anyhow::Result;
6+
use github_actions_models::common::expr::LoE;
57
use github_actions_models::common::{Env, EnvValue};
68
use github_actions_models::workflow::job::StepBody;
79
use github_actions_models::workflow::Job;
@@ -22,7 +24,7 @@ impl InsecureCommands {
2224
&self,
2325
workflow: &'w Workflow,
2426
location: SymbolicLocation<'w>,
25-
) -> Finding<'w> {
27+
) -> Result<Finding<'w>> {
2628
Self::finding()
2729
.confidence(Confidence::High)
2830
.severity(Severity::High)
@@ -32,7 +34,6 @@ impl InsecureCommands {
3234
.annotated("insecure commands enabled here"),
3335
)
3436
.build(workflow)
35-
.expect("Cannot build a Finding instance")
3637
}
3738

3839
fn has_insecure_commands_enabled(&self, env: &Env) -> bool {
@@ -43,23 +44,46 @@ impl InsecureCommands {
4344
}
4445
}
4546

46-
fn audit_steps<'w>(&self, workflow: &'w Workflow, steps: Steps<'w>) -> Vec<Finding<'w>> {
47+
fn audit_steps<'w>(
48+
&self,
49+
workflow: &'w Workflow,
50+
steps: Steps<'w>,
51+
) -> Result<Vec<Finding<'w>>> {
4752
steps
4853
.into_iter()
49-
.filter(|step| {
54+
.filter_map(|step| {
5055
let StepBody::Run {
5156
run: _,
5257
working_directory: _,
5358
shell: _,
5459
ref env,
5560
} = &step.deref().body
5661
else {
57-
return false;
62+
return None;
5863
};
5964

60-
self.has_insecure_commands_enabled(env)
65+
match env {
66+
// The entire environment block is an expression, which we
67+
// can't follow (for now). Emit an auditor-only finding.
68+
LoE::Expr(_) => Some(
69+
Self::finding()
70+
.confidence(Confidence::Low)
71+
.severity(Severity::High)
72+
.persona(Persona::Auditor)
73+
.add_location(
74+
step.location()
75+
.with_keys(&["env".into()])
76+
.annotated(
77+
"non-static environment may contain ACTIONS_ALLOW_UNSECURE_COMMANDS"
78+
)
79+
)
80+
.build(workflow)
81+
),
82+
LoE::Literal(env) => self
83+
.has_insecure_commands_enabled(env)
84+
.then(|| self.insecure_commands_allowed(workflow, step.location())),
85+
}
6186
})
62-
.map(|step| self.insecure_commands_allowed(workflow, step.location()))
6387
.collect()
6488
}
6589
}
@@ -76,16 +100,16 @@ impl WorkflowAudit for InsecureCommands {
76100
let mut results = vec![];
77101

78102
if self.has_insecure_commands_enabled(&workflow.env) {
79-
results.push(self.insecure_commands_allowed(workflow, workflow.location()))
103+
results.push(self.insecure_commands_allowed(workflow, workflow.location())?)
80104
}
81105

82106
for job in workflow.jobs() {
83107
if let Job::NormalJob(normal) = *job {
84108
if self.has_insecure_commands_enabled(&normal.env) {
85-
results.push(self.insecure_commands_allowed(workflow, job.location()))
109+
results.push(self.insecure_commands_allowed(workflow, job.location())?)
86110
}
87111

88-
results.extend(self.audit_steps(workflow, job.steps()))
112+
results.extend(self.audit_steps(workflow, job.steps())?)
89113
}
90114
}
91115

tests/snapshot.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,3 +168,17 @@ fn unpinned_uses() -> Result<()> {
168168

169169
Ok(())
170170
}
171+
172+
#[test]
173+
fn insecure_commands() -> Result<()> {
174+
insta::assert_snapshot!(zizmor()
175+
.workflow(workflow_under_test("insecure-commands.yml"))
176+
.args(["--persona=auditor"])
177+
.run()?);
178+
179+
insta::assert_snapshot!(zizmor()
180+
.workflow(workflow_under_test("insecure-commands.yml"))
181+
.run()?);
182+
183+
Ok(())
184+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
---
2+
source: tests/snapshot.rs
3+
expression: "zizmor().workflow(workflow_under_test(\"insecure-commands.yml\")).run()?"
4+
snapshot_kind: text
5+
---
6+
error[insecure-commands]: execution of insecure workflow commands is enabled
7+
--> @@INPUT@@:8:5
8+
|
9+
8 | env:
10+
| _____^
11+
9 | | ACTIONS_ALLOW_UNSECURE_COMMANDS: yes
12+
| |__________________________________________^ insecure commands enabled here
13+
|
14+
= note: audit confidenceHigh
15+
16+
2 findings (1 suppressed): 0 unknown, 0 informational, 0 low, 0 medium, 1 high
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
---
2+
source: tests/snapshot.rs
3+
expression: "zizmor().workflow(workflow_under_test(\"insecure-commands.yml\")).args([\"--persona=auditor\"]).run()?"
4+
snapshot_kind: text
5+
---
6+
error[insecure-commands]: execution of insecure workflow commands is enabled
7+
--> @@INPUT@@:8:5
8+
|
9+
8 | env:
10+
| _____^
11+
9 | | ACTIONS_ALLOW_UNSECURE_COMMANDS: yes
12+
| |__________________________________________^ insecure commands enabled here
13+
|
14+
= note: audit confidenceHigh
15+
16+
error[insecure-commands]: execution of insecure workflow commands is enabled
17+
--> @@INPUT@@:22:9
18+
|
19+
22 | env: ${{ matrix.env }}
20+
| ^^^^^^^^^^^^^^^^^^^^^^ non-static environment may contain ACTIONS_ALLOW_UNSECURE_COMMANDS
21+
|
22+
= note: audit confidenceLow
23+
24+
2 findings: 0 unknown, 0 informational, 0 low, 0 medium, 2 high

tests/test-data/insecure-commands.yml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,14 @@ jobs:
99
ACTIONS_ALLOW_UNSECURE_COMMANDS: yes
1010
steps:
1111
- run: echo "don't do this"
12+
13+
env-via-matrix:
14+
runs-on: ubuntu-latest
15+
strategy:
16+
matrix:
17+
env:
18+
- ACTIONS_ALLOW_UNSECURE_COMMANDS: yes
19+
20+
steps:
21+
- run: echo "don't do this"
22+
env: ${{ matrix.env }}

0 commit comments

Comments
 (0)