Skip to content

Commit b500b22

Browse files
Semgrep and Bandit config refactor (#2646)
* semgrep bandit fixes Signed-off-by: Barabanov <[email protected]> * semgrep bandit logic fix Signed-off-by: Barabanov <[email protected]> * fix bandit flow Signed-off-by: Barabanov <[email protected]> * fix semgrep flow Signed-off-by: Barabanov <[email protected]> * fix semgrep cli Signed-off-by: Barabanov <[email protected]> * fix semgrep cli Signed-off-by: Barabanov <[email protected]> * rename env vars Signed-off-by: Barabanov <[email protected]> --------- Signed-off-by: Barabanov <[email protected]> Co-authored-by: Samet Akcay <[email protected]>
1 parent 0354cd5 commit b500b22

File tree

6 files changed

+108
-66
lines changed

6 files changed

+108
-66
lines changed

.github/actions/security/bandit/action.yaml

+47-28
Original file line numberDiff line numberDiff line change
@@ -55,22 +55,22 @@ inputs:
5555
paths:
5656
description: "Paths to scan when using all scope"
5757
required: false
58-
default: "./src"
58+
default: "." # all scope by default, exclude_dirs are taken from pyproject.toml
5959
config_file:
6060
description: "Path to pyproject.toml or custom bandit config"
6161
required: false
6262
default: "pyproject.toml"
63-
severity_level:
63+
severity-level:
6464
description: "Minimum severity level to report (all/LOW/MEDIUM/HIGH)"
6565
default: "LOW"
66-
confidence_level:
66+
confidence-level:
6767
description: "Minimum confidence level to report (all/LOW/MEDIUM/HIGH)"
6868
required: false
6969
default: "LOW"
7070
output-format:
71-
description: "Format for scan results (json/txt/html/csv)"
71+
description: "Format for scan results (json/txt/html/csv/sarif)"
7272
required: false
73-
default: "json"
73+
default: "sarif" # by default to upload into Security tab
7474
fail-on-findings:
7575
description: "Whether to fail the action if issues are found"
7676
required: false
@@ -88,20 +88,20 @@ runs:
8888
using: composite
8989
steps:
9090
- name: Set up Python
91-
uses: actions/setup-python@v4
91+
uses: actions/setup-python@8d9ed9ac5c53483de85588cdf95a591a75ab9f55 # v5.5.0
9292
with:
9393
python-version: "3.10"
9494

9595
- name: Install Bandit
9696
shell: bash
9797
run: |
9898
python -m pip install --upgrade pip
99-
pip install bandit[toml]
99+
pip install bandit[toml,sarif]
100100
101101
- name: Get changed files
102102
if: inputs.scan-scope == 'changed'
103103
id: changed-files
104-
uses: tj-actions/changed-files@v41
104+
uses: tj-actions/changed-files@823fcebdb31bb35fdf2229d9f769b400309430d0 # v46.0.3
105105
with:
106106
files: |
107107
**/*.py
@@ -111,31 +111,50 @@ runs:
111111
- name: Run Bandit scan
112112
id: run-bandit
113113
shell: bash
114+
env:
115+
INPUTS_SCAN_SCOPE: ${{ inputs.scan-scope }}
116+
INPUTS_PATHS: ${{ inputs.paths }}
117+
INPUTS_CONFIG_FILE: ${{ inputs.config_file }}
118+
INPUTS_SEVERITY_LEVEL: ${{ inputs.severity-level }}
119+
INPUTS_CONFIDENCE_LEVEL: ${{ inputs.confidence-level }}
120+
INPUTS_OUTPUT_FORMAT: ${{ inputs.output-format }}
121+
INPUTS_FAIL_ON_FINDINGS: ${{ inputs.fail-on-findings }}
114122
run: |
115-
REPORT_FILE="bandit-report.${{ inputs.output-format }}"
116-
117-
if [[ "${{ inputs.scan-scope }}" == "changed" && -n "${{ steps.changed-files.outputs.all_changed_files }}" ]]; then
118-
echo "Running Bandit on changed files"
119-
FILES="${{ steps.changed-files.outputs.all_changed_files }}"
120-
else
121-
echo "Running Bandit on all files in ${{ inputs.paths }}"
122-
FILES="${{ inputs.paths }}"
123-
fi
123+
set +e
124+
REPORT_FILE="bandit-report.$INPUTS_OUTPUT_FORMAT"
124125
125126
# Convert severity and confidence to lowercase
126-
SEVERITY=$(echo "${{ inputs.severity_level }}" | tr '[:upper:]' '[:lower:]')
127-
CONFIDENCE=$(echo "${{ inputs.confidence_level }}" | tr '[:upper:]' '[:lower:]')
127+
SEVERITY=$(echo "$INPUTS_SEVERITY_LEVEL" | tr '[:upper:]' '[:lower:]')
128+
CONFIDENCE=$(echo "$INPUTS_CONFIDENCE_LEVEL" | tr '[:upper:]' '[:lower:]')
128129
129-
bandit \
130-
-c ${{ inputs.config_file }} \
131-
--severity-level ${SEVERITY} \
132-
--confidence-level ${CONFIDENCE} \
133-
-f ${{ inputs.output-format }} \
134-
-o "${REPORT_FILE}" \
135-
-r ${FILES} || echo "exit_code=$?" >> $GITHUB_OUTPUT
130+
if [[ "$INPUTS_SCAN_SCOPE" == "changed" && -n "${{ steps.changed-files.outputs.all_changed_files }}" ]]; then
131+
echo "Running Bandit on changed files, output results into workflow log only"
132+
FILES="${{ steps.changed-files.outputs.all_changed_files }}"
133+
bandit \
134+
-a file \
135+
-c "$INPUTS_CONFIG_FILE" \
136+
--severity-level ${SEVERITY} \
137+
--confidence-level ${CONFIDENCE} \
138+
-r ${FILES}
139+
exit_code="$?"
140+
echo "exit_code=$exit_code" >> $GITHUB_OUTPUT
136141
137-
echo "report_path=${REPORT_FILE}" >> $GITHUB_OUTPUT
142+
elif [[ "$INPUTS_SCAN_SCOPE" == "all" ]] ; then
143+
echo "Running Bandit on all files in $INPUTS_PATHS"
144+
bandit \
145+
-c "$INPUTS_CONFIG_FILE" \
146+
--severity-level ${SEVERITY} \
147+
--confidence-level ${CONFIDENCE} \
148+
-f "$INPUTS_OUTPUT_FORMAT" \
149+
-o "${REPORT_FILE}" \
150+
-r "$INPUTS_PATHS"
151+
exit_code="$?"
152+
echo "exit_code=$exit_code" >> $GITHUB_OUTPUT
153+
echo "report_path=${REPORT_FILE}" >> $GITHUB_OUTPUT
154+
else
155+
echo "No files to scan found"
156+
fi
138157
139-
if [[ "${{ inputs.fail-on-findings }}" == "true" && -n "$exit_code" && "$exit_code" != "0" ]]; then
158+
if [[ "$INPUTS_FAIL_ON_FINDINGS" == "true" && -n "$exit_code" && "$exit_code" != "0" ]]; then
140159
exit $exit_code
141160
fi

.github/actions/security/semgrep/action.yaml

+44-32
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ inputs:
5959
config:
6060
description: "Semgrep rules or config to use"
6161
required: false
62-
default: "p/default"
62+
default: "p/default p/cwe-top-25 p/trailofbits p/owasp-top-ten"
6363
severity:
6464
description: "Minimum severity level to report (ERROR/WARNING/INFO)"
6565
required: false
@@ -88,68 +88,80 @@ outputs:
8888
runs:
8989
using: composite
9090
steps:
91-
- name: Set up Python
92-
uses: actions/setup-python@v4
93-
with:
94-
python-version: "3.10"
95-
96-
- name: Install Semgrep
97-
shell: bash
98-
run: |
99-
python -m pip install --upgrade pip
100-
pip install semgrep
101-
10291
- name: Get changed files
10392
if: inputs.scan-scope == 'changed'
10493
id: changed-files
105-
uses: tj-actions/changed-files@v41
94+
uses: tj-actions/changed-files@823fcebdb31bb35fdf2229d9f769b400309430d0 # v46.0.3
10695
with:
10796
files: |
10897
**/*.*
10998
11099
- name: Run Semgrep scan
111100
id: run-semgrep
112101
shell: bash
102+
# Set the SEMGREP_RULES environment variable to specify which rules Semgrep should use.
103+
env:
104+
SEMGREP_RULES: ${{ inputs.config }}
105+
INPUTS_SCAN_SCOPE: ${{ inputs.scan-scope }}
106+
INPUTS_PATHS: ${{ inputs.paths }}
107+
INPUTS_SEVERITY: ${{ inputs.severity }}
108+
INPUTS_TIMEOUT: ${{ inputs.timeout }}
109+
INPUTS_OUTPUT_FORMAT: ${{ inputs.output-format }}
110+
INPUTS_FAIL_ON_FINDINGS: ${{ inputs.fail-on-findings }}
113111
run: |
112+
set +e
114113
# Map standard severity levels to Semgrep's levels
115-
case "${{ inputs.severity }}" in
114+
# Semgrep does not support hierarchy, levels must be set explicitly
115+
case "$INPUTS_SEVERITY" in
116116
"LOW")
117-
SEMGREP_SEVERITY="INFO"
117+
SEMGREP_SEVERITY="--severity INFO --severity WARNING --severity ERROR"
118118
;;
119119
"MEDIUM")
120-
SEMGREP_SEVERITY="WARNING"
120+
SEMGREP_SEVERITY="--severity WARNING --severity ERROR"
121121
;;
122122
"HIGH"|"CRITICAL")
123-
SEMGREP_SEVERITY="ERROR"
123+
SEMGREP_SEVERITY="--severity ERROR"
124124
;;
125125
*)
126-
SEMGREP_SEVERITY="WARNING"
126+
SEMGREP_SEVERITY="--severity WARNING --severity ERROR"
127127
;;
128128
esac
129129
130130
# Create results directory
131131
mkdir -p security-results/semgrep
132132
133-
REPORT_FILE="security-results/semgrep/semgrep-results.${{ inputs.output-format }}"
133+
REPORT_FILE="security-results/semgrep/semgrep-results.$INPUTS_OUTPUT_FORMAT"
134134
135-
if [[ "${{ inputs.scan-scope }}" == "changed" && -n "${{ steps.changed-files.outputs.all_changed_files }}" ]]; then
136-
echo "Running Semgrep on changed files"
135+
if [[ "$INPUTS_SCAN_SCOPE" == "changed" && -n "${{ steps.changed-files.outputs.all_changed_files }}" ]]; then
136+
echo "Running Semgrep on changed files, output results into workflow log only"
137137
FILES="${{ steps.changed-files.outputs.all_changed_files }}"
138-
else
139-
echo "Running Semgrep on all files in ${{ inputs.paths }}"
140-
FILES="${{ inputs.paths }}"
141-
fi
138+
semgrep \
139+
${SEMGREP_SEVERITY} \
140+
--error \
141+
--timeout "$INPUTS_TIMEOUT" \
142+
--metrics=off \
143+
${FILES}
144+
exit_code="$?"
145+
echo "exit_code=$exit_code" >> $GITHUB_OUTPUT
142146
147+
elif [[ "$INPUTS_SCAN_SCOPE" == "all" ]] ; then
148+
echo "Running Semgrep on all files in $INPUTS_PATHS"
143149
semgrep \
144-
--config ${{ inputs.config }} \
145-
--severity $SEMGREP_SEVERITY \
146-
--timeout ${{ inputs.timeout }} \
147-
--${{ inputs.output-format }} \
150+
${SEMGREP_SEVERITY} \
151+
--error \
152+
--metrics=off \
153+
--timeout "$INPUTS_TIMEOUT" \
154+
--"$INPUTS_OUTPUT_FORMAT" \
148155
-o "${REPORT_FILE}" \
149-
${FILES} || echo "exit_code=$?" >> $GITHUB_OUTPUT
150-
156+
"$INPUTS_PATHS"
157+
exit_code="$?"
158+
echo "exit_code=$exit_code" >> $GITHUB_OUTPUT
151159
echo "report_path=${REPORT_FILE}" >> $GITHUB_OUTPUT
152160
153-
if [[ "${{ inputs.fail-on-findings }}" == "true" && -n "$exit_code" && "$exit_code" != "0" ]]; then
161+
else
162+
echo "No files to scan found"
163+
fi
164+
165+
if [[ "$INPUTS_FAIL_ON_FINDINGS" == "true" && -n "$exit_code" && "$exit_code" != "0" ]]; then
154166
exit $exit_code
155167
fi

