diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8a4bb09f..27149bb2 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -16,7 +16,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - name: Set up Python uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6 @@ -38,7 +38,7 @@ jobs: --cov-fail-under=98 -v --tb=short || true - name: Upload coverage to Codecov - uses: codecov/codecov-action@3f20e214133d0983f9a10f3d63b0faf9241a3daa # v6 + uses: codecov/codecov-action@57e3a136b779b570ffcdbf80b3bdc90e7fab3de2 # v6 with: token: ${{ secrets.CODECOV_TOKEN }} files: ./coverage.xml @@ -53,12 +53,16 @@ jobs: run: | docker run --rm -w /app/dns-server squawk-dns-server:test python3.13 -m pytest tests/ -v --tb=short + - name: Clean up Playwright artifacts + if: always() + run: rm -rf /tmp/playwright-squawk + docker-multi-build: runs-on: ubuntu-latest needs: build-and-test steps: - - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - name: Build unified Docker image - DNS Server run: | @@ -87,7 +91,7 @@ jobs: contents: read steps: - - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - name: Set up Python for bandit uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6 @@ -95,7 +99,7 @@ jobs: python-version: ${{ env.PYTHON_VERSION }} - name: Set up Go for gosec - uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff # v5 + uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6 with: go-version: ${{ env.GO_VERSION }} @@ -106,13 +110,13 @@ jobs: continue-on-error: true - name: Run gosec (Go security scanner) - uses: securego/gosec@5e5517beec77b8228ba43ec8d7cc22d82ed31924 # v2.25.0 + uses: securego/gosec@223e19b8856e00f02cc67804499a83f77e208f3c # v2.25.0 with: args: '-no-fail -fmt json -out gosec-results.json ./...' continue-on-error: true - name: Run Trivy vulnerability scanner - uses: aquasecurity/trivy-action@57a97c7e7821a5776cebc9bb87c984fa69cba8f1 # v0.35.0 + uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0 with: version: 'v0.69.3' scan-type: 'fs' @@ -121,7 +125,7 @@ jobs: output: 'trivy-results.sarif' - name: Upload Trivy results to GitHub Security - uses: github/codeql-action/upload-sarif@ebcb5b36ded6beda4ceefea6a8bc4cc885255bb3 # v3 + uses: github/codeql-action/upload-sarif@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4 if: always() with: sarif_file: 'trivy-results.sarif' diff --git a/.github/workflows/cron.yml b/.github/workflows/cron.yml index 76cfb420..d7bdcbd6 100644 --- a/.github/workflows/cron.yml +++ b/.github/workflows/cron.yml @@ -20,22 +20,22 @@ jobs: contents: read steps: - name: Check out the repo - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - name: Set up QEMU - uses: docker/setup-qemu-action@29109295f81e9b11de45568b7b44d28e85c0b8e7 # v3 + uses: docker/setup-qemu-action@ce360397dd3f832beb865e1373c09c0e9f86d70a # v4 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@b5ca514318bd6ebba01cdb9b2d9a7ac3b0c11b09 # v3 + uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4 - name: Log in to Docker Hub - uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf5ca # v3 + uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4 with: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} - name: Log in to the Container registry - uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf5ca # v3 + uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4 with: registry: ghcr.io username: ${{ github.actor }} @@ -48,7 +48,7 @@ jobs: echo "tag=$PLATFORM_TAG" >> $GITHUB_OUTPUT - name: Build Docker image for ${{ matrix.platform }} - uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6 + uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7 with: context: . platforms: ${{ matrix.platform }} @@ -60,7 +60,7 @@ jobs: cache-to: type=gha,mode=max,scope=${{ matrix.platform }} - name: Build Docker image with static tags for ${{ matrix.platform }} - uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6 + uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7 with: context: . platforms: ${{ matrix.platform }} @@ -81,23 +81,23 @@ jobs: contents: read steps: - name: Check out the repo - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - name: Log in to Docker Hub - uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf5ca # v3 + uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4 with: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} - name: Log in to the Container registry - uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf5ca # v3 + uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Set up Docker Buildx - uses: docker/setup-buildx-action@b5ca514318bd6ebba01cdb9b2d9a7ac3b0c11b09 # v3 + uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4 - name: Merge manifests for cron tags run: | diff --git a/.github/workflows/gitstream.yml b/.github/workflows/gitstream.yml index 7fa77faf..0f898388 100644 --- a/.github/workflows/gitstream.yml +++ b/.github/workflows/gitstream.yml @@ -35,7 +35,7 @@ jobs: name: gitStream workflow automation steps: - name: Evaluate Rules - uses: linear-b/gitstream-github-action@v1 + uses: linear-b/gitstream-github-action@825397d4e97fe47dd3f27215dbd4e8e39d2e7a4b # v2.0.2 id: rules-engine with: full_repository: ${{ github.event.inputs.full_repository }} diff --git a/.github/workflows/go-client-release.yml b/.github/workflows/go-client-release.yml index 77bafb0a..67d1085b 100644 --- a/.github/workflows/go-client-release.yml +++ b/.github/workflows/go-client-release.yml @@ -23,15 +23,15 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout code - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - name: Set up Go - uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff # v5 + uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6 with: go-version: ${{ env.GO_VERSION }} - name: Cache Go modules - uses: actions/cache@v4 + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5 with: path: | ~/.cache/go-build @@ -57,7 +57,7 @@ jobs: - name: Security scan working-directory: dns-client-go run: | - go install github.com/securego/gosec/v2/cmd/gosec@latest + go install github.com/securego/gosec/v2/cmd/gosec@v2.22.11 make security # Build and release job - triggered by version tags @@ -75,7 +75,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 with: fetch-depth: 0 @@ -89,10 +89,10 @@ jobs: echo "BINARY_NAME=squawk" >> $GITHUB_OUTPUT - name: Set up Docker Buildx - uses: docker/setup-buildx-action@b5ca514318bd6ebba01cdb9b2d9a7ac3b0c11b09 # v3 + uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4 - name: Set up QEMU - uses: docker/setup-qemu-action@29109295f81e9b11de45568b7b44d28e85c0b8e7 # v3 + uses: docker/setup-qemu-action@ce360397dd3f832beb865e1373c09c0e9f86d70a # v4 - name: Build Go binary for platform working-directory: dns-client-go @@ -119,7 +119,7 @@ jobs: ' - name: Upload binary artifacts - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 with: name: binaries-${{ matrix.platform == 'linux/amd64' && 'amd64' || 'arm64' }} path: dns-client-go/build/ @@ -138,7 +138,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 with: fetch-depth: 0 @@ -152,20 +152,20 @@ jobs: echo "BINARY_NAME=squawk" >> $GITHUB_OUTPUT - name: Set up Docker Buildx - uses: docker/setup-buildx-action@b5ca514318bd6ebba01cdb9b2d9a7ac3b0c11b09 # v3 + uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4 - name: Set up QEMU - uses: docker/setup-qemu-action@29109295f81e9b11de45568b7b44d28e85c0b8e7 # v3 + uses: docker/setup-qemu-action@ce360397dd3f832beb865e1373c09c0e9f86d70a # v4 - name: Log in to GitHub Container Registry - uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf5ca # v3 + uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Build and push Docker image (per platform) - uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6 + uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7 env: VERSION: ${{ steps.version.outputs.VERSION }} BINARY_NAME: ${{ steps.version.outputs.BINARY_NAME }} @@ -202,7 +202,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 with: fetch-depth: 0 @@ -215,14 +215,14 @@ jobs: echo "VERSION=$VERSION" >> $GITHUB_OUTPUT - name: Log in to GitHub Container Registry - uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf5ca # v3 + uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Set up Docker Buildx - uses: docker/setup-buildx-action@b5ca514318bd6ebba01cdb9b2d9a7ac3b0c11b09 # v3 + uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4 - name: Merge Docker manifests env: @@ -249,7 +249,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 with: fetch-depth: 0 @@ -263,7 +263,7 @@ jobs: echo "BINARY_NAME=squawk" >> $GITHUB_OUTPUT - name: Download all binary artifacts - uses: actions/download-artifact@v4 + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8 - name: Create Release Packages env: @@ -284,7 +284,7 @@ jobs: sha256sum * > SHA256SUMS - name: Upload Release Assets - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 with: name: release-packages path: release/packages/ @@ -299,7 +299,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 with: fetch-depth: 0 @@ -312,7 +312,7 @@ jobs: echo "VERSION=$VERSION" >> $GITHUB_OUTPUT - name: Download release packages - uses: actions/download-artifact@v4 + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8 with: name: release-packages diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index d06e8d29..4ce0c96a 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -43,13 +43,13 @@ jobs: - linux/arm64 steps: - name: Check out the repo - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3 + uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4 - name: Log in to Container registry - uses: docker/login-action@f054a8b539a109f9f41c372932f1ae047eff08c9 + uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4 with: registry: ghcr.io username: ${{ github.actor }} @@ -57,13 +57,13 @@ jobs: - name: Extract metadata id: meta - uses: docker/metadata-action@98669ae865ea3cffbcbaa878cf57c20bbf1c6c38 + uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf # v6 with: images: ${{ env.IMAGE }} - name: Build and push by digest id: build - uses: docker/build-push-action@ad44023a93711e3deb337508980b4b5e9bcdc5dc + uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7 with: context: . platforms: ${{ matrix.platform }} @@ -100,10 +100,10 @@ jobs: merge-multiple: true - name: Set up Docker Buildx - uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3 + uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4 - name: Log in to Container registry - uses: docker/login-action@f054a8b539a109f9f41c372932f1ae047eff08c9 + uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4 with: registry: ghcr.io username: ${{ github.actor }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 52b151ea..37691f06 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -21,13 +21,13 @@ jobs: - linux/arm64 steps: - name: Check out the repo - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3 + uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4 - name: Log in to Container registry - uses: docker/login-action@f054a8b539a109f9f41c372932f1ae047eff08c9 + uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4 with: registry: ghcr.io username: ${{ github.actor }} @@ -35,7 +35,7 @@ jobs: - name: Extract metadata id: meta - uses: docker/metadata-action@98669ae865ea3cffbcbaa878cf57c20bbf1c6c38 + uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf # v6 with: images: ${{ env.IMAGE }} tags: | @@ -45,7 +45,7 @@ jobs: - name: Build and push by digest id: build - uses: docker/build-push-action@ad44023a93711e3deb337508980b4b5e9bcdc5dc + uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7 with: context: . platforms: ${{ matrix.platform }} @@ -82,10 +82,10 @@ jobs: merge-multiple: true - name: Set up Docker Buildx - uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3 + uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4 - name: Log in to Container registry - uses: docker/login-action@f054a8b539a109f9f41c372932f1ae047eff08c9 + uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4 with: registry: ghcr.io username: ${{ github.actor }} @@ -93,7 +93,7 @@ jobs: - name: Extract metadata id: meta - uses: docker/metadata-action@98669ae865ea3cffbcbaa878cf57c20bbf1c6c38 + uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf # v6 with: images: ${{ env.IMAGE }} tags: | diff --git a/.github/workflows/server-release.yml b/.github/workflows/server-release.yml index 7db44275..12200e7a 100644 --- a/.github/workflows/server-release.yml +++ b/.github/workflows/server-release.yml @@ -16,7 +16,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout code - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - name: Set up Python uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6 @@ -38,7 +38,7 @@ jobs: --cov-fail-under=98 -v --tb=short || true - name: Upload coverage to Codecov - uses: codecov/codecov-action@3f20e214133d0983f9a10f3d63b0faf9241a3daa # v6 + uses: codecov/codecov-action@57e3a136b779b570ffcdbf80b3bdc90e7fab3de2 # v6 with: token: ${{ secrets.CODECOV_TOKEN }} files: ./coverage.xml @@ -90,18 +90,18 @@ jobs: packages: write steps: - name: Checkout code - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 with: fetch-depth: 0 - name: Set up QEMU - uses: docker/setup-qemu-action@29109295f81e9b11de45568b7b44d28e85c0b8e7 # v3 + uses: docker/setup-qemu-action@ce360397dd3f832beb865e1373c09c0e9f86d70a # v4 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@b5ca514318bd6ebba01cdb9b2d9a7ac3b0c11b09 # v3 + uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4 - name: Log in to GitHub Container Registry - uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf5ca # v3 + uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4 with: registry: ghcr.io username: ${{ github.actor }} @@ -123,7 +123,7 @@ jobs: echo "tag=$PLATFORM_TAG" >> $GITHUB_OUTPUT - name: Build and push ${{ matrix.service }} Docker image for ${{ matrix.platform }} - uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6 + uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7 with: context: . target: ${{ matrix.target }} @@ -150,19 +150,19 @@ jobs: packages: write steps: - name: Checkout code - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 with: fetch-depth: 0 - name: Log in to GitHub Container Registry - uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf5ca # v3 + uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Set up Docker Buildx - uses: docker/setup-buildx-action@b5ca514318bd6ebba01cdb9b2d9a7ac3b0c11b09 # v3 + uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4 - name: Extract version from tag id: version @@ -218,7 +218,7 @@ jobs: contents: write steps: - name: Checkout code - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 with: fetch-depth: 0 diff --git a/.github/workflows/version-monitor.yml b/.github/workflows/version-monitor.yml index 78fd4b90..82040f04 100644 --- a/.github/workflows/version-monitor.yml +++ b/.github/workflows/version-monitor.yml @@ -21,7 +21,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 with: fetch-depth: 2 @@ -65,7 +65,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - name: Verify DNS Server version support run: | @@ -101,7 +101,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - name: Set up Python for bandit uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6 @@ -109,7 +109,7 @@ jobs: python-version: ${{ env.PYTHON_VERSION }} - name: Set up Go for gosec - uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff # v5 + uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6 with: go-version: ${{ env.GO_VERSION }} @@ -120,7 +120,7 @@ jobs: continue-on-error: true - name: Run gosec - uses: securego/gosec@5e5517beec77b8228ba43ec8d7cc22d82ed31924 # v2.25.0 + uses: securego/gosec@223e19b8856e00f02cc67804499a83f77e208f3c # v2.25.0 with: args: '-no-fail -fmt json -out gosec-results.json ./...' continue-on-error: true diff --git a/k8s/manifests/httproute.yaml b/k8s/manifests/httproute.yaml new file mode 100644 index 00000000..a2f2e36a --- /dev/null +++ b/k8s/manifests/httproute.yaml @@ -0,0 +1,41 @@ +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: squawk + namespace: squawk +spec: + parentRefs: + - name: shared + namespace: gateway + sectionName: https + hostnames: + - "squawk.penguintech.cloud" + rules: + - matches: + - path: + type: PathPrefix + value: /api/v1 + backendRefs: + - name: flask-api + port: 8000 + - matches: + - path: + type: PathPrefix + value: /dns-query + backendRefs: + - name: dns-server + port: 8080 + - matches: + - path: + type: PathPrefix + value: /health + backendRefs: + - name: dns-server + port: 8080 + - matches: + - path: + type: PathPrefix + value: / + backendRefs: + - name: webui + port: 3000 diff --git a/tests/e2e/dns-webui.spec.ts b/tests/e2e/dns-webui.spec.ts index 030dda84..8a52d6d8 100644 --- a/tests/e2e/dns-webui.spec.ts +++ b/tests/e2e/dns-webui.spec.ts @@ -1,13 +1,66 @@ -import { test, expect } from '@playwright/test'; +import { test, expect, Browser, BrowserContext } from '@playwright/test'; +import path from 'path'; +import fs from 'fs'; const BASE = process.env.DNS_WEBUI_URL ?? 'http://localhost:5173'; +const TEST_USER = process.env.TEST_USER ?? 'admin@localhost'; +const TEST_PASS = process.env.TEST_PASS ?? 'admin123'; +const AUTH_FILE = path.join(__dirname, '.auth', 'user.json'); + +const servicesRunning = () => process.env.CI_SERVICES_RUNNING === 'true'; + +// Protected console routes every authenticated user should reach without JS errors. +const PROTECTED_ROUTES = [ + '/dns_console/index', + '/dns_console/tokens', + '/dns_console/domains', + '/dns_console/permissions', + '/dns_console/blacklist', + '/dns_console/certificates', + '/dns_console/logs', +] as const; + +// --------------------------------------------------------------------------- +// Auth setup — log in once, reuse storage state for protected-route tests +// --------------------------------------------------------------------------- + +let authContext: BrowserContext | null = null; + +test.beforeAll(async ({ browser }: { browser: Browser }) => { + if (!servicesRunning()) return; + + fs.mkdirSync(path.dirname(AUTH_FILE), { recursive: true }); + + authContext = await browser.newContext(); + const page = await authContext.newPage(); + + await page.goto(`${BASE}/login`); + await page + .locator('input[type="email"], input[name="username"], input[name="email"]') + .first() + .fill(TEST_USER); + await page.locator('input[type="password"]').fill(TEST_PASS); + await page.keyboard.press('Enter'); + + // Wait for redirect away from login; swallow if login fails (no live server) + await page.waitForURL(/\/dns_console\//, { timeout: 10_000 }).catch(() => {}); + + await authContext.storageState({ path: AUTH_FILE }); + await page.close(); +}); + +test.afterAll(async () => { + await authContext?.close(); + authContext = null; +}); + +// --------------------------------------------------------------------------- +// Unauthenticated smoke tests +// --------------------------------------------------------------------------- test.describe('DNS WebUI — page loads', () => { test('login page loads without JS errors', async ({ page }) => { - test.skip( - process.env.CI_SERVICES_RUNNING !== 'true', - 'Services not running — skipping live test' - ); + test.skip(!servicesRunning(), 'Services not running — skipping live test'); const errors: string[] = []; page.on('pageerror', (e) => errors.push(e.message)); @@ -17,29 +70,20 @@ test.describe('DNS WebUI — page loads', () => { }); test('unauthenticated navigation redirects to login', async ({ page }) => { - test.skip( - process.env.CI_SERVICES_RUNNING !== 'true', - 'Services not running — skipping live test' - ); + test.skip(!servicesRunning(), 'Services not running — skipping live test'); await page.goto(`${BASE}/`); await page.waitForURL(`${BASE}/login`); await expect(page).toHaveURL(/\/login/); }); test('login page has password field', async ({ page }) => { - test.skip( - process.env.CI_SERVICES_RUNNING !== 'true', - 'Services not running — skipping live test' - ); + test.skip(!servicesRunning(), 'Services not running — skipping live test'); await page.goto(`${BASE}/login`); await expect(page.locator('input[type="password"]')).toBeVisible(); }); test('invalid credentials show error', async ({ page }) => { - test.skip( - process.env.CI_SERVICES_RUNNING !== 'true', - 'Services not running — skipping live test' - ); + test.skip(!servicesRunning(), 'Services not running — skipping live test'); await page.goto(`${BASE}/login`); const emailOrUser = page.locator('input[type="email"], input[name="username"], input[name="email"]').first(); const password = page.locator('input[type="password"]'); @@ -52,3 +96,56 @@ test.describe('DNS WebUI — page loads', () => { await expect(page).toHaveURL(/\/login/); }); }); + +// --------------------------------------------------------------------------- +// Authenticated smoke tests — protected routes +// --------------------------------------------------------------------------- + +test.describe('DNS WebUI — protected pages', () => { + test.use({ storageState: AUTH_FILE }); + + for (const route of PROTECTED_ROUTES) { + test(`${route} loads without JS errors`, async ({ page }) => { + test.skip(!servicesRunning(), 'Services not running — skipping live test'); + + const errors: string[] = []; + page.on('pageerror', (e) => errors.push(e.message)); + + await page.goto(`${BASE}${route}`); + + // Should not be bounced back to login + await expect(page).not.toHaveURL(/\/login/); + expect(errors).toHaveLength(0); + }); + } +}); + +// --------------------------------------------------------------------------- +// Form modal tests — open, empty submit triggers validation error +// --------------------------------------------------------------------------- + +test.describe('DNS WebUI — form modals', () => { + test.use({ storageState: AUTH_FILE }); + + test('new token form shows validation error on empty submit', async ({ page }) => { + test.skip(!servicesRunning(), 'Services not running — skipping live test'); + + await page.goto(`${BASE}/dns_console/tokens/new`); + await page.locator('button[type="submit"], [data-testid="submit"]').click(); + + await expect( + page.locator('.error, .invalid-feedback, [data-testid="form-error"], .flash-error, .alert-danger').first() + ).toBeVisible({ timeout: 5_000 }); + }); + + test('new domain form shows validation error on empty submit', async ({ page }) => { + test.skip(!servicesRunning(), 'Services not running — skipping live test'); + + await page.goto(`${BASE}/dns_console/domains/new`); + await page.locator('button[type="submit"], [data-testid="submit"]').click(); + + await expect( + page.locator('.error, .invalid-feedback, [data-testid="form-error"], .flash-error, .alert-danger').first() + ).toBeVisible({ timeout: 5_000 }); + }); +}); diff --git a/tests/smoke/test_dns_server_api.py b/tests/smoke/test_dns_server_api.py index d66a49f0..73fdddc6 100644 --- a/tests/smoke/test_dns_server_api.py +++ b/tests/smoke/test_dns_server_api.py @@ -13,6 +13,16 @@ class TestDNSServerHealth: """Test DNS server health endpoints""" + def test_ready_endpoint(self, config, http_session): + """GET /ready returns readiness status""" + url = f"{config.dns_server_url}/ready" + + response = http_session.get(url, timeout=config.request_timeout) + + assert response.status_code == 200 + data = response.json() + assert data.get("status") in ("ready", "ok", "healthy") + def test_health_endpoint(self, config, http_session): """GET /health returns healthy status""" url = f"{config.dns_server_url}/health" diff --git a/tests/smoke/test_web_console_api.py b/tests/smoke/test_web_console_api.py index 557c1c7d..c402395f 100644 --- a/tests/smoke/test_web_console_api.py +++ b/tests/smoke/test_web_console_api.py @@ -7,6 +7,22 @@ import requests +@pytest.mark.smoke +@pytest.mark.api +class TestWebConsoleHealth: + """Test web console health and readiness endpoints""" + + def test_ready_endpoint(self, config, fresh_http_session): + """GET /ready returns readiness status""" + url = f"{config.web_console_url}/ready" + + response = fresh_http_session.get(url, timeout=config.request_timeout) + + assert response.status_code == 200 + data = response.json() + assert data.get("status") in ("ready", "ok", "healthy") + + @pytest.mark.smoke @pytest.mark.api @pytest.mark.auth @@ -161,6 +177,55 @@ def test_clear_logs_endpoint(self, authenticated_client): assert "success" in data +@pytest.mark.smoke +@pytest.mark.api +@pytest.mark.auth +class TestQueryFilterParams: + """Test filter and pagination params on query/domain/log endpoints""" + + def test_queries_pagination_params(self, authenticated_client): + """GET /api/v1/queries supports page and limit params""" + response = authenticated_client.get("/api/v1/queries", params={"page": 1, "limit": 10}) + + assert response.status_code == 200 + data = response.json() + assert "queries" in data + assert len(data["queries"]) <= 10 + + def test_queries_empty_far_page(self, authenticated_client): + """GET /api/v1/queries with a far-out page returns empty list, not error""" + response = authenticated_client.get("/api/v1/queries", params={"page": 99999, "limit": 10}) + + assert response.status_code == 200 + data = response.json() + items = data.get("queries", data.get("items", data.get("data", []))) + assert isinstance(items, list) + + def test_domains_filter_active(self, authenticated_client): + """GET /api/v1/domains?active=true returns only active domains""" + response = authenticated_client.get("/api/v1/domains", params={"active": "true"}) + + assert response.status_code == 200 + + def test_domains_filter_inactive(self, authenticated_client): + """GET /api/v1/domains?active=false returns only inactive domains""" + response = authenticated_client.get("/api/v1/domains", params={"active": "false"}) + + assert response.status_code == 200 + + def test_logs_pagination_params(self, authenticated_client): + """GET /api/v1/logs supports page and limit params""" + response = authenticated_client.get("/api/v1/logs", params={"page": 1, "limit": 50}) + + assert response.status_code == 200 + + def test_ioc_feed_invalid_id_returns_404(self, authenticated_client): + """GET /api/v1/ioc/feeds/ returns 404""" + response = authenticated_client.get("/api/v1/ioc/feeds/nonexistent-feed-id-000") + + assert response.status_code == 404 + + @pytest.mark.smoke @pytest.mark.api class TestUnauthenticatedAPIAccess: