agentsclimarketplace

Actions security

Skill bitwise-media-group/skills/plugins/actions/skills/actions-security

Coding agent marketplace for skills used by the BitWise Media Group.

Install
npx -y skills add bitwise-media-group/skills --skill actions-security

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 2 stars2 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

Security hardening for GitHub Actions workflows with a least-privilege posture — default-deny permissions ({} then minimal per-job scopes), SHA-pinning every third-party action, avoiding pull_request_target (and the safe trusted-event allowlist pattern when an elevated trigger is unavoidable), preventing expression/script injection via env indirection, explicit secrets (no inherit), and OIDC over long-lived keys. Use when writing or reviewing GitHub Actions workflows for security, locking down permissions or GITHUB_TOKEN, deciding whether pull_request_target is safe, handling untrusted PR input or secrets, or pinning actions.

The file declares its own license as MIT. That is the author’s claim about this one file, and it is not the same thing as the license GitHub reports for the repository, which is listed with the other numbers below.

SKILL.md

5.1 KB, as published. Nobody here has run it

GitHub Actions security posture

A reusable workflow cannot constrain how a consumer wires its trigger, and a CI workflow runs on code a stranger proposed. Treat every workflow as reachable by an untrusted contributor and apply least privilege. These rules are enforced mechanically by the actions-validate skill (actionlint + zizmor) and CodeQL; the authoring conventions they assume live in actions-style. For attack anatomy and extended examples, see reference.md.

1. Default-deny permissions

Declare permissions: {} at the top level and grant each job only the scopes it needs. Never use permissions: write-all, and never rely on the repository default token (it can be broad).

permissions: {} # deny everything by default
jobs:
  build:
    permissions:
      contents: read # the minimum: check out the repo

A job that uploads code-scanning results adds security-events: write; one that comments on a PR adds pull-requests: write. Grant at the job level so a compromised step holds only that job's token. A reusable workflow's jobs can never exceed the caller's grant — keep that ceiling tight.

2. Pin every third-party action to a full commit SHA

A tag (@v4) or branch (@main) is mutable: whoever controls the action's repo can repoint it at new code that then runs with your token. Pin to a full 40-character commit SHA with the version in a comment — for first-party actions too.

- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3

Keep pins fresh with Dependabot's github-actions ecosystem so updates arrive as reviewable PRs:

version: 2
updates:
  - package-ecosystem: github-actions
    directory: /
    schedule:
      interval: daily

3. Avoid pull_request_target

pull_request_target runs in the base repository's context — with repository secrets and a read/write GITHUB_TOKEN — while the pull request comes from an untrusted fork. Check out and run the PR's head code under it and a fork author can exfiltrate those secrets (the "pwn request"). So:

  • Test PR code with pull_request, not pull_request_target. Fork PRs then get a read-only token and no secrets — safe by construction.

  • If you genuinely need base context (label, size, or comment on a PR), run no PR-controlled code and never check out the PR head. The org's merge-notice and dependabot-merge callers use pull_request_target safely because they only call the API — they check out and run nothing.

  • For privileged build/release jobs that a fork-controllable event could reach, gate them on a positive trusted-event allowlist so the path fails closed:

    if: ${{ contains(fromJSON('["push","workflow_dispatch","schedule"]'), github.event_name) }}
    

4. Prevent expression/script injection

Never interpolate attacker-controlled text — a PR title, branch name, or comment body — directly into a run: script. The value is substituted before the shell runs, so $(...) or backticks in a PR title execute as code. Pass it through a quoted environment variable and reference the variable:

# WRONG — the title is injected into the shell
- run: echo "Title: ${{ github.event.pull_request.title }}"

# RIGHT — the title is data in the environment, never code
- env:
    PR_TITLE: ${{ github.event.pull_request.title }}
  run: echo "Title: $PR_TITLE"

5. Handle secrets explicitly

  • Pass secrets to a reusable workflow by name through secrets:; never secrets: inherit, which hands over every secret in the repo.
  • Don't echo secrets or write them to logs/artifacts; GitHub masks known secret values but not values derived from them.
  • Prefer OIDC (permissions: id-token: write plus the cloud's federation) to authenticate to AWS/GCP/Azure over storing long-lived access keys as secrets.

6. Know the GITHUB_TOKEN

On a fork pull request the GITHUB_TOKEN is read-only and secrets are withheld; on a same-repo event it carries whatever permissions: you granted. Scope it down rather than up, and gate same-repo-only logic (label arming, review handling) so fork PRs skip it.

7. Enforce mechanically

Run the actions-validate loop (actionlint + zizmor) locally before committing, and scan actions with CodeQL in CI (the actions-reusable-workflows skill wires the shared codeql.yaml). Treat their findings — unpinned actions, injectable expressions, over-broad permissions — as violations, not suggestions.

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.