diff --git a/.github/dependabot.yml b/.github/dependabot.yml
index aef9bce64eff..9cec7116807d 100644
--- a/.github/dependabot.yml
+++ b/.github/dependabot.yml
@@ -12,6 +12,7 @@ updates:
# see https://github.com/jenkinsci/jenkins/pull/5112#issuecomment-744429487 and https://github.com/jenkinsci/jenkins/pull/5116#issuecomment-744526638
# it would be good to update it at some point, but requires significant testing
- dependency-name: "org.codehaus.groovy:groovy-all"
+ versions: [">=2.5.0"]
# see https://github.com/jenkinsci/jenkins/pull/5184 should be updated with groovy-all
- dependency-name: "org.fusesource.jansi:jansi"
# see https://github.com/jenkinsci/jenkins/pull/5144#pullrequestreview-559661934
diff --git a/.github/workflows/changelog.yml b/.github/workflows/changelog.yml
index 4991a9fd5cab..e7b74ccfd717 100644
--- a/.github/workflows/changelog.yml
+++ b/.github/workflows/changelog.yml
@@ -28,7 +28,7 @@ jobs:
env:
GITHUB_AUTH: github-actions:${{ secrets.GITHUB_TOKEN }}
- name: Upload Changelog YAML
- uses: actions/upload-artifact@v2.3.0
+ uses: actions/upload-artifact@v2.3.1
with:
name: changelog.yaml
path: changelog.yaml
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index d981755ee137..23053598b75a 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -9,12 +9,10 @@ This page provides information about contributing code to the Jenkins core codeb
1. Fork the repository on GitHub
2. Clone the forked repository to your machine
3. Install the necessary development tools. In order to develop Jenkins, you need the following:
- * Java Development Kit (JDK) 8 or 11.
- In the Jenkins project we usually use [OpenJDK](http://openjdk.java.net/) or [AdoptOpenJDK](https://adoptopenjdk.net/), but you can use other JDKs as well.
- * For JDK 11 there might be some compatibility issues in developer tools,
- please see [this page](https://wiki.jenkins.io/display/JENKINS/Java+11+Developer+Guidelines#Java11DeveloperGuidelines-Knowndevelopertoolsissues) for more info.
- If you find a new issue, please report it with a `java11-devtools-compatibility` label in our issue tracker.
- * Maven 3.5.4 or above. You can [download Maven here].
+ * Java Development Kit (JDK) 11 or 8.
+ In the Jenkins project we usually use [Eclipse Adoptium](https://adoptium.net/) or [OpenJDK](https://openjdk.java.net/), but you can use other JDKs as well.
+ * Apache Maven 3.8.1 or above. You can [download Maven here].
+ In the Jenkins project we usually use the most recent Maven release.
* Any IDE which supports importing Maven projects.
* Install [NodeJS](https://nodejs.org/en/). **Note:** only needed to work on the frontend assets found in the `war` module.
* Frontend tasks are run using [yarn](https://yarnpkg.com/lang/en/). Run `npm install -g yarn` to install it.
@@ -92,7 +90,7 @@ cd war; yarn test
## Proposing Changes
The Jenkins project source code repositories are hosted at GitHub.
-All proposed changes are submitted, and code reviewed, using the _GitHub Pull Request_ process.
+All proposed changes are submitted, and code reviewed, using a [GitHub pull request] process.
To submit a pull request:
@@ -123,6 +121,7 @@ This will help minimize the diff, which makes reviewing PRs easier.
We also do not recommend `*` imports in the production code.
Please disable them in Settings > Editor > Codestyle > Java by setting _Class count to use import with '*'_ and Names count to use import with '*'_ to a high value, e.g. 100.
+
## Copyright
The Jenkins core is licensed under [MIT license], with a few exceptions in bundled classes.
@@ -150,15 +149,15 @@ just submit a pull request.
# Links
* [Jenkins Contribution Landing Page](https://www.jenkins.io/participate/)
-* [Jenkins IRC Channel](https://www.jenkins.io/chat/)
-* [Beginners Guide To Contributing](https://wiki.jenkins.io/display/JENKINS/Beginners+Guide+to+Contributing)
+* [Jenkins Chat Channels](https://www.jenkins.io/chat/)
+* [Beginners Guide To Contributing](https://www.jenkins.io/participate/)
* [List of newbie-friendly issues in the core](https://issues.jenkins.io/issues/?jql=project%20%3D%20JENKINS%20AND%20status%20in%20(Open%2C%20%22In%20Progress%22%2C%20Reopened)%20AND%20component%20%3D%20core%20AND%20labels%20in%20(newbie-friendly))
[Preparing for Plugin Development]: https://www.jenkins.io/doc/developer/tutorial/prepare/
[newbie friendly issues]: https://issues.jenkins.io/issues/?jql=project%20%3D%20JENKINS%20AND%20status%20in%20(Open%2C%20%22In%20Progress%22%2C%20Reopened)%20AND%20component%20%3D%20core%20AND%20labels%20in%20(newbie-friendly)
[Participate]: https://www.jenkins.io/participate/
[building and debugging process here]: https://www.jenkins.io/doc/developer/building/
-[guide]: https://wiki.jenkins.io/display/JENKINS/Starting+and+Accessing+Jenkins
+[guide]: https://www.jenkins.io/doc/book/installing/war-file/#run-the-war-file
[Remote Debug Flags]: https://stackoverflow.com/questions/975271/remote-debugging-a-java-application
[Acceptance Test Harness (ATH)]: https://github.com/jenkinsci/acceptance-test-harness
[backporting process]: https://www.jenkins.io/download/lts/
@@ -170,3 +169,4 @@ just submit a pull request.
[Jenkins Pipeline]: https://www.jenkins.io/doc/book/pipeline/
[Jenkinsfile]: ./Jenkinsfile
[download Maven here]: https://maven.apache.org/download.cgi
+[GitHub pull request]: https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-pull-requests
\ No newline at end of file
diff --git a/Jenkinsfile b/Jenkinsfile
index 4cd19525f6e9..c4fca9efa1fd 100644
--- a/Jenkinsfile
+++ b/Jenkinsfile
@@ -7,8 +7,6 @@
def buildNumber = BUILD_NUMBER as int; if (buildNumber > 1) milestone(buildNumber - 1); milestone(buildNumber) // JENKINS-43353 / JENKINS-58625
-// TEST FLAG - to make it easier to turn on/off unit tests for speeding up access to later stuff.
-def runTests = true
def failFast = false
// Same memory sizing for both builds and ATH
def javaOpts = ["JAVA_OPTS=-Xmx1536m -Xms512m","MAVEN_OPTS=-Xmx1536m -Xms512m"]
@@ -18,8 +16,7 @@ properties([
disableConcurrentBuilds(abortPrevious: true)
])
-// TODO: Restore 'Windows' once https://groups.google.com/forum/#!topic/jenkinsci-dev/v9d-XosOp2s is resolved
-def buildTypes = ['Linux']
+def buildTypes = ['Linux', 'Windows']
def jdks = [8, 11]
def builds = [:]
@@ -27,9 +24,16 @@ for(i = 0; i < buildTypes.size(); i++) {
for(j = 0; j < jdks.size(); j++) {
def buildType = buildTypes[i]
def jdk = jdks[j]
+ if (buildType == 'Windows' && jdk == 8) {
+ continue // unnecessary use of hardware
+ }
builds["${buildType}-jdk${jdk}"] = {
// see https://github.com/jenkins-infra/documentation/blob/master/ci.adoc#node-labels for information on what node types are available
- node(buildType == 'Linux' ? (jdk == 8 ? 'maven' : 'maven-11') : buildType.toLowerCase()) {
+ String agentContainerLabel = jdk == 8 ? 'maven' : 'maven-11'
+ if (buildType == 'Windows') {
+ agentContainerLabel += '-windows'
+ }
+ node(agentContainerLabel) {
// First stage is actually checking out the source. Since we're using Multibranch
// currently, we can use "checkout scm".
stage('Checkout') {
@@ -42,21 +46,21 @@ for(j = 0; j < jdks.size(); j++) {
// Now run the actual build.
stage("${buildType} Build / Test") {
timeout(time: 300, unit: 'MINUTES') {
+ realtimeJUnit(healthScaleFactor: 20.0, testResults: '*/target/surefire-reports/*.xml,war/junit.xml') {
// -Dmaven.repo.local=… tells Maven to create a subdir in the temporary directory for the local Maven repository
// -ntp requires Maven >= 3.6.1
- def mvnCmd = "mvn -Pdebug -Pjapicmp -U -Dset.changelist help:evaluate -Dexpression=changelist -Doutput=$changelistF clean install ${runTests ? '-Dmaven.test.failure.ignore' : '-DskipTests'} -V -B -ntp -Dmaven.repo.local=$m2repo -Dspotbugs.failOnError=false -Dcheckstyle.failOnViolation=false -e"
+ def mvnCmd = "mvn -Pdebug -Pjapicmp -U -Dset.changelist help:evaluate -Dexpression=changelist -Doutput=$changelistF clean install -Dmaven.test.failure.ignore -V -B -ntp -Dmaven.repo.local=$m2repo -Dspotbugs.failOnError=false -Dcheckstyle.failOnViolation=false -e"
infra.runWithMaven(mvnCmd, jdk.toString(), javaOpts, true)
if(isUnix()) {
sh 'git add . && git diff --exit-code HEAD'
}
+ }
}
}
// Once we've built, archive the artifacts and the test results.
stage("${buildType} Publishing") {
- if (runTests) {
- junit healthScaleFactor: 20.0, testResults: '*/target/surefire-reports/*.xml,war/junit.xml'
archiveArtifacts allowEmptyArchive: true, artifacts: '**/target/surefire-reports/*.dumpstream'
if (! fileExists('core/target/surefire-reports/TEST-jenkins.Junit4TestsRanTest.xml') ) {
error 'junit 4 tests are no longer being run for the core package'
@@ -67,7 +71,6 @@ for(j = 0; j < jdks.size(); j++) {
if (failFast && currentBuild.result == 'UNSTABLE') {
error 'There were test failures; halting early'
}
- }
if (buildType == 'Linux' && jdk == jdks[0]) {
def folders = env.JOB_NAME.split('/')
if (folders.length > 1) {
@@ -109,8 +112,6 @@ for(j = 0; j < jdks.size(); j++) {
}
}}
-// TODO: Restore ATH once https://groups.google.com/forum/#!topic/jenkinsci-dev/v9d-XosOp2s is resolved
-// TODO: ATH flow now supports Java 8 only, it needs to be reworked (INFRA-1690)
builds.ath = {
node("docker-highmem") {
// Just to be safe
@@ -120,7 +121,7 @@ builds.ath = {
dir("sources") {
checkout scm
def mvnCmd = 'mvn --batch-mode --show-version -ntp -Pquick-build -am -pl war package -Dmaven.repo.local=$WORKSPACE_TMP/m2repo'
- infra.runWithMaven(mvnCmd, "8", javaOpts, true)
+ infra.runWithMaven(mvnCmd, "11", javaOpts, true)
dir("war/target") {
fileUri = "file://" + pwd() + "/jenkins.war"
}
diff --git a/bom/pom.xml b/bom/pom.xml
index 2584d153c91c..305fcfeda881 100644
--- a/bom/pom.xml
+++ b/bom/pom.xml
@@ -41,8 +41,8 @@ THE SOFTWARE.
9.21.7.32
- 1593.v0e838714faae
- 2.4.12
+ 1612.v2a13b906bf3a
+ 2.4.21
@@ -50,7 +50,7 @@ THE SOFTWARE.
org.springframework.securityspring-security-bom
- 5.6.0
+ 5.6.1pomimport
@@ -173,18 +173,18 @@ THE SOFTWARE.
org.jenkins-ciannotation-indexer
- 1.15
+ 1.16org.jenkins-civersion-number
- 1.8
+ 1.9org.jenkins-cicrypto-util
- 1.6
+ 1.7org.connectbot.jbcrypt
@@ -234,7 +234,7 @@ THE SOFTWARE.
org.jenkins-citask-reactor
- 1.6
+ 1.7org.jvnet.localizer
@@ -284,7 +284,7 @@ THE SOFTWARE.
org.jenkins-cimemory-monitor
- 1.10
+ 1.11net.java.dev.jna
diff --git a/cli/pom.xml b/cli/pom.xml
index 0a296a810e00..59a7354a8527 100644
--- a/cli/pom.xml
+++ b/cli/pom.xml
@@ -103,6 +103,13 @@
com.github.spotbugsspotbugs-annotationsprovided
+ true
+
+
+ com.google.code.findbugs
+ jsr305
+
+ commons-lang
diff --git a/cli/src/main/java/hudson/cli/CLIConnectionFactory.java b/cli/src/main/java/hudson/cli/CLIConnectionFactory.java
index eceff79eff3c..0553dd215de5 100644
--- a/cli/src/main/java/hudson/cli/CLIConnectionFactory.java
+++ b/cli/src/main/java/hudson/cli/CLIConnectionFactory.java
@@ -1,5 +1,6 @@
package hudson.cli;
+import java.nio.charset.StandardCharsets;
import java.util.Base64;
/**
@@ -33,7 +34,7 @@ public CLIConnectionFactory basicAuth(String username, String password) {
* Cf. {@code BasicHeaderApiTokenAuthenticator}.
*/
public CLIConnectionFactory basicAuth(String userInfo) {
- return authorization("Basic " + Base64.getEncoder().encodeToString(userInfo.getBytes()));
+ return authorization("Basic " + Base64.getEncoder().encodeToString(userInfo.getBytes(StandardCharsets.UTF_8)));
}
/**
diff --git a/cli/src/main/java/hudson/cli/PrivateKeyProvider.java b/cli/src/main/java/hudson/cli/PrivateKeyProvider.java
index 1ab1eff33049..f283053f9cb5 100644
--- a/cli/src/main/java/hudson/cli/PrivateKeyProvider.java
+++ b/cli/src/main/java/hudson/cli/PrivateKeyProvider.java
@@ -23,7 +23,6 @@
*/
package hudson.cli;
-import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.logging.Level.FINE;
import java.io.ByteArrayInputStream;
@@ -32,6 +31,7 @@
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.InvalidPathException;
import java.nio.file.Paths;
@@ -134,7 +134,7 @@ private static String readPemFile(File f) throws IOException{
DataInputStream dis = new DataInputStream(is)) {
byte[] bytes = new byte[(int) f.length()];
dis.readFully(bytes);
- return new String(bytes);
+ return new String(bytes, StandardCharsets.UTF_8);
} catch (InvalidPathException e) {
throw new IOException(e);
}
@@ -143,7 +143,7 @@ private static String readPemFile(File f) throws IOException{
public static KeyPair loadKey(String pemString, String passwd) throws IOException, GeneralSecurityException {
Iterable itr = SecurityUtils.loadKeyPairIdentities(null,
new PathResource(Paths.get("key")),
- new ByteArrayInputStream(pemString.getBytes(UTF_8)),
+ new ByteArrayInputStream(pemString.getBytes(StandardCharsets.UTF_8)),
FilePasswordProvider.of(passwd));
long numLoaded = itr == null ? 0 : StreamSupport.stream(itr.spliterator(), false).count();
if (numLoaded <= 0) {
diff --git a/core/pom.xml b/core/pom.xml
index b85438f1c60b..48d4ea485f9b 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -41,7 +41,7 @@ THE SOFTWARE.
true2.2
- 2.8.3
+ 2.8.4
@@ -127,12 +127,6 @@ THE SOFTWARE.
org.kohsuke.staplerstapler
-
-
- com.google.code.findbugs
- jsr305
-
- org.kohsuke.stapler
@@ -517,12 +511,16 @@ THE SOFTWARE.
com.github.spotbugsspotbugs-annotations
- true
+
+
+ com.google.code.findbugs
+ jsr305
+
+ net.jcipjcip-annotations
- true
diff --git a/core/src/main/java/hudson/ClassicPluginStrategy.java b/core/src/main/java/hudson/ClassicPluginStrategy.java
index 523474a89d88..ef29f481586b 100644
--- a/core/src/main/java/hudson/ClassicPluginStrategy.java
+++ b/core/src/main/java/hudson/ClassicPluginStrategy.java
@@ -480,7 +480,7 @@ private static void parseClassPath(Manifest manifest, File archive, List p
* Explodes the plugin into a directory, if necessary.
*/
private static void explode(File archive, File destDir) throws IOException {
- Files.createDirectories(Util.fileToPath(destDir));
+ Util.createDirectories(Util.fileToPath(destDir));
// timestamp check
File explodeTime = new File(destDir,".timestamp2");
@@ -549,7 +549,7 @@ protected void zipDir(Resource dir, ZipOutputStream zOut, String vPath,
};
z.setProject(prj);
z.setTaskType("zip");
- Files.createDirectories(Util.fileToPath(classesJar.getParentFile()));
+ Util.createDirectories(Util.fileToPath(classesJar.getParentFile()));
z.setDestFile(classesJar);
z.add(mapper);
z.execute();
diff --git a/core/src/main/java/hudson/ExtensionList.java b/core/src/main/java/hudson/ExtensionList.java
index c453a46b3d30..89098e86ea28 100644
--- a/core/src/main/java/hudson/ExtensionList.java
+++ b/core/src/main/java/hudson/ExtensionList.java
@@ -38,6 +38,7 @@
import java.util.Iterator;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.Vector;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
@@ -327,7 +328,7 @@ private List> ensureLoaded() {
* Chooses the object that locks the loading of the extension instances.
*/
protected Object getLoadLock() {
- return jenkins.lookup.setIfNull(Lock.class,new Lock());
+ return Objects.requireNonNull(jenkins).lookup.setIfNull(Lock.class,new Lock());
}
/**
@@ -379,7 +380,7 @@ protected List> load() {
LOGGER.log(Level.FINER, String.format("Loading ExtensionList '%s' from", extensionType.getName()), new Throwable("Only present for stacktrace information"));
}
- return jenkins.getPluginManager().getPluginStrategy().findComponents(extensionType, hudson);
+ return Objects.requireNonNull(jenkins).getPluginManager().getPluginStrategy().findComponents(extensionType, hudson);
}
/**
diff --git a/core/src/main/java/hudson/FilePath.java b/core/src/main/java/hudson/FilePath.java
index 5b79710b25f3..cadb8cc257e9 100644
--- a/core/src/main/java/hudson/FilePath.java
+++ b/core/src/main/java/hudson/FilePath.java
@@ -86,6 +86,7 @@
import java.net.URI;
import java.net.URL;
import java.net.URLConnection;
+import java.nio.charset.Charset;
import java.nio.file.FileSystemException;
import java.nio.file.FileSystems;
import java.nio.file.Files;
@@ -118,10 +119,8 @@
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
-import jenkins.FilePathFilter;
import jenkins.MasterToSlaveFileCallable;
import jenkins.SlaveToMasterFileCallable;
-import jenkins.SoloFilePathFilter;
import jenkins.model.Jenkins;
import jenkins.security.MasterToSlaveCallable;
import jenkins.util.ContextResettingExecutorService;
@@ -515,7 +514,7 @@ public int archive(final ArchiverFactory factory, OutputStream os, final DirScan
final OutputStream out = channel != null ? new RemoteOutputStream(os) : os;
return act(new Archive(factory, out, scanner, verificationRoot, noFollowLinks));
}
- private static class Archive extends SecureFileCallable {
+ private static class Archive extends MasterToSlaveFileCallable {
private final ArchiverFactory factory;
private final OutputStream out;
private final DirScanner scanner;
@@ -533,7 +532,7 @@ private static class Archive extends SecureFileCallable {
public Integer invoke(File f, VirtualChannel channel) throws IOException {
Archiver a = factory.create(out);
try {
- scanner.scan(f, ignoringSymlinks(reading(a), verificationRoot, noFollowLinks));
+ scanner.scan(f, ignoringSymlinks(a, verificationRoot, noFollowLinks));
} finally {
a.close();
}
@@ -568,7 +567,7 @@ public void unzip(final FilePath target) throws IOException, InterruptedExceptio
target.act(new UnzipLocal(this));
}
}
- private static class UnzipRemote extends SecureFileCallable {
+ private static class UnzipRemote extends MasterToSlaveFileCallable {
private final RemoteInputStream in;
UnzipRemote(RemoteInputStream in) {
this.in = in;
@@ -580,7 +579,7 @@ public Void invoke(File dir, VirtualChannel channel) throws IOException, Interru
}
private static final long serialVersionUID = 1L;
}
- private static class UnzipLocal extends SecureFileCallable {
+ private static class UnzipLocal extends MasterToSlaveFileCallable {
private final FilePath filePath;
@@ -593,7 +592,7 @@ public Void invoke(File dir, VirtualChannel channel) throws IOException, Interru
if (this.filePath.isRemote()) {
throw new IllegalStateException("Expected local path for file: " + filePath); // this.channel==target.channel above
}
- unzip(dir, reading(new File(this.filePath.getRemote()))); // shortcut to local file
+ unzip(dir, new File(this.filePath.getRemote())); // shortcut to local file
return null;
}
private static final long serialVersionUID = 1L;
@@ -619,7 +618,7 @@ public void untar(final FilePath target, final TarCompression compression) throw
target.act(new UntarLocal(source, compression));
}
}
- private static class UntarRemote extends SecureFileCallable {
+ private static class UntarRemote extends MasterToSlaveFileCallable {
private final TarCompression compression;
private final RemoteInputStream in;
private final String name;
@@ -635,7 +634,7 @@ public Void invoke(File dir, VirtualChannel channel) throws IOException, Interru
}
private static final long serialVersionUID = 1L;
}
- private static class UntarLocal extends SecureFileCallable {
+ private static class UntarLocal extends MasterToSlaveFileCallable {
private final TarCompression compression;
private final FilePath filePath;
@@ -663,7 +662,7 @@ public void unzipFrom(InputStream _in) throws IOException, InterruptedException
final InputStream in = new RemoteInputStream(_in, Flag.GREEDY);
act(new UnzipFrom(in));
}
- private static class UnzipFrom extends SecureFileCallable {
+ private static class UnzipFrom extends MasterToSlaveFileCallable {
private final InputStream in;
UnzipFrom(InputStream in) {
this.in = in;
@@ -709,7 +708,7 @@ private static void unzip(File dir, File zipFile) throws IOException {
mkdirs(p);
}
try (InputStream input = zip.getInputStream(e)) {
- IOUtils.copy(input, writing(f));
+ IOUtils.copy(input, f);
}
try {
FilePath target = new FilePath(f);
@@ -733,11 +732,11 @@ private static void unzip(File dir, File zipFile) throws IOException {
public FilePath absolutize() throws IOException, InterruptedException {
return new FilePath(channel, act(new Absolutize()));
}
- private static class Absolutize extends SecureFileCallable {
+ private static class Absolutize extends MasterToSlaveFileCallable {
private static final long serialVersionUID = 1L;
@Override
public String invoke(File f, VirtualChannel channel) throws IOException {
- return stating(f).getAbsolutePath();
+ return f.getAbsolutePath();
}
}
@@ -745,7 +744,7 @@ public String invoke(File f, VirtualChannel channel) throws IOException {
public boolean hasSymlink(FilePath verificationRoot, boolean noFollowLinks) throws IOException, InterruptedException {
return act(new HasSymlink(verificationRoot == null ? null : verificationRoot.remote, noFollowLinks));
}
- private static class HasSymlink extends SecureFileCallable {
+ private static class HasSymlink extends MasterToSlaveFileCallable {
private static final long serialVersionUID = 1L;
private final String verificationRoot;
private final boolean noFollowLinks;
@@ -757,7 +756,7 @@ private static class HasSymlink extends SecureFileCallable {
@Override
public Boolean invoke(File f, VirtualChannel channel) throws IOException {
- return isSymlink(stating(f), verificationRoot, noFollowLinks);
+ return isSymlink(f, verificationRoot, noFollowLinks);
}
}
@@ -795,7 +794,7 @@ public boolean accept(File file) {
public void symlinkTo(final String target, final TaskListener listener) throws IOException, InterruptedException {
act(new SymlinkTo(target, listener));
}
- private static class SymlinkTo extends SecureFileCallable {
+ private static class SymlinkTo extends MasterToSlaveFileCallable {
private final String target;
private final TaskListener listener;
SymlinkTo(String target, TaskListener listener) {
@@ -805,7 +804,7 @@ private static class SymlinkTo extends SecureFileCallable {
private static final long serialVersionUID = 1L;
@Override
public Void invoke(File f, VirtualChannel channel) throws IOException, InterruptedException {
- Util.createSymlink(symlinking(f).getParentFile(), target, f.getName(), listener);
+ Util.createSymlink(f.getParentFile(), target, f.getName(), listener);
return null;
}
}
@@ -820,11 +819,11 @@ public Void invoke(File f, VirtualChannel channel) throws IOException, Interrupt
public String readLink() throws IOException, InterruptedException {
return act(new ReadLink());
}
- private static class ReadLink extends SecureFileCallable {
+ private static class ReadLink extends MasterToSlaveFileCallable {
private static final long serialVersionUID = 1L;
@Override
public String invoke(File f, VirtualChannel channel) throws IOException, InterruptedException {
- return Util.resolveSymlink(reading(f));
+ return Util.resolveSymlink(f);
}
}
@@ -898,7 +897,7 @@ public void untarFrom(InputStream _in, final TarCompression compression) throws
_in.close();
}
}
- private static class UntarFrom extends SecureFileCallable {
+ private static class UntarFrom extends MasterToSlaveFileCallable {
private final TarCompression compression;
private final InputStream in;
UntarFrom(TarCompression compression, InputStream in) {
@@ -907,7 +906,7 @@ private static class UntarFrom extends SecureFileCallable {
}
@Override
public Void invoke(File dir, VirtualChannel channel) throws IOException {
- readFromTar("input stream",dir, compression.extract(in)); // #writing etc. are called in #readFromTar
+ readFromTar("input stream",dir, compression.extract(in));
return null;
}
private static final long serialVersionUID = 1L;
@@ -1097,7 +1096,7 @@ public void copyFrom(FilePath src) throws IOException, InterruptedException {
public void copyFrom(FileItem file) throws IOException, InterruptedException {
if(channel==null) {
try {
- file.write(writing(new File(remote)));
+ file.write(new File(remote));
} catch (IOException e) {
throw e;
} catch (Exception e) {
@@ -1138,15 +1137,6 @@ public interface FileCallable extends Serializable, RoleSensitive {
T invoke(File f, VirtualChannel channel) throws IOException, InterruptedException;
}
- /**
- * {@link FileCallable}s that can be executed anywhere, including the controller.
- *
- * The code is the same as {@link SlaveToMasterFileCallable}, but used as a marker to
- * designate those impls that use {@link FilePathFilter}.
- */
- /*package*/ abstract static class SecureFileCallable extends SlaveToMasterFileCallable {
- }
-
/**
* Executes some program on the machine that this {@link FilePath} exists,
* so that one can perform local file operations.
@@ -1300,11 +1290,11 @@ public void checkRoles(RoleChecker checker) throws SecurityException {
public URI toURI() throws IOException, InterruptedException {
return act(new ToURI());
}
- private static class ToURI extends SecureFileCallable {
+ private static class ToURI extends MasterToSlaveFileCallable {
private static final long serialVersionUID = 1L;
@Override
public URI invoke(File f, VirtualChannel channel) {
- return stating(f).toURI();
+ return f.toURI();
}
}
@@ -1342,7 +1332,7 @@ public void mkdirs() throws IOException, InterruptedException {
throw new IOException("Failed to mkdirs: " + remote);
}
}
- private static class Mkdirs extends SecureFileCallable {
+ private static class Mkdirs extends MasterToSlaveFileCallable {
private static final long serialVersionUID = 1L;
@Override
public Boolean invoke(File f, VirtualChannel channel) throws IOException, InterruptedException {
@@ -1368,14 +1358,14 @@ public void deleteSuffixesRecursive() throws IOException, InterruptedException {
/**
* Deletes all suffixed directories that are separated by {@link WorkspaceList#COMBINATOR}, including all its contents recursively.
*/
- private static class DeleteSuffixesRecursive extends SecureFileCallable {
+ private static class DeleteSuffixesRecursive extends MasterToSlaveFileCallable {
private static final long serialVersionUID = 1L;
@Override
public Void invoke(File f, VirtualChannel channel) throws IOException {
for (File file : listParentFiles(f)) {
if (file.getName().startsWith(f.getName() + WorkspaceList.COMBINATOR)) {
- Util.deleteRecursive(file.toPath(), path -> deleting(path.toFile()));
+ Util.deleteRecursive(file.toPath(), path -> path.toFile());
}
}
@@ -1400,11 +1390,11 @@ private static File[] listParentFiles(File f) {
public void deleteRecursive() throws IOException, InterruptedException {
act(new DeleteRecursive());
}
- private static class DeleteRecursive extends SecureFileCallable {
+ private static class DeleteRecursive extends MasterToSlaveFileCallable {
private static final long serialVersionUID = 1L;
@Override
public Void invoke(File f, VirtualChannel channel) throws IOException {
- Util.deleteRecursive(fileToPath(f), path -> deleting(path.toFile()));
+ Util.deleteRecursive(fileToPath(f), path -> path.toFile());
return null;
}
}
@@ -1415,11 +1405,11 @@ public Void invoke(File f, VirtualChannel channel) throws IOException {
public void deleteContents() throws IOException, InterruptedException {
act(new DeleteContents());
}
- private static class DeleteContents extends SecureFileCallable {
+ private static class DeleteContents extends MasterToSlaveFileCallable {
private static final long serialVersionUID = 1L;
@Override
public Void invoke(File f, VirtualChannel channel) throws IOException {
- Util.deleteContentsRecursive(fileToPath(f), path -> deleting(path.toFile()));
+ Util.deleteContentsRecursive(fileToPath(f), path -> path.toFile());
return null;
}
}
@@ -1518,7 +1508,7 @@ public FilePath createTempFile(final String prefix, final String suffix) throws
throw new IOException("Failed to create a temp file on "+remote,e);
}
}
- private static class CreateTempFile extends SecureFileCallable {
+ private static class CreateTempFile extends MasterToSlaveFileCallable {
private final String prefix;
private final String suffix;
CreateTempFile(String prefix, String suffix) {
@@ -1528,8 +1518,7 @@ private static class CreateTempFile extends SecureFileCallable {
private static final long serialVersionUID = 1L;
@Override
public String invoke(File dir, VirtualChannel channel) throws IOException {
- creating(new File(dir, prefix + "-security-check-dummy-" + suffix)); // use fake file to check access before creation
- File f = creating(File.createTempFile(prefix, suffix, dir));
+ File f = File.createTempFile(prefix, suffix, dir);
return f.getName();
}
}
@@ -1583,7 +1572,7 @@ public FilePath createTextTempFile(final String prefix, final String suffix, fin
throw new IOException("Failed to create a temp file on "+remote,e);
}
}
- private static class CreateTextTempFile extends SecureFileCallable {
+ private static class CreateTextTempFile extends MasterToSlaveFileCallable {
private static final long serialVersionUID = 1L;
private final boolean inThisDirectory;
private final String prefix;
@@ -1604,13 +1593,12 @@ public String invoke(File dir, VirtualChannel channel) throws IOException {
File f;
try {
- creating(new File(dir, prefix + "-security-check-dummy-" + suffix)); // use fake file to check access before creation
- f = creating(File.createTempFile(prefix, suffix, dir));
+ f = File.createTempFile(prefix, suffix, dir);
} catch (IOException e) {
throw new IOException("Failed to create a temporary directory in "+dir,e);
}
- try (Writer w = new FileWriter(writing(f))) {
+ try (Writer w = new FileWriter(f)) {
w.write(contents);
}
@@ -1646,7 +1634,7 @@ public FilePath createTempDir(final String prefix, final String suffix) throws I
throw new IOException("Failed to create a temp directory on "+remote,e);
}
}
- private static class CreateTempDir extends SecureFileCallable {
+ private static class CreateTempDir extends MasterToSlaveFileCallable {
private final String name;
CreateTempDir(String name) {
this.name = name;
@@ -1654,7 +1642,6 @@ private static class CreateTempDir extends SecureFileCallable {
private static final long serialVersionUID = 1L;
@Override
public String invoke(File dir, VirtualChannel channel) throws IOException {
- mkdirsing(new File(dir, name + "-security-test")); // ensure access
Path tempPath;
final boolean isPosix = FileSystems.getDefault().supportedFileAttributeViews().contains("posix");
@@ -1666,7 +1653,7 @@ public String invoke(File dir, VirtualChannel channel) throws IOException {
tempPath = Files.createTempDirectory(Util.fileToPath(dir), name);
}
- if (mkdirsing(tempPath.toFile()) == null) {
+ if (tempPath.toFile() == null) {
throw new IOException("Failed to obtain file from path " + dir);
}
return tempPath.toFile().getName();
@@ -1682,11 +1669,11 @@ public boolean delete() throws IOException, InterruptedException {
act(new Delete());
return true;
}
- private static class Delete extends SecureFileCallable {
+ private static class Delete extends MasterToSlaveFileCallable {
private static final long serialVersionUID = 1L;
@Override
public Void invoke(File f, VirtualChannel channel) throws IOException {
- Util.deleteFile(deleting(f));
+ Util.deleteFile(f);
return null;
}
}
@@ -1697,11 +1684,11 @@ public Void invoke(File f, VirtualChannel channel) throws IOException {
public boolean exists() throws IOException, InterruptedException {
return act(new Exists());
}
- private static class Exists extends SecureFileCallable {
+ private static class Exists extends MasterToSlaveFileCallable {
private static final long serialVersionUID = 1L;
@Override
public Boolean invoke(File f, VirtualChannel channel) throws IOException {
- return stating(f).exists();
+ return f.exists();
}
}
@@ -1715,11 +1702,11 @@ public Boolean invoke(File f, VirtualChannel channel) throws IOException {
public long lastModified() throws IOException, InterruptedException {
return act(new LastModified());
}
- private static class LastModified extends SecureFileCallable {
+ private static class LastModified extends MasterToSlaveFileCallable {
private static final long serialVersionUID = 1L;
@Override
public Long invoke(File f, VirtualChannel channel) throws IOException {
- return stating(f).lastModified();
+ return f.lastModified();
}
}
@@ -1731,7 +1718,7 @@ public Long invoke(File f, VirtualChannel channel) throws IOException {
public void touch(final long timestamp) throws IOException, InterruptedException {
act(new Touch(timestamp));
}
- private static class Touch extends SecureFileCallable {
+ private static class Touch extends MasterToSlaveFileCallable {
private final long timestamp;
Touch(long timestamp) {
this.timestamp = timestamp;
@@ -1740,9 +1727,9 @@ private static class Touch extends SecureFileCallable {
@Override
public Void invoke(File f, VirtualChannel channel) throws IOException {
if(!f.exists()) {
- Files.newOutputStream(fileToPath(creating(f))).close();
+ Files.newOutputStream(fileToPath(f)).close();
}
- if(!stating(f).setLastModified(timestamp))
+ if(!f.setLastModified(timestamp))
throw new IOException("Failed to set the timestamp of "+f+" to "+timestamp);
return null;
}
@@ -1755,7 +1742,7 @@ private void setLastModifiedIfPossible(final long timestamp) throws IOException,
LOGGER.warning(message);
}
}
- private static class SetLastModified extends SecureFileCallable {
+ private static class SetLastModified extends MasterToSlaveFileCallable {
private final long timestamp;
SetLastModified(long timestamp) {
this.timestamp = timestamp;
@@ -1763,7 +1750,7 @@ private static class SetLastModified extends SecureFileCallable {
private static final long serialVersionUID = -828220335793641630L;
@Override
public String invoke(File f, VirtualChannel channel) throws IOException {
- if(!writing(f).setLastModified(timestamp)) {
+ if(!f.setLastModified(timestamp)) {
if (Functions.isWindows()) {
// On Windows this seems to fail often. See JENKINS-11073
// Therefore don't fail, but just log a warning
@@ -1782,11 +1769,11 @@ public String invoke(File f, VirtualChannel channel) throws IOException {
public boolean isDirectory() throws IOException, InterruptedException {
return act(new IsDirectory());
}
- private static class IsDirectory extends SecureFileCallable {
+ private static class IsDirectory extends MasterToSlaveFileCallable {
private static final long serialVersionUID = 1L;
@Override
public Boolean invoke(File f, VirtualChannel channel) throws IOException {
- return stating(f).isDirectory();
+ return f.isDirectory();
}
}
@@ -1798,11 +1785,11 @@ public Boolean invoke(File f, VirtualChannel channel) throws IOException {
public long length() throws IOException, InterruptedException {
return act(new Length());
}
- private static class Length extends SecureFileCallable {
+ private static class Length extends MasterToSlaveFileCallable {
private static final long serialVersionUID = 1L;
@Override
public Long invoke(File f, VirtualChannel channel) throws IOException {
- return stating(f).length();
+ return f.length();
}
}
@@ -1875,7 +1862,7 @@ public void chmod(final int mask) throws IOException, InterruptedException {
if(!isUnix() || mask==-1) return;
act(new Chmod(mask));
}
- private static class Chmod extends SecureFileCallable {
+ private static class Chmod extends MasterToSlaveFileCallable {
private static final long serialVersionUID = 1L;
private final int mask;
Chmod(int mask) {
@@ -1883,7 +1870,7 @@ private static class Chmod extends SecureFileCallable {
}
@Override
public Void invoke(File f, VirtualChannel channel) throws IOException {
- _chmod(writing(f), mask);
+ _chmod(f, mask);
return null;
}
@@ -1914,11 +1901,11 @@ public int mode() throws IOException, InterruptedException, PosixException {
if(!isUnix()) return -1;
return act(new Mode());
}
- private static class Mode extends SecureFileCallable {
+ private static class Mode extends MasterToSlaveFileCallable {
private static final long serialVersionUID = 1L;
@Override
public Integer invoke(File f, VirtualChannel channel) throws IOException {
- return IOUtils.mode(stating(f));
+ return IOUtils.mode(f);
}
}
@@ -1984,7 +1971,7 @@ public List list(final FileFilter filter) throws IOException, Interrup
}
return act(new ListFilter(filter), (filter != null ? filter : this).getClass().getClassLoader());
}
- private static class ListFilter extends SecureFileCallable> {
+ private static class ListFilter extends MasterToSlaveFileCallable> {
private final FileFilter filter;
ListFilter(FileFilter filter) {
this.filter = filter;
@@ -1992,7 +1979,7 @@ private static class ListFilter extends SecureFileCallable> {
private static final long serialVersionUID = 1L;
@Override
public List invoke(File f, VirtualChannel channel) throws IOException {
- File[] children = reading(f).listFiles(filter);
+ File[] children = f.listFiles(filter);
if (children == null) {
return Collections.emptyList();
}
@@ -2050,7 +2037,7 @@ public FilePath[] list(final String includes, final String excludes) throws IOEx
public FilePath[] list(final String includes, final String excludes, final boolean defaultExcludes) throws IOException, InterruptedException {
return act(new ListGlob(includes, excludes, defaultExcludes));
}
- private static class ListGlob extends SecureFileCallable {
+ private static class ListGlob extends MasterToSlaveFileCallable {
private final String includes;
private final String excludes;
private final boolean defaultExcludes;
@@ -2062,11 +2049,11 @@ private static class ListGlob extends SecureFileCallable {
private static final long serialVersionUID = 1L;
@Override
public FilePath[] invoke(File f, VirtualChannel channel) throws IOException {
- String[] files = glob(reading(f), includes, excludes, defaultExcludes);
+ String[] files = glob(f, includes, excludes, defaultExcludes);
FilePath[] r = new FilePath[files.length];
for( int i=0; i {
+ private static class Read extends MasterToSlaveFileCallable {
private static final long serialVersionUID = 1L;
private final Pipe p;
private String verificationRoot;
@@ -2202,7 +2189,7 @@ private static class Read extends SecureFileCallable {
}
@Override
public Void invoke(File f, VirtualChannel channel) throws IOException, InterruptedException {
- try (InputStream fis = newInputStreamDenyingSymlinkAsNeeded(reading(f), verificationRoot, noFollowLinks); OutputStream out = p.getOut()) {
+ try (InputStream fis = newInputStreamDenyingSymlinkAsNeeded(f, verificationRoot, noFollowLinks); OutputStream out = p.getOut()) {
org.apache.commons.io.IOUtils.copy(fis, out);
} catch (Exception x) {
p.error(x);
@@ -2256,7 +2243,7 @@ public int read(byte[] b) throws IOException {
return new java.util.zip.GZIPInputStream(p.getIn());
}
- private static class OffsetPipeSecureFileCallable extends SecureFileCallable {
+ private static class OffsetPipeSecureFileCallable extends MasterToSlaveFileCallable {
private static final long serialVersionUID = 1L;
private Pipe p;
@@ -2271,7 +2258,7 @@ private OffsetPipeSecureFileCallable(Pipe p, long offset) {
public Void invoke(File f, VirtualChannel channel) throws IOException {
try (OutputStream os = p.getOut();
OutputStream out = new java.util.zip.GZIPOutputStream(os, 8192);
- RandomAccessFile raf = new RandomAccessFile(reading(f), "r")) {
+ RandomAccessFile raf = new RandomAccessFile(f, "r")) {
raf.seek(offset);
byte[] buf = new byte[8192];
int len;
@@ -2289,11 +2276,11 @@ public Void invoke(File f, VirtualChannel channel) throws IOException {
public String readToString() throws IOException, InterruptedException {
return act(new ReadToString());
}
- private static class ReadToString extends SecureFileCallable {
+ private static class ReadToString extends MasterToSlaveFileCallable {
private static final long serialVersionUID = 1L;
@Override
public String invoke(File f, VirtualChannel channel) throws IOException, InterruptedException {
- return new String(Files.readAllBytes(fileToPath(reading(f))));
+ return new String(Files.readAllBytes(fileToPath(f)), Charset.defaultCharset());
}
}
@@ -2313,18 +2300,18 @@ public OutputStream write() throws IOException, InterruptedException {
if(channel==null) {
File f = new File(remote).getAbsoluteFile();
mkdirs(f.getParentFile());
- return Files.newOutputStream(fileToPath(writing(f))); // TODO #writing seems unnecessary on a local file
+ return Files.newOutputStream(fileToPath(f));
}
return act(new WritePipe());
}
- private static class WritePipe extends SecureFileCallable {
+ private static class WritePipe extends MasterToSlaveFileCallable {
private static final long serialVersionUID = 1L;
@Override
public OutputStream invoke(File f, VirtualChannel channel) throws IOException, InterruptedException {
f = f.getAbsoluteFile();
mkdirs(f.getParentFile());
- return new RemoteOutputStream(Files.newOutputStream(fileToPath(writing(f))));
+ return new RemoteOutputStream(Files.newOutputStream(fileToPath(f)));
}
}
@@ -2338,7 +2325,7 @@ public OutputStream invoke(File f, VirtualChannel channel) throws IOException, I
public void write(final String content, final String encoding) throws IOException, InterruptedException {
act(new Write(encoding, content));
}
- private static class Write extends SecureFileCallable {
+ private static class Write extends MasterToSlaveFileCallable {
private static final long serialVersionUID = 1L;
private final String encoding;
private final String content;
@@ -2349,8 +2336,8 @@ private static class Write extends SecureFileCallable {
@Override
public Void invoke(File f, VirtualChannel channel) throws IOException {
mkdirs(f.getParentFile());
- try (OutputStream fos = Files.newOutputStream(fileToPath(writing(f)));
- Writer w = encoding != null ? new OutputStreamWriter(fos, encoding) : new OutputStreamWriter(fos)) {
+ try (OutputStream fos = Files.newOutputStream(fileToPath(f));
+ Writer w = encoding != null ? new OutputStreamWriter(fos, encoding) : new OutputStreamWriter(fos, Charset.defaultCharset())) {
w.write(content);
}
return null;
@@ -2364,11 +2351,11 @@ public Void invoke(File f, VirtualChannel channel) throws IOException {
public String digest() throws IOException, InterruptedException {
return act(new Digest());
}
- private static class Digest extends SecureFileCallable {
+ private static class Digest extends MasterToSlaveFileCallable {
private static final long serialVersionUID = 1L;
@Override
public String invoke(File f, VirtualChannel channel) throws IOException {
- return Util.getDigestOf(reading(f));
+ return Util.getDigestOf(f);
}
}
@@ -2382,7 +2369,7 @@ public void renameTo(final FilePath target) throws IOException, InterruptedExcep
}
act(new RenameTo(target));
}
- private static class RenameTo extends SecureFileCallable {
+ private static class RenameTo extends MasterToSlaveFileCallable {
private final FilePath target;
RenameTo(FilePath target) {
this.target = target;
@@ -2390,7 +2377,7 @@ private static class RenameTo extends SecureFileCallable {
private static final long serialVersionUID = 1L;
@Override
public Void invoke(File f, VirtualChannel channel) throws IOException {
- Files.move(fileToPath(deleting(reading(f))), fileToPath(writing(creating(new File(target.remote)))), LinkOption.NOFOLLOW_LINKS);
+ Files.move(fileToPath(f), fileToPath(new File(target.remote)), LinkOption.NOFOLLOW_LINKS);
return null;
}
}
@@ -2406,7 +2393,7 @@ public void moveAllChildrenTo(final FilePath target) throws IOException, Interru
}
act(new MoveAllChildrenTo(target));
}
- private static class MoveAllChildrenTo extends SecureFileCallable {
+ private static class MoveAllChildrenTo extends MasterToSlaveFileCallable {
private final FilePath target;
MoveAllChildrenTo(FilePath target) {
this.target = target;
@@ -2417,17 +2404,17 @@ public Void invoke(File f, VirtualChannel channel) throws IOException {
// JENKINS-16846: if f.getName() is the same as one of the files/directories in f,
// then the rename op will fail
File tmp = new File(f.getAbsolutePath()+".__rename");
- if (!deleting(f).renameTo(creating(tmp)))
+ if (!f.renameTo(tmp))
throw new IOException("Failed to rename "+f+" to "+tmp);
File t = new File(target.getRemote());
- for(File child : reading(tmp).listFiles()) {
+ for(File child : tmp.listFiles()) {
File target = new File(t, child.getName());
- if(!deleting(reading(child)).renameTo(writing(creating(target))))
+ if(!child.renameTo(target))
throw new IOException("Failed to rename "+child+" to "+target);
}
- Files.deleteIfExists(Util.fileToPath(deleting(tmp)));
+ Files.deleteIfExists(Util.fileToPath(tmp));
return null;
}
}
@@ -2461,7 +2448,7 @@ public void copyToWithPermission(FilePath target) throws IOException, Interrupte
target.chmod(mode());
target.setLastModifiedIfPossible(lastModified());
}
- private static class CopyToWithPermission extends SecureFileCallable {
+ private static class CopyToWithPermission extends MasterToSlaveFileCallable {
private final FilePath target;
CopyToWithPermission(FilePath target) {
this.target = target;
@@ -2470,9 +2457,8 @@ private static class CopyToWithPermission extends SecureFileCallable {
public Void invoke(File f, VirtualChannel channel) throws IOException {
File targetFile = new File(target.remote);
File targetDir = targetFile.getParentFile();
- filterNonNull().mkdirs(targetDir);
Files.createDirectories(fileToPath(targetDir));
- Files.copy(fileToPath(reading(f)), fileToPath(writing(targetFile)), StandardCopyOption.COPY_ATTRIBUTES, StandardCopyOption.REPLACE_EXISTING);
+ Files.copy(fileToPath(f), fileToPath(targetFile), StandardCopyOption.COPY_ATTRIBUTES, StandardCopyOption.REPLACE_EXISTING);
return null;
}
}
@@ -2489,7 +2475,7 @@ public void copyTo(OutputStream os) throws IOException, InterruptedException {
// this is needed because I/O operation is asynchronous
syncIO();
}
- private static class CopyTo extends SecureFileCallable {
+ private static class CopyTo extends MasterToSlaveFileCallable {
private static final long serialVersionUID = 4088559042349254141L;
private final OutputStream out;
CopyTo(OutputStream out) {
@@ -2497,7 +2483,7 @@ private static class CopyTo extends SecureFileCallable {
}
@Override
public Void invoke(File f, VirtualChannel channel) throws IOException {
- try (InputStream fis = Files.newInputStream(fileToPath(reading(f)))) {
+ try (InputStream fis = Files.newInputStream(fileToPath(f))) {
org.apache.commons.io.IOUtils.copy(fis, out);
return null;
} finally {
@@ -2667,7 +2653,7 @@ private IOException ioWithCause(ExecutionException e) {
;
}
- private static class CopyRecursiveLocal extends SecureFileCallable {
+ private static class CopyRecursiveLocal extends MasterToSlaveFileCallable {
private final FilePath target;
private final DirScanner scanner;
CopyRecursiveLocal(FilePath target, DirScanner scanner) {
@@ -2685,7 +2671,7 @@ public Integer invoke(File base, VirtualChannel channel) throws IOException {
}
final File dest = new File(target.remote);
final AtomicInteger count = new AtomicInteger();
- scanner.scan(base, reading(new FileVisitor() {
+ scanner.scan(base, new FileVisitor() {
private boolean exceptionEncountered;
private boolean logMessageShown;
@SuppressFBWarnings(value = "PATH_TRAVERSAL_IN", justification = "TODO needs triage")
@@ -2694,7 +2680,7 @@ public void visit(File f, String relativePath) throws IOException {
if (f.isFile()) {
File target = new File(dest, relativePath);
mkdirsE(target.getParentFile());
- Path targetPath = fileToPath(writing(target));
+ Path targetPath = fileToPath(target);
exceptionEncountered = exceptionEncountered || !tryCopyWithAttributes(f, targetPath);
if (exceptionEncountered) {
Files.copy(fileToPath(f), targetPath, StandardCopyOption.REPLACE_EXISTING);
@@ -2727,18 +2713,17 @@ public boolean understandsSymlink() {
public void visitSymlink(File link, String target, String relativePath) throws IOException {
try {
mkdirsE(new File(dest, relativePath).getParentFile());
- writing(new File(dest, target));
Util.createSymlink(dest, target, relativePath, TaskListener.NULL);
} catch (InterruptedException x) {
throw new IOException(x);
}
count.incrementAndGet();
}
- }));
+ });
return count.get();
}
}
- private static class ReadFromTar extends SecureFileCallable {
+ private static class ReadFromTar extends MasterToSlaveFileCallable {
private final Pipe pipe;
private final String description;
private final TarCompression compression;
@@ -2759,7 +2744,7 @@ public Void invoke(File f, VirtualChannel channel) throws IOException {
}
}
}
- private static class WriteToTar extends SecureFileCallable {
+ private static class WriteToTar extends MasterToSlaveFileCallable {
private final DirScanner scanner;
private final Pipe pipe;
private final TarCompression compression;
@@ -2771,10 +2756,10 @@ private static class WriteToTar extends SecureFileCallable {
private static final long serialVersionUID = 1L;
@Override
public Integer invoke(File f, VirtualChannel channel) throws IOException, InterruptedException {
- return writeToTar(reading(f), scanner, compression.compress(pipe.getOut()));
+ return writeToTar(f, scanner, compression.compress(pipe.getOut()));
}
}
- private static class CopyRecursiveRemoteToLocal extends SecureFileCallable {
+ private static class CopyRecursiveRemoteToLocal extends MasterToSlaveFileCallable {
private static final long serialVersionUID = 1L;
private final Pipe pipe;
private final DirScanner scanner;
@@ -2787,7 +2772,7 @@ private static class CopyRecursiveRemoteToLocal extends SecureFileCallable files) {
@NonNull
public static final LocalChannel localChannel = new LocalChannel(threadPoolForRemoting);
- private static @NonNull SoloFilePathFilter filterNonNull() {
- final SoloFilePathFilter filter = SoloFilePathFilter.wrap(FilePathFilter.current());
- return filter != null ? filter : UNRESTRICTED;
- }
-
- /**
- * Wraps {@link FileVisitor} to notify read access to {@link FilePathFilter}.
- */
- private static FileVisitor reading(final FileVisitor v) {
- final FilePathFilter filter = SoloFilePathFilter.wrap(FilePathFilter.current());
- if (filter==null) return v;
-
- return new FileVisitor() {
- @Override
- public void visit(File f, String relativePath) throws IOException {
- filter.read(f);
- v.visit(f,relativePath);
- }
-
- @Override
- public void visitSymlink(File link, String target, String relativePath) throws IOException {
- filter.read(link);
- v.visitSymlink(link, target, relativePath);
- }
-
- @Override
- public boolean understandsSymlink() {
- return v.understandsSymlink();
- }
- };
- }
-
/**
* Wraps {@link FileVisitor} to ignore symlinks.
*/
@@ -3501,76 +3454,8 @@ public boolean understandsSymlink() {
return v;
}
- /**
- * Pass through 'f' after ensuring that we can read that file.
- */
- private static File reading(File f) {
- filterNonNull().read(f);
- return f;
- }
-
- /**
- * Pass through 'f' after ensuring that we can access the file attributes.
- */
- private static File stating(File f) {
- filterNonNull().stat(f);
- return f;
- }
-
- /**
- * Pass through 'f' after ensuring that we can create that file/dir.
- */
- private static File creating(File f) {
- filterNonNull().create(f);
- return f;
- }
-
- /**
- * Pass through 'f' after ensuring that we can write to that file.
- */
- private static File writing(File f) {
- FilePathFilter filter = filterNonNull();
- if (!f.exists())
- filter.create(f);
- filter.write(f);
- return f;
- }
-
- /**
- * Pass through 'f' after ensuring that we can create that symlink.
- */
- private static File symlinking(File f) {
- FilePathFilter filter = filterNonNull();
- if (!f.exists())
- filter.create(f);
- filter.symlink(f);
- return f;
- }
-
- /**
- * Pass through 'f' after ensuring that we can delete that file.
- */
- private static File deleting(File f) {
- filterNonNull().delete(f);
- return f;
- }
-
- /**
- * Pass through 'f' after ensuring that we can mkdirs that directory.
- */
- private static File mkdirsing(File f) {
- filterNonNull().mkdirs(f);
- return f;
- }
-
private static boolean mkdirs(File dir) throws IOException {
if (dir.exists()) return false;
-
- File reference = dir;
- while (reference != null && !reference.exists()) {
- filterNonNull().mkdirs(reference);
- reference = reference.getParentFile();
- }
Files.createDirectories(fileToPath(dir));
return true;
}
@@ -3579,11 +3464,6 @@ private static File mkdirsE(File dir) throws IOException {
if (dir.exists()) {
return dir;
}
- File reference = dir;
- while (reference != null && !reference.exists()) {
- filterNonNull().mkdirs(reference);
- reference = reference.getParentFile();
- }
return IOUtils.mkdirs(dir);
}
@@ -3597,7 +3477,7 @@ public boolean isDescendant(@NonNull String potentialChildRelativePath) throws I
return act(new IsDescendant(potentialChildRelativePath));
}
- private static class IsDescendant extends SecureFileCallable {
+ private static class IsDescendant extends MasterToSlaveFileCallable {
private static final long serialVersionUID = 1L;
private String potentialChildRelativePath;
@@ -3611,7 +3491,7 @@ public Boolean invoke(@NonNull File parentFile, @NonNull VirtualChannel channel)
throw new IllegalArgumentException("Only a relative path is supported, the given path is absolute: " + potentialChildRelativePath);
}
- Path parentAbsolutePath = Util.fileToPath(stating(parentFile).getAbsoluteFile());
+ Path parentAbsolutePath = Util.fileToPath(parentFile.getAbsoluteFile());
Path parentRealPath;
try {
if (Functions.isWindows()) {
@@ -3716,8 +3596,6 @@ private static Path getRealPath(Path path) throws IOException {
return path.toRealPath(LinkOption.NOFOLLOW_LINKS);
}
- private static final SoloFilePathFilter UNRESTRICTED = SoloFilePathFilter.wrap(FilePathFilter.UNRESTRICTED);
-
private static class SymlinkDiscardingFileFilter implements FileFilter, Serializable {
private final String verificationRoot;
@@ -3734,5 +3612,4 @@ public boolean accept(File file) {
}
private static final long serialVersionUID = 1L;
}
-
}
diff --git a/core/src/main/java/hudson/PluginManager.java b/core/src/main/java/hudson/PluginManager.java
index 3dbb793b19c9..99313e18c703 100644
--- a/core/src/main/java/hudson/PluginManager.java
+++ b/core/src/main/java/hudson/PluginManager.java
@@ -110,6 +110,7 @@
import java.util.stream.Collectors;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
+import javax.xml.XMLConstants;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParserFactory;
import jenkins.ClassLoaderReflectionToolkit;
@@ -355,7 +356,7 @@ protected PluginManager(ServletContext context, File rootDir) {
this.rootDir = rootDir;
try {
- Files.createDirectories(rootDir.toPath());
+ Util.createDirectories(rootDir.toPath());
} catch (IOException e) {
throw new UncheckedIOException(e);
}
@@ -2108,7 +2109,10 @@ public HttpResponse doInstallNecessaryPlugins(StaplerRequest req) throws IOExcep
public Map parseRequestedPlugins(InputStream configXml) throws IOException {
final Map requestedPlugins = new TreeMap<>();
try {
- SAXParserFactory.newInstance().newSAXParser().parse(configXml, new DefaultHandler() {
+ SAXParserFactory spf = SAXParserFactory.newInstance();
+ spf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
+ spf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
+ spf.newSAXParser().parse(configXml, new DefaultHandler() {
@Override public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
String plugin = attributes.getValue("plugin");
if (plugin == null) {
diff --git a/core/src/main/java/hudson/PluginWrapper.java b/core/src/main/java/hudson/PluginWrapper.java
index d2ed43ec2e28..0e417f3d6780 100644
--- a/core/src/main/java/hudson/PluginWrapper.java
+++ b/core/src/main/java/hudson/PluginWrapper.java
@@ -972,7 +972,7 @@ public boolean hasLicensesXml() {
if (dependency == null) {
PluginWrapper failedDependency = NOTICE.getPlugin(d.shortName);
if (failedDependency != null) {
- dependencyErrors.put(Messages.PluginWrapper_failed_to_load_dependency(failedDependency.getLongName(), failedDependency.getVersion()), true);
+ dependencyErrors.put(Messages.PluginWrapper_failed_to_load_dependency_2(failedDependency.getLongName(), failedDependency.getShortName(), failedDependency.getVersion()), true);
break;
} else {
dependencyErrors.put(Messages.PluginWrapper_missing(d.shortName, d.version), false);
@@ -980,13 +980,13 @@ public boolean hasLicensesXml() {
} else {
if (dependency.isActive()) {
if (isDependencyObsolete(d, dependency)) {
- versionDependencyError(Messages.PluginWrapper_obsolete(dependency.getLongName(), dependency.getVersion(), d.version), dependency.getVersion(), d.version);
+ versionDependencyError(Messages.PluginWrapper_obsolete_2(dependency.getLongName(), dependency.getShortName(), dependency.getVersion(), d.version), dependency.getVersion(), d.version);
}
} else {
if (isDependencyObsolete(d, dependency)) {
- versionDependencyError(Messages.PluginWrapper_disabledAndObsolete(dependency.getLongName(), dependency.getVersion(), d.version), dependency.getVersion(), d.version);
+ versionDependencyError(Messages.PluginWrapper_obsolete_2(dependency.getLongName(), dependency.getShortName(), dependency.getVersion(), d.version), dependency.getVersion(), d.version);
} else {
- dependencyErrors.put(Messages.PluginWrapper_disabled(dependency.getLongName()), false);
+ dependencyErrors.put(Messages.PluginWrapper_disabled_2(dependency.getLongName(), dependency.getShortName()), false);
}
}
@@ -997,7 +997,7 @@ public boolean hasLicensesXml() {
PluginWrapper dependency = parent.getPlugin(d.shortName);
if (dependency != null && dependency.isActive()) {
if (isDependencyObsolete(d, dependency)) {
- versionDependencyError(Messages.PluginWrapper_obsolete(dependency.getLongName(), dependency.getVersion(), d.version), dependency.getVersion(), d.version);
+ versionDependencyError(Messages.PluginWrapper_obsolete_2(dependency.getLongName(), dependency.getShortName(), dependency.getVersion(), d.version), dependency.getVersion(), d.version);
} else {
dependencies.add(d);
}
@@ -1006,7 +1006,7 @@ public boolean hasLicensesXml() {
if (!dependencyErrors.isEmpty()) {
NOTICE.addPlugin(this);
StringBuilder messageBuilder = new StringBuilder();
- messageBuilder.append(Messages.PluginWrapper_failed_to_load_plugin(getLongName(), getVersion())).append(System.lineSeparator());
+ messageBuilder.append(Messages.PluginWrapper_failed_to_load_plugin_2(getLongName(), getShortName(), getVersion())).append(System.lineSeparator());
for (Iterator iterator = dependencyErrors.keySet().iterator(); iterator.hasNext(); ) {
String dependencyError = iterator.next();
messageBuilder.append(" - ").append(dependencyError);
@@ -1029,7 +1029,7 @@ private boolean isDependencyObsolete(Dependency d, PluginWrapper dependency) {
*/
private void versionDependencyError(String message, String actual, String minimum) {
if (isSnapshot(actual) || isSnapshot(minimum)) {
- LOGGER.log(WARNING, "Suppressing dependency error in {0} v{1}: {2}", new Object[] {getLongName(), getVersion(), message});
+ LOGGER.log(WARNING, "Suppressing dependency error in {0} v{1}: {2}", new Object[] {getShortName(), getVersion(), message});
} else {
dependencyErrors.put(message, false);
}
@@ -1411,6 +1411,24 @@ public List getDeprecations() {
return deprecations;
}
+ @Restricted(NoExternalUse.class)
+ public String getIssueTrackerReportUrl() {
+ final UpdateCenter updateCenter = Jenkins.get().getUpdateCenter();
+ if (updateCenter.isSiteDataReady()) {
+ for (UpdateSite site : updateCenter.getSites()) {
+ final UpdateSite.Plugin sitePlugin = site.getPlugin(this.shortName);
+ if (sitePlugin != null && sitePlugin.issueTrackers != null) {
+ for (UpdateSite.IssueTracker issueTracker : sitePlugin.issueTrackers) {
+ if (issueTracker.reportUrl != null) {
+ return issueTracker.reportUrl;
+ }
+ }
+ }
+ }
+ }
+ return null;
+ }
+
private static final Logger LOGGER = Logger.getLogger(PluginWrapper.class.getName());
/**
diff --git a/core/src/main/java/hudson/Proc.java b/core/src/main/java/hudson/Proc.java
index dd94d2097c39..24cd16552295 100644
--- a/core/src/main/java/hudson/Proc.java
+++ b/core/src/main/java/hudson/Proc.java
@@ -39,6 +39,7 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.nio.charset.Charset;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.CancellationException;
@@ -354,7 +355,7 @@ public int join() throws InterruptedException, IOException {
// } catch (IOException x) {
// LOGGER.log(Level.FINE,"stderr termination failed",x);
// }
- out.write(msg.getBytes());
+ out.write(msg.getBytes(Charset.defaultCharset()));
out.write('\n');
}
return r;
diff --git a/core/src/main/java/hudson/Util.java b/core/src/main/java/hudson/Util.java
index 32d114efb20f..df22f9faab86 100644
--- a/core/src/main/java/hudson/Util.java
+++ b/core/src/main/java/hudson/Util.java
@@ -67,6 +67,7 @@
import java.nio.file.StandardCopyOption;
import java.nio.file.attribute.BasicFileAttributes;
import java.nio.file.attribute.DosFileAttributes;
+import java.nio.file.attribute.FileAttribute;
import java.nio.file.attribute.PosixFilePermission;
import java.nio.file.attribute.PosixFilePermissions;
import java.security.DigestInputStream;
@@ -1727,6 +1728,63 @@ public static Set modeToPermissions(int mode) throws IOExce
}
}
+ /**
+ * Create a directory by creating all nonexistent parent directories first.
+ *
+ *
Unlike {@link Files#createDirectory}, an exception is not thrown
+ * if the directory could not be created because it already exists.
+ * Unlike {@link Files#createDirectories}, an exception is not thrown
+ * if the directory (or one of its parents) is a symbolic link.
+ *
+ *
The {@code attrs} parameter contains optional {@link FileAttribute file attributes}
+ * to set atomically when creating the nonexistent directories.
+ * Each file attribute is identified by its {@link FileAttribute#name}.
+ * If more than one attribute of the same name is included in the array,
+ * then all but the last occurrence is ignored.
+ *
+ *
If this method fails,
+ * then it may do so after creating some, but not all, of the parent directories.
+ *
+ * @param dir The directory to create.
+ * @param attrs An optional list of file attributes to set atomically
+ * when creating the directory.
+ * @return The directory.
+ * @throws UnsupportedOperationException If the array contains an attribute
+ * that cannot be set atomically when creating the directory.
+ * @throws FileAlreadyExistsException If {@code dir} exists but is not a directory.
+ * @throws IOException If an I/O error occurs.
+ * @see Files#createDirectories(Path, FileAttribute[])
+ */
+ @Restricted(NoExternalUse.class)
+ public static Path createDirectories(@NonNull Path dir, FileAttribute>... attrs) throws IOException {
+ dir = dir.toAbsolutePath();
+
+ Path parent;
+ for (parent = dir.getParent(); parent != null; parent = parent.getParent()) {
+ if (Files.exists(parent)) {
+ break;
+ }
+ }
+
+ if (parent == null) {
+ if (Files.isDirectory(dir)) {
+ return dir;
+ } else {
+ return Files.createDirectory(dir, attrs);
+ }
+ }
+
+ Path child = parent;
+ for (Path name : parent.relativize(dir)) {
+ child = child.resolve(name);
+ if (!Files.isDirectory(child)) {
+ Files.createDirectory(child, attrs);
+ }
+ }
+
+ return dir;
+ }
+
/**
* Compute the number of calendar days elapsed since the given date.
* As it's only the calendar days difference that matter, "11.00pm" to "2.00am the day after" returns 1,
diff --git a/core/src/main/java/hudson/WebAppMain.java b/core/src/main/java/hudson/WebAppMain.java
index f41de96eac8d..b0596a66374d 100644
--- a/core/src/main/java/hudson/WebAppMain.java
+++ b/core/src/main/java/hudson/WebAppMain.java
@@ -50,6 +50,7 @@
import java.io.OutputStream;
import java.net.URL;
import java.net.URLClassLoader;
+import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.InvalidPathException;
import java.nio.file.StandardOpenOption;
@@ -71,8 +72,6 @@
import javax.servlet.ServletContextListener;
import javax.servlet.ServletResponse;
import javax.servlet.SessionTrackingMode;
-import javax.xml.transform.TransformerFactory;
-import javax.xml.transform.TransformerFactoryConfigurationError;
import jenkins.model.Jenkins;
import jenkins.util.JenkinsJVM;
import jenkins.util.SystemProperties;
@@ -190,7 +189,7 @@ public Locale get() {
final FileAndDescription describedHomeDir = getHomeDir(event);
home = describedHomeDir.file.getAbsoluteFile();
try {
- Files.createDirectories(home.toPath());
+ Util.createDirectories(home.toPath());
} catch (IOException | InvalidPathException e) {
throw (NoHomeDir)new NoHomeDir(home).initCause(e);
}
@@ -235,23 +234,6 @@ public Locale get() {
throw new NoTempDir(e);
}
- // Tomcat breaks XSLT with JDK 5.0 and onward. Check if that's the case, and if so,
- // try to correct it
- try {
- TransformerFactory.newInstance();
- // if this works we are all happy
- } catch (TransformerFactoryConfigurationError x) {
- // no it didn't.
- LOGGER.log(WARNING, "XSLT not configured correctly. Hudson will try to fix this. See https://bz.apache.org/bugzilla/show_bug.cgi?id=40895 for more details",x);
- System.setProperty(TransformerFactory.class.getName(),"com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl");
- try {
- TransformerFactory.newInstance();
- LOGGER.info("XSLT is set to the JAXP RI in JRE");
- } catch(TransformerFactoryConfigurationError y) {
- LOGGER.log(SEVERE, "Failed to correct the problem.");
- }
- }
-
installExpressionFactory(event);
context.setAttribute(APP,new HudsonIsLoading());
@@ -312,7 +294,7 @@ public void joinInit() throws InterruptedException {
*/
private void recordBootAttempt(File home) {
try (OutputStream o=Files.newOutputStream(BootFailure.getBootFailureFile(home).toPath(), StandardOpenOption.CREATE, StandardOpenOption.APPEND)) {
- o.write((new Date() + System.getProperty("line.separator", "\n")).getBytes());
+ o.write((new Date() + System.getProperty("line.separator", "\n")).getBytes(Charset.defaultCharset()));
} catch (IOException | InvalidPathException e) {
LOGGER.log(WARNING, "Failed to record boot attempts",e);
}
diff --git a/core/src/main/java/hudson/XmlFile.java b/core/src/main/java/hudson/XmlFile.java
index 9a36d4484d20..c9f0478f0bb4 100644
--- a/core/src/main/java/hudson/XmlFile.java
+++ b/core/src/main/java/hudson/XmlFile.java
@@ -52,6 +52,7 @@
import java.util.function.Supplier;
import java.util.logging.Level;
import java.util.logging.Logger;
+import javax.xml.XMLConstants;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParserFactory;
import org.apache.commons.io.IOUtils;
@@ -251,7 +252,7 @@ public void delete() throws IOException {
}
public void mkdirs() throws IOException {
- Files.createDirectories(Util.fileToPath(file.getParentFile()));
+ Util.createDirectories(Util.fileToPath(file.getParentFile()));
}
@Override
@@ -320,7 +321,11 @@ class Eureka extends SAXException {
try (InputStream in = Files.newInputStream(file.toPath())) {
InputSource input = new InputSource(file.toURI().toASCIIString());
input.setByteStream(in);
- JAXP.newSAXParser().parse(input,new DefaultHandler() {
+ SAXParserFactory spf = SAXParserFactory.newInstance();
+ spf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
+ spf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
+ spf.setNamespaceAware(true);
+ spf.newSAXParser().parse(input, new DefaultHandler() {
private Locator loc;
@Override
public void setDocumentLocator(Locator locator) {
@@ -372,13 +377,7 @@ private void attempt() throws Eureka {
private static final Logger LOGGER = Logger.getLogger(XmlFile.class.getName());
- private static final SAXParserFactory JAXP = SAXParserFactory.newInstance();
-
private static final HierarchicalStreamDriver DEFAULT_DRIVER = XStream2.getDefaultDriver();
private static final XStream DEFAULT_XSTREAM = new XStream2(DEFAULT_DRIVER);
-
- static {
- JAXP.setNamespaceAware(true);
- }
}
diff --git a/core/src/main/java/hudson/cli/CLICommand.java b/core/src/main/java/hudson/cli/CLICommand.java
index 198736dc9fca..d27d0b6beff5 100644
--- a/core/src/main/java/hudson/cli/CLICommand.java
+++ b/core/src/main/java/hudson/cli/CLICommand.java
@@ -40,6 +40,7 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
+import java.io.UnsupportedEncodingException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Type;
import java.nio.charset.Charset;
@@ -404,7 +405,11 @@ protected void printUsage(PrintStream stderr, CmdLineParser p) {
public final String getSingleLineSummary() {
ByteArrayOutputStream out = new ByteArrayOutputStream();
getCmdLineParser().printSingleLineUsage(out);
- return out.toString();
+ try {
+ return out.toString(getClientCharset().name());
+ } catch (UnsupportedEncodingException e) {
+ throw new AssertionError(e);
+ }
}
/**
@@ -414,7 +419,11 @@ public final String getSingleLineSummary() {
public final String getUsage() {
ByteArrayOutputStream out = new ByteArrayOutputStream();
getCmdLineParser().printUsage(out);
- return out.toString();
+ try {
+ return out.toString(getClientCharset().name());
+ } catch (UnsupportedEncodingException e) {
+ throw new AssertionError(e);
+ }
}
/**
@@ -427,7 +436,11 @@ public final String getLongDescription() {
printUsageSummary(ps);
ps.close();
- return out.toString();
+ try {
+ return out.toString(getClientCharset().name());
+ } catch (UnsupportedEncodingException e) {
+ throw new AssertionError(e);
+ }
}
/**
@@ -457,7 +470,7 @@ public void setClientCharset(@NonNull Charset encoding) {
this.encoding = encoding;
}
- protected @NonNull Charset getClientCharset() throws IOException, InterruptedException {
+ protected @NonNull Charset getClientCharset() {
if (encoding != null) {
return encoding;
}
diff --git a/core/src/main/java/hudson/console/AnnotatedLargeText.java b/core/src/main/java/hudson/console/AnnotatedLargeText.java
index af70227ad1a5..dafff632d305 100644
--- a/core/src/main/java/hudson/console/AnnotatedLargeText.java
+++ b/core/src/main/java/hudson/console/AnnotatedLargeText.java
@@ -186,7 +186,7 @@ public long writeHtmlTo(long start, Writer w) throws IOException {
oos.close();
StaplerResponse rsp = Stapler.getCurrentResponse();
if (rsp!=null)
- rsp.setHeader("X-ConsoleAnnotator", new String(Base64.getEncoder().encode(baos.toByteArray())));
+ rsp.setHeader("X-ConsoleAnnotator", Base64.getEncoder().encodeToString(baos.toByteArray()));
return r;
}
diff --git a/core/src/main/java/hudson/console/ConsoleAnnotationOutputStream.java b/core/src/main/java/hudson/console/ConsoleAnnotationOutputStream.java
index 00a84b6f4612..6e70a743d824 100644
--- a/core/src/main/java/hudson/console/ConsoleAnnotationOutputStream.java
+++ b/core/src/main/java/hudson/console/ConsoleAnnotationOutputStream.java
@@ -121,7 +121,7 @@ public ConsoleAnnotator annotate(T context, MarkupText text) {
}
} catch (IOException | ClassNotFoundException e) {
// if we failed to resurrect an annotation, ignore it.
- LOGGER.log(Level.FINE, "Failed to resurrect annotation from \"" + StringEscapeUtils.escapeJava(new String(in, next, rest)) + "\"", e);
+ LOGGER.log(Level.FINE, "Failed to resurrect annotation from \"" + StringEscapeUtils.escapeJava(new String(in, next, rest, Charset.defaultCharset())) + "\"", e);
}
int bytesUsed = rest - b.available(); // bytes consumed by annotations
diff --git a/core/src/main/java/hudson/console/ConsoleNote.java b/core/src/main/java/hudson/console/ConsoleNote.java
index 443625350cb2..62a9349c02c0 100644
--- a/core/src/main/java/hudson/console/ConsoleNote.java
+++ b/core/src/main/java/hudson/console/ConsoleNote.java
@@ -43,6 +43,7 @@
import java.io.OutputStream;
import java.io.Serializable;
import java.io.Writer;
+import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Base64;
@@ -325,11 +326,13 @@ public static void skip(DataInputStream in) throws IOException {
* Preamble of the encoded form. ANSI escape sequence to stop echo back
* plus a few magic characters.
*/
- public static final byte[] PREAMBLE = PREAMBLE_STR.getBytes();
+ @SuppressFBWarnings(value = "MS_PKGPROTECT", justification = "used in several plugins")
+ public static final byte[] PREAMBLE = PREAMBLE_STR.getBytes(StandardCharsets.UTF_8);
/**
* Post amble is the ANSI escape sequence that brings back the echo.
*/
- public static final byte[] POSTAMBLE = POSTAMBLE_STR.getBytes();
+ @SuppressFBWarnings(value = "MS_PKGPROTECT", justification = "used in several plugins")
+ public static final byte[] POSTAMBLE = POSTAMBLE_STR.getBytes(StandardCharsets.UTF_8);
/**
* Locates the preamble in the given buffer.
diff --git a/core/src/main/java/hudson/init/impl/InitialUserContent.java b/core/src/main/java/hudson/init/impl/InitialUserContent.java
index e057ba16a3f7..810bcdcce7d4 100644
--- a/core/src/main/java/hudson/init/impl/InitialUserContent.java
+++ b/core/src/main/java/hudson/init/impl/InitialUserContent.java
@@ -43,7 +43,7 @@ public class InitialUserContent {
public static void init(Jenkins h) throws IOException {
File userContentDir = new File(h.getRootDir(), "userContent");
if (!Files.isDirectory(Util.fileToPath(userContentDir))) {
- Files.createDirectories(Util.fileToPath(userContentDir));
+ Util.createDirectories(Util.fileToPath(userContentDir));
FileUtils.writeStringToFile(new File(userContentDir,"readme.txt"), Messages.Hudson_USER_CONTENT_README() + "\n");
}
}
diff --git a/core/src/main/java/hudson/model/AbstractBuild.java b/core/src/main/java/hudson/model/AbstractBuild.java
index 0a7f1cb68323..add476c3cea7 100644
--- a/core/src/main/java/hudson/model/AbstractBuild.java
+++ b/core/src/main/java/hudson/model/AbstractBuild.java
@@ -65,6 +65,7 @@
import java.io.IOException;
import java.io.InterruptedIOException;
import java.lang.ref.WeakReference;
+import java.nio.channels.ClosedByInterruptException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
@@ -668,7 +669,7 @@ public void defaultCheckout() throws IOException, InterruptedException {
}
} catch (AbortException e) {
listener.error(e.getMessage());
- } catch (InterruptedIOException e) {
+ } catch (ClosedByInterruptException | InterruptedIOException e) {
throw (InterruptedException)new InterruptedException().initCause(e);
} catch (IOException e) {
// checkout error not yet reported
diff --git a/core/src/main/java/hudson/model/AbstractItem.java b/core/src/main/java/hudson/model/AbstractItem.java
index e16836c8a807..58a1d90bc5cb 100644
--- a/core/src/main/java/hudson/model/AbstractItem.java
+++ b/core/src/main/java/hudson/model/AbstractItem.java
@@ -353,6 +353,7 @@ protected void checkRename(@NonNull String newName) throws Failure {
* Not all the Items need to support this operation, but if you decide to do so,
* you can use this method.
*/
+ @SuppressFBWarnings(value = "SWL_SLEEP_WITH_LOCK_HELD", justification = "no big deal")
protected void renameTo(final String newName) throws IOException {
if (!isNameEditable()) {
diff --git a/core/src/main/java/hudson/model/AutoCompletionCandidates.java b/core/src/main/java/hudson/model/AutoCompletionCandidates.java
index 824915477b97..19fff10cca29 100644
--- a/core/src/main/java/hudson/model/AutoCompletionCandidates.java
+++ b/core/src/main/java/hudson/model/AutoCompletionCandidates.java
@@ -25,6 +25,7 @@
package hudson.model;
import edu.umd.cs.findbugs.annotations.CheckForNull;
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import hudson.search.Search;
import hudson.search.UserSearchProperty;
import java.io.IOException;
@@ -108,6 +109,7 @@ public static AutoCompletionCandidates ofJobNames(final Class AutoCompletionCandidates ofJobNames(final Class type, final String value, ItemGroup container) {
final AutoCompletionCandidates candidates = new AutoCompletionCandidates();
class Visitor extends ItemVisitor {
diff --git a/core/src/main/java/hudson/model/BooleanParameterDefinition.java b/core/src/main/java/hudson/model/BooleanParameterDefinition.java
index 99e0ced4bb6a..2c1cd6279110 100644
--- a/core/src/main/java/hudson/model/BooleanParameterDefinition.java
+++ b/core/src/main/java/hudson/model/BooleanParameterDefinition.java
@@ -23,6 +23,8 @@
*/
package hudson.model;
+import edu.umd.cs.findbugs.annotations.CheckForNull;
+import edu.umd.cs.findbugs.annotations.NonNull;
import hudson.Extension;
import java.util.Objects;
import net.sf.json.JSONObject;
@@ -43,11 +45,11 @@ public class BooleanParameterDefinition extends SimpleParameterDefinition {
* @since 2.281
*/
@DataBoundConstructor
- public BooleanParameterDefinition(String name) {
+ public BooleanParameterDefinition(@NonNull String name) {
super(name);
}
- public BooleanParameterDefinition(String name, boolean defaultValue, String description) {
+ public BooleanParameterDefinition(@NonNull String name, boolean defaultValue, @CheckForNull String description) {
this(name);
setDefaultValue(defaultValue);
setDescription(description);
diff --git a/core/src/main/java/hudson/model/Cause.java b/core/src/main/java/hudson/model/Cause.java
index 256fbb24eb3c..b58129f19ae1 100644
--- a/core/src/main/java/hudson/model/Cause.java
+++ b/core/src/main/java/hudson/model/Cause.java
@@ -26,6 +26,7 @@
import com.thoughtworks.xstream.converters.UnmarshallingContext;
import edu.umd.cs.findbugs.annotations.CheckForNull;
import edu.umd.cs.findbugs.annotations.NonNull;
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import hudson.Util;
import hudson.console.ModelHyperlinkNote;
import hudson.diagnosis.OldDataMonitor;
@@ -129,6 +130,7 @@ public void print(TaskListener listener) {
*/
@Deprecated
public static class LegacyCodeCause extends Cause {
+ @SuppressFBWarnings(value = "URF_UNREAD_FIELD", justification = "for backward compatibility")
private StackTraceElement [] stackTrace;
public LegacyCodeCause() {
stackTrace = new Exception().getStackTrace();
diff --git a/core/src/main/java/hudson/model/ChoiceParameterDefinition.java b/core/src/main/java/hudson/model/ChoiceParameterDefinition.java
index 286d12505a76..c83d27de63b1 100644
--- a/core/src/main/java/hudson/model/ChoiceParameterDefinition.java
+++ b/core/src/main/java/hudson/model/ChoiceParameterDefinition.java
@@ -36,26 +36,26 @@ public class ChoiceParameterDefinition extends SimpleParameterDefinition {
private /* quasi-final */ List choices;
private final String defaultValue;
- public static boolean areValidChoices(String choices) {
+ public static boolean areValidChoices(@NonNull String choices) {
String strippedChoices = choices.trim();
return !StringUtils.isEmpty(strippedChoices) && strippedChoices.split(CHOICES_DELIMITER).length > 0;
}
- public ChoiceParameterDefinition(@NonNull String name, @NonNull String choices, String description) {
+ public ChoiceParameterDefinition(@NonNull String name, @NonNull String choices, @CheckForNull String description) {
super(name, description);
setChoicesText(choices);
defaultValue = null;
}
- public ChoiceParameterDefinition(@NonNull String name, @NonNull String[] choices, String description) {
+ public ChoiceParameterDefinition(@NonNull String name, @NonNull String[] choices, @CheckForNull String description) {
super(name, description);
this.choices = Stream.of(choices).map(Util::fixNull).collect(Collectors.toCollection(ArrayList::new));
defaultValue = null;
}
- private ChoiceParameterDefinition(@NonNull String name, @NonNull List choices, String defaultValue, String description) {
+ private ChoiceParameterDefinition(@NonNull String name, @NonNull List choices, String defaultValue, @CheckForNull String description) {
super(name, description);
- this.choices = choices;
+ this.choices = Util.fixNull(choices);
this.defaultValue = defaultValue;
}
@@ -110,7 +110,7 @@ public void setChoices(Object choices) {
throw new IllegalArgumentException("expected String or List, but got " + choices.getClass().getName());
}
- private void setChoicesText(String choices) {
+ private void setChoicesText(@NonNull String choices) {
this.choices = Arrays.asList(choices.split(CHOICES_DELIMITER));
}
@@ -124,6 +124,7 @@ public ParameterDefinition copyWithDefaultValue(ParameterValue defaultValue) {
}
}
+ @NonNull
@Exported
public List getChoices() {
return choices;
diff --git a/core/src/main/java/hudson/model/Computer.java b/core/src/main/java/hudson/model/Computer.java
index a7ac71edc807..8c1ce41e1aae 100644
--- a/core/src/main/java/hudson/model/Computer.java
+++ b/core/src/main/java/hudson/model/Computer.java
@@ -1678,7 +1678,7 @@ public static void relocateOldLogs() {
if (m.matches()) {
File newLocation = new File(dir, "logs/slaves/" + m.group(1) + "/slave.log" + Util.fixNull(m.group(2)));
try {
- Files.createDirectories(newLocation.getParentFile().toPath());
+ Util.createDirectories(newLocation.getParentFile().toPath());
Files.move(f.toPath(), newLocation.toPath(), StandardCopyOption.REPLACE_EXISTING);
LOGGER.log(Level.INFO, "Relocated log file {0} to {1}",new Object[] {f.getPath(),newLocation.getPath()});
} catch (IOException | InvalidPathException e) {
diff --git a/core/src/main/java/hudson/model/DirectoryBrowserSupport.java b/core/src/main/java/hudson/model/DirectoryBrowserSupport.java
index 5ed6f2260905..cc4c13f14dff 100644
--- a/core/src/main/java/hudson/model/DirectoryBrowserSupport.java
+++ b/core/src/main/java/hudson/model/DirectoryBrowserSupport.java
@@ -750,6 +750,7 @@ private static final class BuildChildPaths extends MasterToSlaveCallable> buildChildPaths(VirtualFile cur, Locale locale) throws IOException {
List> r = new ArrayList<>();
diff --git a/core/src/main/java/hudson/model/FileParameterDefinition.java b/core/src/main/java/hudson/model/FileParameterDefinition.java
index 84e29f67b1b3..2d40c2d2fcab 100644
--- a/core/src/main/java/hudson/model/FileParameterDefinition.java
+++ b/core/src/main/java/hudson/model/FileParameterDefinition.java
@@ -23,6 +23,8 @@
*/
package hudson.model;
+import edu.umd.cs.findbugs.annotations.CheckForNull;
+import edu.umd.cs.findbugs.annotations.NonNull;
import hudson.Extension;
import hudson.cli.CLICommand;
import java.io.File;
@@ -50,11 +52,11 @@ public class FileParameterDefinition extends ParameterDefinition {
* @since 2.281
*/
@DataBoundConstructor
- public FileParameterDefinition(String name) {
+ public FileParameterDefinition(@NonNull String name) {
super(name);
}
- public FileParameterDefinition(String name, String description) {
+ public FileParameterDefinition(@NonNull String name, @CheckForNull String description) {
this(name);
setDescription(description);
}
diff --git a/core/src/main/java/hudson/model/FileParameterValue.java b/core/src/main/java/hudson/model/FileParameterValue.java
index b3e5c146cd9d..cc9f39faefee 100644
--- a/core/src/main/java/hudson/model/FileParameterValue.java
+++ b/core/src/main/java/hudson/model/FileParameterValue.java
@@ -36,6 +36,7 @@
import java.io.OutputStream;
import java.io.UncheckedIOException;
import java.io.UnsupportedEncodingException;
+import java.nio.charset.Charset;
import java.nio.file.Files;
import java.util.regex.Pattern;
import jenkins.util.SystemProperties;
@@ -302,7 +303,7 @@ public String getString(String encoding) throws UnsupportedEncodingException {
@Override
public String getString() {
- return new String(get());
+ return new String(get(), Charset.defaultCharset());
}
@Override
diff --git a/core/src/main/java/hudson/model/Fingerprint.java b/core/src/main/java/hudson/model/Fingerprint.java
index 3d49b1977b1f..19c8a17feced 100644
--- a/core/src/main/java/hudson/model/Fingerprint.java
+++ b/core/src/main/java/hudson/model/Fingerprint.java
@@ -33,6 +33,7 @@
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
import edu.umd.cs.findbugs.annotations.CheckForNull;
import edu.umd.cs.findbugs.annotations.NonNull;
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import hudson.BulkChange;
import hudson.Extension;
import hudson.ExtensionList;
@@ -961,7 +962,8 @@ public RangeSet getRangeSet(Job job) {
/**
* Gets the sorted list of job names where this jar is used.
*/
- public @NonNull List getJobs() {
+ @NonNull
+ public synchronized List getJobs() {
List r = new ArrayList<>(usages.keySet());
Collections.sort(r);
return r;
@@ -1024,6 +1026,7 @@ public synchronized void add(@NonNull String jobFullName, int n) throws IOExcept
}
// JENKINS-49588
+ @SuppressFBWarnings(value = "IS2_INCONSISTENT_SYNC", justification = "nothing should be competing with XStream during deserialization")
protected Object readResolve() {
if (usages == null) {
usages = new Hashtable<>();
diff --git a/core/src/main/java/hudson/model/FreeStyleProject.java b/core/src/main/java/hudson/model/FreeStyleProject.java
index 9992ffcc80c2..73234b815683 100644
--- a/core/src/main/java/hudson/model/FreeStyleProject.java
+++ b/core/src/main/java/hudson/model/FreeStyleProject.java
@@ -23,6 +23,7 @@
*/
package hudson.model;
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import hudson.Extension;
import jenkins.model.Jenkins;
import jenkins.model.item_category.StandaloneProjectsCategory;
@@ -70,10 +71,12 @@ public DescriptorImpl getDescriptor() {
*/
@Deprecated
@Restricted(NoExternalUse.class)
+ @SuppressFBWarnings(value = "MS_PKGPROTECT", justification = "for backward compatibility")
public static /*almost final*/ DescriptorImpl DESCRIPTOR;
@Extension(ordinal=1000) @Symbol({"freeStyle","freeStyleJob"})
public static class DescriptorImpl extends AbstractProjectDescriptor {
+ @SuppressFBWarnings(value = "ST_WRITE_TO_STATIC_FROM_INSTANCE_METHOD", justification = "for backward compatibility")
public DescriptorImpl() {
DESCRIPTOR = this;
}
diff --git a/core/src/main/java/hudson/model/ItemGroupMixIn.java b/core/src/main/java/hudson/model/ItemGroupMixIn.java
index b1d52d9bfeee..54798d497868 100644
--- a/core/src/main/java/hudson/model/ItemGroupMixIn.java
+++ b/core/src/main/java/hudson/model/ItemGroupMixIn.java
@@ -99,7 +99,7 @@ protected ItemGroupMixIn(ItemGroup parent, AccessControlled acl) {
*/
public static Map loadChildren(ItemGroup parent, File modulesDir, Function1 extends K,? super V> key) {
try {
- Files.createDirectories(modulesDir.toPath());
+ Util.createDirectories(modulesDir.toPath());
} catch (IOException e) {
throw new UncheckedIOException(e);
}
@@ -273,7 +273,7 @@ public synchronized TopLevelItem createProjectFromXML(String name, InputStream x
final File dir = configXml.getParentFile();
boolean success = false;
try {
- Files.createDirectories(dir.toPath());
+ Util.createDirectories(dir.toPath());
XMLUtils.safeTransform(new StreamSource(xml), new StreamResult(configXml));
// load it
diff --git a/core/src/main/java/hudson/model/Job.java b/core/src/main/java/hudson/model/Job.java
index dc1ac34a4282..1e0d90c8ca1e 100644
--- a/core/src/main/java/hudson/model/Job.java
+++ b/core/src/main/java/hudson/model/Job.java
@@ -664,7 +664,7 @@ public void renameTo(String newName) throws IOException {
super.renameTo(newName);
File newBuildDir = getBuildDir();
if (Files.isDirectory(Util.fileToPath(oldBuildDir)) && !Files.isDirectory(Util.fileToPath(newBuildDir))) {
- Files.createDirectories(Util.fileToPath(newBuildDir.getParentFile()));
+ Util.createDirectories(Util.fileToPath(newBuildDir.getParentFile()));
Files.move(Util.fileToPath(oldBuildDir), Util.fileToPath(newBuildDir));
}
}
diff --git a/core/src/main/java/hudson/model/PasswordParameterDefinition.java b/core/src/main/java/hudson/model/PasswordParameterDefinition.java
index 05008c365108..b5422725984a 100644
--- a/core/src/main/java/hudson/model/PasswordParameterDefinition.java
+++ b/core/src/main/java/hudson/model/PasswordParameterDefinition.java
@@ -23,6 +23,8 @@
*/
package hudson.model;
+import edu.umd.cs.findbugs.annotations.CheckForNull;
+import edu.umd.cs.findbugs.annotations.NonNull;
import hudson.Extension;
import hudson.util.Secret;
import java.util.Objects;
@@ -48,14 +50,14 @@ public class PasswordParameterDefinition extends SimpleParameterDefinition {
private Secret defaultValue;
@Deprecated
- public PasswordParameterDefinition(String name, String defaultValue, String description) {
+ public PasswordParameterDefinition(@NonNull String name, @CheckForNull String defaultValue, @CheckForNull String description) {
super(name, description);
this.defaultValue = Secret.fromString(defaultValue);
}
// TODO consider switching @DataBoundConstructor to a PasswordParameterDefinition(String) overload
@DataBoundConstructor
- public PasswordParameterDefinition(String name, Secret defaultValueAsSecret, String description) {
+ public PasswordParameterDefinition(@NonNull String name, @CheckForNull Secret defaultValueAsSecret, @CheckForNull String description) {
super(name, description);
this.defaultValue = defaultValueAsSecret;
}
@@ -90,6 +92,7 @@ public ParameterValue getDefaultParameterValue() {
return new PasswordParameterValue(getName(), getDefaultValue(), getDescription());
}
+ @NonNull
public String getDefaultValue() {
return Secret.toString(defaultValue);
}
diff --git a/core/src/main/java/hudson/model/Queue.java b/core/src/main/java/hudson/model/Queue.java
index 9d3bfb1194b4..1790fec1f21a 100644
--- a/core/src/main/java/hudson/model/Queue.java
+++ b/core/src/main/java/hudson/model/Queue.java
@@ -1370,6 +1370,7 @@ public static java.util.concurrent.Callable wrapWithLock(java.util.concur
}
@Override
+ @SuppressFBWarnings(value = "WA_AWAIT_NOT_IN_LOOP", justification = "the caller does indeed call this method in a loop")
protected void _await() throws InterruptedException {
condition.await();
}
diff --git a/core/src/main/java/hudson/model/ResourceController.java b/core/src/main/java/hudson/model/ResourceController.java
index 3ae1b597e6e1..7c15a52f98e0 100644
--- a/core/src/main/java/hudson/model/ResourceController.java
+++ b/core/src/main/java/hudson/model/ResourceController.java
@@ -24,6 +24,7 @@
package hudson.model;
import edu.umd.cs.findbugs.annotations.NonNull;
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import hudson.util.AdaptedIterator;
import java.util.AbstractCollection;
import java.util.Collection;
@@ -166,6 +167,7 @@ public ResourceActivity getBlockingActivity(ResourceActivity activity) {
return null;
}
+ @SuppressFBWarnings(value = "WA_NOT_IN_LOOP", justification = "the caller does indeed call this method in a loop")
protected void _await() throws InterruptedException {
wait();
}
diff --git a/core/src/main/java/hudson/model/Run.java b/core/src/main/java/hudson/model/Run.java
index e331a9e0c172..33673abfcdc3 100644
--- a/core/src/main/java/hudson/model/Run.java
+++ b/core/src/main/java/hudson/model/Run.java
@@ -1509,11 +1509,11 @@ public Collection getBuildFingerprints() {
}
String message = "No such file: " + logFile;
- return new ByteArrayInputStream(charset != null ? message.getBytes(charset) : message.getBytes());
+ return new ByteArrayInputStream(charset != null ? message.getBytes(charset) : message.getBytes(Charset.defaultCharset()));
}
public @NonNull Reader getLogReader() throws IOException {
- if (charset==null) return new InputStreamReader(getLogInputStream());
+ if (charset==null) return new InputStreamReader(getLogInputStream(),Charset.defaultCharset());
else return new InputStreamReader(getLogInputStream(),charset);
}
diff --git a/core/src/main/java/hudson/model/RunMap.java b/core/src/main/java/hudson/model/RunMap.java
index 6b2ce262acc4..451fd577f584 100644
--- a/core/src/main/java/hudson/model/RunMap.java
+++ b/core/src/main/java/hudson/model/RunMap.java
@@ -27,6 +27,7 @@
import static jenkins.model.lazy.AbstractLazyLoadRunMap.Direction.ASC;
import static jenkins.model.lazy.AbstractLazyLoadRunMap.Direction.DESC;
+import hudson.Util;
import java.io.File;
import java.io.IOException;
import java.io.UncheckedIOException;
@@ -195,7 +196,7 @@ public R put(R r) {
proposeNewNumber(r.getNumber());
}
try {
- Files.createDirectories(rootDir.toPath());
+ Util.createDirectories(rootDir.toPath());
} catch (IOException e) {
throw new UncheckedIOException(e);
}
diff --git a/core/src/main/java/hudson/model/RunParameterDefinition.java b/core/src/main/java/hudson/model/RunParameterDefinition.java
index 33b5d64f3bf4..56e6f4e77594 100644
--- a/core/src/main/java/hudson/model/RunParameterDefinition.java
+++ b/core/src/main/java/hudson/model/RunParameterDefinition.java
@@ -23,6 +23,8 @@
*/
package hudson.model;
+import edu.umd.cs.findbugs.annotations.CheckForNull;
+import edu.umd.cs.findbugs.annotations.NonNull;
import hudson.Extension;
import hudson.util.EnumConverter;
import hudson.util.RunList;
@@ -67,7 +69,7 @@ public String getName() {
* @since 1.517
*/
@DataBoundConstructor
- public RunParameterDefinition(String name, String projectName, String description, RunParameterFilter filter) {
+ public RunParameterDefinition(@NonNull String name, String projectName, @CheckForNull String description, @CheckForNull RunParameterFilter filter) {
super(name, description);
this.projectName = projectName;
this.runId = null;
@@ -78,12 +80,12 @@ public RunParameterDefinition(String name, String projectName, String descriptio
* @deprecated as of 1.517
*/
@Deprecated
- public RunParameterDefinition(String name, String projectName, String description) {
+ public RunParameterDefinition(@NonNull String name, String projectName, @CheckForNull String description) {
// delegate to updated constructor with additional RunParameterFilter parameter defaulted to ALL.
this(name, projectName, description, RunParameterFilter.ALL);
}
- private RunParameterDefinition(String name, String projectName, String runId, String description, RunParameterFilter filter) {
+ private RunParameterDefinition(@NonNull String name, String projectName, String runId, @CheckForNull String description, @CheckForNull RunParameterFilter filter) {
super(name, description);
this.projectName = projectName;
this.runId = runId;
diff --git a/core/src/main/java/hudson/model/SimpleParameterDefinition.java b/core/src/main/java/hudson/model/SimpleParameterDefinition.java
index 0cbc6ddc6817..0be175bb4a86 100644
--- a/core/src/main/java/hudson/model/SimpleParameterDefinition.java
+++ b/core/src/main/java/hudson/model/SimpleParameterDefinition.java
@@ -1,5 +1,7 @@
package hudson.model;
+import edu.umd.cs.findbugs.annotations.CheckForNull;
+import edu.umd.cs.findbugs.annotations.NonNull;
import hudson.cli.CLICommand;
import java.io.IOException;
import org.kohsuke.stapler.DataBoundConstructor;
@@ -11,7 +13,7 @@
* @author Kohsuke Kawaguchi
*/
public abstract class SimpleParameterDefinition extends ParameterDefinition {
- protected SimpleParameterDefinition(String name) {
+ protected SimpleParameterDefinition(@NonNull String name) {
super(name);
}
@@ -19,7 +21,7 @@ protected SimpleParameterDefinition(String name) {
* @deprecated Prefer {@link #SimpleParameterDefinition(String)} with a {@link DataBoundConstructor} and allow {@link #setDescription} to be used as needed
*/
@Deprecated
- protected SimpleParameterDefinition(String name, String description) {
+ protected SimpleParameterDefinition(@NonNull String name, @CheckForNull String description) {
super(name, description);
}
diff --git a/core/src/main/java/hudson/model/StringParameterDefinition.java b/core/src/main/java/hudson/model/StringParameterDefinition.java
index ff44daa3540d..1bff7a75a241 100644
--- a/core/src/main/java/hudson/model/StringParameterDefinition.java
+++ b/core/src/main/java/hudson/model/StringParameterDefinition.java
@@ -23,6 +23,7 @@
*/
package hudson.model;
+import edu.umd.cs.findbugs.annotations.CheckForNull;
import edu.umd.cs.findbugs.annotations.NonNull;
import hudson.Extension;
import hudson.Util;
@@ -47,24 +48,24 @@ public class StringParameterDefinition extends SimpleParameterDefinition {
* @since 2.281
*/
@DataBoundConstructor
- public StringParameterDefinition(String name) {
+ public StringParameterDefinition(@NonNull String name) {
super(name);
}
- public StringParameterDefinition(String name, String defaultValue, String description, boolean trim) {
+ public StringParameterDefinition(@NonNull String name, @CheckForNull String defaultValue, @CheckForNull String description, boolean trim) {
this(name);
setDefaultValue(defaultValue);
setDescription(description);
setTrim(trim);
}
- public StringParameterDefinition(String name, String defaultValue, String description) {
+ public StringParameterDefinition(@NonNull String name, @CheckForNull String defaultValue, @CheckForNull String description) {
this(name);
setDefaultValue(defaultValue);
setDescription(description);
}
- public StringParameterDefinition(String name, String defaultValue) {
+ public StringParameterDefinition(@NonNull String name, @CheckForNull String defaultValue) {
this(name);
setDefaultValue(defaultValue);
}
@@ -79,6 +80,7 @@ public ParameterDefinition copyWithDefaultValue(ParameterValue defaultValue) {
}
}
+ @NonNull
public String getDefaultValue() {
return defaultValue;
}
@@ -96,7 +98,7 @@ public String getDefaultValue4Build() {
}
@DataBoundSetter
- public void setDefaultValue(String defaultValue) {
+ public void setDefaultValue(@CheckForNull String defaultValue) {
this.defaultValue = Util.fixEmpty(defaultValue);
}
@@ -145,7 +147,7 @@ public String getHelpFile() {
@Override
public ParameterValue createValue(StaplerRequest req, JSONObject jo) {
StringParameterValue value = req.bindJSON(StringParameterValue.class, jo);
- if (isTrim() && value!=null) {
+ if (isTrim()) {
value.doTrim();
}
value.setDescription(getDescription());
diff --git a/core/src/main/java/hudson/model/TextParameterDefinition.java b/core/src/main/java/hudson/model/TextParameterDefinition.java
index 5235dbd33b3d..d639b88b6d92 100644
--- a/core/src/main/java/hudson/model/TextParameterDefinition.java
+++ b/core/src/main/java/hudson/model/TextParameterDefinition.java
@@ -23,6 +23,8 @@
*/
package hudson.model;
+import edu.umd.cs.findbugs.annotations.CheckForNull;
+import edu.umd.cs.findbugs.annotations.NonNull;
import hudson.Extension;
import java.util.Objects;
import net.sf.json.JSONObject;
@@ -39,11 +41,11 @@ public class TextParameterDefinition extends StringParameterDefinition {
* @since 2.281
*/
@DataBoundConstructor
- public TextParameterDefinition(String name) {
+ public TextParameterDefinition(@NonNull String name) {
super(name);
}
- public TextParameterDefinition(String name, String defaultValue, String description) {
+ public TextParameterDefinition(@NonNull String name, @CheckForNull String defaultValue, @CheckForNull String description) {
this(name);
setDefaultValue(defaultValue);
setDescription(description);
diff --git a/core/src/main/java/hudson/model/UpdateCenter.java b/core/src/main/java/hudson/model/UpdateCenter.java
index fdcaa3ada6f4..e3b0dcb40f77 100644
--- a/core/src/main/java/hudson/model/UpdateCenter.java
+++ b/core/src/main/java/hudson/model/UpdateCenter.java
@@ -68,6 +68,7 @@
import java.net.URL;
import java.net.URLConnection;
import java.net.UnknownHostException;
+import java.nio.charset.StandardCharsets;
import java.nio.file.AtomicMoveNotSupportedException;
import java.nio.file.Files;
import java.nio.file.InvalidPathException;
@@ -1240,6 +1241,7 @@ public void postValidate(DownloadJob job, File src) throws IOException {
* @throws IOException if there were problems downloading the resource.
* @see DownloadJob
*/
+ @SuppressFBWarnings(value = "WEAK_MESSAGE_DIGEST_SHA1", justification = "SHA-1 is only used as a fallback if SHA-256/SHA-512 are not available")
public File download(DownloadJob job, URL src) throws IOException {
MessageDigest sha1 = null;
MessageDigest sha256 = null;
@@ -2024,11 +2026,11 @@ private static VerificationResult verifyChecksums(String expectedDigest, String
}
if (caseSensitive) {
- if (MessageDigest.isEqual(expectedDigest.getBytes(), actualDigest.getBytes())) {
+ if (MessageDigest.isEqual(expectedDigest.getBytes(StandardCharsets.US_ASCII), actualDigest.getBytes(StandardCharsets.US_ASCII))) {
return VerificationResult.PASS;
}
} else {
- if (MessageDigest.isEqual(expectedDigest.toLowerCase().getBytes(), actualDigest.toLowerCase().getBytes())) {
+ if (MessageDigest.isEqual(expectedDigest.toLowerCase().getBytes(StandardCharsets.US_ASCII), actualDigest.toLowerCase().getBytes(StandardCharsets.US_ASCII))) {
return VerificationResult.PASS;
}
}
diff --git a/core/src/main/java/hudson/model/UpdateSite.java b/core/src/main/java/hudson/model/UpdateSite.java
index 63c82a96f76a..cf56d13bea44 100644
--- a/core/src/main/java/hudson/model/UpdateSite.java
+++ b/core/src/main/java/hudson/model/UpdateSite.java
@@ -1078,6 +1078,42 @@ private static String get(JSONObject o, String prop) {
static final Predicate
*
- * @version $Revision: 1783032 $
+ * @version $Revision$
* @since Validator 1.4
*/
//[PATCH]
@@ -131,6 +131,34 @@ public boolean isValidInet4Address(String inet4Address) {
* @since 1.4.1
*/
public boolean isValidInet6Address(String inet6Address) {
+ String[] parts;
+ // remove prefix size. This will appear after the zone id (if any)
+ parts = inet6Address.split("/", -1);
+ if (parts.length > 2) {
+ return false; // can only have one prefix specifier
+ }
+ if (parts.length == 2) {
+ if (parts[1].matches("\\d{1,3}")) { // Need to eliminate signs
+ int bits = Integer.parseInt(parts[1]); // cannot fail because of RE check
+ if (bits < 0 || bits > 128) {
+ return false; // out of range
+ }
+ } else {
+ return false; // not a valid number
+ }
+ }
+ // remove zone-id
+ parts = parts[0].split("%", -1);
+ if (parts.length > 2) {
+ return false;
+ } else if (parts.length == 2){
+ // The id syntax is implemenatation independent, but it presumably cannot allow:
+ // whitespace, '/' or '%'
+ if (!parts[1].matches("[^\\s/%]+")) {
+ return false; // invalid id
+ }
+ }
+ inet6Address = parts[0];
boolean containsCompressedZeroes = inet6Address.contains("::");
if (containsCompressedZeroes && (inet6Address.indexOf("::") != inet6Address.lastIndexOf("::"))) {
return false;
diff --git a/core/src/main/java/jenkins/org/apache/commons/validator/routines/RegexValidator.java b/core/src/main/java/jenkins/org/apache/commons/validator/routines/RegexValidator.java
index 3b08c1da07dc..3fd140711c99 100644
--- a/core/src/main/java/jenkins/org/apache/commons/validator/routines/RegexValidator.java
+++ b/core/src/main/java/jenkins/org/apache/commons/validator/routines/RegexValidator.java
@@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-/* Copied from commons-validator:commons-validator:1.6, with [PATCH] modifications */
+/* Copied from commons-validator:commons-validator:1.7, with [PATCH] modifications */
package jenkins.org.apache.commons.validator.routines;
import java.io.Serializable;
@@ -43,7 +43,7 @@
*
Validate returning an aggregated String of the matched groups:
@@ -67,7 +67,7 @@
* to the {@link Pattern} API are safe to use in a multi-threaded environment.
*
*
- * @version $Revision: 1739356 $
+ * @version $Revision$
* @since Validator 1.4
*/
//[PATCH]
diff --git a/core/src/main/java/jenkins/org/apache/commons/validator/routines/UrlValidator.java b/core/src/main/java/jenkins/org/apache/commons/validator/routines/UrlValidator.java
index 5fc7c4500077..5272e3a2e9d9 100644
--- a/core/src/main/java/jenkins/org/apache/commons/validator/routines/UrlValidator.java
+++ b/core/src/main/java/jenkins/org/apache/commons/validator/routines/UrlValidator.java
@@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-/* Copied from commons-validator:commons-validator:1.6, with [PATCH] modifications */
+/* Copied from commons-validator:commons-validator:1.7, with [PATCH] modifications */
package jenkins.org.apache.commons.validator.routines;
import java.io.Serializable;
@@ -50,7 +50,7 @@
*
* String[] schemes = {"http","https"}.
* UrlValidator urlValidator = new UrlValidator(schemes);
- * if (urlValidator.isValidRootUrl("ftp://foo.bar.com/")) {
+ * if (urlValidator.isValid("ftp://foo.bar.com/")) {
* System.out.println("url is valid");
* } else {
* System.out.println("url is invalid");
@@ -60,7 +60,7 @@
* If instead the default constructor is used.
*
* UrlValidator urlValidator = new UrlValidator();
- * if (urlValidator.isValidRootUrl("ftp://foo.bar.com/")) {
+ * if (urlValidator.isValid("ftp://foo.bar.com/")) {
* System.out.println("url is valid");
* } else {
* System.out.println("url is invalid");
@@ -74,7 +74,7 @@
* Uniform Resource Identifiers (URI): Generic Syntax
*
*
- * @version $Revision: 1783203 $
+ * @version $Revision$
* @since Validator 1.4
*/
//[PATCH]
@@ -110,30 +110,6 @@ public class UrlValidator implements Serializable {
*/
public static final long ALLOW_LOCAL_URLS = 1 << 3; // CHECKSTYLE IGNORE MagicNumber
- /**
- * This expression derived/taken from the BNF for URI (RFC2396).
- */
- private static final String URL_REGEX =
- "^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?";
- // 12 3 4 5 6 7 8 9
- private static final Pattern URL_PATTERN = Pattern.compile(URL_REGEX);
-
- /**
- * Schema/Protocol (ie. http:, ftp:, file:, etc).
- */
- private static final int PARSE_URL_SCHEME = 2;
-
- /**
- * Includes hostname/ip and port number.
- */
- private static final int PARSE_URL_AUTHORITY = 4;
-
- private static final int PARSE_URL_PATH = 5;
-
- private static final int PARSE_URL_QUERY = 7;
-
- private static final int PARSE_URL_FRAGMENT = 9;
-
/**
* Protocol scheme (e.g. http, ftp, https).
*/
@@ -144,7 +120,8 @@ public class UrlValidator implements Serializable {
// TODO does not allow for optional userinfo.
// Validation of character set is done by isValidAuthority
private static final String AUTHORITY_CHARS_REGEX = "\\p{Alnum}\\-\\."; // allows for IPV4 but not IPV6
- private static final String IPV6_REGEX = "[0-9a-fA-F:]+"; // do this as separate match because : could cause ambiguity with port prefix
+ // Allow for IPv4 mapped addresses: ::FFF:123.123.123.123
+ private static final String IPV6_REGEX = "::FFFF:(?:\\d{1,3}\\.){3}\\d{1,3}|[0-9a-fA-F:]+"; // do this as separate match because : could cause ambiguity with port prefix
// userinfo = *( unreserved / pct-encoded / sub-delims / ":" )
// unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
@@ -211,6 +188,8 @@ public static UrlValidator getInstance() {
return DEFAULT_URL_VALIDATOR;
}
+ private final DomainValidator domainValidator;
+
/**
* Create a UrlValidator with default properties.
*/
@@ -274,7 +253,29 @@ public UrlValidator(RegexValidator authorityValidator, long options) {
* enables both of those options.
*/
public UrlValidator(String[] schemes, RegexValidator authorityValidator, long options) {
+ this(schemes, authorityValidator, options, DomainValidator.getInstance(isOn(ALLOW_LOCAL_URLS, options)));
+ }
+
+ /**
+ * Customizable constructor. Validation behavior is modified by passing in options.
+ * @param schemes the set of valid schemes. Ignored if the ALLOW_ALL_SCHEMES option is set.
+ * @param authorityValidator Regular expression validator used to validate the authority part
+ * @param options Validation options. Set using the public constants of this class.
+ * To set multiple options, simply add them together:
+ *
{@code ALLOW_2_SLASHES + NO_FRAGMENTS}
+ * enables both of those options.
+ * @param domainValidator the DomainValidator to use; must agree with ALLOW_LOCAL_URLS setting
+ * @since 1.7
+ */
+ public UrlValidator(String[] schemes, RegexValidator authorityValidator, long options, DomainValidator domainValidator) {
this.options = options;
+ if (domainValidator == null) {
+ throw new IllegalArgumentException("DomainValidator must not be null");
+ }
+ if (domainValidator.isAllowLocal() != ((options & ALLOW_LOCAL_URLS) > 0)){
+ throw new IllegalArgumentException("DomainValidator disagrees with ALLOW_LOCAL_URLS setting");
+ }
+ this.domainValidator = domainValidator;
if (isOn(ALLOW_ALL_SCHEMES)) {
allowedSchemes = Collections.emptySet();
@@ -306,41 +307,40 @@ public boolean isValid(String value) {
return false;
}
- // Check the whole url address structure
- Matcher urlMatcher = URL_PATTERN.matcher(value);
- if (!urlMatcher.matches()) {
+ URI uri; // ensure value is a valid URI
+ try {
+ uri = new URI(value);
+ } catch (URISyntaxException e) {
return false;
}
+ // OK, perfom additional validation
- String scheme = urlMatcher.group(PARSE_URL_SCHEME);
+ String scheme = uri.getScheme();
if (!isValidScheme(scheme)) {
return false;
}
- String authority = urlMatcher.group(PARSE_URL_AUTHORITY);
- if ("file".equals(scheme)) {// Special case - file: allows an empty authority
- if (authority != null) {
- if (authority.contains(":")) { // but cannot allow trailing :
- return false;
- }
- }
- // drop through to continue validation
- } else { // not file:
+ String authority = uri.getRawAuthority();
+ if ("file".equals(scheme) && (authority == null || "".equals(authority))) {// Special case - file: allows an empty authority
+ return true; // this is a local file - nothing more to do here
+ } else if ("file".equals(scheme) && authority != null && authority.contains(":")) {
+ return false;
+ } else {
// Validate the authority
if (!isValidAuthority(authority)) {
return false;
}
}
- if (!isValidPath(urlMatcher.group(PARSE_URL_PATH))) {
+ if (!isValidPath(uri.getRawPath())) {
return false;
}
- if (!isValidQuery(urlMatcher.group(PARSE_URL_QUERY))) {
+ if (!isValidQuery(uri.getRawQuery())) {
return false;
}
- if (!isValidFragment(urlMatcher.group(PARSE_URL_FRAGMENT))) {
+ if (!isValidFragment(uri.getRawFragment())) {
return false;
}
@@ -361,7 +361,6 @@ protected boolean isValidScheme(String scheme) {
return false;
}
- // TODO could be removed if external schemes were checked in the ctor before being stored
if (!SCHEME_PATTERN.matcher(scheme).matches()) {
return false;
}
@@ -411,8 +410,7 @@ protected boolean isValidAuthority(String authority) {
String hostLocation = authorityMatcher.group(PARSE_AUTHORITY_HOST_IP);
// check if authority is hostname or IP address:
// try a hostname first since that's much more likely
- DomainValidator domainValidator = DomainValidator.getInstance(isOn(ALLOW_LOCAL_URLS));
- if (!domainValidator.isValid(hostLocation)) {
+ if (!this.domainValidator.isValid(hostLocation)) {
// try an IPv4 address
InetAddressValidator inetAddressValidator = InetAddressValidator.getInstance();
if (!inetAddressValidator.isValidInet4Address(hostLocation)) {
@@ -456,7 +454,8 @@ protected boolean isValidPath(String path) {
}
try {
- URI uri = new URI(null,null,path,null);
+ // Don't omit host otherwise leading path may be taken as host if it starts with //
+ URI uri = new URI(null,"localhost",path,null);
String norm = uri.normalize().getPath();
if (norm.startsWith("/../") // Trying to go via the parent dir
|| norm.equals("/..")) { // Trying to go to the parent dir
@@ -531,6 +530,19 @@ private boolean isOn(long flag) {
return (options & flag) > 0;
}
+ /**
+ * Tests whether the given flag is on. If the flag is not a power of 2
+ * (e.g. 3) this tests whether the combination of flags is on.
+ *
+ * @param flag Flag value to check.
+ * @param options what to check
+ *
+ * @return whether the specified flag value is on.
+ */
+ private static boolean isOn(long flag, long options) {
+ return (options & flag) > 0;
+ }
+
/**
* Tests whether the given flag is off. If the flag is not a power of 2
* (ie. 3) this tests whether the combination of flags is off.
@@ -542,9 +554,4 @@ private boolean isOn(long flag) {
private boolean isOff(long flag) {
return (options & flag) == 0;
}
-
- // Unit test access to pattern matcher
- Matcher matchURL(String value) {
- return URL_PATTERN.matcher(value);
- }
}
diff --git a/core/src/main/java/jenkins/security/DefaultConfidentialStore.java b/core/src/main/java/jenkins/security/DefaultConfidentialStore.java
index d24bc837dd4a..49513a02ef60 100644
--- a/core/src/main/java/jenkins/security/DefaultConfidentialStore.java
+++ b/core/src/main/java/jenkins/security/DefaultConfidentialStore.java
@@ -8,6 +8,7 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.InvalidPathException;
import java.security.GeneralSecurityException;
@@ -151,5 +152,5 @@ public byte[] randomBytes(int size) {
return random;
}
- private static final byte[] MAGIC = "::::MAGIC::::".getBytes();
+ private static final byte[] MAGIC = "::::MAGIC::::".getBytes(StandardCharsets.US_ASCII);
}
diff --git a/core/src/main/java/jenkins/security/apitoken/ApiTokenStats.java b/core/src/main/java/jenkins/security/apitoken/ApiTokenStats.java
index 42a12b9f817c..deda472e48e8 100644
--- a/core/src/main/java/jenkins/security/apitoken/ApiTokenStats.java
+++ b/core/src/main/java/jenkins/security/apitoken/ApiTokenStats.java
@@ -26,6 +26,7 @@
import com.google.common.annotations.VisibleForTesting;
import edu.umd.cs.findbugs.annotations.CheckForNull;
import edu.umd.cs.findbugs.annotations.NonNull;
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import hudson.BulkChange;
import hudson.Util;
import hudson.XmlFile;
@@ -161,6 +162,7 @@ public synchronized void removeAllExcept(@NonNull String tokenUuid) {
}
+ @SuppressFBWarnings(value = "IS2_INCONSISTENT_SYNC", justification = "access is in fact synchronized")
private synchronized SingleTokenStats updateUsageForIdIfNeeded(@NonNull String tokenUuid) {
SingleTokenStats stats = findById(tokenUuid)
.orElseGet(() -> {
diff --git a/core/src/main/java/jenkins/security/s2m/AdminCallableMonitor.java b/core/src/main/java/jenkins/security/s2m/AdminCallableMonitor.java
deleted file mode 100644
index 4b8c07d92034..000000000000
--- a/core/src/main/java/jenkins/security/s2m/AdminCallableMonitor.java
+++ /dev/null
@@ -1,71 +0,0 @@
-package jenkins.security.s2m;
-
-import hudson.Extension;
-import hudson.FilePath;
-import hudson.model.AdministrativeMonitor;
-import hudson.remoting.Callable;
-import java.io.IOException;
-import javax.inject.Inject;
-import jenkins.model.Jenkins;
-import org.jenkinsci.Symbol;
-import org.kohsuke.stapler.HttpResponse;
-import org.kohsuke.stapler.HttpResponses;
-import org.kohsuke.stapler.QueryParameter;
-import org.kohsuke.stapler.interceptor.RequirePOST;
-
-/**
- * Report any rejected {@link Callable}s and {@link FilePath} executions and allow
- * admins to whitelist them.
- *
- * @since 1.587 / 1.580.1
- * @author Kohsuke Kawaguchi
- */
-@Extension @Symbol({"agentToControllerAccessControl", "slaveToMasterAccessControl"})
-public class AdminCallableMonitor extends AdministrativeMonitor {
- @Inject
- Jenkins jenkins;
-
- @Inject
- AdminWhitelistRule rule;
-
- public AdminCallableMonitor() {
- super("slaveToMasterAccessControl"); // TODO Can we change this while retaining compatibility?
- }
-
- @Override
- public boolean isSecurity() {
- return true;
- }
-
- @Override
- public boolean isActivated() {
- return !rule.rejected.describe().isEmpty();
- }
-
- @Override
- public String getDisplayName() {
- return Messages.AdminCallableMonitor_DisplayName();
- }
-
- // bind this to URL
- public AdminWhitelistRule getRule() {
- return rule;
- }
-
- /**
- * Depending on whether the user said "examine" or "dismiss", send him to the right place.
- */
- @RequirePOST
- public HttpResponse doAct(@QueryParameter String dismiss) throws IOException {
- if(dismiss!=null) {
- disable(true);
- return HttpResponses.redirectViaContextPath("/manage");
- } else {
- return HttpResponses.redirectTo("rule/");
- }
- }
-
- public HttpResponse doIndex() {
- return HttpResponses.redirectTo("rule/");
- }
-}
diff --git a/core/src/main/java/jenkins/security/s2m/AdminCallableWhitelist.java b/core/src/main/java/jenkins/security/s2m/AdminCallableWhitelist.java
deleted file mode 100644
index 2dc832fa0c60..000000000000
--- a/core/src/main/java/jenkins/security/s2m/AdminCallableWhitelist.java
+++ /dev/null
@@ -1,31 +0,0 @@
-package jenkins.security.s2m;
-
-import hudson.Extension;
-import hudson.remoting.Callable;
-import java.util.Collection;
-import javax.inject.Inject;
-import org.jenkinsci.Symbol;
-import org.jenkinsci.remoting.Role;
-import org.jenkinsci.remoting.RoleSensitive;
-
-/**
- * Whitelists {@link Callable}s that are approved by the admins.
- *
- *
- *
- * Smaller ordinal value allows other programmable {@link CallableWhitelist} to accept/reject
- * {@link Callable}s without bothering the admins. This impl should be used only for those
- * {@link Callable}s that our program does not have any idea for.
- *
- * @author Kohsuke Kawaguchi
- */
-@Extension(ordinal=-100) @Symbol("admin")
-public class AdminCallableWhitelist extends CallableWhitelist {
- @Inject
- AdminWhitelistRule rule;
-
- @Override
- public boolean isWhitelisted(RoleSensitive subject, Collection expected, Object context) {
- return rule.isWhitelisted(subject,expected,context);
- }
-}
diff --git a/core/src/main/java/jenkins/security/s2m/AdminFilePathFilter.java b/core/src/main/java/jenkins/security/s2m/AdminFilePathFilter.java
deleted file mode 100644
index bf2ce553b420..000000000000
--- a/core/src/main/java/jenkins/security/s2m/AdminFilePathFilter.java
+++ /dev/null
@@ -1,50 +0,0 @@
-package jenkins.security.s2m;
-
-import edu.umd.cs.findbugs.annotations.Nullable;
-import hudson.Extension;
-import hudson.remoting.ChannelBuilder;
-import java.io.File;
-import javax.inject.Inject;
-import jenkins.FilePathFilter;
-import jenkins.ReflectiveFilePathFilter;
-import jenkins.security.ChannelConfigurator;
-
-/**
- * {@link FilePathFilter} that allows admins to whitelist specific file access.
- *
- *
- * This class is just a glue, and the real logic happens inside {@link AdminWhitelistRule}
- *
- * @author Kohsuke Kawaguchi
- * @since 1.587 / 1.580.1
- */
-public class AdminFilePathFilter extends ReflectiveFilePathFilter {
-
- private final AdminWhitelistRule rule;
-
- public AdminFilePathFilter(AdminWhitelistRule rule) {
- this.rule = rule;
- }
-
- @Override
- protected boolean op(String op, File path) throws SecurityException {
- return rule.checkFileAccess(op,path);
- }
-
- @Extension
- public static class ChannelConfiguratorImpl extends ChannelConfigurator {
- @Inject
- AdminWhitelistRule rule;
-
- @Override
- public void onChannelBuilding(ChannelBuilder builder, @Nullable Object context) {
- new AdminFilePathFilter(rule).installTo(builder,ORDINAL);
- }
- }
-
- /**
- * Local user preference should have higher priority than random FilePathFilters that
- * plugins might provide.
- */
- public static final double ORDINAL = 100;
-}
diff --git a/core/src/main/java/jenkins/security/s2m/AdminWhitelistRule.java b/core/src/main/java/jenkins/security/s2m/AdminWhitelistRule.java
index 98bcac0f7b8b..03622c94bc4e 100644
--- a/core/src/main/java/jenkins/security/s2m/AdminWhitelistRule.java
+++ b/core/src/main/java/jenkins/security/s2m/AdminWhitelistRule.java
@@ -1,238 +1,40 @@
package jenkins.security.s2m;
+import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING;
-import edu.umd.cs.findbugs.annotations.CheckReturnValue;
-import edu.umd.cs.findbugs.annotations.NonNull;
import hudson.Extension;
-import hudson.FilePath;
-import hudson.Functions;
-import hudson.Util;
-import hudson.util.HttpResponses;
-import java.io.BufferedReader;
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.PrintStream;
-import java.nio.charset.Charset;
-import java.util.Collection;
-import java.util.Enumeration;
import java.util.logging.Logger;
-import jenkins.model.Jenkins;
-import jenkins.util.io.FileBoolean;
-import org.apache.commons.io.FileUtils;
-import org.jenkinsci.remoting.Role;
-import org.jenkinsci.remoting.RoleSensitive;
-import org.kohsuke.stapler.HttpResponse;
-import org.kohsuke.stapler.QueryParameter;
-import org.kohsuke.stapler.StaplerProxy;
-import org.kohsuke.stapler.StaplerRequest;
-import org.kohsuke.stapler.interceptor.RequirePOST;
/**
- * Rules of whitelisting for {@link RoleSensitive} objects and {@link FilePath}s.
+ * @deprecated This class no longer has any effect.
+ * Support for allowlisting {@link hudson.remoting.Callable}s predating the introduction of the {@link org.jenkinsci.remoting.RoleSensitive} interface for SECURITY-144 in 2014 has been dropped.
+ * {@link hudson.FilePath} no longer supports being invoked from agents at all, so {@code FilePathFilter} etc. have been removed.
+ *
+ * @see Agent-to-controller security subsystem documentation
*
- * @author Kohsuke Kawaguchi
*/
+@Deprecated
@Extension
-public class AdminWhitelistRule implements StaplerProxy {
- /**
- * Ones that we rejected but want to run by admins.
- */
- public final CallableRejectionConfig rejected;
-
- /**
- * Callables that admins have whitelisted explicitly.
- */
- public final CallableWhitelistConfig whitelisted;
-
- /**
- * FilePath access pattern rules specified by the admin
- */
- public final FilePathRuleConfig filePathRules;
-
- private boolean masterKillSwitch;
-
- public AdminWhitelistRule() throws IOException, InterruptedException {
- final Jenkins jenkins = Jenkins.get();
-
- // while this file is not a secret, write access to this file is dangerous,
- // so put this in the better-protected part of $JENKINS_HOME, which is in secrets/
-
- // overwrite 30-default.conf with what we think is the best from the core.
- // this file shouldn't be touched by anyone. For local customization, use other files in the conf dir.
- // 0-byte file is used as a signal from the admin to prevent this overwriting
- try (InputStream callable = getClass().getResourceAsStream("callable.conf")) {
- placeDefaultRule(
- new File(jenkins.getRootDir(), "secrets/whitelisted-callables.d/default.conf"),
- callable);
- }
- try (InputStream filepathFilter = getClass().getResourceAsStream("filepath-filter.conf")) {
- placeDefaultRule(
- new File(jenkins.getRootDir(), "secrets/filepath-filters.d/30-default.conf"),
- transformForWindows(filepathFilter));
- }
-
- this.whitelisted = new CallableWhitelistConfig(
- new File(jenkins.getRootDir(),"secrets/whitelisted-callables.d/gui.conf"));
- this.rejected = new CallableRejectionConfig(
- new File(jenkins.getRootDir(),"secrets/rejected-callables.txt"),
- whitelisted);
- this.filePathRules = new FilePathRuleConfig(
- new File(jenkins.getRootDir(),"secrets/filepath-filters.d/50-gui.conf"));
-
- File f = getMasterKillSwitchFile(jenkins);
- this.masterKillSwitch = loadMasterKillSwitchFile(f);
- }
-
- /**
- * Reads the master kill switch from a file.
- *
- * Instead of {@link FileBoolean}, we use a text file so that the admin can prevent Jenkins from
- * writing this to file.
- * @param f File to load
- * @return {@code true} if the file was loaded, {@code false} otherwise
- */
- @CheckReturnValue
- private boolean loadMasterKillSwitchFile(@NonNull File f) {
- try {
- if (!f.exists()) return true;
- return Boolean.parseBoolean(FileUtils.readFileToString(f, Charset.defaultCharset()).trim());
- } catch (IOException e) {
- LOGGER.log(WARNING, "Failed to read "+f, e);
- return false;
- }
- }
-
- @NonNull
- private File getMasterKillSwitchFile(@NonNull Jenkins jenkins) {
- return new File(jenkins.getRootDir(),"secrets/slave-to-master-security-kill-switch");
- }
-
- /**
- * Transform path for Windows.
- */
- private InputStream transformForWindows(InputStream src) throws IOException {
- BufferedReader r = new BufferedReader(new InputStreamReader(src));
- ByteArrayOutputStream out = new ByteArrayOutputStream();
- try (PrintStream p = new PrintStream(out)) {
- String line;
- while ((line = r.readLine()) != null) {
- if (!line.startsWith("#") && Functions.isWindows())
- line = line.replace("/", "\\\\");
- p.println(line);
- }
- }
- return new ByteArrayInputStream(out.toByteArray());
- }
-
- private void placeDefaultRule(File f, InputStream src) throws IOException, InterruptedException {
- try {
- new FilePath(f).copyFrom(src);
- } catch (IOException e) {
- // we allow admins to create a read-only file here to block overwrite,
- // so this can fail legitimately
- if (!f.canWrite()) return;
- LOGGER.log(WARNING, "Failed to generate "+f,e);
- }
- }
+public class AdminWhitelistRule {
- public boolean isWhitelisted(RoleSensitive subject, Collection expected, Object context) {
- if (masterKillSwitch)
- return true; // master kill switch is on. subsystem deactivated
-
- String name = subject.getClass().getName();
-
- if (whitelisted.contains(name))
- return true; // whitelisted by admin
-
- // otherwise record the problem and refuse to execute that
- rejected.report(subject.getClass());
- return false;
- }
-
- public boolean checkFileAccess(String op, File f) {
- // if the master kill switch is off, we allow everything
- if (masterKillSwitch)
- return true;
-
- return filePathRules.checkFileAccess(op, f);
- }
-
- @RequirePOST
- public HttpResponse doSubmit(StaplerRequest req) throws IOException {
- StringBuilder whitelist = new StringBuilder(Util.fixNull(req.getParameter("whitelist")));
- if (whitelist.length() > 0 && whitelist.charAt(whitelist.length() - 1) != '\n')
- whitelist.append("\n");
-
- Enumeration e = req.getParameterNames();
- while (e.hasMoreElements()) {
- String name = (String) e.nextElement();
- if (name.startsWith("class:")) {
- whitelist.append(name.substring(6)).append("\n");
- }
- }
-
- whitelisted.set(whitelist.toString());
-
- String newRules = Util.fixNull(req.getParameter("filePathRules"));
- filePathRules.parseTest(newRules); // test first before writing a potentially broken rules
- filePathRules.set(newRules);
-
- return HttpResponses.redirectToDot();
- }
-
- /**
- * Approves all the currently rejected subjects
- */
- @RequirePOST
- public HttpResponse doApproveAll() throws IOException {
- StringBuilder buf = new StringBuilder();
- for (Class c : rejected.get()) {
- buf.append(c.getName()).append('\n');
- }
- whitelisted.append(buf.toString());
-
- return HttpResponses.ok();
- }
-
- /**
- * Approves specific callables by their names.
- */
- @RequirePOST
- public HttpResponse doApprove(@QueryParameter String value) throws IOException {
- whitelisted.append(value);
- return HttpResponses.ok();
+ public AdminWhitelistRule() {
}
public boolean getMasterKillSwitch() {
- return masterKillSwitch;
+ LOGGER.log(WARNING, "AdminWhitelistRule no longer has any effect but an attempt was made to read its current configuration value. See https://www.jenkins.io/redirect/AdminWhitelistRule to learn more.", new Exception());
+ return false;
}
public void setMasterKillSwitch(boolean state) {
- final Jenkins jenkins = Jenkins.get();
- try {
- jenkins.checkPermission(Jenkins.ADMINISTER);
- File f = getMasterKillSwitchFile(jenkins);
- FileUtils.writeStringToFile(f, Boolean.toString(state), Charset.defaultCharset());
- // treat the file as the canonical source of information in case write fails
- masterKillSwitch = loadMasterKillSwitchFile(f);
- } catch (IOException e) {
- LOGGER.log(WARNING, "Failed to write master kill switch", e);
+ if (state) {
+ // an attempt to disable protections should warn
+ LOGGER.log(WARNING, "Setting AdminWhitelistRule no longer has any effect. See https://www.jenkins.io/redirect/AdminWhitelistRule to learn more.", new Exception());
+ } else {
+ // This is basically no-op
+ LOGGER.log(INFO, "Setting AdminWhitelistRule no longer has any effect. See https://www.jenkins.io/redirect/AdminWhitelistRule to learn more.", new Exception());
}
}
- /**
- * Restricts the access to administrator.
- */
- @Override
- public Object getTarget() {
- Jenkins.get().checkPermission(Jenkins.ADMINISTER);
- return this;
- }
-
private static final Logger LOGGER = Logger.getLogger(AdminWhitelistRule.class.getName());
}
diff --git a/core/src/main/java/jenkins/security/s2m/CallableDirectionChecker.java b/core/src/main/java/jenkins/security/s2m/CallableDirectionChecker.java
index 5f05307ed059..bb3807341add 100644
--- a/core/src/main/java/jenkins/security/s2m/CallableDirectionChecker.java
+++ b/core/src/main/java/jenkins/security/s2m/CallableDirectionChecker.java
@@ -19,17 +19,13 @@
import org.kohsuke.accmod.restrictions.NoExternalUse;
/**
- * Inspects {@link Callable}s that run on the master.
+ * Inspects {@link Callable}s that run on the controller.
*
* @author Kohsuke Kawaguchi
* @since 1.587 / 1.580.1
*/
@Restricted(NoExternalUse.class) // used implicitly via listener
public class CallableDirectionChecker extends RoleChecker {
- /**
- * Context parameter given to {@link ChannelConfigurator#onChannelBuilding(ChannelBuilder, Object)}.
- */
- private final Object context;
private static final String BYPASS_PROP = CallableDirectionChecker.class.getName()+".allow";
@@ -44,10 +40,6 @@ public class CallableDirectionChecker extends RoleChecker {
@SuppressFBWarnings(value = "MS_SHOULD_BE_FINAL", justification = "for script console")
public static boolean BYPASS = SystemProperties.getBoolean(BYPASS_PROP);
- private CallableDirectionChecker(Object context) {
- this.context = context;
- }
-
@Override
public void check(RoleSensitive subject, @NonNull Collection expected) throws SecurityException {
final String name = subject.getClass().getName();
@@ -63,26 +55,14 @@ public void check(RoleSensitive subject, @NonNull Collection expected) thr
return;
}
- if (isWhitelisted(subject,expected)) {
- // this subject is dubious, but we are letting it through as per whitelisting
- LOGGER.log(Level.FINE, "Explicitly allowing {0} to be sent from agent to controller", name);
+ if (BYPASS) {
+ LOGGER.log(Level.FINE, "Allowing {0} to be sent from agent to controller because bypass is set", name);
return;
}
throw new SecurityException("Sending " + name + " from agent to controller is prohibited.\nSee https://www.jenkins.io/redirect/security-144 for more details");
}
- /**
- * Is this subject class name whitelisted?
- */
- private boolean isWhitelisted(RoleSensitive subject, Collection expected) {
- for (CallableWhitelist w : CallableWhitelist.all()) {
- if (w.isWhitelisted(subject, expected, context))
- return true;
- }
- return false;
- }
-
/**
* Installs {@link CallableDirectionChecker} to every channel.
*/
@@ -97,20 +77,7 @@ public void onChannelBuilding(ChannelBuilder builder, Object context) {
builder.withRemoteClassLoadingAllowed(false);
}
// In either of the above cases, the check method will return normally, but may log things.
- builder.withRoleChecker(new CallableDirectionChecker(context));
- }
- }
-
- /**
- * Whitelist rule based on system properties.
- *
- * For the bypass "kill" switch to be effective, it needs to have a high enough priority
- */
- @Extension(ordinal=100)
- public static class DefaultWhitelist extends CallableWhitelist {
- @Override
- public boolean isWhitelisted(RoleSensitive subject, Collection expected, Object context) {
- return BYPASS;
+ builder.withRoleChecker(new CallableDirectionChecker());
}
}
diff --git a/core/src/main/java/jenkins/security/s2m/CallableRejectionConfig.java b/core/src/main/java/jenkins/security/s2m/CallableRejectionConfig.java
deleted file mode 100644
index 29ae9d4d0750..000000000000
--- a/core/src/main/java/jenkins/security/s2m/CallableRejectionConfig.java
+++ /dev/null
@@ -1,78 +0,0 @@
-package jenkins.security.s2m;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-import jenkins.model.Jenkins;
-
-/**
- * Text file that lists whitelisted callables.
- *
- * @author Kohsuke Kawaguchi
- */
-public class CallableRejectionConfig extends ConfigFile> {
- private final CallableWhitelistConfig whitelist;
-
- CallableRejectionConfig(File file, CallableWhitelistConfig whitelist) {
- super(file);
- this.whitelist = whitelist;
- }
-
- @Override
- protected Set create() {
- return new HashSet<>();
- }
-
- @Override
- protected Set readOnly(Set base) {
- return Collections.unmodifiableSet(new HashSet<>(base));
- }
-
- @Override
- protected Class parse(String line) {
- try {
- line = line.trim();
- if (whitelist.contains(line)) return null; // already whitelisted
-
- return Jenkins.get().pluginManager.uberClassLoader.loadClass(line);
- } catch (ClassNotFoundException e) {
- // no longer present in the system?
- return null;
- }
- }
-
- /**
- * This method gets called every time we see a new type of callable that we reject,
- * so that we can persist the list.
- */
- void report(Class c) {
- if (!get().contains(c)) {
- try {
- append(c.getName());
- } catch (IOException e) {
- LOGGER.log(Level.WARNING, "Failed to persist " + file, e);
- }
- }
- }
-
- /**
- * Return the object that helps the UI rendering by providing the details.
- */
- public List describe() {
- List l = new ArrayList<>();
- for (Class c : get()) {
- if (!whitelist.contains(c.getName()))
- l.add(new RejectedCallable(c));
- }
- return l;
- }
-
-
- private static final Logger LOGGER = Logger.getLogger(CallableRejectionConfig.class.getName());
-}
diff --git a/core/src/main/java/jenkins/security/s2m/CallableWhitelist.java b/core/src/main/java/jenkins/security/s2m/CallableWhitelist.java
deleted file mode 100644
index 7be48452c796..000000000000
--- a/core/src/main/java/jenkins/security/s2m/CallableWhitelist.java
+++ /dev/null
@@ -1,47 +0,0 @@
-package jenkins.security.s2m;
-
-import hudson.ExtensionList;
-import hudson.ExtensionPoint;
-import hudson.remoting.Callable;
-import hudson.remoting.ChannelBuilder;
-import java.util.Collection;
-import jenkins.model.Jenkins;
-import jenkins.security.ChannelConfigurator;
-import org.jenkinsci.remoting.Role;
-import org.jenkinsci.remoting.RoleChecker;
-import org.jenkinsci.remoting.RoleSensitive;
-
-/**
- * Used on the master to selectively allow specific {@link Callable}s to execute on the master
- * even when those {@link Callable}s do not have proper {@link Role} declarations from its
- * {@link Callable#checkRoles(RoleChecker)} method.
- *
- * @author Kohsuke Kawaguchi
- * @since 1.587 / 1.580.1
- */
-public abstract class CallableWhitelist implements ExtensionPoint {
- /**
- * Returns true if given {@code subject} should be allowed to execute on the master even though
- * it came over channel from other JVMs.
- *
- * @param subject
- * See {@link RoleChecker#check(RoleSensitive, Collection)}
- * @param expected
- * See {@link RoleChecker#check(RoleSensitive, Collection)}
- * @param context
- * Parameter given to {@link ChannelConfigurator#onChannelBuilding(ChannelBuilder, Object)}
- * @return
- * true to allow this subject to execute. No further {@link CallableWhitelist} is consulted
- * when this method returns true.
- * false to "-0" this subject. Other {@link CallableWhitelist}s will be given a chance to
- * accept/reject this subject, and if no one accepts it, the subject will be rejected.
- * @throws SecurityException
- * to blacklist a subject, throw this exception. No further {@link CallableWhitelist} is consulted,
- * and the execution will be rejected.
- */
- public abstract boolean isWhitelisted(RoleSensitive subject, Collection expected, Object context);
-
- public static ExtensionList all() {
- return Jenkins.get().getExtensionList(CallableWhitelist.class);
- }
-}
diff --git a/core/src/main/java/jenkins/security/s2m/CallableWhitelistConfig.java b/core/src/main/java/jenkins/security/s2m/CallableWhitelistConfig.java
deleted file mode 100644
index 0eb434554dbf..000000000000
--- a/core/src/main/java/jenkins/security/s2m/CallableWhitelistConfig.java
+++ /dev/null
@@ -1,38 +0,0 @@
-package jenkins.security.s2m;
-
-import hudson.Util;
-import java.io.File;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Set;
-import org.jenkinsci.remoting.RoleSensitive;
-
-/**
- * Set of fully-qualified {@link RoleSensitive} (mostly Callable) class names that are whitelisted by admin.
- *
- * @author Kohsuke Kawaguchi
- */
-class CallableWhitelistConfig extends ConfigDirectory> {
- CallableWhitelistConfig(File file) {
- super(file);
- }
-
- @Override
- protected Set create() {
- return new HashSet<>();
- }
-
- @Override
- protected Set readOnly(Set base) {
- return Collections.unmodifiableSet(new HashSet<>(base));
- }
-
- @Override
- protected String parse(String line) {
- return Util.fixEmptyAndTrim(line);
- }
-
- public boolean contains(String name) {
- return get().contains(name);
- }
-}
diff --git a/core/src/main/java/jenkins/security/s2m/ConfigDirectory.java b/core/src/main/java/jenkins/security/s2m/ConfigDirectory.java
deleted file mode 100644
index cbfa699e3acf..000000000000
--- a/core/src/main/java/jenkins/security/s2m/ConfigDirectory.java
+++ /dev/null
@@ -1,58 +0,0 @@
-package jenkins.security.s2m;
-
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileReader;
-import java.io.IOException;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-/**
- * Bit of a hack to expand {@link ConfigFile} to support conf.d format that assembles the fragment.
- *
- *
- * {@link #file} points to the "primary" file that we programmatically write to.
- *
- * @author Kohsuke Kawaguchi
- */
-abstract class ConfigDirectory> extends ConfigFile {
- private final File dir;
-
- protected ConfigDirectory(File file) {
- super(file);
- this.dir = file.getParentFile();
- }
-
- @Override
- public synchronized void load2() {
- COL result = create();
-
- if (dir.exists()) {
- String[] fragments = dir.list((dir, name) -> name.endsWith(".conf"));
- if (fragments!=null) {
- Arrays.sort(fragments);
-
- for (String fragment : fragments) {
- File f = new File(dir, fragment);
- try (BufferedReader reader = new BufferedReader(new FileReader(f))) {
- String line;
- while ((line=reader.readLine())!=null) {
- if (line.startsWith("#")) continue; // comment
- T r = parse(line);
- if (r != null)
- result.add(r);
- }
- } catch (IOException e) {
- LOGGER.log(Level.WARNING, "Failed to parse "+f,e);
- }
- }
- }
- }
-
- parsed = readOnly(result);
- }
-
- private static final Logger LOGGER = Logger.getLogger(ConfigDirectory.class.getName());
-}
diff --git a/core/src/main/java/jenkins/security/s2m/ConfigFile.java b/core/src/main/java/jenkins/security/s2m/ConfigFile.java
deleted file mode 100644
index 6bec4a8ab3e5..000000000000
--- a/core/src/main/java/jenkins/security/s2m/ConfigFile.java
+++ /dev/null
@@ -1,122 +0,0 @@
-package jenkins.security.s2m;
-
-import hudson.CopyOnWrite;
-import hudson.util.TextFile;
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.IOException;
-import java.io.StringReader;
-import java.util.Collection;
-import java.util.stream.Stream;
-import jenkins.model.Jenkins;
-
-/**
- * Abstraction of a line-by-line configuration text file that gets parsed into some in-memory data form.
- *
- * @author Kohsuke Kawaguchi
- */
-abstract class ConfigFile> extends TextFile {
- @CopyOnWrite
- protected volatile COL parsed;
-
- ConfigFile(File file) {
- super(file);
- }
-
- protected abstract COL create();
- protected abstract COL readOnly(COL base);
-
- /**
- * Loads the configuration from the configuration file.
- *
- * This method is equivalent to {@link #load2()}, except that any
- * {@link java.io.IOException} that occurs is wrapped as a
- * {@link java.lang.RuntimeException}.
- *
- * This method exists for source compatibility. Users should call
- * {@link #load2()} instead.
- * @deprecated use {@link #load2()} instead.
- */
- @Deprecated
- public void load() {
- try {
- load2();
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- }
-
- /**
- * Loads the configuration from the configuration file.
- * @throws IOException if the configuration file could not be read.
- * @since 2.111
- */
- public synchronized void load2() throws IOException {
- COL result = create();
-
- if (exists()) {
- try (Stream stream = lines()) {
- stream.forEach(line -> {
- if (line.startsWith("#")) return; // comment
- T r = parse(line);
- if (r != null)
- result.add(r);
- });
- }
- }
-
- parsed = readOnly(result);
- }
-
- /**
- * Goes through the parser with the given text to make sure it doesn't yield any error.
- */
- public void parseTest(String candidate) {
- try {
- BufferedReader r = new BufferedReader(new StringReader(candidate));
- String line;
- while ((line=r.readLine())!=null) {
- if (line.startsWith("#")) continue; // comment
- parse(line);
- }
- } catch (IOException e) {
- throw new IllegalArgumentException(e); // can't happen but just in case
- }
- }
-
- protected abstract T parse(String line);
-
- public synchronized void set(String newContent) throws IOException {
- Jenkins.get().checkPermission(Jenkins.ADMINISTER);
-
- write(newContent);
- load2();
- }
-
- public synchronized void append(String additional) throws IOException {
- if (!exists()) {
- set(additional);
- return;
- }
- String s = read();
- if (!s.endsWith("\n"))
- s += "\n";
- s+= additional;
-
- set(s);
- }
-
- public COL get() {
- // load upon the first use
- if (parsed==null) {
- synchronized (this) {
- if (parsed==null) {
- load();
- }
- }
- }
- return parsed;
- }
-
-
-}
diff --git a/core/src/main/java/jenkins/security/s2m/DefaultFilePathFilter.java b/core/src/main/java/jenkins/security/s2m/DefaultFilePathFilter.java
deleted file mode 100644
index a9ab17d2636b..000000000000
--- a/core/src/main/java/jenkins/security/s2m/DefaultFilePathFilter.java
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * The MIT License
- *
- * Copyright 2014 Jesse Glick.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-package jenkins.security.s2m;
-
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
-import hudson.Extension;
-import hudson.ExtensionList;
-import hudson.remoting.ChannelBuilder;
-import hudson.remoting.Command;
-import hudson.remoting.Request;
-import java.io.File;
-import java.lang.reflect.Field;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-import jenkins.ReflectiveFilePathFilter;
-import jenkins.security.ChannelConfigurator;
-import jenkins.telemetry.impl.SlaveToMasterFileCallableUsage;
-import jenkins.util.SystemProperties;
-import org.kohsuke.accmod.Restricted;
-import org.kohsuke.accmod.restrictions.NoExternalUse;
-
-/**
- * Blocks agents from writing to files on the master by default (and also provide the kill switch.)
- */
-@Restricted(NoExternalUse.class) // impl
-@Extension public class DefaultFilePathFilter extends ChannelConfigurator {
-
- /**
- * Escape hatch to disable this check completely.
- */
- @SuppressFBWarnings(value = "MS_SHOULD_BE_FINAL", justification = "for script console")
- public static boolean BYPASS = SystemProperties.getBoolean(DefaultFilePathFilter.class.getName()+".allow");
-
- private static final Logger LOGGER = Logger.getLogger(DefaultFilePathFilter.class.getName());
-
- @Override
- public void onChannelBuilding(ChannelBuilder builder, Object context) {
- new ReflectiveFilePathFilter() {
- @Override
- protected boolean op(String op, File f) throws SecurityException {
- if (BYPASS) {
- LOGGER.log(Level.FINE, "agent allowed to {0} {1}", new Object[] {op, f});
- return true;
- } else {
- try {
- Field current = Request.class.getDeclaredField("CURRENT");
- current.setAccessible(true);
- Field createdAt = Command.class.getDeclaredField("createdAt");
- createdAt.setAccessible(true);
- Throwable trace = (Throwable) createdAt.get(((ThreadLocal) current.get(null)).get());
- ExtensionList.lookupSingleton(SlaveToMasterFileCallableUsage.class).recordTrace(trace);
- LOGGER.log(Level.WARNING, "Permitting agent-to-controller '" + op + "' on '" + f + "'. This is deprecated and will soon be rejected. Learn more: https://www.jenkins.io/redirect/permitted-agent-to-controller-file-access", trace);
- } catch (Exception x) {
- LOGGER.log(Level.WARNING, null, x);
- }
- return false;
- }
- }
- }.installTo(builder, AdminFilePathFilter.ORDINAL+100);
- // for the bypass switch to be effective, it should have a high priority
- }
-}
diff --git a/core/src/main/java/jenkins/security/s2m/FilePathRule.java b/core/src/main/java/jenkins/security/s2m/FilePathRule.java
deleted file mode 100644
index 010748897ade..000000000000
--- a/core/src/main/java/jenkins/security/s2m/FilePathRule.java
+++ /dev/null
@@ -1,21 +0,0 @@
-package jenkins.security.s2m;
-
-import hudson.FilePath;
-import java.util.regex.Pattern;
-
-/**
- * One entry of the {@link FilePath} access rule.
- *
- * @author Kohsuke Kawaguchi
- */
-class FilePathRule {
- final Pattern path;
- final OpMatcher op;
- final boolean allow;
-
- FilePathRule(Pattern path, OpMatcher op, boolean allow) {
- this.path = path;
- this.op = op;
- this.allow = allow;
- }
-}
diff --git a/core/src/main/java/jenkins/security/s2m/FilePathRuleConfig.java b/core/src/main/java/jenkins/security/s2m/FilePathRuleConfig.java
deleted file mode 100644
index ab0a463d2c5e..000000000000
--- a/core/src/main/java/jenkins/security/s2m/FilePathRuleConfig.java
+++ /dev/null
@@ -1,121 +0,0 @@
-package jenkins.security.s2m;
-
-import static hudson.Functions.isWindows;
-
-import hudson.Functions;
-import hudson.model.Failure;
-import java.io.File;
-import java.io.IOException;
-import java.io.UncheckedIOException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-import jenkins.model.Jenkins;
-
-/**
- * Config file that lists {@link FilePathRule} rules.
- *
- * @author Kohsuke Kawaguchi
- */
-class FilePathRuleConfig extends ConfigDirectory> {
-
- private static final Logger LOGGER = Logger.getLogger(FilePathRuleConfig.class.getName());
-
- FilePathRuleConfig(File file) {
- super(file);
- }
-
- @Override
- protected List create() {
- return new ArrayList<>();
- }
-
- @Override
- protected List readOnly(List base) {
- return Collections.unmodifiableList(new ArrayList<>(base));
- }
-
- @Override
- protected FilePathRule parse(String line) {
- line = line.trim();
- if (line.isEmpty()) return null;
-
- // TODO This does not support custom build dir configuration (Jenkins#getRawBuildsDir() etc.)
- line = line.replace("","/builds/[0-9]+");
-
- // Kept only for compatibility with custom user-provided rules:
- line = line.replace("","(?:[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]_[0-9][0-9]-[0-9][0-9]-[0-9][0-9]|[0-9]+)");
- line = line.replace("","/jobs/.+");
- final File jenkinsHome = Jenkins.get().getRootDir();
- try {
- line = line.replace("","\\Q" + jenkinsHome.getCanonicalPath() + "\\E");
- } catch (IOException e) {
- LOGGER.log(Level.WARNING, e, () -> "Failed to determine canonical path to Jenkins home directory, falling back to configured value: " + jenkinsHome.getPath());
- line = line.replace("","\\Q" + jenkinsHome.getPath() + "\\E");
- }
-
- // config file is always /-separated even on Windows, so bring it back to \-separation.
- // This is done in the context of regex, so it has to be \\, which means in the source code it is \\\\
- if (isWindows()) line = line.replace("/","\\\\");
-
- Matcher m = PARSER.matcher(line);
- if (!m.matches())
- throw new Failure("Invalid filter rule line: "+line);
-
- try {
- return new FilePathRule(
- Pattern.compile(m.group(3)),
- createOpMatcher(m.group(2)),
- m.group(1).equals("allow"));
- } catch (RuntimeException e) {
- throw new Failure("Invalid filter rule line: "+line+"\n"+ Functions.printThrowable(e));
- }
- }
-
- private OpMatcher createOpMatcher(String token) {
- if (token.equals("all"))
- return OpMatcher.ALL;
-
- final Set ops = Collections.unmodifiableSet(new HashSet<>(Arrays.asList(token.split(","))));
- return ops::contains;
- }
-
- public boolean checkFileAccess(String op, File path) throws SecurityException {
- String pathStr = null;
-
- for (FilePathRule rule : get()) {
- if (rule.op.matches(op)) {
- if (pathStr==null) {
- try {
- pathStr = path.getCanonicalPath();
- } catch (IOException ex) {
- throw new UncheckedIOException(ex);
- }
- if (isWindows()) // Windows accepts '/' as separator, but for rule matching we want to normalize for consistent comparison
- pathStr = pathStr.replace('/','\\');
- }
-
- if (rule.path.matcher(pathStr).matches()) {
- // exclusion rule is only to bypass later path rules within #filePathRules,
- // and we still want other FilePathFilters to whitelist/blacklist access.
- // therefore I'm not throwing a SecurityException here
- return rule.allow;
- }
- }
- }
-
- return false;
- }
-
- /**
- *
- */
- private static final Pattern PARSER = Pattern.compile("(allow|deny)\\s+([a-z,]+)\\s+(.*)");
-}
diff --git a/core/src/main/java/jenkins/security/s2m/MasterKillSwitchConfiguration.java b/core/src/main/java/jenkins/security/s2m/MasterKillSwitchConfiguration.java
deleted file mode 100644
index bd906bc22335..000000000000
--- a/core/src/main/java/jenkins/security/s2m/MasterKillSwitchConfiguration.java
+++ /dev/null
@@ -1,70 +0,0 @@
-package jenkins.security.s2m;
-
-import edu.umd.cs.findbugs.annotations.NonNull;
-import hudson.Extension;
-import javax.inject.Inject;
-import jenkins.model.GlobalConfiguration;
-import jenkins.model.GlobalConfigurationCategory;
-import jenkins.model.Jenkins;
-import net.sf.json.JSONObject;
-import org.kohsuke.stapler.StaplerRequest;
-
-/**
- * Exposes {@code AdminWhitelistRule#masterKillSwitch} to the admin.
- *
- * @author Kohsuke Kawaguchi
- * @since 1.587 / 1.580.1
- */
-@Extension
-public class MasterKillSwitchConfiguration extends GlobalConfiguration {
- @Inject
- AdminWhitelistRule rule;
-
- @Inject
- Jenkins jenkins;
-
- @Override
- public @NonNull GlobalConfigurationCategory getCategory() {
- return GlobalConfigurationCategory.get(GlobalConfigurationCategory.Security.class);
- }
-
- /**
- * @deprecated Use {@link #getAgentToControllerAccessControl()} instead
- */
- @Deprecated
- public boolean getMasterToSlaveAccessControl() {
- return getAgentToControllerAccessControl();
- }
-
- /**
- * @since 2.310
- */
- public boolean getAgentToControllerAccessControl() {
- return !rule.getMasterKillSwitch();
- }
-
- @Override
- public boolean configure(StaplerRequest req, JSONObject json) throws FormException {
- if (isRelevant()) {
- // don't record on/off unless this becomes relevant, so that we can differentiate
- // those who have disabled vs those who haven't cared.
- rule.setMasterKillSwitch(!json.has("agentToControllerAccessControl"));
- }
- return true;
- }
-
- /**
- * Returns true if the configuration of this subsystem is relevant.
- *
- *
Historically, this was only shown when "security" (authn/authz) was enabled.
- * That missed the use case of trusted local networks and Jenkins building public (untrusted) pull requests.
- * To be sure we're not missing another case where this option is useful, just show it always.
- */
- public boolean isRelevant() {
- /*
- * TODO Consider restricting this again to something like:
- * return !jenkins.clouds.isEmpty() || !jenkins.getNodes().isEmpty();
- */
- return true;
- }
-}
diff --git a/core/src/main/java/jenkins/security/s2m/MasterKillSwitchWarning.java b/core/src/main/java/jenkins/security/s2m/MasterKillSwitchWarning.java
deleted file mode 100644
index 10f142f6db3f..000000000000
--- a/core/src/main/java/jenkins/security/s2m/MasterKillSwitchWarning.java
+++ /dev/null
@@ -1,50 +0,0 @@
-package jenkins.security.s2m;
-
-import hudson.Extension;
-import hudson.model.AdministrativeMonitor;
-import java.io.IOException;
-import javax.inject.Inject;
-import org.kohsuke.stapler.HttpResponse;
-import org.kohsuke.stapler.HttpResponses;
-import org.kohsuke.stapler.QueryParameter;
-import org.kohsuke.stapler.interceptor.RequirePOST;
-
-/**
- * If {@link AdminWhitelistRule#masterKillSwitch} is on, warn the user.
- *
- * @author Kohsuke Kawaguchi
- * @since 1.587 / 1.580.1
- */
-@Extension
-public class MasterKillSwitchWarning extends AdministrativeMonitor {
- @Inject
- AdminWhitelistRule rule;
-
- @Inject
- MasterKillSwitchConfiguration config;
-
- @Override
- public boolean isActivated() {
- return rule.getMasterKillSwitch() && config.isRelevant();
- }
-
- @Override
- public boolean isSecurity() {
- return true;
- }
-
- @Override
- public String getDisplayName() {
- return Messages.MasterKillSwitchWarning_DisplayName();
- }
-
- @RequirePOST
- public HttpResponse doAct(@QueryParameter String dismiss) throws IOException {
- if(dismiss!=null) {
- disable(true);
- return HttpResponses.redirectViaContextPath("/manage");
- } else {
- return HttpResponses.redirectViaContextPath("configureSecurity");
- }
- }
-}
diff --git a/core/src/main/java/jenkins/security/s2m/OpMatcher.java b/core/src/main/java/jenkins/security/s2m/OpMatcher.java
deleted file mode 100644
index b78f1fc62316..000000000000
--- a/core/src/main/java/jenkins/security/s2m/OpMatcher.java
+++ /dev/null
@@ -1,15 +0,0 @@
-package jenkins.security.s2m;
-
-import java.io.File;
-import jenkins.ReflectiveFilePathFilter;
-
-/**
- * Tests a match against file operation name of {@link ReflectiveFilePathFilter#op(String, File)}.
- *
- * @author Kohsuke Kawaguchi
- */
-interface OpMatcher {
- boolean matches(String op);
-
- OpMatcher ALL = op -> true;
-}
diff --git a/core/src/main/java/jenkins/security/s2m/RejectedCallable.java b/core/src/main/java/jenkins/security/s2m/RejectedCallable.java
deleted file mode 100644
index 42cb5b515b4f..000000000000
--- a/core/src/main/java/jenkins/security/s2m/RejectedCallable.java
+++ /dev/null
@@ -1,21 +0,0 @@
-package jenkins.security.s2m;
-
-import edu.umd.cs.findbugs.annotations.CheckForNull;
-import hudson.PluginWrapper;
-import jenkins.model.Jenkins;
-
-/**
-* @author Kohsuke Kawaguchi
-*/
-public /*for Jelly*/ class RejectedCallable {
- public final Class clazz;
-
- /*package*/ RejectedCallable(Class clazz) {
- this.clazz = clazz;
- }
-
- public @CheckForNull
- PluginWrapper getPlugin() {
- return Jenkins.get().pluginManager.whichPlugin(clazz);
- }
-}
diff --git a/core/src/main/java/jenkins/security/s2m/RunningBuildFilePathFilter.java b/core/src/main/java/jenkins/security/s2m/RunningBuildFilePathFilter.java
deleted file mode 100644
index 5e4d88757e75..000000000000
--- a/core/src/main/java/jenkins/security/s2m/RunningBuildFilePathFilter.java
+++ /dev/null
@@ -1,152 +0,0 @@
-/*
- * The MIT License
- *
- * Copyright 2021 CloudBees, Inc.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-package jenkins.security.s2m;
-
-import edu.umd.cs.findbugs.annotations.CheckForNull;
-import edu.umd.cs.findbugs.annotations.Nullable;
-import hudson.Extension;
-import hudson.model.Computer;
-import hudson.model.Executor;
-import hudson.model.Queue;
-import hudson.model.Run;
-import hudson.remoting.ChannelBuilder;
-import java.io.File;
-import java.io.IOException;
-import java.nio.file.Path;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-import java.util.regex.Pattern;
-import jenkins.ReflectiveFilePathFilter;
-import jenkins.model.Jenkins;
-import jenkins.security.ChannelConfigurator;
-import jenkins.util.SystemProperties;
-import org.kohsuke.accmod.Restricted;
-import org.kohsuke.accmod.restrictions.NoExternalUse;
-
-/**
- * When an agent tries to access build directories on the controller, limit it to those for builds running on that agent.
- *
- * @since 2.319
- */
-@Restricted(NoExternalUse.class)
-public class RunningBuildFilePathFilter extends ReflectiveFilePathFilter {
-
- /**
- * By default, unauthorized accesses will result in a {@link SecurityException}.
- * If this is set to {@code false}, instead just log a warning.
- */
- private static final String FAIL_PROPERTY = RunningBuildFilePathFilter.class.getName() + ".FAIL";
-
- /**
- * Disables this filter entirely.
- */
- private static final String SKIP_PROPERTY = RunningBuildFilePathFilter.class.getName() + ".SKIP";
-
- private static final Logger LOGGER = Logger.getLogger(RunningBuildFilePathFilter.class.getName());
-
- private final Object context;
-
- public RunningBuildFilePathFilter(Object context) {
- this.context = context;
- }
-
- @Override
- protected boolean op(String name, File path) throws SecurityException {
- if (SystemProperties.getBoolean(SKIP_PROPERTY)) {
- LOGGER.log(Level.FINE, () -> "Skipping check for '" + name + "' on '" + path + "'");
- return false;
- }
-
- final Jenkins jenkins = Jenkins.get();
-
- String patternString;
- try {
- patternString = Jenkins.expandVariablesForDirectory(jenkins.getRawBuildsDir(), "(.+)", "\\Q" + Jenkins.get().getRootDir().getCanonicalPath().replace('\\', '/') + "\\E/jobs/(.+)") + "/[0-9]+(/.*)?";
- } catch (IOException e) {
- LOGGER.log(Level.WARNING, "Failed to obtain canonical path to Jenkins home directory", e);
- throw new SecurityException("Failed to obtain canonical path"); // Minimal details
- }
- final Pattern pattern = Pattern.compile(patternString);
-
- String absolutePath;
- try {
- absolutePath = path.getCanonicalPath().replace('\\', '/');
- } catch (IOException e) {
- LOGGER.log(Level.WARNING, "Failed to obtain canonical path to '" + path + "'", e);
- throw new SecurityException("Failed to obtain canonical path"); // Minimal details
- }
- if (!pattern.matcher(absolutePath).matches()) {
- /* This is not a build directory, so another filter will take care of it */
- LOGGER.log(Level.FINE, "Not a build directory, so skipping: " + absolutePath);
- return false;
- }
-
- if (!(context instanceof Computer)) {
- LOGGER.warning(() -> "Unrecognized context " + context + " rejected for " + name + " on " + path);
- throw new SecurityException("Failed to discover context of access to build directory"); // Minimal details
- }
- Computer c = (Computer) context;
- final Path thePath = path.getAbsoluteFile().toPath();
- for (Executor executor : c.getExecutors()) {
- Run, ?> build = findRun(executor.getCurrentExecutable());
- if (build == null) {
- continue;
- }
- final Path buildDir = build.getRootDir().getAbsoluteFile().toPath();
- // If the directory being accessed is for a build currently running on this node, allow it
- if (thePath.startsWith(buildDir)) {
- return false;
- }
- }
-
- final String computerName = c.getName();
- if (SystemProperties.getBoolean(FAIL_PROPERTY, true)) {
- // This filter can only prohibit by throwing a SecurityException; it never allows on its own.
- LOGGER.log(Level.WARNING, "Rejecting unexpected agent-to-controller file path access: Agent '" + computerName + "' is attempting to access '" + absolutePath + "' using operation '" + name + "'. Learn more: https://www.jenkins.io/redirect/security-144/");
- throw new SecurityException("Agent tried to access build directory of a build not currently running on this system. Learn more: https://www.jenkins.io/redirect/security-144/");
- } else {
- LOGGER.log(Level.WARNING, "Unexpected agent-to-controller file path access: Agent '" + computerName + "' is accessing '" + absolutePath + "' using operation '" + name + "'. Learn more: https://www.jenkins.io/redirect/security-144/");
- return false;
- }
- }
-
- private static @CheckForNull Run, ?> findRun(@CheckForNull Queue.Executable exec) {
- if (exec == null) {
- return null;
- } else if (exec instanceof Run) {
- return (Run) exec;
- } else {
- return findRun(exec.getParentExecutable());
- }
- }
-
- @Extension
- public static class ChannelConfiguratorImpl extends ChannelConfigurator {
- @Override
- public void onChannelBuilding(ChannelBuilder builder, @Nullable Object context) {
- new RunningBuildFilePathFilter(context).installTo(builder, 150.0);
- }
- }
-}
diff --git a/core/src/main/java/jenkins/security/s2m/package-info.java b/core/src/main/java/jenkins/security/s2m/package-info.java
index 0339747c1692..03e604c5be4b 100644
--- a/core/src/main/java/jenkins/security/s2m/package-info.java
+++ b/core/src/main/java/jenkins/security/s2m/package-info.java
@@ -1,4 +1,4 @@
/**
- * Agent → master security.
+ * Agent → controller security.
*/
package jenkins.security.s2m;
diff --git a/core/src/main/java/jenkins/slaves/NioChannelSelector.java b/core/src/main/java/jenkins/slaves/NioChannelSelector.java
index fbf2c0597421..80cda778c44c 100644
--- a/core/src/main/java/jenkins/slaves/NioChannelSelector.java
+++ b/core/src/main/java/jenkins/slaves/NioChannelSelector.java
@@ -46,7 +46,7 @@ public void cleanUp() throws IOException {
/**
* Escape hatch to disable use of NIO.
*/
- public static boolean DISABLED = SystemProperties.getBoolean(NioChannelSelector.class.getName()+".disabled");
+ static boolean DISABLED = SystemProperties.getBoolean(NioChannelSelector.class.getName()+".disabled");
private static final Logger LOGGER = Logger.getLogger(NioChannelSelector.class.getName());
}
diff --git a/core/src/main/java/jenkins/slaves/restarter/UnixSlaveRestarter.java b/core/src/main/java/jenkins/slaves/restarter/UnixSlaveRestarter.java
index d0114c85fa8b..8b7aeeefbdf5 100644
--- a/core/src/main/java/jenkins/slaves/restarter/UnixSlaveRestarter.java
+++ b/core/src/main/java/jenkins/slaves/restarter/UnixSlaveRestarter.java
@@ -13,6 +13,7 @@
import hudson.Extension;
import java.io.File;
import java.io.IOException;
+import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.logging.Logger;
import jenkins.util.JavaVMArguments;
@@ -107,7 +108,7 @@ private static String resolveSymlink(File link) throws IOException {
byte[] buf = new byte[r];
m.read(0, buf, 0, r);
- return new String(buf);
+ return new String(buf, StandardCharsets.UTF_8);
}
throw new IOException("Failed to readlink " + link);
diff --git a/core/src/main/java/jenkins/telemetry/impl/SlaveToMasterFileCallableUsage.java b/core/src/main/java/jenkins/telemetry/impl/SlaveToMasterFileCallableUsage.java
deleted file mode 100644
index 2c577d64f5af..000000000000
--- a/core/src/main/java/jenkins/telemetry/impl/SlaveToMasterFileCallableUsage.java
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * The MIT License
- *
- * Copyright 2021 CloudBees, Inc.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-package jenkins.telemetry.impl;
-
-import hudson.Extension;
-import hudson.Functions;
-import java.time.LocalDate;
-import java.util.Map;
-import java.util.Set;
-import java.util.TreeMap;
-import java.util.TreeSet;
-import java.util.regex.Matcher;
-import jenkins.SlaveToMasterFileCallable;
-import jenkins.security.s2m.DefaultFilePathFilter;
-import jenkins.telemetry.Telemetry;
-import net.sf.json.JSONObject;
-import org.kohsuke.accmod.Restricted;
-import org.kohsuke.accmod.restrictions.NoExternalUse;
-
-/**
- * Records when {@link DefaultFilePathFilter} found {@link SlaveToMasterFileCallable} or similar being used.
- */
-@Extension
-@Restricted(NoExternalUse.class)
-public class SlaveToMasterFileCallableUsage extends Telemetry {
-
- private Set traces = new TreeSet<>();
-
- @Override
- public String getDisplayName() {
- return "Access to files on controllers from code running on an agent";
- }
-
- @Override
- public LocalDate getStart() {
- return LocalDate.of(2021, 11, 4); // https://www.jenkins.io/security/advisory/2021-11-04/
- }
-
- @Override
- public LocalDate getEnd() {
- return LocalDate.of(2022, 3, 1);
- }
-
- @Override
- public synchronized JSONObject createContent() {
- Map info = new TreeMap<>();
- info.put("traces", traces);
- info.put("components", buildComponentInformation());
- JSONObject json = JSONObject.fromObject(info);
- traces.clear();
- return json;
- }
-
- public synchronized void recordTrace(Throwable trace) {
- traces.add(generalize(Functions.printThrowable(trace)));
- }
-
- protected static String generalize(String trace) {
- return trace
- .replaceAll("@[a-f0-9]+", "@…")
- .replaceAll("]\\([0-9]+\\) created at", "](…) created at")
- .replaceAll("com[.]sun[.]proxy[.][$]Proxy[0-9]+[.]", Matcher.quoteReplacement("com.sun.proxy.$Proxy…."));
- }
-}
diff --git a/core/src/main/java/jenkins/util/DirectedGraph.java b/core/src/main/java/jenkins/util/DirectedGraph.java
index 916e49423413..cc781cabc7ab 100644
--- a/core/src/main/java/jenkins/util/DirectedGraph.java
+++ b/core/src/main/java/jenkins/util/DirectedGraph.java
@@ -1,5 +1,6 @@
package jenkins.util;
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.util.AbstractSet;
import java.util.ArrayList;
import java.util.Collection;
@@ -76,6 +77,7 @@ class Node {
*/
int lowlink;
+ @SuppressFBWarnings(value = "URF_UNREAD_FIELD", justification = "no big deal")
SCC scc;
Node(N n) {
diff --git a/core/src/main/java/jenkins/util/JSONSignatureValidator.java b/core/src/main/java/jenkins/util/JSONSignatureValidator.java
index 274c0cb96cd9..a6bd08c91a42 100644
--- a/core/src/main/java/jenkins/util/JSONSignatureValidator.java
+++ b/core/src/main/java/jenkins/util/JSONSignatureValidator.java
@@ -1,5 +1,6 @@
package jenkins.util;
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import hudson.util.FormValidation;
import java.io.ByteArrayInputStream;
import java.io.File;
@@ -52,6 +53,7 @@ public JSONSignatureValidator(String name) {
/**
* Verifies the signature in the update center data file.
*/
+ @SuppressFBWarnings(value = "WEAK_MESSAGE_DIGEST_SHA1", justification = "SHA-1 is only used as a fallback if SHA-512 is not available")
public FormValidation verifySignature(JSONObject o) throws IOException {
try {
FormValidation warning = null;
@@ -239,7 +241,7 @@ private boolean verifySignature(Signature signature, String providedSignature) {
* Utility method supporting both possible digest formats: Base64 and Hex
*/
private boolean digestMatches(byte[] digest, String providedDigest) {
- return providedDigest.equalsIgnoreCase(Hex.encodeHexString(digest)) || providedDigest.equalsIgnoreCase(new String(Base64.getEncoder().encode(digest)));
+ return providedDigest.equalsIgnoreCase(Hex.encodeHexString(digest)) || providedDigest.equalsIgnoreCase(Base64.getEncoder().encodeToString(digest));
}
diff --git a/core/src/main/java/jenkins/util/groovy/GroovyHookScript.java b/core/src/main/java/jenkins/util/groovy/GroovyHookScript.java
index cf39bffbaba9..178ae3cb9b82 100644
--- a/core/src/main/java/jenkins/util/groovy/GroovyHookScript.java
+++ b/core/src/main/java/jenkins/util/groovy/GroovyHookScript.java
@@ -3,6 +3,7 @@
import static java.util.logging.Level.WARNING;
import edu.umd.cs.findbugs.annotations.NonNull;
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import groovy.lang.Binding;
import groovy.lang.GroovyCodeSource;
import groovy.lang.GroovyShell;
@@ -129,6 +130,7 @@ protected void execute(File f) {
}
}
+ @SuppressFBWarnings(value = "GROOVY_SHELL", justification = "Groovy hook scripts are a feature, not a bug")
protected void execute(GroovyCodeSource s) {
try {
createShell().evaluate(s);
diff --git a/core/src/main/java/jenkins/util/io/FileBoolean.java b/core/src/main/java/jenkins/util/io/FileBoolean.java
index 83ab8a507ac4..9963150d7f50 100644
--- a/core/src/main/java/jenkins/util/io/FileBoolean.java
+++ b/core/src/main/java/jenkins/util/io/FileBoolean.java
@@ -1,5 +1,6 @@
package jenkins.util.io;
+import hudson.Util;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
@@ -59,7 +60,7 @@ public void set(boolean b) {
public void on() {
try {
- Files.createDirectories(file.getParentFile().toPath());
+ Util.createDirectories(file.getParentFile().toPath());
Files.newOutputStream(file.toPath()).close();
get(); // update state
} catch (IOException | InvalidPathException e) {
diff --git a/core/src/main/java/jenkins/util/io/PathRemover.java b/core/src/main/java/jenkins/util/io/PathRemover.java
index 8a7d8b299ffc..bf8ff997d4e4 100644
--- a/core/src/main/java/jenkins/util/io/PathRemover.java
+++ b/core/src/main/java/jenkins/util/io/PathRemover.java
@@ -214,6 +214,7 @@ private List tryRemoveRecursive(@NonNull Path path) {
return accumulatedErrors;
}
+ @SuppressFBWarnings(value = "RCN_REDUNDANT_NULLCHECK_WOULD_HAVE_BEEN_A_NPE", justification = "https://github.com/spotbugs/spotbugs/issues/756")
private List tryRemoveDirectoryContents(@NonNull Path path) {
Path normalized = path.normalize();
List accumulatedErrors = new ArrayList<>();
@@ -228,6 +229,7 @@ private List tryRemoveDirectoryContents(@NonNull Path path) {
return accumulatedErrors;
}
+ @SuppressFBWarnings(value = "RCN_REDUNDANT_NULLCHECK_WOULD_HAVE_BEEN_A_NPE", justification = "https://github.com/spotbugs/spotbugs/issues/756")
private void removeOrMakeRemovableThenRemove(@NonNull Path path) throws IOException {
pathChecker.check(path);
try {
diff --git a/core/src/main/java/jenkins/util/xml/XMLUtils.java b/core/src/main/java/jenkins/util/xml/XMLUtils.java
index 0eac9169269c..c0d59bded42e 100644
--- a/core/src/main/java/jenkins/util/xml/XMLUtils.java
+++ b/core/src/main/java/jenkins/util/xml/XMLUtils.java
@@ -1,6 +1,7 @@
package jenkins.util.xml;
import edu.umd.cs.findbugs.annotations.NonNull;
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
@@ -109,6 +110,7 @@ public static void safeTransform(@NonNull Source source, @NonNull Result out) th
* @throws IOException Error reading from the steam.
* @since 2.265
*/
+ @SuppressFBWarnings(value = "XXE_DOCUMENT", justification = "newDocumentBuilderFactory() does what FindSecBugs recommends, yet FindSecBugs cannot see this")
public static @NonNull Document parse(@NonNull InputStream stream) throws SAXException, IOException {
DocumentBuilder docBuilder;
@@ -133,6 +135,7 @@ public static void safeTransform(@NonNull Source source, @NonNull Result out) th
* @throws IOException Error reading from the steam.
* @since 2.0
*/
+ @SuppressFBWarnings(value = "XXE_DOCUMENT", justification = "newDocumentBuilderFactory() does what FindSecBugs recommends, yet FindSecBugs cannot see this")
public static @NonNull Document parse(@NonNull Reader stream) throws SAXException, IOException {
DocumentBuilder docBuilder;
diff --git a/core/src/main/java/org/jenkins/ui/icon/IconSet.java b/core/src/main/java/org/jenkins/ui/icon/IconSet.java
index f43b7a0e40ea..6880b3041d01 100644
--- a/core/src/main/java/org/jenkins/ui/icon/IconSet.java
+++ b/core/src/main/java/org/jenkins/ui/icon/IconSet.java
@@ -363,7 +363,6 @@ private static String toNormalizedIconUrl(String url) {
icons.addIcon(new Icon("icon-computer-flash icon-md", "24x24/computer-flash.gif", Icon.ICON_MEDIUM_STYLE));
icons.addIcon(new BuildStatusIcon("icon-disabled icon-md", "build-status/build-status-sprite.svg#last-disabled", Icon.ICON_MEDIUM_STYLE));
icons.addIcon(new BuildStatusIcon("icon-disabled-anime icon-md", "build-status/build-status-sprite.svg#last-disabled", Icon.ICON_MEDIUM_STYLE, true));
- icons.addIcon(new Icon("icon-document-properties icon-md", "24x24/document-properties.gif", Icon.ICON_MEDIUM_STYLE));
icons.addIcon(new Icon("icon-empty icon-md", "24x24/empty.gif", Icon.ICON_MEDIUM_STYLE));
icons.addIcon(new Icon("icon-green icon-md", "24x24/green.gif", Icon.ICON_MEDIUM_STYLE));
icons.addIcon(new Icon("icon-green-anime icon-md", "24x24/green_anime.gif", Icon.ICON_MEDIUM_STYLE));
@@ -380,7 +379,6 @@ private static String toNormalizedIconUrl(String url) {
icons.addIcon(new BuildStatusIcon("icon-red-anime icon-md", "build-status/build-status-sprite.svg#last-failed", Icon.ICON_MEDIUM_STYLE, true));
icons.addIcon(new BuildStatusIcon("icon-yellow icon-md", "build-status/build-status-sprite.svg#last-unstable", Icon.ICON_MEDIUM_STYLE));
icons.addIcon(new BuildStatusIcon("icon-yellow-anime icon-md", "build-status/build-status-sprite.svg#last-unstable", Icon.ICON_MEDIUM_STYLE, true));
- icons.addIcon(new Icon("icon-document-properties icon-md", "24x24/document-properties.png", Icon.ICON_MEDIUM_STYLE));
icons.addIcon(new Icon("icon-empty icon-md", "24x24/empty.png", Icon.ICON_MEDIUM_STYLE));
icons.addIcon(new Icon("icon-grey icon-md", "24x24/grey.png", Icon.ICON_MEDIUM_STYLE));
@@ -516,6 +514,7 @@ private static void initializeSVGs() {
images.add("user");
images.add("video");
images.add("warning");
+ images.add("document-properties");
Map materialIcons = new HashMap<>();
materialIcons.put("help", "svg-sprite-action-symbol.svg#ic_help_24px");
diff --git a/core/src/main/resources/hudson/Messages.properties b/core/src/main/resources/hudson/Messages.properties
index 67d9aaf1bd2a..2f0ca5b9f88e 100644
--- a/core/src/main/resources/hudson/Messages.properties
+++ b/core/src/main/resources/hudson/Messages.properties
@@ -124,11 +124,10 @@ ProxyConfiguration.Success=Success
Functions.NoExceptionDetails=No Exception details
PluginWrapper.missing=Plugin is missing: {0} ({1})
-PluginWrapper.failed_to_load_plugin=Failed to load: {0} ({1})
-PluginWrapper.failed_to_load_dependency=Failed to load: {0} ({1})
-PluginWrapper.disabledAndObsolete=Update required: {0} ({1}) to {2} or higher
-PluginWrapper.disabled=Required plugin is disabled: {0}
-PluginWrapper.obsolete=Update required: {0} ({1}) to be updated to {2} or higher
+PluginWrapper.failed_to_load_plugin_2=Failed to load: {0} ({1} {2})
+PluginWrapper.failed_to_load_dependency_2=Failed to load: {0} ({1} {2})
+PluginWrapper.disabled_2=Required plugin is disabled: {0} ({1})
+PluginWrapper.obsolete_2=Update required: {0} ({1} {2}) to be updated to {3} or higher
PluginWrapper.obsoleteCore=Jenkins ({1}) or higher required
PluginWrapper.obsoleteJava=Java ({1}) or higher required
PluginWrapper.PluginWrapperAdministrativeMonitor.DisplayName=Plugins Failed To Load
diff --git a/core/src/main/resources/hudson/PluginManager/installed.jelly b/core/src/main/resources/hudson/PluginManager/installed.jelly
index bb6f9bf8ec41..a2ca8a67ef02 100644
--- a/core/src/main/resources/hudson/PluginManager/installed.jelly
+++ b/core/src/main/resources/hudson/PluginManager/installed.jelly
@@ -128,6 +128,14 @@ THE SOFTWARE.
+
+
+
diff --git a/core/src/main/resources/hudson/PluginManager/installed.properties b/core/src/main/resources/hudson/PluginManager/installed.properties
index d1a35a9d3b98..fa5ecc5526a9 100644
--- a/core/src/main/resources/hudson/PluginManager/installed.properties
+++ b/core/src/main/resources/hudson/PluginManager/installed.properties
@@ -31,3 +31,4 @@ securityWarning=\
adoptThisPlugin=\
This plugin is up for adoption! We are looking for new maintainers. \
Visit our Adopt a Plugin initiative for more information.
+reportIssue=Report an issue with this plugin
diff --git a/core/src/main/resources/hudson/model/BooleanParameterDefinition/help-boolean-default_ru.html b/core/src/main/resources/hudson/model/BooleanParameterDefinition/help-boolean-default_ru.html
new file mode 100644
index 000000000000..faf96921432f
--- /dev/null
+++ b/core/src/main/resources/hudson/model/BooleanParameterDefinition/help-boolean-default_ru.html
@@ -0,0 +1,3 @@
+
+ Задает значение поля по умолчанию.
+
\ No newline at end of file
diff --git a/core/src/main/resources/hudson/model/RunParameterDefinition/config_ru.properties b/core/src/main/resources/hudson/model/RunParameterDefinition/config_ru.properties
index f8ff0f56ce70..46cd35fdf206 100644
--- a/core/src/main/resources/hudson/model/RunParameterDefinition/config_ru.properties
+++ b/core/src/main/resources/hudson/model/RunParameterDefinition/config_ru.properties
@@ -21,3 +21,10 @@
# THE SOFTWARE.
Project=\u041F\u0440\u043E\u0435\u043A\u0442
+Description=\u041e\u043f\u0438\u0441\u0430\u043d\u0438\u0435
+Completed\ Builds\ Only=\u0422\u043e\u043b\u044c\u043a\u043e \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u043d\u044b\u0435 \u0441\u0431\u043e\u0440\u043a\u0438
+All\ Builds=\u0412\u0441\u0435 \u0441\u0431\u043e\u0440\u043a\u0438
+Name=\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435
+Filter=\u0424\u0438\u043b\u044c\u0442\u0440
+Stable\ Builds\ Only=\u0422\u043e\u043b\u044c\u043a\u043e \u0441\u0442\u0430\u0431\u0438\u043b\u044c\u043d\u044b\u0435 \u0441\u0431\u043e\u0440\u043a\u0438
+Successful\ Builds\ Only=\u0422\u043e\u043b\u044c\u043a\u043e \u0443\u0441\u043f\u0435\u0448\u043d\u044b\u0435 \u0441\u0431\u043e\u0440\u043a\u0438
\ No newline at end of file
diff --git a/core/src/main/resources/jenkins/formelementpath/form-element-path.js b/core/src/main/resources/jenkins/formelementpath/form-element-path.js
index 0bba9171438e..6c512faeea86 100644
--- a/core/src/main/resources/jenkins/formelementpath/form-element-path.js
+++ b/core/src/main/resources/jenkins/formelementpath/form-element-path.js
@@ -62,7 +62,8 @@ document.addEventListener("DOMContentLoaded", function(){
case "button":
var element
// modern buttons aren't wrapped in spans
- if (e.classList.contains('jenkins-button')) {
+ if (e.classList.contains('jenkins-button') || e.classList.contains('repeatable-delete')) {
+ p = findParent(e);
element = e
} else {
p = findParent(e);
diff --git a/core/src/main/resources/jenkins/model/Messages_ru.properties b/core/src/main/resources/jenkins/model/Messages_ru.properties
index 5fe1b314824c..255a60bca237 100644
--- a/core/src/main/resources/jenkins/model/Messages_ru.properties
+++ b/core/src/main/resources/jenkins/model/Messages_ru.properties
@@ -55,3 +55,4 @@ PatternProjectNamingStrategy.NamePatternInvalidSyntax=\u041D\u0435\u043A\u043E\u
ParameterizedJobMixIn.build_with_parameters=\u0421\u043E\u0431\u0440\u0430\u0442\u044C \u0441 \u043F\u0430\u0440\u0430\u043C\u0435\u0442\u0440\u0430\u043C\u0438
CLI.disable-job.shortDescription=\u0417\u0430\u043F\u0440\u0435\u0442\u0438\u0442\u044C \u0437\u0430\u0434\u0430\u0447\u0443
CLI.enable-job.shortDescription=\u0420\u0430\u0437\u0440\u0435\u0448\u0438\u0442\u044C \u0437\u0430\u0434\u0430\u0447\u0443
+NewViewLink.NewView=\u041d\u043e\u0432\u043e\u0435\u0020\u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u0435
\ No newline at end of file
diff --git a/core/src/main/resources/jenkins/security/s2m/AdminCallableMonitor/message.jelly b/core/src/main/resources/jenkins/security/s2m/AdminCallableMonitor/message.jelly
deleted file mode 100644
index f599db7a1b31..000000000000
--- a/core/src/main/resources/jenkins/security/s2m/AdminCallableMonitor/message.jelly
+++ /dev/null
@@ -1,34 +0,0 @@
-
-
-
-
-
-
- ${%blurb}
-
-
diff --git a/core/src/main/resources/jenkins/security/s2m/AdminCallableMonitor/message.properties b/core/src/main/resources/jenkins/security/s2m/AdminCallableMonitor/message.properties
deleted file mode 100644
index 89d5bfb1799b..000000000000
--- a/core/src/main/resources/jenkins/security/s2m/AdminCallableMonitor/message.properties
+++ /dev/null
@@ -1,2 +0,0 @@
-blurb=Jenkins has rejected some commands from agents, because we are unsure if it is open for agents to execute, \
- but this rejection has probably broken some builds. You should examine the situation to see if they should be whitelisted.
diff --git a/core/src/main/resources/jenkins/security/s2m/AdminCallableMonitor/message_bg.properties b/core/src/main/resources/jenkins/security/s2m/AdminCallableMonitor/message_bg.properties
deleted file mode 100644
index 190c24f3282b..000000000000
--- a/core/src/main/resources/jenkins/security/s2m/AdminCallableMonitor/message_bg.properties
+++ /dev/null
@@ -1,32 +0,0 @@
-# The MIT License
-#
-# Bulgarian translation: Copyright (c) 2016, Alexander Shopov
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-
-Dismiss=\
- \u041e\u0442\u043a\u0430\u0437
-# Jenkins has rejected some commands from agents, because we are unsure if it is open for agents to execute, \
-# but this rejection has probably broken some builds. You should examine the situation to see if they should be whitelisted.
-blurb=\
- Jenkins \u043e\u0442\u0445\u0432\u044a\u0440\u043b\u0438 \u0447\u0430\u0441\u0442 \u043e\u0442 \u043a\u043e\u043c\u0430\u043d\u0434\u0438\u0442\u0435 \u043e\u0442 \u0430\u0433\u0435\u043d\u0442\u0438\u0442\u0435, \u0437\u0430\u0449\u043e\u0442\u043e \u043d\u0435 \u0435 \u044f\u0441\u043d\u043e \u0434\u0430\u043b\u0438 \u043c\u043e\u0436\u0435 \u0434\u0430\
- \u0433\u0438 \u043f\u0440\u0438\u0435\u043c\u0430. \u0412\u044a\u0437\u043c\u043e\u0436\u043d\u043e \u0435 \u0442\u043e\u0432\u0430 \u0434\u0430 \u0435 \u0441\u0447\u0443\u043f\u0438\u043b\u043e \u0447\u0430\u0441\u0442 \u043e\u0442 \u0438\u0437\u0433\u0440\u0430\u0436\u0434\u0430\u043d\u0438\u044f\u0442\u0430. \u041f\u0440\u0435\u0433\u043b\u0435\u0434\u0430\u0439\u0442\u0435 \u0433\u0438,\
- \u0437\u0430 \u0434\u0430 \u0440\u0435\u0448\u0438\u0442\u0435 \u0434\u0430\u043b\u0438 \u0438\u0437\u0440\u0438\u0447\u043d\u043e \u0434\u0430 \u043d\u0435 \u0433\u0438 \u043f\u043e\u0437\u0432\u043e\u043b\u0438\u0442\u0435.
-Examine=\
- \u041f\u0440\u0435\u0433\u043b\u0435\u0434
diff --git a/core/src/main/resources/jenkins/security/s2m/AdminCallableMonitor/message_de.properties b/core/src/main/resources/jenkins/security/s2m/AdminCallableMonitor/message_de.properties
deleted file mode 100644
index dd2a59ad1aee..000000000000
--- a/core/src/main/resources/jenkins/security/s2m/AdminCallableMonitor/message_de.properties
+++ /dev/null
@@ -1,26 +0,0 @@
-# The MIT License
-#
-# Copyright (c) 2017 Daniel Beck and a number of other of contributors
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-
-Dismiss=Schlie\u00DFen
-Examine=Pr\u00FCfen
-blurb=Jenkins hat Befehle von Agenten aufgrund der aktuellen Einstellungen abgelehnt. Dadurch sind vermutlich Builds fehlgeschlagen. \
- Es wird empfohlen, die Situation zu pr\u00FCfen, und die abgelehnten Befehle ggf. in die Positivliste aufzunehmen.
diff --git a/core/src/main/resources/jenkins/security/s2m/AdminCallableMonitor/message_it.properties b/core/src/main/resources/jenkins/security/s2m/AdminCallableMonitor/message_it.properties
deleted file mode 100644
index 1c88cf915c28..000000000000
--- a/core/src/main/resources/jenkins/security/s2m/AdminCallableMonitor/message_it.properties
+++ /dev/null
@@ -1,29 +0,0 @@
-# The MIT License
-#
-# Italian localization plugin for Jenkins
-# Copyright 2020 Alessandro Menti
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-
-blurb=Jenkins ha rifiutato alcuni comandi dagli agenti perch non certo \
- che siano liberamente eseguibili da questi, ma tale rifiuto probabilmente \
- ha mandato in errore alcune compilazioni. Si dovrebbe esaminare la \
- situazione per verificare se sia possibile aggiungerli alla whitelist.
-Dismiss=Nascondi
-Examine=Esamina
diff --git a/core/src/main/resources/jenkins/security/s2m/AdminCallableMonitor/message_pt_BR.properties b/core/src/main/resources/jenkins/security/s2m/AdminCallableMonitor/message_pt_BR.properties
deleted file mode 100644
index 25a215256fc0..000000000000
--- a/core/src/main/resources/jenkins/security/s2m/AdminCallableMonitor/message_pt_BR.properties
+++ /dev/null
@@ -1,24 +0,0 @@
-# The MIT License
-#
-# Copyright (c) 2004-, Kohsuke Kawaguchi, Sun Microsystems, Inc., and a number of other of contributers
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-
-Examine=
-Dismiss=
diff --git a/core/src/main/resources/jenkins/security/s2m/AdminCallableMonitor/message_sr.properties b/core/src/main/resources/jenkins/security/s2m/AdminCallableMonitor/message_sr.properties
deleted file mode 100644
index e2deed27ee89..000000000000
--- a/core/src/main/resources/jenkins/security/s2m/AdminCallableMonitor/message_sr.properties
+++ /dev/null
@@ -1,6 +0,0 @@
-# This file is under the MIT License by authors
-
-Examine=\u041F\u0440\u0435\u0433\u043B\u0435\u0434\u0430\u0458
-Dismiss=\u041E\u0442\u043A\u0430\u0436\u0438
-blurb=Jenkins \u0458\u0435 \u043E\u0434\u0431\u0438\u0458\u043E \u043D\u0435\u043A\u0435 \u0430\u0433\u0435\u043D\u0442\u043E\u0432\u0435 \u043A\u043E\u043C\u0430\u043D\u0434\u0435, \u0437\u0430\u0448\u0442\u043E \u043D\u0438\u0458\u0435 \u0458\u0430\u0441\u043D\u043E \u0430\u043A\u043E \u0458\u0435 \u0434\u043E\u0437\u0432\u043E\u0459\u0435\u043D\u043E \u0430\u0433\u0435\u043D\u0442\u0438\u043C\u0430, \
- \u043C\u0435\u0452\u0443\u0442\u0438\u043C \u043E\u0432\u0430 \u043E\u043F\u0435\u0440\u0430\u0446\u0438\u0458\u0430 \u0458\u0435 \u0432\u0435\u0440\u043E\u0432\u0430\u0442\u043D\u043E \u043F\u0440\u0435\u043A\u0438\u043D\u0443\u043B\u0430 \u043D\u0435\u043A\u0430 \u0438\u0437\u0433\u0440\u0430\u0434\u045A\u0430. \u0420\u0430\u0437\u043C\u0438\u0441\u043B\u0438\u0442\u0435 \u0430\u043A\u043E \u0431\u0438 \u043C\u043E\u0433\u043E \u0434\u043E\u0437\u0432\u043E\u043B\u0438\u0442\u0438 \u0430\u0433\u0435\u043D\u0442\u0438\u043C\u0430.
diff --git a/core/src/main/resources/jenkins/security/s2m/AdminWhitelistRule/index.jelly b/core/src/main/resources/jenkins/security/s2m/AdminWhitelistRule/index.jelly
deleted file mode 100644
index 14dd6056c2d4..000000000000
--- a/core/src/main/resources/jenkins/security/s2m/AdminWhitelistRule/index.jelly
+++ /dev/null
@@ -1,113 +0,0 @@
-
-
-
-
-
-
-
${%Agent → Controller Access Control}
-
-
- The Jenkins controller is now more strict about what commands its agents can send to the controller.
- Unfortunately, this prevents some plugins from functioning correctly, as those plugins do not
- specify which commands are open for agents to execute and which ones are not.
- While plugin developers work on improving this,
- as an administrator, you can mark commands as OK for agents to execute.
-
- The agent → controller access control subsystem is currently disabled.
- This is unsafe if you have agents from other less trusted people.
- You can turn it back on from Global Security Configuration UI.
-
-
-
-
-
-
-
diff --git a/core/src/main/resources/jenkins/security/s2m/AdminWhitelistRule/index_bg.properties b/core/src/main/resources/jenkins/security/s2m/AdminWhitelistRule/index_bg.properties
deleted file mode 100644
index 79e58cf1d1a8..000000000000
--- a/core/src/main/resources/jenkins/security/s2m/AdminWhitelistRule/index_bg.properties
+++ /dev/null
@@ -1,28 +0,0 @@
-# The MIT License
-#
-# Bulgarian translation: Copyright (c) 2016, Alexander Shopov
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-
-Whitelist=\
- \u041e\u0434\u043e\u0431\u0440\u0435\u043d \u0441\u043f\u0438\u0441\u044a\u043a
-Update=\
- \u041e\u0431\u043d\u043e\u0432\u044f\u0432\u0430\u043d\u0435
-Agent\ &\#8594;\ Master\ Access\ Control=\
- \u0410\u0433\u0435\u043d\u0442 \u2192 \u0423\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u043d\u0430 \u0434\u043e\u0441\u0442\u044a\u043f\u0430
diff --git a/core/src/main/resources/jenkins/security/s2m/AdminWhitelistRule/index_de.properties b/core/src/main/resources/jenkins/security/s2m/AdminWhitelistRule/index_de.properties
deleted file mode 100644
index 0f7ba5afe444..000000000000
--- a/core/src/main/resources/jenkins/security/s2m/AdminWhitelistRule/index_de.properties
+++ /dev/null
@@ -1,25 +0,0 @@
-# The MIT License
-#
-# Copyright (c) 2017 Daniel Beck and a number of other of contributors
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-
-Whitelist=Positivliste (Whitelist)
-Agent\ &\#8594;\ Master\ Access\ Control=Zugangskontrolle Agent → Master
-Update=Aktualisieren
diff --git a/core/src/main/resources/jenkins/security/s2m/AdminWhitelistRule/index_it.properties b/core/src/main/resources/jenkins/security/s2m/AdminWhitelistRule/index_it.properties
deleted file mode 100644
index 0d5f667b67f0..000000000000
--- a/core/src/main/resources/jenkins/security/s2m/AdminWhitelistRule/index_it.properties
+++ /dev/null
@@ -1,26 +0,0 @@
-# The MIT License
-#
-# Italian localization plugin for Jenkins
-# Copyright 2020 Alessandro Menti
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-
-Agent\ &\#8594;\ Master\ Access\ Control=Controllo accessi agente→master
-Update=Aggiorna
-Whitelist=Whitelist
diff --git a/core/src/main/resources/jenkins/security/s2m/AdminWhitelistRule/index_ja.properties b/core/src/main/resources/jenkins/security/s2m/AdminWhitelistRule/index_ja.properties
deleted file mode 100644
index a4a88ca399f0..000000000000
--- a/core/src/main/resources/jenkins/security/s2m/AdminWhitelistRule/index_ja.properties
+++ /dev/null
@@ -1,24 +0,0 @@
-# The MIT License
-#
-# Copyright (c) 2004-, Kohsuke Kawaguchi, Sun Microsystems, Inc., and a number of other of contributers
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-
-Whitelist=\u30db\u30ef\u30a4\u30c8\u30ea\u30b9\u30c8
-Update=\u66f4\u65b0
diff --git a/core/src/main/resources/jenkins/security/s2m/AdminWhitelistRule/index_pt_BR.properties b/core/src/main/resources/jenkins/security/s2m/AdminWhitelistRule/index_pt_BR.properties
deleted file mode 100644
index 42d23f6efe16..000000000000
--- a/core/src/main/resources/jenkins/security/s2m/AdminWhitelistRule/index_pt_BR.properties
+++ /dev/null
@@ -1,24 +0,0 @@
-# The MIT License
-#
-# Copyright (c) 2004-2015, Seiji Sogabe
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-
-Whitelist=
-Update=
diff --git a/core/src/main/resources/jenkins/security/s2m/AdminWhitelistRule/index_sr.properties b/core/src/main/resources/jenkins/security/s2m/AdminWhitelistRule/index_sr.properties
deleted file mode 100644
index 605e8f9eda41..000000000000
--- a/core/src/main/resources/jenkins/security/s2m/AdminWhitelistRule/index_sr.properties
+++ /dev/null
@@ -1,5 +0,0 @@
-# This file is under the MIT License by authors
-
-Whitelist=
-Agent\ \u2192\ Master\ Access\ Control=
-Update=\u0410\u0436\u0443\u0440\u0438\u0440\u0430\u0458
diff --git a/core/src/main/resources/jenkins/security/s2m/MasterKillSwitchConfiguration/config.groovy b/core/src/main/resources/jenkins/security/s2m/MasterKillSwitchConfiguration/config.groovy
deleted file mode 100644
index 40d93c8fd986..000000000000
--- a/core/src/main/resources/jenkins/security/s2m/MasterKillSwitchConfiguration/config.groovy
+++ /dev/null
@@ -1,13 +0,0 @@
-package jenkins.security.s2m.MasterKillSwitchConfiguration
-
-def f=namespace(lib.FormTagLib)
-
-if (instance.isRelevant()) {
- f.section(title: _('Agent \u2192 Controller Security')) {
- f.optionalBlock(field: "agentToControllerAccessControl", title: _("Enable Agent \u2192 Controller Access Control")) {
- f.nested() {
- raw _("Rules can be tweaked here")
- }
- }
- }
-}
diff --git a/core/src/main/resources/jenkins/security/s2m/MasterKillSwitchConfiguration/config_it.properties b/core/src/main/resources/jenkins/security/s2m/MasterKillSwitchConfiguration/config_it.properties
deleted file mode 100644
index 5445c9b31d4e..000000000000
--- a/core/src/main/resources/jenkins/security/s2m/MasterKillSwitchConfiguration/config_it.properties
+++ /dev/null
@@ -1,29 +0,0 @@
-# The MIT License
-#
-# Italian localization plugin for Jenkins
-# Copyright 2020 Alessandro Menti
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-
-Agent\ \\u2192\ Master\ Security=Sicurezza agente\u2192master
-Enable\ Agent\ \\u2192\ Master\ Access\ Control=Abilita controllo accessi \
- agente\u2192master
-Rules\ can\ be\ tweaked\ here=\
- possibile personalizzare le regole qui
diff --git a/core/src/main/resources/jenkins/security/s2m/MasterKillSwitchConfiguration/help-agentToControllerAccessControl.html b/core/src/main/resources/jenkins/security/s2m/MasterKillSwitchConfiguration/help-agentToControllerAccessControl.html
deleted file mode 100644
index ab058fba3e8e..000000000000
--- a/core/src/main/resources/jenkins/security/s2m/MasterKillSwitchConfiguration/help-agentToControllerAccessControl.html
+++ /dev/null
@@ -1,4 +0,0 @@
-
- See Jenkins project website for discussion of this feature.
- We strongly recommend you enable this.
-
-
diff --git a/core/src/main/resources/jenkins/security/s2m/MasterKillSwitchWarning/message.properties b/core/src/main/resources/jenkins/security/s2m/MasterKillSwitchWarning/message.properties
deleted file mode 100644
index 04be9aaf9347..000000000000
--- a/core/src/main/resources/jenkins/security/s2m/MasterKillSwitchWarning/message.properties
+++ /dev/null
@@ -1,2 +0,0 @@
-blurb=Agent to controller security subsystem is currently off. \
- Please read the documentation and consider turning it on.
diff --git a/core/src/main/resources/jenkins/security/s2m/MasterKillSwitchWarning/message_bg.properties b/core/src/main/resources/jenkins/security/s2m/MasterKillSwitchWarning/message_bg.properties
deleted file mode 100644
index a8091c3b1914..000000000000
--- a/core/src/main/resources/jenkins/security/s2m/MasterKillSwitchWarning/message_bg.properties
+++ /dev/null
@@ -1,32 +0,0 @@
-# The MIT License
-#
-# Bulgarian translation: Copyright (c) 2016, 2017, Alexander Shopov
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-
-Dismiss=\
- \u041e\u0442\u043a\u0430\u0437
-Examine=\
- \u041f\u0440\u0435\u0433\u043b\u0435\u0434
-# Agent to master security subsystem is currently off. \
-# Please read the documentation and consider turning it on.
-blurb=\
- \u0410\u0433\u0435\u043d\u0442\u044a\u0442 \u0437\u0430 \u043e\u0441\u043d\u043e\u0432\u043d\u0430\u0442\u0430 \u043f\u043e\u0434\u0441\u0438\u0441\u0442\u0435\u043c\u0430 \u0437\u0430 \u0441\u0438\u0433\u0443\u0440\u043d\u043e\u0441\u0442\u0442\u0430 \u0435 \u0438\u0437\u043a\u043b\u044e\u0447\u0435\u043d \u0432 \u043c\u043e\u043c\u0435\u043d\u0442\u0430.\
- \u041f\u0440\u0435\u0433\u043b\u0435\u0434\u0430\u0439\u0442\u0435\
- \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044f\u0442\u0430 \u0438 \u0433\u043e \u0432\u043a\u043b\u044e\u0447\u0435\u0442\u0435.
diff --git a/core/src/main/resources/jenkins/security/s2m/MasterKillSwitchWarning/message_de.properties b/core/src/main/resources/jenkins/security/s2m/MasterKillSwitchWarning/message_de.properties
deleted file mode 100644
index 24f77098d88f..000000000000
--- a/core/src/main/resources/jenkins/security/s2m/MasterKillSwitchWarning/message_de.properties
+++ /dev/null
@@ -1,26 +0,0 @@
-# The MIT License
-#
-# Copyright (c) 2017 Daniel Beck and a number of other of contributors
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-
-Examine=Pr\u00FCfen
-Dismiss=Schlie\u00DFen
-blurb=Das Agent-Master-Sicherheits-Subsystem ist aktuell ausgeschaltet und sollte eingeschaltet werden. \
- Bitte lesen Sie die Dokumentation.
diff --git a/core/src/main/resources/jenkins/security/s2m/MasterKillSwitchWarning/message_it.properties b/core/src/main/resources/jenkins/security/s2m/MasterKillSwitchWarning/message_it.properties
deleted file mode 100644
index 37994252bcde..000000000000
--- a/core/src/main/resources/jenkins/security/s2m/MasterKillSwitchWarning/message_it.properties
+++ /dev/null
@@ -1,29 +0,0 @@
-# The MIT License
-#
-# Italian localization plugin for Jenkins
-# Copyright 2020 Alessandro Menti
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-
-blurb=Il sottosistema di sicurezza da agente a master attualmente \
- disabilitato. Si legga la documentazione e si prenda in \
- considerazione l''ipotesi di abilitarlo.
-Dismiss=Nascondi
-Examine=Esamina
diff --git a/core/src/main/resources/jenkins/security/s2m/MasterKillSwitchWarning/message_pt_BR.properties b/core/src/main/resources/jenkins/security/s2m/MasterKillSwitchWarning/message_pt_BR.properties
deleted file mode 100644
index 01a6d07f6f3d..000000000000
--- a/core/src/main/resources/jenkins/security/s2m/MasterKillSwitchWarning/message_pt_BR.properties
+++ /dev/null
@@ -1,26 +0,0 @@
-# The MIT License
-#
-# Copyright (c) 2004-, Kohsuke Kawaguchi, Sun Microsystems, Inc., and a number of other of contributers
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-
-Examine=
-Dismiss=
-blurb=O agente para o master security subsystem est\u00e1 desligado. \
- Por favor leia a documenta\u00e7\u00e3o e ligue novamente.
diff --git a/core/src/main/resources/jenkins/security/s2m/MasterKillSwitchWarning/message_sr.properties b/core/src/main/resources/jenkins/security/s2m/MasterKillSwitchWarning/message_sr.properties
deleted file mode 100644
index 9d3bdc19a71a..000000000000
--- a/core/src/main/resources/jenkins/security/s2m/MasterKillSwitchWarning/message_sr.properties
+++ /dev/null
@@ -1,6 +0,0 @@
-# This file is under the MIT License by authors
-
-Examine=
-Dismiss=\u041E\u0442\u043A\u0430\u0436\u0438
-blurb=\u0421\u0438\u0441\u0442\u0435\u043C \u043E\u0431\u0435\u0437\u0431\u0435\u0436\u0438\u0432\u0430\u045A\u0430 \u0432\u0435\u0437\u043E\u043C \u0438\u0437\u043C\u0435\u0452\u0443 \u0430\u0433\u0435\u043D\u0442\u0430 \u0438 \u043C\u0430\u0441\u0442\u0435\u0440\u0430 \u0458\u0435 \u0438\u0441\u043A\u0459\u0443\u0447\u0435\u043D. \
- \u041C\u043E\u043B\u0438\u043C\u043E \u0432\u0430\u0441, \u043F\u0440\u043E\u0447\u0438\u0442\u0430\u0458\u0442\u0435 \u0434\u043E\u043A\u043C\u0435\u043D\u0442\u0430\u0446\u0438\u0458\u0443 \u0438 \u0443\u043A\u0459\u0443\u0447\u0438\u0442\u0435 \u0430\u043A\u043E \u0432\u0430\u043C \u0458\u0435 \u043F\u043E\u0442\u0440\u0435\u0431\u043D\u043E.
diff --git a/core/src/main/resources/jenkins/security/s2m/MasterKillSwitchWarning/message_tr.properties b/core/src/main/resources/jenkins/security/s2m/MasterKillSwitchWarning/message_tr.properties
deleted file mode 100644
index 4a99fc431b8e..000000000000
--- a/core/src/main/resources/jenkins/security/s2m/MasterKillSwitchWarning/message_tr.properties
+++ /dev/null
@@ -1,24 +0,0 @@
-# The MIT License
-#
-# Copyright (c) 2021, Mustafa Ulu
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-
-Examine=Denetle
-Dismiss=Reddet
diff --git a/core/src/main/resources/jenkins/security/s2m/Messages.properties b/core/src/main/resources/jenkins/security/s2m/Messages.properties
deleted file mode 100644
index e2b2c28f3bd3..000000000000
--- a/core/src/main/resources/jenkins/security/s2m/Messages.properties
+++ /dev/null
@@ -1,2 +0,0 @@
-AdminCallableMonitor.DisplayName=Rejected Agent \u2192 Controller Access Attempt
-MasterKillSwitchWarning.DisplayName=Disabled Agent \u2192 Controller Access Control
diff --git a/core/src/main/resources/jenkins/security/s2m/Messages_bg.properties b/core/src/main/resources/jenkins/security/s2m/Messages_bg.properties
deleted file mode 100644
index 199b5d56ec12..000000000000
--- a/core/src/main/resources/jenkins/security/s2m/Messages_bg.properties
+++ /dev/null
@@ -1,28 +0,0 @@
-# The MIT License
-#
-# Bulgarian translation: Copyright (c) 2017, Alexander Shopov
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-
-# Rejected Agent \u2192 Controller Access Attempt
-AdminCallableMonitor.DisplayName=\
- \u041e\u0442\u0445\u0432\u044a\u0440\u043b\u0435\u043d \u043f\u043e\u0434\u0447\u0438\u043d\u0435\u043d \u043a\u043e\u043c\u043f\u044e\u0442\u044a\u0440 \u2014 \u043e\u043f\u0438\u0442 \u0437\u0430 \u0434\u043e\u0441\u0442\u044a\u043f \u0434\u043e \u043a\u043e\u043c\u0430\u043d\u0434\u043d\u0438\u044f \u043a\u043e\u043c\u043f\u044e\u0442\u044a\u0440
-# Disabled Agent \u2192 Controller Access Control
-MasterKillSwitchWarning.DisplayName=\
- \u0418\u0437\u043a\u043b\u044e\u0447\u0435\u043d \u043f\u043e\u0434\u0447\u0438\u043d\u0435\u043d \u043a\u043e\u043c\u043f\u044e\u0442\u044a\u0440 \u2014 \u043a\u043e\u043d\u0442\u0440\u043e\u043b \u043d\u0430 \u0434\u043e\u0441\u0442\u044a\u043f\u0430 \u0434\u043e \u043a\u043e\u043c\u0430\u043d\u0434\u043d\u0438\u044f \u043a\u043e\u043c\u043f\u044e\u0442\u044a\u0440
diff --git a/core/src/main/resources/jenkins/security/s2m/Messages_de.properties b/core/src/main/resources/jenkins/security/s2m/Messages_de.properties
deleted file mode 100644
index 162be628503e..000000000000
--- a/core/src/main/resources/jenkins/security/s2m/Messages_de.properties
+++ /dev/null
@@ -1,24 +0,0 @@
-# The MIT License
-#
-# Copyright (c) 2017 Daniel Beck and a number of other of contributors
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-
-AdminCallableMonitor.DisplayName=Zugriffsversuch Agent \u2192 Master abgelehnt
-MasterKillSwitchWarning.DisplayName=Sicherheitsfunktionen Agent \u2192 Master deaktiviert
diff --git a/core/src/main/resources/jenkins/security/s2m/Messages_it.properties b/core/src/main/resources/jenkins/security/s2m/Messages_it.properties
deleted file mode 100644
index 2c259e5b976a..000000000000
--- a/core/src/main/resources/jenkins/security/s2m/Messages_it.properties
+++ /dev/null
@@ -1,27 +0,0 @@
-# The MIT License
-#
-# Italian localization plugin for Jenkins
-# Copyright 2020 Alessandro Menti
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-
-AdminCallableMonitor.DisplayName=Tentativo di accesso agente\u2192master \
- rifiutato
-MasterKillSwitchWarning.DisplayName=Controllo accessi agente\u2192master \
- disabilitato
diff --git a/core/src/main/resources/jenkins/security/s2m/callable.conf b/core/src/main/resources/jenkins/security/s2m/callable.conf
deleted file mode 100644
index 440e02d19fc2..000000000000
--- a/core/src/main/resources/jenkins/security/s2m/callable.conf
+++ /dev/null
@@ -1,29 +0,0 @@
-# GENERATED FILE. DO NOT MODIFY.
-#
-# This file is for Jenkins core developers to list plugin callables which we believe are safe.
-#
-# To whitelist other names, place *.conf files by other names into this folder.
-# This file gets overwritten every time Jenkins starts.
-#
-# See https://www.jenkins.io/redirect/security-144 for more details.
-
-# maven plugin
-hudson.maven.MavenBuildProxy$Filter$AsyncInvoker
-
-# cloudbees-deployer plugin
-com.cloudbees.plugins.deployer.engines.Engine$FingerprintDecorator
-
-# subversion plugin
-hudson.scm.SubversionWorkspaceSelector$1
-
-# git-client plugin (prior to #147)
-org.jenkinsci.plugins.gitclient.CliGitAPIImpl$GetPrivateKeys
-
-# ssh-credentials plugin
-com.cloudbees.jenkins.plugins.sshcredentials.SSHAuthenticator$1
-
-# cygwin-process-killer plugin
-com.synopsys.arc.jenkinsci.plugins.cygwinprocesskiller.CygwinProcessKiller$KillerRemoteCall
-
-# selenium-plugin
-hudson.plugins.selenium.JenkinsCapabilityMatcher$LabelMatcherCallable
diff --git a/core/src/main/resources/jenkins/security/s2m/filepath-filter.conf b/core/src/main/resources/jenkins/security/s2m/filepath-filter.conf
deleted file mode 100644
index 543c56e83f85..000000000000
--- a/core/src/main/resources/jenkins/security/s2m/filepath-filter.conf
+++ /dev/null
@@ -1,49 +0,0 @@
-# GENERATED FILE. DO NOT MODIFY.
-#
-# This file is for Jenkins core developers to list what we think are the best filtering rules
-# for apparently harmless accesses to files on the Jenkins master from agents.
-#
-# To override these rules, place *.conf files by other names into this folder. Files are sorted
-# before parsed, so using a lower number allows you to override what we have here. This file
-# gets overwritten every time Jenkins starts.
-#
-# See https://www.jenkins.io/redirect/security-144 for more details.
-
-# This directory contains credentials, master encryption keys, and other sensitive information
-# that agents have absolutely no business with.
-# Unless there are rules in other files allowing access to other portions of $JENKINS_HOME,
-# this rule as it stands here has no effect, because anything left unspecified is rejected.
-deny all /secrets($|/.*)
-
-# User content is publicly readable, so quite safe for agents to read, too.
-# (The xunit plugin is known to read from here.)
-# https://www.jenkins.io/redirect/user-content-directory
-allow read,stat /userContent($|/.*)
-
-# In the next rule we grant general access under build directories, so first we protect
-# the actual build record that Jenkins core reads, which nothing should be touching.
-deny all /build.xml
-# Similarly for Pipeline build (WorkflowRun) metadata:
-deny all /program.dat
-deny all /workflow($|/.*)
-deny all /libs($|/.*)
-deny all /checkpoints($|/.*)
-
-# Various plugins read/write files under build directories, so allow them all.
-# - git 1.x writes changelog.xml from the agent (2.x writes from the master so need not be listed)
-# - analysis-core and plugins based on it write reports to workspace-files/
-# - cobertura writes coverage.xml
-# - violations writes violations.xml and other content under violations/
-# - dependency-check writes archive/artifacts.txt
-# But not allowing deletion to prevent data loss and symlink to prevent jailbreaking.
-allow create,mkdirs,read,stat,write /.+
-
-# cobertura also writes out annotated sources to a dir under the Maven module:
-allow create,mkdirs,read,stat,write /modules/([^/]+)/cobertura($|/.*)
-
-# Some maven-plugin reporters also create content outside of build directories (including one in cobertura but that is covered above):
-allow create,mkdirs,read,stat,write (/jobs/([^/]+))+(|/modules/([^/]+))/(javadoc|test-javadoc)($|/.*)
-allow create,mkdirs,read,stat,write (/jobs/([^/]+))+/site($|/.*)
-
-# all the other accesses that aren't specified here will be left up to other rules in this directory.
-# if no rules in those other files matches, then the access will be rejected.
diff --git a/core/src/main/resources/jenkins/telemetry/impl/SlaveToMasterFileCallableUsage/description.jelly b/core/src/main/resources/jenkins/telemetry/impl/SlaveToMasterFileCallableUsage/description.jelly
deleted file mode 100644
index 83f02a200bf1..000000000000
--- a/core/src/main/resources/jenkins/telemetry/impl/SlaveToMasterFileCallableUsage/description.jelly
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
- Jenkins controllers construct a remote-procedure-call (RPC) channel to agents to instruct them to perform work.
- This channel is bidirectional and in a handful of cases agents made requests of the controller.
- This was always tricky to secure,
- and recently
- the category of usages which involved access to files was more tightly restricted than before;
- Jenkins developers are considering disabling this kind of usage entirely.
- Since it is difficult to determine via static analysis or even manual code inspection which plugins are using this system,
- we are collecting information on how widely it is used.
- The data includes names of Java classes mainly in Jenkins core and plugins as well as method names and line numbers.
- It does not include the names of files being accessed or anything else not determined by versions of software components in use.
-
diff --git a/core/src/main/resources/lib/form/dropdownList.jelly b/core/src/main/resources/lib/form/dropdownList.jelly
index b45a868906e6..159e4fc37ad9 100644
--- a/core/src/main/resources/lib/form/dropdownList.jelly
+++ b/core/src/main/resources/lib/form/dropdownList.jelly
@@ -39,7 +39,7 @@ THE SOFTWARE.
-
+
${attrs.title}
diff --git a/core/src/main/resources/lib/form/radio.jelly b/core/src/main/resources/lib/form/radio.jelly
index c549bb49de65..953504a48654 100644
--- a/core/src/main/resources/lib/form/radio.jelly
+++ b/core/src/main/resources/lib/form/radio.jelly
@@ -47,7 +47,7 @@ THE SOFTWARE.
-
+
diff --git a/core/src/main/resources/lib/form/repeatable.jelly b/core/src/main/resources/lib/form/repeatable.jelly
index dc43213ccaa5..33b24776fd1c 100644
--- a/core/src/main/resources/lib/form/repeatable.jelly
+++ b/core/src/main/resources/lib/form/repeatable.jelly
@@ -133,7 +133,7 @@ THE SOFTWARE.
-
+
${header}
diff --git a/core/src/test/java/hudson/PluginManagerTest.java b/core/src/test/java/hudson/PluginManagerTest.java
index af9aef0135b5..9fcb0310d55d 100644
--- a/core/src/test/java/hudson/PluginManagerTest.java
+++ b/core/src/test/java/hudson/PluginManagerTest.java
@@ -85,7 +85,7 @@ public void parseInvalidRequestedPlugins() throws Exception {
() -> pluginManager.parseRequestedPlugins(new ByteArrayInputStream(evilXML.getBytes())),
"XML contains an external entity, but no exception was thrown.");
assertThat(ex.getCause(), instanceOf(SAXException.class));
- assertThat(ex.getCause().getMessage(), containsString("Refusing to resolve entity with publicId(null) and systemId (file:///)"));
+ assertThat(ex.getCause().getMessage(), containsString("DOCTYPE is disallowed"));
}
@Test
diff --git a/core/src/test/java/hudson/PluginWrapperTest.java b/core/src/test/java/hudson/PluginWrapperTest.java
index 405473699952..0a0e51a588ff 100644
--- a/core/src/test/java/hudson/PluginWrapperTest.java
+++ b/core/src/test/java/hudson/PluginWrapperTest.java
@@ -26,6 +26,7 @@
import jenkins.model.Jenkins;
import jenkins.util.AntClassLoader;
import jenkins.util.URLClassLoader2;
+import org.apache.commons.lang.StringUtils;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
@@ -45,7 +46,9 @@ public static void before() {
@AfterAll
public static void after() {
- Locale.setDefault(loc);
+ if (loc != null) {
+ Locale.setDefault(loc);
+ }
}
@Test
@@ -71,7 +74,7 @@ public void jenkinsCoreTooOld() {
PluginWrapper pw = pluginWrapper("fake").requiredCoreVersion("3.0").buildLoaded();
final IOException ex = assertThrows(IOException.class, pw::resolvePluginDependencies);
- assertContains(ex, "Failed to load: fake (42)", "Jenkins (3.0) or higher required");
+ assertContains(ex, "Failed to load: Fake (fake 42)", "Jenkins (3.0) or higher required");
}
@Test
@@ -79,7 +82,7 @@ public void dependencyNotInstalled() {
PluginWrapper pw = pluginWrapper("dependee").deps("dependency:42").buildLoaded();
final IOException ex = assertThrows(IOException.class, pw::resolvePluginDependencies);
- assertContains(ex, "Failed to load: dependee (42)", "Plugin is missing: dependency (42)");
+ assertContains(ex, "Failed to load: Dependee (dependee 42)", "Plugin is missing: dependency (42)");
}
@Test
@@ -88,7 +91,7 @@ public void dependencyOutdated() {
PluginWrapper pw = pluginWrapper("dependee").deps("dependency:5").buildLoaded();
final IOException ex = assertThrows(IOException.class, pw::resolvePluginDependencies);
- assertContains(ex, "Failed to load: dependee (42)", "Update required: dependency (3) to be updated to 5 or higher");
+ assertContains(ex, "Failed to load: Dependee (dependee 42)", "Update required: Dependency (dependency 3) to be updated to 5 or higher");
}
@Test
@@ -97,7 +100,7 @@ public void dependencyFailedToLoad() {
PluginWrapper pw = pluginWrapper("dependee").deps("dependency:3").buildLoaded();
final IOException ex = assertThrows(IOException.class, pw::resolvePluginDependencies);
- assertContains(ex, "Failed to load: dependee (42)", "Failed to load: dependency (5)");
+ assertContains(ex, "Failed to load: Dependee (dependee 42)", "Failed to load: Dependency (dependency 5)");
}
@Issue("JENKINS-66563")
@@ -196,9 +199,10 @@ private PluginWrapper buildFailed() {
private PluginWrapper build() {
Manifest manifest = new Manifest();
Attributes attributes = manifest.getMainAttributes();
- attributes.put(new Attributes.Name("Short-Name"), name);
- attributes.put(new Attributes.Name("Jenkins-Version"), requiredCoreVersion);
- attributes.put(new Attributes.Name("Plugin-Version"), version);
+ attributes.putValue("Short-Name", name);
+ attributes.putValue("Long-Name", StringUtils.capitalize(name));
+ attributes.putValue("Jenkins-Version", requiredCoreVersion);
+ attributes.putValue("Plugin-Version", version);
return new PluginWrapper(
pm,
new File("/tmp/" + name + ".jpi"),
diff --git a/core/src/test/java/hudson/UtilTest.java b/core/src/test/java/hudson/UtilTest.java
index c1ca84ff5700..253b7b7a326d 100644
--- a/core/src/test/java/hudson/UtilTest.java
+++ b/core/src/test/java/hudson/UtilTest.java
@@ -34,6 +34,7 @@
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.junit.Assume.assumeFalse;
+import static org.junit.Assume.assumeNoException;
import static org.junit.Assume.assumeTrue;
import hudson.model.TaskListener;
@@ -43,8 +44,10 @@
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
+import java.nio.file.AccessDeniedException;
import java.nio.file.Files;
import java.nio.file.Path;
+import java.nio.file.Paths;
import java.nio.file.attribute.PosixFilePermissions;
import java.text.ParseException;
import java.text.SimpleDateFormat;
@@ -630,6 +633,53 @@ public void resolveSymlinkToFile() throws Exception {
assertNull(Util.resolveSymlinkToFile(new File(_a, "aa/aa.txt")));
}
+ @Test
+ @Issue("JENKINS-67372")
+ public void createDirectories() throws Exception {
+ assumeFalse(Functions.isWindows());
+ // root
+ // /a
+ // /a1
+ // /a2 => symlink to a1
+ // /b => symlink to a
+ Path root = tmp.getRoot().toPath();
+ Path a = root.resolve("a");
+ Path a1 = a.resolve("a1");
+ Files.createDirectories(a1);
+
+ Path a2 = a.resolve("a2");
+ Util.createSymlink(a2.getParent().toFile(), a1.getFileName().toString(), a2.getFileName().toString(), TaskListener.NULL);
+
+ Path b = root.resolve("b");
+ Util.createSymlink(b.getParent().toFile(), a.getFileName().toString(), b.getFileName().toString(), TaskListener.NULL);
+
+ assertTrue(Files.isSymbolicLink(a2));
+ assertTrue(Files.isSymbolicLink(b));
+
+ assertEquals(a.resolve("new1"), Util.createDirectories(a.resolve("new1")).toRealPath());
+ assertEquals(a1.resolve("new2"), Util.createDirectories(a1.resolve("new2")).toRealPath());
+ assertEquals(a1.resolve("new3"), Util.createDirectories(a2.resolve("new3")).toRealPath());
+ assertEquals(a.resolve("new4"), Util.createDirectories(b.resolve("new4")).toRealPath());
+ assertEquals(a1.resolve("new5"), Util.createDirectories(b.resolve("a1").resolve("new5")).toRealPath());
+ assertEquals(a1.resolve("new6"), Util.createDirectories(b.resolve("a2").resolve("new6")).toRealPath());
+ }
+
+ @Test
+ @Issue("JENKINS-67372")
+ public void createDirectoriesInRoot() throws Exception {
+ assumeFalse(Functions.isWindows());
+ Path newDirInRoot = Paths.get("/new-dir-in-root");
+ Path newSymlinkInRoot = Paths.get("/new-symlink-in-root");
+ try {
+ assertEquals(newDirInRoot.resolve("new1"), Util.createDirectories(newDirInRoot.resolve("new1")).toRealPath());
+ Util.createSymlink(newSymlinkInRoot.getParent().toFile(), newDirInRoot.getFileName().toString(), newSymlinkInRoot.getFileName().toString(), TaskListener.NULL);
+ assertEquals(newDirInRoot.resolve("new2"), Util.createDirectories(newSymlinkInRoot.resolve("new2")).toRealPath());
+ } catch (AccessDeniedException e) {
+ // Not running as root
+ assumeNoException(e);
+ }
+ }
+
@Test
public void ifOverriddenSuccess() {
assertTrue(Util.ifOverridden(() -> true, BaseClass.class, DerivedClassSuccess.class, "method"));
diff --git a/core/src/test/java/jenkins/org/apache/commons/validator/ResultPair.java b/core/src/test/java/jenkins/org/apache/commons/validator/ResultPair.java
new file mode 100644
index 000000000000..f31c35bf1c84
--- /dev/null
+++ b/core/src/test/java/jenkins/org/apache/commons/validator/ResultPair.java
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package jenkins.org.apache.commons.validator;
+
+/**
+ * Groups tests and expected results.
+ *
+ * @version $Revision$
+ */
+ public class ResultPair {
+ public final String item;
+ public final boolean valid;
+
+ public ResultPair(String item, boolean valid) {
+ this.item = item;
+ this.valid = valid; //Whether the individual part of url is valid.
+ }
+ }
diff --git a/core/src/test/java/jenkins/org/apache/commons/validator/routines/DomainValidatorTest.java b/core/src/test/java/jenkins/org/apache/commons/validator/routines/DomainValidatorTest.java
new file mode 100644
index 000000000000..2cdd2c5880db
--- /dev/null
+++ b/core/src/test/java/jenkins/org/apache/commons/validator/routines/DomainValidatorTest.java
@@ -0,0 +1,639 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package jenkins.org.apache.commons.validator.routines;
+
+import java.io.BufferedReader;
+import java.io.Closeable;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.net.HttpURLConnection;
+import java.net.IDN;
+import java.net.URL;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import jenkins.org.apache.commons.validator.routines.DomainValidator.ArrayType;
+import junit.framework.TestCase;
+
+/**
+ * Tests for the DomainValidator.
+ *
+ * @version $Revision$
+ */
+public class DomainValidatorTest extends TestCase {
+
+ private DomainValidator validator;
+
+ @Override
+ public void setUp() {
+ validator = DomainValidator.getInstance();
+ }
+
+ public void testValidDomains() {
+ assertTrue("apache.org should validate", validator.isValid("apache.org"));
+ assertTrue("www.google.com should validate", validator.isValid("www.google.com"));
+
+ assertTrue("test-domain.com should validate", validator.isValid("test-domain.com"));
+ assertTrue("test---domain.com should validate", validator.isValid("test---domain.com"));
+ assertTrue("test-d-o-m-ain.com should validate", validator.isValid("test-d-o-m-ain.com"));
+ assertTrue("two-letter domain label should validate", validator.isValid("as.uk"));
+
+ assertTrue("case-insensitive ApAchE.Org should validate", validator.isValid("ApAchE.Org"));
+
+ assertTrue("single-character domain label should validate", validator.isValid("z.com"));
+
+ assertTrue("i.have.an-example.domain.name should validate", validator.isValid("i.have.an-example.domain.name"));
+ }
+
+ public void testInvalidDomains() {
+ assertFalse("bare TLD .org shouldn't validate", validator.isValid(".org"));
+ assertFalse("domain name with spaces shouldn't validate", validator.isValid(" apache.org "));
+ assertFalse("domain name containing spaces shouldn't validate", validator.isValid("apa che.org"));
+ assertFalse("domain name starting with dash shouldn't validate", validator.isValid("-testdomain.name"));
+ assertFalse("domain name ending with dash shouldn't validate", validator.isValid("testdomain-.name"));
+ assertFalse("domain name starting with multiple dashes shouldn't validate", validator.isValid("---c.com"));
+ assertFalse("domain name ending with multiple dashes shouldn't validate", validator.isValid("c--.com"));
+ assertFalse("domain name with invalid TLD shouldn't validate", validator.isValid("apache.rog"));
+
+ assertFalse("URL shouldn't validate", validator.isValid("http://www.apache.org"));
+ assertFalse("Empty string shouldn't validate as domain name", validator.isValid(" "));
+ assertFalse("Null shouldn't validate as domain name", validator.isValid(null));
+ }
+
+ public void testTopLevelDomains() {
+ // infrastructure TLDs
+ assertTrue(".arpa should validate as iTLD", validator.isValidInfrastructureTld(".arpa"));
+ assertFalse(".com shouldn't validate as iTLD", validator.isValidInfrastructureTld(".com"));
+
+ // generic TLDs
+ assertTrue(".name should validate as gTLD", validator.isValidGenericTld(".name"));
+ assertFalse(".us shouldn't validate as gTLD", validator.isValidGenericTld(".us"));
+
+ // country code TLDs
+ assertTrue(".uk should validate as ccTLD", validator.isValidCountryCodeTld(".uk"));
+ assertFalse(".org shouldn't validate as ccTLD", validator.isValidCountryCodeTld(".org"));
+
+ // case-insensitive
+ assertTrue(".COM should validate as TLD", validator.isValidTld(".COM"));
+ assertTrue(".BiZ should validate as TLD", validator.isValidTld(".BiZ"));
+
+ // corner cases
+ assertFalse("invalid TLD shouldn't validate", validator.isValid(".nope")); // TODO this is not guaranteed invalid forever
+ assertFalse("empty string shouldn't validate as TLD", validator.isValid(""));
+ assertFalse("null shouldn't validate as TLD", validator.isValid(null));
+ }
+
+ public void testAllowLocal() {
+ DomainValidator noLocal = DomainValidator.getInstance(false);
+ DomainValidator allowLocal = DomainValidator.getInstance(true);
+
+ // Default is false, and should use singletons
+ assertEquals(noLocal, validator);
+
+ // Default won't allow local
+ assertFalse("localhost.localdomain should validate", noLocal.isValid("localhost.localdomain"));
+ assertFalse("localhost should validate", noLocal.isValid("localhost"));
+
+ // But it may be requested
+ assertTrue("localhost.localdomain should validate", allowLocal.isValid("localhost.localdomain"));
+ assertTrue("localhost should validate", allowLocal.isValid("localhost"));
+ assertTrue("hostname should validate", allowLocal.isValid("hostname"));
+ assertTrue("machinename should validate", allowLocal.isValid("machinename"));
+
+ // Check the localhost one with a few others
+ assertTrue("apache.org should validate", allowLocal.isValid("apache.org"));
+ assertFalse("domain name with spaces shouldn't validate", allowLocal.isValid(" apache.org "));
+ }
+
+ public void testIDN() {
+ assertTrue("b\u00fccher.ch in IDN should validate", validator.isValid("www.xn--bcher-kva.ch"));
+ }
+
+ public void testIDNJava6OrLater() {
+ String version = System.getProperty("java.version");
+ if (version.compareTo("1.6") < 0) {
+ System.out.println("Cannot run Unicode IDN tests");
+ return; // Cannot run the test
+ } // xn--d1abbgf6aiiy.xn--p1ai http://президент.рф
+ assertTrue("b\u00fccher.ch should validate", validator.isValid("www.b\u00fccher.ch"));
+ assertTrue("xn--d1abbgf6aiiy.xn--p1ai should validate", validator.isValid("xn--d1abbgf6aiiy.xn--p1ai"));
+ assertTrue("президент.рф should validate", validator.isValid("президент.рф"));
+ assertFalse("www.\uFFFD.ch FFFD should fail", validator.isValid("www.\uFFFD.ch"));
+ }
+
+ // RFC2396: domainlabel = alphanum | alphanum *( alphanum | "-" ) alphanum
+ public void testRFC2396domainlabel() { // use fixed valid TLD
+ assertTrue("a.ch should validate", validator.isValid("a.ch"));
+ assertTrue("9.ch should validate", validator.isValid("9.ch"));
+ assertTrue("az.ch should validate", validator.isValid("az.ch"));
+ assertTrue("09.ch should validate", validator.isValid("09.ch"));
+ assertTrue("9-1.ch should validate", validator.isValid("9-1.ch"));
+ assertFalse("91-.ch should not validate", validator.isValid("91-.ch"));
+ assertFalse("-.ch should not validate", validator.isValid("-.ch"));
+ }
+
+ // RFC2396 toplabel = alpha | alpha *( alphanum | "-" ) alphanum
+ public void testRFC2396toplabel() {
+ // These tests use non-existent TLDs so currently need to use a package protected method
+ assertTrue("a.c (alpha) should validate", validator.isValidDomainSyntax("a.c"));
+ assertTrue("a.cc (alpha alpha) should validate", validator.isValidDomainSyntax("a.cc"));
+ assertTrue("a.c9 (alpha alphanum) should validate", validator.isValidDomainSyntax("a.c9"));
+ assertTrue("a.c-9 (alpha - alphanum) should validate", validator.isValidDomainSyntax("a.c-9"));
+ assertTrue("a.c-z (alpha - alpha) should validate", validator.isValidDomainSyntax("a.c-z"));
+
+ assertFalse("a.9c (alphanum alpha) should fail", validator.isValidDomainSyntax("a.9c"));
+ assertFalse("a.c- (alpha -) should fail", validator.isValidDomainSyntax("a.c-"));
+ assertFalse("a.- (-) should fail", validator.isValidDomainSyntax("a.-"));
+ assertFalse("a.-9 (- alphanum) should fail", validator.isValidDomainSyntax("a.-9"));
+ }
+
+ public void testDomainNoDots() {// rfc1123
+ assertTrue("a (alpha) should validate", validator.isValidDomainSyntax("a"));
+ assertTrue("9 (alphanum) should validate", validator.isValidDomainSyntax("9"));
+ assertTrue("c-z (alpha - alpha) should validate", validator.isValidDomainSyntax("c-z"));
+
+ assertFalse("c- (alpha -) should fail", validator.isValidDomainSyntax("c-"));
+ assertFalse("-c (- alpha) should fail", validator.isValidDomainSyntax("-c"));
+ assertFalse("- (-) should fail", validator.isValidDomainSyntax("-"));
+ }
+
+ public void testValidator297() {
+ assertTrue("xn--d1abbgf6aiiy.xn--p1ai should validate", validator.isValid("xn--d1abbgf6aiiy.xn--p1ai")); // This uses a valid TLD
+ }
+
+ // labels are a max of 63 chars and domains 253
+ public void testValidator306() {
+ final String longString = "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz0123456789A";
+ assertEquals(63, longString.length()); // 26 * 2 + 11
+
+ assertTrue("63 chars label should validate", validator.isValidDomainSyntax(longString+".com"));
+ assertFalse("64 chars label should fail", validator.isValidDomainSyntax(longString+"x.com"));
+
+ assertTrue("63 chars TLD should validate", validator.isValidDomainSyntax("test."+longString));
+ assertFalse("64 chars TLD should fail", validator.isValidDomainSyntax("test.x"+longString));
+
+ final String longDomain =
+ longString
+ + "." + longString
+ + "." + longString
+ + "." + longString.substring(0,61)
+ ;
+ assertEquals(253, longDomain.length());
+ assertTrue("253 chars domain should validate", validator.isValidDomainSyntax(longDomain));
+ assertFalse("254 chars domain should fail", validator.isValidDomainSyntax(longDomain+"x"));
+ }
+
+ // Check that IDN.toASCII behaves as it should (when wrapped by DomainValidator.unicodeToASCII)
+ // Tests show that method incorrectly trims a trailing "." character
+ public void testUnicodeToASCII() {
+ String[] asciidots = {
+ "",
+ ",",
+ ".", // fails IDN.toASCII, but should pass wrapped version
+ "a.", // ditto
+ "a.b",
+ "a..b",
+ "a...b",
+ ".a",
+ "..a",
+ };
+ for(String s : asciidots) {
+ assertEquals(s,DomainValidator.unicodeToASCII(s));
+ }
+ // RFC3490 3.1. 1)
+// Whenever dots are used as label separators, the following
+// characters MUST be recognized as dots: U+002E (full stop), U+3002
+// (ideographic full stop), U+FF0E (fullwidth full stop), U+FF61
+// (halfwidth ideographic full stop).
+ final String[][] otherDots = {
+ {"b\u3002", "b.",},
+ {"b\uFF0E", "b.",},
+ {"b\uFF61", "b.",},
+ {"\u3002", ".",},
+ {"\uFF0E", ".",},
+ {"\uFF61", ".",},
+ };
+ for(String[] s : otherDots) {
+ assertEquals(s[1],DomainValidator.unicodeToASCII(s[0]));
+ }
+ }
+
+ // Check if IDN.toASCII is broken or not
+ public void testIsIDNtoASCIIBroken() {
+ System.out.println(">>DomainValidatorTest.testIsIDNtoASCIIBroken()");
+ final String input = ".";
+ final boolean ok = input.equals(IDN.toASCII(input));
+ System.out.println("IDN.toASCII is " + (ok? "OK" : "BROKEN"));
+ String[] props = {
+ "java.version", // Java Runtime Environment version
+ "java.vendor", // Java Runtime Environment vendor
+ "java.vm.specification.version", // Java Virtual Machine specification version
+ "java.vm.specification.vendor", // Java Virtual Machine specification vendor
+ "java.vm.specification.name", // Java Virtual Machine specification name
+ "java.vm.version", // Java Virtual Machine implementation version
+ "java.vm.vendor", // Java Virtual Machine implementation vendor
+ "java.vm.name", // Java Virtual Machine implementation name
+ "java.specification.version", // Java Runtime Environment specification version
+ "java.specification.vendor", // Java Runtime Environment specification vendor
+ "java.specification.name", // Java Runtime Environment specification name
+ "java.class.version", // Java class format version number
+ };
+ for(String t : props) {
+ System.out.println(t + "=" + System.getProperty(t));
+ }
+ System.out.println("< ianaTlds = new HashSet<>(); // keep for comparison with array contents
+ DomainValidator dv = DomainValidator.getInstance();
+ File txtFile = new File("target/tlds-alpha-by-domain.txt");
+ long timestamp = download(txtFile, "https://data.iana.org/TLD/tlds-alpha-by-domain.txt", 0L);
+ final File htmlFile = new File("target/tlds-alpha-by-domain.html");
+ // N.B. sometimes the html file may be updated a day or so after the txt file
+ // if the txt file contains entries not found in the html file, try again in a day or two
+ download(htmlFile,"https://www.iana.org/domains/root/db", timestamp);
+
+ BufferedReader br = new BufferedReader(new FileReader(txtFile));
+ String line;
+ final String header;
+ line = br.readLine(); // header
+ if (line.startsWith("# Version ")) {
+ header = line.substring(2);
+ } else {
+ br.close();
+ throw new IOException("File does not have expected Version header");
+ }
+ final boolean generateUnicodeTlds = false; // Change this to generate Unicode TLDs as well
+
+ // Parse html page to get entries
+ Map htmlInfo = getHtmlInfo(htmlFile);
+ Map missingTLD = new TreeMap<>(); // stores entry and comments as String[]
+ Map missingCC = new TreeMap<>();
+ while((line = br.readLine()) != null) {
+ if (!line.startsWith("#")) {
+ final String unicodeTld; // only different from asciiTld if that was punycode
+ final String asciiTld = line.toLowerCase(Locale.ENGLISH);
+ if (line.startsWith("XN--")) {
+ unicodeTld = IDN.toUnicode(line);
+ } else {
+ unicodeTld = asciiTld;
+ }
+ if (!dv.isValidTld(asciiTld)) {
+ String [] info = htmlInfo.get(asciiTld);
+ if (info != null) {
+ String type = info[0];
+ String comment = info[1];
+ if ("country-code".equals(type)) { // Which list to use?
+ missingCC.put(asciiTld, unicodeTld + " " + comment);
+ if (generateUnicodeTlds) {
+ missingCC.put(unicodeTld, asciiTld + " " + comment);
+ }
+ } else {
+ missingTLD.put(asciiTld, unicodeTld + " " + comment);
+ if (generateUnicodeTlds) {
+ missingTLD.put(unicodeTld, asciiTld + " " + comment);
+ }
+ }
+ } else {
+ System.err.println("Expected to find HTML info for "+ asciiTld);
+ }
+ }
+ ianaTlds.add(asciiTld);
+ // Don't merge these conditions; generateUnicodeTlds is final so needs to be separate to avoid a warning
+ if (generateUnicodeTlds) {
+ if (!unicodeTld.equals(asciiTld)) {
+ ianaTlds.add(unicodeTld);
+ }
+ }
+ }
+ }
+ br.close();
+ // List html entries not in TLD text list
+ for(String key : (new TreeMap<>(htmlInfo)).keySet()) {
+ if (!ianaTlds.contains(key)) {
+ if (isNotInRootZone(key)) {
+ System.out.println("INFO: HTML entry not yet in root zone: "+key);
+ } else {
+ System.err.println("WARN: Expected to find text entry for html: "+key);
+ }
+ }
+ }
+ if (!missingTLD.isEmpty()) {
+ printMap(header, missingTLD, "TLD");
+ }
+ if (!missingCC.isEmpty()) {
+ printMap(header, missingCC, "CC");
+ }
+ // Check if internal tables contain any additional entries
+ isInIanaList("INFRASTRUCTURE_TLDS", ianaTlds);
+ isInIanaList("COUNTRY_CODE_TLDS", ianaTlds);
+ isInIanaList("GENERIC_TLDS", ianaTlds);
+ // Don't check local TLDS isInIanaList("LOCAL_TLDS", ianaTlds);
+ System.out.println("Finished checks");
+ }
+
+ private static void printMap(final String header, Map map, String string) {
+ System.out.println("Entries missing from "+ string +" List\n");
+ if (header != null) {
+ System.out.println(" // Taken from " + header);
+ }
+ for (Map.Entry me : map.entrySet()) {
+ System.out.println(" \"" + me.getKey() + "\", // " + me.getValue());
+ }
+ System.out.println("\nDone");
+ }
+
+ private static Map getHtmlInfo(final File f) throws IOException {
+ final Map info = new HashMap<>();
+
+//
");
+
+ final BufferedReader br = new BufferedReader(new FileReader(f));
+ String line;
+ while((line=br.readLine())!=null){
+ Matcher m = domain.matcher(line);
+ if (m.lookingAt()) {
+ String dom = m.group(1);
+ String typ = "??";
+ String com = "??";
+ line = br.readLine();
+ while (line.matches("^\\s*$")) { // extra blank lines introduced
+ line = br.readLine();
+ }
+ Matcher t = type.matcher(line);
+ if (t.lookingAt()) {
+ typ = t.group(1);
+ line = br.readLine();
+ if (line.matches("\\s+.*")){
+ line = br.readLine();
+ }
+ line = br.readLine();
+ }
+ // Should have comment; is it wrapped?
+ while(!line.matches(".*.*")){
+ line += " " +br.readLine();
+ }
+ Matcher n = comment.matcher(line);
+ if (n.lookingAt()) {
+ com = n.group(1);
+ }
+ // Don't save unused entries
+ if (com.contains("Not assigned") || com.contains("Retired") || typ.equals("test")) {
+// System.out.println("Ignored: " + typ + " " + dom + " " +com);
+ } else {
+ info.put(dom.toLowerCase(Locale.ENGLISH), new String[]{typ, com});
+// System.out.println("Storing: " + typ + " " + dom + " " +com);
+ }
+ } else {
+ System.err.println("Unexpected type: " + line);
+ }
+ }
+ }
+ br.close();
+ return info;
+ }
+
+ /*
+ * Download a file if it is more recent than our cached copy.
+ * Unfortunately the server does not seem to honour If-Modified-Since for the
+ * Html page, so we check if it is newer than the txt file and skip download if so
+ */
+ private static long download(File f, String tldurl, long timestamp) throws IOException {
+ final int HOUR = 60*60*1000; // an hour in ms
+ final long modTime;
+ // For testing purposes, don't download files more than once an hour
+ if (f.canRead()) {
+ modTime = f.lastModified();
+ if (modTime > System.currentTimeMillis()-HOUR) {
+ System.out.println("Skipping download - found recent " + f);
+ return modTime;
+ }
+ } else {
+ modTime = 0;
+ }
+ HttpURLConnection hc = (HttpURLConnection) new URL(tldurl).openConnection();
+ if (modTime > 0) {
+ SimpleDateFormat sdf = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z");//Sun, 06 Nov 1994 08:49:37 GMT
+ String since = sdf.format(new Date(modTime));
+ hc.addRequestProperty("If-Modified-Since", since);
+ System.out.println("Found " + f + " with date " + since);
+ }
+ if (hc.getResponseCode() == 304) {
+ System.out.println("Already have most recent " + tldurl);
+ } else {
+ System.out.println("Downloading " + tldurl);
+ byte[] buff = new byte[1024];
+ InputStream is = hc.getInputStream();
+
+ FileOutputStream fos = new FileOutputStream(f);
+ int len;
+ while((len=is.read(buff)) != -1) {
+ fos.write(buff, 0, len);
+ }
+ fos.close();
+ is.close();
+ System.out.println("Done");
+ }
+ return f.lastModified();
+ }
+
+ /**
+ * Check whether the domain is in the root zone currently.
+ * Reads the URL http://www.iana.org/domains/root/db/*domain*.html
+ * (using a local disk cache)
+ * and checks for the string "This domain is not present in the root zone at this time."
+ * @param domain the domain to check
+ * @return true if the string is found
+ */
+ private static boolean isNotInRootZone(String domain) {
+ String tldurl = "http://www.iana.org/domains/root/db/" + domain + ".html";
+ File rootCheck = new File("target","tld_" + domain + ".html");
+ BufferedReader in = null;
+ try {
+ download(rootCheck, tldurl, 0L);
+ in = new BufferedReader(new FileReader(rootCheck));
+ String inputLine;
+ while ((inputLine = in.readLine()) != null) {
+ if (inputLine.contains("This domain is not present in the root zone at this time.")) {
+ return true;
+ }
+ }
+ in.close();
+ } catch (IOException e) {
+ } finally {
+ closeQuietly(in);
+ }
+ return false;
+ }
+
+ private static void closeQuietly(Closeable in) {
+ if (in != null) {
+ try {
+ in.close();
+ } catch (IOException e) {
+ }
+ }
+ }
+
+ // isInIanaList and isSorted are split into two methods.
+ // If/when access to the arrays is possible without reflection, the intermediate
+ // methods can be dropped
+ private static boolean isInIanaList(String arrayName, Set ianaTlds) throws Exception {
+ Field f = DomainValidator.class.getDeclaredField(arrayName);
+ final boolean isPrivate = Modifier.isPrivate(f.getModifiers());
+ if (isPrivate) {
+ f.setAccessible(true);
+ }
+ String[] array = (String[]) f.get(null);
+ try {
+ return isInIanaList(arrayName, array, ianaTlds);
+ } finally {
+ if (isPrivate) {
+ f.setAccessible(false);
+ }
+ }
+ }
+
+ private static boolean isInIanaList(String name, String [] array, Set ianaTlds) {
+ for (String s : array) {
+ if (!ianaTlds.contains(s)) {
+ System.out.println(name + " contains unexpected value: " + s);
+ }
+ }
+ return true;
+ }
+
+ private static boolean isSortedLowerCase(String arrayName) throws Exception {
+ Field f = DomainValidator.class.getDeclaredField(arrayName);
+ final boolean isPrivate = Modifier.isPrivate(f.getModifiers());
+ if (isPrivate) {
+ f.setAccessible(true);
+ }
+ String[] array = (String[]) f.get(null);
+ try {
+ return isSortedLowerCase(arrayName, array);
+ } finally {
+ if (isPrivate) {
+ f.setAccessible(false);
+ }
+ }
+ }
+
+ private static boolean isLowerCase(String string) {
+ return string.equals(string.toLowerCase(Locale.ENGLISH));
+ }
+
+ // Check if an array is strictly sorted - and lowerCase
+ private static boolean isSortedLowerCase(String name, String [] array) {
+ boolean sorted = true;
+ boolean strictlySorted = true;
+ final int length = array.length;
+ boolean lowerCase = isLowerCase(array[length-1]); // Check the last entry
+ for(int i = 0; i < length-1; i++) { // compare all but last entry with next
+ final String entry = array[i];
+ final String nextEntry = array[i+1];
+ final int cmp = entry.compareTo(nextEntry);
+ if (cmp > 0) { // out of order
+ System.out.println("Out of order entry: " + entry + " < " + nextEntry + " in " + name);
+ sorted = false;
+ } else if (cmp == 0) {
+ strictlySorted = false;
+ System.out.println("Duplicated entry: " + entry + " in " + name);
+ }
+ if (!isLowerCase(entry)) {
+ System.out.println("Non lowerCase entry: " + entry + " in " + name);
+ lowerCase = false;
+ }
+ }
+ return sorted && strictlySorted && lowerCase;
+ }
+}
diff --git a/core/src/test/java/jenkins/org/apache/commons/validator/routines/InetAddressValidatorTest.java b/core/src/test/java/jenkins/org/apache/commons/validator/routines/InetAddressValidatorTest.java
new file mode 100644
index 000000000000..866c39cab190
--- /dev/null
+++ b/core/src/test/java/jenkins/org/apache/commons/validator/routines/InetAddressValidatorTest.java
@@ -0,0 +1,651 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jenkins.org.apache.commons.validator.routines;
+
+import junit.framework.TestCase;
+
+/**
+ * Test cases for InetAddressValidator.
+ *
+ * @version $Revision$
+ */
+public class InetAddressValidatorTest extends TestCase {
+
+ private InetAddressValidator validator;
+
+ /**
+ * Constructor.
+ */
+ public InetAddressValidatorTest(String name) {
+ super(name);
+ }
+
+ @Override
+ protected void setUp() {
+ validator = new InetAddressValidator();
+ }
+
+ /**
+ * Test IPs that point to real, well-known hosts (without actually looking them up).
+ */
+ public void testInetAddressesFromTheWild() {
+ assertTrue("www.apache.org IP should be valid", validator.isValid("140.211.11.130"));
+ assertTrue("www.l.google.com IP should be valid", validator.isValid("72.14.253.103"));
+ assertTrue("fsf.org IP should be valid", validator.isValid("199.232.41.5"));
+ assertTrue("appscs.ign.com IP should be valid", validator.isValid("216.35.123.87"));
+ }
+
+ public void testVALIDATOR_335() {
+ assertTrue("2001:0438:FFFE:0000:0000:0000:0000:0A35 should be valid", validator.isValid("2001:0438:FFFE:0000:0000:0000:0000:0A35"));
+ }
+
+ public void testVALIDATOR_419() {
+ String addr;
+ addr = "0:0:0:0:0:0:13.1.68.3";
+ assertTrue(addr, validator.isValid(addr));
+ addr = "0:0:0:0:0:FFFF:129.144.52.38";
+ assertTrue(addr, validator.isValid(addr));
+ addr = "::13.1.68.3";
+ assertTrue(addr, validator.isValid(addr));
+ addr = "::FFFF:129.144.52.38";
+ assertTrue(addr, validator.isValid(addr));
+
+ addr = "::ffff:192.168.1.1:192.168.1.1";
+ assertFalse(addr, validator.isValid(addr));
+ addr = "::192.168.1.1:192.168.1.1";
+ assertFalse(addr, validator.isValid(addr));
+ }
+
+ /**
+ * Inet6Address may also contain a scope id
+ */
+ public void testVALIDATOR_445() {
+ String [] valid = {
+ "2001:0000:1234:0000:0000:C1C0:ABCD:0876",
+ "2001:0000:1234:0000:0000:C1C0:ABCD:0876/123",
+ "2001:0000:1234:0000:0000:C1C0:ABCD:0876/0",
+ "2001:0000:1234:0000:0000:C1C0:ABCD:0876%0",
+ "2001:0000:1234:0000:0000:C1C0:ABCD:0876%abcdefgh",
+ };
+ String [] invalid = {
+ "2001:0000:1234:0000:0000:C1C0:ABCD:0876/129", // too big
+ "2001:0000:1234:0000:0000:C1C0:ABCD:0876/-0", // sign not allowed
+ "2001:0000:1234:0000:0000:C1C0:ABCD:0876/+0", // sign not allowed
+ "2001:0000:1234:0000:0000:C1C0:ABCD:0876/10O", // non-digit
+ "2001:0000:1234:0000:0000:C1C0:ABCD:0876/0%0", // /bits before %node-id
+ "2001:0000:1234:0000:0000:C1C0:ABCD:0876%abc defgh", // space in node id
+ "2001:0000:1234:0000:0000:C1C0:ABCD:0876%abc%defgh", // '%' in node id
+ };
+ for(String item : valid) {
+ assertTrue(String.format("%s should be valid", item), validator.isValid(item));
+ }
+ for(String item : invalid) {
+ assertFalse(String.format("%s should be invalid", item), validator.isValid(item));
+ }
+ }
+
+ /**
+ * Test valid and invalid IPs from each address class.
+ */
+ public void testInetAddressesByClass() {
+ assertTrue("class A IP should be valid", validator.isValid("24.25.231.12"));
+ assertFalse("illegal class A IP should be invalid", validator.isValid("2.41.32.324"));
+
+ assertTrue("class B IP should be valid", validator.isValid("135.14.44.12"));
+ assertFalse("illegal class B IP should be invalid", validator.isValid("154.123.441.123"));
+
+ assertTrue("class C IP should be valid", validator.isValid("213.25.224.32"));
+ assertFalse("illegal class C IP should be invalid", validator.isValid("201.543.23.11"));
+
+ assertTrue("class D IP should be valid", validator.isValid("229.35.159.6"));
+ assertFalse("illegal class D IP should be invalid", validator.isValid("231.54.11.987"));
+
+ assertTrue("class E IP should be valid", validator.isValid("248.85.24.92"));
+ assertFalse("illegal class E IP should be invalid", validator.isValid("250.21.323.48"));
+ }
+
+ /**
+ * Test reserved IPs.
+ */
+ public void testReservedInetAddresses() {
+ assertTrue("localhost IP should be valid", validator.isValid("127.0.0.1"));
+ assertTrue("broadcast IP should be valid", validator.isValid("255.255.255.255"));
+ }
+
+ /**
+ * Test obviously broken IPs.
+ */
+ public void testBrokenInetAddresses() {
+ assertFalse("IP with characters should be invalid", validator.isValid("124.14.32.abc"));
+ // TODO: there is some debate as to whether leading zeros should be allowed
+ // They are ambiguous: does the leading 0 mean octal?
+ assertFalse("IP with leading zeroes should be invalid", validator.isValid("124.14.32.01"));
+ assertFalse("IP with three groups should be invalid", validator.isValid("23.64.12"));
+ assertFalse("IP with five groups should be invalid", validator.isValid("26.34.23.77.234"));
+ assertFalse("IP empty string should be invalid", validator.isValidInet6Address(""));// empty string
+ }
+
+ /**
+ * Test IPv6 addresses.
+ *
");
}
@Test
diff --git a/test/src/test/java/jenkins/security/ClassFilterImplTest.java b/test/src/test/java/jenkins/security/ClassFilterImplTest.java
index cb793c0b8c4c..52d044e093ae 100644
--- a/test/src/test/java/jenkins/security/ClassFilterImplTest.java
+++ b/test/src/test/java/jenkins/security/ClassFilterImplTest.java
@@ -113,7 +113,7 @@ public void agentToControllerRequiresWhitelist() throws Exception {
FreeStyleProject p = r.createFreeStyleProject();
p.setAssignedNode(r.createSlave());
p.getBuildersList().add(new S2MBuilder());
- r.assertBuildStatus(Result.FAILURE, p.scheduleBuild2(0));
+ r.buildAndAssertStatus(Result.FAILURE, p);
}
public static class S2MBuilder extends Builder {
@Override
diff --git a/test/src/test/java/jenkins/security/FilePathSecureTest.java b/test/src/test/java/jenkins/security/FilePathSecureTest.java
index 02ca3a68cdde..936fc47fe314 100644
--- a/test/src/test/java/jenkins/security/FilePathSecureTest.java
+++ b/test/src/test/java/jenkins/security/FilePathSecureTest.java
@@ -35,6 +35,7 @@
import org.junit.Test;
import org.jvnet.hudson.test.JenkinsRule;
+// TODO What is this even testing?
public class FilePathSecureTest {
@Rule public JenkinsRule r = new JenkinsRule();
diff --git a/test/src/test/java/jenkins/security/Security2455Test.java b/test/src/test/java/jenkins/security/Security2455Test.java
deleted file mode 100644
index 71613ceb60d3..000000000000
--- a/test/src/test/java/jenkins/security/Security2455Test.java
+++ /dev/null
@@ -1,867 +0,0 @@
-package jenkins.security;
-
-import static org.hamcrest.CoreMatchers.containsString;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.not;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-import static org.junit.Assume.assumeFalse;
-import static org.jvnet.hudson.test.LoggerRule.recorded;
-
-import hudson.ExtensionList;
-import hudson.FilePath;
-import hudson.Functions;
-import hudson.Util;
-import hudson.model.Cause;
-import hudson.model.FreeStyleBuild;
-import hudson.model.FreeStyleProject;
-import hudson.model.Node;
-import hudson.model.TaskListener;
-import hudson.remoting.VirtualChannel;
-import hudson.slaves.DumbSlave;
-import java.io.File;
-import java.io.FileReader;
-import java.io.IOException;
-import java.lang.reflect.Constructor;
-import java.net.URI;
-import java.nio.file.Files;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Objects;
-import java.util.logging.Level;
-import jenkins.SlaveToMasterFileCallable;
-import jenkins.SoloFilePathFilter;
-import jenkins.agents.AgentComputerUtil;
-import jenkins.security.s2m.AdminWhitelistRule;
-import org.apache.commons.io.IOUtils;
-import org.apache.commons.io.output.NullOutputStream;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.function.ThrowingRunnable;
-import org.jvnet.hudson.test.FlagRule;
-import org.jvnet.hudson.test.Issue;
-import org.jvnet.hudson.test.JenkinsRule;
-import org.jvnet.hudson.test.LoggerRule;
-import org.jvnet.hudson.test.MockFolder;
-import org.jvnet.hudson.test.recipes.LocalData;
-
-@SuppressWarnings("ThrowableNotThrown")
-@Issue("SECURITY-2455")
-public class Security2455Test {
-
- // TODO After merge, reference the class directly
- private static final String SECURITY_2428_KILLSWITCH = "jenkins.security.s2m.RunningBuildFilePathFilter.FAIL";
-
- @Rule
- public final FlagRule flagRule = FlagRule.systemProperty(SECURITY_2428_KILLSWITCH, "false");
-
- @Rule
- public JenkinsRule j = new JenkinsRule();
-
- @Rule
- public LoggerRule logging = new LoggerRule().record(SoloFilePathFilter.class, Level.WARNING);
-
- @Before
- public void setup() {
- ExtensionList.lookupSingleton(AdminWhitelistRule.class).setMasterKillSwitch(false);
- }
-
- // --------
-
- @Test
- @Issue("SECURITY-2427")
- public void mkdirsParentsTest() {
- final File buildStuff = new File(j.jenkins.getRootDir(), "job/nonexistent/builds/1/foo/bar");
- logging.capture(10);
- SecurityException ex = assertThrowsIOExceptionCausedBySecurityException(() -> invokeOnAgent(new MkdirsParentsCallable(buildStuff)));
- assertThat(logging, recorded(containsString("foo/bar")));
- assertThat(ex.getMessage(), not(containsString("foo/bar"))); // test error redaction
-
- SoloFilePathFilter.REDACT_ERRORS = false;
- try {
- SecurityException ex2 = assertThrowsIOExceptionCausedBySecurityException(() -> invokeOnAgent(new MkdirsParentsCallable(buildStuff)));
- assertThat(ex2.getMessage(), containsString("foo/bar")); // test error redaction
- } finally {
- SoloFilePathFilter.REDACT_ERRORS = true;
- }
- }
- private static class MkdirsParentsCallable extends MasterToSlaveCallable {
- private final File file;
-
- private MkdirsParentsCallable(File file) {
- this.file = file;
- }
-
- @Override
- public String call() throws Exception {
- toFilePathOnController(this.file).mkdirs();
- return null;
- }
- }
-
- // --------
-
- @Test
- @Issue("SECURITY-2444")
- public void testNonCanonicalPath() throws Exception {
- assumeFalse(Functions.isWindows());
- final FreeStyleBuild build = j.createFreeStyleProject().scheduleBuild2(0, new Cause.UserIdCause()).waitForStart();
- j.waitForCompletion(build);
- final File link = new File(build.getRootDir(), "link");
- final File secrets = new File(j.jenkins.getRootDir(), "secrets/master.key");
- Files.createSymbolicLink(link.toPath(), secrets.toPath());
- assertThrowsIOExceptionCausedBySecurityException(() -> invokeOnAgent(new ReadToStringCallable(link)));
- }
- @Test
- @Issue("SECURITY-2444")
- public void testNonCanonicalPathOnController() throws Exception {
- assumeFalse(Functions.isWindows());
- final FreeStyleBuild build = j.createFreeStyleProject().scheduleBuild2(0, new Cause.UserIdCause()).waitForStart();
- j.waitForCompletion(build);
- final File link = new File(build.getRootDir(), "link");
- final File secrets = new File(j.jenkins.getRootDir(), "secrets/master.key");
- Files.createSymbolicLink(link.toPath(), secrets.toPath());
- String result = FilePath.localChannel.call(new ReadToStringCallable(link));
- assertEquals(IOUtils.readLines(new FileReader(secrets)).get(0), result);
- }
-
- private static class ReadToStringCallable extends MasterToSlaveCallable {
-
- final String abs;
-
- ReadToStringCallable(File link) {
- abs = link.getPath();
- }
-
- @Override
- public String call() throws IOException {
- FilePath p = toFilePathOnController(new File(abs));
- try {
- return p.readToString();
- } catch (InterruptedException e) {
- throw new IOException(e);
- }
- }
- }
-
- // --------
-
- @Test
- @Issue({"SECURITY-2446", "SECURITY-2531"})
- // $ tar tvf symlink.tar
- // lrwxr-xr-x 0 501 20 0 Oct 5 09:50 foo -> ../../../../secrets
- @LocalData
- public void testUntaringSymlinksFails() throws Exception {
- final FreeStyleBuild freeStyleBuild = j.buildAndAssertSuccess(j.createFreeStyleProject());
- final File symlinkTarFile = new File(j.jenkins.getRootDir(), "symlink.tar");
- final File untarTargetFile = new File(freeStyleBuild.getRootDir(), "foo");
- assertThrowsIOExceptionCausedBySecurityException(() -> invokeOnAgent(new UntarFileCallable(symlinkTarFile, untarTargetFile)));
- }
- private static final class UntarFileCallable extends MasterToSlaveCallable {
- private final File source;
- private final File destination;
-
- private UntarFileCallable(File source, File destination) {
- this.source = source;
- this.destination = destination;
- }
-
- @Override
- public Integer call() throws Exception {
- final FilePath sourceFilePath = new FilePath(source);
- final FilePath destinationFilePath = toFilePathOnController(destination);
- sourceFilePath.untar(destinationFilePath, FilePath.TarCompression.NONE);
- return 1;
- }
- }
-
- // --------
-
- @Test
- @Issue("SECURITY-2453")
- public void testTarSymlinksThatAreSafe() throws Exception {
- assumeFalse(Functions.isWindows());
- final File buildDir = j.buildAndAssertSuccess(j.createFreeStyleProject()).getRootDir();
- // We cannot touch the build dir itself
- final File innerDir = new File(buildDir, "dir");
- final File innerDir2 = new File(buildDir, "dir2");
- assertTrue(innerDir.mkdirs());
- assertTrue(innerDir2.mkdirs());
- assertTrue(new File(innerDir2, "the-file").createNewFile());
- Util.createSymlink(innerDir, "../dir2", "link", TaskListener.NULL);
- assertTrue(new File(innerDir, "link/the-file").exists());
- final int files = invokeOnAgent(new TarCaller(innerDir));
- assertEquals(1, files);
- }
- @Test
- @Issue("SECURITY-2453")
- public void testTarSymlinksOutsideAllowedDirs() throws Exception {
- assumeFalse(Functions.isWindows());
- final File buildDir = j.buildAndAssertSuccess(j.createFreeStyleProject()).getRootDir();
- // We cannot touch the build dir itself
- final File innerDir = new File(buildDir, "dir");
- assertTrue(innerDir.mkdirs());
- Util.createSymlink(innerDir, "../../../../../secrets", "secrets-link", TaskListener.NULL);
- assertTrue(new File(innerDir, "secrets-link/master.key").exists());
- logging.capture(10);
- assertThrowsIOExceptionCausedBySecurityException(() -> invokeOnAgent(new TarCaller(innerDir)));
- assertThat(logging, recorded(containsString("filepath-filters.d")));
- }
-
- private static class TarCaller extends MasterToSlaveCallable {
- private final File root;
-
- private TarCaller(File root) {
- this.root = root;
- }
-
- @Override
- public Integer call() throws Exception {
- return toFilePathOnController(root).tar(NullOutputStream.NULL_OUTPUT_STREAM, "**");
- }
- }
-
- // --------
-
- @Test
- @Issue("SECURITY-2484")
- public void zipTest() {
- final File secrets = new File(j.jenkins.getRootDir(), "secrets");
- assertTrue(secrets.exists());
- assertThrowsIOExceptionCausedBySecurityException(() -> invokeOnAgent(new ZipTestCallable(secrets)));
- }
- @Test
- @Issue("SECURITY-2484")
- public void zipTestController() throws Exception {
- final File secrets = new File(j.jenkins.getRootDir(), "secrets");
- assertTrue(secrets.exists());
- FilePath.localChannel.call(new ZipTestCallable(secrets));
- }
-
- private static class ZipTestCallable extends MasterToSlaveCallable {
- private final File file;
-
- private ZipTestCallable(File file) {
- this.file = file;
- }
-
- @Override
- public String call() throws Exception {
- final File tmp = File.createTempFile("security2455_", ".zip");
- tmp.deleteOnExit();
- toFilePathOnController(file).zip(new FilePath(tmp));
- return tmp.getName();
- }
- }
-
- // --------
-
- @Test
- @Issue("SECURITY-2485")
- @LocalData
- public void unzipRemoteTest() {
- final File targetDir = j.jenkins.getRootDir();
- final File source = new File(targetDir, "file.zip"); // in this test, controller and agent are on same FS so this works -- file needs to exist but content should not be read
- assertTrue(targetDir.exists());
- final List filesBefore = Arrays.asList(Objects.requireNonNull(targetDir.listFiles()));
- assertThrowsIOExceptionCausedBySecurityException(() -> invokeOnAgent(new UnzipRemoteTestCallable(targetDir, source)));
- final List filesAfter = Arrays.asList(Objects.requireNonNull(targetDir.listFiles()));
- // We cannot do a direct comparison here because `logs/` appears during execution
- assertEquals(filesBefore.size(), filesAfter.stream().filter(it -> !it.getName().equals("logs")).count());
- }
-
- private static class UnzipRemoteTestCallable extends MasterToSlaveCallable {
- private final File destination;
- private final File source;
-
- private UnzipRemoteTestCallable(File destination, File source) {
- this.destination = destination;
- this.source = source;
- }
-
- @Override
- public String call() throws Exception {
- FilePath onAgent = new FilePath(source);
- onAgent.unzip(toFilePathOnController(destination));
- return null;
- }
- }
-
- // --------
-
- @Test
- @Issue("SECURITY-2485")
- public void testCopyRecursiveFromControllerToAgent() {
- IOException ex = assertThrowsIOExceptionCausedBy(IOException.class, () -> invokeOnAgent(new CopyRecursiveToFromControllerToAgentCallable(new FilePath(new File(j.jenkins.getRootDir(), "secrets")))));
- assertThat(Objects.requireNonNull(ex).getMessage(), containsString("Unexpected end of ZLIB input stream")); // TODO this used to say "premature", why the change?
- }
- private static class CopyRecursiveToFromControllerToAgentCallable extends MasterToSlaveCallable {
- private final FilePath controllerFilePath;
-
- private CopyRecursiveToFromControllerToAgentCallable(FilePath controllerFilePath) {
- this.controllerFilePath = controllerFilePath;
- }
-
- @Override
- public Integer call() throws Exception {
- return controllerFilePath.copyRecursiveTo(new FilePath(Files.createTempDirectory("jenkins-test").toFile()));
- }
- }
-
- // --------
-
- @Test
- @Issue("SECURITY-2485")
- public void testCopyRecursiveFromAgentToController() {
- assertThrowsIOExceptionCausedBy(SecurityException.class, () -> invokeOnAgent(new CopyRecursiveToFromAgentToControllerCallable(new FilePath(new File(j.jenkins.getRootDir(), "secrets")))));
- }
- private static class CopyRecursiveToFromAgentToControllerCallable extends MasterToSlaveCallable {
- private final FilePath controllerFilePath;
-
- private CopyRecursiveToFromAgentToControllerCallable(FilePath controllerFilePath) {
- this.controllerFilePath = controllerFilePath;
- }
-
- @Override
- public Integer call() throws Exception {
- final File localPath = Files.createTempDirectory("jenkins-test").toFile();
- assertTrue(new File(localPath, "tmpfile").createNewFile());
- return new FilePath(localPath).copyRecursiveTo(controllerFilePath);
- }
- }
-
- // --------
-
- @Test
- @Issue("SECURITY-2486")
- public void testDecoyWrapper() {
- assertThrowsIOExceptionCausedBySecurityException(() -> invokeOnAgent(new ReadToStringBypassCallable(j.jenkins.getRootDir())));
- }
-
- private static class ReadToStringBypassCallable extends MasterToSlaveCallable {
- private final File file;
-
- private ReadToStringBypassCallable(File file) {
- this.file = file;
- }
-
- @Override
- public String call() throws Exception {
- final Class> readToStringClass = Class.forName("hudson.FilePath$ReadToString");
- final Constructor> constructor = readToStringClass.getDeclaredConstructor(); // Used to have FilePath.class from non-static context
- constructor.setAccessible(true);
-
- //FilePath agentFilePath = new FilePath(new File("on agent lol")); // only used for the core code before fix
-
- final SlaveToMasterFileCallable> callable = (SlaveToMasterFileCallable>) constructor.newInstance(); // agentFilePath
-
- FilePath controllerFilePath = toFilePathOnController(new File(file, "secrets/master.key"));
- final Object returned = controllerFilePath.act(callable);
- return (String) returned;
- }
- }
-
- // --------
-
- @Test
- @Issue("SECURITY-2531")
- public void testSymlinkCheck() throws Exception {
- final File buildDir = j.buildAndAssertSuccess(j.createFreeStyleProject()).getRootDir();
- assertThrowsIOExceptionCausedBySecurityException(() -> invokeOnAgent(new SymlinkCreator(buildDir)));
- }
- private static class SymlinkCreator extends MasterToSlaveCallable {
- private final File baseDir;
- private SymlinkCreator(File baseDir) {
- this.baseDir = baseDir;
- }
-
- @Override
- public String call() throws Exception {
- toFilePathOnController(new File(baseDir, "child")).symlinkTo(baseDir.getPath() + "child2", TaskListener.NULL);
- return null;
- }
- }
-
- // --------
-
- // --------
-
- @Test
- @Issue("SECURITY-2538") // SECURITY-2538 adjacent, confirms that reading, stating etc. is supposed to be possible for userContent
- public void testReadUserContent() throws Exception {
- invokeOnAgent(new UserContentReader(new File(j.jenkins.getRootDir(), "userContent")));
- }
- private static class UserContentReader extends MasterToSlaveCallable {
- private final File userContent;
-
- private UserContentReader(File userContent) {
- this.userContent = userContent;
- }
-
- @Override
- public String call() throws Exception {
- final FilePath userContentFilePath = toFilePathOnController(userContent);
- userContentFilePath.lastModified();
- userContentFilePath.zip(NullOutputStream.NULL_OUTPUT_STREAM);
- assertThat(userContentFilePath.child("readme.txt").readToString(), containsString(hudson.model.Messages.Hudson_USER_CONTENT_README()));
- return null;
- }
- }
-
- @Test
- @Issue("SECURITY-2538")
- public void testRenameTo() throws Exception {
- final File buildDir = j.buildAndAssertSuccess(j.createFreeStyleProject()).getRootDir();
- final File userContentDir = new File(j.jenkins.getRootDir(), "userContent");
- final File readme = new File(userContentDir, "readme.txt");
- final File to = new File(buildDir, "readme.txt");
- assertTrue("readme.txt is a file", readme.isFile());
- assertThrowsIOExceptionCausedBySecurityException(() -> invokeOnAgent(new RenameToCaller(readme, to)));
- assertTrue("readme.txt is still a file", readme.isFile());
- assertFalse("to does not exist", to.exists());
- }
- private static class RenameToCaller extends MasterToSlaveCallable {
- private final File from;
- private final File to;
-
- private RenameToCaller(File from, File to) {
- this.from = from;
- this.to = to;
- }
-
- @Override
- public String call() throws Exception {
- toFilePathOnController(from).renameTo(toFilePathOnController(to));
- return null;
- }
- }
-
- @Test
- @Issue("SECURITY-2538")
- public void testMoveChildren() throws Exception {
- final File buildDir = j.buildAndAssertSuccess(j.createFreeStyleProject()).getRootDir();
- final File userContentDir = new File(j.jenkins.getRootDir(), "userContent");
- // The implementation of MoveAllChildrenTo seems odd and ends up removing the source directory, so work only in subdir of userContent
- final File userContentSubDir = new File(userContentDir, "stuff");
- assertTrue(userContentSubDir.mkdirs());
- final File userContentSubDirFileA = new File(userContentSubDir, "fileA");
- final File userContentSubDirFileB = new File(userContentSubDir, "fileB");
- assertTrue(userContentSubDirFileA.createNewFile());
- assertTrue(userContentSubDirFileB.createNewFile());
- assertTrue("userContentSubDir is a directory", userContentSubDir.isDirectory());
- assertThrowsIOExceptionCausedBySecurityException(() -> invokeOnAgent(new MoveAllChildrenToCaller(userContentSubDir, buildDir)));
- assertTrue("userContentSubDir is a directory", userContentSubDir.isDirectory());
- assertFalse("no fileA in buildDir", new File(buildDir, "fileA").exists());
- assertFalse("no fileB in buildDir", new File(buildDir, "fileB").exists());
- assertTrue("fileA is still a file", userContentSubDirFileA.isFile());
- assertTrue("fileB is still a file", userContentSubDirFileB.isFile());
- }
- private static class MoveAllChildrenToCaller extends MasterToSlaveCallable {
- private final File from;
- private final File to;
-
- private MoveAllChildrenToCaller(File from, File to) {
- this.from = from;
- this.to = to;
- }
-
- @Override
- public String call() throws Exception {
- toFilePathOnController(from).moveAllChildrenTo(toFilePathOnController(to));
- return null;
- }
- }
-
- // --------
-
- @Test
- @Issue("SECURITY-2539")
- public void testCreateTempFile() {
- final File rootDir = j.jenkins.getRootDir();
- assertFalse(Arrays.stream(Objects.requireNonNull(rootDir.listFiles())).anyMatch(it -> it.getName().contains("prefix")));
-
- // Extra level of catch/throw
- logging.capture(10);
- final IOException ioException = assertThrowsIOExceptionCausedBy(IOException.class, () -> invokeOnAgent(new CreateTempFileCaller(rootDir)));
- assertNotNull(ioException);
- final Throwable cause = ioException.getCause();
- assertNotNull(cause);
- assertTrue(cause instanceof SecurityException);
- assertThat(cause.getMessage(), not(containsString("prefix"))); // redacted
- assertThat(logging, recorded(containsString("'create'")));
- assertThat(logging, recorded(containsString("/prefix-security-check-dummy-suffix")));
- assertFalse(Arrays.stream(Objects.requireNonNull(rootDir.listFiles())).anyMatch(it -> it.getName().contains("prefix")));
- }
- private static class CreateTempFileCaller extends MasterToSlaveCallable {
- private final File baseDir;
-
- private CreateTempFileCaller(File baseDir) {
- this.baseDir = baseDir;
- }
-
- @Override
- public String call() throws Exception {
- toFilePathOnController(baseDir).createTempFile("prefix", "suffix");
- return null;
- }
- }
-
-
- @Test
- @Issue("SECURITY-2539")
- public void testCreateTextTempFile() {
- final File rootDir = j.jenkins.getRootDir();
- assertFalse(Arrays.stream(Objects.requireNonNull(rootDir.listFiles())).anyMatch(it -> it.getName().contains("prefix")));
-
- // Extra level of catch/throw
- logging.capture(10);
- final IOException ioException = assertThrowsIOExceptionCausedBy(IOException.class, () -> invokeOnAgent(new CreateTextTempFileCaller(rootDir)));
- assertNotNull(ioException);
- final Throwable cause = ioException.getCause();
- assertNotNull(cause);
- assertTrue(cause instanceof SecurityException);
- assertThat(cause.getMessage(), not(containsString("prefix"))); // redacted
- assertThat(logging, recorded(containsString("'create'")));
- assertThat(logging, recorded(containsString("/prefix-security-check-dummy-suffix")));
- assertFalse(Arrays.stream(Objects.requireNonNull(rootDir.listFiles())).anyMatch(it -> it.getName().contains("prefix")));
- }
- private static class CreateTextTempFileCaller extends MasterToSlaveCallable {
- private final File baseDir;
-
- private CreateTextTempFileCaller(File baseDir) {
- this.baseDir = baseDir;
- }
-
- @Override
- public String call() throws Exception {
- toFilePathOnController(baseDir).createTextTempFile("prefix", "suffix", "content");
- return null;
- }
- }
-
- @Test
- @Issue("SECURITY-2539")
- public void testCreateTempDir() {
- final File rootDir = j.jenkins.getRootDir();
- assertFalse(Arrays.stream(Objects.requireNonNull(rootDir.listFiles())).anyMatch(it -> it.getName().contains("prefix")));
-
- // Extra level of catch/throw
- logging.capture(10);
- final IOException ioException = assertThrowsIOExceptionCausedBy(IOException.class, () -> invokeOnAgent(new CreateTempDirCaller(rootDir)));
- assertNotNull(ioException);
- final Throwable cause = ioException.getCause();
- assertNotNull(cause);
- assertTrue(cause instanceof SecurityException);
- assertThat(cause.getMessage(), not(containsString("prefix"))); // redacted
- assertThat(logging, recorded(containsString("'mkdirs'")));
- assertThat(logging, recorded(containsString("/prefix.suffix-security-test"))); // weird but that's what it looks like
- assertFalse(Arrays.stream(Objects.requireNonNull(rootDir.listFiles())).anyMatch(it -> it.getName().contains("prefix")));
- }
- private static class CreateTempDirCaller extends MasterToSlaveCallable {
- private final File baseDir;
-
- private CreateTempDirCaller(File baseDir) {
- this.baseDir = baseDir;
- }
-
- @Override
- public String call() throws Exception {
- toFilePathOnController(baseDir).createTempDir("prefix", "suffix");
- return null;
- }
- }
- // --------
-
- @Test
- @Issue("SECURITY-2541")
- public void testStatStuff() {
- assertThrowsIOExceptionCausedBySecurityException(() -> invokeOnAgent(new ToURICaller(j.jenkins.getRootDir())));
- assertThrowsIOExceptionCausedBySecurityException(() -> invokeOnAgent(new FreeDiskSpaceCaller(j.jenkins.getRootDir())));
- assertThrowsIOExceptionCausedBySecurityException(() -> invokeOnAgent(new TotalDiskSpaceCaller(j.jenkins.getRootDir())));
- assertThrowsIOExceptionCausedBySecurityException(() -> invokeOnAgent(new UsableDiskSpaceCaller(j.jenkins.getRootDir())));
- assertThrowsIOExceptionCausedBySecurityException(() -> invokeOnAgent(new AbsolutizeCaller(j.jenkins.getRootDir())));
- assertThrowsIOExceptionCausedBySecurityException(() -> invokeOnAgent(new HasSymlinkCaller(new File (j.jenkins.getRootDir(), "secrets"), j.jenkins.getRootDir())));
- assertThrowsIOExceptionCausedBySecurityException(() -> invokeOnAgent(new IsDescendantCaller(j.jenkins.getRootDir(), "secrets")));
- }
-
- private static class ToURICaller extends MasterToSlaveCallable {
- private final File file;
-
- private ToURICaller(File file) {
- this.file = file;
- }
-
- @Override
- public URI call() throws Exception {
- return toFilePathOnController(file).toURI();
- }
- }
- private static class AbsolutizeCaller extends MasterToSlaveCallable {
- private final File file;
-
- private AbsolutizeCaller(File file) {
- this.file = file;
- }
-
- @Override
- public String call() throws Exception {
- return toFilePathOnController(file).absolutize().getRemote();
- }
- }
- private static class HasSymlinkCaller extends MasterToSlaveCallable {
- private final File file;
- private final File root;
-
- private HasSymlinkCaller(File file, File root) {
- this.file = file;
- this.root = root;
- }
-
- @Override
- public Boolean call() throws Exception {
- return toFilePathOnController(file).hasSymlink(toFilePathOnController(root), false);
- }
- }
- private static class IsDescendantCaller extends MasterToSlaveCallable {
- private final File file;
- private final String childPath;
-
- private IsDescendantCaller(File file, String childPath) {
- this.file = file;
- this.childPath = childPath;
- }
-
- @Override
- public Boolean call() throws Exception {
- return toFilePathOnController(file).isDescendant(childPath);
- }
- }
- private static class FreeDiskSpaceCaller extends MasterToSlaveCallable {
- private final File file;
-
- private FreeDiskSpaceCaller(File file) {
- this.file = file;
- }
-
- @Override
- public Long call() throws Exception {
- return toFilePathOnController(file).getFreeDiskSpace();
- }
- }
- private static class UsableDiskSpaceCaller extends MasterToSlaveCallable {
- private final File file;
-
- private UsableDiskSpaceCaller(File file) {
- this.file = file;
- }
-
- @Override
- public Long call() throws Exception {
- return toFilePathOnController(file).getUsableDiskSpace();
- }
- }
- private static class TotalDiskSpaceCaller extends MasterToSlaveCallable {
- private final File file;
-
- private TotalDiskSpaceCaller(File file) {
- this.file = file;
- }
-
- @Override
- public Long call() throws Exception {
- return toFilePathOnController(file).getTotalDiskSpace();
- }
- }
-
- // --------
-
- @Test
- @Issue("SECURITY-2542") // adjacent, this confirms we follow symlinks when it's within allowed directories
- public void testGlobFollowsSymlinks() throws Exception {
- assumeFalse(Functions.isWindows());
- final File buildDir = j.buildAndAssertSuccess(j.createFreeStyleProject()).getRootDir();
- // We cannot touch the build dir itself
- final File innerDir = new File(buildDir, "dir");
- final File innerDir2 = new File(buildDir, "dir2");
- assertTrue(innerDir.mkdirs());
- assertTrue(innerDir2.mkdirs());
- assertTrue(new File(innerDir2, "the-file").createNewFile());
- Util.createSymlink(innerDir, "../dir2", "link", TaskListener.NULL);
- assertTrue(new File(innerDir, "link/the-file").exists());
- final int files = invokeOnAgent(new GlobCaller(innerDir));
- assertEquals(1, files);
- }
- @Test
- @Issue("SECURITY-2542")
- public void testGlobSymlinksThrowsOutsideAllowedDirectories() throws Exception {
- assumeFalse(Functions.isWindows());
- final File buildDir = j.buildAndAssertSuccess(j.createFreeStyleProject()).getRootDir();
- // We cannot touch the build dir itself
- final File innerDir = new File(buildDir, "dir");
- assertTrue(innerDir.mkdirs());
- Util.createSymlink(innerDir, "../../../../../secrets", "secrets-link", TaskListener.NULL);
- assertTrue(new File(innerDir, "secrets-link/master.key").exists());
- assertThrowsIOExceptionCausedBySecurityException(() -> invokeOnAgent(new GlobCaller(innerDir)));
- }
- private static class GlobCaller extends MasterToSlaveCallable {
- private final File root;
-
- private GlobCaller(File root) {
- this.root = root;
- }
-
- @Override
- public Integer call() throws Exception {
- return toFilePathOnController(root).list("**/*", "", false).length;
- }
- }
-
- // --------
-
- @Issue("SECURITY-2455") // general issue -- Maven Projects would no longer be allowed to perform some actions
- @Test
- public void testMavenReportersAllowListForTopLevelJob() throws Exception {
- final FreeStyleProject project = j.createFreeStyleProject();
- final File topLevelProjectDir = project.getRootDir();
-
- // similar but wrong names:
- assertThrowsIOExceptionCausedBySecurityException(() -> invokeOnAgent(new MkDirsWriter(new File(topLevelProjectDir, "not-site"))));
- assertThrowsIOExceptionCausedBySecurityException(() -> invokeOnAgent(new MkDirsWriter(new File(topLevelProjectDir, "not-javadoc"))));
-
- // project-level archived stuff:
- invokeOnAgent(new MkDirsWriter(new File(topLevelProjectDir, "javadoc")));
- invokeOnAgent(new MkDirsWriter(new File(topLevelProjectDir, "test-javadoc")));
- invokeOnAgent(new MkDirsWriter(new File(topLevelProjectDir, "site")));
- assertThrowsIOExceptionCausedBySecurityException(() -> invokeOnAgent(new MkDirsWriter(new File(topLevelProjectDir, "cobertura"))));
-
- // cannot mkdirs this from agent:
- assertThrowsIOExceptionCausedBySecurityException(() -> invokeOnAgent(new MkDirsWriter(new File(topLevelProjectDir, "modules"))));
-
- final File mavenModuleDir = new File(topLevelProjectDir, "modules/pretend-maven-module");
- assertTrue(mavenModuleDir.mkdirs());
-
- // module-level archived stuff:
- invokeOnAgent(new MkDirsWriter(new File(mavenModuleDir, "javadoc")));
- invokeOnAgent(new MkDirsWriter(new File(mavenModuleDir, "test-javadoc")));
- assertThrowsIOExceptionCausedBySecurityException(() -> invokeOnAgent(new MkDirsWriter(new File(mavenModuleDir, "site"))));
- invokeOnAgent(new MkDirsWriter(new File(mavenModuleDir, "cobertura")));
- }
-
- @Issue("SECURITY-2455") // general issue -- Maven Projects would no longer be allowed to perform some actions
- @Test
- public void testMavenReportersAllowListForJobInFolder() throws Exception {
- final MockFolder theFolder = j.createFolder("theFolder");
- {
- // basic child job
- final FreeStyleProject childProject = theFolder.createProject(FreeStyleProject.class, "child");
- final File childProjectRootDir = childProject.getRootDir();
-
- // project-level archived stuff for child project inside folder:
- invokeOnAgent(new MkDirsWriter(new File(childProjectRootDir, "javadoc")));
- invokeOnAgent(new MkDirsWriter(new File(childProjectRootDir, "test-javadoc")));
- invokeOnAgent(new MkDirsWriter(new File(childProjectRootDir, "site")));
- assertThrowsIOExceptionCausedBySecurityException(() -> invokeOnAgent(new MkDirsWriter(new File(childProjectRootDir, "cobertura"))));
- }
-
- { // misleadingly named child job (like one of the approved folders):
- final FreeStyleProject siteChildProject = theFolder.createProject(FreeStyleProject.class, "site");
- final File siteChildProjectRootDir = siteChildProject.getRootDir();
-
- // cannot mkdirs this from agent despite 'site' in the path (but on wrong level):
- assertThrowsIOExceptionCausedBySecurityException(() -> invokeOnAgent(new MkDirsWriter(siteChildProjectRootDir)));
- assertThrowsIOExceptionCausedBySecurityException(() -> invokeOnAgent(new MkDirsWriter(new File(siteChildProjectRootDir, "foo"))));
- assertThrowsIOExceptionCausedBySecurityException(() -> invokeOnAgent(new MkDirsWriter(new File(siteChildProjectRootDir, "modules"))));
-
- // project-level archived stuff for another child inside folder:
- invokeOnAgent(new MkDirsWriter(new File(siteChildProjectRootDir, "javadoc")));
- invokeOnAgent(new MkDirsWriter(new File(siteChildProjectRootDir, "test-javadoc")));
- invokeOnAgent(new MkDirsWriter(new File(siteChildProjectRootDir, "site")));
- assertThrowsIOExceptionCausedBySecurityException(() -> invokeOnAgent(new MkDirsWriter(new File(siteChildProjectRootDir, "cobertura"))));
-
- final File childProjectMavenModuleDir = new File(siteChildProjectRootDir, "modules/pretend-maven-module");
- assertTrue(childProjectMavenModuleDir.mkdirs());
-
- // module-level archived stuff:
- invokeOnAgent(new MkDirsWriter(new File(childProjectMavenModuleDir, "javadoc")));
- invokeOnAgent(new MkDirsWriter(new File(childProjectMavenModuleDir, "test-javadoc")));
- assertThrowsIOExceptionCausedBySecurityException(() -> invokeOnAgent(new MkDirsWriter(new File(childProjectMavenModuleDir, "site"))));
- invokeOnAgent(new MkDirsWriter(new File(childProjectMavenModuleDir, "cobertura")));
- }
- }
-
- private static class MkDirsWriter extends MasterToSlaveCallable {
- private final File root;
-
- private MkDirsWriter(File root) {
- this.root = root;
- }
-
- @Override
- public Object call() throws Exception {
- toFilePathOnController(root).mkdirs();
- toFilePathOnController(new File(root, "file.txt")).write("text", "UTF-8");
- return null;
- }
- }
-
- // --------
-
- // Misc tests
-
- @LocalData
- @Test
- public void testRemoteLocalUnzip() throws Exception {
- final DumbSlave onlineSlave = j.createOnlineSlave();
- final File zipFile = new File(j.jenkins.getRootDir(), "file.zip");
- assertTrue(zipFile.isFile());
- final FilePath agentRootPath = onlineSlave.getRootPath();
- final FilePath agentZipPath = agentRootPath.child("file.zip");
- new FilePath(zipFile).copyTo(agentZipPath);
- agentZipPath.unzip(agentRootPath);
- }
-
- // --------
-
- // Utility functions
-
- protected static FilePath toFilePathOnController(File file) {
- return toFilePathOnController(file.getPath());
- }
-
- protected static FilePath toFilePathOnController(String path) {
- final VirtualChannel channel = AgentComputerUtil.getChannelToMaster();
- return new FilePath(channel, path);
- }
-
- protected Node agent;
-
- protected T invokeOnAgent(MasterToSlaveCallable callable) throws Exception, X {
- if (agent == null) {
- agent = j.createOnlineSlave();
- }
- return Objects.requireNonNull(agent.getChannel()).call(callable);
- }
-
- private static SecurityException assertThrowsIOExceptionCausedBySecurityException(ThrowingRunnable runnable) {
- return assertThrowsIOExceptionCausedBy(SecurityException.class, runnable);
- }
-
- private static X assertThrowsIOExceptionCausedBy(Class causeClass, ThrowingRunnable runnable) {
- try {
- runnable.run();
- } catch (IOException ex) {
- final Throwable cause = ex.getCause();
- assertTrue("IOException with message: '" + ex.getMessage() + "' wasn't caused by " + causeClass + ": " + (cause == null ? "(null)" : (cause.getClass().getName() + ": " + cause.getMessage())),
- cause != null && causeClass.isAssignableFrom(cause.getClass()));
- return causeClass.cast(cause);
- } catch (Throwable t) {
- fail("Threw other Throwable: " + t.getClass() + " with message " + t.getMessage());
- }
- fail("Expected exception but passed");
- return null;
- }
-}
diff --git a/test/src/test/java/jenkins/security/s2m/AdminFilePathFilterTest.java b/test/src/test/java/jenkins/security/s2m/AdminFilePathFilterTest.java
deleted file mode 100644
index 383417a2db9e..000000000000
--- a/test/src/test/java/jenkins/security/s2m/AdminFilePathFilterTest.java
+++ /dev/null
@@ -1,235 +0,0 @@
-/*
- * The MIT License
- *
- * Copyright 2016 CloudBees, Inc.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-package jenkins.security.s2m;
-
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.allOf;
-import static org.hamcrest.Matchers.containsString;
-import static org.hamcrest.Matchers.empty;
-import static org.hamcrest.Matchers.hasSize;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-import static org.jvnet.hudson.test.LoggerRule.recorded;
-
-import hudson.ExtensionList;
-import hudson.FilePath;
-import hudson.model.Slave;
-import hudson.remoting.Callable;
-import java.io.File;
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.io.StringWriter;
-import java.lang.reflect.Field;
-import java.util.List;
-import java.util.logging.Level;
-import javax.inject.Inject;
-import jenkins.SoloFilePathFilter;
-import jenkins.telemetry.impl.SlaveToMasterFileCallableUsage;
-import org.jenkinsci.remoting.RoleChecker;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.jvnet.hudson.test.Issue;
-import org.jvnet.hudson.test.JenkinsRule;
-import org.jvnet.hudson.test.LoggerRule;
-
-public class AdminFilePathFilterTest {
-
- @Rule
- public JenkinsRule r = new JenkinsRule();
-
- @Rule
- public LoggerRule logging = new LoggerRule().record(SoloFilePathFilter.class, Level.WARNING);
-
- @Inject
- AdminWhitelistRule rule;
-
- @Before
- public void setUp() {
- r.jenkins.getInjector().injectMembers(this);
- rule.setMasterKillSwitch(false);
- }
-
- @Issue({"JENKINS-27055", "SECURITY-358"})
- @Test
- public void matchBuildDir() throws Exception {
- File buildDir = r.buildAndAssertSuccess(r.createFreeStyleProject()).getRootDir();
- assertTrue(rule.checkFileAccess("write", new File(buildDir, "whatever")));
- assertFalse(rule.checkFileAccess("write", new File(buildDir, "build.xml")));
- // WorkflowRun:
- assertFalse(rule.checkFileAccess("write", new File(buildDir, "program.dat")));
- assertFalse(rule.checkFileAccess("write", new File(buildDir, "workflow/23.xml")));
- }
-
- @Test
- public void slaveCannotReadFileFromSecrets_butCanFromUserContent() throws Exception {
- Slave s = r.createOnlineSlave();
- FilePath root = r.jenkins.getRootPath();
-
- { // agent can read userContent folder
- FilePath rootUserContentFolder = root.child("userContent");
- FilePath rootTargetPublic = rootUserContentFolder.child("target_public.txt");
- rootTargetPublic.write("target_public", null);
- checkSlave_can_readFile(s, rootTargetPublic);
- }
-
- { // agent cannot read files inside secrets
- FilePath rootSecretFolder = root.child("secrets");
- FilePath rootTargetPrivate = rootSecretFolder.child("target_private.txt");
- rootTargetPrivate.write("target_private", null);
-
- checkSlave_cannot_readFile(s, rootTargetPrivate);
- }
-
- rule.setMasterKillSwitch(true);
-
- { // with the master kill switch activated, agent can read files inside secrets
- FilePath rootSecretFolder = root.child("secrets");
- FilePath rootTargetPrivate = rootSecretFolder.child("target_private.txt");
-
- checkSlave_can_readFile(s, rootTargetPrivate);
- }
-
- @SuppressWarnings("unchecked")
- List traces = (List) ExtensionList.lookupSingleton(SlaveToMasterFileCallableUsage.class).createContent().getJSONArray("traces");
- assertThat(traces, hasSize(1));
- assertThat(traces.get(0), allOf(containsString("Command UserRequest:hudson.FilePath$ReadToString@… created at"), containsString(ReadFileS2MCallable.class.getName() + ".call")));
- @SuppressWarnings("unchecked")
- List cleared = (List) ExtensionList.lookupSingleton(SlaveToMasterFileCallableUsage.class).createContent().getJSONArray("traces");
- assertThat(cleared, empty());
- }
-
- private static class ReadFileS2MCallable implements Callable {
- private final FilePath p;
- ReadFileS2MCallable(FilePath p) {
- this.p = p;
- }
- @Override
- public String call() throws Exception {
- assertTrue(p.isRemote());
- return p.readToString();
- }
- @Override
- public void checkRoles(RoleChecker checker) throws SecurityException {
- // simulate legacy Callable impls
- throw new NoSuchMethodError();
- }
- }
-
- @Test
- @Issue("SECURITY-788")
- public void slaveCannotUse_dotDotSlashStuff_toBypassRestriction() throws Exception {
- Slave s = r.createOnlineSlave();
- FilePath root = r.jenkins.getRootPath();
-
- { // use ../ to access a non-restricted folder
- FilePath rootUserContentFolder = root.child("userContent");
- FilePath rootTargetPublic = rootUserContentFolder.child("target_public.txt");
- rootTargetPublic.write("target_public", null);
-
- FilePath dotDotSlashTargetPublic = root.child("logs/target_public.txt");
- replaceRemote(dotDotSlashTargetPublic, "logs", "logs/../userContent");
-
- checkSlave_can_readFile(s, dotDotSlashTargetPublic);
- }
-
- { // use ../ to try to bypass the rules
- FilePath rootSecretFolder = root.child("secrets");
- FilePath rootTargetPrivate = rootSecretFolder.child("target_private.txt");
- rootTargetPrivate.write("target_private", null);
-
- FilePath dotDotSlashTargetPrivate = root.child("userContent/target_private.txt");
- replaceRemote(dotDotSlashTargetPrivate, "userContent", "userContent/../secrets");
-
- checkSlave_cannot_readFile(s, dotDotSlashTargetPrivate);
- }
- }
-
- @Test
- @Issue("SECURITY-788")
- public void slaveCannotUse_encodedCharacters_toBypassRestriction() throws Exception {
- Slave s = r.createOnlineSlave();
- FilePath root = r.jenkins.getRootPath();
-
- // \u002e is the Unicode of . and is interpreted directly by Java as .
-
- { // use ../ to access a non-restricted folder
- FilePath rootUserContentFolder = root.child("userContent");
- FilePath rootTargetPublic = rootUserContentFolder.child("target_public.txt");
- rootTargetPublic.write("target_public", null);
-
- FilePath dotDotSlashTargetPublic = root.child("logs/target_public.txt");
- replaceRemote(dotDotSlashTargetPublic, "logs", "logs/\u002e\u002e/userContent");
-
- checkSlave_can_readFile(s, dotDotSlashTargetPublic);
- }
-
- { // use ../ to try to bypass the rules
- FilePath rootSecretFolder = root.child("secrets");
- FilePath rootTargetPrivate = rootSecretFolder.child("target_private.txt");
- rootTargetPrivate.write("target_private", null);
-
- FilePath dotDotSlashTargetPrivate = root.child("userContent/target_private.txt");
- replaceRemote(dotDotSlashTargetPrivate, "userContent", "userContent/\u002e\u002e/secrets");
-
- checkSlave_cannot_readFile(s, dotDotSlashTargetPrivate);
- }
- }
-
- private void checkSlave_can_readFile(Slave s, FilePath target) throws Exception {
- // The agent can read file from userContent
- String content = s.getChannel().call(new ReadFileS2MCallable(target));
- // and the controller can directly reach it
- assertEquals(target.readToString(), content);
- }
-
- private void checkSlave_cannot_readFile(Slave s, FilePath target) throws Exception {
- try {
- logging.capture(10);
- s.getChannel().call(new ReadFileS2MCallable(target));
- fail("Slave should not be able to read file in " + target.getRemote());
- } catch (IOException e){
- Throwable t = e.getCause();
- assertTrue(t instanceof SecurityException);
- SecurityException se = (SecurityException) t;
- StringWriter sw = new StringWriter();
- se.printStackTrace(new PrintWriter(sw));
- assertTrue(sw.toString().contains("Agent may not access a file path"));
-
- assertThat(logging, recorded(containsString("Agent may not 'read' at")));
- }
- }
-
- // to bypass the normalization done in constructor
- private void replaceRemote(FilePath p, String before, String after) throws Exception {
- Field field = FilePath.class.getDeclaredField("remote");
- field.setAccessible(true);
- String currentRemote = (String) field.get(p);
- String newRemote = currentRemote.replace(before, after);
- field.set(p, newRemote);
- }
-}
diff --git a/test/src/test/java/jenkins/security/s2m/DefaultFilePathFilterTest.java b/test/src/test/java/jenkins/security/s2m/DefaultFilePathFilterTest.java
deleted file mode 100644
index a9ff8d4fe501..000000000000
--- a/test/src/test/java/jenkins/security/s2m/DefaultFilePathFilterTest.java
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * The MIT License
- *
- * Copyright 2014 Jesse Glick.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-package jenkins.security.s2m;
-
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.containsString;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertThrows;
-import static org.junit.Assert.assertTrue;
-
-import hudson.FilePath;
-import hudson.Functions;
-import hudson.model.Slave;
-import hudson.remoting.Callable;
-import java.io.File;
-import java.io.IOException;
-import javax.inject.Inject;
-import org.jenkinsci.remoting.RoleChecker;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.jvnet.hudson.test.JenkinsRule;
-
-public class DefaultFilePathFilterTest {
-
- @Rule public JenkinsRule r = new JenkinsRule();
-
- @Inject
- AdminWhitelistRule rule;
-
- @Before
- public void setUp() {
- r.jenkins.getInjector().injectMembers(this);
- rule.setMasterKillSwitch(false);
- }
-
- @Test public void remotePath() throws Exception {
- Slave s = r.createOnlineSlave();
- FilePath forward = s.getRootPath().child("forward");
- forward.write("hello", null);
- assertEquals("hello", s.getRootPath().act(new LocalCallable(forward)));
- FilePath reverse = new FilePath(new File(r.jenkins.root, "reverse"));
- assertFalse(reverse.exists());
- IOException x = assertThrows(IOException.class, () -> s.getChannel().call(new ReverseCallable(reverse)));
- // make sure that the stack trace contains the call site info to help assist diagnosis
- assertThat(Functions.printThrowable(x), containsString(DefaultFilePathFilterTest.class.getName() + ".remotePath"));
- assertFalse(reverse.exists());
- DefaultFilePathFilter.BYPASS = true;
- s.getChannel().call(new ReverseCallable(reverse));
- assertTrue(reverse.exists());
- assertEquals("goodbye", reverse.readToString());
- }
-
- private static class LocalCallable implements Callable {
- private final FilePath p;
- LocalCallable(FilePath p) {
- this.p = p;
- }
- @Override public String call() throws Exception {
- assertFalse(p.isRemote());
- return p.readToString();
- }
- @Override
- public void checkRoles(RoleChecker checker) throws SecurityException {
- throw new NoSuchMethodError(); // simulate legacy Callable impls
- }
- }
-
- private static class ReverseCallable implements Callable {
- private final FilePath p;
- ReverseCallable(FilePath p) {
- this.p = p;
- }
- @Override public Void call() throws Exception {
- assertTrue(p.isRemote());
- p.write("goodbye", null);
- return null;
- }
- @Override
- public void checkRoles(RoleChecker checker) throws SecurityException {
- throw new NoSuchMethodError(); // simulate legacy Callable impls
- }
- }
-
-}
diff --git a/test/src/test/java/jenkins/security/s2m/RunningBuildFilePathFilterTest.java b/test/src/test/java/jenkins/security/s2m/RunningBuildFilePathFilterTest.java
deleted file mode 100644
index b560f4df312b..000000000000
--- a/test/src/test/java/jenkins/security/s2m/RunningBuildFilePathFilterTest.java
+++ /dev/null
@@ -1,146 +0,0 @@
-/*
- * The MIT License
- *
- * Copyright 2021 CloudBees, Inc.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-package jenkins.security.s2m;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-
-import hudson.ExtensionList;
-import hudson.FilePath;
-import hudson.Functions;
-import hudson.Launcher;
-import hudson.model.AbstractBuild;
-import hudson.model.BuildListener;
-import hudson.model.FreeStyleProject;
-import java.io.File;
-import java.io.IOException;
-import java.nio.charset.StandardCharsets;
-import java.util.function.Function;
-import jenkins.security.MasterToSlaveCallable;
-import org.apache.commons.io.FileUtils;
-import org.junit.ClassRule;
-import org.junit.Rule;
-import org.junit.Test;
-import org.jvnet.hudson.test.BuildWatcher;
-import org.jvnet.hudson.test.Issue;
-import org.jvnet.hudson.test.JenkinsRule;
-import org.jvnet.hudson.test.TestBuilder;
-
-@Issue("SECURITY-2428")
-public class RunningBuildFilePathFilterTest {
-
- @ClassRule
- public static BuildWatcher buildWatcher = new BuildWatcher();
-
- @Rule
- public JenkinsRule r = new JenkinsRule();
-
- @Test
- public void accessPermittedOnlyFromCurrentBuild() throws Exception {
- ExtensionList.lookupSingleton(AdminWhitelistRule.class).setMasterKillSwitch(false);
- FreeStyleProject main = r.createFreeStyleProject("main");
- main.setAssignedNode(r.createSlave());
- WriteBackPublisher wbp = new WriteBackPublisher();
- main.getBuildersList().add(wbp);
- // Normal case: writing to our own build directory
- wbp.controllerFile = build -> new File(build.getRootDir(), "stuff.txt");
- r.buildAndAssertSuccess(main);
- // Attacks:
- wbp.legal = false;
- // Writing to someone else’s build directory (covered by RunningBuildFilePathFilter)
- FreeStyleProject other = r.createFreeStyleProject("other");
- r.buildAndAssertSuccess(other);
- wbp.controllerFile = build -> new File(other.getBuildByNumber(1).getRootDir(), "hack");
- r.buildAndAssertSuccess(main);
- // Writing to some other directory (covered by AdminWhitelistRule)
- wbp.controllerFile = build -> new File(r.jenkins.getRootDir(), "hack");
- r.buildAndAssertSuccess(main);
- // Writing to a sensitive file even in my own build dir (covered by AdminWhitelistRule)
- wbp.controllerFile = build -> new File(build.getRootDir(), "build.xml");
- r.buildAndAssertSuccess(main);
- // Writing to the directory of an earlier build
- wbp.controllerFile = build -> new File(main.getBuildByNumber(1).getRootDir(), "stuff.txt");
- r.buildAndAssertSuccess(main);
-
- System.setProperty(RunningBuildFilePathFilter.class.getName() + ".FAIL", "false");
- try {
- wbp.legal = true;
- wbp.controllerFile = build -> new File(main.getBuildByNumber(1).getRootDir(), "stuff.txt");
- r.buildAndAssertSuccess(main);
- } finally {
- System.clearProperty(RunningBuildFilePathFilter.class.getName() + ".FAIL");
- }
- }
-
- private static final class WriteBackPublisher extends TestBuilder {
- Function, File> controllerFile;
- boolean legal = true;
- @Override
- public boolean perform(AbstractBuild, ?> build, Launcher launcher, BuildListener listener) throws InterruptedException, IOException {
- File f = controllerFile.apply(build);
- listener.getLogger().println("Will try to write to " + f + "; legal? " + legal);
- String text = build.getExternalizableId();
- try {
- launcher.getChannel().call(new WriteBackCallable(new FilePath(f), text));
- if (legal) {
- assertEquals(text, FileUtils.readFileToString(f, StandardCharsets.UTF_8));
- listener.getLogger().println("Allowed as expected");
- } else {
- fail("should not have been allowed");
- }
- } catch (Exception x) {
- if (!legal && x.toString().contains("SecurityException")) {
- // TODO assert error message is either from RunningBuildFilePathFilter or from SoloFilePathFilter
- Functions.printStackTrace(x, listener.error("Rejected as expected!"));
- } else {
- throw x;
- }
- }
- return true;
- }
- }
-
- private static final class WriteBackCallable extends MasterToSlaveCallable {
- private final FilePath controllerFile;
- private final String text;
- WriteBackCallable(FilePath controllerFile, String text) {
- this.controllerFile = controllerFile;
- this.text = text;
- }
- @Override
- public Void call() throws IOException {
- assertTrue(controllerFile.isRemote());
- try {
- controllerFile.write(text, null);
- } catch (InterruptedException x) {
- throw new IOException(x);
- }
- return null;
- }
-
- }
-
-}
diff --git a/test/src/test/java/jenkins/security/stapler/Security914Test.java b/test/src/test/java/jenkins/security/stapler/Security914Test.java
index 3bbeb36d98c5..3b66d4da6595 100644
--- a/test/src/test/java/jenkins/security/stapler/Security914Test.java
+++ b/test/src/test/java/jenkins/security/stapler/Security914Test.java
@@ -25,18 +25,19 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assume.assumeTrue;
import com.gargoylesoftware.htmlunit.Page;
import com.gargoylesoftware.htmlunit.WebRequest;
import hudson.Functions;
import java.net.HttpURLConnection;
import java.net.URL;
-import org.junit.Assume;
import org.junit.Rule;
import org.junit.Test;
import org.jvnet.hudson.test.Issue;
import org.jvnet.hudson.test.JenkinsRule;
-import org.jvnet.hudson.test.TestPluginManager;
+import org.jvnet.hudson.test.recipes.WithPlugin;
@Issue("SECURITY-914")
public class Security914Test {
@@ -45,12 +46,11 @@ public class Security914Test {
public JenkinsRule j = new JenkinsRule();
@Test
+ @WithPlugin("credentials.hpi")
public void cannotUseInvalidLocale_toTraverseFolder() throws Exception {
- Assume.assumeTrue(Functions.isWindows());
+ assumeTrue(Functions.isWindows());
- if (j.jenkins.pluginManager.getPlugin("credentials") == null) {
- ((TestPluginManager) j.jenkins.pluginManager).installDetachedPlugin("credentials");
- }
+ assertNotNull(j.getPluginManager().getPlugin("credentials"));
j.createWebClient().goTo("plugin/credentials/images/24x24/credentials.png", "image/png");
JenkinsRule.WebClient wc = j.createWebClient()
@@ -67,12 +67,11 @@ public void cannotUseInvalidLocale_toTraverseFolder() throws Exception {
}
@Test
+ @WithPlugin("credentials.hpi")
public void cannotUseInvalidLocale_toAnyFileInSystem() throws Exception {
- Assume.assumeTrue(Functions.isWindows());
-
- if (j.jenkins.pluginManager.getPlugin("credentials") == null) {
- ((TestPluginManager) j.jenkins.pluginManager).installDetachedPlugin("credentials");
- }
+ assumeTrue(Functions.isWindows());
+
+ assertNotNull(j.getPluginManager().getPlugin("credentials"));
j.createWebClient().goTo("plugin/credentials/images/24x24/credentials.png", "image/png");
JenkinsRule.WebClient wc = j.createWebClient()
diff --git a/test/src/test/java/jenkins/security/stapler/StaplerDispatchValidatorTest.java b/test/src/test/java/jenkins/security/stapler/StaplerDispatchValidatorTest.java
index f5554da6505c..fd02d7dee8a2 100644
--- a/test/src/test/java/jenkins/security/stapler/StaplerDispatchValidatorTest.java
+++ b/test/src/test/java/jenkins/security/stapler/StaplerDispatchValidatorTest.java
@@ -59,8 +59,8 @@ public void canViewStaplerViews() throws Exception {
String[] urls = {"annotated/explicitRoot", "extended/explicitRoot", "extended/whitelistedRoot"};
for (String url : urls) {
HtmlPage root = j.createWebClient().goTo(url);
- assertEquals("Fragment", root.getElementById("frag").asText());
- assertEquals("Explicit Fragment", root.getElementById("explicit-frag").asText());
+ assertEquals("Fragment", root.getElementById("frag").asNormalizedText());
+ assertEquals("Explicit Fragment", root.getElementById("explicit-frag").asNormalizedText());
}
}
@@ -87,7 +87,7 @@ public void canViewIndex() throws Exception {
String[] urls = {"annotated", "groovy", "jelly"};
for (String url : urls) {
HtmlPage root = j.createWebClient().goTo(url);
- assertEquals("Fragment", root.getElementById("frag").asText());
+ assertEquals("Fragment", root.getElementById("frag").asNormalizedText());
}
}
@@ -96,7 +96,7 @@ public void canViewPagesThatIncludeViews() throws Exception {
String[] urls = {"groovy/include", "jelly/include"};
for (String url : urls) {
HtmlPage root = j.createWebClient().goTo(url);
- assertEquals("Fragment", root.getElementById("frag").asText());
+ assertEquals("Fragment", root.getElementById("frag").asNormalizedText());
}
}
@@ -105,7 +105,7 @@ public void canViewPagesThatRedirectToViews() throws Exception {
String[] urls = {"groovy/redirect", "jelly/redirect"};
for (String url : urls) {
HtmlPage root = j.createWebClient().goTo(url);
- assertEquals("Fragment", root.getElementById("frag").asText());
+ assertEquals("Fragment", root.getElementById("frag").asNormalizedText());
}
}
@@ -114,7 +114,7 @@ public void canViewCompressedViews() throws Exception {
String[] urls = {"groovy/compress", "jelly/compress"};
for (String url : urls) {
HtmlPage root = j.createWebClient().goTo(url);
- assertEquals("Fragment", root.getElementById("frag").asText());
+ assertEquals("Fragment", root.getElementById("frag").asNormalizedText());
}
}
diff --git a/test/src/test/java/jenkins/tasks/SimpleBuildWrapperTest.java b/test/src/test/java/jenkins/tasks/SimpleBuildWrapperTest.java
index bb469e8bea0b..3278150953a8 100644
--- a/test/src/test/java/jenkins/tasks/SimpleBuildWrapperTest.java
+++ b/test/src/test/java/jenkins/tasks/SimpleBuildWrapperTest.java
@@ -189,7 +189,7 @@ private static final class DisposerImpl extends Disposer {
FreeStyleProject p = r.createFreeStyleProject();
p.setScm(new FailingSCM());
p.getBuildWrappersList().add(new PreCheckoutWrapperWithDisposer());
- FreeStyleBuild b = r.assertBuildStatus(Result.FAILURE, p.scheduleBuild2(0));
+ FreeStyleBuild b = r.buildAndAssertStatus(Result.FAILURE, p);
r.assertLogContains("ran DisposerImpl #1", b);
r.assertLogNotContains("ran DisposerImpl #2", b);
}
@@ -221,7 +221,7 @@ public ChangeLogParser createChangeLogParser() {
p.getBuildWrappersList().add(new WrapperWithDisposer());
p.getBuildWrappersList().add(new InterruptedDisposerWrapper());
// build is ABORTED because of InterruptedException during tearDown (trumps the FAILURE result)
- FreeStyleBuild b = r.assertBuildStatus(Result.ABORTED, p.scheduleBuild2(0));
+ FreeStyleBuild b = r.buildAndAssertStatus(Result.ABORTED, p);
r.assertLogContains("tearDown InterruptedDisposerImpl", b);
r.assertLogContains("ran DisposerImpl", b); // ran despite earlier InterruptedException
}
diff --git a/test/src/test/java/jenkins/tasks/filters/impl/RetainVariablesLocalRuleTest.java b/test/src/test/java/jenkins/tasks/filters/impl/RetainVariablesLocalRuleTest.java
index 14010c6e7c7d..47c12d62cd19 100644
--- a/test/src/test/java/jenkins/tasks/filters/impl/RetainVariablesLocalRuleTest.java
+++ b/test/src/test/java/jenkins/tasks/filters/impl/RetainVariablesLocalRuleTest.java
@@ -250,7 +250,7 @@ public void retainVariable_removeSystemVariables_shell() throws Exception {
Shell shell = new Shell("env");
p.getBuildersList().add(shell);
- FreeStyleBuild build = j.assertBuildStatus(Result.SUCCESS, p.scheduleBuild2(0, (Cause) null));
+ FreeStyleBuild build = j.buildAndAssertSuccess(p);
List unfilteredLogOutput = build.getLog(200).stream().filter(s -> s.contains("=")).map(s -> s.substring(0, s.indexOf('='))).collect(Collectors.toList());
p.getBuildersList().remove(shell);
@@ -264,7 +264,7 @@ public void retainVariable_removeSystemVariables_shell() throws Exception {
filteredShell.setConfiguredLocalRules(Collections.singletonList(localRule));
p.getBuildersList().add(filteredShell);
- build = j.assertBuildStatus(Result.SUCCESS, p.scheduleBuild2(0, (Cause) null));
+ build = j.buildAndAssertSuccess(p);
List filteredLogOutput = build.getLog(200).stream().filter(s -> s.contains("=")).map(s -> s.substring(0, s.indexOf('='))).collect(Collectors.toList());
assertTrue(filteredLogOutput.size() < unfilteredLogOutput.size() - 10); // 10 is a value slightly larger than the number of characteristic env vars (7)
diff --git a/test/src/test/java/jenkins/triggers/ReverseBuildTriggerTest.java b/test/src/test/java/jenkins/triggers/ReverseBuildTriggerTest.java
index d89b57db0112..1cbfada7afaa 100644
--- a/test/src/test/java/jenkins/triggers/ReverseBuildTriggerTest.java
+++ b/test/src/test/java/jenkins/triggers/ReverseBuildTriggerTest.java
@@ -206,7 +206,7 @@ public void nullJobInTriggerNotCausesNPE() throws Exception {
r.configRoundtrip(downstreamJob2);
r.jenkins.rebuildDependencyGraph();
- final FreeStyleBuild build = upstreamJob.scheduleBuild2(0).get();
+ final FreeStyleBuild build = r.buildAndAssertSuccess(upstreamJob);
r.waitUntilNoActivity();
r.assertLogNotContains("java.lang.NullPointerException", build);
diff --git a/test/src/test/java/jenkins/widgets/BuildTimeTrendTest.java b/test/src/test/java/jenkins/widgets/BuildTimeTrendTest.java
index c19572fae3cf..ac1c3e2579b6 100644
--- a/test/src/test/java/jenkins/widgets/BuildTimeTrendTest.java
+++ b/test/src/test/java/jenkins/widgets/BuildTimeTrendTest.java
@@ -27,12 +27,14 @@
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeFalse;
import com.gargoylesoftware.htmlunit.html.DomNode;
import com.gargoylesoftware.htmlunit.html.DomNodeList;
import com.gargoylesoftware.htmlunit.html.HtmlPage;
import com.gargoylesoftware.htmlunit.html.HtmlTable;
import edu.umd.cs.findbugs.annotations.NonNull;
+import hudson.Functions;
import hudson.XmlFile;
import hudson.model.FreeStyleProject;
import hudson.model.ItemGroup;
@@ -62,8 +64,9 @@ public class BuildTimeTrendTest {
@Test
public void withAbstractJob_OnBuiltInNode() throws Exception {
+ assumeFalse("TODO: Windows container agents do not have enough resources to run this test", Functions.isWindows() && System.getenv("CI") != null);
FreeStyleProject p = j.createFreeStyleProject();
- j.assertBuildStatusSuccess(p.scheduleBuild2(0));
+ j.buildAndAssertSuccess(p);
JenkinsRule.WebClient wc = j.createWebClient();
@@ -76,11 +79,12 @@ public void withAbstractJob_OnBuiltInNode() throws Exception {
@Test
public void withAbstractJob_OnAgentNode() throws Exception {
+ assumeFalse("TODO: Windows container agents do not have enough resources to run this test", Functions.isWindows() && System.getenv("CI") != null);
DumbSlave agent = j.createSlave();
FreeStyleProject p = j.createFreeStyleProject();
p.setAssignedNode(agent);
- j.assertBuildStatusSuccess(p.scheduleBuild2(0));
+ j.buildAndAssertSuccess(p);
JenkinsRule.WebClient wc = j.createWebClient();
@@ -95,14 +99,15 @@ public void withAbstractJob_OnAgentNode() throws Exception {
@Test
public void withAbstractJob_OnBoth() throws Exception {
+ assumeFalse("TODO: Windows container agents do not have enough resources to run this test", Functions.isWindows() && System.getenv("CI") != null);
DumbSlave agent = j.createSlave();
FreeStyleProject p = j.createFreeStyleProject();
p.setAssignedNode(j.jenkins);
- j.assertBuildStatusSuccess(p.scheduleBuild2(0));
+ j.buildAndAssertSuccess(p);
p.setAssignedNode(agent);
- j.assertBuildStatusSuccess(p.scheduleBuild2(0));
+ j.buildAndAssertSuccess(p);
JenkinsRule.WebClient wc = j.createWebClient();
@@ -128,6 +133,7 @@ public void withAbstractJob_OnBoth() throws Exception {
@Test
@LocalData("localDataNonAbstractJob")
public void withNonAbstractJob_withoutAgents() throws Exception {
+ assumeFalse("TODO: Windows container agents do not have enough resources to run this test", Functions.isWindows() && System.getenv("CI") != null);
JenkinsRule.WebClient wc = j.createWebClient();
TopLevelItem p = j.jenkins.getItem("job0");
assertThat(p, instanceOf(NonAbstractJob.class));
@@ -147,6 +153,7 @@ public void withNonAbstractJob_withoutAgents() throws Exception {
@LocalData("localDataNonAbstractJob")
@Issue("JENKINS-63232")
public void withNonAbstractJob_withAgents() throws Exception {
+ assumeFalse("TODO: Windows container agents do not have enough resources to run this test", Functions.isWindows() && System.getenv("CI") != null);
// just to trigger data-is-distributed-build-enabled = true
j.createSlave();
diff --git a/test/src/test/java/lib/form/ApplyButtonTest.java b/test/src/test/java/lib/form/ApplyButtonTest.java
index 295f172917ee..3914b68f247b 100644
--- a/test/src/test/java/lib/form/ApplyButtonTest.java
+++ b/test/src/test/java/lib/form/ApplyButtonTest.java
@@ -27,7 +27,7 @@ public class ApplyButtonTest {
public void editDescription() throws Exception {
j.jenkins.setMarkupFormatter(RawHtmlMarkupFormatter.INSTANCE); // need something using CodeMirror
FreeStyleProject p = j.createFreeStyleProject();
- FreeStyleBuild b = j.assertBuildStatusSuccess(p.scheduleBuild2(0));
+ FreeStyleBuild b = j.buildAndAssertSuccess(p);
HtmlPage config = j.createWebClient().getPage(b, "configure");
HtmlForm form = config.getFormByName("config");
diff --git a/test/src/test/java/lib/layout/IconTest.java b/test/src/test/java/lib/layout/IconTest.java
index 18c115dfe01b..e59e01e47def 100644
--- a/test/src/test/java/lib/layout/IconTest.java
+++ b/test/src/test/java/lib/layout/IconTest.java
@@ -142,7 +142,7 @@ public void testTasks() throws Exception {
assertIconToImageOkay(taskDivs.get(0).getElementsByTagName("img").get(0), "/images/svgs/up.svg", "icon-up icon-md");
// this is loading the png from cloudbees-folder plugin
// when this is swapped to an SVG and the dep updated this test will need to change
- assertIconToImageOkay(taskDivs.get(1).getElementsByTagName("img").get(0), "/images/24x24/folder.png", "icon-folder icon-md");
+ assertIconToImageOkay(taskDivs.get(1).getElementsByTagName("img").get(0), "/images/svgs/folder.svg", "icon-folder icon-md");
assertIconToImageOkay(taskDivs.get(2).getElementsByTagName("img").get(0), "/images/svgs/package.svg", "icon-package icon-xlg");
assertIconToImageOkay(taskDivs.get(3).getElementsByTagName("img").get(0), "/images/svgs/package.svg", "icon-package icon-xlg");
assertIconToImageOkay(taskDivs.get(4).getElementsByTagName("img").get(0), "/images/svgs/package.svg", "icon-package icon-xlg");
diff --git a/war/pom.xml b/war/pom.xml
index 413436828bda..d6cb8dddc820 100644
--- a/war/pom.xml
+++ b/war/pom.xml
@@ -63,7 +63,7 @@ THE SOFTWARE.
org.jenkins-ciexecutable-war
- 2.1
+ 2.2provided
diff --git a/war/src/main/js/widgets/config/tabbar.less b/war/src/main/js/widgets/config/tabbar.less
index 93131e6b9536..4b0f51411409 100644
--- a/war/src/main/js/widgets/config/tabbar.less
+++ b/war/src/main/js/widgets/config/tabbar.less
@@ -12,6 +12,7 @@
min-height:2.5em;
.border-radius-top(@border-radius);
z-index:5;
+ overflow: hidden;
nav:before, nav:after {
display: none;
@@ -50,7 +51,6 @@
.form-config.tabBarFrame {
position: relative;
- border-bottom: solid 1px var(--medium-grey);
.config-section-activators {
margin: 0;
@@ -99,10 +99,11 @@
}
#jenkins{
.jenkins-config {
- border:1px solid var(--input-border);
- padding: 2rem;
- border-top:none;
- background:var(--bright-bg-color);
+ border: 1px solid var(--input-border);
+ padding: 2rem 2rem 0 2rem;
+ border-top: none;
+ border-bottom: none;
+ background: var(--bright-bg-color);
.border-radius-bottom(@border-radius);
.showTabs{
diff --git a/war/src/main/less/base/style.less b/war/src/main/less/base/style.less
index 37f491781d48..1a7f7a97481f 100644
--- a/war/src/main/less/base/style.less
+++ b/war/src/main/less/base/style.less
@@ -446,18 +446,26 @@ div.behavior-loading {
}
}
-label.attach-previous {
- margin-left: 0.5em;
-}
-
.bottom-sticker,
#bottom-sticker {
width: 100%; /* it needs to occupy the entire width or else the underlying content will see through */
}
.bottom-sticker-inner {
+ position: relative;
+ padding: 1em 0;
+ z-index: 0;
+
+ &::before {
+ content: "";
+ position: absolute;
+ top: 0;
+ left: calc(-2rem - 2px);
+ bottom: 0;
+ right: 0;
background: var(--background);
- padding: 1em 0;
+ z-index: -1;
+ }
}
.top-sticker,
diff --git a/war/src/main/less/modules/form.less b/war/src/main/less/modules/form.less
index 9c72fa5a3854..9543a4b7cba5 100644
--- a/war/src/main/less/modules/form.less
+++ b/war/src/main/less/modules/form.less
@@ -245,7 +245,7 @@
max-width: 100% !important; // TODO remove important after https://github.com/jenkinsci/credentials-plugin/pull/255
border-radius: 6px;
box-shadow: 0 0 0 10px transparent;
- transition: 0.2s ease;
+ transition: var(--standard-transition);
min-height: 38px;
&:hover {
@@ -265,6 +265,25 @@
}
}
+.jenkins-multi-select {
+ position: relative;
+ width: 100%;
+ border: 2px solid var(--input-border);
+ border-radius: var(--form-input-border-radius);
+ box-shadow: 0 0 0 10px transparent;
+ transition: var(--standard-transition);
+ outline: none;
+
+ &:focus {
+ border-color: var(--focus-input-border);
+ box-shadow: 0 0 0 5px var(--focus-input-glow);
+ }
+
+ &:disabled {
+ pointer-events: none;
+ }
+}
+
.jenkins-radio-help-wrapper {
display: flex;
align-items: center;
diff --git a/war/src/main/webapp/help/parameter/boolean_ru.html b/war/src/main/webapp/help/parameter/boolean_ru.html
new file mode 100644
index 000000000000..ab41a328b8e3
--- /dev/null
+++ b/war/src/main/webapp/help/parameter/boolean_ru.html
@@ -0,0 +1,4 @@
+
+ Boolean-параметр, который можно использовать во время сборки, либо в качестве переменной окружения, либо
+ путем подстановки переменных в других частях конфигурации. Строковое значение будет представлено в виде 'true' или 'false'.
+
\ No newline at end of file
diff --git a/war/src/main/webapp/help/parameter/choice-choices_ru.html b/war/src/main/webapp/help/parameter/choice-choices_ru.html
new file mode 100644
index 000000000000..9e2c4ad4012f
--- /dev/null
+++ b/war/src/main/webapp/help/parameter/choice-choices_ru.html
@@ -0,0 +1,3 @@
+
+ Возможные варианты, каждый должен начинаться с новой строки. Первое значение будет значением по умолчанию.
+
\ No newline at end of file
diff --git a/war/src/main/webapp/help/parameter/description_ru.html b/war/src/main/webapp/help/parameter/description_ru.html
new file mode 100644
index 000000000000..85aafe977dd0
--- /dev/null
+++ b/war/src/main/webapp/help/parameter/description_ru.html
@@ -0,0 +1,3 @@
+
+ Описание, которое увидит пользователь при запуске задачи.
+
\ No newline at end of file
diff --git a/war/src/main/webapp/help/parameter/name_ru.html b/war/src/main/webapp/help/parameter/name_ru.html
new file mode 100644
index 000000000000..088414b866bb
--- /dev/null
+++ b/war/src/main/webapp/help/parameter/name_ru.html
@@ -0,0 +1,6 @@
+
+ Название параметра
+
+
+ Эти параметры доступны при сборке в качестве переменных среды.
+
\ No newline at end of file
diff --git a/war/src/main/webapp/help/parameter/run-filter_ru.html b/war/src/main/webapp/help/parameter/run-filter_ru.html
new file mode 100644
index 000000000000..564628f79803
--- /dev/null
+++ b/war/src/main/webapp/help/parameter/run-filter_ru.html
@@ -0,0 +1,9 @@
+
+Фильтрация списка сборок, которые доступны при запуске задачи.
+
+'Все сборки' - Все сборки, включая незавершенные.
+'Только завершенные сборки' - Все завершенные сборки.
+'Только успешные сборки' - Все успешные сборки (стабильные и нестабильные).
+'Только стабильные сборки' - Все стабильные сборки.
+
+
\ No newline at end of file
diff --git a/war/src/main/webapp/help/parameter/run-project_ru.html b/war/src/main/webapp/help/parameter/run-project_ru.html
new file mode 100644
index 000000000000..3d9b28dd8fb2
--- /dev/null
+++ b/war/src/main/webapp/help/parameter/run-project_ru.html
@@ -0,0 +1,11 @@
+
+ Задача, из которой пользователь может выбирать определенную сборку. По умолчанию выбирается последняя сборка.
+ Эти параметры доступны во время сборки в виде переменных окружения:
+