fix: Address Dockerfile issues identified by toolbox-qadocker audit

This commit fixes several issues in the toolbox-base Dockerfile that were identified during the audit:

- Added SHELL directive with pipefail option where pipes are used
- Fixed syntax error in user creation logic by changing 'else if' to 'elif'
- Removed problematic 'cd' usage, replacing with 'git -C' for directory-specific operations
- Added SHELL directive to second stage where pipes are used
- Improved multi-line RUN command formatting with proper semicolon usage

These changes resolve the following Hadolint errors:
- DL4006: Missing pipefail in RUN commands with pipes
- SC1075: Incorrect use of 'else if' instead of 'elif'
- DL3003: Usage of 'cd' instead of WORKDIR

The Dockerfile now passes Hadolint validation when ignoring version pinning
and multiple RUN command warnings, which are expected in this context.
This commit is contained in:
2025-10-31 14:56:53 -05:00
parent 343534ac12
commit becd640c86

View File

@@ -57,6 +57,7 @@ RUN ln -sf /usr/bin/fdfind /usr/local/bin/fd \
&& ln -sf /usr/bin/batcat /usr/local/bin/bat && ln -sf /usr/bin/batcat /usr/local/bin/bat
# ROOT: Install Gitea tea CLI (system-wide) # ROOT: Install Gitea tea CLI (system-wide)
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
RUN curl -fsSL "https://dl.gitea.io/tea/${TEA_VERSION}/tea-${TEA_VERSION}-linux-amd64" -o /tmp/tea \ RUN curl -fsSL "https://dl.gitea.io/tea/${TEA_VERSION}/tea-${TEA_VERSION}-linux-amd64" -o /tmp/tea \
&& curl -fsSL "https://dl.gitea.io/tea/${TEA_VERSION}/tea-${TEA_VERSION}-linux-amd64.sha256" -o /tmp/tea.sha256 \ && curl -fsSL "https://dl.gitea.io/tea/${TEA_VERSION}/tea-${TEA_VERSION}-linux-amd64.sha256" -o /tmp/tea.sha256 \
&& sed -n 's/ .*//p' /tmp/tea.sha256 | awk '{print $1 " /tmp/tea"}' | sha256sum -c - \ && sed -n 's/ .*//p' /tmp/tea.sha256 | awk '{print $1 " /tmp/tea"}' | sha256sum -c - \
@@ -70,27 +71,32 @@ ENV LANG=en_US.UTF-8 \
LC_ALL=en_US.UTF-8 LC_ALL=en_US.UTF-8
# ROOT: Install Starship prompt (system-wide) # ROOT: Install Starship prompt (system-wide)
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
RUN curl -fsSL https://starship.rs/install.sh | sh -s -- -y -b /usr/local/bin RUN curl -fsSL https://starship.rs/install.sh | sh -s -- -y -b /usr/local/bin
# Install aqua package manager (manages additional CLI tooling) # Install aqua package manager (manages additional CLI tooling)
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
RUN curl -sSfL https://raw.githubusercontent.com/aquaproj/aqua-installer/v2.3.1/aqua-installer | AQUA_ROOT_DIR=/usr/local/share/aquaproj-aqua bash \ RUN curl -sSfL https://raw.githubusercontent.com/aquaproj/aqua-installer/v2.3.1/aqua-installer | AQUA_ROOT_DIR=/usr/local/share/aquaproj-aqua bash \
&& ln -sf /usr/local/share/aquaproj-aqua/bin/aqua /usr/local/bin/aqua && ln -sf /usr/local/share/aquaproj-aqua/bin/aqua /usr/local/bin/aqua
# Install mise for runtime management (no global toolchains pre-installed) # Install mise for runtime management (no global toolchains pre-installed)
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
RUN curl -sSfL https://mise.jdx.dev/install.sh | env MISE_INSTALL_PATH=/usr/local/bin/mise MISE_INSTALL_HELP=0 sh RUN curl -sSfL https://mise.jdx.dev/install.sh | env MISE_INSTALL_PATH=/usr/local/bin/mise MISE_INSTALL_HELP=0 sh
# Install Node.js via mise to enable npm package installation # Install Node.js via mise to enable npm package installation
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
RUN mise install node@22.13.0 && mise global node@22.13.0 RUN mise install node@22.13.0 && mise global node@22.13.0
# Create non-root user with matching UID/GID for host mapping # Create non-root user with matching UID/GID for host mapping
# Check if user/group already exists and handle appropriately # Check if user/group already exists and handle appropriately
RUN if getent passwd "${USER_ID}" >/dev/null; then \ RUN set -eux; \
if getent passwd "${USER_ID}" >/dev/null; then \
existing_user="$(getent passwd "${USER_ID}" | cut -d: -f1)"; \ existing_user="$(getent passwd "${USER_ID}" | cut -d: -f1)"; \
echo "User with UID ${USER_ID} already exists: ${existing_user}" >&2; \ echo "User with UID ${USER_ID} already exists: ${existing_user}" >&2; \
elif ! getent group "${GROUP_ID}" >/dev/null; then \
groupadd --gid "${GROUP_ID}" "${USERNAME}"; \
useradd --uid "${USER_ID}" --gid "${GROUP_ID}" --shell /usr/bin/zsh --create-home "${USERNAME}"; \
else \ else \
if ! getent group "${GROUP_ID}" >/dev/null; then \
groupadd --gid "${GROUP_ID}" "${USERNAME}"; \
fi \
useradd --uid "${USER_ID}" --gid "${GROUP_ID}" --shell /usr/bin/zsh --create-home "${USERNAME}"; \ useradd --uid "${USER_ID}" --gid "${GROUP_ID}" --shell /usr/bin/zsh --create-home "${USERNAME}"; \
fi fi
@@ -101,6 +107,9 @@ RUN chown -R "${USER_ID}:${GROUP_ID}" "/home/${USERNAME}"
USER ${USERNAME} USER ${USERNAME}
WORKDIR /home/${USERNAME} WORKDIR /home/${USERNAME}
# Ensure the workspace directory exists with proper permissions
RUN mkdir -p /workspace && chmod 755 /workspace
# NON-ROOT: Install mise runtime manager for toolbox user # NON-ROOT: Install mise runtime manager for toolbox user
RUN curl -sSfL https://mise.jdx.dev/install.sh | sh RUN curl -sSfL https://mise.jdx.dev/install.sh | sh
@@ -166,9 +175,8 @@ RUN mise exec -- npm install -g bats@1.11.0 && mise reshim
# NON-ROOT: Install BATS testing framework from source (baked into image) # NON-ROOT: Install BATS testing framework from source (baked into image)
RUN git clone https://github.com/bats-core/bats-core.git /tmp/bats-core \ RUN git clone https://github.com/bats-core/bats-core.git /tmp/bats-core \
&& cd /tmp/bats-core \ && git -C /tmp/bats-core checkout v1.11.0 \
&& git checkout v1.11.0 \ && /tmp/bats-core/install.sh "$HOME/.local" \
&& ./install.sh "$HOME/.local" \
&& rm -rf /tmp/bats-core && rm -rf /tmp/bats-core
# Prepare workspace directory with appropriate ownership # Prepare workspace directory with appropriate ownership
@@ -178,6 +186,8 @@ RUN mkdir -p /workspace \
# Remove sudo to ensure no root escalation is possible at runtime # Remove sudo to ensure no root escalation is possible at runtime
RUN apt-get remove -y sudo 2>/dev/null || true && apt-get autoremove -y 2>/dev/null || true && rm -rf /var/lib/apt/lists/* 2>/dev/null || true RUN apt-get remove -y sudo 2>/dev/null || true && apt-get autoremove -y 2>/dev/null || true && rm -rf /var/lib/apt/lists/* 2>/dev/null || true
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
ENV SHELL=/usr/bin/zsh \ ENV SHELL=/usr/bin/zsh \
AQUA_GLOBAL_CONFIG=/home/${USERNAME}/.config/aquaproj-aqua/aqua.yaml \ AQUA_GLOBAL_CONFIG=/home/${USERNAME}/.config/aquaproj-aqua/aqua.yaml \
PATH=/home/${USERNAME}/.local/share/aquaproj-aqua/bin:/home/${USERNAME}/.local/share/mise/shims:/home/${USERNAME}/.local/bin:${PATH} PATH=/home/${USERNAME}/.local/share/aquaproj-aqua/bin:/home/${USERNAME}/.local/share/mise/shims:/home/${USERNAME}/.local/bin:${PATH}
@@ -244,19 +254,21 @@ ENV LANG=en_US.UTF-8 \
LC_ALL=en_US.UTF-8 LC_ALL=en_US.UTF-8
# ROOT: Create user/group structure # ROOT: Create user/group structure
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
# First clean up any existing user/group with the same ID # First clean up any existing user/group with the same ID
RUN if getent passwd "${USER_ID}" >/dev/null; then \ RUN set -eux; \
if getent passwd "${USER_ID}" >/dev/null; then \
existing_user="$(getent passwd "${USER_ID}" | cut -d: -f1)"; \ existing_user="$(getent passwd "${USER_ID}" | cut -d: -f1)"; \
userdel --remove "${existing_user}"; \ userdel --remove "${existing_user}"; \
fi \ fi; \
&& if getent group "${GROUP_ID}" >/dev/null; then \ if getent group "${GROUP_ID}" >/dev/null; then \
groupdel "$(getent group "${GROUP_ID}" | cut -d: -f1)"; \ groupdel "$(getent group "${GROUP_ID}" | cut -d: -f1)"; \
fi \ fi; \
# Create the group and user # Create the group and user
&& groupadd --gid "${GROUP_ID}" "${USERNAME}" \ groupadd --gid "${GROUP_ID}" "${USERNAME}"; \
&& useradd --uid "${USER_ID}" --gid "${GROUP_ID}" --shell /usr/bin/zsh --create-home "${USERNAME}" \ useradd --uid "${USER_ID}" --gid "${GROUP_ID}" --shell /usr/bin/zsh --create-home "${USERNAME}"; \
# Ensure proper ownership of home directory # Ensure proper ownership of home directory
&& chown -R "${USER_ID}:${GROUP_ID}" "/home/${USERNAME}" chown -R "${USER_ID}:${GROUP_ID}" "/home/${USERNAME}"
# ROOT: Copy the complete user environment from the installer stage # ROOT: Copy the complete user environment from the installer stage
COPY --from=installer --chown=${USER_ID}:${GROUP_ID} /home/${USERNAME} /home/${USERNAME} COPY --from=installer --chown=${USER_ID}:${GROUP_ID} /home/${USERNAME} /home/${USERNAME}
@@ -265,6 +277,7 @@ COPY --from=installer --chown=${USER_ID}:${GROUP_ID} /home/${USERNAME} /home/${U
RUN mkdir -p /workspace && chown "${USER_ID}:${GROUP_ID}" /workspace RUN mkdir -p /workspace && chown "${USER_ID}:${GROUP_ID}" /workspace
# ROOT: Install system-wide tools (tea and starship) which were in the source image # ROOT: Install system-wide tools (tea and starship) which were in the source image
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
RUN curl -fsSL "https://dl.gitea.io/tea/${TEA_VERSION}/tea-${TEA_VERSION}-linux-amd64" -o /tmp/tea \ RUN curl -fsSL "https://dl.gitea.io/tea/${TEA_VERSION}/tea-${TEA_VERSION}-linux-amd64" -o /tmp/tea \
&& curl -fsSL "https://dl.gitea.io/tea/${TEA_VERSION}/tea-${TEA_VERSION}-linux-amd64.sha256" -o /tmp/tea.sha256 \ && curl -fsSL "https://dl.gitea.io/tea/${TEA_VERSION}/tea-${TEA_VERSION}-linux-amd64.sha256" -o /tmp/tea.sha256 \
&& sed -n 's/ .*//p' /tmp/tea.sha256 | awk '{print $1 " /tmp/tea"}' | sha256sum -c - \ && sed -n 's/ .*//p' /tmp/tea.sha256 | awk '{print $1 " /tmp/tea"}' | sha256sum -c - \
@@ -281,6 +294,8 @@ ENV PATH=/home/${USERNAME}/.local/share/aquaproj-aqua/bin:/home/${USERNAME}/.loc
ENV SHELL=/usr/bin/zsh \ ENV SHELL=/usr/bin/zsh \
AQUA_GLOBAL_CONFIG=/home/${USERNAME}/.config/aquaproj-aqua/aqua.yaml AQUA_GLOBAL_CONFIG=/home/${USERNAME}/.config/aquaproj-aqua/aqua.yaml
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
# FINAL USER: Switch to toolbox user for runtime # FINAL USER: Switch to toolbox user for runtime
USER ${USERNAME} USER ${USERNAME}
WORKDIR /workspace WORKDIR /workspace