Skip to main content

Controls configuration

The field-by-field reference for the gitlab.controls: and github.controls: sections of .plumber.yaml. Use the index to jump to a control. For the three ways to write the file see Configuration; for what a control checks and why, the Controls catalog; for a finding’s remediation, Issues.

Reading the tables

Default is the value in the plumber:default baseline: what runs with no .plumber.yaml, what an overlay inherits, and what plumber config generate writes (Plumber v0.5.10). Where the CLI behaves differently when a key is simply omitted from a full configuration, that fallback is given as unset. Long baseline lists are shown in the YAML block under each table rather than in the table itself.

How a control block works

Where a block lives

Every control is a block under gitlab.controls.<name> or github.controls.<name> in a schema version: "2.0" file. The block name is the name you pass to --controls and --skip-controls, and the controlName of its findings in the JSON report.

Turning a control on or off

Every block accepts enabled: true | false. What a missing block means depends on the mode:

ModeMissing block
Overlay (extends: plumber:default)Inherits the baseline block
Full configuration (no extends)The control does not run

A control that is off or absent is reported as skipped, never as passed.

Overlays: lists replace, maps merge

In an overlay, nested maps merge key by key, but a list you write replaces the baseline list entirely. Writing tags: [nightly] under containerImageMustNotUseForbiddenTags drops latest, dev and the other baseline tags, so repeat the entries you want to keep.

The two curated allowlists are the exception. containerImageMustComeFromAuthorizedSources (trustedUrls) and githubActionMustComeFromAuthorizedSources (trustedGithubActions) carry an includePlumberDefaults switch: with the default true, your entries are added to Plumber’s curated list; with false, only your entries are trusted. The switch only acts in overlay mode. plumber config resolve prints the final list.

Fields you leave unset

Fields that describe the setting a project must have (allowForcePush, codeOwnerApprovalRequired, every field of the merge request settings controls) are only compared when you set them. Leaving one out asserts nothing about it, and there is no way to expect the unsafe value.

A few booleans are true when omitted from a full configuration. Every other omitted boolean is false.

FieldControl
detectInsecureDaemonpipelineMustNotUseDockerInDocker
defaultBranchIsForbiddenVersionincludesMustNotUseForbiddenVersions
includePlumberDefaultsboth allowlist controls
trustGithubOfficialActions, trustSameOrgActionsgithubActionMustComeFromAuthorizedSources
allowFailureMustBeFalse.enabled, rulesMustNotBeRedefined.enabled, whenMustNotBeManual.enabledsecurityJobsMustNotBeWeakened

Controls that need configuration

Some controls assert nothing until they are given something to check: a list of tags, a set of required components, a minimum number of approvals. They carry a Needs configuration badge below. In an overlay the baseline supplies that configuration; in a full configuration, enabled: true alone makes the control run and pass with nothing to check.

Required expressions

The three “must include” controls (pipelineMustIncludeComponent, pipelineMustIncludeTemplate, workflowMustIncludeRequiredActions) take their requirement in one of two equivalent forms, never both.

  • required: a boolean expression with AND, OR and parentheses. AND binds tighter than OR, so a AND b OR c reads (a AND b) OR c.
  • requiredGroups: the same logic as a list of lists. The outer list is an OR, each inner list is an AND.
# These two are equivalent
required: (templates/go/go AND templates/trivy/trivy) OR templates/full-go-pipeline
requiredGroups:
- ["templates/go/go", "templates/trivy/trivy"]
- ["templates/full-go-pipeline"]

What is fixed

  • Severity. Each issue code carries a fixed severity (see Issues). Configuration decides whether a control runs and what it checks, not how a finding is graded.
  • GitLab plan tiers. Controls and fields that read a setting only a paid plan exposes carry a GitLab Premium or GitLab Ultimate badge. On GitLab Free, enable only what your plan exposes.
  • Validation. Unknown control names and fields produce a warning with a “did you mean” suggestion and are ignored. plumber config validate reports them without a scan; plumber analyze --fail-warnings turns them into a failure.

Controls index

Baseline is the state in plumber:default. Controls listed under both providers share one block definition.

Both providers

GitLab

GitHub

Info

