From a9b3e8438b93712d42d031597fd2519e2fef6145 Mon Sep 17 00:00:00 2001 From: vwinland Date: Wed, 23 Sep 2026 13:37:58 -0400 Subject: [PATCH 1/3] feat: add security automation framework - .github/dependabot.yml: weekly dependency scanning across all 11 package locations (pip, npm, github-actions) - .github/workflows/dependency-review.yml: blocks PRs introducing medium+ severity vulnerabilities; posts summary comment on PR - .github/workflows/dependabot-auto-approve.yml: auto-approves Dependabot patch/minor PRs; labels major updates for manual review - .github/workflows/codeql-analysis.yml: static analysis for Python and JavaScript on push, PR, and weekly schedule - .github/workflows/python-security.yml: pip-audit across all requirements files + Bandit SARIF upload on push and PR - .github/workflows/secret-scan.yml: detect-secrets against baseline on every push and PR - SECURITY.md: vulnerability reporting process and security policy This framework would have prevented the archival incident in issue #136 by catching vulnerable dependency updates before they merged and opening automated fix PRs within days of a CVE being published. --- .github/dependabot.yml | 119 ++++++++++++++++++ .github/workflows/codeql-analysis.yml | 42 +++++++ .github/workflows/dependabot-auto-approve.yml | 38 ++++++ .github/workflows/dependency-review.yml | 28 +++++ .github/workflows/python-security.yml | 79 ++++++++++++ .github/workflows/secret-scan.yml | 35 ++++++ SECURITY.md | 34 +++++ 7 files changed, 375 insertions(+) create mode 100644 .github/dependabot.yml create mode 100644 .github/workflows/codeql-analysis.yml create mode 100644 .github/workflows/dependabot-auto-approve.yml create mode 100644 .github/workflows/dependency-review.yml create mode 100644 .github/workflows/python-security.yml create mode 100644 .github/workflows/secret-scan.yml create mode 100644 SECURITY.md diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 000000000..3e3a5b04c --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,119 @@ +version: 2 + +updates: + # GitHub Actions + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + day: "monday" + labels: + - "dependencies" + - "github-actions" + + # Root pyproject.toml + - package-ecosystem: "pip" + directory: "/" + schedule: + interval: "weekly" + day: "monday" + labels: + - "dependencies" + - "python" + + # requirements.txt files + - package-ecosystem: "pip" + directory: "/tutorials/17-data-streaming/confluent-modernization" + schedule: + interval: "weekly" + day: "monday" + labels: + - "dependencies" + - "python" + + - package-ecosystem: "pip" + directory: "/tutorials/13-full-stack-applications/ai-stylist" + schedule: + interval: "weekly" + day: "monday" + labels: + - "dependencies" + - "python" + + - package-ecosystem: "pip" + directory: "/tutorials/07-guardrails-and-safety/granite-guardian-webapp" + schedule: + interval: "weekly" + day: "monday" + labels: + - "dependencies" + - "python" + + - package-ecosystem: "pip" + directory: "/tutorials/07-guardrails-and-safety/ai-agent-security" + schedule: + interval: "weekly" + day: "monday" + labels: + - "dependencies" + - "python" + + - package-ecosystem: "pip" + directory: "/tutorials/03-multi-agent-systems/bee-ai-multi-agent-contract-management" + schedule: + interval: "weekly" + day: "monday" + labels: + - "dependencies" + - "python" + + - package-ecosystem: "pip" + directory: "/tutorials/02-agents-and-orchestration/sql-agent-app" + schedule: + interval: "weekly" + day: "monday" + labels: + - "dependencies" + - "python" + + # pyproject.toml files (uv-managed) + - package-ecosystem: "pip" + directory: "/tutorials/03-multi-agent-systems/my_retail_advisor" + schedule: + interval: "weekly" + day: "monday" + labels: + - "dependencies" + - "python" + + - package-ecosystem: "pip" + directory: "/tutorials/03-multi-agent-systems/multiagent-collab-cs-call-center-analysis" + schedule: + interval: "weekly" + day: "monday" + labels: + - "dependencies" + - "python" + + # npm + - package-ecosystem: "npm" + directory: "/tutorials/13-full-stack-applications/silly_story_time" + schedule: + interval: "weekly" + day: "monday" + groups: + npm-all: + patterns: + - "*" + labels: + - "dependencies" + - "javascript" + + - package-ecosystem: "npm" + directory: "/tutorials/16-ibm-bob/mcp-server-integration-ibm-bob/arxiv-server" + schedule: + interval: "weekly" + day: "monday" + labels: + - "dependencies" + - "javascript" diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml new file mode 100644 index 000000000..5dd54bb9b --- /dev/null +++ b/.github/workflows/codeql-analysis.yml @@ -0,0 +1,42 @@ +name: CodeQL Security Analysis + +on: + push: + branches: [main] + pull_request: + branches: [main] + schedule: + # Run every Monday at 09:00 UTC + - cron: "0 9 * * 1" + +permissions: + contents: read + security-events: write + +jobs: + analyze: + name: Analyze (${{ matrix.language }}) + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + language: [python, javascript-typescript] + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Initialize CodeQL + uses: github/codeql-action/init@v3 + with: + languages: ${{ matrix.language }} + # Use the security-extended query suite for broader coverage + queries: security-extended + + - name: Autobuild + uses: github/codeql-action/autobuild@v3 + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v3 + with: + category: "/language:${{ matrix.language }}" diff --git a/.github/workflows/dependabot-auto-approve.yml b/.github/workflows/dependabot-auto-approve.yml new file mode 100644 index 000000000..63d1b51be --- /dev/null +++ b/.github/workflows/dependabot-auto-approve.yml @@ -0,0 +1,38 @@ +name: Dependabot Auto-Approve + +on: + pull_request: + types: [opened, synchronize] + +permissions: + contents: read + pull-requests: write + +jobs: + auto-approve: + runs-on: ubuntu-latest + if: github.actor == 'dependabot[bot]' + steps: + - name: Fetch Dependabot metadata + id: metadata + uses: dependabot/fetch-metadata@v2 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + + # Auto-approve patch and minor updates — safe to merge with minimal review + - name: Auto-approve patch and minor updates + if: >- + steps.metadata.outputs.update-type == 'version-update:semver-patch' || + steps.metadata.outputs.update-type == 'version-update:semver-minor' + run: gh pr review "$PR_URL" --approve --body "Auto-approved: patch/minor dependency update." + env: + PR_URL: ${{ github.event.pull_request.html_url }} + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + # Label major updates so they are easy to find and prioritise + - name: Label major updates for manual review + if: steps.metadata.outputs.update-type == 'version-update:semver-major' + run: gh pr edit "$PR_URL" --add-label "major-update,needs-review" + env: + PR_URL: ${{ github.event.pull_request.html_url }} + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml new file mode 100644 index 000000000..c012d1873 --- /dev/null +++ b/.github/workflows/dependency-review.yml @@ -0,0 +1,28 @@ +name: Dependency Review + +on: + pull_request: + branches: [main] + +permissions: + contents: read + pull-requests: write + +jobs: + dependency-review: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Dependency Review + uses: actions/dependency-review-action@v4 + with: + # Block PRs that introduce medium severity or higher vulnerabilities + fail-on-severity: moderate + # Allow these licenses (Apache, MIT, BSD, ISC are standard open source) + allow-licenses: >- + Apache-2.0, MIT, BSD-2-Clause, BSD-3-Clause, ISC, Python-2.0, + PSF-2.0, CC0-1.0, Unlicense, 0BSD + # Post a comment on the PR summarising any findings + comment-summary-in-pr: always diff --git a/.github/workflows/python-security.yml b/.github/workflows/python-security.yml new file mode 100644 index 000000000..6457a7d14 --- /dev/null +++ b/.github/workflows/python-security.yml @@ -0,0 +1,79 @@ +name: Python Security Scan + +on: + push: + branches: [main] + pull_request: + branches: [main] + schedule: + # Run every Monday at 09:00 UTC + - cron: "0 9 * * 1" + +permissions: + contents: read + security-events: write + +jobs: + pip-audit: + name: pip-audit + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install pip-audit + run: pip install pip-audit + + # Audit each requirements.txt independently so one failure doesn't hide others + - name: Audit requirements files + # continue-on-error so all files are scanned even if some have vulnerabilities + continue-on-error: true + run: | + find . -name "requirements*.txt" \ + -not -path "*/node_modules/*" \ + -not -path "*/.venv/*" \ + -not -path "*/WareHouse/*" \ + | while read f; do + echo "=== $f ===" + pip-audit -r "$f" --desc || true + done + + bandit: + name: Bandit + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install Bandit + run: pip install bandit[sarif] + + - name: Run Bandit + # Educational content intentionally uses patterns Bandit flags (e.g. hardcoded + # example passwords, subprocess calls) — treat as informational, not blocking. + continue-on-error: true + run: | + bandit -r tutorials/ \ + --exclude tutorials/03-multi-agent-systems/chatdev_watsonx_tutorial_/WareHouse \ + -f sarif \ + -o bandit-results.sarif \ + --severity-level medium \ + --confidence-level medium \ + -q + + - name: Upload Bandit SARIF + uses: github/codeql-action/upload-sarif@v3 + if: always() + with: + sarif_file: bandit-results.sarif + category: bandit diff --git a/.github/workflows/secret-scan.yml b/.github/workflows/secret-scan.yml new file mode 100644 index 000000000..c32ff66b3 --- /dev/null +++ b/.github/workflows/secret-scan.yml @@ -0,0 +1,35 @@ +name: Secret Scan + +on: + push: + branches: [main] + pull_request: + branches: [main] + +permissions: + contents: read + +jobs: + detect-secrets: + name: detect-secrets + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install detect-secrets + run: pip install detect-secrets + + - name: Run detect-secrets against baseline + run: | + detect-secrets scan \ + --baseline .secrets.baseline \ + --exclude-files '.*\.lock$' \ + --exclude-files '.*package-lock\.json$' \ + --exclude-files '.*\.ipynb$' \ + --exclude-files '.*/node_modules/.*' diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 000000000..e43e4660e --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,34 @@ +# Security Policy + +## Reporting a Vulnerability + +Please **do not** open a public GitHub issue for security vulnerabilities. + +Report vulnerabilities privately via [GitHub's private vulnerability reporting](https://github.com/IBM/ibmdotcom-tutorials/security/advisories/new) or by emailing the maintainers directly. + +We will acknowledge your report within 48 hours and aim to ship a fix within 7 days for critical issues. + +## Supported Versions + +Security fixes are applied to the `main` branch only. Older tutorial snapshots are not backported. + +## Automated Security Tooling + +This repository uses the following automated security measures: + +| Tool | What it does | +|------|-------------| +| **Dependabot** | Opens weekly PRs to update vulnerable dependencies across all 11 package locations | +| **Dependency Review** | Blocks any PR that introduces a medium-severity or higher vulnerability | +| **CodeQL** | Static analysis for Python and JavaScript on every push and PR | +| **pip-audit + Bandit** | Scans Python dependencies and source code on every push and PR | +| **detect-secrets** | Prevents accidental credential commits on every push and PR | + +## For Tutorial Users + +These tutorials install third-party packages. Before running any tutorial in a production or sensitive environment: + +- Use a dedicated virtual environment (`python -m venv .venv`) +- Pin dependency versions to known-good values +- Review the tutorial's `requirements.txt` or `pyproject.toml` against current CVE databases +- Never commit real API keys or credentials — use environment variables or `.env` files (which are gitignored) From f8f10278bd0bd58a242892f87ad2fa37b2db16ec Mon Sep 17 00:00:00 2001 From: vwinland Date: Fri, 25 Sep 2026 14:16:07 -0400 Subject: [PATCH 2/3] docs: add dependency and security standards to CONTRIBUTING.md --- CONTRIBUTING.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f0b0f60e4..e2a8464ee 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -270,6 +270,13 @@ All Python code must pass: - ✅ No hardcoded secrets - ✅ No syntax errors +### Dependency & Security Standards + +To keep dependencies secure across all tutorial subdirectories, PRs run automated CI checks: +- **Dependency Review**: Blocks PRs that introduce dependencies with known moderate or higher severity vulnerabilities. +- **CodeQL & Python Security Scan**: Automated static analysis and dependency audits (`pip-audit`, Bandit) run against pull requests. +- **Pinning**: In tutorial `requirements.txt` or `pyproject.toml` files, ensure direct dependencies do not pin versions with known critical/high CVEs. + #### Tutorial-Specific Guidelines **✅ Do:** From 78384c2d2863506523982cf2402acad7df429c33 Mon Sep 17 00:00:00 2001 From: vwinland Date: Tue, 29 Sep 2026 14:23:15 -0400 Subject: [PATCH 3/3] fix(security-framework): address Copilot review findings for scanning, manifests, and permissions --- .github/dependabot.yml | 74 ++++++++++++++++++- .github/workflows/dependabot-auto-approve.yml | 7 +- .github/workflows/python-security.yml | 30 +++++--- .github/workflows/secret-scan.yml | 10 +-- SECURITY.md | 14 ++-- 5 files changed, 108 insertions(+), 27 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 3e3a5b04c..ca9b09646 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -67,6 +67,33 @@ updates: - "dependencies" - "python" + - package-ecosystem: "pip" + directory: "/tutorials/03-multi-agent-systems/chatdev_watsonx_tutorial_" + schedule: + interval: "weekly" + day: "monday" + labels: + - "dependencies" + - "python" + + - package-ecosystem: "pip" + directory: "/tutorials/03-multi-agent-systems/a2a_tutorial/beeai-a2a-client" + schedule: + interval: "weekly" + day: "monday" + labels: + - "dependencies" + - "python" + + - package-ecosystem: "pip" + directory: "/tutorials/03-multi-agent-systems/a2a_tutorial/beeai-a2a-server" + schedule: + interval: "weekly" + day: "monday" + labels: + - "dependencies" + - "python" + - package-ecosystem: "pip" directory: "/tutorials/02-agents-and-orchestration/sql-agent-app" schedule: @@ -76,7 +103,16 @@ updates: - "dependencies" - "python" - # pyproject.toml files (uv-managed) + - package-ecosystem: "pip" + directory: "/tutorials/02-agents-and-orchestration/wxo-text-classification/product-review-sentiment-agent" + schedule: + interval: "weekly" + day: "monday" + labels: + - "dependencies" + - "python" + + # pyproject.toml files (uv / poetry / standard pip) - package-ecosystem: "pip" directory: "/tutorials/03-multi-agent-systems/my_retail_advisor" schedule: @@ -95,6 +131,42 @@ updates: - "dependencies" - "python" + - package-ecosystem: "pip" + directory: "/tutorials/03-multi-agent-systems/acp_tutorial" + schedule: + interval: "weekly" + day: "monday" + labels: + - "dependencies" + - "python" + + - package-ecosystem: "pip" + directory: "/tutorials/03-multi-agent-systems/acp_tutorial/beeai_agent_server" + schedule: + interval: "weekly" + day: "monday" + labels: + - "dependencies" + - "python" + + - package-ecosystem: "pip" + directory: "/tutorials/03-multi-agent-systems/acp_tutorial/crewai_agent_server" + schedule: + interval: "weekly" + day: "monday" + labels: + - "dependencies" + - "python" + + - package-ecosystem: "pip" + directory: "/tutorials/02-agents-and-orchestration/react-agent-it-support" + schedule: + interval: "weekly" + day: "monday" + labels: + - "dependencies" + - "python" + # npm - package-ecosystem: "npm" directory: "/tutorials/13-full-stack-applications/silly_story_time" diff --git a/.github/workflows/dependabot-auto-approve.yml b/.github/workflows/dependabot-auto-approve.yml index 63d1b51be..61be94187 100644 --- a/.github/workflows/dependabot-auto-approve.yml +++ b/.github/workflows/dependabot-auto-approve.yml @@ -5,8 +5,9 @@ on: types: [opened, synchronize] permissions: - contents: read + contents: write pull-requests: write + issues: write jobs: auto-approve: @@ -32,7 +33,9 @@ jobs: # Label major updates so they are easy to find and prioritise - name: Label major updates for manual review if: steps.metadata.outputs.update-type == 'version-update:semver-major' - run: gh pr edit "$PR_URL" --add-label "major-update,needs-review" + run: | + gh pr edit "$PR_URL" --add-label "dependencies" || true + gh pr comment "$PR_URL" --body "⚠️ **Major version update detected.** Manual review and testing required before merging." env: PR_URL: ${{ github.event.pull_request.html_url }} GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/python-security.yml b/.github/workflows/python-security.yml index 6457a7d14..e1ce01ec8 100644 --- a/.github/workflows/python-security.yml +++ b/.github/workflows/python-security.yml @@ -29,19 +29,29 @@ jobs: - name: Install pip-audit run: pip install pip-audit - # Audit each requirements.txt independently so one failure doesn't hide others - - name: Audit requirements files - # continue-on-error so all files are scanned even if some have vulnerabilities - continue-on-error: true + # Audit each requirements.txt and pyproject.toml independently + - name: Audit requirements and pyproject files run: | - find . -name "requirements*.txt" \ + exit_code=0 + while IFS= read -r f; do + echo "=== Auditing $f ===" + if [[ "$f" == *"requirements"*".txt" ]]; then + if ! pip-audit -r "$f" --desc; then + echo "❌ Vulnerabilities found in $f" + exit_code=1 + fi + elif [[ "$f" == *"pyproject.toml" ]]; then + dir=$(dirname "$f") + if ! (cd "$dir" && pip-audit --desc); then + echo "❌ Vulnerabilities found in $f" + exit_code=1 + fi + fi + done < <(find . \( -name "requirements*.txt" -o -name "pyproject.toml" \) \ -not -path "*/node_modules/*" \ -not -path "*/.venv/*" \ - -not -path "*/WareHouse/*" \ - | while read f; do - echo "=== $f ===" - pip-audit -r "$f" --desc || true - done + -not -path "*/WareHouse/*") + exit $exit_code bandit: name: Bandit diff --git a/.github/workflows/secret-scan.yml b/.github/workflows/secret-scan.yml index c32ff66b3..23a15f4f1 100644 --- a/.github/workflows/secret-scan.yml +++ b/.github/workflows/secret-scan.yml @@ -25,11 +25,7 @@ jobs: - name: Install detect-secrets run: pip install detect-secrets - - name: Run detect-secrets against baseline + - name: Check staged/committed files against baseline run: | - detect-secrets scan \ - --baseline .secrets.baseline \ - --exclude-files '.*\.lock$' \ - --exclude-files '.*package-lock\.json$' \ - --exclude-files '.*\.ipynb$' \ - --exclude-files '.*/node_modules/.*' + # Use detect-secrets-hook so new unbaselined secrets cause a non-zero exit code + git ls-files | grep -v -E '(\.lock$|package-lock\.json$|\.ipynb$|/node_modules/)' | xargs detect-secrets-hook --baseline .secrets.baseline diff --git a/SECURITY.md b/SECURITY.md index e43e4660e..dfb72931d 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -16,13 +16,13 @@ Security fixes are applied to the `main` branch only. Older tutorial snapshots a This repository uses the following automated security measures: -| Tool | What it does | -|------|-------------| -| **Dependabot** | Opens weekly PRs to update vulnerable dependencies across all 11 package locations | -| **Dependency Review** | Blocks any PR that introduces a medium-severity or higher vulnerability | -| **CodeQL** | Static analysis for Python and JavaScript on every push and PR | -| **pip-audit + Bandit** | Scans Python dependencies and source code on every push and PR | -| **detect-secrets** | Prevents accidental credential commits on every push and PR | +| Tool | Schedule / Triggers | What it does | +|------|--------------------|-------------| +| **Dependabot** | Weekly (Mondays 09:00 UTC) | Scans and opens PRs to update vulnerable dependencies across all package manifests | +| **Dependency Review** | PRs targeting `main` | Blocks any PR that introduces moderate or higher severity vulnerabilities | +| **CodeQL** | Push/PR to `main`, weekly schedule | Static analysis for Python and JavaScript | +| **pip-audit + Bandit** | Push/PR to `main`, weekly schedule | Scans Python dependencies for vulnerabilities and analyzes code | +| **detect-secrets** | Push/PR to `main` | Blocks commits containing unbaselined secrets or credentials | ## For Tutorial Users