-
Notifications
You must be signed in to change notification settings - Fork 3
ci: consolidate fuzz and coverity workflows #36
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
dmkarthi
merged 3 commits into
OpenVisualCloud:main
from
roshan-ku:workflowUpdatesCoverity
Jul 3, 2026
Merged
Changes from 2 commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,86 @@ | ||
| # | ||
| # BSD 3-Clause License | ||
| # Copyright (C) 2026 Intel Corporation | ||
| # SPDX-License-Identifier: BSD-3-Clause | ||
| # | ||
| name: 'AFL Fuzz' | ||
| description: 'Build and run AFL++ fuzzing for config parser harness' | ||
|
|
||
| inputs: | ||
| max-seconds: | ||
| description: 'Maximum AFL fuzzing run time in seconds' | ||
| required: false | ||
| default: '300' | ||
|
|
||
| runs: | ||
| using: composite | ||
| steps: | ||
| - name: Install AFL dependencies | ||
| shell: bash | ||
| run: | | ||
| sudo apt-get update -qq | ||
| sudo apt-get install -y -qq afl++ libavutil-dev libavformat-dev libavcodec-dev libswscale-dev pkg-config | ||
|
|
||
| - name: Verify AFL environment | ||
| shell: bash | ||
| run: | | ||
| echo "=== AFL++ ===" | ||
| afl-clang-fast --version || { echo "ERROR: afl-clang-fast not found"; exit 1; } | ||
| echo "" | ||
| echo "=== libavutil ===" | ||
| dpkg -l libavutil-dev | grep -q ii || { echo "ERROR: libavutil-dev not installed"; exit 1; } | ||
| echo " Header: $(find /usr/include -name 'avutil.h' | head -1)" | ||
| echo " Library: $(find /usr/lib -name 'libavutil.so*' | head -1)" | ||
| echo "" | ||
| echo "=== Compiler ===" | ||
| gcc --version | head -1 | ||
| echo "" | ||
| echo "Environment OK" | ||
|
|
||
| - name: Build AFL fuzz harness | ||
| shell: bash | ||
| run: | | ||
| cd fuzz | ||
| export CC=afl-clang-fast | ||
| $CC -g -O1 -fno-omit-frame-pointer -I../include -c fuzz_config_reader.c -o fuzz_config_reader.o | ||
| $CC -g -O1 -fno-omit-frame-pointer -I../include -c ../src/util/config_reader.c -o config_reader.o | ||
| $CC -g -O1 -fno-omit-frame-pointer -I../include -c ../src/util/logger.c -o logger.o | ||
| $CC -o fuzz_config_reader fuzz_config_reader.o config_reader.o logger.o -lavutil -lm | ||
| echo "Build successful: $(file fuzz_config_reader)" | ||
|
|
||
| - name: Run AFL fuzzer | ||
| shell: bash | ||
| env: | ||
| MAX_SECONDS: ${{ inputs.max-seconds }} | ||
| run: | | ||
| cd fuzz | ||
| if ! [[ "$MAX_SECONDS" =~ ^[0-9]+$ ]]; then | ||
| echo "ERROR: max-seconds must be a positive integer" | ||
| exit 1 | ||
| fi | ||
| mkdir -p findings | ||
| export AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES=1 | ||
| export AFL_SKIP_CPUFREQ=1 | ||
| timeout "$MAX_SECONDS" afl-fuzz -i corpus/ -o findings/ -V "$MAX_SECONDS" -- ./fuzz_config_reader @@ || true | ||
|
|
||
| - name: Check AFL crashes | ||
| shell: bash | ||
| run: | | ||
| cd fuzz | ||
| CRASH_COUNT=$(find findings/default/crashes -type f ! -name "README.txt" 2>/dev/null | wc -l) | ||
| echo "Crashes found: $CRASH_COUNT" | ||
| if [ "$CRASH_COUNT" -gt 0 ]; then | ||
| echo "::error::AFL found $CRASH_COUNT crash(es)!" | ||
| ls -la findings/default/crashes/ | ||
| exit 1 | ||
| fi | ||
| echo "No crashes found - fuzzing passed." | ||
|
|
||
| - name: Sanitize AFL filenames | ||
| if: always() | ||
| shell: bash | ||
| run: | | ||
| cd fuzz/findings | ||
| find . -name '*:*' | while read -r f; do | ||
| mv "$f" "$(echo "$f" | tr ':' '_')" | ||
| done |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,192 @@ | ||
| # | ||
| # BSD 3-Clause License | ||
| # Copyright (C) 2026 Intel Corporation | ||
| # SPDX-License-Identifier: BSD-3-Clause | ||
| # | ||
| name: 'Coverity Scan' | ||
| description: 'Install Coverity, run static analysis, and emit JSON + SARIF reports' | ||
|
|
||
| runs: | ||
| using: composite | ||
| steps: | ||
| - name: Detect Coverity credentials | ||
| id: coverity_secrets | ||
| shell: bash | ||
| env: | ||
| COVERITY_TOKEN: ${{ env.COVERITY_TOKEN }} | ||
| run: | | ||
| set -euo pipefail | ||
| if [ -n "${COVERITY_TOKEN:-}" ]; then | ||
| echo "available=true" >> "$GITHUB_OUTPUT" | ||
| else | ||
| echo "available=false" >> "$GITHUB_OUTPUT" | ||
| echo "Coverity secrets are not available; skipping Coverity analysis." | ||
| fi | ||
|
|
||
| - name: Install Coverity | ||
| if: steps.coverity_secrets.outputs.available == 'true' | ||
| shell: bash | ||
| env: | ||
| COVERITY_TOKEN: ${{ env.COVERITY_TOKEN }} | ||
| run: | | ||
| set -euo pipefail | ||
| echo "===== Coverity Setup =====" | ||
| COVERITY_DIR="$HOME/coverity" | ||
| COVERITY_TARBALL="/tmp/coverity.tar.gz" | ||
|
|
||
| if [ -x "$COVERITY_DIR/bin/cov-build" ]; then | ||
| echo " [OK] Coverity already installed at $COVERITY_DIR" | ||
| "$COVERITY_DIR/bin/cov-build" --ident | head -1 || true | ||
| exit 0 | ||
| fi | ||
|
|
||
| rm -f "$COVERITY_TARBALL" | ||
| mkdir -p "$COVERITY_DIR" | ||
|
|
||
| echo " Downloading Coverity from scan.coverity.com..." | ||
| wget -q --post-data "token=${COVERITY_TOKEN}&project=OpenVisualCloud%2Fdirectview-led-software-toolkit" \ | ||
| -O "$COVERITY_TARBALL" "https://scan.coverity.com/download/linux64" | ||
|
|
||
| if [ ! -s "$COVERITY_TARBALL" ]; then | ||
| echo "ERROR: Coverity download failed." | ||
| exit 1 | ||
| fi | ||
|
|
||
| tar xzf "$COVERITY_TARBALL" --strip-components=1 -C "$COVERITY_DIR" | ||
| rm -f "$COVERITY_TARBALL" | ||
|
|
||
| if [ ! -x "$COVERITY_DIR/bin/cov-build" ]; then | ||
| echo "ERROR: Coverity install incomplete (missing cov-build)." | ||
| exit 1 | ||
| fi | ||
|
|
||
| echo " [OK] Downloaded from scan.coverity.com" | ||
| "$COVERITY_DIR/bin/cov-build" --ident | head -1 || true | ||
|
|
||
| - name: Run Coverity analysis | ||
| if: steps.coverity_secrets.outputs.available == 'true' | ||
| shell: bash | ||
| env: | ||
| COVERITY_TOKEN: ${{ env.COVERITY_TOKEN }} | ||
| run: | | ||
| set -euo pipefail | ||
| export PKG_CONFIG_PATH=/usr/local/lib/pkgconfig:/usr/local/lib/x86_64-linux-gnu/pkgconfig:${PKG_CONFIG_PATH:-} | ||
| if ! pkg-config --exists mtl 2>/dev/null; then | ||
| MTL_PC=$(find /usr /home /opt -name "mtl.pc" 2>/dev/null | head -1) | ||
| if [ -z "$MTL_PC" ]; then | ||
| echo "ERROR: MTL pkg-config file not found." | ||
| exit 1 | ||
| fi | ||
| MTL_PC_DIR=$(dirname "$MTL_PC") | ||
| echo "Found MTL pkgconfig at: $MTL_PC_DIR" | ||
| export PKG_CONFIG_PATH="${MTL_PC_DIR}:${PKG_CONFIG_PATH}" | ||
| fi | ||
|
|
||
| REPORT_DIR="$GITHUB_WORKSPACE/reports" | ||
| mkdir -p "$REPORT_DIR" | ||
|
|
||
| $HOME/coverity/bin/cov-configure --compiler cc --comptype gcc --template | ||
| rm -rf build cov-int | ||
| meson setup build | ||
| $HOME/coverity/bin/cov-build --dir cov-int ninja -C build | ||
|
|
||
| echo "===== Submitting build to scan.coverity.com =====" | ||
| tail cov-int/build-log.txt | ||
| tar czf /tmp/cov-int.tar.gz cov-int/ | ||
| ls -lh /tmp/cov-int.tar.gz | ||
|
|
||
| curl --form "token=${COVERITY_TOKEN}" \ | ||
| --form "email=karthik.d.m@intel.com" \ | ||
| --form "file=@/tmp/cov-int.tar.gz" \ | ||
| --form "version=$(git rev-parse --short HEAD 2>/dev/null || echo unknown)" \ | ||
| --form "description=CI build from branch ${GITHUB_REF_NAME:-unknown}" \ | ||
| "https://scan.coverity.com/builds?project=OpenVisualCloud%2Fdirectview-led-software-toolkit" | ||
|
|
||
| rm -f /tmp/cov-int.tar.gz | ||
| echo '{"issues":[]}' > "$REPORT_DIR/coverity-report.json" | ||
| echo " Results will be available at: https://scan.coverity.com/projects/openvisualcloud-directview-led-software-toolkit" | ||
|
|
||
| - name: Convert Coverity JSON to SARIF | ||
| if: steps.coverity_secrets.outputs.available == 'true' | ||
| shell: bash | ||
| run: | | ||
| REPORT_DIR="$GITHUB_WORKSPACE/reports" | ||
| python3 - <<'EOF' | ||
| import json, sys | ||
|
|
||
| sarif = { | ||
| "$schema": "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/main/sarif-2.1/schema/sarif-schema-2.1.0.json", | ||
| "version": "2.1.0", | ||
| "runs": [{ | ||
| "tool": { | ||
| "driver": { | ||
| "name": "Coverity", | ||
| "informationUri": "https://www.synopsys.com/software-integrity/security-testing/static-analysis-sast.html", | ||
| "rules": [] | ||
| } | ||
| }, | ||
| "results": [] | ||
| }] | ||
| } | ||
|
|
||
| try: | ||
| with open("reports/coverity-report.json") as f: | ||
| cov = json.load(f) | ||
| except (FileNotFoundError, json.JSONDecodeError): | ||
| with open("reports/coverity-results.sarif", "w") as f: | ||
| json.dump(sarif, f, indent=2) | ||
| sys.exit(0) | ||
|
|
||
| rules_map = {} | ||
| results = [] | ||
|
|
||
| for issue in cov.get("issues", []): | ||
| checker = issue.get("checkerName", "unknown") | ||
| if checker not in rules_map: | ||
| rule_idx = len(rules_map) | ||
| rules_map[checker] = rule_idx | ||
| sarif["runs"][0]["tool"]["driver"]["rules"].append({ | ||
| "id": checker, | ||
| "shortDescription": {"text": issue.get("checkerProperties", {}).get("subcategoryShortDescription", checker)}, | ||
| "helpUri": f"https://community.synopsys.com/s/article/{checker}" | ||
| }) | ||
|
|
||
| events = issue.get("events", []) | ||
| main_event = events[0] if events else {} | ||
| file_path = main_event.get("strippedFilePathname", main_event.get("filePathname", "unknown")) | ||
| line = main_event.get("lineNumber", 1) | ||
|
|
||
| results.append({ | ||
| "ruleId": checker, | ||
| "ruleIndex": rules_map[checker], | ||
| "level": "warning", | ||
| "message": {"text": issue.get("checkerProperties", {}).get("subcategoryLongDescription", checker)}, | ||
| "locations": [{ | ||
| "physicalLocation": { | ||
| "artifactLocation": {"uri": file_path, "uriBaseId": "%SRCROOT%"}, | ||
| "region": {"startLine": line} | ||
| } | ||
| }] | ||
| }) | ||
|
|
||
| sarif["runs"][0]["results"] = results | ||
|
|
||
| with open("reports/coverity-results.sarif", "w") as f: | ||
| json.dump(sarif, f, indent=2) | ||
|
|
||
| print(f"Converted {len(results)} Coverity issues to SARIF") | ||
| EOF | ||
|
|
||
| - name: Write empty SARIF when Coverity is skipped | ||
| if: steps.coverity_secrets.outputs.available != 'true' | ||
| shell: bash | ||
| run: | | ||
| mkdir -p "$GITHUB_WORKSPACE/reports" | ||
| cat > "$GITHUB_WORKSPACE/reports/coverity-results.sarif" <<'EOF' | ||
| { | ||
| "$schema": "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/main/sarif-2.1/schema/sarif-schema-2.1.0.json", | ||
| "version": "2.1.0", | ||
| "runs": [] | ||
| } | ||
| EOF | ||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,78 @@ | ||
| # | ||
| # BSD 3-Clause License | ||
| # Copyright (C) 2026 Intel Corporation | ||
| # SPDX-License-Identifier: BSD-3-Clause | ||
| # | ||
| name: 'libFuzzer' | ||
| description: 'Build and run libFuzzer with ASan and UBSan for config parser harness' | ||
|
|
||
| inputs: | ||
| max-seconds: | ||
| description: 'Maximum libFuzzer run time in seconds per sanitizer mode' | ||
| required: false | ||
| default: '300' | ||
|
|
||
| runs: | ||
| using: composite | ||
| steps: | ||
| - name: Install libFuzzer dependencies | ||
| shell: bash | ||
| run: | | ||
| sudo apt-get update -qq | ||
| sudo apt-get install -y -qq clang libavutil-dev libavformat-dev libavcodec-dev libswscale-dev pkg-config | ||
|
|
||
| - name: Build and run libFuzzer (ASan) | ||
| shell: bash | ||
| env: | ||
| MAX_SECONDS: ${{ inputs.max-seconds }} | ||
| run: | | ||
| if ! [[ "$MAX_SECONDS" =~ ^[0-9]+$ ]]; then | ||
| echo "ERROR: max-seconds must be a positive integer" | ||
| exit 1 | ||
| fi | ||
| cd fuzz | ||
| export CC=clang | ||
| $CC -fsanitize=fuzzer,address -g -O1 -fno-omit-frame-pointer -I../include \ | ||
| fuzz_config_reader_libfuzzer.c \ | ||
| ../src/util/config_reader.c \ | ||
| ../src/util/logger.c \ | ||
| -o fuzz_config_reader_libfuzzer \ | ||
| $(pkg-config --cflags --libs libavutil) -lm | ||
|
|
||
| mkdir -p libfuzzer_corpus | ||
| ./fuzz_config_reader_libfuzzer \ | ||
| libfuzzer_corpus/ corpus/ \ | ||
| -max_total_time="$MAX_SECONDS" \ | ||
| -print_final_stats=1 || FUZZ_EXIT=$? | ||
| if [ "${FUZZ_EXIT:-0}" -ne 0 ]; then | ||
| echo "::error::libFuzzer found a crash (exit code $FUZZ_EXIT)!" | ||
| exit 1 | ||
| fi | ||
|
|
||
| - name: Build and run libFuzzer (UBSan) | ||
| shell: bash | ||
| env: | ||
| MAX_SECONDS: ${{ inputs.max-seconds }} | ||
| run: | | ||
| if ! [[ "$MAX_SECONDS" =~ ^[0-9]+$ ]]; then | ||
| echo "ERROR: max-seconds must be a positive integer" | ||
| exit 1 | ||
| fi | ||
| cd fuzz | ||
| export CC=clang | ||
| $CC -fsanitize=fuzzer,undefined -g -O1 -fno-omit-frame-pointer -I../include \ | ||
| fuzz_config_reader_libfuzzer.c \ | ||
| ../src/util/config_reader.c \ | ||
| ../src/util/logger.c \ | ||
| -o fuzz_config_reader_ubsan \ | ||
| $(pkg-config --cflags --libs libavutil) -lm | ||
|
|
||
| mkdir -p ubsan_corpus | ||
| ./fuzz_config_reader_ubsan \ | ||
| ubsan_corpus/ corpus/ \ | ||
| -max_total_time="$MAX_SECONDS" \ | ||
| -print_final_stats=1 || FUZZ_EXIT=$? | ||
| if [ "${FUZZ_EXIT:-0}" -ne 0 ]; then | ||
| echo "::error::libFuzzer+UBSan found an issue (exit code $FUZZ_EXIT)!" | ||
| exit 1 | ||
| fi |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.