Skip to content

Commit 2cf290b

Browse files
Merge pull request #723 from AlexNPavel/govulncheck-script
hack: add govulncheck-wrapper.sh and config
2 parents b3c6ccd + a88f926 commit 2cf290b

3 files changed

Lines changed: 178 additions & 0 deletions

File tree

.govulncheck-ignore.yaml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# govulncheck ignore configuration
2+
#
3+
# This file specifies vulnerabilities that should be ignored when running
4+
# govulncheck. Vulnerabilities are only ignored if no fix is available.
5+
# Once a fix is released, the wrapper script will flag them automatically.
6+
#
7+
# Each entry must specify:
8+
# - id: The vulnerability ID (GO-XXXX-XXXX format)
9+
# - module: The exact module path where the vulnerability exists
10+
# - reason: Why this vulnerability is accepted (should note "No fix available")
11+
12+
ignored_vulnerabilities:
13+
- id: GO-2023-1901
14+
module: github.com/tektoncd/pipeline
15+
reason: "No fix available - Pipelines do not validate child UIDs in Tekton Pipeline (CVE-2023-37264)"

Makefile

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,3 +54,9 @@ vendor:
5454
go mod tidy
5555
go mod vendor
5656
.PHONY: vendor
57+
58+
# Override the vulncheck target from build-machinery-go to use our wrapper
59+
# that supports ignoring vulnerabilities with no available fix
60+
vulncheck:
61+
./hack/govulncheck-wrapper.sh
62+
.PHONY: vulncheck