.github/workflows/_reusable-security-scan.yaml

+13-3
Original file line numberDiff line numberDiff line change
@@ -77,15 +77,21 @@ jobs:
7777
severity-level: ${{ inputs.severity-level }}
7878
fail-on-findings: ${{ inputs.fail-on-findings }}
7979
- uses: actions/upload-artifact@v4
80-
if: always()
80+
if: hashFiles('bandit-report.*') != '' # if any report is available
8181
with:
8282
name: bandit-results
83-
path: security-results/bandit
83+
path: bandit-report.*
8484
retention-days: 7
85+
- uses: github/codeql-action/upload-sarif@1b549b9259bda1cb5ddde3b41741a82a2d15a841 # v3.28.8
86+
if: hashFiles('bandit-report.sarif') != '' # if SARIF is available, upload it
87+
with:
88+
sarif_file: bandit-report.sarif
8589

8690
semgrep:
8791
if: contains(inputs.tools, 'semgrep')
8892
runs-on: ubuntu-latest
93+
container:
94+
image: returntocorp/semgrep
8995
steps:
9096
- uses: actions/checkout@v4
9197
- name: Run Semgrep scan
@@ -95,11 +101,15 @@ jobs:
95101
severity: ${{ inputs.severity-level }}
96102
fail-on-findings: ${{ inputs.fail-on-findings }}
97103
- uses: actions/upload-artifact@v4
98-
if: always()
104+
if: hashFiles('security-results/semgrep/*') != '' # if any report is available
99105
with:
100106
name: semgrep-results
101107
path: security-results/semgrep
102108
retention-days: 7
109+
- uses: github/codeql-action/upload-sarif@1b549b9259bda1cb5ddde3b41741a82a2d15a841 # v3.28.8
110+
if: hashFiles('security-results/semgrep/semgrep-results.sarif') != '' # if SARIF is available, upload it
111+
with:
112+
sarif_file: security-results/semgrep/semgrep-results.sarif
103113

