diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..5827c6d --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,11 @@ +version: 2 +updates: + - package-ecosystem: github-actions + directory: / + schedule: + interval: weekly + + - package-ecosystem: pip + directory: / + schedule: + interval: weekly diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml new file mode 100644 index 0000000..ee2f348 --- /dev/null +++ b/.github/workflows/ci.yaml @@ -0,0 +1,42 @@ +name: CI + +on: + pull_request: + branches: [main] + push: + branches: [main] + +permissions: + contents: read + +jobs: + lint-and-test: + name: Lint & Test + runs-on: ubuntu-latest + timeout-minutes: 15 + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + + - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6 + with: + python-version: "3.11" + + - name: Install uv + run: pip install uv + + - name: Install dependencies + run: uv sync --all-extras --dev 2>/dev/null || uv pip install -e ".[dev]" 2>/dev/null || true + + - name: Install pre-commit + run: pip install pre-commit + + - name: Run pre-commit + run: pre-commit run --all-files + + - name: Run tests + run: | + if [ -d tests ] || [ -d test ]; then + uv run pytest -v 2>/dev/null || pytest -v 2>/dev/null || echo "No test runner configured" + else + echo "No tests directory found — skipping" + fi diff --git a/.github/workflows/scorecard.yaml b/.github/workflows/scorecard.yaml new file mode 100644 index 0000000..b223845 --- /dev/null +++ b/.github/workflows/scorecard.yaml @@ -0,0 +1,38 @@ +name: Scorecard + +on: + push: + branches: [main] + schedule: + - cron: "30 6 * * 1" # Weekly Monday 6:30 AM UTC + workflow_dispatch: + +permissions: read-all + +jobs: + analysis: + name: OpenSSF Scorecard + runs-on: ubuntu-latest + permissions: + security-events: write + id-token: write + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + with: + persist-credentials: false + + - uses: ossf/scorecard-action@4eaacf0543bb3f2c246792bd56e8cdeffafb205a # v2.4.3 + with: + results_file: results.sarif + results_format: sarif + publish_results: true + + - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7 + with: + name: scorecard-results + path: results.sarif + retention-days: 30 + + - uses: github/codeql-action/upload-sarif@0d579ffd059c29b07949a3cce3983f0780820c98 # v4 + with: + sarif_file: results.sarif diff --git a/.github/workflows/security-scans.yaml b/.github/workflows/security-scans.yaml new file mode 100644 index 0000000..04d511f --- /dev/null +++ b/.github/workflows/security-scans.yaml @@ -0,0 +1,92 @@ +name: Security Scans + +on: + pull_request: + branches: [main] + +permissions: {} + +jobs: + dependency-review: + name: Dependency Review + runs-on: ubuntu-latest + permissions: + contents: read + pull-requests: write + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + - uses: actions/dependency-review-action@2031cfc080254a8a887f58cffee85186f0e49e48 # v4 + with: + fail-on-severity: moderate + deny-licenses: GPL-3.0, AGPL-3.0 + + trivy-scan: + name: Trivy Filesystem Scan + runs-on: ubuntu-latest + permissions: + contents: read + security-events: write + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + + - uses: aquasecurity/trivy-action@57a97c7e7821a5776cebc9bb87c984fa69cba8f1 # v0.35.0 + with: + scan-type: fs + scan-ref: . + severity: CRITICAL,HIGH + exit-code: 1 + format: sarif + output: trivy-results.sarif + + - uses: github/codeql-action/upload-sarif@0d579ffd059c29b07949a3cce3983f0780820c98 # v4 + if: always() + with: + sarif_file: trivy-results.sarif + + codeql: + name: CodeQL Analysis + runs-on: ubuntu-latest + permissions: + security-events: write + contents: read + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + + - uses: github/codeql-action/init@0d579ffd059c29b07949a3cce3983f0780820c98 # v4 + with: + languages: python + queries: security-extended + + - uses: github/codeql-action/analyze@0d579ffd059c29b07949a3cce3983f0780820c98 # v4 + + action-pinning: + name: Verify Action Pinning + runs-on: ubuntu-latest + permissions: + contents: read + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + + - name: Check for unpinned GitHub Actions + run: | + echo "=== Checking for unpinned GitHub Actions ===" + echo "" + echo "Pinning to SHAs prevents supply chain attacks." + echo "See: https://docs.github.com/en/actions/security-for-github-actions/security-guides/security-hardening-for-github-actions#using-third-party-actions" + echo "" + + UNPINNED=$(grep -rh "uses:" .github/workflows/ | grep -v "#" | grep -E "@v[0-9]|@main|@master" | sort -u || true) + + if [ -n "$UNPINNED" ]; then + echo "::warning::Found actions not pinned to SHA commits:" + echo "" + echo "$UNPINNED" + echo "" + echo "To pin an action, replace:" + echo " uses: actions/checkout@v6" + echo "With:" + echo " uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6" + exit 1 + else + echo "All actions are properly SHA-pinned." + fi diff --git a/.github/workflows/stale.yaml b/.github/workflows/stale.yaml new file mode 100644 index 0000000..2cbb185 --- /dev/null +++ b/.github/workflows/stale.yaml @@ -0,0 +1,10 @@ +name: Close Stale Issues and PRs + +on: + schedule: + - cron: "30 6 * * *" + workflow_dispatch: + +jobs: + stale: + uses: kagenti/.github/.github/workflows/stale.yaml@main