Skip to content
cccskills
FreePublished on GitHub

meta-reviewing-infra-reviewing

Infrastructure code review — supply-chain pinning, secret exposure, workflow triggers and untrusted input, container hygiene, deployment safety. Load when reviewing a diff that touches CI/CD workflows, Dockerfiles, deployment configs or IaC.

The English summary is being prepared. Showing the original description.

Run score Not tested

See how to install

Files included (3)

  • SKILL.md15.9 KB
  • examples/core.md6.8 KB
  • metadata.yaml621 B

SKILL.md (original)

Review the instructions the agent will receive before you install.

Infrastructure Code Review Patterns

Quick Guide: Review operational code for what it can leak and what it lets a stranger run. Secrets, mutable third-party references and outsider input reaching a privileged job block the merge in any diff, at any scale; deployment ceremony scales with what the diff actually deploys. Recent facts that change the answer: actions/checkout v7 refuses fork-PR code under pull_request_target and workflow_run unless told otherwise; since December 2025 pull_request_target always runs the default branch's copy of the workflow; an organisation can enforce SHA pinning so a tag-pinned action fails at job setup; and GitHub's workflow dependency lockfile (public preview) pins what a uses: tag resolves to.

Detailed Resources:

  • examples/core.md — a good/bad pair for each pattern below: pinned actions and installs, secret mounts and log hygiene, permissions, privileged triggers and script injection, the non-root Dockerfile, deploy ordering, signal-safe startup

<critical_requirements>

Before reviewing infrastructure code

Grep the diff for credentials before reading it for anything else. A pushed secret stays reachable through clones, forks and the pull request's own refs, so the fix to ask for is revocation, not a follow-up commit — and the check takes seconds.

Resolve every external reference to something immutable. A tag or branch on an action, base image, provider or module runs whatever its owner — or whoever holds the owner's token — points it at next, with this pipeline's secrets in scope.

Trace each secret from its store to the one process that needs it. A build arg lands in image history, a job-level env: reaches every step including third-party actions, and log masking matches only the exact registered string — each hop the diff adds is a place it leaks.

Read a workflow's trigger and permissions: before its steps. Under pull_request_target and workflow_run the job holds the base repository's secrets and token while handling fork-controlled input, and a missing permissions: block inherits a default set in settings the diff never shows.

Treat every value an outsider can set as code. A PR title, branch name or comment body written as ${{ }} inside run: is spliced into the script before the shell parses it, so a quote in that value ends the string and the rest runs as commands.

Check that a production image ends on a non-root USER. Without one the process runs as root, and an application compromise starts one container-escape bug away from the host.

</critical_requirements>


Auto-detection: .github/workflows, uses:, permissions:, pull_request_target, workflow_run, GITHUB_TOKEN, secrets., allow-unsafe-pr-checkout, concurrency:, .gitlab-ci.yml, Dockerfile, .dockerignore, compose.yaml, FROM, USER, ARG, RUN --mount=type=secret, Kubernetes manifest, Helm chart, readinessProbe, terraform, .terraform.lock.hcl, Pulumi, CI review, Dockerfile review, IaC review

Applies to:

  • Diffs touching CI/CD workflow files — triggers, permissions, pinned references, secrets, job ordering
  • Dockerfiles, .dockerignore and compose files
  • Deployment manifests and platform configs — readiness, resources, shutdown, per-environment config
  • IaC — provider and module pinning, state, plan and apply in CI
  • Release and build scripts — what they publish, sign and tag, and which credentials they read
  • Lockfile and package-manager changes with supply-chain reach

Handled elsewhere:

  • Application code in the same diff — reviewed against its own layer's conventions; this skill reviews what builds, ships and runs it.
  • Severity vocabulary, comment format and the approve-or-request-changes call — the general review practice, which this skill's severities slot into.
  • Designing or restructuring a pipeline — a review names the defect and its fix; the platform's shape belongs to whoever owns it.
  • Incident response and live operations — a review gates a diff, not a running system.

<philosophy>

Philosophy

