Skip to content

Commit 6333a2a

Browse files
committed
Merge branch 'master' into JENKINS-72988
2 parents c5840c0 + 5fe9a44 commit 6333a2a

File tree

16 files changed

+82
-20
lines changed

16 files changed

+82
-20
lines changed

ath.sh

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ set -o xtrace
66
cd "$(dirname "$0")"
77

88
# https://github.com/jenkinsci/acceptance-test-harness/releases
9-
export ATH_VERSION=5941.v95f3439136c7
9+
export ATH_VERSION=5957.v7c0e2f7ca_63e
1010

1111
if [[ $# -eq 0 ]]; then
1212
export JDK=17

bom/pom.xml

+1-1
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,7 @@ THE SOFTWARE.
189189
<dependency>
190190
<groupId>org.apache.ant</groupId>
191191
<artifactId>ant</artifactId>
192-
<version>1.10.14</version>
192+
<version>1.10.15</version>
193193
</dependency>
194194
<dependency>
195195
<groupId>org.apache.commons</groupId>

cli/src/main/java/hudson/cli/CLI.java

+23-1
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import jakarta.websocket.ClientEndpointConfig;
3333
import jakarta.websocket.Endpoint;
3434
import jakarta.websocket.EndpointConfig;
35+
import jakarta.websocket.HandshakeResponse;
3536
import jakarta.websocket.Session;
3637
import java.io.DataInputStream;
3738
import java.io.File;
@@ -64,6 +65,7 @@
6465
import org.glassfish.tyrus.client.ClientManager;
6566
import org.glassfish.tyrus.client.ClientProperties;
6667
import org.glassfish.tyrus.client.SslEngineConfigurator;
68+
import org.glassfish.tyrus.client.exception.DeploymentHandshakeException;
6769
import org.glassfish.tyrus.container.jdk.client.JdkClientContainer;
6870

6971
/**
@@ -340,13 +342,19 @@ public void onOpen(Session session, EndpointConfig config) {}
340342
}
341343

342344
class Authenticator extends ClientEndpointConfig.Configurator {
345+
HandshakeResponse hr;
343346
@Override
344347
public void beforeRequest(Map<String, List<String>> headers) {
345348
if (factory.authorization != null) {
346349
headers.put("Authorization", List.of(factory.authorization));
347350
}
348351
}
352+
@Override
353+
public void afterResponse(HandshakeResponse hr) {
354+
this.hr = hr;
355+
}
349356
}
357+
var authenticator = new Authenticator();
350358

351359
ClientManager client = ClientManager.createClient(JdkClientContainer.class.getName()); // ~ ContainerProvider.getWebSocketContainer()
352360
client.getProperties().put(ClientProperties.REDIRECT_ENABLED, true); // https://tyrus-project.github.io/documentation/1.13.1/index/tyrus-proprietary-config.html#d0e1775
@@ -357,7 +365,21 @@ public void beforeRequest(Map<String, List<String>> headers) {
357365
sslEngineConfigurator.setHostnameVerifier((s, sslSession) -> true);
358366
client.getProperties().put(ClientProperties.SSL_ENGINE_CONFIGURATOR, sslEngineConfigurator);
359367
}
360-
Session session = client.connectToServer(new CLIEndpoint(), ClientEndpointConfig.Builder.create().configurator(new Authenticator()).build(), URI.create(url.replaceFirst("^http", "ws") + "cli/ws"));
368+
Session session;
369+
try {
370+
session = client.connectToServer(new CLIEndpoint(), ClientEndpointConfig.Builder.create().configurator(authenticator).build(), URI.create(url.replaceFirst("^http", "ws") + "cli/ws"));
371+
} catch (DeploymentHandshakeException x) {
372+
System.err.println("CLI handshake failed with status code " + x.getHttpStatusCode());
373+
if (authenticator.hr != null) {
374+
for (var entry : authenticator.hr.getHeaders().entrySet()) {
375+
// org.glassfish.tyrus.core.Utils.parseHeaderValue improperly splits values like Date at commas, so undo that:
376+
System.err.println(entry.getKey() + ": " + String.join(", ", entry.getValue()));
377+
}
378+
// UpgradeResponse.getReasonPhrase is useless since Jetty generates it from the code,
379+
// and the body is not accessible at all.
380+
}
381+
return 15; // compare CLICommand.main
382+
}
361383
PlainCLIProtocol.Output out = new PlainCLIProtocol.Output() {
362384
@Override
363385
public void send(byte[] data) throws IOException {

core/src/main/java/hudson/model/TopLevelItemDescriptor.java

+9
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import edu.umd.cs.findbugs.annotations.CheckForNull;
2828
import edu.umd.cs.findbugs.annotations.NonNull;
2929
import hudson.ExtensionList;
30+
import hudson.util.FormValidation;
3031
import java.io.StringWriter;
3132
import java.util.logging.Level;
3233
import java.util.logging.Logger;
@@ -37,7 +38,11 @@
3738
import org.jenkins.ui.icon.Icon;
3839
import org.jenkins.ui.icon.IconSet;
3940
import org.jenkins.ui.icon.IconSpec;
41+
import org.kohsuke.accmod.Restricted;
42+
import org.kohsuke.accmod.restrictions.NoExternalUse;
43+
import org.kohsuke.stapler.AncestorInPath;
4044
import org.kohsuke.stapler.MetaClass;
45+
import org.kohsuke.stapler.QueryParameter;
4146
import org.kohsuke.stapler.Stapler;
4247
import org.kohsuke.stapler.StaplerRequest;
4348
import org.kohsuke.stapler.WebApp;
@@ -286,4 +291,8 @@ public static ExtensionList<TopLevelItemDescriptor> all() {
286291
return Items.all();
287292
}
288293

294+
@Restricted(NoExternalUse.class)
295+
public FormValidation doCheckDisplayNameOrNull(@AncestorInPath TopLevelItem item, @QueryParameter String value) {
296+
return Jenkins.get().doCheckDisplayName(value, item.getName());
297+
}
289298
}

core/src/main/java/hudson/slaves/JNLPLauncher.java

+1-3
Original file line numberDiff line numberDiff line change
@@ -213,9 +213,7 @@ private String getRemotingOptions(String computerName) {
213213
sb.append("-name ");
214214
sb.append(computerName);
215215
sb.append(' ');
216-
if (isWebSocket()) {
217-
sb.append("-webSocket ");
218-
}
216+
sb.append("-webSocket ");
219217
if (tunnel != null) {
220218
sb.append(" -tunnel ");
221219
sb.append(tunnel);

core/src/main/java/jenkins/model/BackgroundGlobalBuildDiscarder.java

-3
Original file line numberDiff line numberDiff line change
@@ -57,12 +57,9 @@ protected void execute(TaskListener listener) throws IOException, InterruptedExc
5757
}
5858

5959
public static void processJob(TaskListener listener, Job job) {
60-
listener.getLogger().println("Processing " + job.getFullName());
6160
GlobalBuildDiscarderConfiguration.get().getConfiguredBuildDiscarders().forEach(strategy -> {
6261
String displayName = strategy.getDescriptor().getDisplayName();
63-
listener.getLogger().println("Offering " + job.getFullName() + " to " + displayName);
6462
if (strategy.isApplicable(job)) {
65-
listener.getLogger().println(job.getFullName() + " accepted by " + displayName);
6663
try {
6764
strategy.apply(job);
6865
} catch (Exception ex) {

core/src/main/java/jenkins/security/ResourceDomainRootAction.java

+5-1
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ public Object getDynamic(String id, StaplerRequest req, StaplerResponse rsp) thr
117117
return null;
118118
}
119119

120-
if (!ACL.isAnonymous2(Jenkins.getAuthentication2())) {
120+
if (!ALLOW_AUTHENTICATED_USER && !ACL.isAnonymous2(Jenkins.getAuthentication2())) {
121121
rsp.sendError(400);
122122
return null;
123123
}
@@ -327,4 +327,8 @@ private static Token decode(String value) {
327327
// Not @Restricted because the entire class is
328328
@SuppressFBWarnings(value = "MS_SHOULD_BE_FINAL", justification = "for script console")
329329
public static /* not final for Groovy */ int VALID_FOR_MINUTES = SystemProperties.getInteger(ResourceDomainRootAction.class.getName() + ".validForMinutes", 30);
330+
331+
/* Escape hatch for a security hardening preventing one of the known ways to elevate arbitrary file read to RCE */
332+
@SuppressFBWarnings(value = "MS_SHOULD_BE_FINAL", justification = "for script console")
333+
public static /* not final for Groovy */ boolean ALLOW_AUTHENTICATED_USER = SystemProperties.getBoolean(ResourceDomainRootAction.class.getName() + ".allowAuthenticatedUser", false);
330334
}

core/src/main/resources/hudson/PluginManager/updates.jelly

+2-2
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ THE SOFTWARE.
6969
<tr>
7070
<l:isAdmin>
7171
<th data-sort-disable="true">
72-
<l:rowSelectionController>
72+
<l:rowSelectionController class="app-checkbox-install-plugin">
7373
<j:if test="${app.updateCenter.hasIncompatibleUpdates(cache)}">
7474
<button tooltip="${%CompatibleTooltip}" type="button" data-select="compatible" class="jenkins-button jenkins-button--tertiary">
7575
<div class="jenkins-table__checkbox-dropdown__icon">
@@ -101,7 +101,7 @@ THE SOFTWARE.
101101
id="plugin.${p.name}.${p.sourceId}"
102102
checked="${installedOk ? 'checked' : null}"
103103
disabled="${installedOk ? 'disabled' : null}"
104-
data-compat-warning="${!p.isCompatible(cache)}"/>
104+
data-compat-warning="${!p.isCompatible(cache)}" class="app-checkbox-install-plugin"/>
105105
<label for="plugin.${p.name}.${p.sourceId}" />
106106
</span>
107107
</td>

core/src/main/resources/hudson/slaves/JNLPLauncher/main.properties

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ slaveAgent.cli.run=Run from agent command line:
2626
slaveAgent.cli.run.secret=Or run from agent command line, with the secret stored in a file:
2727
powerShell.cli.curl=Note: PowerShell users must use curl.exe instead of curl because curl is a default PowerShell cmdlet alias for Invoke-WebRequest.
2828
commonOptions=\
29-
The most commonly used option is <code>-webSocket</code>. \
29+
If you prefer to use TCP instead of WebSockets, remove the <code>-webSocket</code> option. \
3030
Run <code>java -jar agent.jar -help</code> for more.
3131
tcp-port-disabled=\
3232
The TCP port is disabled so TCP agents may not be connected. \

core/src/main/resources/lib/layout/rowSelectionController.jelly

+5-1
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,14 @@ THE SOFTWARE.
2626
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout">
2727
<st:documentation>
2828
Controls checkbox selections in tables
29+
<st:attribute name="class" use="required">
30+
Used for the queryselector to find the checkboxes that should change state when clicking
31+
one of the available buttons.
32+
</st:attribute>
2933
</st:documentation>
3034

3135
<div class="jenkins-table__checkbox-container">
32-
<button type="button" class="jenkins-table__checkbox">
36+
<button type="button" class="jenkins-table__checkbox" data-checkbox-class="${attrs.class}">
3337
<l:icon src="symbol-check" class="jenkins-table__checkbox__all-symbol" />
3438
<l:icon src="symbol-indeterminate" class="jenkins-table__checkbox__indeterminate-symbol" />
3539
</button>

pom.xml

+1-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ THE SOFTWARE.
2828
<parent>
2929
<groupId>org.jenkins-ci</groupId>
3030
<artifactId>jenkins</artifactId>
31-
<version>1.119</version>
31+
<version>1.122</version>
3232
<relativePath />
3333
</parent>
3434

test/src/test/java/hudson/cli/CLIActionTest.java

+17
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
package hudson.cli;
22

3+
import static org.hamcrest.MatcherAssert.assertThat;
4+
import static org.hamcrest.Matchers.allOf;
5+
import static org.hamcrest.Matchers.containsString;
6+
import static org.hamcrest.Matchers.is;
37
import static org.junit.Assert.assertEquals;
48

59
import hudson.Functions;
@@ -131,6 +135,19 @@ private void assertExitCode(int code, boolean useApiToken, File jar, String... a
131135
assertEquals(code, proc.join());
132136
}
133137

138+
@Test public void authenticationFailed() throws Exception {
139+
j.jenkins.setSecurityRealm(j.createDummySecurityRealm());
140+
j.jenkins.setAuthorizationStrategy(new MockAuthorizationStrategy().grant(Jenkins.ADMINISTER).everywhere().toAuthenticated());
141+
var jar = tmp.newFile("jenkins-cli.jar");
142+
FileUtils.copyURLToFile(j.jenkins.getJnlpJars("jenkins-cli.jar").getURL(), jar);
143+
var baos = new ByteArrayOutputStream();
144+
var exitStatus = new Launcher.LocalLauncher(StreamTaskListener.fromStderr()).launch().cmds(
145+
"java", "-jar", jar.getAbsolutePath(), "-s", j.getURL().toString(), "-auth", "user:bogustoken", "who-am-i"
146+
).stdout(baos).start().join();
147+
assertThat(baos.toString(), allOf(containsString("status code 401"), containsString("Server: Jetty")));
148+
assertThat(exitStatus, is(15));
149+
}
150+
134151
@Issue("JENKINS-41745")
135152
@Test
136153
public void encodingAndLocale() throws Exception {

test/src/test/java/jenkins/security/ResourceDomainTest.java

+9-1
Original file line numberDiff line numberDiff line change
@@ -399,7 +399,7 @@ public HttpResponse doDynamic() throws Exception {
399399
}
400400

401401
@Test
402-
public void authenticatedCannotAccessResourceDomain() throws Exception {
402+
public void authenticatedCannotAccessResourceDomainUnlessAllowedBySystemProperty() throws Exception {
403403
j.jenkins.setSecurityRealm(j.createDummySecurityRealm());
404404
final MockAuthorizationStrategy authorizationStrategy = new MockAuthorizationStrategy();
405405
authorizationStrategy.grant(Jenkins.ADMINISTER).everywhere().to("admin").grant(Jenkins.READ).everywhere().toEveryone();
@@ -416,5 +416,13 @@ public void authenticatedCannotAccessResourceDomain() throws Exception {
416416
try (JenkinsRule.WebClient wc = j.createWebClient().withBasicCredentials("admin")) {
417417
assertThat(assertThrows(FailingHttpStatusCodeException.class, () -> wc.getPage(new URL(resourceUrl))).getStatusCode(), is(400));
418418
}
419+
420+
ResourceDomainRootAction.ALLOW_AUTHENTICATED_USER = true;
421+
try (JenkinsRule.WebClient wc = j.createWebClient().withBasicApiToken("admin")) {
422+
assertThat(wc.getPage(new URL(resourceUrl)).getWebResponse().getStatusCode(), is(200));
423+
}
424+
try (JenkinsRule.WebClient wc = j.createWebClient().withBasicCredentials("admin")) {
425+
assertThat(wc.getPage(new URL(resourceUrl)).getWebResponse().getStatusCode(), is(200));
426+
}
419427
}
420428
}

war/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -65,5 +65,5 @@
6565
"defaults",
6666
"not IE 11"
6767
],
68-
"packageManager": "[email protected].0"
68+
"packageManager": "[email protected].1"
6969
}

war/pom.xml

+2-2
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,8 @@ THE SOFTWARE.
5353
<!-- frontend-maven-plugin will install this Yarn version as bootstrap, then hand over control to Yarn Berry. -->
5454
<yarn.version>1.22.19</yarn.version>
5555
<!-- maven-antrun-plugin will download this Yarn version. -->
56-
<yarn-berry.version>4.4.0</yarn-berry.version>
57-
<yarn-berry.sha256sum>5f228cb28f2edb97d8c3b667fb7b2fdcf06c46798e25ea889dad9e0b4bc2e2c1</yarn-berry.sha256sum>
56+
<yarn-berry.version>4.4.1</yarn-berry.version>
57+
<yarn-berry.sha256sum>920b4530755296dc2ce8b4351f057d4a26429524fcb2789d277560d94837c27e</yarn-berry.sha256sum>
5858
</properties>
5959

6060
<dependencyManagement>

war/src/main/js/components/row-selection-controller/index.js

+4-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,10 @@ const rowSelectionControllers = document.querySelectorAll(
44

55
rowSelectionControllers.forEach((headerCheckbox) => {
66
const table = headerCheckbox.closest(".jenkins-table");
7-
const tableCheckboxes = table.querySelectorAll("input[type='checkbox']");
7+
const checkboxClass = headerCheckbox.dataset.checkboxClass;
8+
const tableCheckboxes = table.querySelectorAll(
9+
`input[type='checkbox'].${checkboxClass}`,
10+
);
811
const moreOptionsButton = table.querySelector(
912
".jenkins-table__checkbox-options",
1013
);

0 commit comments

Comments
 (0)