Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 90 additions & 0 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
name: Python Lint
'on':
push:
branches:
- main
- private/soc2
pull_request:

jobs:
setup:
name: Shared Setup
runs-on: ubuntu-latest
outputs:
python-version: '3.10'
steps:
- name: Checkout Code
uses: actions/checkout@v3
- name: Export Python Version
run: echo "python-version=3.10" >> $GITHUB_OUTPUT

ruff-lint-and-pr:
name: Ruff Lint & Auto PR
needs: setup
runs-on: ubuntu-latest
outputs:
ruff-issues-found: ${{ steps.scan.outputs.ruff_issues_found }}
permissions:
contents: write
pull-requests: write

steps:
- name: Checkout code
uses: actions/checkout@v3

- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '${{ needs.setup.outputs.python-version }}'

- name: Install Ruff
run: pip install ruff

- name: Sanitize branch name
run: echo "SAFE_REF_NAME=${GITHUB_REF_NAME//\//-}" >> $GITHUB_ENV

- name: Run Ruff Lint Scan
id: scan
run: |
echo "Running Ruff lint scan..."
mkdir -p tmp
ruff check . --select E,F,I --output-format=json > tmp/ruff_output.json || true
echo -e "\nHuman-readable Ruff output:\n"
ruff check . --select E,F,I || true
cat tmp/ruff_output.json || echo "[]"

issue_count=$(jq 'length' tmp/ruff_output.json || echo 0)

if [[ "$issue_count" -gt 0 ]]; then
echo "ruff_issues_found=true" >> "$GITHUB_OUTPUT"
else
echo "ruff_issues_found=false" >> "$GITHUB_OUTPUT"
fi

- name: Upload Ruff Report
uses: actions/upload-artifact@v4
with:
name: ruff-json-${{ env.SAFE_REF_NAME }}
path: tmp/ruff_output.json

- name: Generate PR Body (if issues found)
if: ${{ steps.scan.outputs.ruff_issues_found == 'true' }}
run: |
echo "# Ruff Lint Report for branch \`${GITHUB_REF_NAME}\`" > tmp/pr-body.md
jq -r '.[] | "* File: \(.filename)\n • Line: \(.location.row)\n • Column: \(.location.column)\n • Rule: \(.code)\n • Message: \(.message)\n"' \
tmp/ruff_output.json >> tmp/pr-body.md

- name: Create Pull Request (if issues found)
if: ${{ github.event_name == 'push' && steps.scan.outputs.ruff_issues_found == 'true' }}
uses: peter-evans/create-pull-request@v5
with:
commit-message: 'chore: Ruff lint issues detected'
title: 'Ruff Lint Report for branch ${{ github.ref_name }}'
body-path: tmp/pr-body.md
branch: auto/ruff-lint/${{ env.SAFE_REF_NAME }}
base: ${{ github.ref_name }}
delete-branch: true

- name: Fail Job If Issues Found
if: ${{ steps.scan.outputs.ruff_issues_found == 'true' }}
run: exit 1
172 changes: 172 additions & 0 deletions .github/workflows/security-scan.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
name: Python Security scan
'on':
push:
branches:
- master
- private/soc2
pull_request:

jobs:
setup:
name: Shared Setup
runs-on: ubuntu-latest
outputs:
python-version: '3.10'
steps:
- name: Checkout Code
uses: actions/checkout@v3
- name: Export Python Version
run: echo "python-version=3.10" >> $GITHUB_OUTPUT

bandit_scan:
name: Bandit Security Scan (Full)
needs: setup
runs-on: ubuntu-latest
outputs:
bandit-high-found: ${{ steps.scan.outputs.bandit_high_found }}
permissions:
contents: write
pull-requests: write
steps:
- name: Checkout Code
uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '${{ needs.setup.outputs.python-version }}'
- name: Install Bandit
run: pip install bandit jq
- name: Sanitize branch name
run: echo "SAFE_REF_NAME=${GITHUB_REF_NAME//\//-}" >> $GITHUB_ENV
- name: Run Full Bandit Scan
id: scan
run: |
echo "Running full Bandit scan..."
mkdir -p tmp
bandit -r . --severity-level medium -f json -o tmp/bandit_output.json || true
echo -e "\nHuman-readable Bandit output:\n"
bandit -r . --severity-level medium || true
cat tmp/bandit_output.json || echo "{}"
count=$(jq '.results | map(select(.issue_severity == "HIGH")) | length' tmp/bandit_output.json || echo 0)

if [[ "$count" -gt 0 ]]; then
echo "bandit_high_found=true" >> "$GITHUB_OUTPUT"
else
echo "bandit_high_found=false" >> "$GITHUB_OUTPUT"
fi

- name: Upload Bandit Report
uses: actions/upload-artifact@v4
with:
name: bandit-json-${{ env.SAFE_REF_NAME }}
path: tmp/bandit_output.json

