Skip to content

Commit 606485e

Browse files
Update fuzzing workflow to use ubuntu-latest
1 parent c3c5573 commit 606485e

1 file changed

Lines changed: 33 additions & 167 deletions

File tree

.github/workflows/fuzzing-smoke.yml

Lines changed: 33 additions & 167 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ concurrency:
2828
jobs:
2929
fuzzing-smoke:
3030
name: AFL++ fuzzing smoke test
31-
runs-on: ubuntu-24.04
31+
runs-on: ubuntu-latest
3232
timeout-minutes: 60
3333

3434
env:
@@ -68,213 +68,79 @@ jobs:
6868
| awk '{print $2}'
6969
)"
7070
71-
if [ -z "$BEST_PKG" ]; then
72-
echo "Failed to determine Lua dev package"
73-
printf '%s\n' "$CANDIDATES"
74-
exit 1
75-
fi
76-
7771
BEST_VER="$(printf '%s\n' "$BEST_PKG" | sed -E 's/^liblua([0-9]+\.[0-9]+)-dev$/\1/')"
7872
LUA_PKG="lua$BEST_VER"
7973
8074
echo "lua_dev_pkg=$BEST_PKG" >> "$GITHUB_OUTPUT"
8175
echo "lua_pkg=$LUA_PKG" >> "$GITHUB_OUTPUT"
8276
83-
echo "Using Lua dev package: $BEST_PKG"
84-
echo "Using Lua interpreter: $LUA_PKG"
85-
8677
- name: Install dependencies
87-
shell: bash
8878
run: |
89-
set -euo pipefail
90-
9179
sudo apt-get install -y \
92-
autoconf \
93-
automake \
94-
build-essential \
95-
afl++ \
96-
clang \
97-
libtool \
98-
pkg-config \
99-
libyajl-dev \
100-
libcurl4-openssl-dev \
101-
liblmdb-dev \
80+
autoconf automake build-essential afl++ clang libtool pkg-config \
81+
libyajl-dev libcurl4-openssl-dev liblmdb-dev \
10282
${{ steps.detect_lua.outputs.lua_dev_pkg }} \
10383
${{ steps.detect_lua.outputs.lua_pkg }} \
104-
libmaxminddb-dev \
105-
libpcre2-dev \
106-
libxml2-dev \
107-
libfuzzy-dev \
108-
pcre2-utils \
109-
libpcre3-dev \
110-
bison \
111-
flex \
112-
python3 \
113-
python3-venv
84+
libmaxminddb-dev libpcre2-dev libxml2-dev libfuzzy-dev \
85+
pcre2-utils libpcre3-dev bison flex python3 python3-venv
11486
115-
- name: Show Lua installation
116-
shell: bash
117-
run: |
118-
which lua || true
119-
lua -v || true
120-
dpkg -l | grep lua || true
121-
122-
- name: Build ModSecurity with AFL++ instrumentation
123-
shell: bash
87+
- name: Build ModSecurity with AFL++
12488
env:
12589
CC: afl-clang-fast
12690
CXX: afl-clang-fast++
12791
run: |
128-
set -euo pipefail
12992
./build.sh
130-
./configure \
131-
--enable-afl-fuzz \
132-
--enable-parser-generation \
133-
--enable-assertions=yes
93+
./configure --enable-afl-fuzz --enable-parser-generation --enable-assertions=yes
13494
make -j"$(nproc)"
13595
136-
- name: Locate AFL fuzzer target
96+
- name: Locate AFL target
13797
id: target
138-
shell: bash
13998
run: |
140-
set -euo pipefail
141-
142-
CANDIDATES=(
143-
"./test/fuzzer/afl_fuzzer"
144-
"./test/fuzzer/.libs/afl_fuzzer"
145-
)
146-
147-
TARGET=""
148-
for candidate in "${CANDIDATES[@]}"; do
149-
if [ -x "$candidate" ]; then
150-
TARGET="$candidate"
151-
break
152-
fi
99+
for f in ./test/fuzzer/afl_fuzzer ./test/fuzzer/.libs/afl_fuzzer; do
100+
[ -x "$f" ] && echo "target=$f" >> $GITHUB_OUTPUT && exit 0
153101
done
154-
155-
if [ -z "$TARGET" ]; then
156-
echo "Could not find test/fuzzer/afl_fuzzer"
157-
find . -path "*afl_fuzzer*" -maxdepth 6 -print || true
158-
exit 1
159-
fi
160-
161-
echo "target=$TARGET" >> "$GITHUB_OUTPUT"
162-
echo "Using AFL target: $TARGET"
102+
echo "Fuzzer not found" && exit 1
163103
164104
- name: Create seed corpus
165-
shell: bash
166105
run: |
167-
set -euo pipefail
168106
rm -rf fuzz-in fuzz-out
169107
mkdir -p fuzz-in fuzz-out
170-
171-
# Keep seeds small because the existing ModSecurity AFL harness reads 128 bytes.
172108
printf '' > fuzz-in/empty
173109
printf 'abc' > fuzz-in/plain
174-
printf '../../etc/passwd' > fuzz-in/path-traversal
175-
printf '%s' '<script>alert(1)</script>' > fuzz-in/xss
176-
printf "%s" "' OR '1'='1" > fuzz-in/sqli
177-
printf '%s' 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' > fuzz-in/max-128-a
178-
printf '%s' '%u0101%u4141%2f%2e%2e%2f' > fuzz-in/url-encoded
179-
printf '%s' '\x00\xff\xfe\xfd{{{{[[[[' > fuzz-in/binary-ish
180-
printf '%s' 'SecRule ARGS "@rx ^(a+)+$" "id:1,phase:2,deny"' > fuzz-in/rule-ish
181-
printf '%s' '../../../../../../../../../../tmp/a' > fuzz-in/deep-path
182-
printf '%s' '() { :;}; echo vulnerable' > fuzz-in/shellshock-ish
183110
184-
ls -la fuzz-in
185-
186-
- name: Dry-run fuzzer target
187-
shell: bash
188-
run: |
189-
set -euo pipefail
190-
timeout 10s "${{ steps.target.outputs.target }}" < fuzz-in/plain
111+
- name: Dry-run
112+
run: timeout 10s "${{ steps.target.outputs.target }}" < fuzz-in/plain
191113

