Skip to content

[TASK] Add keyboard navigation tests for report comparison view #923 #10

[TASK] Add keyboard navigation tests for report comparison view #923

[TASK] Add keyboard navigation tests for report comparison view #923 #10

name: Docker Hardening Checks
on:
push:
branches: [main]
paths:
- "backend/Dockerfile"
- "frontend/Dockerfile"
- ".github/workflows/docker-hardening.yml"
pull_request:
branches: [main]
paths:
- "backend/Dockerfile"
- "frontend/Dockerfile"
- ".github/workflows/docker-hardening.yml"
workflow_dispatch:
jobs:
build:
name: Build ${{ matrix.service }} image
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
include:
- service: backend
context: ./backend
dockerfile: ./backend/Dockerfile
image: secuscan-backend
- service: frontend
context: ./frontend
dockerfile: ./frontend/Dockerfile
image: secuscan-frontend
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build ${{ matrix.service }} image
uses: docker/build-push-action@v6
with:
context: ${{ matrix.context }}
file: ${{ matrix.dockerfile }}
push: false
load: true
tags: ${{ matrix.image }}:ci
cache-from: type=gha,scope=${{ matrix.service }}
cache-to: type=gha,scope=${{ matrix.service }},mode=max
- name: Save image as tar
run: docker save ${{ matrix.image }}:ci -o /tmp/${{ matrix.image }}.tar
- name: Upload image artifact
uses: actions/upload-artifact@v4
with:
name: ${{ matrix.image }}-tar
path: /tmp/${{ matrix.image }}.tar
retention-days: 1
hardening-check:
name: Hardening checks – ${{ matrix.service }}
runs-on: ubuntu-latest
needs: build
strategy:
fail-fast: false
matrix:
include:
- service: backend
image: secuscan-backend
- service: frontend
image: secuscan-frontend
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Download image artifact
uses: actions/download-artifact@v4
with:
name: ${{ matrix.image }}-tar
path: /tmp
- name: Load image
run: docker load -i /tmp/${{ matrix.image }}.tar
# Non-root user check
- name: Assert container does NOT run as root
run: |
WHOAMI=$(docker run --rm ${{ matrix.image }}:ci whoami 2>/dev/null || true)
UID_VAL=$(docker run --rm ${{ matrix.image }}:ci id -u 2>/dev/null || echo "0")
echo "Container user: ${WHOAMI} (UID=${UID_VAL})"
if [ "${UID_VAL}" = "0" ]; then
echo "FAIL: ${{ matrix.service }} container runs as root (UID 0)."
echo " Add a non-root USER instruction to the Dockerfile."
exit 1
fi
echo "PASS: running as non-root user '${WHOAMI}' (UID=${UID_VAL})"
# No SUID/SGID binaries
- name: Check for unexpected SUID/SGID binaries
run: |
SUID_FILES=$(docker run --rm --entrypoint find ${{ matrix.image }}:ci \
/ -xdev \( -perm -4000 -o -perm -2000 \) -type f 2>/dev/null || true)
if [ -n "${SUID_FILES}" ]; then
echo "WARNING: SUID/SGID binaries found in ${{ matrix.service }} image:"
echo "${SUID_FILES}"
# Warn but don't fail – some base images include ping/su; document if intentional
else
echo "PASS: No unexpected SUID/SGID binaries."
fi
# No secrets baked into image
- name: Scan image layers for secrets (Trivy secret scanner)
uses: aquasecurity/trivy-action@v0.36.0
with:
image-ref: ${{ matrix.image }}:ci
format: table
exit-code: "1"
scanners: secret
severity: CRITICAL,HIGH,MEDIUM
# Dockerfile lint (hadolint)
- name: Lint ${{ matrix.service }} Dockerfile with hadolint
uses: hadolint/hadolint-action@v3.1.0
with:
dockerfile: ./${{ matrix.service }}/Dockerfile
failure-threshold: error