GitHub Action Package¶
Reusable GitHub Actions published through action.yml and consumed by workflows.
Charter status¶
| Charter status | Value |
|---|---|
| Maturity | Candidate rubric |
| What this state means | Numbered candidate rules carry severity, required evidence, and an exception policy. A versioned rubric can be cut from this page once its promotion criteria are met. |
| Full rubric | Not authored yet. This page is a charter, not a judgeable standard. |
| Reference implementation | vibecodeqa/ref-github-action-package (published) — see Reference implementation below. |
| Blocking promotion | Matured from a backlog note on 2026-08-09 (#37): candidate rules, upstream references, severity, exception policy, and CI evidence are on the page, and ref-github-action-package is now published in the catalog with a self-reported score, a green CI run, and a hand-argued R-GHA-1 to R-GHA-10 mapping (#44). Blocking v1: that mapping is prose rather than a scanner signal, no independent assessment of the repo exists, no release tag has been cut so R-GHA-8 is documented but never exercised, and the composite and Docker runtimes are still unseparated from the JavaScript rules. |
Maturity states are defined in Standards Authoring.
Metadata lives in standards/registry.json.
Full rubric¶
No full versioned rubric has been authored yet.
Reference implementation¶
| Score evidence | Value |
|---|---|
| Repository | vibecodeqa/ref-github-action-package (published) |
| VCQA report | A 100/100 |
| Verification | self-reported |
| Assessed commit | fd8722d |
| Assessed on | 2026-08-09 |
| CI run | success (2026-08-08) |
| Independent assessment | None published yet. The score above is the reference repo's own claim, not third-party proof. |
| Catalog entry last verified | 2026-08-09 |
Teaching focus¶
This standard teaches that a published action runs inside somebody else's repository, with
that repository's token. The consumer picks a version string, grants a set of permissions,
and hands over untrusted workflow context; from then on the action's metadata, its pinning
policy, and its handling of that token are the whole security boundary. Choosing this stack
means treating action.yml, the release tag, and the built artifact as a public contract
before adding features.
The three action runtimes are not interchangeable here. A JavaScript action ships a bundled artifact the consumer never builds, a composite action re-runs other actions under the caller's permissions, and a Docker action pulls an image. Rules below say which runtime each one applies to rather than pretending they are one shape.
Scope¶
Reusable GitHub Actions published through action.yml and consumed by workflows.
Not in scope¶
- ordinary workflows in
.github/workflows - CLIs that are not packaged as actions
- GitHub Apps
- reusable workflows (
workflow_call), which have their own input and permission model
Composes¶
Upstream references¶
- Metadata syntax for GitHub Actions
- Creating actions
- Secure use reference
- Workflow syntax
- Use
GITHUB_TOKENfor authentication - Node.js security best practices
- npm package.json docs
- OWASP Input Validation Cheat Sheet
VCQA-owned rule surface¶
- action.yml metadata completeness.
- input/output contract and declared-versus-read drift.
- minimum token permissions in the action's own documented examples.
- input validation before filesystem, network, or API side effects.
- secret-input masking and log/output leakage.
- bundled-artifact freshness for JavaScript actions.
- failure signalling through the action protocol.
- pinned runtime and dependency policy, including
uses:pinning inside composite actions. - release tag policy and consumer pinning guidance.
- consumer-shaped smoke evidence.
Detection signals¶
action.ymloraction.yamlat the repository or directory rootruns.using:node20/node22(JavaScript),composite, ordocker- declared
inputs,outputs, andbrandingblocks runs.main,runs.pre,runs.post, orruns.steps- a committed
dist/bundle alongsidesrc/for JavaScript actions @actions/core,@actions/github,@actions/exec, or@actions/tool-cachedependenciesuses:entries insideruns.stepsfor composite actionsDockerfileplusruns.imagefor Docker actions- README workflow examples containing
uses: <owner>/<repo>@ - release tags (
vX,vX.Y.Z) and a tag-moving release workflow - a committed lockfile and a bundler config (
esbuild,ncc, or equivalent)
Combination-born guidelines¶
- Document minimum
GITHUB_TOKENpermissions for every example workflow. - Inputs are validated before filesystem, network, or deployment side effects.
- Release tags and built artifacts must make consumer pinning reliable.
- A composite action inherits the caller's permissions and secrets, so every
uses:it runs is a transitive supply-chain dependency of every consumer. - A JavaScript action ships a bundle nobody rebuilds, so the committed artifact — not the source — is what runs in production.
- Untrusted workflow context (
github.event.*, branch names, issue and PR text) reaches the action as ordinary input, so upstream secure-use guidance applies inside the action, not only in the consuming workflow.
Candidate rules¶
- R-GHA-1:
action.ymlis complete and honest.name,description,author,branding,runs, and every input and output carry a description; each input declaresrequiredand, where optional, adefault. Every input the code reads is declared, and every declared input is read. - R-GHA-2: The runtime is supported and pinned.
runs.usingnames a Node major GitHub still supports, a composite runtime, or a Docker image pinned by digest. The action does not silently depend on a toolchain the runner happens to provide. - R-GHA-3: Documented examples request minimum permissions. Every README and example
workflow sets an explicit
permissions:block containing only the scopes the action needs, and the docs state whichGITHUB_TOKENscopes are required for which feature. - R-GHA-4: Inputs are validated before side effects. Inputs are parsed and range/format checked before filesystem writes, network calls, process execution, or API mutations. Untrusted workflow context is never interpolated into a shell string; argument arrays or library calls are used instead.
- R-GHA-5: Secret inputs never reach logs, outputs, or artifacts. Token-shaped inputs are registered as masked at first use, are excluded from debug output and error messages, and are not written to step outputs, job summaries, uploaded artifacts, or committed files.
- R-GHA-6: Bundled output matches source. A JavaScript action's committed bundle is reproducible from source with a pinned bundler, and CI fails when a clean rebuild produces a diff against the committed artifact.
- R-GHA-7: Failure is signalled through the action protocol. Failures set a failed
conclusion or exit non-zero; an action does not log an error and exit
0. Expected-failure outcomes, such as a quality gate below its threshold, are distinguishable in the log from unexpected runtime failures. - R-GHA-8: Release tags make consumer pinning reliable. Every release publishes an
immutable
vX.Y.Ztag. A floating major tag, if offered, is documented as moving, and the README shows SHA pinning as the option for privileged consumers. - R-GHA-9: Dependencies and nested actions are pinned and reviewed. A lockfile is
committed, third-party
uses:entries inside a composite action are pinned to a reviewed version or SHA per the action's stated policy, and dependency updates are reviewed before a release tag moves. - R-GHA-10: The action is smoke-tested the way a consumer runs it. CI runs the action from the repository in a real workflow job, over at least one success case and one expected failure case, and retains the run as evidence.
Severity and evidence¶
Severities follow the shared rule contract. Severity belongs to the rule, not to the reviewer.
| Rules | Severity | Escalation |
|---|---|---|
| R-GHA-5 | blocker |
Already top severity. A leaked token in a public run log is an incident, not a finding. |
| R-GHA-4 | high |
blocker when an unvalidated input reaches a shell, exec, or an API write. |
| R-GHA-3, R-GHA-6, R-GHA-8, R-GHA-9 | high |
blocker for R-GHA-9 when a privileged composite step is unpinned and the action is documented for use with contents: write or secrets. |
| R-GHA-7 | high |
Stays high; a silent 0 on failure turns every consumer's quality gate into decoration. |
| R-GHA-1, R-GHA-2 | medium |
high when an undeclared input carries a credential, or when the pinned runtime is out of GitHub support. |
| R-GHA-10 | evidence-only |
high when the action performs a privileged mutation (comments, commits, releases, deployments) with no consumer-shaped run to prove it. |
Required evidence:
evidence:
configFiles:
- "action.yml"
- "package.json"
- ".github/workflows/*.yml"
- "Dockerfile"
sourcePaths:
- "src/**"
- "dist/**"
ciArtifacts:
- "clean-rebuild diff job proving the committed bundle matches source"
- "workflow run that invokes the action from this repository"
runtimeProof:
- "run log for one success case and one expected-failure case"
- "step conclusion for the failure case, not just its log line"
negativeEvidence:
- "no secret input value present in the retained run log, outputs, or artifacts"
exceptionFormat: "accepted-exception"
CI expectations: the rebuild-diff job and the consumer-shaped smoke job are required checks
on the release path, not continue-on-error reporting jobs. A workflow that names a check but
cannot fail the release is treated as absent, per the rule contract's anti-gaming section.
Exception policy¶
Exceptions use the acceptedException record in the rule contract:
owner, scope, environment, reason, compensating controls, evidence, expiry, approval trail.
Stack-specific rulings:
- R-GHA-6 is exceptionable only for non-JavaScript actions. Composite and Docker actions have no bundle; record the runtime as the scope rather than claiming an exception.
- R-GHA-8 is exceptionable for an internal action consumed only from the same organisation, with the consuming repositories named as the scope and a review date. It is not exceptionable for a public marketplace action.
- R-GHA-9 may accept a reviewed floating major tag for first-party GitHub-published actions when the compensating control is a pinned lockfile plus a dependency review gate, recorded with an expiry.
- R-GHA-5 is never exceptionable. No owner, expiry, or compensating control makes a leaked token acceptable; the finding is closed by rotating the credential and fixing the leak.
- An exception cannot change the archetype. An action that requires the consumer to grant
contents: writefor a read-only feature is not a minimum-permission action with an exception; the feature set is wrong.
Anti-patterns¶
- Documenting an example workflow with no
permissions:block, leaving consumers on the default token scope. - Reading an environment variable or
github.eventfield the action never declares as an input, so consumers cannot see what it consumes. - Interpolating
${{ github.event.issue.title }}— or any other attacker-controllable context — into arun:step inside a composite action. - Committing
dist/by hand after a local build, with no job that proves it matches source. - Echoing an API key into debug output "just for this release", in a public repository.
- Catching every error, printing it, and exiting
0, so the consumer's gate always passes. - Publishing only a moving
v1tag and documenting it as if it were immutable. - Testing the action by unit-testing its helper functions and never once running it as a workflow step.
- Pinning the action's own dependencies while calling an unpinned third-party action from inside a composite step that runs with the caller's token.
Promotion criteria¶
This charter can be cut as github-action-package/v1 when all of the following are true.
- Every candidate rule is rewritten in the rule-contract rule template, with severity, why,
evidence, exception, a
vcqascanner or human-review signal, and a good/bad example. - Each rule has been judged against a real action package at a named commit, with the
pass/fail recorded, by something other than prose.
ref-github-action-packagemaps every rule on this page to evidence at its assessed commit, but by hand in its report; R-GHA-8 in particular is documented rather than exercised, because no release tag has been cut.vibecodeqa/actionis the only other in-house consumer and it is a composite action, so it cannot exercise the JavaScript-bundle rules on its own. ref-github-action-packageexists and demonstrates the rules this charter claims are checkable, with score evidence incompositions.json(#44). Met — the repo is built and published in the catalog; see Reference implementation above. The 2026-07-25 assessment asked for this charter to be expanded before that repo was built; this page is that expansion.- JavaScript, composite, and Docker expectations are separated at rule level rather than in prose.
- A fresh independent assessment scores this page above the
draftband, replacing the dated report linked below. - The registry entry gains a
v1edition with reviewed, next-review, targets, and lifecycle metadata, and the page is rewritten to the authored charter bar in Standards Authoring.
Review cadence¶
While this charter is a candidate rubric it is reviewed:
- whenever GitHub changes supported action runtimes, metadata syntax, or default token permissions;
- whenever
vibecodeqa/actionchanges runtime, permission surface, or release policy; - whenever a reference implementation for this stack is created or scored;
- and in any case no later than six months after the date of the most recent assessment report linked below.
Benefits¶
- vibecodeqa/action is a public composite action that
runs the VCQA CLI in a consumer's workflow, declares eight inputs and four outputs, and
takes an
anthropic-api-keysecret input. It is the in-house repository this charter's metadata, permission, secret-input, and release-tag rules were written against.
Independent Assessment¶
That report is a dated snapshot: it scored this page on 2026-07-25, when it had no candidate
rules, no upstream references, and no severity or exception policy, and it scored 25/100 for
exactly those gaps. Its Required Fixes are what this page now implements. The score is not
restated here as current, and the report is not rewritten — a re-assessment is a promotion
criterion above.