Saltar al contenido

GitHub Actions Validator: Detecta errores de workflow y agujeros de seguridad antes de hacer push.

Se ejecuta en tu navegador: nada de lo que pegas sale de esta página. Cómo lo demostramos

Playground del validador de workflows de GitHub Actions

Example
.github/workflows/ci.yml
name: PR Build (insecure)

# pull_request_target runs with a privileged token AND secrets.
on:
  pull_request_target:
    types: [opened, synchronize]

# write-all grants the token every scope — far more than this job needs.
permissions: write-all

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      # Checking out the PR head under pull_request_target executes
      # attacker-controlled code with your write token: the "pwn request".
      - uses: actions/checkout@v4
        with:
          ref: ${{ github.event.pull_request.head.sha }}

      # The PR title is attacker-controlled and is interpolated straight into
      # the shell, so a crafted title can break out and run its own commands.
      - name: Greet the contributor
        run: echo "Thanks for the PR titled ${{ github.event.pull_request.title }}"

      # Third-party action pinned to a mutable tag, not a commit SHA.
      - uses: some-vendor/deploy-action@v2
        with:
          token: ${{ secrets.DEPLOY_TOKEN }}

Results update as you type — press ⌘/Ctrl + Enter to run now. Press Esc to release the editor.

fig. 03 — github-actions-validator · ci/cd 1 error · 3 warnings · 2 info
Results

Errors 1

errorError: pull_request_target checks out untrusted PR code.Line 17

This workflow triggers on `pull_request_target` (privileged token + secrets) and checks out the pull request head. Any fork contributor can run arbitrary code with your repository’s write token — the classic “pwn request” vulnerability.

Fix

Do not check out PR head in pull_request_target. Use pull_request for untrusted code, or split into a privileged job (no checkout of PR code) and an unprivileged build job, and never expose secrets to checked-out PR code.

Warnings 3

warningWarning: Top-level `permissions: write-all` is over-broad.Line 9

`write-all` grants the GITHUB_TOKEN write access to every scope (contents, packages, deployments, …). A single compromised step or action then inherits all of it.

Fix

Declare only the scopes you need, e.g. permissions: { contents: read }, and elevate per-job where a specific write is required.

warningWarning: Untrusted input interpolated into a shell command.Line 24

An attacker-controlled GitHub context (such as a PR title, body, or branch name) is expanded directly inside a `run:` step. Because `${{ … }}` is substituted before the shell executes, a malicious value can inject and run arbitrary commands.

Fix

Pass the value through an environment variable instead of inline interpolation, e.g. set env: { TITLE: ${{ github.event.pull_request.title }} } and reference "$TITLE" in the script (quoted).

warningWarning: Third-party action “some-vendor/deploy-action@v2” is not pinned to a commit SHA.Line 27

This action is referenced by a mutable tag or branch (e.g. `@v4` or `@main`). The maintainer — or anyone who compromises the action — can repoint it to malicious code that runs with your workflow’s token and secrets.

Fix

Pin to a full 40-character commit SHA, e.g. uses: some-vendor/…@<sha>, and add a comment with the human-readable version. Tools like Dependabot can keep the SHA up to date.

Info 2

infoInfo: First-party action “actions/checkout@v4” is pinned to a tag, not a SHA.Line 17

This action is maintained by GitHub, so the risk is lower, but tags and branches are still mutable. Pinning to a full commit SHA guarantees the exact code you reviewed runs every time.

Fix

Pin to a full 40-character commit SHA, e.g. uses: actions/…@<sha>, and add a comment with the human-readable version. Tools like Dependabot can keep the SHA up to date.

infoInfo: Secrets referenced in a pull_request-triggered workflow.Line 29

Workflows triggered by `pull_request` from a fork do not receive repository secrets by default. If this runs as `pull_request_target`, the secrets ARE available to untrusted PR code — make sure that is intended.

Fix

