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:
| Mode | Missing 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.
| Field | Control |
|---|---|
detectInsecureDaemon | pipelineMustNotUseDockerInDocker |
defaultBranchIsForbiddenVersion | includesMustNotUseForbiddenVersions |
includePlumberDefaults | both allowlist controls |
trustGithubOfficialActions, trustSameOrgActions | githubActionMustComeFromAuthorizedSources |
allowFailureMustBeFalse.enabled, rulesMustNotBeRedefined.enabled, whenMustNotBeManual.enabled | securityJobsMustNotBeWeakened |
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 withAND,ORand parentheses.ANDbinds tighter thanOR, soa AND b OR creads(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 equivalentrequired: (templates/go/go AND templates/trivy/trivy) OR templates/full-go-pipelinerequiredGroups: - ["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 validatereports them without a scan;plumber analyze --fail-warningsturns them into a failure.
Controls index
Baseline is the state in plumber:default. Controls listed under both providers share one block definition.
Both providers
| Control | Baseline | Issues |
|---|---|---|
containerImageMustNotUseForbiddenTags | on | 102, 103 |
branchMustBeProtected | on | 501, 505 |
pipelineMustNotEnableDebugTrace | on | 203 |
securityJobsMustNotBeWeakened | on | 410 |
pipelineMustNotExecuteUnverifiedScripts | on | 411 |
pipelineMustNotUseDockerInDocker | on | 412, 413 |
externalRefsMustNotCollide | on | 402 |
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
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).
| Field | Default | Description |
|---|---|---|
enabled | true | Turns the control on. |
tags | 6 tags, see below | Tags considered mutable and therefore forbidden. |
containerImagesMustBePinnedByDigest | true (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: truebranchMustBeProtected
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.
| Field | Default | Description |
|---|---|---|
enabled | true | Turns the control on. |
defaultMustBeProtected | true | Also requires the default branch to be protected, whether or not it matches namePatterns. |
namePatterns | 5 patterns, see below | Branch name patterns that must be protected. Wildcards are supported. |
allowForcePush | false | Expectation: false requires force push to be disabled. |
codeOwnerApprovalRequired | false | Expectation: true requires code owner approval. GitLab Premium |
minMergeAccessLevel | GitLab 30, GitHub unset | Expectation: the minimum access level allowed to merge. GitLab only. |
minPushAccessLevel | GitLab 40, GitHub unset | Expectation: 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 onlypipelineMustNotEnableDebugTrace
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.
| Field | Default | Description |
|---|---|---|
enabled | true | Turns the control on. |
forbiddenVariables | per provider, see below | Variable 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] # GitHubsecurityJobsMustNotBeWeakened
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.
| Field | Default | Description |
|---|---|---|
enabled | true | Turns the control on. |
securityJobPatterns | per provider, see below | Job 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.enabled | GitLab false, GitHub true | The 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.enabled | true | The job’s rules: must not be overridden to when: never or when: manual. |
whenMustNotBeManual.enabled | true | The job must not be when: manual (GitLab) or reachable only through a manual dispatch trigger (GitHub). |
# GitLab baselinesecurityJobsMustNotBeWeakened: 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 baselinesecurityJobsMustNotBeWeakened: 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
Flags scripts downloaded and executed in one go (curl | bash, wget | sh) from a URL that is not trusted. Raises ISSUE-411 (high).
| Field | Default | Description |
|---|---|---|
enabled | true | Turns the control on. |
trustedUrls | empty | URL 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
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).
| Field | Default | Description |
|---|---|---|
enabled | true | Turns the control on. |
detectInsecureDaemon | true | Also 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: trueexternalRefsMustNotCollide
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
Flags a job image that does not come from a trusted registry or publisher. Raises ISSUE-101 (high).
| Field | Default | Description |
|---|---|---|
enabled | true | Turns the control on. |
trustDockerHubOfficialImages | true (unset: false) | Trusts Docker Hub official images such as nginx or alpine. |
trustedUrls | curated list, about 90 entries | Trusted 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. |
includePlumberDefaults | true | Overlay 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
Flags an included template or component pinned to a version older than the latest available. Raises ISSUE-403 (low). No field beyond enabled.
includesMustNotUseForbiddenVersions
Flags an include pinned to a mutable ref. Raises ISSUE-404 (medium).
| Field | Default | Description |
|---|---|---|
enabled | true | Turns the control on. |
forbiddenVersions | 5 patterns, see below | Version patterns considered forbidden. |
defaultBranchIsForbiddenVersion | true | Also forbids the included project’s default branch, whatever its name. |
includesMustNotUseForbiddenVersions: enabled: true forbiddenVersions: [latest, "~latest", main, master, HEAD] defaultBranchIsForbiddenVersion: truepipelineMustNotUseUnsafeVariableExpansion
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).
| Field | Default | Description |
|---|---|---|
enabled | true | Turns the control on. |
dangerousVariables | 10 variables, see below | Variables whose value comes from user input. |
allowedPatterns | empty | A 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
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).
| Field | Default | Description |
|---|---|---|
enabled | true | Turns the control on. |
variables | 10 variables, see below | Variable 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_DISABLEDcicdVariablesMustBeProtected
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
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
Flags an approval rule covering all protected branches that requires fewer approvals than the minimum. Raises ISSUE-502 (high).
| Field | Default | Description |
|---|---|---|
enabled | false | Turns the control on. |
minimumRequiredApprovals | 1 | The fewest approvals a rule covering all protected branches must require. Unset asserts nothing. |
mergeRequestApprovalRulesMustRequireMinimumApprovals: enabled: true minimumRequiredApprovals: 2mergeRequestApprovalRulesMustCoverAllProtectedBranches
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
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.
| Field | Default | Expects |
|---|---|---|
enabled | false | Turns the control on. |
preventApprovalByAuthor | unset | Authors cannot approve their own merge requests. |
preventApprovalsByCommitters | unset | Users who committed to a merge request cannot approve it. |
preventEditingApprovalRulesInMR | unset | Approval rules cannot be edited in a merge request. |
requireReAuthToApprove | unset | Approving requires re-authentication (password or SAML). |
behaviorWhenCommitIsAdded | unset | The 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_approvalsmergeRequestSettingsMustBeCompliant
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.
| Field | Default | Expects |
|---|---|---|
enabled | false | Turns the control on. |
mergeMethod | unset | The merge method: merge, ff or rebase_merge. |
squashOption | unset | The squash setting: never, always, default_on or default_off. |
mergePipelinesEnabled | unset | The “merged results pipelines” setting. GitLab Premium |
mergeTrainsEnabled | unset | The “merge trains” setting. GitLab Premium |
allowMergeOnSkippedPipeline | unset | The “allow merge when the pipeline is skipped” setting. |
resolveOutdatedDiffDiscussions | unset | The “automatically resolve outdated diff discussions” setting. |
printingMergeRequestLinkEnabled | unset | The “show the merge request link when pushing” setting. |
removeSourceBranchAfterMerge | unset | The “delete source branch after merge” default. |
mergeRequestSettingsMustBeCompliant: enabled: true mergeMethod: ff squashOption: default_on allowMergeOnSkippedPipeline: false removeSourceBranchAfterMerge: trueprojectMustHaveSecurityPolicySource
Checks that the project is linked to a security policy project, optionally a specific one. Raises ISSUE-601 (critical).
| Field | Default | Description |
|---|---|---|
enabled | false | Turns the control on. |
expectedProjectId | unset | The numeric ID the linked policy project must have. Unset only requires that some policy project is linked. |
expectedProjectPath | unset | The 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-projectpipelineMustNotIncludeHardcodedJobs
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
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.
| Field | Default | Description |
|---|---|---|
enabled | false | Turns the control on. |
required | unset | The requirement as a boolean expression of component paths (group/project/component). See Required expressions. Exclusive with requiredGroups. |
requiredGroups | empty | The 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/plumberpipelineMustIncludeTemplate
Checks that the pipeline includes the required templates and does not override their jobs. Raises ISSUE-405 (high) and ISSUE-406 (medium).
| Field | Default | Description |
|---|---|---|
enabled | false | Turns the control on. |
required | unset | The requirement as a boolean expression of template paths. Exclusive with requiredGroups. |
requiredGroups | empty | The 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
Flags a third-party action referenced by a tag or branch instead of a full commit SHA. Raises ISSUE-701 (high).
| Field | Default | Description |
|---|---|---|
enabled | true | Turns the control on. |
trustedOwners | actions, github | Action owners exempt from the requirement. List only owners inside your trust boundary. |
actionsMustBePinnedByCommitSha: enabled: true trustedOwners: [actions, github, myorg]githubActionMustComeFromAuthorizedSources
Flags an action whose owner is not trusted by any of the four rules below. Raises ISSUE-713 (high).
| Field | Default | Description |
|---|---|---|
enabled | true | Turns the control on. |
trustGithubOfficialActions | true | Trusts GitHub’s own actions, actions/* and github/*. |
trustSameOrgActions | true | Trusts actions owned by the same organization or user as the scanned repository. |
minimumStars | 20000 (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. |
trustedGithubActions | curated list, about 150 entries | Trusted 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. |
includePlumberDefaults | true | Overlay 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-actionactionsMustNotBeArchived
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
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
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
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
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.
| Field | Default | Description |
|---|---|---|
enabled | true | Turns the control on. |
publishActions | 8 actions, see below | uses: prefixes that mark a job as a release job. |
cacheActions | 10 entries, see below | One entry per cache-restoring action, with the rule that decides whether it restores (see the mode table). |
publishScriptPatterns | 12 patterns, see below | A list of regular expressions on shell commands that mark a job as a release job, even under a plain push or tag trigger. |
publishScriptExcludePatterns | 2 patterns, see below | A list of regular expressions; a run block matching one is not a release, even if it also matches publishScriptPatterns. |
allowedJobs | empty | A 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:
mode | Restores the cache | Extra keys |
|---|---|---|
always | whenever the action is present | none |
default | unless the with: input disableInput is set to disableValue | disableInput, disableValue |
opt-in | only 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
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
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
Flags toJSON(secrets) and other expressions that expose the whole secrets context at once. Raises ISSUE-309 (critical). No field beyond enabled.
workflowMustNotInjectUserInputInScripts
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
Flags a step that writes user-controlled content to $GITHUB_ENV or $GITHUB_PATH. Raises ISSUE-209 (high). No field beyond enabled.
workflowMustNotUseDangerousTriggers
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
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
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
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
Checks that the workflows of the repository, taken together, reference the required actions or reusable workflows. Raises ISSUE-417 (high).
| Field | Default | Description |
|---|---|---|
enabled | false | Turns the control on. |
required | unset | The 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. |
requiredGroups | empty | The 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