I have written a few groovy utility methods for use in Jenkins Pipelines. A simple example is:
// src/org/package/utils.groovy
def remove_file(String file) {
new File(file).delete()
}
However, as expected this throws the following exception in Jenkins Pipeline:
org.jenkinsci.plugins.scriptsecurity.sandbox.RejectedAccessException: Scripts not permitted to use new java.io.File java.lang.String
According to the Script Security Plugin documentation, I can bypass this with the @Whitelisted
method annotation:
@Whitelisted
def remove_file(String file) {
new File(file).delete()
}
According to this question, I can bypass this with the @NonCPS
annotation (and the code in this method is of course not necessary to be serializable anyway):
@NonCPS
def remove_file(String file) {
new File(file).delete()
}
The shared library is being used in a global var like the following:
// vars/var.groovy
import org.package.utils
def method(body) {
...
new utils().remove_file('file')
...
}
The library/var is being imported and used in a Jenkinsfile like the following:
library identifier: 'lib@master', retriever: modernSCM(
[$class: 'GitSCMSource',
remote: 'https://github.com/org/repo.git'])
pipeline {
...
script {
var.method {
// body
}
}
...
}
Of the listed code-based approaches to importing a Pipeline shared library, the approach I utilized above seemed to be the only one that actually works with the standard Jenkins Pipeline plugin set, so that was the approach I took.
Both of these have failed at bypassing the security exception that Jenkins Pipeline is throwing. What is the best solution to bypass the script security plugin using code and avoiding manual/human-error solutions such as modifying the whitelist in the GUI?
I think I understand what is going on based on your last comment and your updates to your original question. I'll try and clarify the differences between global shared libraries and "regular" shared libraries.
There are (at the time of writing) a few different ways to register a Shared Library with a Jenkins instance.
Folder
- all items in the folder have the pipeline library available to themLibrary registration requires some setup:
library
step and @Library
annotation@Library
or library
)The Global Shared Libraries referenced above bypasses the security sandbox, which means they can do anything. Here is some relevant documentation:
Since these libraries will be globally usable, any Pipeline in the system can utilize functionality implemented in these libraries.
These libraries are considered "trusted:" they can run any methods in Java, Groovy, Jenkins internal APIs, Jenkins plugins, or third-party libraries. This allows you to define libraries which encapsulate individually unsafe APIs in a higher-level wrapper safe for use from any Pipeline. Beware that anyone able to push commits to this SCM repository could obtain unlimited access to Jenkins. You need the
Overall/RunScripts
permission to configure these libraries (normally this will be granted to Jenkins administrators).
These are the only pipeline libraries that bypass the security sandbox checks. The other kinds (like Folder libraries) are security checked, which means you cannot do things like new File(file).delete()
.
In a pipeline, users can access all of the shared libraries available to them. Implicitly loaded libraries from the installation or the folder (above noted as the Load implicitly option) are automatically available on the script classpath. Other libraries must be brought in using the @Library
annotation or the library
step.
For example, say you had a library with these options:
my-shared-lib
master
false
true
A few ways you could pull in this specific library from a pipeline would be:
@Library('my-shared-lib')
- use with default version@Library('my-shared-lib@develop')
- use with overridden version of develop
def myLib = library('my-shared-lib@develop')
- use with overridden version of develop
These will all use the previously configured library.
In your example, you are using the library
step, but dynamically loading from a remote repository instead of using a registered version. That is also why you are specifying the retriever
parameter. Remember that any library that is not a Global Shared Library cannot bypass the security sandbox.
If you want to have your library bypass the sandbox you need to register it with the Jenkins installation as a Global Shared Library.
A side note about new File(file).delete()
- be careful writing "normal" Groovy inside of Jenkins pipelines (see this answer for some details why).
If you are using Jenkins Job-DSL to create your jobs in a programmatic approach then you might use the following code to approve your pipeline script automatically.
def pipelineScript = 'your pipeline script'
def scriptApproval = Jenkins.instance.getExtensionList('org.jenkinsci.plugins.scriptsecurity.scripts.ScriptApproval')[0]
scriptApproval.approveScript(scriptApproval.hash(pipelineScript, 'groovy'))
script pipelineScript
This code might need a few tweaks depending on the version of the plugin you are using, else this should make things easier.
Peace!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With