Merge branch 'open-edge-platform:main' into main #69
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # Security scan workflow | |
| # | |
| # This workflow performs security scanning using multiple tools to identify vulnerabilities, | |
| # secrets, configuration issues, and code quality problems across the entire codebase. | |
| # It also scans GitHub Actions workflows for potential security issues. | |
| # | |
| # Security Tools: | |
| # - Zizmor: GitHub Actions workflow security scanner | |
| # - Bandit: Python security linter | |
| # - Trivy: Vulnerability and misconfiguration scanner | |
| # - Semgrep: Static analysis tool for finding bugs and security issues | |
| # - ZAP: Dynamic application security testing (DAST) tool | |
| # - Schemathesis: API fuzz testing tool | |
| name: "Security scan" | |
| on: | |
| schedule: | |
| # Run security checks every day at 2 AM UTC | |
| - cron: "0 2 * * *" | |
| workflow_dispatch: | |
| push: | |
| branches: | |
| - main | |
| - release/** | |
| tags: | |
| - v[0-9]+\.[0-9]+\.[0-9]+-RC[0-9]+ | |
| pull_request: | |
| branches: | |
| - main | |
| - release/** | |
| permissions: {} # No permissions by default | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.head_ref || github.ref }} | |
| cancel-in-progress: ${{ github.event_name == 'pull_request' }} | |
| jobs: | |
| zizmor-scan: | |
| runs-on: ${{ github.repository_owner == 'open-edge-platform' && 'overflow' || 'ubuntu-latest' }} | |
| permissions: | |
| contents: read | |
| security-events: write # Needed to upload the results to code-scanning dashboard | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| with: | |
| persist-credentials: false | |
| - name: Run Zizmor scan | |
| uses: open-edge-platform/geti-ci/actions/zizmor@e80098b3d180db37914f11ff6021f9fa34d0bb9f | |
| with: | |
| scan-scope: ${{ github.event_name == 'pull_request' && 'changed' || 'all' }} | |
| severity-level: ${{ github.event_name == 'pull_request' && 'LOW' || 'LOW' }} | |
| confidence-level: ${{ github.event_name == 'pull_request' && 'LOW' || 'LOW' }} | |
| fail-on-findings: ${{ github.event_name == 'pull_request' && 'true' || 'false' }} | |
| bandit-scan: | |
| runs-on: ${{ github.repository_owner == 'open-edge-platform' && 'overflow' || 'ubuntu-latest' }} | |
| permissions: | |
| contents: read | |
| security-events: write # Needed to upload the results to code-scanning dashboard | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| with: | |
| persist-credentials: false | |
| - name: Run Bandit scan | |
| uses: open-edge-platform/geti-ci/actions/bandit@e80098b3d180db37914f11ff6021f9fa34d0bb9f | |
| with: | |
| scan-scope: ${{ github.event_name == 'pull_request' && 'changed' || 'all' }} | |
| severity-level: ${{ github.event_name == 'pull_request' && 'HIGH' || 'LOW' }} | |
| confidence-level: ${{ github.event_name == 'pull_request' && 'LOW' || 'LOW' }} | |
| config_file: ".github/bandit_config.yml" | |
| fail-on-findings: ${{ github.event_name == 'pull_request' && 'true' || 'false' }} | |
| semgrep-scan: | |
| runs-on: ${{ github.repository_owner == 'open-edge-platform' && 'overflow' || 'ubuntu-latest' }} | |
| permissions: | |
| contents: read | |
| security-events: write # Needed to upload the results to code-scanning dashboard | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| with: | |
| persist-credentials: false | |
| fetch-depth: 0 | |
| - name: Run Semgrep scan | |
| uses: open-edge-platform/geti-ci/actions/semgrep@e80098b3d180db37914f11ff6021f9fa34d0bb9f | |
| with: | |
| scan-scope: ${{ github.event_name == 'pull_request' && 'changed' || 'all' }} | |
| severity: ${{ github.event_name == 'pull_request' && 'HIGH' || 'LOW' }} | |
| fail-on-findings: ${{ github.event_name == 'pull_request' && 'true' || 'false' }} | |
| trivy-scan: | |
| if: github.event_name != 'pull_request' | |
| runs-on: ${{ github.repository_owner == 'open-edge-platform' && 'overflow' || 'ubuntu-latest' }} | |
| permissions: | |
| contents: read | |
| security-events: write # Needed to upload the results to code-scanning dashboard | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| with: | |
| persist-credentials: false | |
| - name: Run Trivy scan | |
| id: trivy | |
| uses: open-edge-platform/geti-ci/actions/trivy@e80098b3d180db37914f11ff6021f9fa34d0bb9f | |
| with: | |
| scan_type: "fs" | |
| scan-scope: all | |
| severity: "LOW" | |
| scanners: "vuln,secret,config" | |
| format: "sarif" | |
| timeout: "15m" | |
| ignore_unfixed: "true" | |
| trivy-docker-image-scan: | |
| if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' | |
| runs-on: ${{ github.repository_owner == 'open-edge-platform' && 'overflow' || 'ubuntu-latest' }} | |
| permissions: | |
| contents: read | |
| strategy: | |
| matrix: | |
| ai-device: [cpu, cuda, xpu] | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| with: | |
| persist-credentials: false | |
| - name: Install dependencies | |
| uses: ./.github/actions/install-dependencies | |
| with: | |
| cleanup-runner: "true" | |
| - name: Build docker image | |
| working-directory: application | |
| id: image-name | |
| env: | |
| DEVICE: ${{ matrix.ai-device }} | |
| run: | | |
| just build-target="${DEVICE}" docker-build-context="--no-cache" build-image | |
| IMAGE_NAME=$(just --evaluate build-target="${DEVICE}" image-name) | |
| echo "image=${IMAGE_NAME}" >> $GITHUB_OUTPUT | |
| echo "Image: ${IMAGE_NAME}" | |
| - name: Cleanup docker cache | |
| run: docker builder prune -f | |
| - name: Run Trivy scan | |
| id: trivy | |
| uses: open-edge-platform/geti-ci/actions/trivy@e80098b3d180db37914f11ff6021f9fa34d0bb9f | |
| with: | |
| artifact-name: "trivy-results-docker-${{ matrix.ai-device }}" | |
| scan_type: "image" | |
| scan_target: "${{ steps.image-name.outputs.image }}" | |
| scan-scope: all | |
| severity: "LOW" | |
| scanners: "vuln,secret,config" | |
| format: "table" | |
| timeout: "15m" | |
| ignore_unfixed: "true" | |
| dast-scan: | |
| if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' | |
| runs-on: ${{ github.repository_owner == 'open-edge-platform' && 'overflow' || 'ubuntu-latest' }} | |
| permissions: | |
| contents: read | |
| strategy: | |
| matrix: | |
| ai-device: [xpu] | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| with: | |
| persist-credentials: false | |
| - name: Install dependencies | |
| uses: ./.github/actions/install-dependencies | |
| with: | |
| cleanup-runner: "true" | |
| - name: Build docker image | |
| working-directory: application | |
| id: image-name | |
| env: | |
| DEVICE: ${{ matrix.ai-device }} | |
| run: | | |
| just build-target="${DEVICE}" docker-build-context="--no-cache" build-image | |
| IMAGE_NAME=$(just --evaluate build-target="${DEVICE}" image-name) | |
| echo "image=${IMAGE_NAME}" >> $GITHUB_OUTPUT | |
| echo "Image: ${IMAGE_NAME}" | |
| - name: Start container | |
| env: | |
| IMAGE_NAME: ${{ steps.image-name.outputs.image }} | |
| run: | | |
| CONTAINER_ID=$(docker run -d --rm --name test-container --publish 9100:9100 --env PORT=9100 --env HOST=0.0.0.0 ${IMAGE_NAME}) | |
| echo "Started container: $CONTAINER_ID" | |
| echo "CONTAINER_ID=$CONTAINER_ID" >> $GITHUB_ENV | |
| TEST_URL="localhost:9100" | |
| # Wait for container to be ready (max 60 seconds) | |
| echo "Waiting for container to be ready..." | |
| for i in {1..12}; do | |
| if curl -sf $TEST_URL/health > /dev/null 2>&1; then | |
| echo "✅ Health check passed!" | |
| break | |
| fi | |
| if [ $i -eq 12 ]; then | |
| echo "❌ Health check failed after 60 seconds" | |
| docker logs $CONTAINER_ID | |
| exit 1 | |
| fi | |
| echo "Attempt $i/12 - waiting 5 seconds..." | |
| sleep 5 | |
| done | |
| EXPECTED_TITLE="Geti Instant Learn" | |
| echo "Checking HTML title..." | |
| HTML_CONTENT=$(curl -sf $TEST_URL) | |
| if echo "$HTML_CONTENT" | grep -q "<title>.*$EXPECTED_TITLE.*</title>"; then | |
| echo "✅ HTML title check passed - found: $EXPECTED_TITLE" | |
| else | |
| echo "❌ HTML title check failed - expected title '$EXPECTED_TITLE' not found" | |
| echo "Actual HTML content:" | |
| echo "$HTML_CONTENT" | |
| exit 1 | |
| fi | |
| echo "All checks passed successfully!" | |
| - name: Run ZAP API Scan | |
| uses: zaproxy/action-api-scan@5158fe4d9d8fcc75ea204db81317cce7f9e5453d # v0.10.0 | |
| with: | |
| target: "http://localhost:9100/api/openapi.json" | |
| format: "openapi" | |
| cmd_options: "-a" | |
| allow_issue_writing: false | |
| fail_action: false | |
| artifact_name: zapapi | |
| - name: Run ZAP Full Scan | |
| uses: zaproxy/action-full-scan@3c58388149901b9a03b7718852c5ba889646c27c # v0.13.0 | |
| with: | |
| target: "http://localhost:9100" | |
| cmd_options: "-a -j" | |
| allow_issue_writing: false | |
| fail_action: false | |
| artifact_name: zapfull | |
| - name: Run Schemathesis Scan | |
| uses: schemathesis/action@806cace2053cbbac93188e1281ff7da415643160 # v3 | |
| continue-on-error: true | |
| with: | |
| schema: "http://localhost:9100/api/openapi.json" | |
| args: >- | |
| --continue-on-failure | |
| --report-dir schemathesis-report | |
| --report junit | |
| --report-junit-path schemathesis-report/junit.xml | |
| - name: Upload Schemathesis report | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 | |
| if: always() | |
| with: | |
| name: schemathesis-report-${{ matrix.ai-device }} | |
| path: schemathesis-report/junit.xml | |
| - name: Re-check container health after testing | |
| if: always() | |
| run: | | |
| TEST_URL="localhost:9100" | |
| echo "Running post-scan health check..." | |
| if curl -sf $TEST_URL/health > /dev/null 2>&1; then | |
| echo "✅ Post-scan health check passed!" | |
| else | |
| echo "❌ Post-scan health check failed" | |
| echo "=== Container logs ===" | |
| docker logs $CONTAINER_ID || true | |
| exit 1 | |
| fi | |
| - name: Save container logs to file | |
| if: always() | |
| run: | | |
| mkdir -p dast-scan-logs | |
| if [ -n "$CONTAINER_ID" ]; then | |
| echo "Saving logs for container $CONTAINER_ID" | |
| docker logs "$CONTAINER_ID" > dast-scan-logs/container-$CONTAINER_ID.log 2>&1 || true | |
| else | |
| echo "CONTAINER_ID not set; attempting to capture any matching container logs" > dast-scan-logs/container-lookup.log | |
| docker ps --filter "ancestor=geti-tune-${{ matrix.device }}" --format "{{.ID}} {{.Image}}" >> dast-scan-logs/container-lookup.log || true | |
| fi | |
| - name: Upload container logs | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 | |
| if: always() | |
| with: | |
| name: container-logs | |
| path: dast-scan-logs/ | |
| - name: Stop and Cleanup Container | |
| if: always() | |
| run: | | |
| if [ -n "${CONTAINER_ID}" ]; then | |
| echo "Stopping container ${CONTAINER_ID}..." | |
| docker stop ${CONTAINER_ID} | |
| fi |