Shared libraries are loaded at a fixed revision
jenkins/shared-library-pinned@v1
Every @Library annotation and library step names a tag or a commit sha rather than a branch, so the library code a build runs cannot change underneath it.
| Id | jenkins/shared-library-pinned |
| Version | v1 |
| Category | jenkins |
| Default severity | error |
| Interpreter | python3 |
| Timeout | 30 seconds |
| Violations tolerated | 0 |
| Collects | jenkins |
Collectors
This guardrail gathers nothing itself. It depends on the collectors below, which the CLI runs once per build before any check, and reads what they found out of GUARDRAIL_FACTS. A collector that collects nothing skips this guardrail rather than failing it.
| Collector | Gathers | Inputs it is given |
|---|---|---|
jenkins | The Jenkinsfiles the repository carries, read in Jenkins's own vocabulary: whether each is a declarative pipeline or a scripted one, the agent it asks for, the stages in the order they are declared with the steps inside them, the timeouts its options blocks declare, the shared libraries it loads and how tightly each is pinned, and the credential ids it reaches for. A Jenkinsfile is Groovy rather than a declaration, so it is read by pattern and scanned says so. Names and ids only, never a credential, an environment value or a parameter value. | jenkinsfiles |
The inputs above are this guardrail's own, passed straight through. Configuring one in buildnote.json changes what is collected, and two guardrails configured the same way share the one collection.
Configuration
{
"guardrails": {
"failOn": "error",
"comment": true,
"checks": [
{
"use": "jenkins/shared-library-pinned@v1",
"severity": "error",
"with": {
"jenkinsfiles": "Jenkinsfile,Jenkinsfile.*,*.Jenkinsfile",
"allow": ""
},
"exemptions": []
}
]
}
}Inputs
| Input | Description | Default | Environment variable |
|---|---|---|---|
jenkinsfiles | Comma separated globs naming the Jenkinsfiles to read. | Jenkinsfile,Jenkinsfile.*,*.Jenkinsfile | GUARDRAIL_INPUT_JENKINSFILES |
allow | Comma separated shared library names exempt from pinning, for the ones your own organisation publishes. acme-* is a glob. | `` | GUARDRAIL_INPUT_ALLOW |
How to fix
Name the version you mean, and make it one that does not move:
@Library('acme-pipeline@1.4.2') _A library loaded from a branch is code that whoever can push to that branch runs inside your build, with every credential the job has in scope, and two builds of the same commit can run different library code. A reference with no version at all is worse: it takes the default version configured on the controller, which is usually a branch and lives outside this repository, so nobody reading the Jenkinsfile can tell what ran.
Jenkins cannot tell a tag from a branch by name, so this reads the shape of the ref: a 40 character commit sha, or a version such as 1.4.2 or v1.4.2. Protect the tags you pin to, because git lets a tag move as well.
A scripted pipeline is read too, but only by pattern: a library it loads through ordinary Groovy is not read, so a finding there is real and an absence is not proof. Nothing is suppressed because a sibling file is scripted, and the check skips only when every Jenkinsfile is scripted and none of them names a library that could be read.
More in jenkins
jenkins/job-timeout-set. Every declarative pipeline declares how long it may run, on the pipeline itself or on every stage, so a hung build is cut off rather than holding an executor.