diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 000000000..ca9b09646 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,191 @@ +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/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: + interval: "weekly" + day: "monday" + labels: + - "dependencies" + - "python" + + - 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: + 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" + + - 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" + 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..61be94187 --- /dev/null +++ b/.github/workflows/dependabot-auto-approve.yml @@ -0,0 +1,41 @@ +name: Dependabot Auto-Approve + +on: + pull_request: + types: [opened, synchronize] + +permissions: + contents: write + pull-requests: write + issues: 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 "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/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..e1ce01ec8 --- /dev/null +++ b/.github/workflows/python-security.yml @@ -0,0 +1,89 @@ +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 and pyproject.toml independently + - name: Audit requirements and pyproject files + run: | + 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/*") + exit $exit_code + + 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..23a15f4f1 --- /dev/null +++ b/.github/workflows/secret-scan.yml @@ -0,0 +1,31 @@ +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: Check staged/committed files against baseline + run: | + # 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/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:** diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 000000000..dfb72931d --- /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 | 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 + +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)