hack/govulncheck-wrapper.sh

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
#!/bin/bash
2+
3+
# govulncheck-wrapper.sh - Run govulncheck while ignoring specified vulnerabilities
4+
#
5+
# Usage: ./hack/govulncheck-wrapper.sh [--config FILE] [--verbose]
6+
#
7+
# Configuration file format (YAML):
8+
# ignored_vulnerabilities:
9+
# - id: GO-2024-12345
10+
# module: github.com/example/module
11+
# reason: "Acceptable risk in our context"
12+
#
13+
14+
set -euo pipefail
15+
16+
print_usage() {
17+
cat << 'EOF'
18+
Usage: govulncheck-wrapper.sh [options]
19+
20+
Options:
21+
--config FILE Path to YAML config file (default: .govulncheck-ignore.yaml)
22+
--verbose Enable verbose output
23+
-h, --help Show this help message
24+
25+
Configuration file format (YAML):
26+
ignored_vulnerabilities:
27+
- id: GO-2024-12345
28+
module: github.com/example/module
29+
reason: "Acceptable risk in our context"
30+
31+
EOF
32+
}
33+
34+
# Default values
35+
CONFIG_FILE=".govulncheck-ignore.yaml"
36+
VERBOSE=0
37+
38+
# Parse arguments
39+
while [[ $# -gt 0 ]]; do
40+
case "$1" in
41+
--config)
42+
CONFIG_FILE="$2"
43+
shift 2
44+
;;
45+
--verbose)
46+
VERBOSE=1
47+
shift
48+
;;
49+
-h|--help)
50+
print_usage
51+
exit 0
52+
;;
53+
*)
54+
echo "Unknown option: $1" >&2
55+
print_usage
56+
exit 1
57+
;;
58+
esac
59+
done
60+
61+
log_info() {
62+
echo "[INFO] $*"
63+
}
64+
65+
log_error() {
66+
echo "[ERROR] $*" >&2
67+
}
68+
69+
# Check if govulncheck is installed
70+
if ! command -v govulncheck &> /dev/null; then
71+
log_error "govulncheck not found. Install with: go install golang.org/x/vuln/cmd/govulncheck@latest"
72+
exit 1
73+
fi
74+
75+
# Check if config file exists
76+
if [[ ! -f "$CONFIG_FILE" ]]; then
77+
log_error "Config file not found: $CONFIG_FILE"
78+
exit 1
79+
fi
80+
81+
# Check if jq is installed (for parsing JSON)
82+
if ! command -v jq &> /dev/null; then
83+
log_error "jq not found. Install with your package manager (e.g., apt install jq)"
84+
exit 1
85+
fi
86+
87+
# Check if yq is installed (for parsing YAML config)
88+
if ! command -v yq &> /dev/null; then
89+
log_error "yq not found. Install with: go install github.com/mikefarah/yq/v4@latest"
90+
exit 1
91+
fi
92+
93+
[[ $VERBOSE -eq 1 ]] && log_info "Using config file: $CONFIG_FILE"
94+
95+
# Run govulncheck with JSON output
96+
[[ $VERBOSE -eq 1 ]] && log_info "Running govulncheck..."
97+
VULN_JSON=$(govulncheck -json ./... 2>&1 || true)
98+
99+
# Extract findings from the JSON stream (newline-delimited JSON objects)
100+
# Each finding has: osv (ID), fixed_version (optional), trace[0].module
101+
# Only consider vulnerabilities where our code actually calls the vulnerable function
102+
# (trace length > 1 means there's a call path from our code to the vulnerable function)
103+
FINDINGS=$(echo "$VULN_JSON" | jq -c 'select(.finding) | select(.finding.trace | length > 1) | {id: .finding.osv, module: .finding.trace[0].module, fixed: .finding.fixed_version}' 2>/dev/null || true)
104+
105+
if [[ -z "$FINDINGS" ]]; then
106+
log_info "No vulnerabilities found"
107+
exit 0
108+
fi
109+
110+
# Get unique vulnerabilities (same ID+module can appear multiple times with different traces)
111+
UNIQUE_VULNS=$(echo "$FINDINGS" | jq -c -s 'unique_by(.id + .module)' | jq -c '.[]')
112+
113+
# Parse ignored vulnerabilities from config into a format we can match
114+
IGNORED_LIST=$(yq -r '.ignored_vulnerabilities[] | "\(.id)|\(.module)"' "$CONFIG_FILE" 2>/dev/null || true)
115+
116+
[[ $VERBOSE -eq 1 ]] && log_info "Ignored vulnerabilities in config: $(echo "$IGNORED_LIST" | grep -c . || echo 0)"
117+
118+
# Check each vulnerability
119+
IGNORED_COUNT=0
120+
UNIGNORED_COUNT=0
121+
UNIGNORED_VULNS=""
122+
123+
while IFS= read -r vuln; do
124+
[[ -z "$vuln" ]] && continue
125+
126+
VULN_ID=$(echo "$vuln" | jq -r '.id')
127+
MODULE=$(echo "$vuln" | jq -r '.module')
128+
FIXED=$(echo "$vuln" | jq -r '.fixed // "N/A"')
129+
130+
# Check if this vulnerability is in the ignore list AND has no fix available
131+
# If a fix is available, we should flag it even if it's in the ignore list
132+
if echo "$IGNORED_LIST" | grep -qF "${VULN_ID}|${MODULE}"; then
133+
if [[ "$FIXED" == "N/A" || "$FIXED" == "null" ]]; then
134+
((IGNORED_COUNT++)) || true
135+
[[ $VERBOSE -eq 1 ]] && log_info "Ignored: $VULN_ID in $MODULE (no fix available)"
136+
else
137+
((UNIGNORED_COUNT++)) || true
138+
UNIGNORED_VULNS="${UNIGNORED_VULNS} - $VULN_ID in $MODULE (fix available: $FIXED)\n"
139+
[[ $VERBOSE -eq 1 ]] && log_error "Fix now available: $VULN_ID in $MODULE (fixed in: $FIXED)"
140+
fi
141+
else
142+
((UNIGNORED_COUNT++)) || true
143+
UNIGNORED_VULNS="${UNIGNORED_VULNS} - $VULN_ID in $MODULE (fixed: $FIXED)\n"
144+
[[ $VERBOSE -eq 1 ]] && log_error "Found: $VULN_ID in $MODULE (fixed: $FIXED)"
145+
fi
146+
done <<< "$UNIQUE_VULNS"
147+
148+
# Report results
149+
log_info "Found $UNIGNORED_COUNT unignored vulnerabilities, $IGNORED_COUNT ignored"
150+
151+
if [[ $UNIGNORED_COUNT -gt 0 ]]; then
152+
log_error "Unignored vulnerabilities found:"
153+
echo -e "$UNIGNORED_VULNS" >&2
154+
exit 1
155+
fi
156+
157+
exit 0

0 commit comments

Comments
 (0)