name: Python Dependency Audit on: workflow_call: inputs: python-version: type: string default: '3.11' install-extras: type: string default: 'dev' working-directory: type: string default: '.' workflow_dispatch: jobs: python-dependency-audit: runs-on: ubuntu-latest defaults: run: working-directory: ${{ inputs.working-directory }} steps: - name: Checkout uses: https://gitea.com/actions/checkout@v6 - name: Setup Python uses: https://github.com/actions/setup-python@v5.2.0 with: python-version: ${{ inputs.python-version || '3.11' }} - name: Cache pip uses: https://github.com/actions/cache@v4 with: path: ~/.cache/pip key: ${{ runner.os }}-pip-${{ inputs.python-version }}-${{ hashFiles('**/pyproject.toml', '**/requirements*.txt') }} restore-keys: | ${{ runner.os }}-pip-${{ inputs.python-version }}- ${{ runner.os }}-pip- - name: Install dependencies (isolated venv) run: | # venv pulita: audita SOLO le dipendenze dichiarate dal repo, # non l'ambiente globale del runner (che su runner ML/GPU è contaminato) python -m venv .audit-venv .audit-venv/bin/pip install --upgrade pip pip-audit if [ -n "${{ inputs.install-extras }}" ]; then .audit-venv/bin/pip install -e ".[${{ inputs.install-extras }}]" elif [ -f requirements.txt ]; then .audit-venv/bin/pip install -r requirements.txt else .audit-venv/bin/pip install -e "." fi - name: Security audit (pip-audit) id: audit run: | .audit-venv/bin/pip-audit --format=json --output=audit.json 2>/dev/null || true VULNS=$(python3 -c " import json, sys try: data = json.load(open('audit.json')) deps = data.get('dependencies', []) count = sum(len(d.get('vulns', [])) for d in deps) print(count) except Exception: print(0) ") echo "vulnerabilities=$VULNS" >> $GITHUB_OUTPUT - name: Open or update issue if vulnerabilities found if: steps.audit.outputs.vulnerabilities != '0' env: GITEA_TOKEN: ${{ gitea.token }} SERVER_URL: ${{ gitea.server_url }} REPOSITORY: ${{ gitea.repository }} run: | VULNS="${{ steps.audit.outputs.vulnerabilities }}" DATE=$(date '+%Y-%m-%d') TITLE_PREFIX="Security Python:" VULN_LIST=$(python3 -c " import json try: data = json.load(open('audit.json')) lines = [] for dep in data.get('dependencies', []): for v in dep.get('vulns', []): lines.append(f\"- {dep['name']} {dep.get('version','?')}: {v.get('id','?')} ({v.get('description','')[:80]})\") print('\n'.join(lines[:20])) except Exception: print('N/A') ") printf '## Security Audit Python — %s\n\n### Vulnerabilità trovate: %s\n\n```\n%s\n```\n' \ "$DATE" "$VULNS" "$VULN_LIST" > /tmp/body.md # Deduplica: se esiste già una issue aperta con lo stesso prefisso, # aggiungi un commento invece di aprirne una nuova EXISTING=$(curl -s \ -H "Authorization: token $GITEA_TOKEN" \ "$SERVER_URL/api/v1/repos/$REPOSITORY/issues?state=open&type=issues&limit=50" \ | jq -r --arg p "$TITLE_PREFIX" '[.[] | select(.title | contains($p))] | (first // {}) | .number // empty') if [ -n "$EXISTING" ]; then curl -s -X POST \ -H "Content-Type: application/json" \ -H "Authorization: token $GITEA_TOKEN" \ "$SERVER_URL/api/v1/repos/$REPOSITORY/issues/$EXISTING/comments" \ -d "{ \"body\": $(jq -Rs . /tmp/body.md) }" else curl -s -X POST \ -H "Content-Type: application/json" \ -H "Authorization: token $GITEA_TOKEN" \ "$SERVER_URL/api/v1/repos/$REPOSITORY/issues" \ -d "{ \"title\": \"[$DATE] Security Python: $VULNS vulnerabilità rilevate\", \"body\": $(jq -Rs . /tmp/body.md) }" fi