name: Security Quality Gate on: pull_request: push: branches: - main - master workflow_dispatch: permissions: contents: read jobs: security: name: npm audit, Bandit, pip-audit, detect-secrets runs-on: ubuntu-latest timeout-minutes: 20 env: PIP_INDEX_URL: https://pypi.tuna.tsinghua.edu.cn/simple PIP_DISABLE_PIP_VERSION_CHECK: "1" steps: - name: Checkout uses: actions/checkout@v4 - name: Set up Node.js uses: actions/setup-node@v4 with: node-version: "22" cache: npm - name: Install npm workspace dependencies run: | npm install -g npm@10.9.7 npm ci - name: npm production dependency audit id: npm_audit continue-on-error: true run: | mkdir -p security-artifacts npm audit --omit=dev --audit-level=high --json > security-artifacts/npm-audit.json - name: Set up Python uses: actions/setup-python@v5 with: python-version: "3.12" cache: pip - name: Build project dependency environment run: | python -m venv .venv-project .venv-project/bin/python -m pip install --upgrade pip .venv-project/bin/python -m pip install \ -e yuqei-contract-platform/services/contract-api \ -e yuqei-ai-platform/services/ai-platform-api \ -e yuqei-ai-platform/services/worker \ -e yuqei-ai-sdk-python - name: Install security audit tools run: | python -m venv .venv-audit .venv-audit/bin/python -m pip install --upgrade pip .venv-audit/bin/python -m pip install bandit==1.9.4 pip-audit==2.10.1 detect-secrets==1.5.0 - name: Bandit Python source scan id: bandit continue-on-error: true run: | mkdir -p security-artifacts .venv-audit/bin/bandit \ -r yuqei-contract-platform/services/contract-api/src \ yuqei-ai-platform/services/ai-platform-api/src \ yuqei-ai-platform/services/worker/src \ yuqei-ai-sdk-python/src \ yuqei-infra/deploy \ yuqei-infra/backup \ yuqei-infra/observability \ yuqei-infra/scripts/run-ai-provider-acceptance.py \ -f json \ -o security-artifacts/bandit-project.json - name: Python project dependency audit id: pip_audit continue-on-error: true run: | mkdir -p security-artifacts .venv-audit/bin/pip-audit \ --path .venv-project/lib/python3.12/site-packages \ -f json \ -o security-artifacts/pip-audit-project.json \ --progress-spinner off - name: Tracked source secret scan id: source_secret_scan continue-on-error: true run: | mkdir -p security-artifacts/source-secret-scan .venv-audit/bin/python yuqei-infra/scripts/run-source-secret-scan.py \ --artifact-dir security-artifacts/source-secret-scan - name: Enforce security quality gate run: | failed=0 if [ "${{ steps.npm_audit.outcome }}" != "success" ]; then echo "::error::npm audit found high-or-above vulnerabilities or failed to run" failed=1 fi if [ "${{ steps.bandit.outcome }}" != "success" ]; then echo "::error::Bandit found Python security findings or failed to run" failed=1 fi if [ "${{ steps.pip_audit.outcome }}" != "success" ]; then echo "::error::pip-audit found vulnerable Python dependencies or failed to run" failed=1 fi if [ "${{ steps.source_secret_scan.outcome }}" != "success" ]; then echo "::error::detect-secrets found an unreviewed source secret or failed to run" failed=1 fi exit "$failed" - name: Upload security scan artifacts (GitHub) if: ${{ always() && github.server_url == 'https://github.com' }} uses: actions/upload-artifact@v4 with: name: security-quality-gate path: security-artifacts/ if-no-files-found: warn - name: Upload security scan artifacts (Gitea/GHES) if: ${{ always() && github.server_url != 'https://github.com' }} uses: actions/upload-artifact@v3 with: name: security-quality-gate path: security-artifacts/ if-no-files-found: warn