diff --git a/core/src/main/java/jenkins/security/ResourceDomainRootAction.java b/core/src/main/java/jenkins/security/ResourceDomainRootAction.java index 7955103d6b..fc18071fad 100644 --- a/core/src/main/java/jenkins/security/ResourceDomainRootAction.java +++ b/core/src/main/java/jenkins/security/ResourceDomainRootAction.java @@ -117,7 +117,7 @@ public class ResourceDomainRootAction implements UnprotectedRootAction { return null; } - if (!ACL.isAnonymous2(Jenkins.getAuthentication2())) { + if (!ALLOW_AUTHENTICATED_USER && !ACL.isAnonymous2(Jenkins.getAuthentication2())) { rsp.sendError(400); return null; } @@ -327,4 +327,8 @@ public class ResourceDomainRootAction implements UnprotectedRootAction { // Not @Restricted because the entire class is @SuppressFBWarnings(value = "MS_SHOULD_BE_FINAL", justification = "for script console") public static /* not final for Groovy */ int VALID_FOR_MINUTES = SystemProperties.getInteger(ResourceDomainRootAction.class.getName() + ".validForMinutes", 30); + + /* Escape hatch for a security hardening preventing one of the known ways to elevate arbitrary file read to RCE */ + @SuppressFBWarnings(value = "MS_SHOULD_BE_FINAL", justification = "for script console") + public static /* not final for Groovy */ boolean ALLOW_AUTHENTICATED_USER = SystemProperties.getBoolean(ResourceDomainRootAction.class.getName() + ".allowAuthenticatedUser", false); } diff --git a/test/src/test/java/jenkins/security/ResourceDomainTest.java b/test/src/test/java/jenkins/security/ResourceDomainTest.java index 42f2a1dbbf..b8f2d551b0 100644 --- a/test/src/test/java/jenkins/security/ResourceDomainTest.java +++ b/test/src/test/java/jenkins/security/ResourceDomainTest.java @@ -399,7 +399,7 @@ public class ResourceDomainTest { } @Test - public void authenticatedCannotAccessResourceDomain() throws Exception { + public void authenticatedCannotAccessResourceDomainUnlessAllowedBySystemProperty() throws Exception { j.jenkins.setSecurityRealm(j.createDummySecurityRealm()); final MockAuthorizationStrategy authorizationStrategy = new MockAuthorizationStrategy(); authorizationStrategy.grant(Jenkins.ADMINISTER).everywhere().to("admin").grant(Jenkins.READ).everywhere().toEveryone(); @@ -416,5 +416,13 @@ public class ResourceDomainTest { try (JenkinsRule.WebClient wc = j.createWebClient().withBasicCredentials("admin")) { assertThat(assertThrows(FailingHttpStatusCodeException.class, () -> wc.getPage(new URL(resourceUrl))).getStatusCode(), is(400)); } + + ResourceDomainRootAction.ALLOW_AUTHENTICATED_USER = true; + try (JenkinsRule.WebClient wc = j.createWebClient().withBasicApiToken("admin")) { + assertThat(wc.getPage(new URL(resourceUrl)).getWebResponse().getStatusCode(), is(200)); + } + try (JenkinsRule.WebClient wc = j.createWebClient().withBasicCredentials("admin")) { + assertThat(wc.getPage(new URL(resourceUrl)).getWebResponse().getStatusCode(), is(200)); + } } }