Update to latest version of mudcerts #129
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
| name: ci | |
| on: | |
| push: | |
| branches: [main] | |
| paths-ignore: | |
| - '**.md' | |
| - 'images/**' | |
| - 'LICENSE' | |
| - 'CODE_OF_CONDUCT.md' | |
| - 'CONTRIBUTING.md' | |
| pull_request: | |
| paths-ignore: | |
| - '**.md' | |
| - 'images/**' | |
| - 'LICENSE' | |
| - 'CODE_OF_CONDUCT.md' | |
| - 'CONTRIBUTING.md' | |
| concurrency: | |
| group: ci-${{ github.ref }} | |
| cancel-in-progress: true | |
| jobs: | |
| lint-and-unit: | |
| name: Lint + unit | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 10 | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - name: Dockerfile guard rail — no secrets in image layers | |
| # T-05/T-18 defence-in-depth: fail the build if the Dockerfile | |
| # ever tries to bake a secret into an image layer. Secrets | |
| # must arrive at container start via a compose `secrets:` | |
| # block, a runtime bind-mount, or an external secret manager | |
| # -- never via COPY, ENV, or ARG. The bind-mounted | |
| # `config.ini` handled by docker-compose.yml is fine; this | |
| # check only fires on `COPY .../config.ini` lines in | |
| # Dockerfile itself. | |
| run: | | |
| set -e | |
| bad=$(grep -nEi \ | |
| -e '^[[:space:]]*COPY[[:space:]].*config\.ini' \ | |
| -e '^[[:space:]]*ENV[[:space:]].*(SECRET|CLIENT_SECRET|TOKEN|PASSWORD|PASSPHRASE|API_?KEY|CREDENTIAL|PRIVATE_KEY)([[:space:]=]|$)' \ | |
| -e '^[[:space:]]*ARG[[:space:]].*(SECRET|CLIENT_SECRET|TOKEN|PASSWORD|PASSPHRASE|API_?KEY|CREDENTIAL|PRIVATE_KEY)([[:space:]=]|$)' \ | |
| Dockerfile || true) | |
| if [ -n "$bad" ]; then | |
| echo "::error title=Dockerfile secret leak::Secrets must not be baked into image layers." >&2 | |
| echo "Offending lines:" >&2 | |
| echo "$bad" >&2 | |
| echo "" >&2 | |
| echo "Fix: inject secrets at container start via a compose" >&2 | |
| echo "'secrets:' block, a runtime bind-mount, or an external" >&2 | |
| echo "secret manager. See GITHUB_REMEDIATION_PLAN.md and the" >&2 | |
| echo "discussion in the security notes for details." >&2 | |
| exit 1 | |
| fi | |
| echo "OK: no secret-shaped tokens or config.ini COPYs in Dockerfile." | |
| - uses: actions/setup-python@v7 | |
| with: | |
| python-version: '3.12' | |
| cache: pip | |
| cache-dependency-path: gitmud/requirements.txt | |
| - uses: actions/setup-node@v7 | |
| with: | |
| node-version: '20' | |
| - name: Install Python deps | |
| run: | | |
| python -m pip install --upgrade pip | |
| pip install -r gitmud/requirements.txt | |
| - name: Python syntax check | |
| run: python -m compileall -q gitmud/gitmud mudgen_pcap.py | |
| - name: JS syntax sanity | |
| run: | | |
| for f in assets/js/dom-safe.js \ | |
| assets/js/mudmaker.js \ | |
| assets/js/mudmaker-reload.js \ | |
| assets/js/mudmaker-visualizer.js \ | |
| assets/js/omud.js \ | |
| assets/js/tabs.js \ | |
| assets/js/util.js; do | |
| node --check "$f" | |
| done | |
| - name: Unit tests | |
| env: | |
| GITMUD_CONFIG: ${{ github.workspace }}/gitmud/config.ini | |
| run: | | |
| set -e | |
| mkdir -p gitmud | |
| cat > "$GITMUD_CONFIG" <<'CFG' | |
| [github] | |
| client_id = ci-stub | |
| client_secret = ci-stub | |
| [flask] | |
| secret_key = ci-only-not-secret | |
| [storage] | |
| db_path = /tmp/gitmud-ci.sqlite | |
| [security] | |
| allowed_origins = | |
| session_ttl_seconds = 3600 | |
| janitor_interval_seconds = 3600 | |
| legacy_mudurl_fallback = true | |
| CFG | |
| python tests/test_pcap_sanitise.py | |
| python tests/test_publish_multi.py | |
| python tests/test_acl_dedupe.py | |
| python tests/test_pcap_merge.py | |
| python tests/test_ref_sanitise.py | |
| python tests/test_session_bearer.py | |
| python tests/test_oauth_complete.py | |
| docker-build: | |
| name: Build docker images | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 30 | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - uses: docker/setup-buildx-action@v4 | |
| - name: Validate docker-compose | |
| run: docker compose config --quiet | |
| - name: Verify MUDCERTS_REF is pinned to a commit SHA (T-28) | |
| # Phase 5: reject non-SHA pins so a future accidental | |
| # ``ARG MUDCERTS_REF=main`` cannot silently ship. A 40-char | |
| # lowercase hex string is the only accepted form. | |
| run: | | |
| ref=$(awk '/^ARG MUDCERTS_REF=/{sub(/^ARG MUDCERTS_REF=/,"",$0); print; exit}' Dockerfile) | |
| echo "MUDCERTS_REF = $ref" | |
| if ! echo "$ref" | grep -Eq '^[0-9a-f]{40}$'; then | |
| echo "MUDCERTS_REF must be a 40-char lowercase hex commit SHA" >&2 | |
| exit 1 | |
| fi | |
| - name: Build mudzipserver | |
| uses: docker/build-push-action@v7 | |
| with: | |
| context: . | |
| target: mudzipserver | |
| tags: mudmaker-mudzipserver:ci | |
| outputs: type=docker,dest=/tmp/mudzipserver.tar | |
| cache-from: type=gha,scope=mudzipserver | |
| cache-to: type=gha,scope=mudzipserver,mode=max | |
| - name: Build gitmud | |
| uses: docker/build-push-action@v7 | |
| with: | |
| context: . | |
| target: gitmud | |
| tags: mudmaker-gitmud:ci | |
| outputs: type=docker,dest=/tmp/gitmud.tar | |
| cache-from: type=gha,scope=gitmud | |
| cache-to: type=gha,scope=gitmud,mode=max | |
| - name: Build mudmaker (httpd) | |
| uses: docker/build-push-action@v7 | |
| with: | |
| context: . | |
| tags: mudmaker:ci | |
| outputs: type=docker,dest=/tmp/mudmaker.tar | |
| cache-from: type=gha,scope=mudmaker | |
| cache-to: type=gha,scope=mudmaker,mode=max | |
| - name: Upload image tarballs | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: docker-images-${{ github.sha }} | |
| path: /tmp/*.tar | |
| retention-days: 1 | |
| if-no-files-found: error | |
| integration: | |
| name: Integration (live stack) | |
| runs-on: ubuntu-latest | |
| needs: docker-build | |
| timeout-minutes: 20 | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - uses: actions/setup-python@v7 | |
| with: | |
| python-version: '3.12' | |
| cache: pip | |
| cache-dependency-path: gitmud/requirements.txt | |
| - name: Install Python deps | |
| run: | | |
| python -m pip install --upgrade pip | |
| pip install -r gitmud/requirements.txt | |
| pip install playwright | |
| - name: Download docker image tarballs | |
| uses: actions/download-artifact@v8 | |
| with: | |
| name: docker-images-${{ github.sha }} | |
| path: /tmp/ | |
| - name: Load images and retag as :latest | |
| run: | | |
| set -e | |
| for t in /tmp/*.tar; do | |
| docker load -i "$t" | |
| done | |
| docker tag mudmaker-mudzipserver:ci mudmaker-mudzipserver:latest | |
| docker tag mudmaker-gitmud:ci mudmaker-gitmud:latest | |
| docker tag mudmaker:ci mudmaker:latest | |
| docker image ls | |
| - name: Stub gitmud config | |
| run: | | |
| mkdir -p gitmud | |
| cat > gitmud/config.ini <<'CFG' | |
| [github] | |
| client_id = ci-stub | |
| client_secret = ci-stub | |
| [flask] | |
| secret_key = ci-only-not-secret | |
| [storage] | |
| db_path = /var/lib/gitmud/tokens.sqlite | |
| [security] | |
| allowed_origins = | |
| session_ttl_seconds = 3600 | |
| janitor_interval_seconds = 3600 | |
| legacy_mudurl_fallback = true | |
| CFG | |
| - name: Stage SmarterCoffee pcap fixtures | |
| run: | | |
| mkdir -p tmp/captures_IoT-Sentinel | |
| cp -r tests/fixtures/SmarterCoffee tmp/captures_IoT-Sentinel/ | |
| - name: Bring up stack | |
| run: | | |
| docker compose up -d | |
| # wait up to 60 s for mudmaker.html (httpd) to be served | |
| for i in $(seq 1 30); do | |
| if curl -fsS -o /dev/null http://127.0.0.1:8081/mudmaker.html; then | |
| echo "mudmaker up after ${i} attempts" | |
| break | |
| fi | |
| sleep 2 | |
| done | |
| # wait up to 60 s for /pcap2mud (gitmud, via the proxy) to answer. | |
| # A bare GET should return 405 once the route is registered. | |
| for i in $(seq 1 30); do | |
| code=$(curl -sS -o /dev/null -w '%{http_code}' http://127.0.0.1:8081/pcap2mud || true) | |
| if [ "$code" = "405" ] || [ "$code" = "400" ]; then | |
| echo "gitmud up after ${i} attempts (code ${code})" | |
| exit 0 | |
| fi | |
| sleep 2 | |
| done | |
| echo "stack did not come up in time" | |
| docker compose ps | |
| docker compose logs --no-color | |
| exit 1 | |
| - name: pcap2mud smoke (SmarterCoffee) | |
| run: python tests/smoke_smartercoffee.py | |
| - name: Signing smoke (Chrome, headless) | |
| env: | |
| MUDMAKER_HEADLESS: '1' | |
| MUDMAKER_ARTIFACT_DIR: ${{ github.workspace }}/_artifacts | |
| run: python tests/test_signing_chrome.py | |
| - name: Drag-drop smoke (Chrome, headless) | |
| env: | |
| MUDMAKER_HEADLESS: '1' | |
| run: python tests/test_dragdrop_chrome.py | |
| - name: Capture compose logs | |
| if: always() | |
| run: | | |
| mkdir -p _artifacts | |
| docker compose logs --no-color > _artifacts/compose.log || true | |
| docker compose ps > _artifacts/compose-ps.txt || true | |
| - name: Upload artifacts on failure | |
| if: failure() | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: integration-artifacts-${{ github.sha }} | |
| path: _artifacts/ | |
| retention-days: 7 | |
| if-no-files-found: ignore | |
| - name: Tear down | |
| if: always() | |
| run: docker compose down -v |