Confirm whether forks need these secrets. Prefer running secret-dependent steps on a trusted event (push, workflow_run) rather than untrusted PRs.

Pega un workflow de GitHub Actions y obtén sus errores de YAML más las configuraciones de seguridad incorrectas que los linters habituales pasan por alto — al instante, en tu navegador. Sin instalación, sin registro.

La brecha

La sintaxis es la parte fácil.

actionlint clava la sintaxis del workflow — tipado de expresiones, dependencias de jobs, incluso shellcheck en tus bloques run. Sigue usándolo. Pero un workflow puede ser YAML perfectamente válido y aun así entregar tus secretos a un atacante.

Los errores peligrosos son los de seguridad: ejecutar código de fork no confiable bajo pull_request_target, interpolar texto controlado por un atacante dentro de un shell, traer una acción de terceros mediante una etiqueta que su autor puede mover de forma silenciosa, o conceder un token write-all a un job que solo necesita leer. Estos son los patrones detrás de incidentes reales de cadena de suministro — consulta la guía de fortalecimiento de seguridad de GitHub y el análisis del pwn-request.

Este validador comprueba ambas cosas —el YAML y las configuraciones de seguridad incorrectas— para que puedas validar un workflow de GitHub Actions antes de hacer push, sin nada que instalar y sin nada que subir.

Consulta la lista completa en las comprobaciones de seguridad, o prueba el playground en vivo de arriba.

El pipeline

Cómo funciona.

Cinco pasos deterministas se ejecutan de principio a fin en cada validación — todos dentro de la pestaña de tu navegador, cada vez.

  1. Analiza el YAML.

    Tu workflow se analiza con un lector de YAML: los errores de sintaxis, la indentación incorrecta y los problemas estructurales se reportan con la línea que falló.

  2. Mapea el workflow.

    Los triggers, los jobs, los permisos y cada paso se leen en un modelo para que el analizador sepa qué se ejecuta, cuándo y con qué token.

  3. Ejecuta las comprobaciones.

    Cada regla de seguridad recorre el modelo, buscando combinaciones riesgosas de trigger + checkout, expresiones no confiables en bloques run, anclajes de acciones mutables y permisos amplios.

  4. Ordena por severidad.

    Los hallazgos se gradúan como altos, medios o bajos para que los riesgos de pwn-request e inyección suban a lo más alto, por delante de las nimiedades de estilo.

  5. Muestra la solución.

    Cada hallazgo enlaza la ubicación exacta con una remediación concreta: el trigger seguro, el patrón de variable env, el SHA anclado, el permiso acotado.

Comprobaciones de seguridad

Las 4 comprobaciones que importan.

Más allá de la validez del YAML, el analizador busca las configuraciones incorrectas que conducen a secretos filtrados y pipelines comprometidos. Cada una de las de abajo muestra el patrón riesgoso y la solución.

pull_request_target + checkout (pwn request)

Severidad High

Un workflow con pull_request_target se ejecuta con los secretos del repo base y un token de lectura/escritura, pero puede dispararse desde cualquier fork. Hacer checkout y ejecutar el head del PR ejecuta código no confiable con tus secretos.

Riesgoso

.github/workflows/ci.yml
# DANGEROUS — runs fork code with base-repo secrets
on: pull_request_target
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          ref: ${{ github.event.pull_request.head.sha }}
      - run: npm ci && npm run build   # attacker's code, your token

Corregido

.github/workflows/ci.yml
# SAFE — untrusted code runs with no secrets
on: pull_request          # not pull_request_target
permissions:
  contents: read
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4   # checks out the PR safely
      - run: npm ci && npm run build

Script injection via ${{ github.* }}

Severidad High

Interpolar un valor controlable por un atacante —un título de PR, un nombre de rama o el cuerpo de una issue— directamente en un bloque run permite que ese valor se escape de la cadena y se ejecute como comandos de shell.

Riesgoso

