Skip to content

Commit ca6ad9a

Browse files
committed
Add CI enforcement for fuzz env policies
1 parent b8208df commit ca6ad9a

7 files changed

Lines changed: 79 additions & 11 deletions

File tree

.github/workflows/ci.yml

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,21 @@ jobs:
8484
- name: Run clippy
8585
run: cargo clippy --all-targets --all-features -- -D warnings
8686

87+
fuzz-contracts:
88+
name: Fuzz Contracts
89+
runs-on: ubuntu-latest
90+
steps:
91+
- name: Checkout code
92+
uses: actions/checkout@v4
93+
94+
- name: Install Rust toolchain
95+
uses: dtolnay/rust-toolchain@0f1b44df7e9cbb178d781a242338dfa5e243ad7f
96+
with:
97+
toolchain: nightly-2026-04-16
98+
99+
- name: Run fuzz helper tests
100+
run: cargo test --manifest-path fuzz/Cargo.toml
101+
87102
coverage:
88103
name: Code Coverage
89104
runs-on: ubuntu-latest
@@ -97,7 +112,7 @@ jobs:
97112
toolchain: nightly
98113

99114
- name: Install tarpaulin
100-
run: cargo install cargo-tarpaulin
115+
run: cargo install cargo-tarpaulin --locked --version 0.34.1
101116

102117
- name: Generate coverage
103118
run: cargo tarpaulin

.github/workflows/fuzz-python-env-comparison.yml

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ permissions:
2727
env:
2828
CARGO_TERM_COLOR: always
2929
RUST_BACKTRACE: 1
30+
FUZZ_SEED: "424242"
3031

3132
jobs:
3233
compare-python-env:
@@ -88,6 +89,16 @@ jobs:
8889
fi
8990
printf 'duration=%s\n' "$duration" >> "$GITHUB_OUTPUT"
9091
92+
- name: Record comparison metadata
93+
run: |
94+
mkdir -p fuzz/artifacts/python-env-reports
95+
{
96+
echo "policy=${{ matrix.env_policy }}"
97+
echo "duration=${{ steps.duration.outputs.duration }}"
98+
echo "seed=$FUZZ_SEED"
99+
echo "sha=${{ github.sha }}"
100+
} > "fuzz/artifacts/python-env-reports/${{ matrix.name }}-metadata.txt"
101+
91102
- name: Report effective Python environment
92103
env:
93104
PICKLE_FUZZ_PYTHON_ENV_POLICY: ${{ matrix.env_policy }}
@@ -101,10 +112,12 @@ jobs:
101112
FUZZ_DURATION: ${{ steps.duration.outputs.duration }}
102113
PICKLE_FUZZ_PYTHON_ENV_POLICY: ${{ matrix.env_policy }}
103114
run: |
115+
set -o pipefail
104116
cargo fuzz run validate_with_python -- \
117+
-seed="$FUZZ_SEED" \
105118
-max_total_time="$FUZZ_DURATION" \
106119
-print_final_stats=1 \
107-
-verbosity=1
120+
-verbosity=1 | tee "fuzz/artifacts/python-env-reports/${{ matrix.name }}-fuzz.log"
108121
continue-on-error: true
109122

110123
- name: Check for crashes
@@ -140,5 +153,8 @@ jobs:
140153
uses: actions/upload-artifact@v4
141154
with:
142155
name: fuzz-python-env-report-${{ matrix.name }}
143-
path: fuzz/artifacts/python-env-reports/${{ matrix.name }}.txt
156+
path: |
157+
fuzz/artifacts/python-env-reports/${{ matrix.name }}.txt
158+
fuzz/artifacts/python-env-reports/${{ matrix.name }}-metadata.txt
159+
fuzz/artifacts/python-env-reports/${{ matrix.name }}-fuzz.log
144160
if-no-files-found: error

.github/workflows/security.yml

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,14 @@ jobs:
2323
toolchain: stable
2424

2525
- name: Install cargo-audit
26-
run: cargo install cargo-audit
26+
run: cargo install cargo-audit --locked --version 0.22.1
2727

2828
- name: Run security audit
2929
run: cargo audit
3030

31+
- name: Run fuzz crate security audit
32+
run: cd fuzz && cargo audit
33+
3134
deny:
3235
name: License and Dependency Check
3336
runs-on: ubuntu-latest
@@ -41,7 +44,7 @@ jobs:
4144
toolchain: stable
4245

4346
- name: Install cargo-deny
44-
run: cargo install cargo-deny
47+
run: cargo install cargo-deny --locked --version 0.19.4
4548