- name: Generate PR Body (if vulnerabilities found)
if: ${{ steps.scan.outputs.bandit_high_found == 'true' }}
run: |
echo "# Bandit Scan Report for branch \`${GITHUB_REF_NAME}\`" > tmp/pr-body.md
jq -r '.results[]
| select(.issue_severity == "HIGH")
| "* File: \(.filename)\n • Line: \(.line_number)\n • Severity: \(.issue_severity)\n • Confidence: \(.issue_confidence)\n • Issue: \(.issue_text)\n"' \
tmp/bandit_output.json >> tmp/pr-body.md

- name: Create Pull Request (if vulnerabilities found)
if: ${{ github.event_name == 'push' && steps.scan.outputs.bandit_high_found == 'true' }}
uses: peter-evans/create-pull-request@v5
with:
commit-message: 'chore: issues detected by Bandit (HIGH)'
title: 'Bandit Vulnerability Report for branch ${{ github.ref_name }}'
body-path: tmp/pr-body.md
branch: auto/bandit-scan/${{ env.SAFE_REF_NAME }}
base: ${{ github.ref_name }}
delete-branch: true

- name: Fail Job If Vulnerabilities Found
if: ${{ steps.scan.outputs.bandit_high_found == 'true' }}
run: exit 1

trivy_scan:
name: Trivy Security Scan (Full)
needs: setup
runs-on: ubuntu-latest
outputs:
trivy_issues_found: ${{ steps.scan.outputs.trivy_issues_found }}
permissions:
contents: write
pull-requests: write
steps:
- name: Checkout Code
uses: actions/checkout@v3

- name: Install Trivy
run: |
sudo apt update
sudo apt install wget -y
wget -qO- https://aquasecurity.github.io/trivy-repo/deb/public.key | sudo tee /etc/apt/trusted.gpg.d/trivy.asc
echo "deb https://aquasecurity.github.io/trivy-repo/deb $(lsb_release -sc) main" | sudo tee /etc/apt/sources.list.d/trivy.list
sudo apt update
sudo apt install -y trivy jq

- name: Sanitize branch name
run: echo "SAFE_REF_NAME=${GITHUB_REF_NAME//\//-}" >> $GITHUB_ENV

- name: Run Trivy Filesystem Scan
id: scan
run: |
set -euo pipefail
echo "Running Trivy scan (HIGH/CRITICAL)..."
mkdir -p tmp
trivy fs --format json --severity HIGH,CRITICAL --output tmp/trivy.json .
[[ -f tmp/trivy.json ]] || echo '{"Results":[]}' > tmp/trivy.json
if ! jq -e '.Results and (.Results | length > 0)' tmp/trivy.json >/dev/null; then
echo "No scan results available — likely no supported files found."
echo "trivy_issues_found=false" >> "$GITHUB_OUTPUT"
exit 0
fi
count=$(jq -e '
(.Results // [])
| map(.Vulnerabilities? // [])
| add
| map(select(.Severity=="HIGH" or .Severity=="CRITICAL"))
| length
' tmp/trivy.json)
if [[ "$count" -gt 0 ]]; then
echo "trivy_issues_found=true" >> "$GITHUB_OUTPUT"
else
echo "trivy_issues_found=false" >> "$GITHUB_OUTPUT"
fi

- name: Upload Trivy Report
uses: actions/upload-artifact@v4
with:
name: trivy-json-${{ env.SAFE_REF_NAME }}
path: tmp/trivy.json

- name: Generate PR Body (if vulnerabilities found)
if: ${{ steps.scan.outputs.trivy_issues_found == 'true' }}
run: |
echo "# 🛡️ Trivy Scan Report for branch \`${GITHUB_REF_NAME}\`" > tmp/pr-body.md
jq -r '
(.Results // [])
| .[]
| .Target as $file
| (.Vulnerabilities? // [])
| map(select(.Severity=="HIGH" or .Severity=="CRITICAL"))
| .[]
| "* File: \($file)\n • Vulnerability ID: \(.VulnerabilityID)\n • Pkg: \(.PkgName) \(.InstalledVersion)\n • Severity: \(.Severity)\n • Title: \(.Title)\n"
' tmp/trivy.json >> tmp/pr-body.md

- name: Create Pull Request (if vulnerabilities found)
if: ${{ github.event_name == 'push' && steps.scan.outputs.trivy_issues_found == 'true' }}
uses: peter-evans/create-pull-request@v5
with:
commit-message: 'chore: vulnerabilities detected by Trivy (HIGH/CRITICAL)'
title: 'Trivy Vulnerability Report for branch ${{ github.ref_name }}'
body-path: tmp/pr-body.md
branch: auto/trivy-scan/${{ env.SAFE_REF_NAME }}
base: ${{ github.ref_name }}
delete-branch: true

- name: Fail Job If Vulnerabilities Found
if: ${{ steps.scan.outputs.trivy_issues_found == 'true' }}
run: exit 1
Loading