Eight GitLab controls also accept a block under github.controls: containerImageMustComeFromAuthorizedSources, includesMustBeUpToDate, includesMustNotUseForbiddenVersions, pipelineMustIncludeComponent, pipelineMustIncludeTemplate, pipelineMustNotIncludeHardcodedJobs, pipelineMustNotOverrideJobVariables and pipelineMustNotUseUnsafeVariableExpansion. The block is parsed and validated, but these controls are not active on GitHub yet and produce no finding there.

Controls on both providers

One block definition, under gitlab.controls and under github.controls. Where a default differs between providers, both are given.

containerImageMustNotUseForbiddenTags

On by defaultNeeds configuration

Flags container images referenced by a mutable tag and, when digest pinning is required, any image not pinned by digest. Raises ISSUE-102 (medium) and ISSUE-103 (high).

FieldDefaultDescription
enabledtrueTurns the control on.
tags6 tags, see belowTags considered mutable and therefore forbidden.
containerImagesMustBePinnedByDigesttrue (unset: false)Requires every image to be referenced by an immutable @sha256: digest. Takes precedence over tags: a version tag such as alpine:3.19 is then also flagged.
containerImageMustNotUseForbiddenTags:
enabled: true
tags: [latest, dev, development, staging, main, master]
containerImagesMustBePinnedByDigest: true

branchMustBeProtected

On by defaultNeeds configurationNeeds an API token

Checks that the branches matching namePatterns (and the default branch) are protected, and that their protection meets the expectations you set. Raises ISSUE-501 (critical) when a branch is not protected and ISSUE-505 (high) when its settings are not compliant. Without a token the control abstains. On GitHub it reads both classic branch protection and rulesets, and evaluates the stricter of the two.

FieldDefaultDescription
enabledtrueTurns the control on.
defaultMustBeProtectedtrueAlso requires the default branch to be protected, whether or not it matches namePatterns.
namePatterns5 patterns, see belowBranch name patterns that must be protected. Wildcards are supported.
allowForcePushfalseExpectation: false requires force push to be disabled.
codeOwnerApprovalRequiredfalseExpectation: true requires code owner approval. GitLab Premium
minMergeAccessLevelGitLab 30, GitHub unsetExpectation: the minimum access level allowed to merge. GitLab only.
minPushAccessLevelGitLab 40, GitHub unsetExpectation: the minimum access level allowed to push. GitLab only.

GitLab access levels: 0 No one (the strictest), 30 Developer, 40 Maintainer.

