Skip to content

Commit 5a245e4

Browse files
jglickjenkinsci-cert-ci
authored andcommitted
1 parent 63cde2d commit 5a245e4

File tree

3 files changed

+104
-4
lines changed

3 files changed

+104
-4
lines changed

core/src/main/java/jenkins/security/s2m/FilePathRuleConfig.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,9 @@ protected FilePathRule parse(String line) {
4848
if (line.isEmpty()) return null;
4949

5050
// TODO This does not support custom build dir configuration (Jenkins#getRawBuildsDir() etc.)
51-
line = line.replace("<BUILDDIR>","<JOBDIR>/builds/<BUILDID>");
51+
line = line.replace("<BUILDDIR>","<JOBDIR>/builds/[0-9]+");
52+
53+
// Kept only for compatibility with custom user-provided rules:
5254
line = line.replace("<BUILDID>","(?:[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]+)");
5355
line = line.replace("<JOBDIR>","<JENKINS_HOME>/jobs/.+");
5456
final File jenkinsHome = Jenkins.get().getRootDir();

core/src/main/resources/jenkins/security/s2m/filepath-filter.conf

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,12 @@ deny all <BUILDDIR>/checkpoints($|/.*)
3838
# But not allowing deletion to prevent data loss and symlink to prevent jailbreaking.
3939
allow create,mkdirs,read,stat,write <BUILDDIR>/.+
4040

41-
# cobertura also writes out annotated sources to a dir under the job:
42-
allow create,mkdirs,read,stat,write <JENKINS_HOME>/jobs/.+/cobertura.*
41+
# cobertura also writes out annotated sources to a dir under the Maven module:
42+
allow create,mkdirs,read,stat,write <JOBDIR>/modules/([^/]+)/cobertura($|/.*)
43+
44+
# Some maven-plugin reporters also create content outside of build directories (including one in cobertura but that is covered above):
45+
allow create,mkdirs,read,stat,write <JENKINS_HOME>(/jobs/([^/]+))+(|/modules/([^/]+))/(javadoc|test-javadoc)($|/.*)
46+
allow create,mkdirs,read,stat,write <JENKINS_HOME>(/jobs/([^/]+))+/site($|/.*)
4347

4448
# all the other accesses that aren't specified here will be left up to other rules in this directory.
4549
# if no rules in those other files matches, then the access will be rejected.

test/src/test/java/jenkins/security/Security2455Test.java

Lines changed: 95 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import hudson.Util;
1818
import hudson.model.Cause;
1919
import hudson.model.FreeStyleBuild;
20+
import hudson.model.FreeStyleProject;
2021
import hudson.model.Node;
2122
import hudson.model.TaskListener;
2223
import hudson.remoting.VirtualChannel;
@@ -44,6 +45,7 @@
4445
import org.jvnet.hudson.test.Issue;
4546
import org.jvnet.hudson.test.JenkinsRule;
4647
import org.jvnet.hudson.test.LoggerRule;
48+
import org.jvnet.hudson.test.MockFolder;
4749
import org.jvnet.hudson.test.recipes.LocalData;
4850

4951
@SuppressWarnings("ThrowableNotThrown")
@@ -719,6 +721,94 @@ public Integer call() throws Exception {
719721

720722
// --------
721723

724+
@Issue("SECURITY-2455") // general issue -- Maven Projects would no longer be allowed to perform some actions
725+
@Test
726+
public void testMavenReportersAllowListForTopLevelJob() throws Exception {
727+
final FreeStyleProject project = j.createFreeStyleProject();
728+
final File topLevelProjectDir = project.getRootDir();
729+
730+
// similar but wrong names:
731+
assertThrowsIOExceptionCausedBySecurityException(() -> invokeOnAgent(new MkDirsWriter(new File(topLevelProjectDir, "not-site"))));
732+
assertThrowsIOExceptionCausedBySecurityException(() -> invokeOnAgent(new MkDirsWriter(new File(topLevelProjectDir, "not-javadoc"))));
733+
734+
// project-level archived stuff:
735+
invokeOnAgent(new MkDirsWriter(new File(topLevelProjectDir, "javadoc")));
736+
invokeOnAgent(new MkDirsWriter(new File(topLevelProjectDir, "test-javadoc")));
737+
invokeOnAgent(new MkDirsWriter(new File(topLevelProjectDir, "site")));
738+
assertThrowsIOExceptionCausedBySecurityException(() -> invokeOnAgent(new MkDirsWriter(new File(topLevelProjectDir, "cobertura"))));
739+
740+
// cannot mkdirs this from agent:
741+
assertThrowsIOExceptionCausedBySecurityException(() -> invokeOnAgent(new MkDirsWriter(new File(topLevelProjectDir, "modules"))));
742+
743+
final File mavenModuleDir = new File(topLevelProjectDir, "modules/pretend-maven-module");
744+
assertTrue(mavenModuleDir.mkdirs());
745+
746+
// module-level archived stuff:
747+
invokeOnAgent(new MkDirsWriter(new File(mavenModuleDir, "javadoc")));
748+
invokeOnAgent(new MkDirsWriter(new File(mavenModuleDir, "test-javadoc")));
749+
assertThrowsIOExceptionCausedBySecurityException(() -> invokeOnAgent(new MkDirsWriter(new File(mavenModuleDir, "site"))));
750+
invokeOnAgent(new MkDirsWriter(new File(mavenModuleDir, "cobertura")));
751+
}
752+
753+
@Issue("SECURITY-2455") // general issue -- Maven Projects would no longer be allowed to perform some actions
754+
@Test
755+
public void testMavenReportersAllowListForJobInFolder() throws Exception {
756+
final MockFolder theFolder = j.createFolder("theFolder");
757+
{
758+
// basic child job
759+
final FreeStyleProject childProject = theFolder.createProject(FreeStyleProject.class, "child");
760+
final File childProjectRootDir = childProject.getRootDir();
761+
762+
// project-level archived stuff for child project inside folder:
763+
invokeOnAgent(new MkDirsWriter(new File(childProjectRootDir, "javadoc")));
764+
invokeOnAgent(new MkDirsWriter(new File(childProjectRootDir, "test-javadoc")));
765+
invokeOnAgent(new MkDirsWriter(new File(childProjectRootDir, "site")));
766+
assertThrowsIOExceptionCausedBySecurityException(() -> invokeOnAgent(new MkDirsWriter(new File(childProjectRootDir, "cobertura"))));
767+
}
768+
769+
{ // misleadingly named child job (like one of the approved folders):
770+
final FreeStyleProject siteChildProject = theFolder.createProject(FreeStyleProject.class, "site");
771+
final File siteChildProjectRootDir = siteChildProject.getRootDir();
772+
773+
// cannot mkdirs this from agent despite 'site' in the path (but on wrong level):
774+
assertThrowsIOExceptionCausedBySecurityException(() -> invokeOnAgent(new MkDirsWriter(siteChildProjectRootDir)));
775+
assertThrowsIOExceptionCausedBySecurityException(() -> invokeOnAgent(new MkDirsWriter(new File(siteChildProjectRootDir, "foo"))));
776+
assertThrowsIOExceptionCausedBySecurityException(() -> invokeOnAgent(new MkDirsWriter(new File(siteChildProjectRootDir, "modules"))));
777+
778+
// project-level archived stuff for another child inside folder:
779+
invokeOnAgent(new MkDirsWriter(new File(siteChildProjectRootDir, "javadoc")));
780+
invokeOnAgent(new MkDirsWriter(new File(siteChildProjectRootDir, "test-javadoc")));
781+
invokeOnAgent(new MkDirsWriter(new File(siteChildProjectRootDir, "site")));
782+
assertThrowsIOExceptionCausedBySecurityException(() -> invokeOnAgent(new MkDirsWriter(new File(siteChildProjectRootDir, "cobertura"))));
783+
784+
final File childProjectMavenModuleDir = new File(siteChildProjectRootDir, "modules/pretend-maven-module");
785+
assertTrue(childProjectMavenModuleDir.mkdirs());
786+
787+
// module-level archived stuff:
788+
invokeOnAgent(new MkDirsWriter(new File(childProjectMavenModuleDir, "javadoc")));
789+
invokeOnAgent(new MkDirsWriter(new File(childProjectMavenModuleDir, "test-javadoc")));
790+
assertThrowsIOExceptionCausedBySecurityException(() -> invokeOnAgent(new MkDirsWriter(new File(childProjectMavenModuleDir, "site"))));
791+
invokeOnAgent(new MkDirsWriter(new File(childProjectMavenModuleDir, "cobertura")));
792+
}
793+
}
794+
795+
private static class MkDirsWriter extends MasterToSlaveCallable<Object, Exception> {
796+
private final File root;
797+
798+
private MkDirsWriter(File root) {
799+
this.root = root;
800+
}
801+
802+
@Override
803+
public Object call() throws Exception {
804+
toFilePathOnController(root).mkdirs();
805+
toFilePathOnController(new File(root, "file.txt")).write("text", "UTF-8");
806+
return null;
807+
}
808+
}
809+
810+
// --------
811+
722812
// Utility functions
723813

724814
protected static FilePath toFilePathOnController(File file) {
@@ -730,8 +820,12 @@ protected static FilePath toFilePathOnController(String path) {
730820
return new FilePath(channel, path);
731821
}
732822

823+
protected Node agent;
824+
733825
protected <T, X extends Throwable> T invokeOnAgent(MasterToSlaveCallable<T, X> callable) throws Exception, X {
734-
final Node agent = j.createOnlineSlave();
826+
if (agent == null) {
827+
agent = j.createOnlineSlave();
828+
}
735829
return Objects.requireNonNull(agent.getChannel()).call(callable);
736830
}
737831

0 commit comments

Comments
 (0)