Operational code fails in production only. No unit test catches an unpinned action's compromise or a leaked deploy key, so the review is often the one gate this code passes — and a finding costs a line before merge and a rotation after.

Ceremony scales with blast radius: a production deploy path earns strictness, a lint workflow that deploys nothing does not. Read the diff against what it reaches —

  • A new env var must exist in every environment the app runs in; a renamed job or output must change everywhere that needs or reads it.
  • Ask what the pipeline does on a fork PR, on a re-run, and on two branches at once.

Don't flag:

  • Probes, resource limits or rollback strategy on a docs site or a workflow that deploys nothing
  • A multi-stage build for a CI-only image whose size nothing depends on
  • Missing caching in a job that runs in seconds
  • A vault migration in a diff that consumes an existing secret the established way
</philosophy>

<decision_framework>

Severity for infrastructure findings

Must Fix blocks approval. Should Fix travels with the approval, for the author to fix before merging, and needs a cost concrete in this diff. Nice to Have is the author's to take or leave.

Does the diff introduce a security defect?
├─ Credential literal, or a secret through ARG/ENV, a log line or an artifact → MUST FIX
├─ Third-party action on a tag, or any action on a branch (@v3, @main) → MUST FIX
├─ ${{ }} of outsider-controlled input inside run: → MUST FIX
├─ pull_request_target / workflow_run that checks out or runs PR head code → MUST FIX
├─ Write scopes a job doesn't use, or no permissions: on a workflow fed untrusted input → MUST FIX
├─ Production image without a non-root USER → MUST FIX
└─ NO → Is it an operational-correctness gap?
    ├─ GitHub's own action (actions/*) on a version tag instead of a full SHA → SHOULD FIX
    ├─ Base image on latest or untagged, or without a digest on a deploy path; CI install that ignores the lockfile → SHOULD FIX
    ├─ No permissions: block on any other workflow → SHOULD FIX
    ├─ Debug output beside secret use (set -x, env dumps) → SHOULD FIX
    ├─ Deploy job without a concurrency group, or not waiting on the tests → SHOULD FIX
    ├─ New env var missing from one environment → SHOULD FIX
    ├─ Cache key that never changes; cache-hostile Dockerfile layer order → SHOULD FIX
    ├─ New Dockerfile without .dockerignore; shell-form CMD or CMD via npm → SHOULD FIX
    ├─ New production service without a readiness check, resource requests or limits → SHOULD FIX
    └─ NO → Is it a genuine enhancement?
        ├─ Slimmer base image where size demonstrably matters → NICE TO HAVE
        ├─ Faster caching for an already-fast job → don't write it
        ├─ Production ceremony on a workflow that deploys nothing → don't write it
        └─ Tool preference (compose vs Kubernetes, one package manager over another) → don't write it

</decision_framework>


<patterns>

Core patterns

Pattern 1: Supply-Chain Pinning