104114
trivy:
105115
if: contains(inputs.tools, 'trivy')

.github/workflows/pr.yaml

+2-1
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ jobs:
8383
needs: [] # No dependencies, can run in parallel
8484
uses: ./.github/workflows/_reusable-security-scan.yaml
8585
with:
86-
tools: "semgrep" # Security tools to run
86+
tools: "semgrep,bandit" # Security tools to run
8787
scan-scope: "changed" # Only scan changed files
8888
severity-level: "MEDIUM" # Minimum severity to report
89+
fail-on-findings: true # explicitly set

.github/workflows/security-checks.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -93,4 +93,4 @@ jobs:
9393
tools: ${{ github.event_name == 'schedule' && 'bandit,clamav,semgrep,trivy' || inputs.tools }}
9494
scan-scope: ${{ github.event_name == 'schedule' && 'all' || inputs.scan-scope }}
9595
severity-level: ${{ github.event_name == 'schedule' && 'LOW' || inputs.severity-level }}
96-
fail-on-findings: true
96+
fail-on-findings: false # reports only

pyproject.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -282,7 +282,7 @@ follow_imports_for_stubs = true
282282
# BANDIT CONFIGURATION #
283283
[tool.bandit]
284284
skips = ["B101"]
285-
285+
exclude_dirs = ["tests"]
286286

287287
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
288288
# PYTEST CONFIGURATION #

0 commit comments

Comments
 (0)