step
# DANGEROUS — title is substituted into the shell verbatim
- run: echo "Title: ${{ github.event.pull_request.title }}"
  # a title of  a"; rm -rf ~ #  runs as a command

Corregido

step
# SAFE — pass through env, quote, treat as data
- env:
    TITLE: ${{ github.event.pull_request.title }}
  run: echo "Title: $TITLE"

Unpinned third-party actions

Severidad Medium

Una referencia de etiqueta o rama (@v4, @main) es mutable: el mantenedor de la acción, o cualquiera que comprometa su cuenta, puede moverla a código nuevo que luego se ejecuta en tu pipeline con tu token.

Riesgoso

step
# RISKY — mutable references can change under you
- uses: some-org/deploy-action@v2
- uses: another-org/setup@main

Corregido

step
# SAFE — pin to an immutable full commit SHA
- uses: some-org/deploy-action@a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0  # v2.3.1
- uses: another-org/setup@0f1e2d3c4b5a69788796a5b4c3d2e1f00112233  # main @ 2024-05

Over-broad GITHUB_TOKEN permissions

Severidad Medium

permissions: write-all (o un bloque permissions omitido sobre un valor por defecto permisivo) entrega a cada paso un token que puede hacer push de código, publicar paquetes y editar issues, mucho más de lo que la mayoría de los jobs necesitan.

Riesgoso

.github/workflows/ci.yml
# RISKY — every job gets a fully privileged token
permissions: write-all
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - run: ./run-tests.sh

Corregido

.github/workflows/ci.yml
# SAFE — least privilege, scoped to what's needed
permissions:
  contents: read
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - run: ./run-tests.sh

Los permisos se pueden acotar por job o para todo el workflow — consulta la referencia de permisos de GitHub. La detección de pipe-to-shell y de secretos en pull requests también funciona hoy, con más por venir pronto.

Análisis estático

Una nota sobre el alcance: esto es un analizador estático — lee tu workflow sin ejecutarlo, así que detecta los patrones de configuración incorrecta de alta señal de arriba y los errores de YAML, pero no ejecuta jobs ni resuelve el contenido en vivo de las acciones referenciadas. Trata un resultado limpio como una sólida confianza previa al push, y aun así combínalo con actionlint, protecciones de rama y valores por defecto de mínimo privilegio a nivel de la organización.

FAQ

Tus preguntas, respondidas.

Toca una pregunta para desplegar la respuesta.

Dos cosas a la vez. Primero, analiza el YAML de tu workflow y reporta errores de sintaxis, indentación incorrecta y fallos estructurales: las cosas que hacen que una ejecución falle antes de que se ejecute un solo paso. Segundo, y más importante, ejecuta un conjunto de comprobaciones de configuraciones de seguridad incorrectas que los linters habituales pasan por alto: el patrón «pwn request» de pull_request_target + checkout, la inyección de scripts a través de expresiones ${{ github.* }} no confiables, acciones de terceros ancladas a una etiqueta mutable en lugar de a un SHA de commit, y permisos demasiado amplios como write-all. Obtienes errores y patrones peligrosos en una sola pasada.

No. El validador se ejecuta 100% del lado del cliente. El YAML de tu workflow se analiza dentro de la pestaña de tu navegador: no se sube nada a un servidor, y no hay cuenta ni registro. Puedes pegar con seguridad workflows internos o propietarios, incluidos los que tienen nombres de secretos y etiquetas de runners privados.

actionlint es excelente en sintaxis, tipado de expresiones e integración con shellcheck, y deberías seguir usándolo. Pero los errores que de verdad terminan comprometiendo repositorios son los de seguridad: ejecutar código no confiable con un token privilegiado, interpolar texto controlado por un atacante directamente en un shell, o traer una acción de terceros mediante una etiqueta que su autor puede mover de forma silenciosa. Esta herramienta se centra en esas configuraciones de seguridad incorrectas, valida también el YAML y no necesita instalar nada.

