1+ name : Fuzzing
2+
3+ on :
4+ push :
5+ branches : ["main"]
6+ pull_request :
7+ branches : ["main"]
8+ schedule :
9+ # Run fuzzing daily at 2 AM UTC
10+ - cron : ' 0 2 * * *'
11+ workflow_dispatch :
12+
13+ permissions :
14+ contents : read
15+
16+ jobs :
17+ cross-platform-fuzzing :
18+ runs-on : ${{ matrix.os }}
19+ strategy :
20+ matrix :
21+ os : [ubuntu-latest, macos-latest]
22+ python-version : ["3.11", "3.13"]
23+
24+ steps :
25+ - name : Checkout repository
26+ uses : actions/checkout@v4
27+
28+ - name : Set up Python ${{ matrix.python-version }}
29+ uses : actions/setup-python@v5.2.0
30+ with :
31+ python-version : ${{ matrix.python-version }}
32+
33+ - name : Install uv
34+ uses : astral-sh/setup-uv@v4
35+ with :
36+ version : " latest"
37+
38+ - name : Install dependencies including fuzzing tools
39+ run : |
40+ uv sync --extra fuzzing
41+
42+ - name : Run random fuzzing
43+ run : |
44+ uv run python fuzz/fuzz_parsers.py 200
45+
46+ - name : Upload fuzzing artifacts
47+ if : always()
48+ uses : actions/upload-artifact@v4.4.0
49+ with :
50+ name : fuzzing-results-${{ matrix.os }}-py${{ matrix.python-version }}
51+ path : |
52+ crash-*
53+ *.log
54+ retention-days : 30
55+
56+ hypothesis-fuzzing :
57+ runs-on : ubuntu-latest
58+ strategy :
59+ matrix :
60+ python-version : ["3.11", "3.12", "3.13"]
61+
62+ steps :
63+ - name : Checkout repository
64+ uses : actions/checkout@v4
65+
66+ - name : Set up Python ${{ matrix.python-version }}
67+ uses : actions/setup-python@v5.2.0
68+ with :
69+ python-version : ${{ matrix.python-version }}
70+
71+ - name : Install uv
72+ uses : astral-sh/setup-uv@v4
73+ with :
74+ version : " latest"
75+
76+ - name : Install dependencies including hypothesis
77+ run : |
78+ uv sync --extra fuzzing
79+
80+ - name : Run Hypothesis property-based fuzzing
81+ run : |
82+ uv run python fuzz/fuzz_hypothesis.py
83+
84+ - name : Upload hypothesis results
85+ if : always()
86+ uses : actions/upload-artifact@v4.4.0
87+ with :
88+ name : hypothesis-results-py${{ matrix.python-version }}
89+ path : |
90+ .hypothesis/
91+ retention-days : 30
92+
93+ atheris-fuzzing :
94+ # Only run on Linux where Atheris builds reliably
95+ runs-on : ubuntu-latest
96+
97+ steps :
98+ - name : Checkout repository
99+ uses : actions/checkout@v4
100+
101+ - name : Set up Python 3.13
102+ uses : actions/setup-python@v5.2.0
103+ with :
104+ python-version : " 3.13"
105+
106+ - name : Install system dependencies for Atheris
107+ run : |
108+ sudo apt-get update
109+ sudo apt-get install -y clang
110+
111+ - name : Install uv
112+ uses : astral-sh/setup-uv@v4
113+ with :
114+ version : " latest"
115+
116+ - name : Install dependencies and try Atheris
117+ run : |
118+ uv sync --extra fuzzing
119+ # Try to install Atheris on Linux
120+ uv add --optional fuzzing atheris || echo "⚠️ Atheris installation failed, skipping"
121+
122+ - name : Run Atheris fuzzing if available
123+ run : |
124+ # Check if atheris is available
125+ if uv run python -c "import atheris" 2>/dev/null; then
126+ echo "✅ Atheris available, creating fuzzing target"
127+ cat > atheris_fuzz.py << 'EOF'
128+ import sys
129+ import atheris
130+ sys.path.insert(0, 'src')
131+ from fuzz.fuzz_parsers import fuzz_parse_requirements, generate_malformed_requirements
132+
133+ @atheris.instrument_func
134+ def TestOneInput(data):
135+ try:
136+ content = data.decode('utf-8', errors='ignore')
137+ import tempfile
138+ from pathlib import Path
139+ from superbom.utils.parsers import parse_requirements
140+ with tempfile.NamedTemporaryFile(mode='w', suffix='.txt', delete=False) as f:
141+ f.write(content)
142+ f.flush()
143+ parse_requirements(f.name)
144+ Path(f.name).unlink()
145+ except:
146+ pass
147+
148+ atheris.Setup(sys.argv, TestOneInput)
149+ atheris.Fuzz()
150+ EOF
151+ timeout 300s uv run python atheris_fuzz.py -max_total_time=300 || true
152+ else
153+ echo "⚠️ Atheris not available, skipping advanced fuzzing"
154+ fi
155+
156+ - name : Upload atheris artifacts
157+ if : always()
158+ uses : actions/upload-artifact@v4.4.0
159+ with :
160+ name : atheris-fuzzing-results
161+ path : |
162+ crash-*
163+ leak-*
164+ timeout-*
165+ retention-days : 30
166+
167+ oss-fuzz-preparation :
168+ runs-on : ubuntu-latest
169+
170+ steps :
171+ - name : Checkout repository
172+ uses : actions/checkout@v4
173+
174+ - name : Create OSS-Fuzz integration files
175+ run : |
176+ mkdir -p oss-fuzz-integration
177+
178+ # Create project.yaml for OSS-Fuzz
179+ cat > oss-fuzz-integration/project.yaml << 'EOF'
180+ homepage: "https://github.com/IntelLabs/SuperBOM"
181+ language: python
182+ primary_contact: "michael.beale@intel.com"
183+ auto_ccs:
184+ - "security@intel.com"
185+ sanitizers:
186+ - address
187+ - undefined
188+ fuzzing_engines:
189+ - libfuzzer
190+ - afl
191+ EOF
192+
193+ # Create build.sh for OSS-Fuzz
194+ cat > oss-fuzz-integration/build.sh << 'EOF'
195+ #!/bin/bash -eu
196+ # Build script for OSS-Fuzz integration
197+
198+ pip3 install .
199+
200+ # Create fuzzing targets
201+ for fuzz_target in fuzz_requirements fuzz_conda fuzz_toml; do
202+ cat > $OUT/$fuzz_target.py << INNER_EOF
203+ import sys
204+ import atheris
205+ sys.path.insert(0, '/src')
206+ from fuzz.fuzz_parsers import *
207+
208+ @atheris.instrument_func
209+ def TestOneInput(data):
210+ # Implement specific fuzzing logic here
211+ pass
212+
213+ atheris.Setup(sys.argv, TestOneInput)
214+ atheris.Fuzz()
215+ INNER_EOF
216+ chmod +x $OUT/$fuzz_target.py
217+ done
218+ EOF
219+ chmod +x oss-fuzz-integration/build.sh
220+
221+ - name : Verify OSS-Fuzz compatibility
222+ run : |
223+ echo "✅ OSS-Fuzz integration files created:"
224+ ls -la oss-fuzz-integration/
225+ echo "✅ This project is ready for OSS-Fuzz integration"
226+
227+ - name : Upload OSS-Fuzz integration
228+ uses : actions/upload-artifact@v4.4.0
229+ with :
230+ name : oss-fuzz-integration
231+ path : oss-fuzz-integration/
0 commit comments