Merge branch 'master' into add-groups-to-command-palette

This commit is contained in:
Jan Faracik 2025-02-11 14:44:03 +00:00 committed by GitHub
commit 0ab3665587
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
59 changed files with 531 additions and 417 deletions

View File

@ -12,7 +12,7 @@
[![Gitter](https://img.shields.io/gitter/room/jenkinsci/jenkins)](https://app.gitter.im/#/room/#jenkinsci_jenkins:gitter.im)
In a nutshell, Jenkins is the leading open-source automation server.
Built with Java, it provides over 1,800 [plugins](https://plugins.jenkins.io/) to support automating virtually anything,
Built with Java, it provides over 2,000 [plugins](https://plugins.jenkins.io/) to support automating virtually anything,
so that humans can spend their time doing things machines cannot.
# What to Use Jenkins for and When to Use It

View File

@ -287,11 +287,6 @@ THE SOFTWARE.
<artifactId>localizer</artifactId>
<version>1.31</version>
</dependency>
<dependency>
<groupId>org.jvnet.robust-http-client</groupId>
<artifactId>robust-http-client</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>org.jvnet.winp</groupId>
<artifactId>winp</artifactId>
@ -342,7 +337,7 @@ THE SOFTWARE.
<!-- provided by jcl-over-slf4j -->
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.3.4</version>
<version>1.3.5</version>
<scope>provided</scope>
</dependency>
</dependencies>

View File

@ -325,10 +325,6 @@ THE SOFTWARE.
<groupId>org.jvnet.localizer</groupId>
<artifactId>localizer</artifactId>
</dependency>
<dependency>
<groupId>org.jvnet.robust-http-client</groupId>
<artifactId>robust-http-client</artifactId>
</dependency>
<dependency>
<groupId>org.jvnet.winp</groupId>
<artifactId>winp</artifactId>

View File

@ -1989,7 +1989,7 @@ public class Functions {
*/
public static @CheckForNull String getConsoleUrl(WithConsoleUrl withConsoleUrl) {
String consoleUrl = withConsoleUrl.getConsoleUrl();
return consoleUrl != null ? Stapler.getCurrentRequest().getContextPath() + '/' + consoleUrl : null;
return consoleUrl != null ? Stapler.getCurrentRequest2().getContextPath() + '/' + consoleUrl : null;
}
/**

View File

@ -70,7 +70,6 @@ import jenkins.security.stapler.StaplerAccessibleType;
import jenkins.util.JenkinsJVM;
import jenkins.util.SystemProperties;
import org.jenkinsci.Symbol;
import org.jvnet.robust_http_client.RetryableHttpStream;
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.NoExternalUse;
import org.kohsuke.stapler.DataBoundConstructor;
@ -346,10 +345,10 @@ public final class ProxyConfiguration extends AbstractDescribableImpl<ProxyConfi
public static InputStream getInputStream(URL url) throws IOException {
final ProxyConfiguration p = get();
if (p == null)
return new RetryableHttpStream(url);
return ((HttpURLConnection) url.openConnection()).getInputStream();
Proxy proxy = p.createProxy(url.getHost());
InputStream is = new RetryableHttpStream(url, proxy);
InputStream is = ((HttpURLConnection) url.openConnection(proxy)).getInputStream();
if (p.getUserName() != null) {
// Add an authenticator which provides the credentials for proxy authentication
Authenticator.setDefault(p.authenticator);

View File

@ -92,6 +92,7 @@ import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.EnumSet;
import java.util.HexFormat;
import java.util.List;
import java.util.Locale;
import java.util.Map;
@ -115,7 +116,6 @@ import jenkins.model.Jenkins;
import jenkins.util.MemoryReductionUtil;
import jenkins.util.SystemProperties;
import jenkins.util.io.PathRemover;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.time.FastDateFormat;
import org.apache.tools.ant.BuildException;
@ -635,7 +635,6 @@ public class Util {
* The stream will be closed by this method at the end of this method.
* @return
* 32-char wide string
* @see DigestUtils#md5Hex(InputStream)
*/
@NonNull
public static String getDigestOf(@NonNull InputStream source) throws IOException {
@ -710,13 +709,7 @@ public class Util {
@NonNull
public static String toHexString(@NonNull byte[] data, int start, int len) {
StringBuilder buf = new StringBuilder();
for (int i = 0; i < len; i++) {
int b = data[start + i] & 0xFF;
if (b < 16) buf.append('0');
buf.append(Integer.toHexString(b));
}
return buf.toString();
return HexFormat.of().formatHex(data, start, len);
}
@NonNull
@ -726,12 +719,7 @@ public class Util {
@NonNull
public static byte[] fromHexString(@NonNull String data) {
if (data.length() % 2 != 0)
throw new IllegalArgumentException("data must have an even number of hexadecimal digits");
byte[] r = new byte[data.length() / 2];
for (int i = 0; i < data.length(); i += 2)
r[i / 2] = (byte) Integer.parseInt(data.substring(i, i + 2), 16);
return r;
return HexFormat.of().parseHex(data);
}
/**
@ -1992,9 +1980,18 @@ public class Util {
* Returns Hex string of SHA-256 Digest of passed input
*/
@Restricted(NoExternalUse.class)
public static String getHexOfSHA256DigestOf(byte[] input) throws IOException {
public static String getHexOfSHA256DigestOf(byte[] input) {
//get hex string of sha 256 of payload
byte[] payloadDigest = Util.getSHA256DigestOf(input);
return (payloadDigest != null) ? Util.toHexString(payloadDigest) : null;
}
/**
* Returns Hex string of SHA-256 Digest of passed string
*/
@Restricted(NoExternalUse.class)
public static String getHexOfSHA256DigestOf(String input) {
return getHexOfSHA256DigestOf(input.getBytes(StandardCharsets.UTF_8));
}
}

View File

@ -371,6 +371,11 @@ public final class CronTab {
* (e.g. Jun 31) date, or at least a date too rare to be useful. This addresses JENKINS-41864 and was added in 2.49
*/
public Calendar ceil(Calendar cal) {
if (cal.get(Calendar.SECOND) > 0 || cal.get(Calendar.MILLISECOND) > 0) {
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MILLISECOND, 0);
cal.add(Calendar.MINUTE, 1);
}
Calendar twoYearsFuture = (Calendar) cal.clone();
twoYearsFuture.add(Calendar.YEAR, 2);
OUTER:
@ -440,6 +445,8 @@ public final class CronTab {
* (e.g. Jun 31) date, or at least a date too rare to be useful. This addresses JENKINS-41864 and was added in 2.49
*/
public Calendar floor(Calendar cal) {
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MILLISECOND, 0);
Calendar twoYearsAgo = (Calendar) cal.clone();
twoYearsAgo.add(Calendar.YEAR, -2);

View File

@ -80,7 +80,7 @@ public interface ConsoleUrlProvider extends Describable<ConsoleUrlProvider> {
* @return the URL for the console for the specified build, relative to the web server root
*/
static @NonNull String getRedirectUrl(Run<?, ?> run) {
return Stapler.getCurrentRequest().getContextPath() + '/' + run.getConsoleUrl();
return Stapler.getCurrentRequest2().getContextPath() + '/' + run.getConsoleUrl();
}
static List<ConsoleUrlProvider> all() {

View File

@ -6,7 +6,7 @@ import java.util.logging.Logger;
import net.sf.json.JSONException;
import net.sf.json.JSONObject;
import org.jenkinsci.Symbol;
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.StaplerRequest2;
/**
* Configures check interval for computer retention.
@ -56,7 +56,7 @@ public class GlobalComputerRetentionCheckIntervalConfiguration extends GlobalCon
}
@Override
public boolean configure(StaplerRequest req, JSONObject json) throws FormException {
public boolean configure(StaplerRequest2 req, JSONObject json) throws FormException {
try {
final int interval = json.getInt("computerRetentionCheckInterval");
setComputerRetentionCheckInterval(interval);

View File

@ -67,7 +67,7 @@ public abstract class GlobalConfiguration extends Descriptor<GlobalConfiguration
@Override
public boolean configure(StaplerRequest2 req, JSONObject json) throws FormException {
if (Util.isOverridden(GlobalConfiguration.class, getClass(), "configure", StaplerRequest.class, JSONObject.class)) {
return configure(StaplerRequest.fromStaplerRequest2(req), json);
return configure(req != null ? StaplerRequest.fromStaplerRequest2(req) : null, json);
} else {
return configureImpl(req, json);
}

View File

@ -974,6 +974,9 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve
ClassFilterImpl.register();
LOGGER.info("Starting version " + getVersion());
// Sanity check that we can load the confidential store. Fail fast if we can't.
ConfidentialStore.get();
// initialization consists of ...
executeReactor(is,
pluginManager.initTasks(is), // loading and preparing plugins

View File

@ -1,5 +1,6 @@
package jenkins.security;
import edu.umd.cs.findbugs.annotations.NonNull;
import hudson.FilePath;
import hudson.Util;
import hudson.util.Secret;
@ -19,19 +20,35 @@ import javax.crypto.CipherInputStream;
import javax.crypto.CipherOutputStream;
import javax.crypto.SecretKey;
import jenkins.model.Jenkins;
import jenkins.util.SystemProperties;
/**
* Default portable implementation of {@link ConfidentialStore} that uses
* a directory inside $JENKINS_HOME.
*
* The master key is also stored in this same directory.
* <p>
* The master key is stored by default in <code>$JENKINS_HOME/secrets/master.key</code> but another location can be provided using the system property <code>jenkins.master.key.file</code>.
* <p>
* It is also possible to prevent the generation of the master key file using the system property <code>-Djenkins.master.key.readOnly</code>. In this case, the master key file must be provided or startup will fail.
*
* @author Kohsuke Kawaguchi
*/
// @MetaInfServices --- not annotated because this is the fallback implementation
public class DefaultConfidentialStore extends ConfidentialStore {
static final String MASTER_KEY_FILE_SYSTEM_PROPERTY = DefaultConfidentialStore.class.getName() + ".file";
static final String MASTER_KEY_READONLY_SYSTEM_PROPERTY_NAME = DefaultConfidentialStore.class.getName() + ".readOnly";
private final SecureRandom sr = new SecureRandom();
@NonNull
private static File getMasterKeyFile(File rootDir) {
var jenkinsMasterKey = SystemProperties.getString(MASTER_KEY_FILE_SYSTEM_PROPERTY);
if (jenkinsMasterKey != null) {
return new File(jenkinsMasterKey);
} else {
return new File(rootDir, "master.key");
}
}
/**
* Directory that stores individual keys.
*/
@ -51,6 +68,10 @@ public class DefaultConfidentialStore extends ConfidentialStore {
}
public DefaultConfidentialStore(File rootDir) throws IOException, InterruptedException {
this(rootDir, getMasterKeyFile(rootDir));
}
protected DefaultConfidentialStore(File rootDir, File keyFile) throws IOException, InterruptedException {
this.rootDir = rootDir;
if (rootDir.mkdirs()) {
// protect this directory. but don't change the permission of the existing directory
@ -58,11 +79,15 @@ public class DefaultConfidentialStore extends ConfidentialStore {
new FilePath(rootDir).chmod(0700);
}
TextFile masterSecret = new TextFile(new File(rootDir, "master.key"));
TextFile masterSecret = new TextFile(keyFile);
if (!masterSecret.exists()) {
// we are only going to use small number of bits (since export control limits AES key length)
// but let's generate a long enough key anyway
masterSecret.write(Util.toHexString(randomBytes(128)));
if (SystemProperties.getBoolean(MASTER_KEY_READONLY_SYSTEM_PROPERTY_NAME)) {
throw new IOException(masterSecret + " does not exist and system property " + MASTER_KEY_READONLY_SYSTEM_PROPERTY_NAME + " is set. You must provide a valid master key file.");
} else {
// we are only going to use small number of bits (since export control limits AES key length)
// but let's generate a long enough key anyway
masterSecret.write(Util.toHexString(randomBytes(128)));
}
}
this.masterKey = Util.toAes128Key(masterSecret.readTrim());
}

View File

@ -28,6 +28,7 @@ import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import hudson.BulkChange;
import hudson.Extension;
import hudson.Util;
import hudson.model.User;
import hudson.model.UserProperty;
import hudson.model.UserPropertyDescriptor;
@ -39,7 +40,6 @@ import java.util.Objects;
import jenkins.model.Jenkins;
import jenkins.security.LastGrantedAuthoritiesProperty;
import jenkins.util.SystemProperties;
import org.apache.commons.codec.binary.Hex;
import org.jenkinsci.Symbol;
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.DoNotUse;
@ -103,7 +103,7 @@ public class UserSeedProperty extends UserProperty {
byte[] bytes = new byte[SEED_NUM_BYTES];
while (Objects.equals(newSeed, currentSeed)) {
RANDOM.nextBytes(bytes);
newSeed = new String(Hex.encodeHex(bytes));
newSeed = Util.toHexString(bytes);
}
this.seed = newSeed;
}

View File

@ -32,6 +32,7 @@ import hudson.ExtensionList;
import hudson.ExtensionPoint;
import hudson.PluginWrapper;
import hudson.ProxyConfiguration;
import hudson.Util;
import hudson.model.AsyncPeriodicWork;
import hudson.model.TaskListener;
import hudson.model.UsageStatistics;
@ -51,7 +52,6 @@ import java.util.logging.Logger;
import jenkins.model.Jenkins;
import jenkins.util.SystemProperties;
import net.sf.json.JSONObject;
import org.apache.commons.codec.digest.DigestUtils;
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.NoExternalUse;
@ -221,7 +221,7 @@ public abstract class Telemetry implements ExtensionPoint {
wrappedData.put("type", telemetry.getId());
wrappedData.put("payload", data);
String correlationId = ExtensionList.lookupSingleton(Correlator.class).getCorrelationId();
wrappedData.put("correlator", DigestUtils.sha256Hex(correlationId + telemetry.getId()));
wrappedData.put("correlator", Util.getHexOfSHA256DigestOf(correlationId + telemetry.getId()));
String body = wrappedData.toString();
if (LOGGER.isLoggable(Level.FINEST)) {

View File

@ -1,6 +1,7 @@
package jenkins.util;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import hudson.Util;
import hudson.util.FormValidation;
import java.io.ByteArrayInputStream;
import java.io.File;
@ -33,8 +34,6 @@ import java.util.logging.Level;
import java.util.logging.Logger;
import jenkins.model.Jenkins;
import net.sf.json.JSONObject;
import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Hex;
import org.apache.commons.io.output.TeeOutputStream;
import org.jvnet.hudson.crypto.CertificateUtil;
import org.jvnet.hudson.crypto.SignatureOutputStream;
@ -221,10 +220,10 @@ public class JSONSignatureValidator {
// This approach might look unnecessarily clever, but short of having redundant Signature instances,
// there doesn't seem to be a better approach for this.
try {
if (signature.verify(Hex.decodeHex(providedSignature.toCharArray()))) {
if (signature.verify(Util.fromHexString(providedSignature))) {
return true;
}
} catch (SignatureException | DecoderException ignore) {
} catch (SignatureException | IllegalArgumentException ignore) {
// ignore
}
@ -242,7 +241,7 @@ public class JSONSignatureValidator {
* 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(Base64.getEncoder().encodeToString(digest));
return providedDigest.equalsIgnoreCase(Util.toHexString(digest)) || providedDigest.equalsIgnoreCase(Base64.getEncoder().encodeToString(digest));
}

View File

@ -0,0 +1,279 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
The MIT License
Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi, Tom Huybrechts, id:digerata, Yahoo! 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.
-->
<web-fragment xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-fragment_3_1.xsd"
version="3.1">
<name>jenkins</name>
<servlet>
<servlet-name>Stapler</servlet-name>
<servlet-class>org.kohsuke.stapler.Stapler</servlet-class>
<init-param>
<param-name>default-encodings</param-name>
<param-value>text/html=UTF-8</param-value>
</init-param>
<init-param>
<param-name>diagnosticThreadName</param-name>
<param-value>false</param-value>
</init-param>
<async-supported>true</async-supported>
</servlet>
<servlet-mapping>
<servlet-name>Stapler</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
<filter>
<filter-name>suspicious-request-filter</filter-name>
<filter-class>jenkins.security.SuspiciousRequestFilter</filter-class>
<async-supported>true</async-supported>
</filter>
<filter>
<filter-name>diagnostic-name-filter</filter-name>
<filter-class>org.kohsuke.stapler.DiagnosticThreadNameFilter</filter-class>
<async-supported>true</async-supported>
</filter>
<filter>
<filter-name>encoding-filter</filter-name>
<filter-class>hudson.util.CharacterEncodingFilter</filter-class>
<async-supported>true</async-supported>
</filter>
<filter>
<filter-name>uncaught-exception-filter</filter-name>
<filter-class>org.kohsuke.stapler.UncaughtExceptionFilter</filter-class>
<async-supported>true</async-supported>
</filter>
<filter>
<filter-name>authentication-filter</filter-name>
<filter-class>hudson.security.HudsonFilter</filter-class>
<async-supported>true</async-supported>
</filter>
<filter>
<filter-name>csrf-filter</filter-name>
<filter-class>hudson.security.csrf.CrumbFilter</filter-class>
<async-supported>true</async-supported>
</filter>
<filter>
<filter-name>error-attribute-filter</filter-name>
<filter-class>jenkins.ErrorAttributeFilter</filter-class>
<async-supported>true</async-supported>
</filter>
<filter>
<filter-name>plugins-filter</filter-name>
<filter-class>hudson.util.PluginServletFilter</filter-class>
<async-supported>true</async-supported>
</filter>
<!--
The Headers filter allows us to override headers sent by the container
that may be in conflict with what we want. For example, Tomcat will set
Cache-Control: no-cache for any files behind the security-constraint
below. So if Hudson is on a public server, and you want to only allow
authorized users to access it, you may want to pay attention to this.
See: http://www.nabble.com/No-browser-caching-with-Hudson- -tf4601857.html
<filter>
<filter-name>change-headers-filter</filter-name>
<filter-class>hudson.ResponseHeaderFilter</filter-class>
<!- The value listed here is for 24 hours. Increase or decrease as you see
fit. Value is in seconds. Make sure to keep the public option ->
<init-param>
<param-name>Cache-Control</param-name>
<param-value>max-age=86400, public</param-value>
</init-param>
<!- It turns out that Tomcat just doesn't want to let
go of its cache option. If you override Cache-Control,
it starts to send Pragma: no-cache as a backup.
->
<init-param>
<param-name>Pragma</param-name>
<param-value>public</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>change-headers-filter</filter-name>
<url-pattern>*.css</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>change-headers-filter</filter-name>
<url-pattern>*.gif</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>change-headers-filter</filter-name>
<url-pattern>*.js</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>change-headers-filter</filter-name>
<url-pattern>*.png</url-pattern>
</filter-mapping>
-->
<filter-mapping>
<filter-name>suspicious-request-filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>diagnostic-name-filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>encoding-filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>uncaught-exception-filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>authentication-filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>csrf-filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>error-attribute-filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>plugins-filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<listener>
<!-- Must be before WebAppMain in order to initialize the context before the first use of this class. -->
<listener-class>jenkins.util.SystemProperties$Listener</listener-class>
</listener>
<listener>
<listener-class>hudson.WebAppMain</listener-class>
</listener>
<listener>
<listener-class>jenkins.JenkinsHttpSessionListener</listener-class>
</listener>
<!--
JENKINS-1235 suggests containers interpret '*' as "all roles defined in web.xml"
as opposed to "all roles defined in the security realm", so we need to list some
common names in the hope that users will have at least one of those roles.
-->
<security-role>
<role-name>admin</role-name>
</security-role>
<security-role>
<role-name>user</role-name>
</security-role>
<security-role>
<role-name>hudson</role-name>
</security-role>
<security-constraint>
<web-resource-collection>
<web-resource-name>Hudson</web-resource-name>
<url-pattern>/loginEntry</url-pattern>
<!--http-method>GET</http-method-->
</web-resource-collection>
<auth-constraint>
<role-name>**</role-name>
</auth-constraint>
</security-constraint>
<!-- Disable TRACE method with security constraint (copied from jetty/webdefaults.xml) -->
<security-constraint>
<web-resource-collection>
<web-resource-name>Disable TRACE</web-resource-name>
<url-pattern>/*</url-pattern>
<http-method>TRACE</http-method>
</web-resource-collection>
<auth-constraint />
</security-constraint>
<security-constraint>
<web-resource-collection>
<web-resource-name>other</web-resource-name>
<url-pattern>/*</url-pattern>
</web-resource-collection>
<!-- no security constraint -->
</security-constraint>
<login-config>
<auth-method>FORM</auth-method>
<form-login-config>
<form-login-page>/login</form-login-page>
<form-error-page>/loginError</form-error-page>
</form-login-config>
</login-config>
<!-- configure additional extension-content-type mappings -->
<mime-mapping>
<extension>xml</extension>
<mime-type>application/xml</mime-type>
</mime-mapping>
<!--mime-mapping> commenting out until this works out of the box with JOnAS. See http://www.nabble.com/Error-with-mime-type%2D-%27application-xslt%2Bxml%27-when-deploying-hudson-1.316-in-jonas-td24740489.html
<extension>xsl</extension>
<mime-type>application/xslt+xml</mime-type>
</mime-mapping-->
<mime-mapping>
<extension>log</extension>
<mime-type>text/plain</mime-type>
</mime-mapping>
<mime-mapping>
<extension>war</extension>
<mime-type>application/octet-stream</mime-type>
</mime-mapping>
<mime-mapping>
<extension>ear</extension>
<mime-type>application/octet-stream</mime-type>
</mime-mapping>
<mime-mapping>
<extension>rar</extension>
<mime-type>application/octet-stream</mime-type>
</mime-mapping>
<mime-mapping>
<extension>webm</extension>
<mime-type>video/webm</mime-type>
</mime-mapping>
<error-page>
<exception-type>java.lang.Throwable</exception-type>
<location>/oops</location>
</error-page>
<error-page>
<error-code>404</error-code>
<location>/404</location>
</error-page>
<session-config>
<cookie-config>
<!-- See https://www.owasp.org/index.php/HttpOnly for the discussion of this topic in OWASP -->
<http-only>true</http-only>
</cookie-config>
<!-- Tracking mode is managed by WebAppMain.FORCE_SESSION_TRACKING_BY_COOKIE_PROP -->
</session-config>
</web-fragment>

View File

@ -38,7 +38,7 @@ THE SOFTWARE.
placeholder="${%Search available plugins}"
id="filter-box"
autofocus="true"
value="${request.getParameter('filter')}" />
value="${request2.getParameter('filter')}" />
</div>
<div class="jenkins-app-bar__controls">
<l:isAdmin>

View File

@ -43,7 +43,7 @@ THE SOFTWARE.
placeholder="${%Search installed plugins}"
id="filter-box"
autofocus="true"
value="${request.getParameter('filter')}"
value="${request2.getParameter('filter')}"
enabled="${!noPlugins}" />
</div>
</div>

View File

@ -41,7 +41,7 @@ THE SOFTWARE.
placeholder="${%Search plugin updates}"
id="filter-box"
autofocus="true"
value="${request.getParameter('filter')}"
value="${request2.getParameter('filter')}"
enabled="${!empty(list)}" />
</div>
<div class="jenkins-app-bar__controls">

View File

@ -31,7 +31,7 @@ THE SOFTWARE.
<h1>
${%JVM Memory Usage}
</h1>
<j:set var="type" value="${request.getParameter('type') ?: 'min'}" />
<j:set var="type" value="${request2.getParameter('type') ?: 'min'}" />
<div>
${%Timespan}:
<j:choose>

View File

@ -30,14 +30,14 @@ THE SOFTWARE.
<j:if test="${it.getPrimaryView() != null}">
<j:set var="it" value="${it.getPrimaryView()}"/>
</j:if>
<j:set var="submissionUrl" value="${request.getParameter('submissionUrl')}" defaultValue="submitDescription"/>
<j:set var="submissionUrl" value="${request2.getParameter('submissionUrl')}" defaultValue="submitDescription"/>
<j:invokeStatic className="hudson.Util" method="isSafeToRedirectTo" var="isSafeToRedirectTo">
<j:arg value="${submissionUrl}"/>
</j:invokeStatic>
<j:if test="${!isSafeToRedirectTo}">
<j:set var="submissionUrl" value="submitDescription"/>
</j:if>
<j:set var="initialDescription" value="${request.getParameter('description')}" defaultValue="${it.description}"/>
<j:set var="initialDescription" value="${request2.getParameter('description')}" defaultValue="${it.description}"/>
<l:ajax>
<form action="${submissionUrl}" method="post">
<table>

View File

@ -31,8 +31,8 @@ THE SOFTWARE.
<st:include page="sidepanel.jelly" />
<l:breadcrumb title="${%Changes}" />
<l:main-panel>
<j:set var="from" value="${request.getParameter('from')}"/>
<j:set var="to" value="${request.getParameter('to')}"/>
<j:set var="from" value="${request2.getParameter('from')}"/>
<j:set var="to" value="${request2.getParameter('to')}"/>
<j:set var="browser" value="${it.scm.effectiveBrowser}"/> <!-- for compatibility; newer project-changes.jelly override this -->
<h1>

View File

@ -111,7 +111,7 @@ div {
section(class: "empty-state-section") {
ul(class: "empty-state-section-list") {
li(class: "content-block") {
a(href: "${rootURL}/${app.securityRealm.loginUrl}?from=${request.requestURI}",
a(href: "${rootURL}/${app.securityRealm.loginUrl}?from=${request2.requestURI}",
class: "content-block__link") {
span(_("Log in to Jenkins"))
span(class: "trailing-icon") {

View File

@ -32,7 +32,7 @@ THE SOFTWARE.
<l:main-panel>
<f:form method="post" action="doCreateItem" name="config" class="jenkins-form">
<f:entry title="${%Name}" help="${requestScope.descriptor.getHelpFile('name')}">
<f:textbox name="name" value="${request.getParameter('name')}" clazz="required" checkMessage="${%Name is mandatory}"/>
<f:textbox name="name" value="${request2.getParameter('name')}" clazz="required" checkMessage="${%Name is mandatory}"/>
</f:entry>
<!-- main body of the configuration -->
@ -40,7 +40,7 @@ THE SOFTWARE.
<st:include class="${requestScope.descriptor.clazz}" page="configure-entries.jelly" />
<f:bottomButtonBar>
<input type="hidden" name="type" value="${request.getParameter('mode')}"/>
<input type="hidden" name="type" value="${request2.getParameter('mode')}"/>
<f:submit value="${%Save}"/>
</f:bottomButtonBar>

View File

@ -30,7 +30,7 @@ THE SOFTWARE.
<j:invoke var="buildClass" on="${currentThread.contextClassLoader}" method="loadClass">
<j:arg value="hudson.model.Run" />
</j:invoke>
<j:set var="build" value="${request.findAncestorObject(buildClass)}" />
<j:set var="build" value="${request2.findAncestorObject(buildClass)}" />
<st:include page="sidepanel.jelly" it="${build}" />
<l:main-panel>
<t:buildCaption it="${build}">${title}</t:buildCaption>

View File

@ -40,7 +40,7 @@ THE SOFTWARE.
<l:main-panel>
<h1>${it.job.pronoun} ${it.job.displayName}</h1>
<p class="jenkins-description">${%description}</p>
<j:set var="delay" value="${request.getParameter('delay')}" />
<j:set var="delay" value="${request2.getParameter('delay')}" />
<f:form method="post" action="build${empty(delay)?'':'?delay='+delay}" name="parameters"
tableClass="parameters" class="jenkins-form">
<j:forEach var="parameterDefinition" items="${it.parameterDefinitions}">

View File

@ -20,7 +20,7 @@
<tr><td>
<div id="delete-error" style="display: none">
<pre>
${request.getAttribute('stackTraces')}
${request2.getAttribute('stackTraces')}
</pre>
</div>
</td></tr>

View File

@ -44,7 +44,7 @@ THE SOFTWARE.
</l:main-panel>
<l:header>
<!-- for screen resolution detection -->
<script id="screenResolution-script" data-use-secure-cookie="${request.secure}"/>
<script id="screenResolution-script" data-use-secure-cookie="${request2.secure}"/>
<st:adjunct includes="hudson.model.View.screen-resolution"/>
</l:header>
</l:layout>

View File

@ -27,7 +27,7 @@ THE SOFTWARE.
-->
<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:f="/lib/form" xmlns:i="jelly:fmt">
<j:set var="q" value="${request.getParameter('q')}"/>
<j:set var="q" value="${request2.getParameter('q')}"/>
<j:new var="h" className="hudson.Functions" /><!-- needed for printing title. -->
<l:layout title="${%Search for} '${q}'">
<l:main-panel>
@ -48,7 +48,7 @@ THE SOFTWARE.
</j:forEach>
</ol>
<j:if test="${items.hasMoreResults()}">
<j:set var="max" value="${request.hasParameter('max')?request.getParameter('max'):100}"/>
<j:set var="max" value="${request2.hasParameter('max')?request2.getParameter('max'):100}"/>
<j:choose>
<j:when test="${max &gt; it.getMaxSearchSize()}">
<em>Only showing the first ${it.getMaxSearchSize()} results,

View File

@ -10,7 +10,7 @@ def f=namespace(lib.FormTagLib)
def l=namespace(lib.LayoutTagLib)
def st=namespace("jelly:stapler")
l.layout(permission:app.SYSTEM_READ, title:my.displayName, cssclass:request.getParameter('decorate'), type:"one-column") {
l.layout(permission:app.SYSTEM_READ, title:my.displayName, cssclass:request2.getParameter('decorate'), type:"one-column") {
l.main_panel {
l.app_bar(title: my.displayName)

View File

@ -34,13 +34,13 @@ THE SOFTWARE.
needed to generate a session if non exists,
without it we would add ";jsessionid=" to the url which will result in a 404
-->
<j:set var="_" value="${request.getSession()}"/>
<j:set var="_" value="${request2.getSession()}"/>
<x:doctype name="html"/>
<!-- in case of error we want to surround the form elements with an error hint -->
<j:set var="inputClass" value="${data.errorMessage!=null ? 'jenkins-input--error' : ''}"/>
<j:set var="simpleDecorator" value="${h.simplePageDecorator}"/>
<j:set var="simpleDecorators" value="${h.simplePageDecorators}"/>
<html lang="${request.getLocale().toLanguageTag()}">
<html lang="${request2.getLocale().toLanguageTag()}">
<head data-rooturl="${rootURL}" data-resurl="${resURL}" data-imagesurl="${imagesURL}" resURL="${resURL}">
<title>${%Register} - Jenkins</title>
<!-- we do not want bots on this page -->

View File

@ -65,7 +65,7 @@ THE SOFTWARE.
<h2>Request Headers</h2>
<table>
<j:forEach var="n" items="${request.getHeaderNames()}">
<j:forEach var="n" items="${request2.getHeaderNames()}">
<j:choose>
<j:when test="${it.isHeaderDangerous(n)}" >
<tr>

View File

@ -32,7 +32,7 @@ THE SOFTWARE.
<!-- get default/common page variable -->
${h.initPageVariables(context)}
<x:doctype name="html" />
<html lang="${request.getLocale().toLanguageTag()}">
<html lang="${request2.getLocale().toLanguageTag()}">
<head data-rooturl="${rootURL}" data-resurl="${resURL}" data-imagesurl="${imagesURL}" resURL="${resURL}">
<title>${%Starting Jenkins}</title>
<!-- we do not want bots on this page -->

View File

@ -32,7 +32,7 @@ THE SOFTWARE.
<!-- get default/common page variable -->
${h.initPageVariables(context)}
<x:doctype name="html" />
<html lang="${request.getLocale().toLanguageTag()}">
<html lang="${request2.getLocale().toLanguageTag()}">
<head data-rooturl="${rootURL}" data-resurl="${resURL}" data-imagesurl="${imagesURL}" resURL="${resURL}">
<title>${%Restarting Jenkins}</title>
<!-- we do not want bots on this page -->

View File

@ -37,7 +37,7 @@ THE SOFTWARE.
<st:include it="${requestScope.instance}" class="${requestScope.descriptor.clazz}" page="config.jelly" optional="true" />
<f:bottomButtonBar>
<input type="hidden" name="cloudDescriptorName" value="${request.getParameter('mode')}"/>
<input type="hidden" name="cloudDescriptorName" value="${request2.getParameter('mode')}"/>
<f:submit value="${%Save}"/>
</f:bottomButtonBar>

View File

@ -5,7 +5,7 @@
<l:main-panel>
<st:include page="client-scripts" />
<form action="${app.instance.securityRealm.authenticationGatewayUrl}" method="POST">
<input type="hidden" name="from" value="${request.getParameter('from')}" />
<input type="hidden" name="from" value="${request2.getParameter('from')}" />
<div class="plugin-setup-wizard bootstrap-3">
<div class="modal fade in" style="display: block;">
<div class="modal-dialog">

View File

@ -28,8 +28,8 @@ THE SOFTWARE.
This is the page designated by web.xml to handle 404 errors. Show a nice error message and suggest logging in if applicable.
Generally kept very similar to 'oops.jelly'.
-->
<st:statusCode value="${response.getStatus()}" />
${request.session.setAttribute('from', request.getAttribute('jakarta.servlet.error.request_uri'))}
<st:statusCode value="${response2.getStatus()}" />
${request2.session.setAttribute('from', request2.getAttribute('jakarta.servlet.error.request_uri'))}
<l:layout title="${%title(jakarta.servlet.error.message?:'Not Found')}" type="one-column">
<l:header />
<l:main-panel>
@ -39,7 +39,7 @@ THE SOFTWARE.
</h1>
<div id="error-description" style="text-align: center">
<h2>${%title(jakarta.servlet.error.message?:'Not Found')}</h2>
<j:if test="${!request.getAttribute('jenkins.security.ResourceDomainRootAction.error')}">
<j:if test="${!request2.getAttribute('jenkins.security.ResourceDomainRootAction.error')}">
<p>
<j:choose>
<j:when test="${app.useSecurity}">

View File

@ -36,7 +36,7 @@ THE SOFTWARE.
<st:setHeader name="Referrer-Policy" value="same-origin" />
<st:setHeader name="Cross-Origin-Opener-Policy" value="same-origin" />
<x:doctype name="html" />
<st:statusCode value="${response.getStatus()}" />
<st:statusCode value="${response2.getStatus()}" />
<html>
<head>
<title>${%title(jakarta.servlet.error.message?:'Not Found')}</title>

View File

@ -29,7 +29,7 @@ THE SOFTWARE.
<st:setHeader name="Expires" value="0" />
<st:setHeader name="Cache-Control" value="no-cache,no-store,must-revalidate" />
<!-- needed for cli -->
<j:if test="${request.servletPath=='/' || request.servletPath==''}">
<j:if test="${request2.servletPath=='/' || request2.servletPath==''}">
${h.advertiseHeaders(response)}
<j:forEach var="pd" items="${h.pageDecorators}">
<st:include it="${pd}" page="httpHeaders.jelly" optional="true"/>
@ -39,19 +39,19 @@ THE SOFTWARE.
needed to generate a session if non exists,
without it we would add ";jsessionid=" to the url which will result in a 404
-->
<j:set var="_" value="${request.getSession()}"/>
<j:set var="_" value="${request2.getSession()}"/>
<!-- get default/common page variable -->
${h.initPageVariables(context)}
<x:doctype name="html" />
<!-- needed for the redirect after login -->
<j:set var="from" value="${error ? request.session.getAttribute('from') : request.getParameter('from')}"/>
<j:set var="from" value="${error ? request2.session.getAttribute('from') : request2.getParameter('from')}"/>
<!-- in case of error we want to surround the form elements with an error hint -->
<j:set var="inputClass" value="${error ? 'jenkins-input--error' : ''}"/>
<j:set var="simpleDecorator" value="${h.simplePageDecorator}"/>
<j:set var="simpleDecorators" value="${h.simplePageDecorators}"/>
<!-- real deal starting here -->
<html lang="${request.getLocale().toLanguageTag()}">
<html lang="${request2.getLocale().toLanguageTag()}">
<head data-rooturl="${rootURL}" data-resurl="${resURL}" data-imagesurl="${imagesURL}" resURL="${resURL}">
<title>${%signIn} - Jenkins</title>
<!-- we do not want bots on this page -->

View File

@ -27,7 +27,7 @@ THE SOFTWARE.
<!--
This is the page designated by web.xml and UncaughtExceptionHandler to process an exception thrown by us.
-->
<st:statusCode value="${response.getStatus()}" />
<st:statusCode value="${response2.getStatus()}" />
<l:layout title="Jenkins" type="one-column">
<l:header />
<l:main-panel>
@ -36,12 +36,12 @@ THE SOFTWARE.
</h1>
<div id="error-description">
<h2 style="text-align: center">${%problemHappened}</h2>
<p style="text-align: center">Logging ID=${request.getAttribute('jenkins.exception.id')}</p>
<p style="text-align: center">Logging ID=${request2.getAttribute('jenkins.exception.id')}</p>
</div>
<j:if test="${app.shouldShowStackTrace()}">
<p>${%checkJIRA} ${%vote} ${%pleaseReport} ${%stackTracePlease} ${%checkML}</p>
<h2>${%Stack trace}</h2>
<pre style="margin:2em; clear:both">${h.printThrowable(request.getAttribute('jakarta.servlet.error.exception'))}</pre>
<pre style="margin:2em; clear:both">${h.printThrowable(request2.getAttribute('jakarta.servlet.error.exception'))}</pre>
</j:if>
</l:main-panel>
</l:layout>

View File

@ -44,25 +44,25 @@ THE SOFTWARE.
<form action="projectRelationship" method="get">
<div class="jenkins-form-item">
<label class="jenkins-form-label" for="lhs">${%upstream project}:</label>
<f:combobox id="lhs" fillUrl="fillJobNameItems" name="lhs" value="${request.getParameter('lhs')}"/>
<f:combobox id="lhs" fillUrl="fillJobNameItems" name="lhs" value="${request2.getParameter('lhs')}"/>
</div>
<div class="jenkins-form-item">
<label class="jenkins-form-label" for="rhs">${%downstream project}:</label>
<f:combobox id="rhs" fillUrl="fillJobNameItems" name="rhs" value="${request.getParameter('rhs')}"/>
<f:combobox id="rhs" fillUrl="fillJobNameItems" name="rhs" value="${request2.getParameter('rhs')}"/>
</div>
<f:submit value="${%Compare}"/>
<j:if test="${!empty(request.getParameter('lhs')) and !empty(request.getParameter('rhs'))}">
<j:set var="jl" value="${app.getItemByFullName(request.getParameter('lhs'))}"/>
<j:set var="jr" value="${app.getItemByFullName(request.getParameter('rhs'))}"/>
<j:if test="${!empty(request2.getParameter('lhs')) and !empty(request2.getParameter('rhs'))}">
<j:set var="jl" value="${app.getItemByFullName(request2.getParameter('lhs'))}"/>
<j:set var="jr" value="${app.getItemByFullName(request2.getParameter('rhs'))}"/>
<j:choose>
<j:when test="${jl==null}">
<p class="error">No such project '${request.getParameter('lhs')}'</p>
<p class="error">No such project '${request2.getParameter('lhs')}'</p>
</j:when>
<j:when test="${jr==null}">
<p class="error">No such project '${request.getParameter('rhs')}'</p>
<p class="error">No such project '${request2.getParameter('rhs')}'</p>
</j:when>
<j:otherwise>
<table width="100%">

View File

@ -13,7 +13,7 @@
id="user-seed-property-reset-seed"
data-target-url="${descriptor.descriptorFullUrl}/renewSessionSeed"
data-confirm="${%resetSeed.confirmation}"
data-redirect-url="${request.contextPath}/"
data-redirect-url="${request2.contextPath}/"
data-confirm-title="${%resetSeed.confirmationTitle}">
${%resetSeed.button}
</button>

View File

@ -41,8 +41,8 @@ THE SOFTWARE.
</st:documentation>
<st:once>
<script type="text/javascript" src="${request.contextPath}/scripts/utilities.js"/>
<script type="text/javascript" src="${request.contextPath}/scripts/combobox.js"/>
<script type="text/javascript" src="${request2.contextPath}/scripts/utilities.js"/>
<script type="text/javascript" src="${request2.contextPath}/scripts/combobox.js"/>
</st:once>
<f:prepareDatabinding/>

View File

@ -46,7 +46,7 @@ THE SOFTWARE.
</p>
<form action="script" method="post">
<textarea id="script" name="script" class="script">${request.getParameter('script')}</textarea>
<textarea id="script" name="script" class="script">${request2.getParameter('script')}</textarea>
<div align="right">
<f:submit value="${%Run}"/>
</div>

View File

@ -1,10 +1,16 @@
/**
* Use the `widget-refresh-reference` class on an element with `data-id` and `data-url` attributes.
* The content from the URL will be used to replace the element with the specified ID.
* Usually the URL content is an element with the same ID as specified here, to allow continuous updates.
* This is primarily used for sidepanel widgets, but not exclusively.
*/
Behaviour.specify(
".widget-refresh-reference",
"widget-refresh",
0,
function (e) {
var id = e.getAttribute("data-id");
var url = e.getAttribute("data-url");
let id = e.getAttribute("data-id");
let url = e.getAttribute("data-url");
refreshPart(id, url);
},
);

View File

@ -41,7 +41,7 @@ THE SOFTWARE.
<div id="breadcrumbBar" class="jenkins-breadcrumbs" aria-label="breadcrumb">
<ol class="jenkins-breadcrumbs__list" id="breadcrumbs">
<j:forEach var="anc" items="${request.ancestors}" indexVar="index">
<j:forEach var="anc" items="${request2.ancestors}" indexVar="index">
<j:if test="${h.isModel(anc.object) and anc.prev.url!=anc.url}">
<j:set var="mode" value="breadcrumbs" />
<l:breadcrumb title="${anc.object == app ? '%Dashboard' : anc.object.displayName}"

View File

@ -90,9 +90,9 @@ THE SOFTWARE.
<j:set var="layoutType" value="${attrs.type}"/>
</j:if>
<j:set var="_" value="${request.getSession()}"/>
<j:set var="_" value="${request2.getSession()}"/>
<j:set var="extensionsAvailable" value="${h.extensionsAvailable}"/>
<j:if test="${request.servletPath=='/' || request.servletPath==''}">
<j:if test="${request2.servletPath=='/' || request2.servletPath==''}">
${h.advertiseHeaders(response)}
<j:if test="${extensionsAvailable}">
<j:forEach var="pd" items="${h.pageDecorators}">

View File

@ -239,13 +239,6 @@
<Class name="jenkins.util.xml.XMLUtils"/>
</Or>
</And>
<And>
<Bug pattern="INSECURE_COOKIE"/>
<Or>
<Class name="hudson.security.SecurityRealm"/>
<Class name="jenkins.model.Jenkins"/>
</Or>
</And>
<And>
<Bug pattern="LI_LAZY_INIT_STATIC"/>
<Class name="hudson.triggers.Trigger"/>

View File

@ -81,13 +81,16 @@ public class MarkupTextTest {
* Start/end tag nesting should be correct regardless of the order tags are added.
*/
@Test
public void adjacent() {
public void addMarkupInOrder() {
MarkupText text = new MarkupText("abcdef");
text.addMarkup(0, 3, "$", "$");
text.addMarkup(3, 6, "#", "#");
assertEquals("$abc$#def#", text.toString(false));
}
text = new MarkupText("abcdef");
@Test
public void addMarkupInReversedOrder() {
MarkupText text = new MarkupText("abcdef");
text.addMarkup(3, 6, "#", "#");
text.addMarkup(0, 3, "$", "$");
assertEquals("$abc$#def#", text.toString(false));

View File

@ -27,18 +27,22 @@ package hudson.scheduler;
import static java.util.Calendar.MONDAY;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThrows;
import antlr.ANTLRException;
import java.text.DateFormat;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.Locale;
import java.util.TimeZone;
import java.util.function.Function;
import org.junit.Test;
import org.jvnet.hudson.test.Issue;
@ -340,6 +344,24 @@ public class CronTabTest {
assertEquals("[35, 56]", times.toString());
}
@Test public void floorCeilMinuteGranularity() throws Exception {
var tab = new CronTab("*/5 * * * *");
assertFloorCeil(tab, new GregorianCalendar(2025, 2, 4, 14, 15, 23), "2025-03-04T14:15:00", "2025-03-04T14:20:00");
assertFloorCeil(tab, new GregorianCalendar(2025, 2, 4, 14, 19, 23), "2025-03-04T14:15:00", "2025-03-04T14:20:00");
assertFloorCeil(tab, new GregorianCalendar(2025, 2, 4, 14, 16, 0), "2025-03-04T14:15:00", "2025-03-04T14:20:00");
assertFloorCeil(tab, new GregorianCalendar(2025, 2, 4, 14, 19, 0), "2025-03-04T14:15:00", "2025-03-04T14:20:00");
assertFloorCeil(tab, new GregorianCalendar(2025, 2, 4, 14, 15, 0), "2025-03-04T14:15:00", "2025-03-04T14:15:00");
}
private static void assertFloorCeil(CronTab tab, Calendar now, String expectedFloor, String expectedCeil) {
Function<Calendar, String> fmt = c -> ZonedDateTime.ofInstant(c.toInstant(), c.getTimeZone().toZoneId()).format(DateTimeFormatter.ISO_LOCAL_DATE_TIME);
var nowFormatted = fmt.apply(now);
var nowClone = (Calendar) now.clone();
assertThat("floor of " + nowFormatted, fmt.apply(tab.floor(nowClone)), is(expectedFloor));
nowClone = (Calendar) now.clone();
assertThat("ceil of " + nowFormatted, fmt.apply(tab.ceil(nowClone)), is(expectedCeil));
}
@Issue("SECURITY-790")
@Test(timeout = 1000L) public void testLongMonths() throws Exception {
Calendar cal = Calendar.getInstance();

View File

@ -7,10 +7,14 @@ import static org.junit.Assert.assertTrue;
import hudson.FilePath;
import hudson.Functions;
import hudson.Util;
import hudson.util.TextFile;
import java.io.File;
import java.io.IOException;
import java.nio.charset.MalformedInputException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.security.SecureRandom;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
@ -20,6 +24,8 @@ public class DefaultConfidentialStoreTest {
@Rule
public TemporaryFolder tmpRule = new TemporaryFolder();
private final SecureRandom sr = new SecureRandom();
@Test
public void roundtrip() throws Exception {
File tmp = new File(tmpRule.getRoot(), "tmp"); // let ConfidentialStore create a directory
@ -27,20 +33,8 @@ public class DefaultConfidentialStoreTest {
DefaultConfidentialStore store = new DefaultConfidentialStore(tmp);
ConfidentialKey key = new ConfidentialKey("test") {};
// basic roundtrip
String str = "Hello world!";
store.store(key, str.getBytes(StandardCharsets.UTF_8));
assertEquals(str, new String(store.load(key), StandardCharsets.UTF_8));
// data storage should have some stuff
assertTrue(new File(tmp, "test").exists());
assertTrue(new File(tmp, "master.key").exists());
assertThrows(MalformedInputException.class, () -> Files.readString(tmp.toPath().resolve("test"), StandardCharsets.UTF_8)); // the data shouldn't be a plain text, obviously
if (!Functions.isWindows()) {
assertEquals(0700, new FilePath(tmp).mode() & 0777); // should be read only
}
roundTrip(store, key, tmp);
// if the master key changes, we should gracefully fail to load the store
new File(tmp, "master.key").delete();
@ -49,4 +43,42 @@ public class DefaultConfidentialStoreTest {
assertNull(store2.load(key));
}
private static void roundTrip(DefaultConfidentialStore store, ConfidentialKey key, File tmp) throws IOException, InterruptedException {
// basic roundtrip
String str = "Hello world!";
store.store(key, str.getBytes(StandardCharsets.UTF_8));
assertEquals(str, new String(store.load(key), StandardCharsets.UTF_8));
// data storage should have some stuff
assertTrue(new File(tmp, "test").exists());
assertThrows(MalformedInputException.class, () -> Files.readString(tmp.toPath().resolve("test"), StandardCharsets.UTF_8)); // the data shouldn't be a plain text, obviously
if (!Functions.isWindows()) {
assertEquals(0700, new FilePath(tmp).mode() & 0777); // should be read only
}
}
@Test
public void masterKeyGeneratedBeforehand() throws IOException, InterruptedException {
File external = new File(tmpRule.getRoot(), "external");
File tmp = new File(tmpRule.getRoot(), "tmp");
var masterKeyFile = new File(external, "master.key");
new TextFile(masterKeyFile).write(Util.toHexString(randomBytes(128)));
System.setProperty(DefaultConfidentialStore.MASTER_KEY_FILE_SYSTEM_PROPERTY, masterKeyFile.getAbsolutePath());
System.setProperty(DefaultConfidentialStore.MASTER_KEY_READONLY_SYSTEM_PROPERTY_NAME, "true");
DefaultConfidentialStore store = new DefaultConfidentialStore(tmp);
ConfidentialKey key = new ConfidentialKey("test") {};
roundTrip(store, key, tmp);
// With this configuration, the master key file deletion is fatal
masterKeyFile.delete();
assertThrows(IOException.class, () -> new DefaultConfidentialStore(tmp));
}
private byte[] randomBytes(int size) {
byte[] random = new byte[size];
sr.nextBytes(random);
return random;
}
}

View File

@ -26,12 +26,12 @@
"@babel/cli": "7.26.4",
"@babel/core": "7.26.7",
"@babel/preset-env": "7.26.7",
"@eslint/js": "9.19.0",
"@eslint/js": "9.20.0",
"babel-loader": "9.2.1",
"clean-webpack-plugin": "4.0.0",
"css-loader": "7.1.2",
"css-minimizer-webpack-plugin": "7.0.0",
"eslint": "9.19.0",
"eslint": "9.20.0",
"eslint-config-prettier": "10.0.1",
"eslint-formatter-checkstyle": "8.40.0",
"globals": "15.14.0",
@ -42,7 +42,7 @@
"postcss-preset-env": "10.1.3",
"postcss-scss": "4.0.9",
"prettier": "3.4.2",
"sass": "1.83.4",
"sass": "1.84.0",
"sass-loader": "16.0.4",
"style-loader": "4.0.0",
"stylelint": "16.14.1",

View File

@ -73,9 +73,9 @@ THE SOFTWARE.
</issueManagement>
<properties>
<revision>2.496</revision>
<revision>2.498</revision>
<changelist>-SNAPSHOT</changelist>
<project.build.outputTimestamp>2025-01-28T13:58:52Z</project.build.outputTimestamp>
<project.build.outputTimestamp>2025-02-11T14:10:23Z</project.build.outputTimestamp>
<!-- configuration for patch tracker plugin -->
<project.patchManagement.system>github</project.patchManagement.system>

View File

@ -118,7 +118,7 @@ THE SOFTWARE.
<!-- requireUpperBoundDeps via matrix-project and junit -->
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>script-security</artifactId>
<version>1369.v9b_98a_4e95b_2d</version>
<version>1373.vb_b_4a_a_c26fa_00</version>
</dependency>
<dependency>
<groupId>org.jenkins-ci.plugins.workflow</groupId>
@ -134,7 +134,7 @@ THE SOFTWARE.
<!-- Required by plugin-util-api -->
<groupId>org.jenkins-ci.plugins.workflow</groupId>
<artifactId>workflow-support</artifactId>
<version>944.v5a_859593b_98a_</version>
<version>946.v2a_79d8a_4b_e14</version>
</dependency>
</dependencies>
</dependencyManagement>
@ -224,13 +224,13 @@ THE SOFTWARE.
<dependency>
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>credentials</artifactId>
<version>1405.vb_cda_74a_f8974</version>
<version>1408.va_622a_b_f5b_1b_1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>junit</artifactId>
<version>1312.v1a_235a_b_94a_31</version>
<version>1314.vd966e9a_88895</version>
<scope>test</scope>
</dependency>
<dependency>
@ -344,7 +344,7 @@ THE SOFTWARE.
<artifactItem>
<groupId>io.jenkins.plugins</groupId>
<artifactId>design-library</artifactId>
<version>354.v87d9d5804b_c1</version>
<version>355.v0f007356e15d</version>
<type>hpi</type>
<outputDirectory>${project.build.outputDirectory}/plugins</outputDirectory>
<destFileName>design-library.jpi</destFileName>

View File

@ -102,7 +102,7 @@ public class GlobalComputerRetentionCheckIntervalConfigurationTest {
JSONObject json = new JSONObject();
json.element("computerRetentionCheckInterval", 5);
try {
c.configure(Stapler.getCurrentRequest(), json);
c.configure(Stapler.getCurrentRequest2(), json);
} catch (Descriptor.FormException e) {
throw new RuntimeException(e);
}
@ -121,7 +121,7 @@ public class GlobalComputerRetentionCheckIntervalConfigurationTest {
JSONObject json = new JSONObject();
json.element("computerRetentionCheckInterval", interval);
try {
c.configure(Stapler.getCurrentRequest(), json);
c.configure(Stapler.getCurrentRequest2(), json);
throw new RuntimeException("expected .configure() to throw");
} catch (Descriptor.FormException e) {
assertEquals(e.getMessage(), message);

View File

@ -13,6 +13,7 @@ import static org.junit.Assert.assertTrue;
import edu.umd.cs.findbugs.annotations.CheckForNull;
import edu.umd.cs.findbugs.annotations.NonNull;
import hudson.ExtensionList;
import hudson.Util;
import hudson.model.UnprotectedRootAction;
import hudson.security.csrf.CrumbExclusion;
import jakarta.servlet.FilterChain;
@ -31,7 +32,6 @@ import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.regex.Pattern;
import net.sf.json.JSONObject;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.io.IOUtils;
import org.junit.Before;
import org.junit.Rule;
@ -99,7 +99,7 @@ public class TelemetryTest {
.atMost(10, TimeUnit.SECONDS)
.until(() -> types, hasItem("test-data"));
//90ecf3ce1cd5ba1e5ad3cde7ad08a941e884f2e4d9bd463361715abab8efedc5
assertThat(correlators, hasItem(DigestUtils.sha256Hex(correlationId + "test-data")));
assertThat(correlators, hasItem(Util.getHexOfSHA256DigestOf(correlationId + "test-data")));
}
@Test

View File

@ -309,14 +309,14 @@ THE SOFTWARE.
<!-- dependency of command-launcher, junit, matrix-project, and workflow-support -->
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>script-security</artifactId>
<version>1369.v9b_98a_4e95b_2d</version>
<version>1373.vb_b_4a_a_c26fa_00</version>
<type>hpi</type>
</artifactItem>
<artifactItem>
<!-- detached after 1.577 -->
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>junit</artifactId>
<version>1312.v1a_235a_b_94a_31</version>
<version>1314.vd966e9a_88895</version>
<type>hpi</type>
</artifactItem>
<artifactItem>
@ -344,7 +344,7 @@ THE SOFTWARE.
<!-- dependency of junit -->
<groupId>io.jenkins.plugins</groupId>
<artifactId>checks-api</artifactId>
<version>2.2.1</version>
<version>2.2.2</version>
<type>hpi</type>
</artifactItem>
@ -352,7 +352,7 @@ THE SOFTWARE.
<!-- dependency of checks-api and plugin-util-api -->
<groupId>org.jenkins-ci.plugins.workflow</groupId>
<artifactId>workflow-support</artifactId>
<version>944.v5a_859593b_98a_</version>
<version>946.v2a_79d8a_4b_e14</version>
<type>hpi</type>
</artifactItem>
@ -462,7 +462,7 @@ THE SOFTWARE.
<!-- detached after 2.281 -->
<groupId>org.jenkins-ci.modules</groupId>
<artifactId>sshd</artifactId>
<version>3.330.vc866a_8389b_58</version>
<version>3.353.v2b_d33c46e970</version>
<type>hpi</type>
</artifactItem>
<artifactItem>
@ -497,7 +497,7 @@ THE SOFTWARE.
<!-- dependency of jdk-tool -->
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>apache-httpcomponents-client-4-api</artifactId>
<version>4.5.14-208.v438351942757</version>
<version>4.5.14-269.vfa_2321039a_83</version>
<type>hpi</type>
</artifactItem>
<artifactItem>
@ -525,7 +525,7 @@ THE SOFTWARE.
<!-- dependency of trilead-api -->
<groupId>io.jenkins.plugins</groupId>
<artifactId>gson-api</artifactId>
<version>2.11.0-109.v1ef91dd0829a_</version>
<version>2.12.1-113.v347686d6729f</version>
<type>hpi</type>
</artifactItem>
<artifactItem>
@ -691,12 +691,10 @@ THE SOFTWARE.
<javax.xml.transform.TransformerFactory>com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl</javax.xml.transform.TransformerFactory>
</systemProperties>
<webApp>
<!-- Allows resources to be reloaded, and enable nicer console logging. -->
<extraClasspath>${project.basedir}/../core/src/main/resources,${project.basedir}/../core/target/classes,${project.build.directory}/support-log-formatter.jar</extraClasspath>
<!-- Enable nicer console logging. -->
<extraClasspath>${project.build.directory}/support-log-formatter.jar</extraClasspath>
<contextPath>${contextPath}</contextPath>
<configurationDiscovered>false</configurationDiscovered>
<!-- see https://wiki.eclipse.org/Jetty/Howto/Avoid_slow_deployment -->
<webInfIncludeJarPattern>NONE</webInfIncludeJarPattern>
<webInfIncludeJarPattern>.*(jenkins-core|target/classes).*</webInfIncludeJarPattern>
</webApp>
</configuration>
</plugin>

View File

@ -26,256 +26,7 @@ THE SOFTWARE.
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1"
metadata-complete="true">
version="3.1">
<display-name>Jenkins v${project.version}</display-name>
<description>Build management system</description>
<servlet>
<servlet-name>Stapler</servlet-name>
<servlet-class>org.kohsuke.stapler.Stapler</servlet-class>
<init-param>
<param-name>default-encodings</param-name>
<param-value>text/html=UTF-8</param-value>
</init-param>
<init-param>
<param-name>diagnosticThreadName</param-name>
<param-value>false</param-value>
</init-param>
<async-supported>true</async-supported>
</servlet>
<servlet-mapping>
<servlet-name>Stapler</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
<filter>
<filter-name>suspicious-request-filter</filter-name>
<filter-class>jenkins.security.SuspiciousRequestFilter</filter-class>
<async-supported>true</async-supported>
</filter>
<filter>
<filter-name>diagnostic-name-filter</filter-name>
<filter-class>org.kohsuke.stapler.DiagnosticThreadNameFilter</filter-class>
<async-supported>true</async-supported>
</filter>
<filter>
<filter-name>encoding-filter</filter-name>
<filter-class>hudson.util.CharacterEncodingFilter</filter-class>
<async-supported>true</async-supported>
</filter>
<filter>
<filter-name>uncaught-exception-filter</filter-name>
<filter-class>org.kohsuke.stapler.UncaughtExceptionFilter</filter-class>
<async-supported>true</async-supported>
</filter>
<filter>
<filter-name>authentication-filter</filter-name>
<filter-class>hudson.security.HudsonFilter</filter-class>
<async-supported>true</async-supported>
</filter>
<filter>
<filter-name>csrf-filter</filter-name>
<filter-class>hudson.security.csrf.CrumbFilter</filter-class>
<async-supported>true</async-supported>
</filter>
<filter>
<filter-name>error-attribute-filter</filter-name>
<filter-class>jenkins.ErrorAttributeFilter</filter-class>
<async-supported>true</async-supported>
</filter>
<filter>
<filter-name>plugins-filter</filter-name>
<filter-class>hudson.util.PluginServletFilter</filter-class>
<async-supported>true</async-supported>
</filter>
<!--
The Headers filter allows us to override headers sent by the container
that may be in conflict with what we want. For example, Tomcat will set
Cache-Control: no-cache for any files behind the security-constraint
below. So if Hudson is on a public server, and you want to only allow
authorized users to access it, you may want to pay attention to this.
See: http://www.nabble.com/No-browser-caching-with-Hudson- -tf4601857.html
<filter>
<filter-name>change-headers-filter</filter-name>
<filter-class>hudson.ResponseHeaderFilter</filter-class>
<!- The value listed here is for 24 hours. Increase or decrease as you see
fit. Value is in seconds. Make sure to keep the public option ->
<init-param>
<param-name>Cache-Control</param-name>
<param-value>max-age=86400, public</param-value>
</init-param>
<!- It turns out that Tomcat just doesn't want to let
go of its cache option. If you override Cache-Control,
it starts to send Pragma: no-cache as a backup.
->
<init-param>
<param-name>Pragma</param-name>
<param-value>public</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>change-headers-filter</filter-name>
<url-pattern>*.css</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>change-headers-filter</filter-name>
<url-pattern>*.gif</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>change-headers-filter</filter-name>
<url-pattern>*.js</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>change-headers-filter</filter-name>
<url-pattern>*.png</url-pattern>
</filter-mapping>
-->
<filter-mapping>
<filter-name>suspicious-request-filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>diagnostic-name-filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>encoding-filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>uncaught-exception-filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>authentication-filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>csrf-filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>error-attribute-filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>plugins-filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<listener>
<!-- Must be before WebAppMain in order to initialize the context before the first use of this class. -->
<listener-class>jenkins.util.SystemProperties$Listener</listener-class>
</listener>
<listener>
<listener-class>hudson.WebAppMain</listener-class>
</listener>
<listener>
<listener-class>jenkins.JenkinsHttpSessionListener</listener-class>
</listener>
<!--
JENKINS-1235 suggests containers interpret '*' as "all roles defined in web.xml"
as opposed to "all roles defined in the security realm", so we need to list some
common names in the hope that users will have at least one of those roles.
-->
<security-role>
<role-name>admin</role-name>
</security-role>
<security-role>
<role-name>user</role-name>
</security-role>
<security-role>
<role-name>hudson</role-name>
</security-role>
<security-constraint>
<web-resource-collection>
<web-resource-name>Hudson</web-resource-name>
<url-pattern>/loginEntry</url-pattern>
<!--http-method>GET</http-method-->
</web-resource-collection>
<auth-constraint>
<role-name>**</role-name>
</auth-constraint>
</security-constraint>
<!-- Disable TRACE method with security constraint (copied from jetty/webdefaults.xml) -->
<security-constraint>
<web-resource-collection>
<web-resource-name>Disable TRACE</web-resource-name>
<url-pattern>/*</url-pattern>
<http-method>TRACE</http-method>
</web-resource-collection>
<auth-constraint />
</security-constraint>
<security-constraint>
<web-resource-collection>
<web-resource-name>other</web-resource-name>
<url-pattern>/*</url-pattern>
</web-resource-collection>
<!-- no security constraint -->
</security-constraint>
<login-config>
<auth-method>FORM</auth-method>
<form-login-config>
<form-login-page>/login</form-login-page>
<form-error-page>/loginError</form-error-page>
</form-login-config>
</login-config>
<!-- configure additional extension-content-type mappings -->
<mime-mapping>
<extension>xml</extension>
<mime-type>application/xml</mime-type>
</mime-mapping>
<!--mime-mapping> commenting out until this works out of the box with JOnAS. See http://www.nabble.com/Error-with-mime-type%2D-%27application-xslt%2Bxml%27-when-deploying-hudson-1.316-in-jonas-td24740489.html
<extension>xsl</extension>
<mime-type>application/xslt+xml</mime-type>
</mime-mapping-->
<mime-mapping>
<extension>log</extension>
<mime-type>text/plain</mime-type>
</mime-mapping>
<mime-mapping>
<extension>war</extension>
<mime-type>application/octet-stream</mime-type>
</mime-mapping>
<mime-mapping>
<extension>ear</extension>
<mime-type>application/octet-stream</mime-type>
</mime-mapping>
<mime-mapping>
<extension>rar</extension>
<mime-type>application/octet-stream</mime-type>
</mime-mapping>
<mime-mapping>
<extension>webm</extension>
<mime-type>video/webm</mime-type>
</mime-mapping>
<error-page>
<exception-type>java.lang.Throwable</exception-type>
<location>/oops</location>
</error-page>
<error-page>
<error-code>404</error-code>
<location>/404</location>
</error-page>
<session-config>
<cookie-config>
<!-- See https://www.owasp.org/index.php/HttpOnly for the discussion of this topic in OWASP -->
<http-only>true</http-only>
</cookie-config>
<!-- Tracking mode is managed by WebAppMain.FORCE_SESSION_TRACKING_BY_COOKIE_PROP -->
</session-config>
</web-app>

View File

@ -1669,6 +1669,15 @@ __metadata:
languageName: node
linkType: hard
"@eslint/core@npm:^0.11.0":
version: 0.11.0
resolution: "@eslint/core@npm:0.11.0"
dependencies:
"@types/json-schema": "npm:^7.0.15"
checksum: 10c0/1e0671d035c908175f445864a7864cf6c6a8b67a5dfba8c47b2ac91e2d3ed36e8c1f2fd81d98a73264f8677055559699d4adb0f97d86588e616fc0dc9a4b86c9
languageName: node
linkType: hard
"@eslint/eslintrc@npm:^3.2.0":
version: 3.2.0
resolution: "@eslint/eslintrc@npm:3.2.0"
@ -1686,10 +1695,10 @@ __metadata:
languageName: node
linkType: hard
"@eslint/js@npm:9.19.0":
version: 9.19.0
resolution: "@eslint/js@npm:9.19.0"
checksum: 10c0/45dc544c8803984f80a438b47a8e578fae4f6e15bc8478a703827aaf05e21380b42a43560374ce4dad0d5cb6349e17430fc9ce1686fed2efe5d1ff117939ff90
"@eslint/js@npm:9.20.0":
version: 9.20.0
resolution: "@eslint/js@npm:9.20.0"
checksum: 10c0/10e7b5b9e628b5192e8fc6b0ecd27cf48322947e83e999ff60f9f9e44ac8d499138bcb9383cbfa6e51e780d53b4e76ccc2d1753b108b7173b8404fd484d37328
languageName: node
linkType: hard
@ -3524,16 +3533,16 @@ __metadata:
languageName: node
linkType: hard
"eslint@npm:9.19.0":
version: 9.19.0
resolution: "eslint@npm:9.19.0"
"eslint@npm:9.20.0":
version: 9.20.0
resolution: "eslint@npm:9.20.0"
dependencies:
"@eslint-community/eslint-utils": "npm:^4.2.0"
"@eslint-community/regexpp": "npm:^4.12.1"
"@eslint/config-array": "npm:^0.19.0"
"@eslint/core": "npm:^0.10.0"
"@eslint/core": "npm:^0.11.0"
"@eslint/eslintrc": "npm:^3.2.0"
"@eslint/js": "npm:9.19.0"
"@eslint/js": "npm:9.20.0"
"@eslint/plugin-kit": "npm:^0.2.5"
"@humanfs/node": "npm:^0.16.6"
"@humanwhocodes/module-importer": "npm:^1.0.1"
@ -3569,7 +3578,7 @@ __metadata:
optional: true
bin:
eslint: bin/eslint.js
checksum: 10c0/3b0dfaeff6a831de086884a3e2432f18468fe37c69f35e1a0a9a2833d9994a65b6dd2a524aaee28f361c849035ad9d15e3841029b67d261d0abd62c7de6d51f5
checksum: 10c0/5eb2d9b5ed85a0b022871d19719417d110afb07a4abfedd856ad03d9a821def5f6bc31d7c407ca27f56e5e66e39375300fd2b877017245eb99c44060d6c983bd
languageName: node
linkType: hard
@ -4381,12 +4390,12 @@ __metadata:
"@babel/cli": "npm:7.26.4"
"@babel/core": "npm:7.26.7"
"@babel/preset-env": "npm:7.26.7"
"@eslint/js": "npm:9.19.0"
"@eslint/js": "npm:9.20.0"
babel-loader: "npm:9.2.1"
clean-webpack-plugin: "npm:4.0.0"
css-loader: "npm:7.1.2"
css-minimizer-webpack-plugin: "npm:7.0.0"
eslint: "npm:9.19.0"
eslint: "npm:9.20.0"
eslint-config-prettier: "npm:10.0.1"
eslint-formatter-checkstyle: "npm:8.40.0"
globals: "npm:15.14.0"
@ -4401,7 +4410,7 @@ __metadata:
postcss-preset-env: "npm:10.1.3"
postcss-scss: "npm:4.0.9"
prettier: "npm:3.4.2"
sass: "npm:1.83.4"
sass: "npm:1.84.0"
sass-loader: "npm:16.0.4"
sortablejs: "npm:1.15.6"
style-loader: "npm:4.0.0"
@ -6423,9 +6432,9 @@ __metadata:
languageName: node
linkType: hard
"sass@npm:1.83.4":
version: 1.83.4
resolution: "sass@npm:1.83.4"
"sass@npm:1.84.0":
version: 1.84.0
resolution: "sass@npm:1.84.0"
dependencies:
"@parcel/watcher": "npm:^2.4.1"
chokidar: "npm:^4.0.0"
@ -6436,7 +6445,7 @@ __metadata:
optional: true
bin:
sass: sass.js
checksum: 10c0/6f27f0eebfeb50222b14baaeef548ef58a05daf8abd9797e6c499334ed7ad40541767056c8693780d06ca83d8836348ea7396a923d3be439b133507993ca78be
checksum: 10c0/4af28c12416b6f1fec2423677cfa8c48af7fb7652a50bd076e0cdd1ea260f0330948ddd6075368a734b8d6cfa16c9af5518292181334f47a9471cb542599bc7b
languageName: node
linkType: hard