#!/usr/bin/env bash set -euo pipefail # Security: Validate input parameters to prevent command injection sanitized_input() { local input="$1" # Check for potentially dangerous characters/commands case "$input" in *[\;\|\&\`\$]*) echo "Error: Invalid input detected: $input" >&2 exit 1 ;; esac } # Validate dependencies if ! command -v docker &> /dev/null; then echo "Error: docker is required but not installed." >&2 exit 1 fi if ! docker buildx version &> /dev/null; then echo "Error: docker buildx is required but not available." >&2 exit 1 fi IMAGE_NAME="tsysdevstack-toolboxstack-toolbox-qadocker" SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" # Sanitize user input USER_ID="${USER_ID_OVERRIDE:-$(id -u)}" sanitized_input "$USER_ID" GROUP_ID="${GROUP_ID_OVERRIDE:-$(id -g)}" sanitized_input "$GROUP_ID" USERNAME="${USERNAME_OVERRIDE:-toolbox}" sanitized_input "$USERNAME" TEA_VERSION="${TEA_VERSION_OVERRIDE:-0.11.1}" sanitized_input "$TEA_VERSION" BUILDER_NAME="${BUILDER_NAME:-tsysdevstack-builder}" sanitized_input "$BUILDER_NAME" CACHE_DIR="${SCRIPT_DIR}/.build-cache" TAG="${TAG_OVERRIDE:-dev}" sanitized_input "$TAG" RELEASE_TAG="${RELEASE_TAG_OVERRIDE:-release-current}" sanitized_input "$RELEASE_TAG" VERSION_TAG="${VERSION_TAG_OVERRIDE:-}" if [[ -n "$VERSION_TAG" ]]; then sanitized_input "$VERSION_TAG" fi PUSH="${PUSH_OVERRIDE:-false}" echo "Building ${IMAGE_NAME} with UID=${USER_ID} GID=${GROUP_ID} USERNAME=${USERNAME}" echo "Primary tag: ${TAG}" # Ensure builder exists if ! docker buildx inspect "${BUILDER_NAME}" >/dev/null 2>&1; then echo "Creating builder: ${BUILDER_NAME}" if ! docker buildx create --driver docker-container --name "${BUILDER_NAME}" --use >/dev/null; then echo "Error: Failed to create Docker buildx builder." >&2 exit 1 fi else echo "Using existing builder: ${BUILDER_NAME}" if ! docker buildx use "${BUILDER_NAME}" >/dev/null; then echo "Error: Failed to use Docker buildx builder." >&2 exit 1 fi fi # Ensure cache directory exists if ! mkdir -p "${CACHE_DIR}"; then echo "Error: Failed to create cache directory: ${CACHE_DIR}" >&2 exit 1 fi echo "Starting build..." BUILD_OUTPUT=$(mktemp) trap 'rm -f "$BUILD_OUTPUT"' EXIT # Build the image if ! docker buildx build \ --builder "${BUILDER_NAME}" \ --load \ --progress=plain \ --build-arg USER_ID="${USER_ID}" \ --build-arg GROUP_ID="${GROUP_ID}" \ --build-arg USERNAME="${USERNAME}" \ --build-arg TEA_VERSION="${TEA_VERSION}" \ --cache-from "type=local,src=${CACHE_DIR}" \ --cache-to "type=local,dest=${CACHE_DIR},mode=max" \ --tag "${IMAGE_NAME}:${TAG}" \ "${SCRIPT_DIR}" 2>&1 | tee "${BUILD_OUTPUT}"; then echo "Error: Docker build failed. Check output above for details." >&2 exit 1 fi echo "Build completed successfully." # Run comprehensive verification tests echo "Running comprehensive verification tests..." if ! docker run --rm "${IMAGE_NAME}:${TAG}" zsh -c 'echo "Container starts successfully as $(whoami) user"'; then echo "Error: Failed to start container with basic test." >&2 exit 1 fi # Verify core tools are available to toolbox user echo "Verifying core tools for toolbox user..." CORE_TOOLS=("zsh" "git" "curl" "jq" "docker" "trivy" "hadolint") for tool in "${CORE_TOOLS[@]}"; do if ! docker run --rm "${IMAGE_NAME}:${TAG}" su - toolbox -c "which $tool" >/dev/null 2>&1; then echo "Error: Core tool '$tool' not found in PATH for toolbox user." >&2 exit 1 fi done # Verify Docker QA tools are available to toolbox user echo "Verifying Docker QA tools for toolbox user..." QA_TOOLS=("dockerfilelint" "yq") for tool in "${QA_TOOLS[@]}"; do if ! docker run --rm "${IMAGE_NAME}:${TAG}" su - toolbox -c "which $tool" >/dev/null 2>&1; then echo "Error: QA tool '$tool' not found in PATH for toolbox user." >&2 exit 1 fi done # Verify Node.js and npm are working properly echo "Verifying Node.js runtime..." if ! docker run --rm "${IMAGE_NAME}:${TAG}" su - toolbox -c "node --version && npm --version" >/dev/null 2>&1; then echo "Error: Node.js or npm not working properly for toolbox user." >&2 exit 1 fi # Verify mise is managing tools properly echo "Verifying mise runtime management..." if ! docker run --rm "${IMAGE_NAME}:${TAG}" su - toolbox -c "mise --version" >/dev/null 2>&1; then echo "Error: Mise not available for toolbox user." >&2 exit 1 fi # Verify aqua is managing tools properly echo "Verifying aqua package management..." if ! docker run --rm "${IMAGE_NAME}:${TAG}" su - toolbox -c "aqua --version" >/dev/null 2>&1; then echo "Error: Aqua not available for toolbox user." >&2 exit 1 fi # Final security check: verify container runs as toolbox user echo "Verifying runtime security model..." RUNTIME_USER=$(docker run --rm "${IMAGE_NAME}:${TAG}" whoami) if [ "$RUNTIME_USER" != "toolbox" ]; then echo "Error: Container is not running as toolbox user. Current user: $RUNTIME_USER" >&2 exit 1 fi echo "All verifications passed. Security model is correct." if [[ "${PUSH}" == "true" ]]; then echo "Pushing ${IMAGE_NAME}:${TAG}" if ! docker push "${IMAGE_NAME}:${TAG}"; then echo "Error: Failed to push ${IMAGE_NAME}:${TAG}" >&2 exit 1 fi if [[ "${TAG}" == "dev" && -n "${VERSION_TAG}" ]]; then if ! docker tag "${IMAGE_NAME}:${TAG}" "${IMAGE_NAME}:${VERSION_TAG}"; then echo "Error: Failed to tag ${IMAGE_NAME}:${VERSION_TAG}" >&2 exit 1 fi echo "Pushing ${IMAGE_NAME}:${VERSION_TAG}" if ! docker push "${IMAGE_NAME}:${VERSION_TAG}"; then echo "Error: Failed to push ${IMAGE_NAME}:${VERSION_TAG}" >&2 exit 1 fi fi if [[ "${TAG}" == "dev" ]]; then if ! docker tag "${IMAGE_NAME}:${TAG}" "${IMAGE_NAME}:${RELEASE_TAG}"; then echo "Error: Failed to tag ${IMAGE_NAME}:${RELEASE_TAG}" >&2 exit 1 fi echo "Pushing ${IMAGE_NAME}:${RELEASE_TAG}" if ! docker push "${IMAGE_NAME}:${RELEASE_TAG}"; then echo "Error: Failed to push ${IMAGE_NAME}:${RELEASE_TAG}" >&2 exit 1 fi fi fi # Run security scan if TRIVY is available if command -v trivy &> /dev/null; then echo "Running security scan with Trivy..." trivy image --exit-code 0 --severity HIGH,CRITICAL "${IMAGE_NAME}:${TAG}" else echo "Trivy not found. Install Trivy to perform security scanning." fi echo "Build process completed successfully with all verifications and security checks."