SOC 직업 분류 기준
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
직접 명령은 검토 Prompt를 거치지 않습니다. 실행하기 전에 소스를 확인하세요.
npx skills add https://github.com/tomevault-io/skills-registry --skill harden-github-actions명령은 한 줄로 유지됩니다. 복사하기 전에 가로로 스크롤해 전체 내용을 확인하세요.
로컬 사본을 원하시나요? SkillsMP에서 현재 제공할 수 있는 파일을 다운로드하세요.
SKILL.md 표시 중
| Use when this capability is needed.
> Use when this capability is needed.
Review architecture and API design for the vfs-s3 project. Use when the user mentions @architect, asks to review an issue's design, discuss module boundaries, API shape, or architectural decisions for vfs-s3. Also trigger when the user wants to create an ADR (Architecture Decision Record) or evaluate a technical approach for the project. Intended for dispatch from Codex automation or Claude routines; GitHub trigger phrase: @vfs-s3-bot please prepare design doc Use when this capability is needed.
| name | harden-github-actions |
| description | > Use when this capability is needed. |
zizmor identifies security vulnerabilities in GitHub Actions workflows. This skill documents the decision guidelines for resolving each warning type: when to fix, how to fix, and when to suppress with an inline comment explaining why.
Core principle: Fix the vulnerability whenever possible. Suppress only when the fix would break required functionality, and always include a reason in the suppression comment.
This work should be done on a branch in a git worktree. Before starting any work, verify you are in the worktree directory and on the correct branch:
pwd # should be the worktree path
git branch # should show the feature branch, not main
Always work in this order. Each step is a separate commit.
bin/setup and bin/ci (see below). Skip if these
scripts don't exist in the project.pinact runpermissions: blocks. Replace with permissions: {} and add per-job permissions.
This goes beyond what zizmor flags — zizmor misses single-job workflows. Commit.Run pinact run --min-age 7 from the repository root. This pins all actions in .github/workflows/ to SHA hashes, skipping any versions published less than 7 days ago.
This value must match default-days in dependabot.yml — otherwise dependabot will propose updates that pinact refuses to pin, or pinact will pin versions dependabot has not yet cleared.
Always run zizmor with a GitHub token so that online audits (like ref-version-mismatch
and impostor-commit) can resolve SHAs against the GitHub API. Without a token, these audits
are silently skipped and findings will only surface in CI.
GITHUB_TOKEN=$(gh auth token) zizmor .
Filter severity by passing the flag --min-severity=<level> where level can be high, medium, or low. Informational warnings may be emitted by omitting this flag entirely.
For each severity level (high, then medium, then low, then informational):
zizmor --fix=all --min-severity=<level> . to auto-correct fixable findings (--fix alone uses safe mode which silently holds back some fixes; use --fix=all and rely on diff review as the safety net)cache-poisoning fixes will disable caching — almost always revert these and suppress insteadartipacked fixes add persist-credentials: false — revert if the workflow needs git pushsuperfluous-actions fixes replace actions with inline code — always revert these and suppress insteadbot-conditions auto-fix replaces github.actor with user.login — revert and apply the dual check instead (see rule file)template-injection fixes are generally correct--fix didn't handle. For excessive-permissions: you MUST research each action's permissions. Do not guess. See the permission research process below.zizmor --min-severity=<level> . to verify a clean check at this severity levelAfter completing all default severity levels, run a pedantic pass:
zizmor --persona=pedantic --min-severity=high .excessive-permissions on single-job workflows where zizmor's default persona doesn't
flag it. Apply the same fix: permissions: {} at workflow level, scoped per job.zizmor --persona=pedantic --min-severity=high . to verify cleanWhen you encounter a zizmor finding, read the corresponding rule file in references/ for
full decision guidance, suppression checklists, and examples. Only read the rules you need.
| Rule | File | Action |
|---|---|---|
artipacked | references/rule-artipacked.md | Fix (add persist-credentials: false); suppress only if job does git push |
template-injection | references/rule-template-injection.md | Always fix (move expressions to env: vars) |
excessive-permissions | references/rule-excessive-permissions.md | Always fix (set permissions: {} at workflow level, scope per job) |
dangerous-triggers | references/rule-dangerous-triggers.md | Fix or suppress with 5-point checklist |
secrets-outside-env | references/rule-secrets-outside-env.md | Fix (add environment:) or suppress with 3-point checklist |
bot-conditions | references/rule-bot-conditions.md | Always fix (dual check: actor + user.login); revert auto-fix |
superfluous-actions | references/rule-superfluous-actions.md | Always suppress (never replace with inline code) |
cache-poisoning | references/rule-cache-poisoning.md | Suppress (default); revert auto-fixes; only escalate if custom cache keys |
unpinned-images | references/rule-unpinned-images.md | Suppress (default); digest pinning is nontrivial |
dependabot-execution | references/rule-dependabot-execution.md | Fix or suppress with 3-point checklist |
dependabot-cooldown | references/rule-dependabot-cooldown.md | Always fix (add cooldown: default-days: 7 to all ecosystems) |
Permission mappings for excessive-permissions are in references/permission-mappings.md.
For findings not covered in this skill, consult https://docs.zizmor.sh/audits/ for detailed explanations and resolution guidance.
Always use inline comments with the rule name and a reason:
# zizmor: ignore[rule-name] -- reason why suppression is necessary
The -- separator before the reason is a convention for readability. Never suppress without
a reason. If you can't articulate why the fix would break something, apply the fix instead.
Add this job to the repository's main CI workflow file (often ci.yml or ci-checks.yml).
Placement matters. Before inserting, find the existing lint job (rubocop, eslint,
golangci-lint, etc.) in the workflow and place lint-actions immediately after it. If there
is no lint job, place it immediately before the first test job. Never append it to the end
of the file — it is a linting concern, not a test or deployment step:
lint-actions:
name: GitHub Actions audit
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
persist-credentials: false
- name: Run actionlint
uses: rhysd/actionlint@v1.7.11
- name: Run zizmor
uses: zizmorcore/zizmor-action@v0.5.2
with:
advanced-security: false
Use version tags, not SHA hashes — run pinact run --min-age 7 immediately after adding
this job to pin them. This ensures the SHAs match what pinact produces for the rest of the
workflow.
Before adding this job, check if the workflow already has a standalone actionlint job.
If it does, remove it — lint-actions replaces it. Do not create duplicate actionlint runs.
If the project has a bin/ci script (or equivalent like config/ci.rb), add workflow
linting so developers catch issues locally before pushing. If bin/setup also exists, add
tool installation there too. Skip this section entirely if there is no local CI script.
Check if actionlint, shellcheck, and zizmor are already installed. If not, install
them using the platform's package manager. Read the existing bin/setup script to understand
its conventions before adding to it.
shellcheck is required — actionlint uses it to lint shell scripts in run: blocks.
Without shellcheck, actionlint silently skips script checks and local results won't match CI.
Install all three tools using the same pattern:
for tool in actionlint shellcheck zizmor; do
if ! command -v "$tool" &> /dev/null; then
if command -v brew &> /dev/null; then
brew install "$tool"
elif command -v pacman &> /dev/null; then
sudo pacman -S --noconfirm "$tool"
else
echo "Error: install $tool manually" >&2
exit 1
fi
fi
done
Adapt this to match the script's existing style (e.g., if it uses functions, conditionals, or a different error pattern, follow that convention).
Add actionlint and zizmor as separate steps. Read the existing bin/ci script to understand
its conventions before adding to it.
# Lint GitHub Actions workflows
actionlint
zizmor .
Each tool should be a separate command so failures are clearly attributable. Place these near other linting steps if the script has them.
Ensure .github/dependabot.yml includes a github-actions entry with batching.
The schedule must be weekly — not daily.
- package-ecosystem: github-actions
directory: "/"
groups:
github-actions:
patterns:
- "*"
schedule:
interval: weekly
cooldown:
default-days: 7
The groups block batches all action updates into a single PR instead of one PR per action.
Add cooldown to every ecosystem entry in dependabot.yml. Use semver-granular cooldowns
for real package ecosystems so low-risk patches flow faster while major bumps get more soak
time:
# For package ecosystems (bundler, npm, gomod, gradle, pip, etc.)
cooldown:
semver-major-days: 7
semver-minor-days: 3
semver-patch-days: 2
default-days: 7
# For github-actions (semver-granular keys are NOT supported)
cooldown:
default-days: 7
If an ecosystem entry is missing the cooldown block, add it. If an existing cooldown block has different values, override them with the values above — these are the standard.
| Mistake | Correction |
|---|---|
| Guessing what permissions an action needs | Read the action's README. If it's not in the permission mappings table, research it before proceeding. |
Accepting cache-poisoning auto-fixes without review | --fix=all disables caching; almost always revert and suppress instead |
| Suppressing without a reason | Always explain WHY the fix can't be applied |
Suppressing template-injection | This should always be fixed, never suppressed |
Adding persist-credentials: false to a workflow that does git push | Suppress artipacked with a comment instead |
| Fixing permissions by removing the block entirely | Move to job-level, don't remove — implicit permissions may be too broad |
Using --fix instead of --fix=all | Safe mode silently holds back fixes; use --fix=all and review the diff |
| Committing without verifying clean zizmor output | Always re-run zizmor --min-severity=<level> . before committing |
| Analyzing all findings up front before starting work | Follow the workflow order step by step — CI job, dependabot, local linting, pin, then fix by severity |
| Adding the zizmor CI job at the end of the workflow file | Place it near existing lint jobs — it's a linting concern, not a test |
Replacing an action with inline code for superfluous-actions | Always suppress — actions are more maintainable and receive upstream fixes |
| Not specifying permissions on reusable workflow caller jobs | Caller jobs must declare permissions; reusable workflows inherit from the caller |
| Adding tools to bin/setup when there's no bin/ci | Only add local linting if a local CI script exists to run the tools |
| Running commands in the main repo instead of the worktree | Verify pwd and git branch before starting |
Automated reviewers (Copilot, cubic, etc.) frequently flag these. They are wrong or misleading — dismiss them.
| Feedback | Why it's wrong |
|---|---|
ruby/setup-ruby with bundler-cache: true needs actions: write | No. Bundler cache works with contents: read. The cache API uses the implicit GITHUB_TOKEN. Do not add actions: write. |
persist-credentials: false will break git fetch / git worktree | Only true for private repos. All our target repos are public — unauthenticated HTTPS fetch works fine. |
cooldown is not a valid Dependabot configuration key | It is valid. GitHub added cooldown to Dependabot v2 config in late 2025. Copilot's training data predates this feature. |
| Checkout version inconsistency (v3 in existing jobs vs v6 in lint-actions) | The skill pins existing versions as-is; upgrading is dependabot's job after merge. The lint-actions job template uses v6 independently. |
Source: basecamp/house-skills — distributed by TomeVault.