branchMustBeProtected:
enabled: true
defaultMustBeProtected: true
namePatterns: [main, master, release/*, production, dev]
allowForcePush: false
codeOwnerApprovalRequired: false
minMergeAccessLevel: 30 # GitLab only
minPushAccessLevel: 40 # GitLab only

pipelineMustNotEnableDebugTrace

On by defaultNeeds configuration

Flags a pipeline that sets a debug variable to a truthy value, which dumps every variable, masked secrets included, into the job log. Raises ISSUE-203 (critical). There is no built-in list: the control checks only the variables you list.

FieldDefaultDescription
enabledtrueTurns the control on.
forbiddenVariablesper provider, see belowVariable names that must not be set to true (1 and yes also count on GitHub).
pipelineMustNotEnableDebugTrace:
enabled: true
forbiddenVariables: [CI_DEBUG_TRACE, CI_DEBUG_SERVICES] # GitLab
# forbiddenVariables: [ACTIONS_STEP_DEBUG, ACTIONS_RUNNER_DEBUG] # GitHub

securityJobsMustNotBeWeakened

On by defaultNeeds configuration

Flags security scanning jobs that are allowed to fail, whose rules were overridden to skip them, or that were made manual. Raises ISSUE-410 (critical). Only jobs matching securityJobPatterns are checked, and the control counts as skipped when all three sub-checks are off.

FieldDefaultDescription
enabledtrueTurns the control on.
securityJobPatternsper provider, see belowJob name patterns treated as security jobs. Wildcards are supported. On GitHub the pattern is matched against <workflow file>/<job id>, for example codeql/analyze.
allowFailureMustBeFalse.enabledGitLab false, GitHub trueThe job must not carry allow_failure: true (GitLab) or continue-on-error: true (GitHub). Off on GitLab because GitLab’s own security templates ship with allow_failure: true.
rulesMustNotBeRedefined.enabledtrueThe job’s rules: must not be overridden to when: never or when: manual.
whenMustNotBeManual.enabledtrueThe job must not be when: manual (GitLab) or reachable only through a manual dispatch trigger (GitHub).
# GitLab baseline
securityJobsMustNotBeWeakened:
enabled: true
securityJobPatterns:
- "*-sast"
- "secret_detection"
- "container_scanning"
- "*_dependency_scanning"
- "gemnasium-*"
- "dast"
- "dast_*"
- "license_scanning"
allowFailureMustBeFalse: { enabled: false }
rulesMustNotBeRedefined: { enabled: true }
whenMustNotBeManual: { enabled: true }
# GitHub baseline
securityJobsMustNotBeWeakened:
enabled: true
securityJobPatterns:
- "*codeql*"
- "*dependency-review*"
- "*trufflehog*"
- "*gitleaks*"
- "*osv-scanner*"
- "*semgrep*"
- "*trivy*"
- "*snyk*"
- "*-sast"
- "*-sast-*"
- "*-scan"
- "*scan*"
- "*-security"
- "*-security-*"
- "*-audit"
- "*-audit-*"
- "*secret*detect*"
- "*detect*secret*"
allowFailureMustBeFalse: { enabled: true }
rulesMustNotBeRedefined: { enabled: true }
whenMustNotBeManual: { enabled: true }

pipelineMustNotExecuteUnverifiedScripts

On by default

Flags scripts downloaded and executed in one go (curl | bash, wget | sh) from a URL that is not trusted. Raises ISSUE-411 (high).

FieldDefaultDescription
enabledtrueTurns the control on.
trustedUrlsemptyURL patterns that do not trigger a finding. Wildcards are supported; matching is host-precise, so https://example.com/* does not cover a subdomain.
pipelineMustNotExecuteUnverifiedScripts:
enabled: true
trustedUrls:
- https://internal-artifacts.example.com/*

pipelineMustNotUseDockerInDocker

On by default

Flags jobs that run a Docker-in-Docker service and, optionally, an insecure daemon configuration in those jobs. Raises ISSUE-412 (high) and ISSUE-413 (high).

FieldDefaultDescription
enabledtrueTurns the control on.
detectInsecureDaemontrueAlso flags an empty DOCKER_TLS_CERTDIR or a plaintext DOCKER_HOST such as tcp://docker:2375 in a DinD job (ISSUE-413).
pipelineMustNotUseDockerInDocker:
enabled: true
detectInsecureDaemon: true

externalRefsMustNotCollide

On by defaultNeeds an API token

Flags an include (GitLab) or an action reference (GitHub) whose ref exists both as a tag and as a branch upstream, so what runs is ambiguous. Raises ISSUE-402 (medium). Without a token the control abstains. No field beyond enabled.

GitLab controls

Blocks under gitlab.controls.

containerImageMustComeFromAuthorizedSources

On by default

Flags a job image that does not come from a trusted registry or publisher. Raises ISSUE-101 (high).

FieldDefaultDescription
enabledtrueTurns the control on.
trustDockerHubOfficialImagestrue (unset: false)Trusts Docker Hub official images such as nginx or alpine.
trustedUrlscurated list, about 90 entriesTrusted registry URLs or image patterns, wildcards supported. The baseline covers the project registry ($CI_REGISTRY_IMAGE:*, $CI_REGISTRY/*), registry.gitlab.com/security-products/*, common Docker Hub publishers and selected namespaces on ghcr.io, quay.io, gcr.io and mcr.microsoft.com. Print it with plumber config view.
includePlumberDefaultstrueOverlay mode: true adds your trustedUrls to the curated list, false trusts only your entries.

Prefer namespace patterns such as ghcr.io/myorg/* over host-wide wildcards such as ghcr.io/*, which trust every publisher on that host.

containerImageMustComeFromAuthorizedSources:
enabled: true
trustDockerHubOfficialImages: true
includePlumberDefaults: true
trustedUrls:
- $CI_REGISTRY_IMAGE:*
- registry.example.com/platform/*

includesMustBeUpToDate

On by default

Flags an included template or component pinned to a version older than the latest available. Raises ISSUE-403 (low). No field beyond enabled.

includesMustNotUseForbiddenVersions

On by defaultNeeds configuration

Flags an include pinned to a mutable ref. Raises ISSUE-404 (medium).

FieldDefaultDescription
enabledtrueTurns the control on.
forbiddenVersions5 patterns, see belowVersion patterns considered forbidden.
defaultBranchIsForbiddenVersiontrueAlso forbids the included project’s default branch, whatever its name.
includesMustNotUseForbiddenVersions:
enabled: true
forbiddenVersions: [latest, "~latest", main, master, HEAD]
defaultBranchIsForbiddenVersion: true

pipelineMustNotUseUnsafeVariableExpansion

On by defaultNeeds configuration

Flags a user-controlled variable (merge request title, commit message, branch name) used where the shell re-interprets it: eval, sh -c, bash -c, source <(...), envsubst | sh, xargs sh. Plain uses such as echo $CI_COMMIT_BRANCH are not flagged. Raises ISSUE-204 (medium).

FieldDefaultDescription
enabledtrueTurns the control on.
dangerousVariables10 variables, see belowVariables whose value comes from user input.
allowedPatternsemptyA list of regular expressions; a matching script line is not flagged. Escape $ as \\$ and braces as \\{, \\}.
pipelineMustNotUseUnsafeVariableExpansion:
enabled: true
dangerousVariables:
- CI_MERGE_REQUEST_TITLE
- CI_MERGE_REQUEST_DESCRIPTION
- CI_COMMIT_MESSAGE
- CI_COMMIT_TITLE
- CI_COMMIT_TAG_MESSAGE
- CI_COMMIT_REF_NAME
- CI_COMMIT_REF_SLUG
- CI_COMMIT_BRANCH
- CI_MERGE_REQUEST_SOURCE_BRANCH_NAME
- CI_EXTERNAL_PULL_REQUEST_SOURCE_BRANCH_NAME
allowedPatterns:
- "helm.*--set.*\\$CI_"
- "docker build.*--build-arg.*\\$CI_"

pipelineMustNotOverrideJobVariables

On by defaultNeeds configuration

Flags a controlled variable defined in the pipeline file, where it can silently disable or redirect a security scanner. Such variables belong in the project’s CI/CD settings. Raises ISSUE-205 (high).

FieldDefaultDescription
enabledtrueTurns the control on.
variables10 variables, see belowVariable names that must not be defined in .gitlab-ci.yml, at the top level or in a job.
pipelineMustNotOverrideJobVariables:
enabled: true
variables:
- SECURE_ANALYZERS_PREFIX
- SAST_DISABLED
- SAST_EXCLUDED_PATHS
- SAST_EXCLUDED_ANALYZERS
- SECRET_DETECTION_DISABLED
- SECRET_DETECTION_EXCLUDED_PATHS
- CONTAINER_SCANNING_DISABLED
- DAST_DISABLED
- DEPENDENCY_SCANNING_DISABLED
- LICENSE_SCANNING_DISABLED

cicdVariablesMustBeProtected

Off by defaultNeeds an API token

Flags a CI/CD variable in the project settings that is not marked protected. Raises ISSUE-201 (medium). Needs read access to the project variables; a 401 or 403 is reported as not evaluable, never as a pass. No field beyond enabled.

cicdVariablesMustBeMasked

Off by defaultNeeds an API token

Flags a CI/CD variable in the project settings that is not masked. Raises ISSUE-202 (medium). GitLab cannot mask values shorter than 8 characters; those are still flagged. No field beyond enabled.

mergeRequestApprovalRulesMustRequireMinimumApprovals

Off by defaultNeeds configurationGitLab Premium

Flags an approval rule covering all protected branches that requires fewer approvals than the minimum. Raises ISSUE-502 (high).

FieldDefaultDescription
enabledfalseTurns the control on.
minimumRequiredApprovals1The fewest approvals a rule covering all protected branches must require. Unset asserts nothing.
mergeRequestApprovalRulesMustRequireMinimumApprovals:
enabled: true
minimumRequiredApprovals: 2

mergeRequestApprovalRulesMustCoverAllProtectedBranches

Off by defaultGitLab Premium

Flags a project where no approval rule targets all protected branches. Raises ISSUE-504 (high). On GitLab Free the approvals API returns no rule, so the control would fire on every project. No field beyond enabled.

mergeRequestApprovalSettingsMustBeCompliant

Off by defaultNeeds configurationGitLab Premium

Compares the project’s merge request approval settings with the expectations you set. Raises ISSUE-503 (high). A field you leave out (or set to false) is not checked.

FieldDefaultExpects
enabledfalseTurns the control on.
preventApprovalByAuthorunsetAuthors cannot approve their own merge requests.
preventApprovalsByCommittersunsetUsers who committed to a merge request cannot approve it.
preventEditingApprovalRulesInMRunsetApproval rules cannot be edited in a merge request.
requireReAuthToApproveunsetApproving requires re-authentication (password or SAML).
behaviorWhenCommitIsAddedunsetThe minimum strictness when a commit is added to an open merge request: keep_approvals, remove_approvals_by_code_owners or remove_all_approvals, from least to most strict. A stricter project setting passes.
mergeRequestApprovalSettingsMustBeCompliant:
enabled: true
preventApprovalByAuthor: true
preventApprovalsByCommitters: true
preventEditingApprovalRulesInMR: true
requireReAuthToApprove: true
behaviorWhenCommitIsAdded: remove_all_approvals

mergeRequestSettingsMustBeCompliant

Off by defaultNeeds configuration

Compares the project’s merge request settings with the expectations you set, for exact equality. Raises ISSUE-506 (medium). A field you leave out is not checked.

FieldDefaultExpects
enabledfalseTurns the control on.
mergeMethodunsetThe merge method: merge, ff or rebase_merge.
squashOptionunsetThe squash setting: never, always, default_on or default_off.
mergePipelinesEnabledunsetThe “merged results pipelines” setting. GitLab Premium
mergeTrainsEnabledunsetThe “merge trains” setting. GitLab Premium
allowMergeOnSkippedPipelineunsetThe “allow merge when the pipeline is skipped” setting.
resolveOutdatedDiffDiscussionsunsetThe “automatically resolve outdated diff discussions” setting.
printingMergeRequestLinkEnabledunsetThe “show the merge request link when pushing” setting.
removeSourceBranchAfterMergeunsetThe “delete source branch after merge” default.
mergeRequestSettingsMustBeCompliant:
enabled: true
mergeMethod: ff
squashOption: default_on
allowMergeOnSkippedPipeline: false
removeSourceBranchAfterMerge: true

projectMustHaveSecurityPolicySource

Off by defaultGitLab Ultimate

Checks that the project is linked to a security policy project, optionally a specific one. Raises ISSUE-601 (critical).

FieldDefaultDescription
enabledfalseTurns the control on.
expectedProjectIdunsetThe numeric ID the linked policy project must have. Unset only requires that some policy project is linked.
expectedProjectPathunsetThe namespace/project path the linked policy project must have, compared case-insensitively. Ignored when expectedProjectId is set.
projectMustHaveSecurityPolicySource:
enabled: true
expectedProjectPath: my-group/security-policy-project

pipelineMustNotIncludeHardcodedJobs

Off by default

Flags jobs defined directly in .gitlab-ci.yml instead of coming from an included template or component. Raises ISSUE-401 (medium). Off by default because without a components strategy it flags most jobs. No field beyond enabled.

pipelineMustIncludeComponent

Off by defaultNeeds configuration

Checks that the pipeline includes the required CI/CD components and does not override their jobs. Raises ISSUE-408 (high) when a component is missing and ISSUE-409 (medium) when a required component’s job is overridden.

FieldDefaultDescription
enabledfalseTurns the control on.
requiredunsetThe requirement as a boolean expression of component paths (group/project/component). See Required expressions. Exclusive with requiredGroups.
requiredGroupsemptyThe same requirement as a list of lists (an OR of ANDs). Exclusive with required.
pipelineMustIncludeComponent:
enabled: true
required: components/sast/sast AND components/secret-detection/secret-detection AND getplumber/plumber/plumber

pipelineMustIncludeTemplate

Off by defaultNeeds configuration

Checks that the pipeline includes the required templates and does not override their jobs. Raises ISSUE-405 (high) and ISSUE-406 (medium).

FieldDefaultDescription
enabledfalseTurns the control on.
requiredunsetThe requirement as a boolean expression of template paths. Exclusive with requiredGroups.
requiredGroupsemptyThe same requirement as a list of lists (an OR of ANDs). Exclusive with required.
pipelineMustIncludeTemplate:
enabled: true
requiredGroups:
- ["templates/go/go", "templates/trivy/trivy"]
- ["templates/full-go-pipeline"]

GitHub controls

Blocks under github.controls.

actionsMustBePinnedByCommitSha

On by default

Flags a third-party action referenced by a tag or branch instead of a full commit SHA. Raises ISSUE-701 (high).

FieldDefaultDescription
enabledtrueTurns the control on.
trustedOwnersactions, githubAction owners exempt from the requirement. List only owners inside your trust boundary.
actionsMustBePinnedByCommitSha:
enabled: true
trustedOwners: [actions, github, myorg]

githubActionMustComeFromAuthorizedSources

On by default

Flags an action whose owner is not trusted by any of the four rules below. Raises ISSUE-713 (high).

FieldDefaultDescription
enabledtrueTurns the control on.
trustGithubOfficialActionstrueTrusts GitHub’s own actions, actions/* and github/*.
trustSameOrgActionstrueTrusts actions owned by the same organization or user as the scanned repository.
minimumStars20000 (unset: 0, off)Trusts any action whose repository has at least this many stars. Needs API access; an unresolvable star count falls back to the allowlist instead of a finding.
trustedGithubActionscurated list, about 150 entriesTrusted actions as owner/repo or owner/*. Owner matching is literal and case-sensitive. The baseline covers the major cloud vendors, build and release tooling, language ecosystems, security vendors and developer platforms. Print it with plumber config view.
includePlumberDefaultstrueOverlay mode: true adds your trustedGithubActions to the curated list, false trusts only your entries.
githubActionMustComeFromAuthorizedSources:
enabled: true
trustGithubOfficialActions: true
trustSameOrgActions: true
minimumStars: 20000
includePlumberDefaults: true
trustedGithubActions:
- myorg/*
- partner-org/deploy-action

actionsMustNotBeArchived

On by defaultNeeds an API token

Flags an action hosted in an archived repository, which no longer receives fixes. Raises ISSUE-702 (high). Without API access the control abstains. No field beyond enabled.

actionRefsMustExistUpstream

On by defaultNeeds an API token

Flags a pinned commit SHA that does not belong to the action’s repository. Raises ISSUE-707 (critical). Fires only on a definitive not-found answer from a readable repository. No field beyond enabled.

actionsMustNotCarryKnownCVEs

On by defaultNeeds an API token

Flags an action version that has a published advisory in the GitHub Advisory Database. Raises ISSUE-703 (critical). Without API access the control abstains; PLUMBER_METADATA_TOKEN can carry the version lookup, see the CLI reference. No field beyond enabled.

actionsMustNotExecuteMutableRemoteCode

On by defaultNeeds an API token

Inspects the source of each third-party action and flags one that downloads and runs code from a mutable location. Raises ISSUE-714 (high) for a fetch in the open, ISSUE-715 (critical) for an obfuscated one, and ISSUE-716 (low) when the action source could not be read. No field beyond enabled.

releaseWorkflowsMustNotRestoreUntrustedCache

On by defaultNeeds configuration

Flags a release or publish job that restores a build cache which a pull request could have poisoned. A job has release intent when it uses one of publishActions or runs a command matching publishScriptPatterns; it restores a cache when it uses one of cacheActions in a restoring mode. Raises ISSUE-705 (high), or ISSUE-717 (medium) when a cache condition cannot be resolved per trigger. Job and step if: conditions and github.event_name ==/!= '<event>' comparisons in the cache inputs are resolved per trigger, so a cache that is off on the publish trigger stays silent.

FieldDefaultDescription
enabledtrueTurns the control on.
publishActions8 actions, see belowuses: prefixes that mark a job as a release job.
cacheActions10 entries, see belowOne entry per cache-restoring action, with the rule that decides whether it restores (see the mode table).
publishScriptPatterns12 patterns, see belowA list of regular expressions on shell commands that mark a job as a release job, even under a plain push or tag trigger.
publishScriptExcludePatterns2 patterns, see belowA list of regular expressions; a run block matching one is not a release, even if it also matches publishScriptPatterns.
allowedJobsemptyA list of globs on <workflow file>/<job id>; a matching job is exempt.

Each cacheActions entry has an action (the uses: prefix) and a mode:

modeRestores the cacheExtra keys
alwayswhenever the action is presentnone
defaultunless the with: input disableInput is set to disableValuedisableInput, disableValue
opt-inonly when the with: input enableInput is set (and, with enableContains, contains that substring)enableInput, enableContains
releaseWorkflowsMustNotRestoreUntrustedCache:
enabled: true
publishActions:
- pypa/gh-action-pypi-publish
- JS-DevTools/npm-publish
- gradle/publish-plugin
- softprops/action-gh-release
- ncipollo/release-action
- goreleaser/goreleaser-action
- crazy-max/ghaction-docker-buildx
- changesets/action
cacheActions:
- action: actions/cache
mode: always
- action: actions/cache/restore
mode: always
- action: Swatinem/rust-cache
mode: always
- action: actions/setup-go
mode: default
disableInput: cache
disableValue: false
- action: gradle/actions/setup-gradle
mode: default
disableInput: cache-disabled
disableValue: true
- action: actions/setup-node
mode: opt-in
enableInput: cache
- action: actions/setup-python
mode: opt-in
enableInput: cache
- action: actions/setup-java
mode: opt-in
enableInput: cache
- action: pnpm/action-setup
mode: opt-in
enableInput: cache
- action: docker/build-push-action
mode: opt-in
enableInput: cache-from
enableContains: type=gha
publishScriptPatterns:
- '(?i)(npm|pnpm|yarn|bun)\s+publish'
- '(?i)cargo\s+publish'
- '(?i)twine\s+upload'
- '(?i)poetry\s+publish'
- '(?i)gh\s+release\s+create'
- '(?i)goreleaser\s+release'
- '(?i)semantic-release'
- '(?i)gradlew?\b[^\n]*\bpublish'
- '(?i)\bmvnw?\b[^\n]*\bdeploy\b'
- '(?i)dotnet\s+nuget\s+push'
- '(?i)gem\s+push'
- '(?i)docker\s+push'
publishScriptExcludePatterns:
- '(?i)--dry-run'
- '(?i)publishToMavenLocal'
allowedJobs: []

reusableWorkflowsMustNotInheritSecrets

On by default

Flags a reusable workflow call with secrets: inherit, which passes every repository secret to the callee. Raises ISSUE-302 (high). No field beyond enabled.

checkoutMustNotPersistCredentials

On by default

Flags an actions/checkout step that does not set persist-credentials: false, leaving the token in the clone’s .git/config. Raises ISSUE-307 (low) when the credential merely persists and ISSUE-310 (high) when a later upload-artifact step packs it into a downloadable artifact. No field beyond enabled.

workflowMustNotExportEntireSecretsContext

On by default

Flags toJSON(secrets) and other expressions that expose the whole secrets context at once. Raises ISSUE-309 (critical). No field beyond enabled.

workflowMustNotInjectUserInputInScripts

On by default

Flags a run: script that inlines a user-controlled expression such as ${{ github.event.pull_request.title }}, a template injection. Raises ISSUE-207 (critical). No field beyond enabled.

workflowMustNotWriteUntrustedContentToGitHubEnv

On by default

Flags a step that writes user-controlled content to $GITHUB_ENV or $GITHUB_PATH. Raises ISSUE-209 (high). No field beyond enabled.

workflowMustNotUseDangerousTriggers

On by default

Flags the pull_request_target and workflow_run triggers, which run with write access on untrusted input. Raises ISSUE-802 (critical). No field beyond enabled.

pullRequestTargetMustNotCheckoutHead

On by default

Flags a pull_request_target workflow that checks out the pull request head, running untrusted code with the repository’s secrets. Raises ISSUE-804 (critical). No field beyond enabled.

workflowsMustDeclarePermissions

On by default

Flags a workflow with no explicit permissions: block, which runs with the repository’s default token permissions. Raises ISSUE-801 (medium). No field beyond enabled.

workflowMustNotGrantPermissionsWriteAll

On by default

Flags the permissions: write-all shortcut at the workflow or job level. Per-scope permissions are not audited. Raises ISSUE-803 (high). No field beyond enabled.

workflowMustIncludeRequiredActions

Off by defaultNeeds configuration

Checks that the workflows of the repository, taken together, reference the required actions or reusable workflows. Raises ISSUE-417 (high).

FieldDefaultDescription
enabledfalseTurns the control on.
requiredunsetThe requirement as a boolean expression of owner/repo or owner/repo/path entries, matched without regard to the @ref: myorg/sast-scan matches myorg/sast-scan@v2 and myorg/sast-scan/sub@abc123. Exclusive with requiredGroups.
requiredGroupsemptyThe same requirement as a list of lists (an OR of ANDs). Exclusive with required.
workflowMustIncludeRequiredActions:
enabled: true
required: myorg/sast-scan AND myorg/policy/.github/workflows/scan.yml