jenkinsci / jenkinsci/workflow-cps-plugin

Rebuild-button (which redirects to Replay) can cause inconsistent Shared Lib usage when user miss Run/Build->Replay permission

Open
#1,745 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
Java
Stars
186
Forks
213
Avg merge
12h 12m
Merged PRs (30d)
8

Description

### Jenkins and plugins versions report

Besides we observed this on multiple of our self-hosted instances, it can be reproduced with an up-to-date fresh Docker-Jenkins installation:
* jenkins/jenkins:lts-jdk21 - which gave me Jenkins Version 2.541.2 at the time of writing (Feb. 2026)
* Go through the installation without installing any additional plugins (i used 'Folders', but it's not needed for this case)
* Matrix Authorization Strategy Plugin Version 3.2.9

### What Operating System are you using (both controller, and any agents involved in the problem)?

* RHEL 9 based
* Debian based (see default Docker image jenkins/jenkins_lts-jdk21)

### Reproduction steps

* Start a new container of the Jenkins image
* Follow the setup, do not install any suggested plugins
* After finishing the installation, install the 'Matrix Authorization Strategy' plugin
* Your account will stay administrator
* Set up a very basic Shared Library with
* `resources/endpoints.txt` , with the content
```
Singing in the rain.
```
* `vars/dumpUtil.groovy` with the content
```
void dumpSomething() {
println '### Mambo No 7'
}
```

* the Shared library will be accessible under the name `dancefloor`
* Have a very simple pipeline which is using both the resources
```
@Library('dancefloor') _

pipeline {
agent any
parameters {
choice(
name: 'parChoice',
choices: ['a', 'b', 'c'],
description: '''Your choice
''',
)
} // parameters
stages {
stage('Test') {
steps {
script {
println "* ${STAGE_NAME} - Begin"

println "** Var dump"
println "*** parChoice: [${parChoice}]"

final String txtContent = libraryResource 'endpoints.txt'
println "*** Shared Lib entry: [${txtContent}]"

dumpUtil.dumpSomething()

println "* ${STAGE_NAME} - End"
} // script
} // steps
} // stage
} // stages
} // pipeline
```
* Create a new account for Dummy-User 'Fred'
* Configure the job with the Matrix Auth plugin so that the user is not having Run/Build -> Replay permissions
* In a new private window of your browser, log in as 'Fred' and continue
* Trigger a new build of the job. The console log will show you:
* the chosen parameter
* content of `resources/endpoints.txt`
* content dumped by `vars/dumpUtil.groovy`
```
*** parChoice: [b]
*** Shared Lib entry: [Singing in the rain.
]
### Mambo No 7
```
* Now provide a new version of the Shared Library, where both files are edited
* `resources/endpoints.txt` with the content
```
Singing in the sun.
```
* `vars/dumpUtil.groovy` with the content
```
void dumpSomething() {
println '### Mambo No 5'
}
```
* Now as Fred, hit the 'Rebuild-which-is-pointing-to-Replay'-Button -> 'Run'
* In the console log, you will find
```
*** parChoice: [b]
*** Shared Lib entry: [Singing in the sun.
]
### Mambo No 7
```

### Expected Results

If the user is missing the Run/Build -> Replay permission
* do not show the button at all instead of renaming it to 'Rebuild' and limit the modification possibilities
* block the `/replay` URL and show and error message / go back link / ... so that the user can not work around the missing button

If the 'Rebuild'-functionality is needed, the 'Rebuilder'-plugin can be installed.
It is a slim, save, peaceful, MIT-licensed plugin with no known side effects with other plugins.
In my opinion, it is OK to suggest this as an alternative.

Reason:
If the user is missing the Run/Build -> Replay permission, it makes no sense to provide the 'Rebuild"-Button which is basically doing a Replay without the ability of modifying the sources.
It is correct that the user is not able to do modifications, but selling the blocked 'Replay' as 'Rebuild' is bad in my eyes. The reason is that like this now, it is possible to create builds with a quite undefined state of the Shared Library(s) used. That can also be dangerous and is at least unpredictable and hard to reproduce.

The purpose is clear and good: provide functionality so that a user can at least perform the part from the 'Replay' which allows him / her to trigger a build with the same parameters as for an already run build.
Honestly, this is exactly what the 'Rebuilder'-plugin is for.
Plus, if the 'Replay'-Button would simply be hidden if the permissions are missing, it would also solve
https://issues.jenkins.io/browse/JENKINS-76292 - Non-admin users see "Rebuild" button twice when Rebuilder plugin is installed

### Actual Results

See above;

The output of the Shared Library is mixed, as the `vars` and `src` contents are taken from the non-modifyable 'Replay' section, but `resources` is coming from the latest version of the Shared Library.
That produces a missmatch.

### Anything else?

Scenario / Example:
* Have a user `Fred` with missing permissions of 'Run/Build -> Replay'
* Have a build `#1`, using version `A` of a Shared Library
* Version `A` of Shared Library is providing a method `uploadArtifacts` somewhere under `vars`, which is collecting artifacts from the build and uploads it to an external Artifactory
* The endpoint definition of the external Artifactory is defined in the Shared Library under `resources/endpoints.json`
* Build `#1` uploads artifacts to the endpoint defined in `resources/endpoints.json`
* The company switched the provider of the external Artifactory, a new version `B` of the Shared Library was created
* `resources/endpoints.json` was adapted to point to the new endpoint
* `vars/uploadUtil#uploadArtifacts` was extended with a filter mechanism so that some artifacts are skipped during upload
* Now Fred hits `Rebuild` (which is, as said, `Replay` without the possibility to modify the Shared-Lib contents) on Build `#1`

What is now happening is that:
* Upload to new endpoint as defined in `resources/endpoints.json` (because this is taken from the latest Shared lib version available) -> Shared Lib version `B`
* `vars/uploadUtil#uploadArtifacts` is still the same logic as in build `#1`, missing the filter mechanism -> Shared Lib version `A`

This example shows that it is quite easy to use a totally inconsistant, hard to debug Shared Library version.
I consider this being a bug as the behaviour can be unpredictable and it should not be possible to mix up 'CICD logic' as a user that easy.

### Are you interested in contributing a fix?

_No response_

Contributor guide

Open the contributing guide

Research direction

Start by tracing the `/replay` URL and the Rebuild/Replay permission checks described in the reproduction. Compare how `resources` and `vars`/`src` Shared Library contents are selected when a user lacks Run/Build → Replay permission; done means unauthorized replay is blocked or hidden and the contents are no longer mixed.

Written by the indexing model from the issue text.

Assessment

Tech stack
java
Domain
backend
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.