4649
- name: Check licenses
4750
run: cargo deny check licenses
@@ -65,7 +68,7 @@ jobs:
6568
toolchain: stable
6669

6770
- name: Install cargo-outdated
68-
run: cargo install cargo-outdated
71+
run: cargo install cargo-outdated --locked --version 0.19.0
6972

7073
- name: Check for outdated dependencies
7174
run: cargo outdated --exit-code 1

fuzz/examples/report_python_env.rs

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,22 @@
1414
// See the License for the specific language governing permissions and
1515
// limitations under the License.
1616

17-
use pickle_fuzzer_fuzz::python_env::{spawn_python_command, PythonEnvPolicy};
17+
use pickle_fuzzer_fuzz::python_env::{
18+
spawn_python_command, PythonEnvPolicy, REPORTED_PYTHON_ENV_KEYS,
19+
};
1820
use std::process::ExitCode;
1921

2022
fn main() -> ExitCode {
2123
let policy = PythonEnvPolicy::from_env_var();
22-
let script =
23-
"import os\nfor key, value in sorted(os.environ.items()):\n print(f\"{key}={value}\")\n";
24+
let keys = REPORTED_PYTHON_ENV_KEYS
25+
.iter()
26+
.map(|key| format!("{key:?}"))
27+
.collect::<Vec<_>>()
28+
.join(", ");
29+
30+
let script = format!(
31+
"import os\nfor key in [{keys}]:\n value = os.environ.get(key)\n print(f\"{{key}}={{value if value is not None else '<unset>'}}\")\n"
32+
);
2433

2534
let output = match spawn_python_command(policy).arg("-c").arg(script).output() {
2635
Ok(output) => output,

fuzz/src/python_env.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,19 @@ use std::ffi::OsString;
1919
use std::process::Command;
2020

2121
pub const PYTHON_ENV_POLICY_VAR: &str = "PICKLE_FUZZ_PYTHON_ENV_POLICY";
22+
pub const REPORTED_PYTHON_ENV_KEYS: &[&str] = &[
23+
"pythonLocation",
24+
"Python_ROOT_DIR",
25+
"Python2_ROOT_DIR",
26+
"Python3_ROOT_DIR",
27+
"PKG_CONFIG_PATH",
28+
"LD_LIBRARY_PATH",
29+
"PATH",
30+
"PYTHONHOME",
31+
"PYTHONPATH",
32+
"VIRTUAL_ENV",
33+
"CONDA_PREFIX",
34+
];
2235

2336
const STRIP_SETUP_PYTHON_REMOVALS: &[&str] = &[
2437
"pythonLocation",
@@ -148,6 +161,7 @@ mod tests {
148161
);
149162
}
150163

164+
#[cfg(unix)]
151165
#[test]
152166
fn resolve_non_utf8_values_fall_back_to_safe_default() {
153167
use std::os::unix::ffi::OsStringExt;

fuzz/tests/python_env_contract.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,4 +129,13 @@ fn workflow_and_docs_stay_in_sync_with_supported_policies() {
129129
"workflow path filters must include {path}"
130130
);
131131
}
132+
133+
let main_workflow =
134+
std::fs::read_to_string(repo_root().join(".github/workflows/fuzz.yml"))
135+
.expect("main fuzz workflow should exist");
136+
let policy_count = main_workflow.matches("PICKLE_FUZZ_PYTHON_ENV_POLICY: strip_setup_python").count();
137+
assert_eq!(
138+
policy_count, 2,
139+
"main fuzz workflow should set strip_setup_python for both validate_with_python entry points"
140+
);
132141
}

plans/fuzz-and-security-ci.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ on the branch.
1616
policy variants on `ubuntu-latest`.
1717
- The main fuzz workflow uses the targeted policy without broad leak
1818
suppression.
19+
- The fuzz helper contract tests run in regular PR CI.
20+
- Security CI covers both `Cargo.lock` and `fuzz/Cargo.lock`.
1921
- `cargo clippy --all-targets --all-features -- -D warnings` passes on the
2022
stable toolchain used by GitHub CI.
2123

@@ -45,8 +47,8 @@ GitHub evidence already observed on PR `#24`:
4547
## Notes
4648

4749
- The comparison workflow is diagnostic. It verifies the effective Python child
48-
environment used by the fuzz target and uploads both the env report and fuzz
49-
artifacts for each policy.
50+
environment used by the fuzz target, records policy/duration/seed metadata,
51+
and uploads both the env report and fuzz artifacts for each policy.
5052
- The current branch default remains `strip_setup_python` for scheduled and
5153
custom `validate_with_python` runs because it is the least invasive CI-only
5254
change under active observation.

0 commit comments

Comments
 (0)