192-
- name: Run AFL++ smoke fuzzing
193-
shell: bash
114+
- name: Run AFL++
194115
run: |
195-
set -euo pipefail
196-
197-
RUN_MINUTES="${{ github.event.inputs.run_minutes }}"
198-
if [ -z "$RUN_MINUTES" ]; then
199-
RUN_MINUTES="10"
200-
fi
201-
202-
if ! [[ "$RUN_MINUTES" =~ ^[0-9]+$ ]]; then
203-
echo "run_minutes must be a positive integer"
204-
exit 1
205-
fi
206-
207-
if [ "$RUN_MINUTES" -lt 1 ]; then
208-
echo "run_minutes must be >= 1"
209-
exit 1
210-
fi
211-
212-
echo "Running AFL++ for ${RUN_MINUTES} minute(s)"
213-
214-
# timeout returns 124 when it stops AFL after the requested smoke window.
215-
# That is expected and should not fail the workflow.
216-
set +e
217-
timeout "${RUN_MINUTES}m" \
218-
afl-fuzz \
219-
-i fuzz-in \
220-
-o fuzz-out \
221-
-m none \
222-
-t 1000+ \
223-
-- "${{ steps.target.outputs.target }}"
224-
AFL_EXIT=$?
225-
set -e
116+
timeout "${{ github.event.inputs.run_minutes || '10' }}m" \
117+
afl-fuzz -i fuzz-in -o fuzz-out -m none -t 1000+ \
118+
-- "${{ steps.target.outputs.target }}" || true
226119
227-
if [ "$AFL_EXIT" -ne 0 ] && [ "$AFL_EXIT" -ne 124 ]; then
228-
echo "afl-fuzz exited with unexpected code: $AFL_EXIT"
229-
exit "$AFL_EXIT"
230-
fi
231-
232-
- name: Summarize AFL++ results
120+
- name: Summarize
233121
id: summary
234-
shell: bash
235122
run: |
236-
set -euo pipefail
237-
238-
echo "AFL++ output tree:"
239-
find fuzz-out -maxdepth 4 -type f | sort || true
240-
241-
CRASH_COUNT="$(find fuzz-out -path '*/crashes/id:*' -type f | wc -l | tr -d ' ')"
242-
HANG_COUNT="$(find fuzz-out -path '*/hangs/id:*' -type f | wc -l | tr -d ' ')"
243-
244-
echo "crash_count=$CRASH_COUNT" >> "$GITHUB_OUTPUT"
245-
echo "hang_count=$HANG_COUNT" >> "$GITHUB_OUTPUT"
123+
CRASH=$(find fuzz-out -path '*/crashes/id:*' -type f | wc -l)
124+
HANG=$(find fuzz-out -path '*/hangs/id:*' -type f | wc -l)
125+
echo "crash_count=$CRASH" >> $GITHUB_OUTPUT
126+
echo "hang_count=$HANG" >> $GITHUB_OUTPUT
246127
247-
{
248-
echo "## AFL++ fuzzing smoke result"
249-
echo
250-
echo "- Crashes: \`$CRASH_COUNT\`"
251-
echo "- Hangs: \`$HANG_COUNT\`"
252-
echo "- Target: \`${{ steps.target.outputs.target }}\`"
253-
echo "- Lua dev package: \`${{ steps.detect_lua.outputs.lua_dev_pkg }}\`"
254-
echo "- Lua interpreter: \`${{ steps.detect_lua.outputs.lua_pkg }}\`"
255-
} >> "$GITHUB_STEP_SUMMARY"
128+
- name: Package results
129+
if: always()
130+
run: |
131+
tar -czf afl-fuzz-results.tar.gz fuzz-in fuzz-out
256132
257-
- name: Upload AFL++ corpus and findings
133+
- name: Upload results
258134
if: always()
259-
uses: actions/upload-artifact@v4
135+
uses: actions/upload-artifact@v7
260136
with:
261137
name: afl-fuzz-results-${{ github.run_id }}
262-
path: |
263-
fuzz-in
264-
fuzz-out
265-
if-no-files-found: warn
266-
retention-days: 14
138+
path: afl-fuzz-results.tar.gz
267139

268-
- name: Fail on AFL++ crashes
140+
- name: Fail on crashes
269141
if: steps.summary.outputs.crash_count != '0'
270-
shell: bash
271-
run: |
272-
echo "AFL++ found ${{ steps.summary.outputs.crash_count }} crash(es). Download the afl-fuzz-results artifact."
273-
exit 1
142+
run: exit 1
274143

275-
- name: Fail on AFL++ hangs when enabled
144+
- name: Fail on hangs
276145
if: github.event.inputs.fail_on_hangs == 'true' && steps.summary.outputs.hang_count != '0'
277-
shell: bash
278-
run: |
279-
echo "AFL++ found ${{ steps.summary.outputs.hang_count }} hang(s). Download the afl-fuzz-results artifact."
280-
exit 1
146+
run: exit 1

0 commit comments

Comments
 (0)