Skip to content

Commit

Permalink
Merge branch 'master' into JENKINS-72988
Browse files Browse the repository at this point in the history
  • Loading branch information
mawinter69 committed Aug 30, 2024
2 parents c5840c0 + 5fe9a44 commit 6333a2a
Show file tree
Hide file tree
Showing 16 changed files with 82 additions and 20 deletions.
2 changes: 1 addition & 1 deletion ath.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ set -o xtrace
cd "$(dirname "$0")"

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

if [[ $# -eq 0 ]]; then
export JDK=17
Expand Down
2 changes: 1 addition & 1 deletion bom/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ THE SOFTWARE.
<dependency>
<groupId>org.apache.ant</groupId>
<artifactId>ant</artifactId>
<version>1.10.14</version>
<version>1.10.15</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
Expand Down
24 changes: 23 additions & 1 deletion cli/src/main/java/hudson/cli/CLI.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import jakarta.websocket.ClientEndpointConfig;
import jakarta.websocket.Endpoint;
import jakarta.websocket.EndpointConfig;
import jakarta.websocket.HandshakeResponse;
import jakarta.websocket.Session;
import java.io.DataInputStream;
import java.io.File;
Expand Down Expand Up @@ -64,6 +65,7 @@
import org.glassfish.tyrus.client.ClientManager;
import org.glassfish.tyrus.client.ClientProperties;
import org.glassfish.tyrus.client.SslEngineConfigurator;
import org.glassfish.tyrus.client.exception.DeploymentHandshakeException;
import org.glassfish.tyrus.container.jdk.client.JdkClientContainer;

/**
Expand Down Expand Up @@ -340,13 +342,19 @@ public void onOpen(Session session, EndpointConfig config) {}
}

class Authenticator extends ClientEndpointConfig.Configurator {
HandshakeResponse hr;
@Override
public void beforeRequest(Map<String, List<String>> headers) {
if (factory.authorization != null) {
headers.put("Authorization", List.of(factory.authorization));
}
}
@Override
public void afterResponse(HandshakeResponse hr) {
this.hr = hr;
}
}
var authenticator = new Authenticator();

ClientManager client = ClientManager.createClient(JdkClientContainer.class.getName()); // ~ ContainerProvider.getWebSocketContainer()
client.getProperties().put(ClientProperties.REDIRECT_ENABLED, true); // https://tyrus-project.github.io/documentation/1.13.1/index/tyrus-proprietary-config.html#d0e1775
Expand All @@ -357,7 +365,21 @@ public void beforeRequest(Map<String, List<String>> headers) {
sslEngineConfigurator.setHostnameVerifier((s, sslSession) -> true);
client.getProperties().put(ClientProperties.SSL_ENGINE_CONFIGURATOR, sslEngineConfigurator);
}
Session session = client.connectToServer(new CLIEndpoint(), ClientEndpointConfig.Builder.create().configurator(new Authenticator()).build(), URI.create(url.replaceFirst("^http", "ws") + "cli/ws"));
Session session;
try {
session = client.connectToServer(new CLIEndpoint(), ClientEndpointConfig.Builder.create().configurator(authenticator).build(), URI.create(url.replaceFirst("^http", "ws") + "cli/ws"));
} catch (DeploymentHandshakeException x) {
System.err.println("CLI handshake failed with status code " + x.getHttpStatusCode());
if (authenticator.hr != null) {
for (var entry : authenticator.hr.getHeaders().entrySet()) {
// org.glassfish.tyrus.core.Utils.parseHeaderValue improperly splits values like Date at commas, so undo that:
System.err.println(entry.getKey() + ": " + String.join(", ", entry.getValue()));
}
// UpgradeResponse.getReasonPhrase is useless since Jetty generates it from the code,
// and the body is not accessible at all.
}
return 15; // compare CLICommand.main
}
PlainCLIProtocol.Output out = new PlainCLIProtocol.Output() {
@Override
public void send(byte[] data) throws IOException {
Expand Down
9 changes: 9 additions & 0 deletions core/src/main/java/hudson/model/TopLevelItemDescriptor.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import edu.umd.cs.findbugs.annotations.CheckForNull;
import edu.umd.cs.findbugs.annotations.NonNull;
import hudson.ExtensionList;
import hudson.util.FormValidation;
import java.io.StringWriter;
import java.util.logging.Level;
import java.util.logging.Logger;
Expand All @@ -37,7 +38,11 @@
import org.jenkins.ui.icon.Icon;
import org.jenkins.ui.icon.IconSet;
import org.jenkins.ui.icon.IconSpec;
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.NoExternalUse;
import org.kohsuke.stapler.AncestorInPath;
import org.kohsuke.stapler.MetaClass;
import org.kohsuke.stapler.QueryParameter;
import org.kohsuke.stapler.Stapler;
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.WebApp;
Expand Down Expand Up @@ -286,4 +291,8 @@ public static ExtensionList<TopLevelItemDescriptor> all() {
return Items.all();
}

@Restricted(NoExternalUse.class)
public FormValidation doCheckDisplayNameOrNull(@AncestorInPath TopLevelItem item, @QueryParameter String value) {
return Jenkins.get().doCheckDisplayName(value, item.getName());
}
}
4 changes: 1 addition & 3 deletions core/src/main/java/hudson/slaves/JNLPLauncher.java
Original file line number Diff line number Diff line change
Expand Up @@ -213,9 +213,7 @@ private String getRemotingOptions(String computerName) {
sb.append("-name ");
sb.append(computerName);
sb.append(' ');
if (isWebSocket()) {
sb.append("-webSocket ");
}
sb.append("-webSocket ");
if (tunnel != null) {
sb.append(" -tunnel ");
sb.append(tunnel);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,12 +57,9 @@ protected void execute(TaskListener listener) throws IOException, InterruptedExc
}

public static void processJob(TaskListener listener, Job job) {
listener.getLogger().println("Processing " + job.getFullName());
GlobalBuildDiscarderConfiguration.get().getConfiguredBuildDiscarders().forEach(strategy -> {
String displayName = strategy.getDescriptor().getDisplayName();
listener.getLogger().println("Offering " + job.getFullName() + " to " + displayName);
if (strategy.isApplicable(job)) {
listener.getLogger().println(job.getFullName() + " accepted by " + displayName);
try {
strategy.apply(job);
} catch (Exception ex) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ public Object getDynamic(String id, StaplerRequest req, StaplerResponse rsp) thr
return null;
}

if (!ACL.isAnonymous2(Jenkins.getAuthentication2())) {
if (!ALLOW_AUTHENTICATED_USER && !ACL.isAnonymous2(Jenkins.getAuthentication2())) {
rsp.sendError(400);
return null;
}
Expand Down Expand Up @@ -327,4 +327,8 @@ private static Token decode(String value) {
// 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);
}
4 changes: 2 additions & 2 deletions core/src/main/resources/hudson/PluginManager/updates.jelly
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ THE SOFTWARE.
<tr>
<l:isAdmin>
<th data-sort-disable="true">
<l:rowSelectionController>
<l:rowSelectionController class="app-checkbox-install-plugin">
<j:if test="${app.updateCenter.hasIncompatibleUpdates(cache)}">
<button tooltip="${%CompatibleTooltip}" type="button" data-select="compatible" class="jenkins-button jenkins-button--tertiary">
<div class="jenkins-table__checkbox-dropdown__icon">
Expand Down Expand Up @@ -101,7 +101,7 @@ THE SOFTWARE.
id="plugin.${p.name}.${p.sourceId}"
checked="${installedOk ? 'checked' : null}"
disabled="${installedOk ? 'disabled' : null}"
data-compat-warning="${!p.isCompatible(cache)}"/>
data-compat-warning="${!p.isCompatible(cache)}" class="app-checkbox-install-plugin"/>
<label for="plugin.${p.name}.${p.sourceId}" />
</span>
</td>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ slaveAgent.cli.run=Run from agent command line:
slaveAgent.cli.run.secret=Or run from agent command line, with the secret stored in a file:
powerShell.cli.curl=Note: PowerShell users must use curl.exe instead of curl because curl is a default PowerShell cmdlet alias for Invoke-WebRequest.
commonOptions=\
The most commonly used option is <code>-webSocket</code>. \
If you prefer to use TCP instead of WebSockets, remove the <code>-webSocket</code> option. \
Run <code>java -jar agent.jar -help</code> for more.
tcp-port-disabled=\
The TCP port is disabled so TCP agents may not be connected. \
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,14 @@ THE SOFTWARE.
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout">
<st:documentation>
Controls checkbox selections in tables
<st:attribute name="class" use="required">
Used for the queryselector to find the checkboxes that should change state when clicking
one of the available buttons.
</st:attribute>
</st:documentation>

<div class="jenkins-table__checkbox-container">
<button type="button" class="jenkins-table__checkbox">
<button type="button" class="jenkins-table__checkbox" data-checkbox-class="${attrs.class}">
<l:icon src="symbol-check" class="jenkins-table__checkbox__all-symbol" />
<l:icon src="symbol-indeterminate" class="jenkins-table__checkbox__indeterminate-symbol" />
</button>
Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ THE SOFTWARE.
<parent>
<groupId>org.jenkins-ci</groupId>
<artifactId>jenkins</artifactId>
<version>1.119</version>
<version>1.122</version>
<relativePath />
</parent>

Expand Down
17 changes: 17 additions & 0 deletions test/src/test/java/hudson/cli/CLIActionTest.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
package hudson.cli;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.allOf;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertEquals;

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

@Test public void authenticationFailed() throws Exception {
j.jenkins.setSecurityRealm(j.createDummySecurityRealm());
j.jenkins.setAuthorizationStrategy(new MockAuthorizationStrategy().grant(Jenkins.ADMINISTER).everywhere().toAuthenticated());
var jar = tmp.newFile("jenkins-cli.jar");
FileUtils.copyURLToFile(j.jenkins.getJnlpJars("jenkins-cli.jar").getURL(), jar);
var baos = new ByteArrayOutputStream();
var exitStatus = new Launcher.LocalLauncher(StreamTaskListener.fromStderr()).launch().cmds(
"java", "-jar", jar.getAbsolutePath(), "-s", j.getURL().toString(), "-auth", "user:bogustoken", "who-am-i"
).stdout(baos).start().join();
assertThat(baos.toString(), allOf(containsString("status code 401"), containsString("Server: Jetty")));
assertThat(exitStatus, is(15));
}

@Issue("JENKINS-41745")
@Test
public void encodingAndLocale() throws Exception {
Expand Down
10 changes: 9 additions & 1 deletion test/src/test/java/jenkins/security/ResourceDomainTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -399,7 +399,7 @@ public HttpResponse doDynamic() throws Exception {
}

@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();
Expand All @@ -416,5 +416,13 @@ public void authenticatedCannotAccessResourceDomain() throws Exception {
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));
}
}
}
2 changes: 1 addition & 1 deletion war/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -65,5 +65,5 @@
"defaults",
"not IE 11"
],
"packageManager": "[email protected].0"
"packageManager": "[email protected].1"
}
4 changes: 2 additions & 2 deletions war/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,8 @@ THE SOFTWARE.
<!-- frontend-maven-plugin will install this Yarn version as bootstrap, then hand over control to Yarn Berry. -->
<yarn.version>1.22.19</yarn.version>
<!-- maven-antrun-plugin will download this Yarn version. -->
<yarn-berry.version>4.4.0</yarn-berry.version>
<yarn-berry.sha256sum>5f228cb28f2edb97d8c3b667fb7b2fdcf06c46798e25ea889dad9e0b4bc2e2c1</yarn-berry.sha256sum>
<yarn-berry.version>4.4.1</yarn-berry.version>
<yarn-berry.sha256sum>920b4530755296dc2ce8b4351f057d4a26429524fcb2789d277560d94837c27e</yarn-berry.sha256sum>
</properties>

<dependencyManagement>
Expand Down
5 changes: 4 additions & 1 deletion war/src/main/js/components/row-selection-controller/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ const rowSelectionControllers = document.querySelectorAll(

rowSelectionControllers.forEach((headerCheckbox) => {
const table = headerCheckbox.closest(".jenkins-table");
const tableCheckboxes = table.querySelectorAll("input[type='checkbox']");
const checkboxClass = headerCheckbox.dataset.checkboxClass;
const tableCheckboxes = table.querySelectorAll(
`input[type='checkbox'].${checkboxClass}`,
);
const moreOptionsButton = table.querySelector(
".jenkins-table__checkbox-options",
);
Expand Down

0 comments on commit 6333a2a

Please sign in to comment.