docker-containerization — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited docker-containerization (Agent Skill) and scored it 91/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 1 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 1 flagged
The text {match} is the classic direct prompt-injection phrasing. Placed in a skill body that the agent reads as trusted instructions, it tries to make the agent abandon its prior rules and follow whatever comes next — a full system-prompt override.
ignore/disregard/forget … previous instructions sentence.Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
| Use Case | Base Image |
|---|---|
| Python production | python:3.12-slim |
| Node.js production | node:20-alpine |
| Go production (final stage) | gcr.io/distroless/static |
| Debugging / dev | python:3.12 or node:20 |
| Minimal static binary | scratch |
Rules:
latest tag - pin exact versions for reproducibility-slim or -alpine over full images to reduce attack surface and sizedistroless or scratch for final stages of statically compiled languages# ─── Build Stage ─────────────────────────────────────────────────────────────
FROM python:3.12-slim AS builder
WORKDIR /app
# Install dependencies separately for layer caching
COPY requirements.txt .
RUN pip install --upgrade pip && \
pip install --no-cache-dir --prefix=/install -r requirements.txt
# ─── Final Stage ─────────────────────────────────────────────────────────────
FROM python:3.12-slim
WORKDIR /app
# Copy installed packages from builder
COPY --from=builder /install /usr/local
# Create non-root user (security hardening)
RUN groupadd --gid 1001 appuser && \
useradd --uid 1001 --gid appuser --shell /bin/bash --create-home appuser
# Copy application code
COPY --chown=appuser:appuser . .
USER appuser
EXPOSE 8000
# Health check
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
CMD curl -f http://localhost:8000/health || exit 1
CMD ["python", "-m", "uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]# ─── Dependencies Stage ───────────────────────────────────────────────────────
FROM node:20-alpine AS deps
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
# ─── Build Stage (if TypeScript/Next.js) ─────────────────────────────────────
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# ─── Final Stage ─────────────────────────────────────────────────────────────
FROM node:20-alpine AS runner
WORKDIR /app
# Non-root user
RUN addgroup --gid 1001 nodejs && \
adduser --system --uid 1001 nextjs
# Production dependencies only
COPY --from=deps --chown=nextjs:nodejs /app/node_modules ./node_modules
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
COPY --from=builder --chown=nextjs:nodejs /app/public ./public
USER nextjs
EXPOSE 3000
ENV NODE_ENV=production PORT=3000
CMD ["node", "server.js"]# ─── Build Stage ─────────────────────────────────────────────────────────────
FROM golang:1.22-alpine AS builder
WORKDIR /app
# Module download (separate layer for caching)
COPY go.mod go.sum ./
RUN go mod download
# Build (static binary, no CGO)
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo \
-ldflags="-w -s" -o server ./cmd/server
# ─── Final Stage (distroless - no shell, minimal attack surface) ──────────────
FROM gcr.io/distroless/static:nonroot
WORKDIR /
COPY --from=builder /app/server /server
USER nonroot:nonroot
EXPOSE 8080
ENTRYPOINT ["/server"]Always create .dockerignore to prevent bloating the build context:
.git
.gitignore
node_modules
.next
__pycache__
*.pyc
.env
.env.*
*.log
.DS_Store
dist/
build/
coverage/
*.test.ts
*.spec.ts
README.md
docker-compose*.yml# docker-compose.yml
services:
app:
build:
context: .
target: builder # use build stage for dev (includes dev deps)
ports:
- "3000:3000"
environment:
- NODE_ENV=development
- DATABASE_URL=postgresql://postgres:password@db:5432/myapp
- REDIS_URL=redis://cache:6379
volumes:
- .:/app # hot reload
- /app/node_modules # exclude node_modules from bind mount
depends_on:
db:
condition: service_healthy
cache:
condition: service_started
restart: unless-stopped
db:
image: postgres:16-alpine
environment:
POSTGRES_DB: myapp
POSTGRES_USER: postgres
POSTGRES_PASSWORD: password
volumes:
- postgres_data:/var/lib/postgresql/data
- ./scripts/init.sql:/docker-entrypoint-initdb.d/init.sql
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 5s
retries: 5
ports:
- "5432:5432"
cache:
image: redis:7-alpine
command: redis-server --maxmemory 256mb --maxmemory-policy allkeys-lru
ports:
- "6379:6379"
volumes:
- redis_data:/data
volumes:
postgres_data:
redis_data:# docker-compose.prod.yml
services:
app:
build:
target: runner # production stage
environment:
- NODE_ENV=production
volumes: [] # no bind mounts in production
deploy:
replicas: 3
restart_policy:
condition: on-failure
max_attempts: 3Run with: docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d
# Build
docker build -t myapp:1.0 .
docker build --target builder -t myapp:dev . # specific stage
docker build --no-cache -t myapp:latest . # force fresh build
# Run
docker run -p 3000:3000 --env-file .env myapp:1.0
docker run -d --name myapp -p 3000:3000 myapp:1.0 # detached
# Docker Compose
docker compose up -d # start all services
docker compose up --build # rebuild and start
docker compose logs -f app # follow logs
docker compose exec app sh # shell into running container
docker compose down -v # stop and remove volumes
# Inspect / Debug
docker ps -a # list all containers
docker logs myapp # view container logs
docker exec -it myapp sh # interactive shell
docker inspect myapp # full container metadata
# Cleanup
docker system prune -af # remove all unused images and containers
docker volume prune # remove unused volumes# 1. Non-root user (mandatory)
USER appuser
# 2. Read-only filesystem
# docker run --read-only --tmpfs /tmp myapp
# 3. Drop capabilities
# docker run --cap-drop ALL --cap-add NET_BIND_SERVICE myapp
# 4. No new privileges
# docker run --security-opt no-new-privileges myapp
# 5. Scan for vulnerabilities
# docker scout cves myapp:latest
# trivy image myapp:latestOrder Dockerfile instructions from least frequently changing to most frequently changing:
# ✅ Optimal order
COPY package*.json ./ # changes infrequently → cache hit most of the time
RUN npm ci # only re-runs when package.json changes
COPY . . # app code changes most often - put last
RUN npm run build
# ❌ Wrong order - kills caching
COPY . . # app code changes → invalidates all subsequent layers
RUN npm ci # runs npm install on every code change!node:20.12.0-alpine.dockerignore--only=production or multi-stage buildsARG API_KEY or ENV SECRET= in Dockerfile; use Docker secrets or runtime env varsHEALTHCHECK so orchestrators know when to restart~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.