Every external reference resolves to something immutable, and the record of it is committed.

  • Third-party actions on a full 40-character commit SHA from the action's own repository, not a fork; a trailing # vX.Y.Z comment keeps it readable
  • GitHub's own actions/* on a full SHA as well — GitHub's secure-use guidance makes no first-party exception, and an organisation's SHA-pinning policy fails any unpinned action at job setup. A version tag here is Should Fix rather than Must Fix, because GitHub is a smaller trust grant than a third party
  • Base images on a version tag at minimum, and on a digest on deploy paths — latest changes the build with no diff to review, and a publisher can re-point any tag; a digest is the only immutable form
  • CI installs from the committed lockfile and fails on drift (npm ci, or the package manager's frozen-lockfile mode)
  • Terraform providers and modules carry version constraints, and .terraform.lock.hcl is committed
# Must Fix: whoever controls this repository's tags decides what runs next
- uses: some-org/deploy-action@v4

# Good: immutable; the comment keeps it readable
- uses: some-org/deploy-action@8f4b7f84864484a7bf31766abe9204da3cbe65b3 # v4.1.2

Full code: examples/core.md

Pattern 2: Secret Exposure

Secrets reach the process that needs them and nothing else.

  • No literal tokens, keys, passwords or connection strings — grep for token, key, secret, password and ://user:pass@ shapes
  • Secrets come from the platform's secret store, never a committed file
  • No secret in a Dockerfile ARG or ENV — a secret mount exists for one RUN and in no layer
  • No secret in workflow- or job-level env: when one step uses it — scope it to that step
  • No line prints a secret or a value derived from one; derived values are registered with ::add-mask::
  • Artifacts and caches cannot contain .env files, credentials, Terraform state or saved plans
  • .gitignore and .dockerignore cover the .env and credential paths the diff introduces
# Must Fix: the token is in `docker history` and in the .npmrc layer
ARG NPM_TOKEN
RUN echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > .npmrc && npm ci

# Good: the credentials file is mounted for this one RUN
RUN --mount=type=secret,id=npmrc,target=/root/.npmrc npm ci

Full code: examples/core.md

Pattern 3: Workflow Trust Boundaries

What a workflow can reach is set by its trigger and its permissions:; what an outsider can make it do is set by where their input lands.

  • permissions: read-only (or {}) at workflow level, write scopes granted per job
  • pull_request_target and workflow_run only where the job needs the base repository's secrets or write token, and never checking out or running PR head code
  • Under those triggers: no allow-unsafe-pr-checkout: true, no actions/checkout older than v7, no manual fetch of the PR head — v7's refusal guards only its own default path
  • Outsider-set values — github context fields ending in title, body, head_ref, label, message, name, email or ref — reach run: through env:, never as ${{ }} in the script
# Must Fix: the title is pasted into the script before bash parses it
- run: echo "Checking ${{ github.event.pull_request.title }}"

# Good: the shell receives data, not script
- env:
    PR_TITLE: ${{ github.event.pull_request.title }}
  run: echo "Checking $PR_TITLE"

Full code: examples/core.md

Pattern 4: Dockerfile Hygiene

The image is minimal, cache-friendly and unprivileged.

  • Production stage ends on a non-root USER
  • A multi-stage build keeps compilers and dev dependencies out of the image that ships
  • Dependency manifests are copied and installed before the source, or every code change reinstalls everything
  • .dockerignore excludes .git, .env and local dependency folders — COPY . . copies whatever the context holds
  • Base image minimal for the job (slim, alpine, distroless) where the app's native dependencies allow
# Should Fix: every source change busts the dependency layer
COPY . .
RUN npm ci && npm run build

# Good: the install layer is reused until a manifest changes
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
RUN npm run build

Full code: examples/core.md

Pattern 5: Pipeline Correctness

The jobs compose the way the author thinks they do.

  • needs: matches real dependencies — deploy waits for every test job, not only lint
  • Deploy jobs share a concurrency group, so two quick merges cannot deploy out of order
  • Cache keys include a lockfile hash (hashFiles(...)); a static key never invalidates
  • A renamed job, output or env var is renamed at every reference in the same diff
concurrency:
  group: deploy-production
  cancel-in-progress: false
jobs:
  deploy:
    needs: [lint, test, e2e]

Full code: examples/core.md

Pattern 6: Deployment Safety, Scoped to the Diff

When the diff changes how production runs, check it can fail safely.

  • Services behind a load balancer or orchestrator have a readiness check — without one, traffic reaches a container still booting
  • New containers on a shared cluster declare resource requests and limits — requests are what the scheduler places by, and a memory limit makes a leak kill its own container rather than starve its neighbours
  • The app receives SIGTERM and finishes in-flight work inside the grace period: exec-form CMD, not shell form or npm start, and a handler or an init process, because PID 1 gets no default signal behaviour
  • Every env var and secret the diff introduces exists in every environment the app deploys to
  • IaC backend and locking changes are deliberate, a risky change (a backend move, a destroy or a replace) comes with its terraform plan output so the reviewer sees what the apply will do, and apply in CI runs the plan CI produced against the real state
# Should Fix: /bin/sh -c is PID 1 and does not pass SIGTERM on
CMD node dist/server.js

# Good: the app receives the signal itself
CMD ["node", "dist/server.js"]

Full code: examples/core.md

</patterns>

<red_flags>

Red flags

Common mistakes:

  • SHA-pinning actions/* while a third-party action stays on a tag — backwards, since the third party is the larger trust grant
  • Asking only for a leaked secret's removal from the diff — it is already pushed, so ask for revocation
  • "Fixing" a slow image build by dropping the lockfile instead of reordering the layers
  • Approving an apply in CI because a plan passed locally — against different state it proves nothing about this run

Surprising behaviour:

  • Build-arg values appear in docker history and in max-mode provenance attestations.
  • Log masking redacts the exact registered value; a base64, URL-encoded or substring form prints in clear, and a secret stored as a JSON blob is not reliably masked.
  • Every step in a job sees workflow- and job-level env:, third-party actions included.
  • A job's permissions: block is that job's whole grant: every scope it omits is none, whatever the workflow level said.
  • GITHUB_TOKEN's default comes from organisation and repository settings the diff doesn't show; fork PRs get read-only unless the repository sends write tokens to forks.
  • Since 8 December 2025, pull_request_target runs the default branch's copy of the workflow, so a PR editing that workflow is not exercised by its own run.
  • In a repository with a workflow dependency lockfile, the run executes the commit the lockfile records, so a uses: tag is not mutable there; review the lockfile's changed commit entries instead.
  • In image:tag@sha256:… the digest decides and the tag is a label.
  • .terraform.lock.hcl locks providers only; a module floats within its version constraint unless the constraint is exact.
  • sensitive = true hides a Terraform value from CLI output, not from state or plan files, which hold it in plaintext — a plan uploaded as an artifact ships the secret.
  • Alpine uses musl: a native module compiled in a glibc build stage fails to load in an Alpine runtime stage.
  • Cleanup that outlasts the grace period never finishes — the platform sends SIGKILL (Kubernetes' default is 30 seconds, preStop time included).

</red_flags>

Reviews

No reviews yet. Tell others what you thought.

Comments

No login needed. Share how it worked for you, ask a question, or note where it did not run.

No comments yet. Be the first to write one.

Comments are published right away. Do not include personal information such as email addresses or phone numbers. Up to 2 URLs.

0 / 1,000 characters

This form is protected by reCAPTCHA, and the Google Privacy Policy and Terms of Service apply.

More skills from this repository

Hugging Face Inference SDK patterns for TypeScript/Node.js — InferenceClient setup, chat completion, text generation, streaming, embeddings, image generation, audio transcription, translation, summarization, and Inference Endpoints

The English summary is being prepared. Showing the original description.

agents-inc/skills25Updated Oct 23, 2026

LiteLLM proxy server setup, TypeScript client patterns via OpenAI SDK, model routing, fallbacks, load balancing, spend tracking, virtual keys, and production deployment

The English summary is being prepared. Showing the original description.

agents-inc/skills25Updated Oct 23, 2026

Serverless GPU compute platform for AI model deployment — web endpoints, GPU functions, model serving, and TypeScript client patterns

The English summary is being prepared. Showing the original description.

agents-inc/skills25Updated Oct 23, 2026

Local LLM inference with the Ollama JavaScript client -- chat, streaming, tool calling, vision, embeddings, structured output, model management, and OpenAI-compatible endpoint

The English summary is being prepared. Showing the original description.

agents-inc/skills25Updated Oct 23, 2026

Replicate SDK patterns for TypeScript/Node.js -- client setup, predictions, streaming, webhooks, file handling, model versioning, deployments, and training

The English summary is being prepared. Showing the original description.

agents-inc/skills25Updated Oct 23, 2026

Together AI SDK patterns for TypeScript — client setup, chat completions, streaming, structured output, function calling, embeddings, image generation, fine-tuning, and OpenAI-compatible endpoints

The English summary is being prepared. Showing the original description.

agents-inc/skills25Updated Oct 23, 2026

See all skills by agents-inc

Report a problem with this skill