원클릭으로
zizmor
Audit GitHub Actions workflow and action.yml files for security issues using zizmor rules, then fix violations
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
메뉴
Audit GitHub Actions workflow and action.yml files for security issues using zizmor rules, then fix violations
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
SOC 직업 분류 기준
Navigation-only skill for individual actions in netcracker/qubership-workflow-hub. Use when a workflow needs to consume a specific Qubership action (Docker build/push, version/tag rendering, Maven/npm/Python publishing, package cleanup, Helm charts, security scans, etc.) and you need to find the right action and read its authoritative README. All rules (pinning, permissions, anti-hallucination, naming) live in qubership-workflow-conventions — this skill does not restate them.
Markdownlint ruleset from super-linter. Apply when writing or editing any .md file.
Single source of truth for Qubership GitHub Actions workflows. Use when designing, writing, reviewing, or debugging .github/workflows/*.yml that consume actions or templates from netcracker/qubership-workflow-hub or Netcracker/.github.
Update or create documentation for a specific action or reusable workflow based on git diff and action.yml
Audit files for EditorConfig violations (final newline, trailing whitespace, indent, line endings) and fix them in-place
Generate a PR title and description following project conventions, then create or update the PR via gh CLI
| name | zizmor |
| description | Audit GitHub Actions workflow and action.yml files for security issues using zizmor rules, then fix violations |
| arguments | ["files"] |
Security audit for GitHub Actions workflow and action.yml files.
Apply all rules in-memory when writing or editing any .yml workflow or action file.
When invoked via /workflow-audit [files], audit and fix target files directly.
rules:
dangerous-triggers:
ignore:
- cla.yaml
- pr-assigner.yml
Pedantic rules disabled — skip: anonymous-definition, self-hosted-runner,
stale-action-refs, undocumented-permissions.
| Rule | Flag if | Fix |
|---|---|---|
template-injection | Any ${{ }} interpolation directly inside a run: block (or actions/github-script script:) whose value is not a static/matrix value — this includes inputs.* and steps.*.outputs.* in action.yml composite actions, not just github.event.* sources. Common attacker-controlled sources: github.event.pull_request.title/body/head.ref/head.label, github.event.issue.title/body, github.event.comment.body, github.event.review.body, github.event.discussion.title/body, github.head_ref, github.event.workflow_run.head_branch/head_commit.message, and any ${{ inputs.* }} used in a composite action's run: step | Pass through a step-level env: var instead of direct interpolation, then reference as "${VAR_NAME}" (quoted) in the shell |
excessive-permissions | permissions: write-all anywhere; contents: write at workflow level (not job level); any write permission a job doesn't actually need; a job with no permissions: block at all (defaults to broad token permissions) | Move to job level; start from permissions: {} or permissions: contents: read; grant only what each job needs. Add an explicit permissions: block to every job, even if it's just contents: read |
unpinned-uses | Any uses: not pinned to a full 40-char SHA — branches (@main), mutable tags (@v4, @v1.2.3) are all flagged | Replace with SHA pin. Fetch: gh api repos/<owner>/<repo>/git/ref/tags/<tag> --jq '.object.sha'; if annotated tag: dereference with gh api repos/<owner>/<repo>/git/tags/<sha> --jq '.object.sha' |
artipacked | actions/checkout without persist-credentials: false — flagged unconditionally, not only when upload-artifact is present | Add persist-credentials: false to the checkout step |
secrets-inherit | secrets: inherit in a reusable workflow call | Replace with explicit named secrets only |
dangerous-triggers | pull_request_target or workflow_run AND any step checks out with PR head ref or runs shell using github.event.pull_request.* directly. Ignore if file matches config ignore list | Pin checkout ref: to github.sha; add persist-credentials: false; move untrusted input to env vars |
github-env | Writing untrusted event data to $GITHUB_ENV or $GITHUB_PATH in privileged trigger context | Use $GITHUB_OUTPUT for inter-step data; never write raw event data to GITHUB_ENV |
github-app | GitHub App token passed via ${{ steps.*.outputs.token }} directly in shell interpolation | Always pass via env: var, never interpolate directly |
secrets-outside-env | Deployment/publish job uses ${{ secrets.* }} but has no environment: declaration | Add environment: block — apply judgment, not every workflow needs it |
bot-conditions | if: checks github.actor == 'dependabot[bot]' or similar string comparison | Use github.actor_id == '49699333' or github.event.sender.type == 'Bot' |
unsound-contains | contains() with attacker-controlled input as haystack in security-sensitive condition | Use exact equality == instead |
unsound-condition | if: expression always evaluates to true due to type coercion (e.g. ${{ inputs.flag }} where string "false" is truthy) | Use explicit string comparison: inputs.flag == 'true' |
misfeature | continue-on-error: true on security-sensitive steps (scan, lint, auth); workflow_dispatch inputs used unsafely in shell | Remove continue-on-error from sensitive steps; pass dispatch inputs via env: |
unredacted-secrets | Secret value transformed or concatenated in shell (substring, base64, concat) — bypasses GitHub's auto-redaction | Avoid transforming secrets in shell; if unavoidable, mask result with ::add-mask:: |
concurrency-limits | Workflow triggered by push or pull_request with no concurrency: block | Add concurrency group — see workflow-patterns.md → Concurrency |
insecure-commands | ACTIONS_ALLOW_UNSECURE_COMMANDS: true in any env: block | Remove it; use $GITHUB_OUTPUT, $GITHUB_ENV, $GITHUB_STEP_SUMMARY |
hardcoded-container-credentials | Literal credential string in registry-password:, password:, or similar field | Move to ${{ secrets.* }} |
unpinned-images | container.image or services.*.image uses mutable tag (ubuntu:latest, postgres:15) | Pin to SHA256 digest: ubuntu@sha256:<digest> |
unpinned-tools | run: step does curl | bash, pip install <pkg> without version, npm install -g <pkg> without @version | Pin tool versions explicitly or use SHA-verified download |
cache-poisoning | Release workflow uses actions/cache with cache key derived from untrusted input | Use only deterministic keys (e.g. hash of lock files) |
overprovisioned-secrets | secrets: ${{ secrets }} — whole secrets context passed | Pass only named secrets the callee needs |
archived-uses | uses: references an archived GitHub repository | Replace with maintained alternative or vendor locally. Cannot auto-fix — report to user |
impostor-commit | SHA pin that cannot be verified as a real commit in the upstream repo — most common cause: tag object SHA used instead of commit SHA (annotated tags) | Fix: dereference the tag — gh api repos/<owner>/<repo>/git/ref/tags/<tag> --jq '.object.sha', then if .object.type == "tag" dereference again: gh api repos/<owner>/<repo>/git/tags/<sha> --jq '.object.sha' — use the final commit SHA |
known-vulnerable-actions | uses: references action version with known CVE or GitHub Security Advisory | Cannot auto-fix — report action name and version to user |
obfuscation | run: contains base64 -d, eval, or piped decode patterns | Cannot auto-fix — report to user for manual review |
ref-confusion | uses: references a ref that exists as both branch and tag in upstream repo | Pin to full SHA |
ref-version-mismatch | SHA pin comment doesn't match the actual tag for that SHA | Two cases: (1) annotated tag — the SHA is a tag object, not a commit: dereference with gh api repos/<owner>/<repo>/git/tags/<sha> --jq '.tag'. (2) SHA is a valid commit but not what the tag in the comment points to (common — e.g. a later untagged commit was pinned): resolve the tag's actual commit with gh api repos/<owner>/<repo>/git/refs/tags/<tag> --jq '.object.sha' and repin to that SHA, OR list gh api repos/<owner>/<repo>/tags --jq '.[] | .name + " " + .commit.sha' and find the tag whose commit matches the pinned SHA, updating the comment to that tag |
superfluous-actions | uses: calls an action whose functionality is already in the runner image | Remove the step. Cannot always auto-fix — report to user |
use-trusted-publishing | Publishing to PyPI/npm/RubyGems using ${{ secrets.PYPI_TOKEN }} etc. when registry supports OIDC | Migrate to OIDC trusted publishing with id-token: write |
Known pins used in this repo (always verify with API before applying):
| Action | SHA | Tag |
|---|---|---|
actions/checkout | de0fac2e4500dabe0009e67214ff5f5447ce83dd | v6.0.2 |
actions/upload-artifact | 043fb46d1a93c77aae656e7c1c64a875d1fc6a0a | v7.0.1 |
actions/download-artifact | 3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c | v8.0.1 |
actions/setup-python | a309ff8b426b58ec0e2a45f0f869d46889d02405 | v6.2.0 |
actions/setup-node | 53b83947a5a98c8d113130e565377fae1a50d02f | v6.3.0 |
actions/setup-go | 4a3601121dd01d1626a1e23e37211e3254c1c06c | v6.4.0 |
actions/cache | 668228422ae6a00e4ad889ee87cd7109ec5666a7 | v5.0.4 |
For actions not in this table — always fetch the current SHA via API before applying.
If files provided → audit those files. Otherwise:
Resolve base ref in priority order:
baseRefName from the open PR for the current branch (gh pr view --json baseRefName)gh repo view --json defaultBranchRef)main as final fallback — warn the user if this fallback is usedThen collect changed files:
git diff <BASE>..HEAD --name-only
Keep only .yml/.yaml files matching .github/workflows/ or actions/*/action.yml.
Read each file, check all rules above, collect violations.
Fix all auto-fixable violations directly with Edit. Do not ask the user.
Report violations that cannot be auto-fixed (archived-uses, impostor-commit, known-vulnerable-actions, obfuscation, superfluous-actions) — explain why.
Report: files audited, violations found per rule, fixed vs skipped.