Dockerfile Validator Validate Dockerfiles with deterministic stages, clear severity reporting, and explicit fallbacks when tools or network access are constrained. Trigger Phrases Use this skill when the user asks for tasks like: - "validate this Dockerfile" - "lint/check my Dockerfile" - "security scan Dockerfile" - "optimize Docker image size/build time" - "review Dockerfile before merge" - "find issues in Dockerfile.prod/Dockerfile.dev" Use / Do Not Use Use this skill for: - Syntax and lint validation - Security and secrets checks - Best-practice and performance review - Dockerfile hardeni…

; then\n return 0\n fi\n\n return 1\n}\n\n################################################################################\n# Validation Functions\n################################################################################\n\nrun_hadolint() {\n echo -e \"${CYAN}${BOLD}[1/4] Syntax Validation (hadolint)${NC}\"\n echo \"=====================================\"\n echo \"\"\n\n if \"$HADOLINT_CMD\" \"$DOCKERFILE\" 2>&1; then\n echo \"\"\n echo -e \"${GREEN}✓ Syntax validation passed${NC}\"\n return 0\n else\n local hadolint_exit=$?\n echo \"\"\n echo -e \"${YELLOW}⚠ Syntax issues found${NC}\"\n EXIT_CODE=1\n return $hadolint_exit\n fi\n}\n\nrun_checkov() {\n echo -e \"${CYAN}${BOLD}[2/4] Security Scan (Checkov)${NC}\"\n echo \"================================\"\n echo \"\"\n\n if \"$CHECKOV_CMD\" -f \"$DOCKERFILE\" --framework dockerfile --compact 2>&1; then\n echo \"\"\n echo -e \"${GREEN}✓ Security scan passed${NC}\"\n return 0\n else\n local checkov_exit=$?\n echo \"\"\n echo -e \"${YELLOW}⚠ Security issues found${NC}\"\n EXIT_CODE=1\n return $checkov_exit\n fi\n}\n\nrun_best_practices() {\n echo -e \"${CYAN}${BOLD}[3/4] Best Practices Validation${NC}\"\n echo \"====================================\"\n echo \"\"\n\n # Reset counters\n BP_ERRORS=0\n BP_WARNINGS=0\n BP_INFO=0\n\n # Create normalized version for accurate multi-line instruction counting\n local normalized_content\n normalized_content=$(normalize_dockerfile \"$DOCKERFILE\")\n local final_image\n final_image=$(final_from_image \"$normalized_content\")\n\n # Check for :latest tag\n if grep -qiE \"^[[:space:]]*FROM[[:space:]]+[^[:space:]]+:latest([[:space:]]|$)\" \"$DOCKERFILE\"; then\n echo -e \"${YELLOW}[WARNING] Base image using :latest tag${NC}\"\n echo \" → Use specific version tags for reproducibility\"\n ((BP_WARNINGS++))\n fi\n\n # Check for USER directive\n if ! grep -qiE \"^[[:space:]]*USER[[:space:]]+\" \"$DOCKERFILE\"; then\n if is_nonroot_base_image \"$final_image\"; then\n echo -e \"${PURPLE}[INFO] No USER directive, but final base image is non-root: $final_image${NC}\"\n echo \" → Confirm runtime user requirements for your platform\"\n ((BP_INFO++))\n else\n echo -e \"${YELLOW}[WARNING] No USER directive - container will run as root${NC}\"\n echo \" → Add 'USER \u003cnon-root-user>' before CMD/ENTRYPOINT\"\n ((BP_WARNINGS++))\n fi\n else\n LAST_USER=$(grep -iE \"^[[:space:]]*USER[[:space:]]+\" \"$DOCKERFILE\" | tail -n1 | awk '{print $2}')\n LAST_USER_LOWER=$(echo \"$LAST_USER\" | tr '[:upper:]' '[:lower:]')\n if [[ \"$LAST_USER_LOWER\" == \"root\" ]] || [[ \"$LAST_USER\" == \"0\" ]] || [[ \"$LAST_USER\" == \"0:0\" ]]; then\n echo -e \"${RED}[ERROR] Last USER directive sets user to root${NC}\"\n echo \" → Container should not run as root user\"\n ((BP_ERRORS++))\n EXIT_CODE=1\n fi\n fi\n\n # Check for HEALTHCHECK\n if ! grep -qiE \"^[[:space:]]*HEALTHCHECK[[:space:]]+\" \"$DOCKERFILE\"; then\n if grep -qiE \"^[[:space:]]*EXPOSE[[:space:]]+|^[[:space:]]*(CMD|ENTRYPOINT)[[:space:]].*server\" \"$DOCKERFILE\"; then\n echo -e \"${PURPLE}[INFO] No HEALTHCHECK defined for service container${NC}\"\n echo \" → Consider adding HEALTHCHECK for monitoring\"\n ((BP_INFO++))\n fi\n fi\n\n # Check RUN command efficiency (using normalized content for accurate counting)\n RUN_COUNT=$(count_instruction \"$normalized_content\" \"RUN\")\n if [ \"$RUN_COUNT\" -gt \"5\" ]; then\n echo -e \"${PURPLE}[INFO] High number of RUN commands ($RUN_COUNT)${NC}\"\n echo \" → Consider combining related commands to reduce layers\"\n ((BP_INFO++))\n fi\n\n # Check for apt-get cache cleanup (must happen in same RUN instruction)\n APT_INSTALL_WITHOUT_CLEAN=$(printf '%s\\n' \"$normalized_content\" | awk '\n BEGIN { IGNORECASE=1 }\n /^[[:space:]]*RUN[[:space:]]+/ && /apt-get[[:space:]]+install/ {\n has_clean = ($0 ~ /rm[[:space:]]+-rf[[:space:]]+\\/var\\/lib\\/apt\\/lists/ || $0 ~ /apt-get[[:space:]]+clean/)\n if (!has_clean) { count++ }\n }\n END { print count + 0 }\n ')\n\n if [ \"$APT_INSTALL_WITHOUT_CLEAN\" -gt 0 ]; then\n echo -e \"${YELLOW}[WARNING] apt-get install found without same-layer cache cleanup${NC}\"\n echo \" → Add '&& rm -rf /var/lib/apt/lists/*' to the same RUN instruction\"\n ((BP_WARNINGS++))\n fi\n\n # Check for apk --no-cache (using normalized content)\n APK_ADD_WITHOUT_NOCACHE=$(printf '%s\\n' \"$normalized_content\" | awk '\n BEGIN { IGNORECASE=1 }\n /^[[:space:]]*RUN[[:space:]]+/ && /apk[[:space:]]+add/ {\n has_no_cache = ($0 ~ /apk[[:space:]]+add[^#]*--no-cache/)\n has_manual_cleanup = ($0 ~ /rm[[:space:]]+-rf[[:space:]]+\\/var\\/cache\\/apk/)\n if (!has_no_cache && !has_manual_cleanup) { count++ }\n }\n END { print count + 0 }\n ')\n\n if [ \"$APK_ADD_WITHOUT_NOCACHE\" -gt 0 ]; then\n echo -e \"${YELLOW}[WARNING] apk add without --no-cache or manual cache cleanup${NC}\"\n echo \" → Use 'apk add --no-cache' to avoid cache in image\"\n ((BP_WARNINGS++))\n fi\n\n # Check for hardcoded secrets (ignore comments)\n # Use tolower() instead of IGNORECASE=1 for ~ operator: BSD awk (macOS) does not\n # honour IGNORECASE when using the dynamic ~ operator, only for literal /patterns/.\n # Dockerfile convention is UPPERCASE variable names (ENV PASSWORD=, ENV API_KEY=),\n # so without tolower() all secrets are silently missed on macOS.\n if awk '\n /^[[:space:]]*#/ { next }\n /^[[:space:]]*(ENV|ARG)[[:space:]]/ {\n lower = tolower($0)\n if (lower ~ /(password|secret|api_key|api-key|apikey|token)[[:space:]]*=/) {\n found=1\n }\n }\n END { exit found ? 0 : 1 }\n ' \"$DOCKERFILE\"; then\n echo -e \"${RED}[ERROR] Potential hardcoded secrets in ENV/ARG${NC}\"\n echo \" → Never hardcode secrets in Dockerfiles\"\n ((BP_ERRORS++))\n EXIT_CODE=1\n fi\n\n # Check for poor COPY ordering (COPY . before dependency installation)\n # This hurts build cache efficiency - dependencies should be copied first.\n # Stage-aware: resets tracking on each FROM so a COPY . in a builder stage\n # does not produce a false positive against installs in a separate runtime stage.\n COPY_ORDER_ISSUE=$(printf '%s\\n' \"$normalized_content\" | awk '\n {\n lower = tolower($0)\n\n # New build stage — reset per-stage COPY . tracking\n if (lower ~ /^[[:space:]]*from[[:space:]]+/) {\n stage_copy_line = 0\n next\n }\n\n # Skip comment lines\n if (lower ~ /^[[:space:]]*#/) { next }\n\n # Record the first COPY . in the current stage (ignore COPY --from=)\n if (stage_copy_line == 0 && lower ~ /^[[:space:]]*copy[[:space:]]+/) {\n stripped = lower\n sub(/^[[:space:]]*copy[[:space:]]+/, \"\", stripped)\n while (stripped ~ /^--[^[:space:]]+[[:space:]]+/) {\n sub(/^--[^[:space:]]+[[:space:]]+/, \"\", stripped)\n }\n split(stripped, parts, /[[:space:]]+/)\n if (parts[1] == \".\" || parts[1] == \"./\") {\n stage_copy_line = NR\n }\n }\n\n # Flag if a package install follows COPY . within the same stage\n if (stage_copy_line > 0 && NR > stage_copy_line && lower ~ /^[[:space:]]*run[[:space:]]+/) {\n if (lower ~ /pip[[:space:]]+install|npm[[:space:]]+(install|ci)|yarn([[:space:]]|$)|go[[:space:]]+mod[[:space:]]|apt-get[[:space:]]+install|apk[[:space:]]+add/) {\n found = 1\n exit\n }\n }\n }\n END { print found + 0 }\n ')\n\n if [ \"$COPY_ORDER_ISSUE\" -gt 0 ]; then\n echo -e \"${YELLOW}[WARNING] COPY . appears before dependency installation${NC}\"\n echo \" → Copy dependency files (package.json, requirements.txt) first for better cache\"\n echo \" → Then install dependencies, then COPY . for source code\"\n ((BP_WARNINGS++))\n fi\n\n echo \"\"\n echo \"Best Practices Summary:\"\n echo -e \" Errors: ${RED}$BP_ERRORS${NC}\"\n echo -e \" Warnings: ${YELLOW}$BP_WARNINGS${NC}\"\n echo -e \" Info: ${PURPLE}$BP_INFO${NC}\"\n echo \"\"\n\n if [ $BP_ERRORS -eq 0 ] && [ $BP_WARNINGS -eq 0 ]; then\n echo -e \"${GREEN}✓ Best practices validation passed${NC}\"\n return 0\n elif [ $BP_ERRORS -eq 0 ]; then\n echo -e \"${YELLOW}⚠ Best practices completed with warnings${NC}\"\n BP_HAS_WARNINGS=true\n return 0\n else\n echo -e \"${RED}✗ Best practices validation failed${NC}\"\n return 1\n fi\n}\n\nrun_optimization() {\n echo -e \"${CYAN}${BOLD}[4/4] Optimization Analysis${NC}\"\n echo \"==============================\"\n echo \"\"\n\n # Create normalized version for accurate multi-line instruction counting\n local normalized_content\n normalized_content=$(normalize_dockerfile \"$DOCKERFILE\")\n\n # Analyze base images\n BASE_IMAGES=$(from_images \"$normalized_content\")\n\n echo -e \"${BLUE}Base Image Analysis:${NC}\"\n for image in $BASE_IMAGES; do\n if echo \"$image\" | grep -qi \"distroless\"; then\n continue\n fi\n\n if echo \"$image\" | grep -qiE \"ubuntu|debian|centos|fedora\"; then\n echo -e \" ${PURPLE}[OPTIMIZATION] Consider Alpine alternative for: $image${NC}\"\n echo \" → Alpine images are 10-100x smaller\"\n fi\n done\n echo \"\"\n\n # Multi-stage analysis (using normalized content)\n FROM_COUNT=$(count_instruction \"$normalized_content\" \"FROM\")\n\n echo -e \"${BLUE}Build Structure:${NC}\"\n if [ \"$FROM_COUNT\" -eq \"1\" ]; then\n if echo \"$normalized_content\" | grep -qiE \"apt-get install.*(gcc|make|build)\" || \\\n echo \"$normalized_content\" | grep -qiE \"apk add.*(gcc|make|build)\"; then\n echo -e \" ${PURPLE}[OPTIMIZATION] Build tools detected in single-stage build${NC}\"\n echo \" → Consider multi-stage build to exclude build tools from final image\"\n fi\n else\n FINAL_FROM=$(final_from_image \"$normalized_content\")\n if echo \"$FINAL_FROM\" | grep -qiE \"distroless|alpine|scratch\"; then\n echo -e \" ${GREEN}✓ Using minimal base for final stage: $FINAL_FROM${NC}\"\n else\n echo -e \" ${PURPLE}[OPTIMIZATION] Final stage could use smaller base image${NC}\"\n echo \" → Consider: alpine, distroless, or scratch\"\n fi\n fi\n echo \"\"\n\n # Layer count (reuse RUN_COUNT from best practices if available, otherwise calculate)\n RUN_COUNT=$(count_instruction \"$normalized_content\" \"RUN\")\n\n echo -e \"${BLUE}Layer Optimization:${NC}\"\n echo \" RUN commands: $RUN_COUNT\"\n if [ \"$RUN_COUNT\" -gt \"7\" ]; then\n echo -e \" ${PURPLE}[OPTIMIZATION] Consider combining RUN commands${NC}\"\n echo \" → Reduces layer count and image size\"\n fi\n echo \"\"\n\n # .dockerignore check\n DOCKERFILE_DIR=$(dirname \"$DOCKERFILE\")\n if [ ! -f \"$DOCKERFILE_DIR/.dockerignore\" ]; then\n echo -e \"${YELLOW}[INFO] No .dockerignore file found${NC}\"\n echo \" → Create .dockerignore to optimize build context\"\n echo \"\"\n fi\n\n echo -e \"${GREEN}✓ Optimization analysis complete${NC}\"\n return 0\n}\n\n################################################################################\n# Main Execution\n################################################################################\n\nshow_help() {\n # Use echo -e so that BOLD/NC variables (which hold \\033[...m escape sequences)\n # are interpreted correctly. cat \u003c\u003c EOF expands variables but does not process\n # \\033 escape sequences, causing raw escape codes to appear in the output.\n echo -e \"${BOLD}Dockerfile Validator - Complete Lifecycle${NC}\"\n echo \"\"\n echo \"Validates Dockerfiles with automatic tool management and cleanup.\"\n echo \"\"\n echo -e \"${BOLD}Usage:${NC}\"\n echo \" $(basename \"$0\") [Dockerfile]\"\n echo \"\"\n echo -e \"${BOLD}Validation Stages:${NC}\"\n echo \" 1. Syntax validation (hadolint)\"\n echo \" 2. Security scanning (Checkov)\"\n echo \" 3. Best practices validation\"\n echo \" 4. Optimization analysis\"\n echo \"\"\n echo -e \"${BOLD}Features:${NC}\"\n echo \" • Auto-installs tools if not found\"\n echo \" • Runs all validation stages\"\n echo \" • Auto-cleanup on exit\"\n echo \"\"\n echo -e \"${BOLD}Examples:${NC}\"\n echo \" $(basename \"$0\") # Validate ./Dockerfile\"\n echo \" $(basename \"$0\") Dockerfile.prod # Validate specific file\"\n echo \"\"\n echo -e \"${BOLD}Exit Codes:${NC}\"\n echo \" 0 All validations passed\"\n echo \" 1 One or more validations failed\"\n echo \" 2 Critical error\"\n echo \"\"\n}\n\n# Check for help\nARG1=\"${1:-}\"\nif [[ \"$ARG1\" == \"-h\" ]] || [[ \"$ARG1\" == \"--help\" ]]; then\n show_help\n exit 0\nfi\n\n# Validate input\nif [ ! -f \"$DOCKERFILE\" ]; then\n echo -e \"${RED}ERROR: Dockerfile not found: $DOCKERFILE${NC}\" >&2\n echo \"\"\n echo \"Usage: $(basename \"$0\") [Dockerfile]\"\n exit 2\nfi\n\n# Print header\necho \"\"\necho -e \"${CYAN}${BOLD}========================================${NC}\"\necho -e \"${CYAN}${BOLD} Dockerfile Validator${NC}\"\necho -e \"${CYAN}${BOLD}========================================${NC}\"\necho \"\"\necho -e \"${BOLD}Target:${NC} $DOCKERFILE\"\necho -e \"${BOLD}Date:${NC} $(date '+%Y-%m-%d %H:%M:%S')\"\necho \"\"\n\n# Check and install tools if needed\nif ! check_tools; then\n install_tools\nfi\n\necho -e \"${CYAN}${BOLD}Running Validations...${NC}\"\necho \"\"\n\n# Track results\nHADOLINT_RESULT=\"SKIP\"\nCHECKOV_RESULT=\"SKIP\"\nBEST_PRACTICES_RESULT=\"SKIP\"\nOPTIMIZATION_RESULT=\"SKIP\"\n\n# Run all validations\nrun_hadolint && HADOLINT_RESULT=\"PASS\" || HADOLINT_RESULT=\"FAIL\"\necho \"\"\n\nrun_checkov && CHECKOV_RESULT=\"PASS\" || CHECKOV_RESULT=\"FAIL\"\necho \"\"\n\nrun_best_practices && {\n [ \"$BP_HAS_WARNINGS\" = true ] && BEST_PRACTICES_RESULT=\"WARN\" || BEST_PRACTICES_RESULT=\"PASS\"\n} || BEST_PRACTICES_RESULT=\"FAIL\"\necho \"\"\n\nrun_optimization && OPTIMIZATION_RESULT=\"INFO\"\necho \"\"\n\n# Print summary\necho -e \"${CYAN}${BOLD}========================================${NC}\"\necho -e \"${CYAN}${BOLD} Validation Summary${NC}\"\necho -e \"${CYAN}${BOLD}========================================${NC}\"\necho \"\"\n\n# Print results\n[ \"$HADOLINT_RESULT\" = \"PASS\" ] && echo -e \" Syntax (hadolint): ${GREEN}✓ PASSED${NC}\" || echo -e \" Syntax (hadolint): ${RED}✗ FAILED${NC}\"\n[ \"$CHECKOV_RESULT\" = \"PASS\" ] && echo -e \" Security (Checkov): ${GREEN}✓ PASSED${NC}\" || echo -e \" Security (Checkov): ${RED}✗ FAILED${NC}\"\nif [ \"$BEST_PRACTICES_RESULT\" = \"PASS\" ]; then\n echo -e \" Best Practices: ${GREEN}✓ PASSED${NC}\"\nelif [ \"$BEST_PRACTICES_RESULT\" = \"WARN\" ]; then\n echo -e \" Best Practices: ${YELLOW}⚠ WARNED${NC}\"\nelse\n echo -e \" Best Practices: ${RED}✗ FAILED${NC}\"\nfi\necho -e \" Optimization: ${BLUE}ℹ INFORMATIONAL${NC}\"\n\necho \"\"\necho -e \"${CYAN}${BOLD}========================================${NC}\"\necho \"\"\n\n# Overall result\nif [ $EXIT_CODE -eq 0 ]; then\n echo -e \"${GREEN}${BOLD}✓ Overall Result: PASSED${NC}\"\n echo \"\"\n echo \"Your Dockerfile meets validation requirements.\"\nelse\n echo -e \"${RED}${BOLD}✗ Overall Result: FAILED${NC}\"\n echo \"\"\n echo \"Please address the issues identified above.\"\nfi\n\necho \"\"\n\n# Exit (cleanup trap will run automatically)\nexit $EXIT_CODE\n","content_type":"application/x-sh; charset=utf-8","language":"bash","size":23405,"content_sha256":"bea64bc15cca37b333d374dffdd51dc9c69a4e70f18160446d703a593ab27f35"},{"filename":"scripts/test_validate.sh","content":"#!/usr/bin/env bash\n#\n# CI-friendly regression entrypoint for dockerfile-validator.\n# Runs syntax checks, regression tests, and optional ShellCheck linting.\n\nset -euo pipefail\n\nSCRIPT_DIR=\"$(cd \"$(dirname \"${BASH_SOURCE[0]}\")\" && pwd)\"\nreadonly SCRIPT_DIR\nSKILL_DIR=\"$(cd \"$SCRIPT_DIR/..\" && pwd)\"\nreadonly SKILL_DIR\n\nVALIDATOR_SCRIPT=\"$SKILL_DIR/scripts/dockerfile-validate.sh\"\nREGRESSION_SCRIPT=\"$SKILL_DIR/tests/test_regression.sh\"\nSTRICT_SHELLCHECK=\"${STRICT_SHELLCHECK:-false}\"\n\necho \"Running dockerfile-validator CI checks...\"\n\n# Fast syntax guard for scripts used by the regression suite.\nbash -n \"$VALIDATOR_SCRIPT\" \"$REGRESSION_SCRIPT\" \"$0\"\n\n# Regression coverage for parser/best-practice branches.\nbash \"$REGRESSION_SCRIPT\"\n\nif command -v shellcheck >/dev/null 2>&1; then\n shellcheck \"$VALIDATOR_SCRIPT\" \"$REGRESSION_SCRIPT\" \"$0\"\n echo \"ShellCheck: PASS\"\nelif [[ \"$STRICT_SHELLCHECK\" == \"true\" ]]; then\n echo \"ShellCheck: required but not installed (STRICT_SHELLCHECK=true)\" >&2\n exit 1\nelse\n echo \"ShellCheck: SKIP (not installed; set STRICT_SHELLCHECK=true to require it)\"\nfi\n\necho \"PASS: dockerfile-validator CI checks\"\n","content_type":"application/x-sh; charset=utf-8","language":"bash","size":1148,"content_sha256":"479bf97eb4b1fd562e8f286a073f4b53fac606bb254bbdcb867497179a3ce62f"},{"filename":"tests/fixtures/copy-before-yarn-lock-read.Dockerfile","content":"FROM node:20-alpine\nWORKDIR /app\nCOPY . .\nRUN cat yarn.lock >/dev/null 2>&1 || true\nUSER 1000\nCMD [\"node\", \"server.js\"]\n","content_type":"text/plain; charset=utf-8","language":"docker","size":120,"content_sha256":"7dfc56f21bc36fedfd248e962af56d257319c78bc58a4f018c9ffb3511a53504"},{"filename":"tests/fixtures/copy-before-yarn.Dockerfile","content":"FROM node:20-alpine\nWORKDIR /app\nCOPY . .\nRUN yarn\nUSER 1000\nCMD [\"node\", \"server.js\"]\n","content_type":"text/plain; charset=utf-8","language":"docker","size":87,"content_sha256":"827ad4df81b93baf5fe822e855326d395863bd280ef5b9b696124c18511b1dc9"},{"filename":"tests/fixtures/from-platform-nonroot.Dockerfile","content":"FROM --platform=linux/amd64 golang:1.24-alpine AS build\nWORKDIR /src\nRUN printf '#!/bin/sh\\necho hello\\n' > /out && chmod +x /out\n\nFROM --platform linux/amd64 gcr.io/distroless/static-debian11:nonroot AS runtime\nCOPY --from=build /out /out\nENTRYPOINT [\"/out\"]\n","content_type":"text/plain; charset=utf-8","language":"docker","size":260,"content_sha256":"5e155d2b2d6afa8f3369f4be0b48b495fa3e27dd5ece64d58728f42ef64d0550"},{"filename":"tests/test_regression.sh","content":"#!/usr/bin/env bash\nset -euo pipefail\n\nSKILL_DIR=\"$(cd \"$(dirname \"${BASH_SOURCE[0]}\")/..\" && pwd)\"\nVALIDATOR=\"$SKILL_DIR/scripts/dockerfile-validate.sh\"\nFIXTURES_DIR=\"$SKILL_DIR/tests/fixtures\"\nTMP_DIR=\"$(mktemp -d)\"\n\ncleanup() {\n rm -rf \"$TMP_DIR\"\n}\ntrap cleanup EXIT\n\nPASS=0\nFAIL=0\n\npass() {\n echo \" PASS: $1\"\n PASS=$((PASS + 1))\n}\n\nfail() {\n echo \" FAIL: $1\"\n FAIL=$((FAIL + 1))\n}\n\nmake_stub_tools() {\n mkdir -p \"$TMP_DIR/bin\"\n\n cat > \"$TMP_DIR/bin/hadolint\" \u003c\u003c'EOF'\n#!/usr/bin/env bash\nset -euo pipefail\nexit 0\nEOF\n\n cat > \"$TMP_DIR/bin/checkov\" \u003c\u003c'EOF'\n#!/usr/bin/env bash\nset -euo pipefail\nexit 0\nEOF\n\n chmod +x \"$TMP_DIR/bin/hadolint\" \"$TMP_DIR/bin/checkov\"\n}\n\nvalidator_output() {\n local dockerfile=\"$1\"\n PATH=\"$TMP_DIR/bin:$PATH\" bash \"$VALIDATOR\" \"$dockerfile\" 2>&1 || true\n}\n\nvalidator_exit_code() {\n local dockerfile=\"$1\"\n local exit_code=0\n PATH=\"$TMP_DIR/bin:$PATH\" bash \"$VALIDATOR\" \"$dockerfile\" >/dev/null 2>&1 || exit_code=$?\n echo \"$exit_code\"\n}\n\nassert_output_contains() {\n local label=\"$1\"\n local dockerfile=\"$2\"\n local pattern=\"$3\"\n\n local output\n output=\"$(validator_output \"$dockerfile\")\"\n\n if echo \"$output\" | grep -qE \"$pattern\"; then\n pass \"$label\"\n else\n fail \"$label (missing pattern: $pattern)\"\n echo \" --- validator output ---\"\n echo \"$output\" | sed 's/^/ /'\n echo \" --- end output ---\"\n fi\n}\n\nassert_output_not_contains() {\n local label=\"$1\"\n local dockerfile=\"$2\"\n local pattern=\"$3\"\n\n local output\n output=\"$(validator_output \"$dockerfile\")\"\n\n if echo \"$output\" | grep -qE \"$pattern\"; then\n fail \"$label (unexpected pattern: $pattern)\"\n echo \" --- validator output ---\"\n echo \"$output\" | sed 's/^/ /'\n echo \" --- end output ---\"\n else\n pass \"$label\"\n fi\n}\n\nassert_exit_zero() {\n local label=\"$1\"\n local dockerfile=\"$2\"\n\n local exit_code\n exit_code=\"$(validator_exit_code \"$dockerfile\")\"\n\n if [[ \"$exit_code\" -eq 0 ]]; then\n pass \"$label\"\n else\n fail \"$label (expected 0, got $exit_code)\"\n fi\n}\n\necho \"Running dockerfile-validator regression tests...\"\necho \"\"\n\nmake_stub_tools\n\nCOPY_BEFORE_YARN=\"$FIXTURES_DIR/copy-before-yarn.Dockerfile\"\nCOPY_BEFORE_YARN_LOCK=\"$FIXTURES_DIR/copy-before-yarn-lock-read.Dockerfile\"\nFROM_PLATFORM_NONROOT=\"$FIXTURES_DIR/from-platform-nonroot.Dockerfile\"\n\necho \"[copy-before-yarn]\"\nassert_output_contains \\\n \"flags COPY . before bare RUN yarn\" \\\n \"$COPY_BEFORE_YARN\" \\\n \"COPY \\\\. appears before dependency installation\"\nassert_exit_zero \\\n \"warn-only result stays non-failing\" \\\n \"$COPY_BEFORE_YARN\"\n\necho \"\"\necho \"[copy-before-yarn-lock-read]\"\nassert_output_not_contains \\\n \"does not misclassify yarn.lock as yarn install\" \\\n \"$COPY_BEFORE_YARN_LOCK\" \\\n \"COPY \\\\. appears before dependency installation\"\nassert_exit_zero \\\n \"non-install RUN after COPY . remains non-failing\" \\\n \"$COPY_BEFORE_YARN_LOCK\"\n\necho \"\"\necho \"[from-platform-nonroot]\"\nassert_output_contains \\\n \"parses --platform final image as non-root base\" \\\n \"$FROM_PLATFORM_NONROOT\" \\\n \"No USER directive, but final base image is non-root: gcr.io/distroless/static-debian11:nonroot\"\nassert_output_not_contains \\\n \"does not emit root warning for non-root distroless base\" \\\n \"$FROM_PLATFORM_NONROOT\" \\\n \"container will run as root\"\nassert_output_contains \\\n \"optimization stage reports parsed minimal final base\" \\\n \"$FROM_PLATFORM_NONROOT\" \\\n \"Using minimal base for final stage: gcr.io/distroless/static-debian11:nonroot\"\nassert_output_not_contains \\\n \"base image analysis never treats --platform as image\" \\\n \"$FROM_PLATFORM_NONROOT\" \\\n \"Consider Alpine alternative for: --platform\"\nassert_exit_zero \\\n \"platform flag fixture exits successfully\" \\\n \"$FROM_PLATFORM_NONROOT\"\n\necho \"\"\necho \"Summary: $PASS passed, $FAIL failed\"\n\nif [[ \"$FAIL\" -ne 0 ]]; then\n exit 1\nfi\n\necho \"PASS: dockerfile-validator regression tests\"\n","content_type":"application/x-sh; charset=utf-8","language":"bash","size":4043,"content_sha256":"5ee2becc75a5a6add060855573c6b8bcfd756ef400028d89488c85d7763ce175"}],"content_json":{"type":"doc","content":[{"type":"heading","attrs":{"level":1},"content":[{"text":"Dockerfile Validator","type":"text"}]},{"type":"paragraph","content":[{"text":"Validate Dockerfiles with deterministic stages, clear severity reporting, and explicit fallbacks when tools or network access are constrained.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Trigger Phrases","type":"text"}]},{"type":"paragraph","content":[{"text":"Use this skill when the user asks for tasks like:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"\"validate this Dockerfile\"","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"\"lint/check my Dockerfile\"","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"\"security scan Dockerfile\"","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"\"optimize Docker image size/build time\"","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"\"review Dockerfile before merge\"","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"\"find issues in Dockerfile.prod/Dockerfile.dev\"","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Use / Do Not Use","type":"text"}]},{"type":"paragraph","content":[{"text":"Use this skill for:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Syntax and lint validation","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Security and secrets checks","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Best-practice and performance review","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Dockerfile hardening before CI/CD or production","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"Do not use this skill for:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Generating a new Dockerfile from scratch (use ","type":"text"},{"text":"dockerfile-generator","type":"text","marks":[{"type":"code_inline"}]},{"text":")","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Running containers, debugging runtime behavior, or image registry operations","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Local Files In This Skill","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Validator script: ","type":"text"},{"text":"scripts/dockerfile-validate.sh","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"References:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"references/security_checklist.md","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"references/optimization_guide.md","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"references/docker_best_practices.md","type":"text","marks":[{"type":"code_inline"}]}]}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Example Dockerfiles: ","type":"text"},{"text":"examples/*.Dockerfile","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Deterministic Execution Flow (Required)","type":"text"}]},{"type":"paragraph","content":[{"text":"Run these steps in order. Do not skip steps unless a documented fallback branch applies.","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"1. Preflight and Path Setup","type":"text"}]},{"type":"paragraph","content":[{"text":"Assume repo root as working directory:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"cd /path/to/repo\nSKILL_DIR=\"devops-skills-plugin/skills/dockerfile-validator\"\nTARGET_DOCKERFILE=\"Dockerfile\" # replace when user provides a path","type":"text"}]},{"type":"paragraph","content":[{"text":"Validate inputs before running tools:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"test -f \"$SKILL_DIR/scripts/dockerfile-validate.sh\"\ntest -f \"$TARGET_DOCKERFILE\"","type":"text"}]},{"type":"paragraph","content":[{"text":"If either check fails, stop and report the exact missing path.","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"2. Read the Target Dockerfile Explicitly","type":"text"}]},{"type":"paragraph","content":[{"text":"Use explicit file-read commands (not abstract \"Read tool\" wording):","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"sed -n '1,220p' \"$TARGET_DOCKERFILE\"","type":"text"}]},{"type":"paragraph","content":[{"text":"If needed for long files:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"sed -n '220,440p' \"$TARGET_DOCKERFILE\"","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"3. Run Validation Script","type":"text"}]},{"type":"paragraph","content":[{"text":"Primary command:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"bash \"$SKILL_DIR/scripts/dockerfile-validate.sh\" \"$TARGET_DOCKERFILE\"","type":"text"}]},{"type":"paragraph","content":[{"text":"Optional captured run for structured reporting:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"bash \"$SKILL_DIR/scripts/dockerfile-validate.sh\" \"$TARGET_DOCKERFILE\" | tee /tmp/dockerfile-validator.out","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"4. Classify Findings by Severity (Standard)","type":"text"}]},{"type":"paragraph","content":[{"text":"Use this standard severity model:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Critical","type":"text","marks":[{"type":"code_inline"}]}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Hardcoded secrets/credentials","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Explicit root runtime with high-risk context","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"High-impact security policy failures","type":"text"}]}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"High","type":"text","marks":[{"type":"code_inline"}]}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Checkov failures for container hardening","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"hadolint errors likely to cause insecure/unreliable builds","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Missing or unsafe runtime-user posture (","type":"text"},{"text":"USER","type":"text","marks":[{"type":"code_inline"}]},{"text":")","type":"text"}]}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Medium","type":"text","marks":[{"type":"code_inline"}]}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":":latest","type":"text","marks":[{"type":"code_inline"}]},{"text":" image tags, missing pinning, cache-cleanup misses","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Build cache inefficiency and layered install anti-patterns","type":"text"}]}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Low","type":"text","marks":[{"type":"code_inline"}]}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Style/info guidance and non-blocking optimization suggestions","type":"text"}]}]}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"5. No-Issue Fast Path (Required)","type":"text"}]},{"type":"paragraph","content":[{"text":"If validation has no actionable findings:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Return a concise pass summary.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Do ","type":"text"},{"text":"not","type":"text","marks":[{"type":"strong"}]},{"text":" open reference files.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Do ","type":"text"},{"text":"not","type":"text","marks":[{"type":"strong"}]},{"text":" generate fix diffs.","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"Use fast path when all are true:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Script reports overall pass.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"No security failures.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"No error/warning findings requiring user action.","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"6. Reference Loading Rules (Only When Findings Exist)","type":"text"}]},{"type":"paragraph","content":[{"text":"Only read references that match actual findings. Read each required file once.","type":"text"}]},{"type":"paragraph","content":[{"text":"Issue-to-reference mapping:","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Issue category","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Trigger examples","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Read this file","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Secrets, root user, exposed sensitive ports, hardening gaps","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"CKV_DOCKER_*","type":"text","marks":[{"type":"code_inline"}]},{"text":", hardcoded token/password, root runtime","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"references/security_checklist.md","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Image size, layer count, multi-stage opportunities, cache efficiency, ","type":"text"},{"text":".dockerignore","type":"text","marks":[{"type":"code_inline"}]},{"text":" gaps","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"too many ","type":"text"},{"text":"RUN","type":"text","marks":[{"type":"code_inline"}]},{"text":", single-stage with build deps, cache misses","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"references/optimization_guide.md","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Tag pinning, instruction usage, COPY vs ADD, WORKDIR/CMD/ENTRYPOINT conventions","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":":latest","type":"text","marks":[{"type":"code_inline"}]},{"text":", unpinned packages, instruction-level best practices","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"references/docker_best_practices.md","type":"text","marks":[{"type":"code_inline"}]}]}]}]}]},{"type":"paragraph","content":[{"text":"Explicit read commands:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"sed -n '1,220p' \"$SKILL_DIR/references/security_checklist.md\"\nsed -n '1,220p' \"$SKILL_DIR/references/optimization_guide.md\"\nsed -n '1,220p' \"$SKILL_DIR/references/docker_best_practices.md\"","type":"text"}]},{"type":"paragraph","content":[{"text":"For targeted extraction:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"rg -n \"USER|secrets|EXPOSE|HEALTHCHECK\" \"$SKILL_DIR/references/security_checklist.md\"\nrg -n \"multi-stage|cache|layer|dockerignore\" \"$SKILL_DIR/references/optimization_guide.md\"\nrg -n \"FROM|COPY|ADD|WORKDIR|CMD|ENTRYPOINT|latest\" \"$SKILL_DIR/references/docker_best_practices.md\"","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"7. Produce Standard Report Output","type":"text"}]},{"type":"paragraph","content":[{"text":"Use this template for every non-fast-path run:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"markdown"},"content":[{"text":"## Dockerfile Validation Report\n- Target: \u003cpath>\n- Command: `bash \u003cskill-script> \u003ctarget>`\n- Overall result: PASS | FAIL | PARTIAL (fallback)\n\n### Critical\n- \u003cissue or `None`>\n\n### High\n- \u003cissue or `None`>\n\n### Medium\n- \u003cissue or `None`>\n\n### Low\n- \u003cissue or `None`>\n\n### Recommended Fixes\n- \u003cspecific code-level fix per actionable issue>\n\n### References Used\n- \u003clist only files actually read>\n\n### Fallbacks Used\n- `None` or exact fallback branch + reason","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"8. Offer Fix Application","type":"text"}]},{"type":"paragraph","content":[{"text":"After reporting:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Ask whether to apply fixes.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"If user approves, patch the Dockerfile and rerun validation.","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Fallback Behavior (Explicit)","type":"text"}]},{"type":"paragraph","content":[{"text":"When the primary script cannot complete, use deterministic fallback branches and report them.","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Fallback A: Python/Tool Install Constraint","type":"text"}]},{"type":"paragraph","content":[{"text":"Condition:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Script exits with tool-install failure (for example Python missing, package install blocked, or restricted environment).","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"Action:","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Report primary failure and why.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Run manual minimum checks:","type":"text"}]}]}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# Basic syntax signal (if Docker is available)\nDOCKERFILE_DIR=\"$(dirname \"$TARGET_DOCKERFILE\")\"\ndocker build --no-cache -f \"$TARGET_DOCKERFILE\" \"$DOCKERFILE_DIR\"\n\n# High-value static checks\ngrep -nEi \"^[[:space:]]*FROM[[:space:]]+.*:latest\" \"$TARGET_DOCKERFILE\" || true\ngrep -nEi \"^[[:space:]]*(ENV|ARG)[[:space:]].*(password|secret|token|api[_-]?key)[[:space:]]*=\" \"$TARGET_DOCKERFILE\" || true\ngrep -nEi \"^[[:space:]]*USER[[:space:]]+(root|0(:0)?)$\" \"$TARGET_DOCKERFILE\" || true\ngrep -nEi \"^[[:space:]]*HEALTHCHECK[[:space:]]+\" \"$TARGET_DOCKERFILE\" || true","type":"text"}]},{"type":"ordered_list","attrs":{"order":3,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Classify output with ","type":"text"},{"text":"PARTIAL","type":"text","marks":[{"type":"code_inline"}]},{"text":" result and clearly label skipped checks.","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Fallback B: hadolint Not Available but Docker Available","type":"text"}]},{"type":"paragraph","content":[{"text":"Use hadolint container image:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"docker run --rm -i hadolint/hadolint \u003c \"$TARGET_DOCKERFILE\"","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Fallback C: No Docker, No hadolint/checkov","type":"text"}]},{"type":"paragraph","content":[{"text":"Run only manual regex-based checks (Fallback A step 2), clearly mark as ","type":"text"},{"text":"PARTIAL","type":"text","marks":[{"type":"code_inline"}]},{"text":", and state which scanners were skipped.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Quick Command Set","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Validate one Dockerfile","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"cd /path/to/repo\nbash devops-skills-plugin/skills/dockerfile-validator/scripts/dockerfile-validate.sh Dockerfile","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Validate alternate file","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"cd /path/to/repo\nbash devops-skills-plugin/skills/dockerfile-validator/scripts/dockerfile-validate.sh Dockerfile.prod","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Validate skill examples","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"cd /path/to/repo/devops-skills-plugin/skills/dockerfile-validator\nbash scripts/dockerfile-validate.sh examples/good-example.Dockerfile\nbash scripts/dockerfile-validate.sh examples/security-issues.Dockerfile","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Run regression checks (CI entrypoint)","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"cd /path/to/repo\nbash devops-skills-plugin/skills/dockerfile-validator/scripts/test_validate.sh","type":"text"}]},{"type":"paragraph","content":[{"text":"Optional strict mode for CI environments that must enforce ShellCheck:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"STRICT_SHELLCHECK=true bash devops-skills-plugin/skills/dockerfile-validator/scripts/test_validate.sh","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Progressive Disclosure Rules","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Always read the target Dockerfile first.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Do not read any reference files unless findings require them.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Read only the matching reference file(s) from the issue-to-reference mapping.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Do not reread the same reference unless new issue categories appear.","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Done Criteria","type":"text"}]},{"type":"paragraph","content":[{"text":"Consider this skill execution complete only when all conditions below are satisfied:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Trigger matched a Dockerfile validation/lint/security/optimization request.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Target Dockerfile path was explicitly verified.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Validation command (or explicit fallback) was executed.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Findings were reported using severity buckets (","type":"text"},{"text":"Critical","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"High","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"Medium","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"Low","type":"text","marks":[{"type":"code_inline"}]},{"text":").","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Reference usage matched issue categories and was explicitly listed.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"No-issue fast path skipped unnecessary reference reads.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"If fixes were applied, validation was rerun and final status reported.","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Resources","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Script: ","type":"text"},{"text":"scripts/dockerfile-validate.sh","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"CI/regression entrypoint: ","type":"text"},{"text":"scripts/test_validate.sh","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Security reference: ","type":"text"},{"text":"references/security_checklist.md","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Optimization reference: ","type":"text"},{"text":"references/optimization_guide.md","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Best-practices reference: ","type":"text"},{"text":"references/docker_best_practices.md","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Examples: ","type":"text"},{"text":"examples/good-example.Dockerfile","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"examples/bad-example.Dockerfile","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"examples/security-issues.Dockerfile","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"examples/python-optimized.Dockerfile","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"examples/golang-distroless.Dockerfile","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Source Links","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Docker Build Best Practices","type":"text","marks":[{"type":"link","attrs":{"href":"https://docs.docker.com/build/building/best-practices/","title":null}}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Dockerfile Reference","type":"text","marks":[{"type":"link","attrs":{"href":"https://docs.docker.com/reference/dockerfile/","title":null}}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Checkov Dockerfile Scanning","type":"text","marks":[{"type":"link","attrs":{"href":"https://www.checkov.io/7.Scan%20Examples/Dockerfile.html","title":null}}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"hadolint","type":"text","marks":[{"type":"link","attrs":{"href":"https://github.com/hadolint/hadolint","title":null}}]}]}]}]}]},"metadata":{"date":"2026-11-02","name":"dockerfile-validator","author":"@skillopedia","source":{"stars":331,"repo_name":"cc-devops-skills","origin_url":"https://github.com/akin-ozer/cc-devops-skills/blob/HEAD/devops-skills-plugin/skills/dockerfile-validator/SKILL.md","repo_owner":"akin-ozer","body_sha256":"6b02fd078c813e49c11bc1ad6e1c82bdbe11c65ec2c54fb7c9b0ba1ea6a209d9","cluster_key":"d7a3c62193554e8c59b22f43a149b69f9fe141b3138e497198445c8785a36a95","clean_bundle":{"format":"clean-skill-bundle-v1","source":"akin-ozer/cc-devops-skills/devops-skills-plugin/skills/dockerfile-validator/SKILL.md","text_bytes":57973,"source_commit":"276af751e659315aaf56d3ad13d7c26f4e72e28a","skills_sh_rank":15281,"attachment_count":14,"text_attachments":14,"attachment_storage":"skill_attachments","binary_attachments":0},"cluster_size":1,"skill_md_path":"devops-skills-plugin/skills/dockerfile-validator/SKILL.md","import_metadata":{"date":"2026-11-02","author":"@skillopedia","version":"v1","category":"devops-infrastructure","category_label":"DevOps"},"exact_dupes_collapsed_into_this":0},"version":"v1","category":"devops-infrastructure","import_tag":"skills-sh-top20k-2026-11","description":"Validate, lint, audit, or scan a Dockerfile for security and best practices."}},"renderedAt":1793665961016}

Dockerfile Validator Validate Dockerfiles with deterministic stages, clear severity reporting, and explicit fallbacks when tools or network access are constrained. Trigger Phrases Use this skill when the user asks for tasks like: - "validate this Dockerfile" - "lint/check my Dockerfile" - "security scan Dockerfile" - "optimize Docker image size/build time" - "review Dockerfile before merge" - "find issues in Dockerfile.prod/Dockerfile.dev" Use / Do Not Use Use this skill for: - Syntax and lint validation - Security and secrets checks - Best-practice and performance review - Dockerfile hardeni…