Un workflow disparado por pull_request_target se ejecuta en el contexto del repositorio base, con acceso a sus secretos y a un GITHUB_TOKEN de lectura/escritura, pero puede ser disparado por un pull request desde cualquier fork. Si ese workflow además hace checkout del head del PR (actions/checkout con la ref del PR) y luego compila, prueba o ejecuta scripts a partir de él, el código del fork de un atacante se ejecuta con tus secretos. Ese es el clásico «pwn request». El validador señala pull_request_target siempre que se combine con un checkout de código no confiable del PR.

Cuando incrustas una expresión como ${{ github.event.issue.title }} o ${{ github.head_ref }} directamente dentro de un bloque run:, GitHub sustituye el valor en crudo, controlable por un atacante, dentro de tu script antes de que el shell lo analice. Un título como a"; rm -rf / # se vuelve ejecutable. La solución es pasar los valores no confiables a través de una variable env: y referenciarlos como "$TITLE" para que el shell los trate como datos, nunca como código. El validador señala las expresiones del contexto github no confiables usadas dentro de pasos run.

Una referencia como actions/checkout@v4 o some-org/action@main apunta a una etiqueta o rama que el mantenedor de la acción —o un atacante que comprometa su cuenta— puede mover a código nuevo en cualquier momento, y ese código se ejecuta en tu pipeline con tu token. Anclar a un SHA de commit completo de 40 caracteres (uses: actions/checkout@<sha>) hace que la versión sea inmutable. El validador señala las acciones de terceros ancladas a una etiqueta o rama mutable.

No. Este validador es una herramienta independiente y comunitaria, y no está afiliada, respaldada ni patrocinada por GitHub, Inc. «GitHub» y «GitHub Actions» se usan aquí solo de forma descriptiva, para identificar el formato de workflow que la herramienta comprueba.

Pega el YAML del workflow en un validador basado en navegador para detectar errores antes de hacer commit: sin instalar nada y sin necesidad de push. Esta herramienta ejecuta el conjunto completo de comprobaciones (estructura YAML más configuraciones de seguridad incorrectas) por completo en tu navegador, así que obtienes feedback instantáneo sobre errores de sintaxis, runs-on faltante, nombres de trigger incorrectos y patrones peligrosos como acciones sin anclar o inyección de scripts antes de que el workflow llegue a GitHub.

Los problemas frecuentes incluyen un runs-on faltante en un job, un paso que especifica a la vez uses y run, una referencia needs que apunta a un job que no existe, un nombre de evento trigger inválido y una indentación YAML incorrecta. El validador detecta cada uno de estos como errores estructurales con el número de línea donde ocurre el problema, para que puedas corregirlos antes de que la ejecución falle en GitHub.

La mayoría de los verificadores estáticos —actionlint, zizmor, action-validator— requieren una CLI o una toolchain de Rust/Go. Este validador es gratuito, no necesita instalación ni registro, y se ejecuta por completo en tu navegador. Comprueba tanto la validez del YAML como las configuraciones de seguridad incorrectas (acciones sin anclar, inyección de plantillas, triggers peligrosos, permisos de token demasiado amplios) que cubren las herramientas de CLI, sin ninguna configuración local.

More free, private DevOps tools.

El validador de GitHub Actions es una herramienta dentro de OpsCanopy — una creciente cubierta de validadores, conversores y probadores basados en navegador que nunca tocan un servidor.

¿Empiezas con Docker?  Lee la guía de Docker →

42 herramientas gratuitas, todas pueden funcionar sin conexión — opscanopy.com funciona sin registro y sin subir nada.

Herramientas relacionadas: el conversor de ignores de CVE y AlertLint, el probador de reglas de Loki. Explora el directorio completo de herramientas.

No está afiliado, respaldado ni patrocinado por GitHub, Inc. GitHub y GitHub Actions son marcas comerciales de GitHub, Inc., usadas aquí solo de forma descriptiva para identificar el formato de workflow que esta herramienta comprueba.