diff --git a/.github/workflows/run-since-updater.yml b/.github/workflows/run-since-updater.yml index 619adca29c..45fc101d60 100644 --- a/.github/workflows/run-since-updater.yml +++ b/.github/workflows/run-since-updater.yml @@ -29,7 +29,7 @@ jobs: id: run_script shell: bash - name: Create Pull Request - uses: peter-evans/create-pull-request@dd2324fc52d5d43c699a5636bcf19fceaa70c284 # v7 + uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e # v7 with: token: ${{ secrets.GITHUB_TOKEN }} commit-message: Fill in since annotations diff --git a/bom/pom.xml b/bom/pom.xml index 7ebb05d082..78dcd42bf5 100644 --- a/bom/pom.xml +++ b/bom/pom.xml @@ -56,7 +56,7 @@ THE SOFTWARE. org.slf4j slf4j-bom - 2.0.16 + 2.0.17 pom import diff --git a/cli/src/main/java/hudson/cli/CLI.java b/cli/src/main/java/hudson/cli/CLI.java index 988e9f1635..3dc65e7dfb 100644 --- a/cli/src/main/java/hudson/cli/CLI.java +++ b/cli/src/main/java/hudson/cli/CLI.java @@ -318,7 +318,7 @@ public class CLI { throw new AssertionError(); } - @SuppressFBWarnings(value = {"PATH_TRAVERSAL_IN", "URLCONNECTION_SSRF_FD"}, justification = "User provided values for running the program.") + @SuppressFBWarnings(value = "PATH_TRAVERSAL_IN", justification = "User provided value for running the program.") private static String readAuthFromFile(String auth) throws IOException { Path path; try { @@ -329,7 +329,7 @@ public class CLI { return Files.readString(path, Charset.defaultCharset()); } - @SuppressFBWarnings(value = {"PATH_TRAVERSAL_IN", "URLCONNECTION_SSRF_FD"}, justification = "User provided values for running the program.") + @SuppressFBWarnings(value = "PATH_TRAVERSAL_IN", justification = "User provided value for running the program.") private static File getFileFromArguments(List args) { return new File(args.get(1)); } diff --git a/core/src/main/java/hudson/DescriptorExtensionList.java b/core/src/main/java/hudson/DescriptorExtensionList.java index c658c83fe1..18876582e5 100644 --- a/core/src/main/java/hudson/DescriptorExtensionList.java +++ b/core/src/main/java/hudson/DescriptorExtensionList.java @@ -26,6 +26,7 @@ package hudson; import edu.umd.cs.findbugs.annotations.CheckForNull; import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import hudson.model.Describable; import hudson.model.Descriptor; import hudson.model.Descriptor.FormException; @@ -274,6 +275,7 @@ public class DescriptorExtensionList, D extends Descrip /** * Exposed just for the test harness. Clear legacy instances. */ + @SuppressFBWarnings(value = "HSM_HIDING_METHOD", justification = "TODO needs triage") public static void clearLegacyInstances() { legacyDescriptors.clear(); } diff --git a/core/src/main/java/hudson/ExtensionList.java b/core/src/main/java/hudson/ExtensionList.java index c333b2de9f..fb2eda55ce 100644 --- a/core/src/main/java/hudson/ExtensionList.java +++ b/core/src/main/java/hudson/ExtensionList.java @@ -36,10 +36,12 @@ import java.util.AbstractList; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import java.util.IdentityHashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Set; import java.util.Vector; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArrayList; @@ -48,6 +50,8 @@ import java.util.logging.Logger; import jenkins.ExtensionComponentSet; import jenkins.model.Jenkins; import jenkins.util.io.OnMaster; +import org.kohsuke.accmod.Restricted; +import org.kohsuke.accmod.restrictions.NoExternalUse; /** * Retains the known extension instances for the given type 'T'. @@ -335,27 +339,44 @@ public class ExtensionList extends AbstractList implements OnMaster { /** * Used during {@link Jenkins#refreshExtensions()} to add new components into existing {@link ExtensionList}s. * Do not call from anywhere else. + * @return true if {@link #fireOnChangeListeners} should be called on {@code this} after all lists have been refreshed. */ - public void refresh(ExtensionComponentSet delta) { - boolean fireOnChangeListeners = false; + @Restricted(NoExternalUse.class) + public boolean refresh(ExtensionComponentSet delta) { synchronized (getLoadLock()) { if (extensions == null) - return; // not yet loaded. when we load it, we'll load everything visible by then, so no work needed + return false; // not yet loaded. when we load it, we'll load everything visible by then, so no work needed - Collection> found = load(delta); - if (!found.isEmpty()) { - List> l = new ArrayList<>(extensions); - l.addAll(found); - extensions = sort(l); - fireOnChangeListeners = true; + Collection> newComponents = load(delta); + if (!newComponents.isEmpty()) { + // We check to ensure that we do not insert duplicate instances of already-loaded extensions into the list. + // This can happen when dynamically loading a plugin with an extension A that itself loads another + // extension B from the same plugin in some contexts, such as in A's constructor or via a method in A called + // by an ExtensionListListener. In those cases, ExtensionList.refresh may be called on a list that already + // includes the new extensions. Note that ExtensionComponent objects are always unique, even when + // ExtensionComponent.getInstance is identical, so we have to track the components and instances separately + // to handle ordinal sorting and check for dupes. + List> components = new ArrayList<>(extensions); + Set instances = Collections.newSetFromMap(new IdentityHashMap<>()); + for (ExtensionComponent component : components) { + instances.add(component.getInstance()); + } + boolean fireListeners = false; + for (ExtensionComponent newComponent : newComponents) { + if (instances.add(newComponent.getInstance())) { + fireListeners = true; + components.add(newComponent); + } + } + extensions = sort(new ArrayList<>(components)); + return fireListeners; } } - if (fireOnChangeListeners) { - fireOnChangeListeners(); - } + return false; } - private void fireOnChangeListeners() { + @Restricted(NoExternalUse.class) + public void fireOnChangeListeners() { for (ExtensionListListener listener : listeners) { try { listener.onChange(); diff --git a/core/src/main/java/hudson/PluginManager.java b/core/src/main/java/hudson/PluginManager.java index b90b2eb9f6..7161293300 100644 --- a/core/src/main/java/hudson/PluginManager.java +++ b/core/src/main/java/hudson/PluginManager.java @@ -100,6 +100,7 @@ import java.util.Enumeration; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; +import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Locale; @@ -114,7 +115,6 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.Future; -import java.util.function.Function; import java.util.function.Supplier; import java.util.jar.JarEntry; import java.util.jar.JarFile; @@ -2661,7 +2661,10 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas public Map getDeprecatedPlugins() { return Jenkins.get().getPluginManager().getPlugins().stream() .filter(PluginWrapper::isDeprecated) - .collect(Collectors.toMap(Function.identity(), it -> it.getDeprecations().get(0).url)); + .sorted(Comparator.comparing(PluginWrapper::getDisplayName)) // Sort by plugin name + .collect(LinkedHashMap::new, + (map, plugin) -> map.put(plugin, plugin.getDeprecations().get(0).url), + Map::putAll); } } diff --git a/core/src/main/java/hudson/Util.java b/core/src/main/java/hudson/Util.java index 8e9f1002f3..4783535bd8 100644 --- a/core/src/main/java/hudson/Util.java +++ b/core/src/main/java/hudson/Util.java @@ -1651,7 +1651,7 @@ public class Util { * @since 2.3 / 1.651.2 */ public static boolean isSafeToRedirectTo(@NonNull String uri) { - return !isAbsoluteUri(uri) && !uri.startsWith("//"); + return !isAbsoluteUri(uri) && !uri.startsWith("\\") && !uri.replace('\\', '/').startsWith("//"); } /** diff --git a/core/src/main/java/hudson/cli/GetNodeCommand.java b/core/src/main/java/hudson/cli/GetNodeCommand.java index 62dd218260..6b62729d2f 100644 --- a/core/src/main/java/hudson/cli/GetNodeCommand.java +++ b/core/src/main/java/hudson/cli/GetNodeCommand.java @@ -27,8 +27,11 @@ package hudson.cli; import hudson.Extension; import hudson.model.Computer; import hudson.model.Node; +import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.nio.charset.StandardCharsets; import jenkins.model.Jenkins; +import jenkins.security.ExtendedReadRedaction; import org.kohsuke.args4j.Argument; /** @@ -52,7 +55,16 @@ public class GetNodeCommand extends CLICommand { node.checkPermission(Computer.EXTENDED_READ); - Jenkins.XSTREAM2.toXMLUTF8(node, stdout); + if (node.hasPermission(Computer.CONFIGURE)) { + Jenkins.XSTREAM2.toXMLUTF8(node, stdout); + } else { + var baos = new ByteArrayOutputStream(); + Jenkins.XSTREAM2.toXMLUTF8(node, baos); + String xml = baos.toString(StandardCharsets.UTF_8); + + xml = ExtendedReadRedaction.applyAll(xml); + org.apache.commons.io.IOUtils.write(xml, stdout, StandardCharsets.UTF_8); + } return 0; } diff --git a/core/src/main/java/hudson/cli/GetViewCommand.java b/core/src/main/java/hudson/cli/GetViewCommand.java index e94bb7947a..020b55f719 100644 --- a/core/src/main/java/hudson/cli/GetViewCommand.java +++ b/core/src/main/java/hudson/cli/GetViewCommand.java @@ -26,6 +26,9 @@ package hudson.cli; import hudson.Extension; import hudson.model.View; +import java.io.ByteArrayOutputStream; +import java.nio.charset.StandardCharsets; +import jenkins.security.ExtendedReadRedaction; import org.kohsuke.args4j.Argument; /** @@ -48,7 +51,17 @@ public class GetViewCommand extends CLICommand { protected int run() throws Exception { view.checkPermission(View.READ); - view.writeXml(stdout); + + if (view.hasPermission(View.CONFIGURE)) { + view.writeXml(stdout); + } else { + var baos = new ByteArrayOutputStream(); + view.writeXml(baos); + String xml = baos.toString(StandardCharsets.UTF_8); + + xml = ExtendedReadRedaction.applyAll(xml); + org.apache.commons.io.IOUtils.write(xml, stdout, StandardCharsets.UTF_8); + } return 0; } diff --git a/core/src/main/java/hudson/console/ModelHyperlinkNote.java b/core/src/main/java/hudson/console/ModelHyperlinkNote.java index abf6e371cf..3e8d045366 100644 --- a/core/src/main/java/hudson/console/ModelHyperlinkNote.java +++ b/core/src/main/java/hudson/console/ModelHyperlinkNote.java @@ -1,6 +1,7 @@ package hudson.console; import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import hudson.Extension; import hudson.model.Computer; import hudson.model.Item; @@ -66,6 +67,7 @@ public class ModelHyperlinkNote extends HyperlinkNote { return encodeTo("/" + label.getUrl(), label.getName()); } + @SuppressFBWarnings(value = "HSM_HIDING_METHOD", justification = "TODO needs triage") public static String encodeTo(String url, String text) { return HyperlinkNote.encodeTo(url, text, ModelHyperlinkNote::new); } diff --git a/core/src/main/java/hudson/model/AutoCompletionCandidates.java b/core/src/main/java/hudson/model/AutoCompletionCandidates.java index 366f626e31..0e487ef2f4 100644 --- a/core/src/main/java/hudson/model/AutoCompletionCandidates.java +++ b/core/src/main/java/hudson/model/AutoCompletionCandidates.java @@ -25,7 +25,6 @@ package hudson.model; import edu.umd.cs.findbugs.annotations.CheckForNull; -import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import hudson.search.Search; import hudson.search.UserSearchProperty; import jakarta.servlet.ServletException; @@ -109,7 +108,6 @@ public class AutoCompletionCandidates implements HttpResponse { * The nearby contextual {@link ItemGroup} to resolve relative job names from. * @since 1.553 */ - @SuppressFBWarnings(value = "SBSC_USE_STRINGBUFFER_CONCATENATION", justification = "no big deal") public static AutoCompletionCandidates ofJobNames(final Class type, final String value, ItemGroup container) { final AutoCompletionCandidates candidates = new AutoCompletionCandidates(); class Visitor extends ItemVisitor { diff --git a/core/src/main/java/hudson/model/Computer.java b/core/src/main/java/hudson/model/Computer.java index 0c525dfabe..e8d5428eb8 100644 --- a/core/src/main/java/hudson/model/Computer.java +++ b/core/src/main/java/hudson/model/Computer.java @@ -74,6 +74,7 @@ import hudson.util.RemotingDiagnostics; import hudson.util.RemotingDiagnostics.HeapDump; import hudson.util.RunList; import jakarta.servlet.ServletException; +import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; @@ -83,6 +84,7 @@ import java.net.Inet4Address; import java.net.InetAddress; import java.net.NetworkInterface; import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.InvalidPathException; import java.nio.file.StandardCopyOption; @@ -110,6 +112,8 @@ import jenkins.model.DisplayExecutor; import jenkins.model.IComputer; import jenkins.model.IDisplayExecutor; import jenkins.model.Jenkins; +import jenkins.search.SearchGroup; +import jenkins.security.ExtendedReadRedaction; import jenkins.security.ImpersonatingExecutorService; import jenkins.security.MasterToSlaveCallable; import jenkins.security.stapler.StaplerDispatchable; @@ -1109,6 +1113,11 @@ public /*transient*/ abstract class Computer extends Actionable implements Acces return getUrl(); } + @Override + public SearchGroup getSearchGroup() { + return SearchGroup.get(SearchGroup.ComputerSearchGroup.class); + } + /** * {@link RetentionStrategy} associated with this computer. * @@ -1526,7 +1535,16 @@ public /*transient*/ abstract class Computer extends Actionable implements Acces if (node == null) { throw HttpResponses.notFound(); } - Jenkins.XSTREAM2.toXMLUTF8(node, rsp.getOutputStream()); + if (hasPermission(CONFIGURE)) { + Jenkins.XSTREAM2.toXMLUTF8(node, rsp.getOutputStream()); + } else { + var baos = new ByteArrayOutputStream(); + Jenkins.XSTREAM2.toXMLUTF8(node, baos); + String xml = baos.toString(StandardCharsets.UTF_8); + + xml = ExtendedReadRedaction.applyAll(xml); + org.apache.commons.io.IOUtils.write(xml, rsp.getOutputStream(), StandardCharsets.UTF_8); + } return; } if (req.getMethod().equals("POST")) { diff --git a/core/src/main/java/hudson/model/FileParameterValue.java b/core/src/main/java/hudson/model/FileParameterValue.java index abeb1f4181..85b4635123 100644 --- a/core/src/main/java/hudson/model/FileParameterValue.java +++ b/core/src/main/java/hudson/model/FileParameterValue.java @@ -164,7 +164,7 @@ public class FileParameterValue extends ParameterValue { @Override public BuildWrapper createBuildWrapper(AbstractBuild build) { return new BuildWrapper() { - @SuppressFBWarnings(value = {"FILE_UPLOAD_FILENAME", "NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE"}, justification = "TODO needs triage") + @SuppressFBWarnings(value = "NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE", justification = "TODO needs triage") @Override public Environment setUp(AbstractBuild build, Launcher launcher, BuildListener listener) throws IOException, InterruptedException { if (location != null && !location.isEmpty() && file.getName() != null && !file.getName().isEmpty()) { diff --git a/core/src/main/java/hudson/model/Item.java b/core/src/main/java/hudson/model/Item.java index 838f4e6f75..dd2fd993cb 100644 --- a/core/src/main/java/hudson/model/Item.java +++ b/core/src/main/java/hudson/model/Item.java @@ -39,6 +39,7 @@ import hudson.util.Secret; import java.io.IOException; import java.util.Collection; import jenkins.model.Jenkins; +import jenkins.search.SearchGroup; import jenkins.util.SystemProperties; import jenkins.util.io.OnMaster; import org.kohsuke.stapler.StaplerRequest2; @@ -249,6 +250,11 @@ public interface Item extends PersistenceRoot, SearchableModelObject, AccessCont */ void delete() throws IOException, InterruptedException; + @Override + default SearchGroup getSearchGroup() { + return SearchGroup.get(SearchGroup.ItemSearchGroup.class); + } + PermissionGroup PERMISSIONS = new PermissionGroup(Item.class, Messages._Item_Permissions_Title()); Permission CREATE = new Permission( diff --git a/core/src/main/java/hudson/model/ModifiableItemGroup.java b/core/src/main/java/hudson/model/ModifiableItemGroup.java index d0bcda3118..a9d16491fb 100644 --- a/core/src/main/java/hudson/model/ModifiableItemGroup.java +++ b/core/src/main/java/hudson/model/ModifiableItemGroup.java @@ -24,11 +24,11 @@ package hudson.model; +import hudson.Util; import io.jenkins.servlet.ServletExceptionWrapper; import jakarta.servlet.ServletException; import java.io.IOException; import jenkins.security.stapler.StaplerNotDispatchable; -import org.kohsuke.stapler.ReflectionUtils; import org.kohsuke.stapler.StaplerRequest; import org.kohsuke.stapler.StaplerRequest2; import org.kohsuke.stapler.StaplerResponse; @@ -53,7 +53,7 @@ public interface ModifiableItemGroup extends ItemGroup { */ @RequirePOST default T doCreateItem(StaplerRequest2 req, StaplerResponse2 rsp) throws IOException, ServletException { - if (ReflectionUtils.isOverridden( + if (Util.isOverridden( ModifiableItemGroup.class, getClass(), "doCreateItem", @@ -76,7 +76,7 @@ public interface ModifiableItemGroup extends ItemGroup { @Deprecated @StaplerNotDispatchable default T doCreateItem(StaplerRequest req, StaplerResponse rsp) throws IOException, javax.servlet.ServletException { - if (ReflectionUtils.isOverridden( + if (Util.isOverridden( ModifiableItemGroup.class, getClass(), "doCreateItem", diff --git a/core/src/main/java/hudson/model/UpdateSite.java b/core/src/main/java/hudson/model/UpdateSite.java index b0c30e861e..2dd9f90610 100644 --- a/core/src/main/java/hudson/model/UpdateSite.java +++ b/core/src/main/java/hudson/model/UpdateSite.java @@ -1088,7 +1088,7 @@ public class UpdateSite { * {@code false} if it does; and {@code null} when the affected component isn't being offered, or it's a warning * for something other than core or a plugin. */ - @SuppressFBWarnings(value = "NP_BOOLEAN_RETURN_NULL") + @SuppressFBWarnings(value = "NP_BOOLEAN_RETURN_NULL", justification = "TODO needs triage") public Boolean isFixable() { final Data data = UpdateSite.this.data; if (data == null) { diff --git a/core/src/main/java/hudson/model/User.java b/core/src/main/java/hudson/model/User.java index d40664ca2c..e67e2c7200 100644 --- a/core/src/main/java/hudson/model/User.java +++ b/core/src/main/java/hudson/model/User.java @@ -72,6 +72,7 @@ import jenkins.model.Jenkins; import jenkins.model.Loadable; import jenkins.model.ModelObjectWithContextMenu; import jenkins.scm.RunWithSCM; +import jenkins.search.SearchGroup; import jenkins.security.ImpersonatingUserDetailsService2; import jenkins.security.LastGrantedAuthoritiesProperty; import jenkins.security.UserDetailsCache; @@ -284,6 +285,11 @@ public class User extends AbstractModelObject implements AccessControlled, Descr return UserAvatarResolver.resolve(this, "48x48"); } + @Override + public SearchGroup getSearchGroup() { + return SearchGroup.get(SearchGroup.UserSearchGroup.class); + } + /** * The URL of the user page. */ diff --git a/core/src/main/java/hudson/model/View.java b/core/src/main/java/hudson/model/View.java index 722254572c..d1d607a0a1 100644 --- a/core/src/main/java/hudson/model/View.java +++ b/core/src/main/java/hudson/model/View.java @@ -62,6 +62,7 @@ import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletResponse; import java.io.BufferedInputStream; import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -94,6 +95,8 @@ import jenkins.model.ModelObjectWithContextMenu; import jenkins.model.item_category.Categories; import jenkins.model.item_category.Category; import jenkins.model.item_category.ItemCategory; +import jenkins.search.SearchGroup; +import jenkins.security.ExtendedReadRedaction; import jenkins.security.stapler.StaplerNotDispatchable; import jenkins.util.xml.XMLUtils; import jenkins.widgets.HasWidgets; @@ -565,6 +568,11 @@ public abstract class View extends AbstractModelObject implements AccessControll return "symbol-jobs"; } + @Override + public SearchGroup getSearchGroup() { + return SearchGroup.get(SearchGroup.ViewSearchGroup.class); + } + /** * Returns the transient {@link Action}s associated with the top page. * @@ -983,7 +991,16 @@ public abstract class View extends AbstractModelObject implements AccessControll @Override public void generateResponse(StaplerRequest2 req, StaplerResponse2 rsp, Object node) throws IOException, ServletException { rsp.setContentType("application/xml"); - View.this.writeXml(rsp.getOutputStream()); + if (hasPermission(CONFIGURE)) { + View.this.writeXml(rsp.getOutputStream()); + } else { + var baos = new ByteArrayOutputStream(); + View.this.writeXml(baos); + String xml = baos.toString(StandardCharsets.UTF_8); + + xml = ExtendedReadRedaction.applyAll(xml); + org.apache.commons.io.IOUtils.write(xml, rsp.getOutputStream(), StandardCharsets.UTF_8); + } } }; } diff --git a/core/src/main/java/hudson/search/Search.java b/core/src/main/java/hudson/search/Search.java index 97d15b65c2..4959b52995 100644 --- a/core/src/main/java/hudson/search/Search.java +++ b/core/src/main/java/hudson/search/Search.java @@ -29,6 +29,8 @@ import static jakarta.servlet.http.HttpServletResponse.SC_NOT_FOUND; import edu.umd.cs.findbugs.annotations.CheckForNull; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; +import hudson.ExtensionComponent; +import hudson.ExtensionList; import hudson.Util; import hudson.util.EditDistance; import io.jenkins.servlet.ServletExceptionWrapper; @@ -37,12 +39,16 @@ import java.io.IOException; import java.util.AbstractList; import java.util.ArrayList; import java.util.Collections; +import java.util.Comparator; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; +import java.util.stream.Collectors; import jenkins.model.Jenkins; +import jenkins.search.SearchGroup; import jenkins.security.stapler.StaplerNotDispatchable; import jenkins.util.MemoryReductionUtil; import jenkins.util.SystemProperties; @@ -171,11 +177,26 @@ public class Search implements StaplerProxy { if (iconName.startsWith("symbol")) { r.suggestions.add(new Item(curItem.getPath(), curItem.getUrl(), - Symbol.get(new SymbolRequest.Builder().withRaw(iconName).build()))); + Symbol.get(new SymbolRequest.Builder().withRaw(iconName).build()), "symbol", curItem.item.getSearchGroup().getDisplayName())); } else { - r.suggestions.add(new Item(curItem.getPath(), curItem.getUrl(), iconName, "image")); + r.suggestions.add(new Item(curItem.getPath(), curItem.getUrl(), iconName, "image", curItem.item.getSearchGroup().getDisplayName())); } } + + // Sort results by group + ExtensionList groupsExtensionList = ExtensionList.lookup(SearchGroup.class); + List> components = groupsExtensionList.getComponents(); + Map searchGroupOrdinal = components.stream() + .collect(Collectors.toMap( + (k) -> k.getInstance().getDisplayName(), + ExtensionComponent::ordinal + )); + r.suggestions.sort( + Comparator.comparingDouble((Item item) -> searchGroupOrdinal.getOrDefault(item.getGroup(), Double.MAX_VALUE)) + .reversed() + .thenComparing(item -> item.name) + ); + rsp.serveExposedBean(req, r, new ExportConfig()); } @@ -270,7 +291,6 @@ public class Search implements StaplerProxy { public static class Item { @Exported - @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD", justification = "read by Stapler") public String name; private final String url; @@ -279,22 +299,19 @@ public class Search implements StaplerProxy { private final String icon; + private final String group; + public Item(String name) { - this(name, null, null); + this(name, null, null, "symbol", null); } - public Item(String name, String url, String icon) { + public Item(String name, String url, String icon, String type, String group) { this.name = name; this.url = url; this.icon = icon; - this.type = "symbol"; - } - - public Item(String name, String url, String icon, String type) { this.name = name; - this.url = url; - this.icon = icon; this.type = type; + this.group = group; } @Exported @@ -311,6 +328,11 @@ public class Search implements StaplerProxy { public String getType() { return type; } + + @Exported + public String getGroup() { + return group; + } } private enum Mode { diff --git a/core/src/main/java/hudson/search/SearchItem.java b/core/src/main/java/hudson/search/SearchItem.java index e64efb0257..5453af9f02 100644 --- a/core/src/main/java/hudson/search/SearchItem.java +++ b/core/src/main/java/hudson/search/SearchItem.java @@ -25,6 +25,7 @@ package hudson.search; import hudson.model.Build; +import jenkins.search.SearchGroup; import org.jenkins.ui.icon.IconSpec; /** @@ -63,6 +64,10 @@ public interface SearchItem { return "symbol-search"; } + default SearchGroup getSearchGroup() { + return SearchGroup.get(SearchGroup.UnclassifiedSearchGroup.class); + } + /** * Returns the {@link SearchIndex} to further search sub items inside this item. * diff --git a/core/src/main/java/hudson/security/AuthenticationProcessingFilter2.java b/core/src/main/java/hudson/security/AuthenticationProcessingFilter2.java index 0c9559bf14..a8913772dc 100644 --- a/core/src/main/java/hudson/security/AuthenticationProcessingFilter2.java +++ b/core/src/main/java/hudson/security/AuthenticationProcessingFilter2.java @@ -62,7 +62,6 @@ public final class AuthenticationProcessingFilter2 extends UsernamePasswordAuthe setPasswordParameter("j_password"); } - @SuppressFBWarnings(value = "RV_RETURN_VALUE_IGNORED_NO_SIDE_EFFECT", justification = "request.getSession(true) does in fact have a side effect") @Override protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException { if (SystemProperties.getInteger(SecurityRealm.class.getName() + ".sessionFixationProtectionMode", 1) == 2) { diff --git a/core/src/main/java/hudson/security/BasicAuthenticationFilter.java b/core/src/main/java/hudson/security/BasicAuthenticationFilter.java index 8586fb0317..b9c1f8e9b2 100644 --- a/core/src/main/java/hudson/security/BasicAuthenticationFilter.java +++ b/core/src/main/java/hudson/security/BasicAuthenticationFilter.java @@ -24,7 +24,6 @@ package hudson.security; -import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import hudson.model.User; import hudson.util.Scrambler; import jakarta.servlet.FilterChain; @@ -173,7 +172,6 @@ public class BasicAuthenticationFilter implements CompatibleFilter { d.include(req, rsp); } - @SuppressFBWarnings(value = "UNVALIDATED_REDIRECT", justification = "Redirect is validated as processed.") private void prepareRedirect(HttpServletResponse rsp, String path) { rsp.setStatus(HttpServletResponse.SC_MOVED_TEMPORARILY); rsp.setHeader("Location", path); diff --git a/core/src/main/java/hudson/security/HudsonPrivateSecurityRealm.java b/core/src/main/java/hudson/security/HudsonPrivateSecurityRealm.java index f55576dec4..24014707fa 100644 --- a/core/src/main/java/hudson/security/HudsonPrivateSecurityRealm.java +++ b/core/src/main/java/hudson/security/HudsonPrivateSecurityRealm.java @@ -940,7 +940,6 @@ public class HudsonPrivateSecurityRealm extends AbstractPasswordBasedSecurityRea static class JBCryptEncoder implements PasswordHashEncoder { // in jBCrypt the maximum is 30, which takes ~22h with laptop late-2017 // and for 18, it's "only" 20s - @SuppressFBWarnings(value = "MS_SHOULD_BE_FINAL", justification = "Accessible via System Groovy Scripts") @Restricted(NoExternalUse.class) private static int MAXIMUM_BCRYPT_LOG_ROUND = SystemProperties.getInteger(HudsonPrivateSecurityRealm.class.getName() + ".maximumBCryptLogRound", 18); @@ -1135,7 +1134,7 @@ public class HudsonPrivateSecurityRealm extends AbstractPasswordBasedSecurityRea */ private static final String ENCODED_INVALID_USER_PASSWORD = PASSWORD_ENCODER.encode(generatePassword()); - @SuppressFBWarnings(value = {"DMI_RANDOM_USED_ONLY_ONCE", "PREDICTABLE_RANDOM"}, justification = "https://github.com/spotbugs/spotbugs/issues/1539 and doesn't need to be secure, we're just not hardcoding a 'wrong' password") + @SuppressFBWarnings(value = "PREDICTABLE_RANDOM", justification = "Doesn't need to be secure, we're just not hardcoding a 'wrong' password") private static String generatePassword() { String password = new Random().ints(20, 33, 127).mapToObj(i -> (char) i) .collect(StringBuilder::new, StringBuilder::appendCodePoint, StringBuilder::append).toString(); diff --git a/core/src/main/java/hudson/slaves/SlaveComputer.java b/core/src/main/java/hudson/slaves/SlaveComputer.java index 4165f53a72..901030bc0a 100644 --- a/core/src/main/java/hudson/slaves/SlaveComputer.java +++ b/core/src/main/java/hudson/slaves/SlaveComputer.java @@ -1116,10 +1116,14 @@ public class SlaveComputer extends Computer { this.ringBufferSize = ringBufferSize; } - @Override @SuppressFBWarnings(value = "ST_WRITE_TO_STATIC_FROM_INSTANCE_METHOD", justification = "field is static for the reason explained in the Javadoc for LogHolder") - public Void call() { + private void setLogHandler() { SLAVE_LOG_HANDLER = new RingBufferLogHandler(ringBufferSize); + } + + @Override + public Void call() { + setLogHandler(); // avoid double installation of the handler. Inbound agents can reconnect to the controller multiple times // and each connection gets a different RemoteClassLoader, so we need to evict them by class name, diff --git a/core/src/main/java/hudson/util/RemotingDiagnostics.java b/core/src/main/java/hudson/util/RemotingDiagnostics.java index c18e056e42..ad59767907 100644 --- a/core/src/main/java/hudson/util/RemotingDiagnostics.java +++ b/core/src/main/java/hudson/util/RemotingDiagnostics.java @@ -142,7 +142,6 @@ public final class RemotingDiagnostics { } @Override - @SuppressFBWarnings(value = "GROOVY_SHELL", justification = "script console is a feature, not a bug") public String call() throws RuntimeException { // if we run locally, cl!=null. Otherwise the delegating classloader will be available as context classloader. if (cl == null) cl = Thread.currentThread().getContextClassLoader(); @@ -158,7 +157,7 @@ public final class RemotingDiagnostics { PrintWriter pw = new PrintWriter(out); shell.setVariable("out", pw); try { - Object output = shell.evaluate(script); + Object output = evaluateScript(shell); if (output != null) pw.println("Result: " + output); } catch (Throwable t) { @@ -167,6 +166,11 @@ public final class RemotingDiagnostics { return out.toString(); } + @SuppressFBWarnings(value = "GROOVY_SHELL", justification = "script console is a feature, not a bug") + private Object evaluateScript(GroovyShell shell) { + return shell.evaluate(script); + } + private static final long serialVersionUID = 1L; } diff --git a/core/src/main/java/hudson/util/jna/Advapi32.java b/core/src/main/java/hudson/util/jna/Advapi32.java index 22433e0e91..a020861824 100644 --- a/core/src/main/java/hudson/util/jna/Advapi32.java +++ b/core/src/main/java/hudson/util/jna/Advapi32.java @@ -32,7 +32,6 @@ import java.util.List; * * @author TB */ -@SuppressFBWarnings(value = "MS_OOI_PKGPROTECT", justification = "for backward compatibility") @SuppressWarnings("UnusedReturnValue") public interface Advapi32 extends StdCallLibrary { Advapi32 INSTANCE = (Advapi32) Native.load("Advapi32", Advapi32.class, Options.UNICODE_OPTIONS); diff --git a/core/src/main/java/hudson/util/jna/SHELLEXECUTEINFO.java b/core/src/main/java/hudson/util/jna/SHELLEXECUTEINFO.java index bca0345730..5180fe0adf 100644 --- a/core/src/main/java/hudson/util/jna/SHELLEXECUTEINFO.java +++ b/core/src/main/java/hudson/util/jna/SHELLEXECUTEINFO.java @@ -89,8 +89,6 @@ public class SHELLEXECUTEINFO extends Structure { "hProcess"); } - @SuppressFBWarnings(value = {"UUF_UNUSED_PUBLIC_OR_PROTECTED_FIELD", "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD"}, - justification = "JNA Data Structure") public static class DUMMYUNIONNAME_union extends Union { public Pointer hIcon; public Pointer hMonitor; diff --git a/core/src/main/java/jenkins/fingerprints/FileFingerprintStorage.java b/core/src/main/java/jenkins/fingerprints/FileFingerprintStorage.java index 409788ba57..ae8cfda28f 100644 --- a/core/src/main/java/jenkins/fingerprints/FileFingerprintStorage.java +++ b/core/src/main/java/jenkins/fingerprints/FileFingerprintStorage.java @@ -334,7 +334,6 @@ public class FileFingerprintStorage extends FingerprintStorage { /** * Deletes a directory if it's empty. */ - @SuppressFBWarnings(value = "RCN_REDUNDANT_NULLCHECK_WOULD_HAVE_BEEN_A_NPE", justification = "https://github.com/spotbugs/spotbugs/issues/756") private void deleteIfEmpty(File dir) { try { if (Files.isDirectory(dir.toPath())) { diff --git a/core/src/main/java/jenkins/formelementpath/FormElementPathPageDecorator.java b/core/src/main/java/jenkins/formelementpath/FormElementPathPageDecorator.java index 8a1df70781..0579b083ac 100644 --- a/core/src/main/java/jenkins/formelementpath/FormElementPathPageDecorator.java +++ b/core/src/main/java/jenkins/formelementpath/FormElementPathPageDecorator.java @@ -1,6 +1,5 @@ package jenkins.formelementpath; -import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import hudson.Extension; import hudson.model.PageDecorator; import jenkins.util.SystemProperties; @@ -8,7 +7,6 @@ import jenkins.util.SystemProperties; @Extension public class FormElementPathPageDecorator extends PageDecorator { - @SuppressFBWarnings(value = "MS_SHOULD_BE_FINAL", justification = "for script console") private static /*almost final */ boolean ENABLED = SystemProperties.getBoolean(FormElementPathPageDecorator.class.getName() + ".enabled"); diff --git a/core/src/main/java/jenkins/install/SetupWizard.java b/core/src/main/java/jenkins/install/SetupWizard.java index 2707807906..a0ee143ea3 100644 --- a/core/src/main/java/jenkins/install/SetupWizard.java +++ b/core/src/main/java/jenkins/install/SetupWizard.java @@ -128,7 +128,6 @@ public class SetupWizard extends PageDecorator { * @since 2.260 (with NoExternalUse) */ @Restricted(NoExternalUse.class) - @SuppressFBWarnings(value = "MS_SHOULD_BE_FINAL", justification = "Accessible via System Groovy Scripts") private static /* not final */ String ADMIN_INITIAL_API_TOKEN = SystemProperties.getString(ADMIN_INITIAL_API_TOKEN_PROPERTY_NAME); @NonNull @@ -212,7 +211,6 @@ public class SetupWizard extends PageDecorator { } } - @SuppressFBWarnings(value = "UNSAFE_HASH_EQUALS", justification = "only checked against true") private void createInitialApiToken(User user) throws IOException, InterruptedException { ApiTokenProperty apiTokenProperty = user.getProperty(ApiTokenProperty.class); @@ -763,7 +761,6 @@ public class SetupWizard extends PageDecorator { } @Override - @SuppressFBWarnings(value = "UNVALIDATED_REDIRECT", justification = "TODO needs triage") public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { // Force root requests to the setup wizard if (request instanceof HttpServletRequest && !Jenkins.get().getInstallState().isSetupComplete()) { diff --git a/core/src/main/java/jenkins/model/Jenkins.java b/core/src/main/java/jenkins/model/Jenkins.java index 73ba937696..c7e134f389 100644 --- a/core/src/main/java/jenkins/model/Jenkins.java +++ b/core/src/main/java/jenkins/model/Jenkins.java @@ -2867,11 +2867,21 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve delta = ExtensionComponentSet.union(delta, ef.refresh().filtered()); } + List listsToFireOnChangeListeners = new ArrayList<>(); for (ExtensionList el : extensionLists.values()) { - el.refresh(delta); + if (el.refresh(delta)) { + listsToFireOnChangeListeners.add(el); + } } for (ExtensionList el : descriptorLists.values()) { - el.refresh(delta); + if (el.refresh(delta)) { + listsToFireOnChangeListeners.add(el); + } + } + // Refresh all extension lists before firing any listeners in case a listener would cause any new extension + // lists to be forcibly loaded, which may lead to duplicate entries for the same extension object in a list. + for (var el : listsToFireOnChangeListeners) { + el.fireOnChangeListeners(); } // TODO: we need some generalization here so that extension points can be notified when a refresh happens? @@ -4165,6 +4175,7 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve return new HttpRedirect("."); } + @POST public HttpResponse doToggleCollapse() throws ServletException, IOException { final StaplerRequest2 request = Stapler.getCurrentRequest2(); final String paneId = request.getParameter("paneId"); @@ -4467,15 +4478,6 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve rsp.getWriter().println("GCed"); } - /** - * End point that intentionally throws an exception to test the error behaviour. - * @since 1.467 - */ - @StaplerDispatchable - public void doException() { - throw new RuntimeException(); - } - @Override public ContextMenu doContextMenu(StaplerRequest2 request, StaplerResponse2 response) throws IOException, JellyException { ContextMenu menu = new ContextMenu().from(this, request, response); diff --git a/core/src/main/java/jenkins/search/SearchGroup.java b/core/src/main/java/jenkins/search/SearchGroup.java new file mode 100644 index 0000000000..62ba316601 --- /dev/null +++ b/core/src/main/java/jenkins/search/SearchGroup.java @@ -0,0 +1,73 @@ +package jenkins.search; + +import static jenkins.search.Messages.SearchGroup_ComputerSearchGroup_DisplayName; +import static jenkins.search.Messages.SearchGroup_ItemSearchGroup_DisplayName; +import static jenkins.search.Messages.SearchGroup_UnclassifiedSearchGroup_DisplayName; +import static jenkins.search.Messages.SearchGroup_UserSearchGroup_DisplayName; +import static jenkins.search.Messages.SearchGroup_ViewSearchGroup_DisplayName; + +import edu.umd.cs.findbugs.annotations.NonNull; +import hudson.Extension; +import hudson.ExtensionList; +import hudson.ExtensionPoint; +import hudson.model.ModelObject; + +public interface SearchGroup extends ExtensionPoint, ModelObject { + + static ExtensionList all() { + return ExtensionList.lookup(SearchGroup.class); + } + + static @NonNull T get(Class type) { + T category = all().get(type); + if (category == null) { + throw new AssertionError("Group not found. It seems the " + type + " is not annotated with @Extension and so not registered"); + } + return category; + } + + @Extension(ordinal = -1) + class UnclassifiedSearchGroup implements SearchGroup { + + @Override + public String getDisplayName() { + return SearchGroup_UnclassifiedSearchGroup_DisplayName(); + } + } + + @Extension(ordinal = 999) + class ItemSearchGroup implements SearchGroup { + + @Override + public String getDisplayName() { + return SearchGroup_ItemSearchGroup_DisplayName(); + } + } + + @Extension + class ComputerSearchGroup implements SearchGroup { + + @Override + public String getDisplayName() { + return SearchGroup_ComputerSearchGroup_DisplayName(); + } + } + + @Extension + class ViewSearchGroup implements SearchGroup { + + @Override + public String getDisplayName() { + return SearchGroup_ViewSearchGroup_DisplayName(); + } + } + + @Extension + class UserSearchGroup implements SearchGroup { + + @Override + public String getDisplayName() { + return SearchGroup_UserSearchGroup_DisplayName(); + } + } +} diff --git a/core/src/main/java/jenkins/security/ApiTokenProperty.java b/core/src/main/java/jenkins/security/ApiTokenProperty.java index 804b8edbc5..95cfd4c994 100644 --- a/core/src/main/java/jenkins/security/ApiTokenProperty.java +++ b/core/src/main/java/jenkins/security/ApiTokenProperty.java @@ -26,7 +26,6 @@ package jenkins.security; import edu.umd.cs.findbugs.annotations.CheckForNull; import edu.umd.cs.findbugs.annotations.NonNull; -import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import hudson.Extension; import hudson.Util; import hudson.model.Descriptor.FormException; @@ -89,7 +88,6 @@ public class ApiTokenProperty extends UserProperty { * * @since 1.638 */ - @SuppressFBWarnings(value = "MS_SHOULD_BE_FINAL", justification = "Accessible via System Groovy Scripts") private static /* not final */ boolean SHOW_LEGACY_TOKEN_TO_ADMINS = SystemProperties.getBoolean(ApiTokenProperty.class.getName() + ".showTokenToAdmins"); @@ -102,7 +100,6 @@ public class ApiTokenProperty extends UserProperty { * * @since 2.129 */ - @SuppressFBWarnings(value = "MS_SHOULD_BE_FINAL", justification = "Accessible via System Groovy Scripts") private static /* not final */ boolean ADMIN_CAN_GENERATE_NEW_TOKENS = SystemProperties.getBoolean(ApiTokenProperty.class.getName() + ".adminCanGenerateNewTokens"); @@ -175,7 +172,6 @@ public class ApiTokenProperty extends UserProperty { @NonNull @Restricted(NoExternalUse.class) - @SuppressFBWarnings(value = "UNSAFE_HASH_EQUALS", justification = "Used to prevent use of pre-2013 API tokens, then returning the API token value") /*package*/ String getApiTokenInsecure() { if (apiToken == null) { return Messages.ApiTokenProperty_NoLegacyToken(); diff --git a/core/src/main/java/jenkins/security/apitoken/ApiTokenStore.java b/core/src/main/java/jenkins/security/apitoken/ApiTokenStore.java index 917be3b0fa..67945ac673 100644 --- a/core/src/main/java/jenkins/security/apitoken/ApiTokenStore.java +++ b/core/src/main/java/jenkins/security/apitoken/ApiTokenStore.java @@ -304,7 +304,6 @@ public class ApiTokenStore { * @param tokenUuid The identifier of the token, could be retrieved directly from the {@link HashedToken#getUuid()} * @return the revoked token corresponding to the given {@code tokenUuid} if one was found, otherwise {@code null} */ - @SuppressFBWarnings(value = "UNSAFE_HASH_EQUALS", justification = "Only used during revocation.") public synchronized @CheckForNull HashedToken revokeToken(@NonNull String tokenUuid) { for (Iterator iterator = tokenList.iterator(); iterator.hasNext(); ) { HashedToken token = iterator.next(); diff --git a/core/src/main/java/jenkins/util/JSONSignatureValidator.java b/core/src/main/java/jenkins/util/JSONSignatureValidator.java index d3f416a91f..39ba704071 100644 --- a/core/src/main/java/jenkins/util/JSONSignatureValidator.java +++ b/core/src/main/java/jenkins/util/JSONSignatureValidator.java @@ -245,7 +245,6 @@ public class JSONSignatureValidator { } - @SuppressFBWarnings(value = {"NP_LOAD_OF_KNOWN_NULL_VALUE", "RCN_REDUNDANT_NULLCHECK_OF_NULL_VALUE"}, justification = "https://github.com/spotbugs/spotbugs/issues/756") protected Set loadTrustAnchors(CertificateFactory cf) throws IOException { // if we trust default root CAs, we end up trusting anyone who has a valid certificate, // which isn't useful at all diff --git a/core/src/main/java/jenkins/util/ScriptListener.java b/core/src/main/java/jenkins/util/ScriptListener.java index f75596b8c8..cf987d535e 100644 --- a/core/src/main/java/jenkins/util/ScriptListener.java +++ b/core/src/main/java/jenkins/util/ScriptListener.java @@ -26,7 +26,6 @@ package jenkins.util; import edu.umd.cs.findbugs.annotations.CheckForNull; import edu.umd.cs.findbugs.annotations.NonNull; -import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import groovy.lang.Binding; import hudson.ExtensionPoint; import hudson.model.User; @@ -141,7 +140,6 @@ public interface ScriptListener extends ExtensionPoint { private final String correlationId; private final User user; - @SuppressFBWarnings("EI_EXPOSE_REP2") public ListenerWriter(Writer writer, Object feature, Object context, String correlationId, User user) { this.writer = writer; this.feature = feature; @@ -182,7 +180,6 @@ public interface ScriptListener extends ExtensionPoint { private final String correlationId; private final User user; - @SuppressFBWarnings("EI_EXPOSE_REP2") public ListenerOutputStream(OutputStream os, Charset charset, Object feature, Object context, String correlationId, User user) { this.os = os; this.charset = charset; diff --git a/core/src/main/java/jenkins/util/io/PathRemover.java b/core/src/main/java/jenkins/util/io/PathRemover.java index 06d611febc..4bb293c6ae 100644 --- a/core/src/main/java/jenkins/util/io/PathRemover.java +++ b/core/src/main/java/jenkins/util/io/PathRemover.java @@ -25,7 +25,6 @@ package jenkins.util.io; import edu.umd.cs.findbugs.annotations.NonNull; -import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import hudson.Functions; import hudson.Util; import java.io.IOException; @@ -135,8 +134,6 @@ public class PathRemover { this.waitBetweenRetries = waitBetweenRetries; } - @SuppressFBWarnings(value = "DM_GC", justification = "Garbage collection happens only when " - + "GC_AFTER_FAILED_DELETE is true. It's an experimental feature in Jenkins.") private void gcIfEnabled() { /* If the Jenkins process had the file open earlier, and it has not * closed it then Windows won't let us delete it until the Java object diff --git a/core/src/main/java/jenkins/widgets/HistoryPageFilter.java b/core/src/main/java/jenkins/widgets/HistoryPageFilter.java index f51c27c221..de9030b4b0 100644 --- a/core/src/main/java/jenkins/widgets/HistoryPageFilter.java +++ b/core/src/main/java/jenkins/widgets/HistoryPageFilter.java @@ -67,7 +67,6 @@ public class HistoryPageFilter { public boolean hasDownPage = false; // there are older builds than on this page @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD", justification = "read by Stapler") public long nextBuildNumber; - @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD", justification = "read by Stapler") public HistoryWidget widget; public long newestOnPage = Long.MIN_VALUE; // see updateNewestOldest() diff --git a/core/src/main/java/org/jenkins/ui/symbol/Symbol.java b/core/src/main/java/org/jenkins/ui/symbol/Symbol.java index 52c7faee02..604221d07d 100644 --- a/core/src/main/java/org/jenkins/ui/symbol/Symbol.java +++ b/core/src/main/java/org/jenkins/ui/symbol/Symbol.java @@ -2,7 +2,6 @@ package org.jenkins.ui.symbol; import edu.umd.cs.findbugs.annotations.CheckForNull; import edu.umd.cs.findbugs.annotations.NonNull; -import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import hudson.Functions; import hudson.PluginWrapper; import hudson.Util; @@ -91,7 +90,6 @@ public final class Symbol { } - @SuppressFBWarnings(value = {"NP_LOAD_OF_KNOWN_NULL_VALUE", "RCN_REDUNDANT_NULLCHECK_OF_NULL_VALUE"}, justification = "Spotbugs doesn't grok try-with-resources") private static String loadSymbol(String namespace, String name) { String markup = PLACEHOLDER_SVG; ClassLoader classLoader = getClassLoader(namespace); diff --git a/core/src/main/resources/hudson/diagnosis/OldDataMonitor/manage.jelly b/core/src/main/resources/hudson/diagnosis/OldDataMonitor/manage.jelly index 1bcd7dce1f..84e5c70ae3 100644 --- a/core/src/main/resources/hudson/diagnosis/OldDataMonitor/manage.jelly +++ b/core/src/main/resources/hudson/diagnosis/OldDataMonitor/manage.jelly @@ -31,66 +31,78 @@ THE SOFTWARE.

${%Manage Old Data}

${%blurb.1}

${%blurb.2}

- - - - - - - - - - - - - - - - - - - - - - - - - - - - -
${%Type}${%Name}${%Version}${%Error}
${obj.class.name}${obj.fullName?:obj.fullDisplayName?:obj.displayName?:obj.name} - - - ${item.value} - - - ${item.value} - - - ${item.value.extra}
- + + + + + + + + + + + + + - -

${%Old Data Format}

-

${%blurb.3}

-

${%blurb.4}

-
- ${%Resave data files with structure changes no newer than Jenkins} - - ${%blurb.5} -
- - + + - ${%No old data was found.} + + + + + + + + + + + + + + + + + + + + + + + +
${%Type}${%Name}${%Version}${%Error}
${obj.class.name}${obj.fullName?:obj.fullDisplayName?:obj.displayName?:obj.name} + + + ${item.value} + + + ${item.value} + + + ${item.value.extra}
+
+ + + +

${%Old Data Format}

+

${%blurb.3}

+

${%blurb.4}

+
+ ${%Resave data files with structure changes no newer than Jenkins} + + ${%blurb.5} +
+ + +

${%Unreadable Data}

diff --git a/core/src/main/resources/jenkins/search/Messages.properties b/core/src/main/resources/jenkins/search/Messages.properties new file mode 100644 index 0000000000..2b805c7fb0 --- /dev/null +++ b/core/src/main/resources/jenkins/search/Messages.properties @@ -0,0 +1,27 @@ +# The MIT License +# +# Copyright (c) 2025 Jan Faracik +# +# 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. + +SearchGroup.UnclassifiedSearchGroup.DisplayName=Other +SearchGroup.ItemSearchGroup.DisplayName=Items +SearchGroup.ComputerSearchGroup.DisplayName=Nodes +SearchGroup.ViewSearchGroup.DisplayName=Views +SearchGroup.UserSearchGroup.DisplayName=Users diff --git a/core/src/main/resources/lib/hudson/editableDescription.jelly b/core/src/main/resources/lib/hudson/editableDescription.jelly index 30eba0b393..6af6dc6b78 100644 --- a/core/src/main/resources/lib/hudson/editableDescription.jelly +++ b/core/src/main/resources/lib/hudson/editableDescription.jelly @@ -47,7 +47,7 @@ THE SOFTWARE. -
+
diff --git a/core/src/main/resources/lib/hudson/executors.jelly b/core/src/main/resources/lib/hudson/executors.jelly index 2c2b48159b..ef602de8c0 100644 --- a/core/src/main/resources/lib/hudson/executors.jelly +++ b/core/src/main/resources/lib/hudson/executors.jelly @@ -174,7 +174,9 @@ THE SOFTWARE. ${executorDetails} - + + diff --git a/core/src/main/resources/lib/layout/pane.jelly b/core/src/main/resources/lib/layout/pane.jelly index e7c4a04a82..b88c42df1b 100644 --- a/core/src/main/resources/lib/layout/pane.jelly +++ b/core/src/main/resources/lib/layout/pane.jelly @@ -59,7 +59,9 @@ THE SOFTWARE. - + + diff --git a/core/src/test/java/hudson/UtilTest.java b/core/src/test/java/hudson/UtilTest.java index ac08dccb5d..135da25628 100644 --- a/core/src/test/java/hudson/UtilTest.java +++ b/core/src/test/java/hudson/UtilTest.java @@ -416,12 +416,16 @@ public class UtilTest { } @Test - @Issue("SECURITY-276") + @Issue({"SECURITY-276", "SECURITY-3501"}) public void testIsSafeToRedirectTo() { assertFalse(Util.isSafeToRedirectTo("http://foobar/")); assertFalse(Util.isSafeToRedirectTo("mailto:kk@kohsuke.org")); assertFalse(Util.isSafeToRedirectTo("d123://test/")); assertFalse(Util.isSafeToRedirectTo("//google.com")); + assertFalse(Util.isSafeToRedirectTo("\\\\google.com")); + assertFalse(Util.isSafeToRedirectTo("\\/google.com")); + assertFalse(Util.isSafeToRedirectTo("/\\google.com")); + assertFalse(Util.isSafeToRedirectTo("\\google.com")); assertTrue(Util.isSafeToRedirectTo("foo/bar/abc:def")); assertTrue(Util.isSafeToRedirectTo("foo?abc:def")); diff --git a/package.json b/package.json index 7152a1156c..6b6389ede7 100644 --- a/package.json +++ b/package.json @@ -26,26 +26,26 @@ "@babel/cli": "7.26.4", "@babel/core": "7.26.9", "@babel/preset-env": "7.26.9", - "@eslint/js": "9.21.0", - "babel-loader": "9.2.1", + "@eslint/js": "9.22.0", + "babel-loader": "10.0.0", "clean-webpack-plugin": "4.0.0", "css-loader": "7.1.2", - "css-minimizer-webpack-plugin": "7.0.0", - "eslint": "9.21.0", - "eslint-config-prettier": "10.0.1", + "css-minimizer-webpack-plugin": "7.0.2", + "eslint": "9.22.0", + "eslint-config-prettier": "10.1.1", "eslint-formatter-checkstyle": "8.40.0", "globals": "16.0.0", "handlebars-loader": "1.7.3", "mini-css-extract-plugin": "2.9.2", "postcss": "8.5.3", "postcss-loader": "8.1.1", - "postcss-preset-env": "10.1.4", + "postcss-preset-env": "10.1.5", "postcss-scss": "4.0.9", - "prettier": "3.5.1", - "sass": "1.85.0", + "prettier": "3.5.3", + "sass": "1.85.1", "sass-loader": "16.0.5", "style-loader": "4.0.0", - "stylelint": "16.14.1", + "stylelint": "16.15.0", "stylelint-checkstyle-reporter": "1.0.0", "stylelint-config-standard-scss": "14.0.0", "webpack": "5.98.0", @@ -68,5 +68,5 @@ "engines": { "node": ">=20.0.0" }, - "packageManager": "yarn@4.6.0" + "packageManager": "yarn@4.7.0" } diff --git a/pom.xml b/pom.xml index 74fe1325fc..7f0cf7e828 100644 --- a/pom.xml +++ b/pom.xml @@ -28,7 +28,7 @@ THE SOFTWARE. org.jenkins-ci jenkins - 1.128 + 1.130 @@ -73,9 +73,9 @@ THE SOFTWARE. - 2.500 + 2.502 -SNAPSHOT - 2025-02-25T15:56:49Z + 2025-03-11T13:49:05Z github @@ -87,7 +87,7 @@ THE SOFTWARE. https://www.jenkins.io/changelog - 3283.v92c105e0f819 + 3291.vb_131b_dc231fa_ Max Medium @@ -281,7 +281,7 @@ THE SOFTWARE. com.puppycrawl.tools checkstyle - 10.21.3 + 10.21.4 diff --git a/src/main/js/components/command-palette/datasources.js b/src/main/js/components/command-palette/datasources.js index 58dd5389d5..5fb70b0afe 100644 --- a/src/main/js/components/command-palette/datasources.js +++ b/src/main/js/components/command-palette/datasources.js @@ -21,6 +21,7 @@ export const JenkinsSearchSource = { type: e.type, label: e.name, url: correctAddress(e.url), + group: e.group, }), ); }), diff --git a/src/main/js/components/command-palette/index.js b/src/main/js/components/command-palette/index.js index 56ed7347fb..85b5b2e7c0 100644 --- a/src/main/js/components/command-palette/index.js +++ b/src/main/js/components/command-palette/index.js @@ -5,6 +5,7 @@ import * as Symbols from "./symbols"; import makeKeyboardNavigable from "@/util/keyboard"; import { xmlEscape } from "@/util/security"; import { createElementFromHtml } from "@/util/dom"; +import { groupResultsByCategory } from "@/components/command-palette/utils"; const datasources = [JenkinsSearchSource]; @@ -68,6 +69,7 @@ function init() { label: i18n.dataset.getHelp, url: headerCommandPaletteButton.dataset.searchHelpUrl, isExternal: true, + group: null, }), ]); } else { @@ -77,15 +79,26 @@ function init() { } results.then((results) => { + results = groupResultsByCategory(results); + // Clear current search results searchResults.innerHTML = ""; if (query.length === 0 || Object.keys(results).length > 0) { - results.forEach(function (obj) { - const link = createElementFromHtml(obj.render()); - link.addEventListener("mouseenter", (e) => itemMouseEnter(e)); - searchResults.append(link); - }); + for (const [group, items] of Object.entries(results)) { + if (group !== "null") { + const heading = document.createElement("p"); + heading.className = "jenkins-command-palette__results__heading"; + heading.innerText = group; + searchResults.append(heading); + } + + items.forEach(function (obj) { + const link = createElementFromHtml(obj.render()); + link.addEventListener("mouseenter", (e) => itemMouseEnter(e)); + searchResults.append(link); + }); + } updateSelectedItem(0); } else { diff --git a/src/main/js/components/command-palette/models.js b/src/main/js/components/command-palette/models.js index 0c6a733e0d..79d35fcf7b 100644 --- a/src/main/js/components/command-palette/models.js +++ b/src/main/js/components/command-palette/models.js @@ -7,12 +7,14 @@ import { xmlEscape } from "@/util/security"; * @param {string} params.label * @param {'symbol' | 'image'} params.type * @param {string} params.url + * @param {string | null} params.group * @param {boolean | undefined} params.isExternal */ export function LinkResult(params) { return { label: params.label, url: params.url, + group: params.group, render: () => { return ` { if (instance.loaded) { return; } - instance.popper.addEventListener("click", () => { - instance.hide(); + document.addEventListener("click", (event) => { + const isClickInAnyDropdown = + !!event.target.closest("[data-tippy-root]"); + const isClickOnReference = instance.reference.contains( + event.target, + ); + + if (!isClickInAnyDropdown && !isClickOnReference) { + instance.hide(); + } }); callback(instance); diff --git a/src/main/scss/abstracts/_theme.scss b/src/main/scss/abstracts/_theme.scss index d3f57da2c3..555842288d 100644 --- a/src/main/scss/abstracts/_theme.scss +++ b/src/main/scss/abstracts/_theme.scss @@ -197,17 +197,17 @@ $semantics: ( --link-font-weight: var(--font-bold-weight); // Command Palette - --command-palette-results-backdrop-filter: contrast(0.7) brightness(1.5) - saturate(1.4) blur(20px); + --command-palette-results-backdrop-filter: saturate(1.5) blur(5px); --command-palette-inset-shadow: inset 0 0 2px 2px rgb(255 255 255 / 0.1), - var(--jenkins-border--subtle-shadow); + var(--jenkins-border--subtle-shadow), + 0 5px 10px var(--jenkins-border-color--subtle); ::backdrop { - --command-palette-backdrop-background: radial-gradient( - farthest-corner at 50% 30vh, - rgb(0 0 0 / 0.3), - rgb(0 0 0 / 0.1) + --command-palette-backdrop-background: color-mix( + in sRGB, + var(--black) 17.5%, + transparent ); } diff --git a/src/main/scss/components/_command-palette.scss b/src/main/scss/components/_command-palette.scss index 1d487915e9..8bec54eb62 100644 --- a/src/main/scss/components/_command-palette.scss +++ b/src/main/scss/components/_command-palette.scss @@ -1,6 +1,12 @@ @use "../abstracts/mixins"; @use "../base/breakpoints"; +$command-palette-background: color-mix( + in sRGB, + var(--card-background) 92.5%, + transparent +); + .jenkins-command-palette__dialog { background: none; border: none; @@ -14,19 +20,19 @@ &::backdrop { background: var(--command-palette-backdrop-background); - backdrop-filter: contrast(0.7) brightness(0.9) saturate(1.25) blur(3px); - animation: jenkins-dialog-backdrop-animate-in 0.1s linear; + backdrop-filter: blur(1px); + animation: jenkins-dialog-backdrop-animate-in 0.075s linear; } &[open] { - animation: command-palette-animate-in 0.1s cubic-bezier(0, 0.68, 0.5, 1.5); + animation: command-palette-animate-in 0.075s cubic-bezier(0, 0.68, 0.5, 1.5); } &[closing] { - animation: command-palette-animate-out 0.1s linear; + animation: command-palette-animate-out 0.05s linear; &::backdrop { - animation: jenkins-dialog-backdrop-animate-out 0.1s linear; + animation: jenkins-dialog-backdrop-animate-out 0.05s linear; } } } @@ -48,7 +54,7 @@ } .jenkins-command-palette__wrapper { - --inset: 15vh; + --inset: 10vh; width: 100%; height: 100%; @@ -76,7 +82,7 @@ &__search { --search-bar-height: 3rem !important; - background: transparent; + background: $command-palette-background; box-shadow: var(--command-palette-inset-shadow); margin-bottom: var(--section-padding); border-radius: 1rem; @@ -109,12 +115,13 @@ display: flex; flex-direction: column; border-radius: 1rem; + background: $command-palette-background; backdrop-filter: var(--command-palette-results-backdrop-filter); box-shadow: var(--command-palette-inset-shadow); // If set to 0, Safari won't always show the backdrop-filter height: 1px; - transition: height var(--standard-transition); + transition: height 0.15s ease; overflow: hidden; will-change: height; } @@ -125,14 +132,13 @@ padding: 0.5rem; &__heading { - font-weight: var(--font-bold-weight); font-size: var(--font-size-sm); margin: 0; - padding: 0.75rem 0.75rem 0.625rem; + padding: 0.375rem 0.625rem; color: var(--text-color-secondary); &:not(:first-of-type) { - padding-top: 2rem; + padding-top: 1.375rem; } } @@ -153,10 +159,9 @@ display: flex; align-items: center; justify-content: flex-start; - padding: 0.75rem; + padding: 0.625rem; border-radius: 0.5rem; color: var(--text-color) !important; - transition: var(--standard-transition); &--hover { &::before { @@ -171,7 +176,7 @@ justify-content: center; width: 1.375rem; height: 1.375rem; - margin-right: 0.75rem; + margin-right: 0.625rem; overflow: hidden; pointer-events: none; color: var(--text-color); @@ -210,8 +215,8 @@ &__info { font-size: var(--font-size-sm); margin: 0; - padding: 0 14px; - line-height: 46px; + padding: 0 0.625rem; + line-height: 42px; color: var(--text-color); span { diff --git a/test/pom.xml b/test/pom.xml index bd81136104..e2d9fda289 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -112,7 +112,7 @@ THE SOFTWARE. org.jenkins-ci.plugins scm-api - 703.v72ff4b_259600 + 704.v3ce5c542825a_ @@ -178,7 +178,7 @@ THE SOFTWARE. org.jenkins-ci.main jenkins-test-harness - 2403.v256947ecb_c8a_ + 2411.v1e79b_0dc94b_7 test @@ -206,7 +206,7 @@ THE SOFTWARE. org.jenkins-ci.modules instance-identity - 201.vd2a_b_5a_468a_a_6 + 203.v15e81a_1b_7a_38 test @@ -218,7 +218,7 @@ THE SOFTWARE. org.jenkins-ci.plugins cloudbees-folder - 6.982.vf165a_16c9507 + 6.985.va_f1635030cc5 test @@ -257,6 +257,12 @@ THE SOFTWARE. 343.vdcf37b_a_c81d5 test + + org.jenkins-ci.plugins + variant + 70.va_d9f17f859e0 + test + org.junit.jupiter junit-jupiter @@ -344,7 +350,7 @@ THE SOFTWARE. io.jenkins.plugins design-library - 355.v0f007356e15d + 358.vcc903045e088 hpi ${project.build.outputDirectory}/plugins design-library.jpi @@ -352,7 +358,7 @@ THE SOFTWARE. io.jenkins.plugins prism-api - 1.29.0-19 + 1.30.0-1 hpi ${project.build.outputDirectory}/plugins prism-api.jpi diff --git a/test/src/test/java/hudson/ExtensionListRjrTest.java b/test/src/test/java/hudson/ExtensionListRjrTest.java new file mode 100644 index 0000000000..b35a8bd161 --- /dev/null +++ b/test/src/test/java/hudson/ExtensionListRjrTest.java @@ -0,0 +1,80 @@ +package hudson; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.empty; +import static org.hamcrest.Matchers.hasSize; +import static org.junit.Assert.assertNotNull; + +import java.util.Collection; +import jenkins.plugins.dependee.Dependee; +import jenkins.plugins.dependee.DependeeExtensionPoint; +import jenkins.plugins.dynamic_extension_loading.CustomExtensionLoadedViaConstructor; +import jenkins.plugins.dynamic_extension_loading.CustomExtensionLoadedViaListener; +import jenkins.plugins.dynamic_extension_loading.CustomPeriodicWork; +import jenkins.plugins.optional_depender.OptionalDepender; +import org.junit.Rule; +import org.junit.Test; +import org.jvnet.hudson.test.Issue; +import org.jvnet.hudson.test.JenkinsRule; +import org.jvnet.hudson.test.RealJenkinsRule; +import org.jvnet.hudson.test.RealJenkinsRule.SyntheticPlugin; + +public class ExtensionListRjrTest { + @Rule + public RealJenkinsRule rjr = new RealJenkinsRule(); + + /** + * Check that dynamically loading a plugin does not lead to extension lists with duplicate entries. + * In particular we test extensions that load other extensions in their constructors and extensions that have + * methods that load other extensions and which are called by an {@link ExtensionListListener}. + */ + @Test + @Issue("JENKINS-75232") + public void checkDynamicLoad_singleRegistration() throws Throwable { + var pluginJpi = rjr.createSyntheticPlugin(new SyntheticPlugin(CustomPeriodicWork.class.getPackage()) + .shortName("dynamic-extension-loading") + .header("Plugin-Dependencies", "variant:0")); + var fqcn1 = CustomExtensionLoadedViaListener.class.getName(); + var fqcn2 = CustomExtensionLoadedViaConstructor.class.getName(); + rjr.then(r -> { + r.jenkins.pluginManager.dynamicLoad(pluginJpi); + assertSingleton(r, fqcn1); + assertSingleton(r, fqcn2); + }); + } + + private static void assertSingleton(JenkinsRule r, String fqcn) throws Exception { + var clazz = r.jenkins.pluginManager.uberClassLoader.loadClass(fqcn); + try { + assertNotNull(ExtensionList.lookupSingleton(clazz)); + } catch (Throwable t) { + var list = ExtensionList.lookup(clazz).stream().map(e -> + // TODO: Objects.toIdentityString in Java 19+ + e.getClass().getName() + "@" + Integer.toHexString(System.identityHashCode(e))).toList(); + System.out.println("Duplicates are: " + list); + throw t; + } + } + + @Test + @Issue({ "JENKINS-50336", "JENKINS-60449" }) + public void installDependedOptionalPluginWithoutRestart() throws Throwable { + var optionalDependerJpi = rjr.createSyntheticPlugin(new SyntheticPlugin(OptionalDepender.class.getPackage()) + .header("Plugin-Dependencies", "variant:0,dependee:0;resolution:=optional")); + var dependeeJpi = rjr.createSyntheticPlugin(new SyntheticPlugin(Dependee.class.getPackage()).shortName("dependee")); + var fqcn1 = OptionalDepender.class.getName(); + var fqcn2 = DependeeExtensionPoint.class.getName(); + rjr.then(r -> { + // Load optional-depender. + r.jenkins.pluginManager.dynamicLoad(optionalDependerJpi); + // JENKINS-60449: Extension depending on dependee class isn't loaded + assertThat((Collection) r.jenkins.getExtensionList(fqcn1), empty()); + // Load dependee. + r.jenkins.pluginManager.dynamicLoad(dependeeJpi); + // JENKINS-60449: Classes in depender are loaded prior to OptionalDepender being loaded. + assertThat((Collection) r.jenkins.getExtensionList(fqcn1), hasSize(1)); + // JENKINS-50336: Extension point from dependee now includes optional extension from optional-depender + assertThat((Collection) r.jenkins.getExtensionList(fqcn2), hasSize(1)); + }); + } +} diff --git a/test/src/test/java/hudson/PluginManagerTest.java b/test/src/test/java/hudson/PluginManagerTest.java index ec25bc96a3..d4bcc4e9bf 100644 --- a/test/src/test/java/hudson/PluginManagerTest.java +++ b/test/src/test/java/hudson/PluginManagerTest.java @@ -86,7 +86,6 @@ import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; import jenkins.ClassLoaderReflectionToolkit; import jenkins.RestartRequiredException; -import jenkins.model.GlobalConfiguration; import jenkins.model.Jenkins; import net.sf.json.JSONArray; import net.sf.json.JSONObject; @@ -389,28 +388,6 @@ public class PluginManagerTest { assertEquals(1, r.jenkins.getExtensionList("org.jenkinsci.plugins.dependencytest.depender.DependerExtension").size()); } - /** - * Load "optional-depender" and then load "dependee". - * Asserts that "depender" can access to "dependee". - */ - @Issue("JENKINS-60449") - @WithPlugin("variant.hpi") - @Test public void installDependedOptionalPluginWithoutRestart() throws Exception { - // Load optional-depender. - { - dynamicLoad("optional-depender-0.0.2.hpi"); - } - // Extension depending on dependee class isn't loaded - assertTrue(r.jenkins.getExtensionList("org.jenkinsci.plugins.dependencytest.optionaldepender.OptionalDependerExtension").isEmpty()); - // Load dependee. - { - dynamicLoad("dependee-0.0.2.hpi"); - } - - // Extensions in depender are loaded. - assertEquals(1, r.jenkins.getExtensionList("org.jenkinsci.plugins.dependencytest.optionaldepender.OptionalDependerExtension").size()); - } - @Issue("JENKINS-21486") @Test public void installPluginWithObsoleteDependencyFails() throws Exception { // Load dependee 0.0.1. @@ -611,22 +588,6 @@ public class PluginManagerTest { assertEquals(fromPlugin, fromToolkit); } - // Sources for jenkins-50336.hpi are available at https://github.com/Vlatombe/jenkins-50336 - // - // package io.jenkins.plugins; - // import org.jenkinsci.plugins.variant.OptionalExtension; - // import jenkins.model.GlobalConfiguration; - // @OptionalExtension public class MyGlobalConfiguration extends GlobalConfiguration {} - // - @Issue("JENKINS-50336") - @Test - public void optionalExtensionCanBeFoundAfterDynamicLoadOfVariant() throws Exception { - dynamicLoad("variant.hpi"); - assertNotNull(r.jenkins.getPluginManager().getPlugin("variant")); - dynamicLoad("jenkins-50336.hpi"); - assertTrue(ExtensionList.lookup(GlobalConfiguration.class).stream().anyMatch(gc -> "io.jenkins.plugins.MyGlobalConfiguration".equals(gc.getClass().getName()))); - } - @Test @Issue("JENKINS-64840") @WithPlugin({"mandatory-depender-0.0.2.hpi", "dependee-0.0.2.hpi", "depender-0.0.2.hpi"}) public void getPluginsSortedByTitle() throws Exception { diff --git a/test/src/test/java/hudson/cli/DisablePluginCommandTest.java b/test/src/test/java/hudson/cli/DisablePluginCommandTest.java index 220998236b..b4a4f5f49c 100644 --- a/test/src/test/java/hudson/cli/DisablePluginCommandTest.java +++ b/test/src/test/java/hudson/cli/DisablePluginCommandTest.java @@ -190,11 +190,10 @@ public class DisablePluginCommandTest { @Ignore("TODO calling restart seems to break Surefire") @Test @Issue("JENKINS-27177") - @WithPlugin({"variant.hpi", "depender-0.0.2.hpi", "mandatory-depender-0.0.2.hpi", "plugin-first.hpi", "dependee-0.0.2.hpi"}) + @WithPlugin({"depender-0.0.2.hpi", "mandatory-depender-0.0.2.hpi", "plugin-first.hpi", "dependee-0.0.2.hpi"}) public void restartAfterDisablePluginsAndErrors() { assumeNotWindows(); - assertThat(disablePluginsCLiCommand("-restart", "variant", "dependee", "depender", "plugin-first", "mandatory-depender"), failedWith(RETURN_CODE_NOT_DISABLED_DEPENDANTS)); - assertPluginDisabled("variant"); + assertThat(disablePluginsCLiCommand("-restart", "dependee", "depender", "plugin-first", "mandatory-depender"), failedWith(RETURN_CODE_NOT_DISABLED_DEPENDANTS)); assertPluginEnabled("dependee"); assertPluginDisabled("depender"); assertPluginDisabled("plugin-first"); @@ -207,13 +206,12 @@ public class DisablePluginCommandTest { */ @Test @Issue("JENKINS-27177") - @WithPlugin({"variant.hpi", "depender-0.0.2.hpi", "mandatory-depender-0.0.2.hpi", "plugin-first.hpi", "dependee-0.0.2.hpi"}) + @WithPlugin({"depender-0.0.2.hpi", "mandatory-depender-0.0.2.hpi", "plugin-first.hpi", "dependee-0.0.2.hpi"}) public void disablePluginsStrategyAll() { assertPluginEnabled("dependee"); assertPluginEnabled("depender"); assertPluginEnabled("mandatory-depender"); - assertThat(disablePluginsCLiCommand("-strategy", "all", "variant", "dependee", "plugin-first"), succeeded()); - assertPluginDisabled("variant"); + assertThat(disablePluginsCLiCommand("-strategy", "all", "dependee", "plugin-first"), succeeded()); assertPluginDisabled("dependee"); assertPluginDisabled("depender"); assertPluginDisabled("plugin-first"); @@ -225,10 +223,9 @@ public class DisablePluginCommandTest { */ @Test @Issue("JENKINS-27177") - @WithPlugin({"variant.hpi", "depender-0.0.2.hpi", "mandatory-depender-0.0.2.hpi", "plugin-first.hpi", "dependee-0.0.2.hpi"}) + @WithPlugin({"depender-0.0.2.hpi", "mandatory-depender-0.0.2.hpi", "plugin-first.hpi", "dependee-0.0.2.hpi"}) public void disablePluginsStrategyMandatory() { - assertThat(disablePluginsCLiCommand("-strategy", "mandatory", "variant", "dependee", "plugin-first"), succeeded()); - assertPluginDisabled("variant"); + assertThat(disablePluginsCLiCommand("-strategy", "mandatory", "dependee", "plugin-first"), succeeded()); assertPluginDisabled("dependee"); assertPluginEnabled("depender"); assertPluginDisabled("plugin-first"); diff --git a/test/src/test/java/hudson/cli/EnablePluginCommandTest.java b/test/src/test/java/hudson/cli/EnablePluginCommandTest.java index e80b5e76b5..85d5c43aba 100644 --- a/test/src/test/java/hudson/cli/EnablePluginCommandTest.java +++ b/test/src/test/java/hudson/cli/EnablePluginCommandTest.java @@ -139,7 +139,7 @@ public class EnablePluginCommandTest { @Issue("JENKINS-52950") public void enableNoPluginsWithRestartIsNoOp() { assumeNotWindows(); - String name = "variant"; + String name = "icon-shim"; assertThat(installTestPlugin(name), succeeded()); assertThat(enablePlugins("-restart", name), succeeded()); assertJenkinsNotInQuietMode(); diff --git a/test/src/test/java/hudson/model/AbstractProjectTest.java b/test/src/test/java/hudson/model/AbstractProjectTest.java index b8a9d8e1ea..ab1cc450c9 100644 --- a/test/src/test/java/hudson/model/AbstractProjectTest.java +++ b/test/src/test/java/hudson/model/AbstractProjectTest.java @@ -666,6 +666,7 @@ public class AbstractProjectTest { o.put("url", JSONObject.fromObject(null)); o.put("icon", JSONObject.fromObject(null)); o.put("type", "symbol"); + o.put("group", JSONObject.fromObject(null)); expected.add(o); } assertThat(suggestions.containsAll(expected), is(true)); diff --git a/test/src/test/java/hudson/model/ComputerSetTest.java b/test/src/test/java/hudson/model/ComputerSetTest.java index e137cbfec8..2a091ca784 100644 --- a/test/src/test/java/hudson/model/ComputerSetTest.java +++ b/test/src/test/java/hudson/model/ComputerSetTest.java @@ -173,7 +173,7 @@ public class ComputerSetTest { new OfflineCause.ChannelTermination(new RuntimeException(message)) ); - WebClient wc = j.createWebClient(); + WebClient wc = j.createWebClient().withJavaScriptEnabled(false); Page page = wc.getPage(wc.createCrumbedUrl(HasWidgetHelper.getWidget(j.jenkins.getComputer(), ExecutorsWidget.class).orElseThrow().getUrl() + "ajax")); String content = page.getWebResponse().getContentAsString(); assertThat(content, not(containsString(message))); diff --git a/test/src/test/java/hudson/model/ComputerTest.java b/test/src/test/java/hudson/model/ComputerTest.java index 3acac9b9fc..ee0cf8a686 100644 --- a/test/src/test/java/hudson/model/ComputerTest.java +++ b/test/src/test/java/hudson/model/ComputerTest.java @@ -285,7 +285,7 @@ public class ComputerTest { new OfflineCause.ChannelTermination(new RuntimeException(message)) ); - WebClient wc = j.createWebClient(); + WebClient wc = j.createWebClient().withJavaScriptEnabled(false); Page page = wc.getPage(wc.createCrumbedUrl(HasWidgetHelper.getWidget(agent.toComputer(), ExecutorsWidget.class).orElseThrow().getUrl() + "ajax")); String content = page.getWebResponse().getContentAsString(); assertThat(content, not(containsString(message))); diff --git a/test/src/test/java/jenkins/model/JenkinsTest.java b/test/src/test/java/jenkins/model/JenkinsTest.java index acef3b6a23..f4a5b37616 100644 --- a/test/src/test/java/jenkins/model/JenkinsTest.java +++ b/test/src/test/java/jenkins/model/JenkinsTest.java @@ -72,6 +72,7 @@ import hudson.slaves.OfflineCause; import hudson.util.FormValidation; import hudson.util.HttpResponses; import hudson.util.VersionNumber; +import jakarta.servlet.http.HttpServletResponse; import java.io.File; import java.io.IOException; import java.net.HttpURLConnection; @@ -130,6 +131,16 @@ public class JenkinsTest { @Rule public TemporaryFolder tmp = new TemporaryFolder(); + @Test + @Issue("SECURITY-3498") + public void testPaneToggleCollapse() throws Exception { + try (WebClient wc = j.createWebClient()) { + final FailingHttpStatusCodeException ex = assertThrows(FailingHttpStatusCodeException.class, () -> wc.goTo("toggleCollapse?paneId=foo")); + // @POST responds 404 when the verb is wrong; @RequirePOST would respond 405. + assertThat(ex.getStatusCode(), is(HttpServletResponse.SC_NOT_FOUND)); + } + } + @Test @Issue("SECURITY-3073") public void verifyUploadedFingerprintFilePermission() throws Exception { diff --git a/test/src/test/java/jenkins/plugins/dependee/Dependee.java b/test/src/test/java/jenkins/plugins/dependee/Dependee.java new file mode 100644 index 0000000000..3e795950f9 --- /dev/null +++ b/test/src/test/java/jenkins/plugins/dependee/Dependee.java @@ -0,0 +1,7 @@ +package jenkins.plugins.dependee; + +public class Dependee { + public static String getValue() { + return "dependee"; + } +} diff --git a/test/src/test/java/jenkins/plugins/dependee/DependeeExtensionPoint.java b/test/src/test/java/jenkins/plugins/dependee/DependeeExtensionPoint.java new file mode 100644 index 0000000000..3dd7c7bde2 --- /dev/null +++ b/test/src/test/java/jenkins/plugins/dependee/DependeeExtensionPoint.java @@ -0,0 +1,6 @@ +package jenkins.plugins.dependee; + +import hudson.ExtensionPoint; + +public class DependeeExtensionPoint implements ExtensionPoint { +} diff --git a/test/src/test/java/jenkins/plugins/dynamic_extension_loading/CustomExtensionLoadedViaConstructor.java b/test/src/test/java/jenkins/plugins/dynamic_extension_loading/CustomExtensionLoadedViaConstructor.java new file mode 100644 index 0000000000..aa268a43b1 --- /dev/null +++ b/test/src/test/java/jenkins/plugins/dynamic_extension_loading/CustomExtensionLoadedViaConstructor.java @@ -0,0 +1,14 @@ +package jenkins.plugins.dynamic_extension_loading; + +import java.util.logging.Level; +import java.util.logging.Logger; +import org.jenkinsci.plugins.variant.OptionalExtension; + +@OptionalExtension +public class CustomExtensionLoadedViaConstructor { + private static final Logger LOGGER = Logger.getLogger(CustomExtensionLoadedViaConstructor.class.getName()); + + public CustomExtensionLoadedViaConstructor() { + LOGGER.log(Level.INFO, null, new Exception("Instantiating CustomExtensionLoadedViaConstructor")); + } +} diff --git a/test/src/test/java/jenkins/plugins/dynamic_extension_loading/CustomExtensionLoadedViaListener.java b/test/src/test/java/jenkins/plugins/dynamic_extension_loading/CustomExtensionLoadedViaListener.java new file mode 100644 index 0000000000..acae8c68de --- /dev/null +++ b/test/src/test/java/jenkins/plugins/dynamic_extension_loading/CustomExtensionLoadedViaListener.java @@ -0,0 +1,16 @@ +package jenkins.plugins.dynamic_extension_loading; + +import java.util.logging.Level; +import java.util.logging.Logger; +import org.jenkinsci.plugins.variant.OptionalExtension; + +@OptionalExtension +public class CustomExtensionLoadedViaListener { + private static final Logger LOGGER = Logger.getLogger(CustomExtensionLoadedViaListener.class.getName()); + + public long recurrencePeriod = 120; + + public CustomExtensionLoadedViaListener() { + LOGGER.log(Level.INFO, null, new Exception("Instantiating CustomExtensionLoadedViaListener")); + } +} diff --git a/test/src/test/java/jenkins/plugins/dynamic_extension_loading/CustomPeriodicWork.java b/test/src/test/java/jenkins/plugins/dynamic_extension_loading/CustomPeriodicWork.java new file mode 100644 index 0000000000..a89c180f84 --- /dev/null +++ b/test/src/test/java/jenkins/plugins/dynamic_extension_loading/CustomPeriodicWork.java @@ -0,0 +1,27 @@ +package jenkins.plugins.dynamic_extension_loading; + +import hudson.ExtensionList; +import hudson.model.PeriodicWork; +import java.util.logging.Level; +import java.util.logging.Logger; +import org.jenkinsci.plugins.variant.OptionalExtension; + +@OptionalExtension +public class CustomPeriodicWork extends PeriodicWork { + + private static final Logger LOGGER = Logger.getLogger(CustomPeriodicWork.class.getName()); + + public CustomPeriodicWork() { + LOGGER.log(Level.INFO, null, new Exception("Instantiating CustomPeriodicWork")); + ExtensionList.lookupSingleton(CustomExtensionLoadedViaConstructor.class); + } + + @Override + protected void doRun() {} + + @Override + public long getRecurrencePeriod() { + LOGGER.log(Level.INFO, null, new Exception("Loading CustomExtensionLoadedViaListener")); + return ExtensionList.lookupSingleton(CustomExtensionLoadedViaListener.class).recurrencePeriod; + } +} diff --git a/test/src/test/java/jenkins/plugins/dynamic_extension_loading/package-info.java b/test/src/test/java/jenkins/plugins/dynamic_extension_loading/package-info.java new file mode 100644 index 0000000000..db5a09b6d7 --- /dev/null +++ b/test/src/test/java/jenkins/plugins/dynamic_extension_loading/package-info.java @@ -0,0 +1,4 @@ +@OptionalPackage(requirePlugins = "dynamic-extension-loading") +package jenkins.plugins.dynamic_extension_loading; + +import org.jenkinsci.plugins.variant.OptionalPackage; diff --git a/test/src/test/java/jenkins/plugins/optional_depender/OptionalDepender.java b/test/src/test/java/jenkins/plugins/optional_depender/OptionalDepender.java new file mode 100644 index 0000000000..95e4c61591 --- /dev/null +++ b/test/src/test/java/jenkins/plugins/optional_depender/OptionalDepender.java @@ -0,0 +1,9 @@ +package jenkins.plugins.optional_depender; + +import jenkins.plugins.dependee.Dependee; +import org.jenkinsci.plugins.variant.OptionalExtension; + +@OptionalExtension(requirePlugins = "dependee") +public class OptionalDepender { + private static void foo(Dependee d) {} +} diff --git a/test/src/test/java/jenkins/plugins/optional_depender/OptionalDependerExtension.java b/test/src/test/java/jenkins/plugins/optional_depender/OptionalDependerExtension.java new file mode 100644 index 0000000000..64f5ef386a --- /dev/null +++ b/test/src/test/java/jenkins/plugins/optional_depender/OptionalDependerExtension.java @@ -0,0 +1,9 @@ +package jenkins.plugins.optional_depender; + +import jenkins.plugins.dependee.DependeeExtensionPoint; +import org.jenkinsci.plugins.variant.OptionalExtension; + +@OptionalExtension(requirePlugins = "dependee") +public class OptionalDependerExtension extends DependeeExtensionPoint { + +} diff --git a/test/src/test/java/jenkins/security/Security3501Test.java b/test/src/test/java/jenkins/security/Security3501Test.java new file mode 100644 index 0000000000..f266b2f4c1 --- /dev/null +++ b/test/src/test/java/jenkins/security/Security3501Test.java @@ -0,0 +1,33 @@ +package jenkins.security; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; + +import java.util.List; +import org.htmlunit.FailingHttpStatusCodeException; +import org.junit.Assert; +import org.junit.Rule; +import org.junit.Test; +import org.jvnet.hudson.test.JenkinsRule; +import org.jvnet.hudson.test.RealJenkinsRule; + +public class Security3501Test { + @Rule + public RealJenkinsRule jj = new RealJenkinsRule(); + + @Test + public void testRedirects() throws Throwable { + jj.then(Security3501Test::_testRedirects); + } + + public static void _testRedirects(JenkinsRule j) throws Exception { + List prohibitedPaths = List.of("%5C%5Cexample.org", "%5C/example.org", "/%5Cexample.org", "//example.org", "https://example.org", "\\example.org"); + for (String path : prohibitedPaths) { + try (JenkinsRule.WebClient wc = j.createWebClient().withRedirectEnabled(false)) { + final FailingHttpStatusCodeException fhsce = Assert.assertThrows(FailingHttpStatusCodeException.class, () -> wc.goTo("userContent?path=" + path)); + assertThat(fhsce.getStatusCode(), is(302)); + assertThat(fhsce.getResponse().getResponseHeaderValue("Location"), is(j.getURL().toExternalForm() + "userContent/")); + } + } + } +} diff --git a/test/src/test/java/jenkins/security/StackTraceSuppressionTest.java b/test/src/test/java/jenkins/security/StackTraceSuppressionTest.java index b13a5d11c4..ccda0b9d94 100644 --- a/test/src/test/java/jenkins/security/StackTraceSuppressionTest.java +++ b/test/src/test/java/jenkins/security/StackTraceSuppressionTest.java @@ -29,7 +29,9 @@ import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.not; import hudson.model.FreeStyleProject; +import hudson.model.InvisibleAction; import hudson.model.ItemGroup; +import hudson.model.RootAction; import hudson.model.TopLevelItemDescriptor; import hudson.model.User; import java.io.IOException; @@ -41,6 +43,8 @@ import org.junit.Rule; import org.junit.Test; import org.jvnet.hudson.test.JenkinsRule; import org.jvnet.hudson.test.MockAuthorizationStrategy; +import org.jvnet.hudson.test.TestExtension; +import org.kohsuke.stapler.WebMethod; import org.xml.sax.SAXException; public class StackTraceSuppressionTest { @@ -129,19 +133,15 @@ public class StackTraceSuppressionTest { @Test public void exceptionEndpoint() throws Exception { - /* This test is based upon a testing endpoint that really shouldn't exist in production code. - If Jenkins is improved to eliminate this endpoint, this test may erroneously fail. */ String relativePath = "exception"; - String detailString = "Jenkins.doException"; + String detailString = "ExceptionAction.doException"; checkSuppressedStack(relativePath, detailString); } @Test public void exceptionEndpointShowsTrace() throws Exception { - /* This test is based upon a testing endpoint that really shouldn't exist in production code. - If Jenkins is improved to eliminate this endpoint, this test may erroneously fail. */ String relativePath = "exception"; - String detailString = "Jenkins.doException"; + String detailString = "ExceptionAction.doException"; checkDisplayedStackTrace(relativePath, detailString); } @@ -188,4 +188,18 @@ public class StackTraceSuppressionTest { assertThat(content, containsString(detailString)); } + /* Replacement for historical Jenkins#doException URL */ + @TestExtension + public static class ExceptionAction extends InvisibleAction implements RootAction { + @WebMethod(name = "") + public void doException() { + throw new RuntimeException(); + } + + @Override + public String getUrlName() { + return "exception"; + } + } + } diff --git a/test/src/test/java/lib/form/PasswordTest.java b/test/src/test/java/lib/form/PasswordTest.java index 526ef1586a..c3d6f7f17c 100644 --- a/test/src/test/java/lib/form/PasswordTest.java +++ b/test/src/test/java/lib/form/PasswordTest.java @@ -24,6 +24,7 @@ package lib.form; +import static java.nio.file.Files.readString; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.is; @@ -40,6 +41,8 @@ import hudson.FilePath; import hudson.Launcher; import hudson.cli.CopyJobCommand; import hudson.cli.GetJobCommand; +import hudson.cli.GetNodeCommand; +import hudson.cli.GetViewCommand; import hudson.model.AbstractProject; import hudson.model.Action; import hudson.model.Computer; @@ -48,25 +51,35 @@ import hudson.model.Item; import hudson.model.Job; import hudson.model.JobProperty; import hudson.model.JobPropertyDescriptor; +import hudson.model.ListView; +import hudson.model.Node; import hudson.model.RootAction; import hudson.model.Run; import hudson.model.TaskListener; import hudson.model.User; +import hudson.model.View; +import hudson.model.ViewProperty; +import hudson.security.ACL; +import hudson.slaves.DumbSlave; +import hudson.slaves.NodeProperty; import hudson.tasks.BuildStepDescriptor; import hudson.tasks.Builder; import hudson.util.FormValidation; import hudson.util.Secret; import java.io.ByteArrayOutputStream; +import java.io.File; import java.io.IOException; import java.io.PrintStream; import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.Locale; +import java.util.Map; import java.util.regex.Pattern; import jenkins.model.GlobalConfiguration; import jenkins.model.Jenkins; import jenkins.model.TransientActionFactory; +import jenkins.security.ExtendedReadRedaction; import jenkins.security.ExtendedReadSecretRedaction; import jenkins.tasks.SimpleBuildStep; import org.htmlunit.Page; @@ -124,6 +137,145 @@ public class PasswordTest { } } + @For({ExtendedReadRedaction.class, ExtendedReadSecretRedaction.class}) + @Issue("SECURITY-3495") + @Test + public void testNodeSecrets() throws Exception { + Computer.EXTENDED_READ.setEnabled(true); + j.jenkins.setSecurityRealm(j.createDummySecurityRealm()); + j.jenkins.setAuthorizationStrategy(new MockAuthorizationStrategy().grant(Jenkins.ADMINISTER).everywhere().to("alice").grant(Jenkins.READ, Computer.EXTENDED_READ).everywhere().to("bob")); + + final DumbSlave onlineSlave = j.createOnlineSlave(); + final String secretText = "t0ps3cr3td4t4_node"; + final Secret encryptedSecret = Secret.fromString(secretText); + final String encryptedSecretText = encryptedSecret.getEncryptedValue(); + + onlineSlave.getNodeProperties().add(new NodePropertyWithSecret(encryptedSecret)); + onlineSlave.save(); + + assertThat(readString(new File(onlineSlave.getRootDir(), "config.xml").toPath()), containsString(encryptedSecretText)); + + + { // admin can see encrypted value + GetNodeCommand command = new GetNodeCommand(); + try (JenkinsRule.WebClient wc = j.createWebClient().login("alice")) { + final Page page = wc.goTo(onlineSlave.getComputer().getUrl() + "config.xml", "application/xml"); + final String content = page.getWebResponse().getContentAsString(); + + assertThat(content, not(containsString(secretText))); + assertThat(content, containsString(encryptedSecretText)); + assertThat(content, containsString("" + encryptedSecretText + "")); + + var baos = new ByteArrayOutputStream(); + try (var unused = ACL.as(User.get("alice", true, Map.of()))) { + command.setTransportAuth2(Jenkins.getAuthentication2()); + command.main(List.of(onlineSlave.getNodeName()), Locale.US, System.in, new PrintStream(baos), System.err); + } + assertEquals(content, baos.toString(page.getWebResponse().getContentCharset())); + } + } + + { // extended reader gets only redacted value + GetNodeCommand command = new GetNodeCommand(); + try (JenkinsRule.WebClient wc = j.createWebClient().login("bob")) { + final Page page = wc.goTo(onlineSlave.getComputer().getUrl() + "config.xml", "application/xml"); + final String content = page.getWebResponse().getContentAsString(); + + assertThat(content, not(containsString(secretText))); + assertThat(content, not(containsString(encryptedSecretText))); + assertThat(content, containsString("********")); + + var baos = new ByteArrayOutputStream(); + try (var unused = ACL.as(User.get("bob", true, Map.of()))) { + command.setTransportAuth2(Jenkins.getAuthentication2()); + command.main(List.of(onlineSlave.getNodeName()), Locale.US, System.in, new PrintStream(baos), System.err); + } + assertEquals(content, baos.toString(page.getWebResponse().getContentCharset())); + } + } + } + + public static class NodePropertyWithSecret extends NodeProperty { + private final Secret secret; + + public NodePropertyWithSecret(Secret secret) { + this.secret = secret; + } + + public Secret getSecret() { + return secret; + } + } + + @For({ExtendedReadRedaction.class, ExtendedReadSecretRedaction.class}) + @Issue("SECURITY-3496") + @Test + public void testViewSecrets() throws Exception { + j.jenkins.setSecurityRealm(j.createDummySecurityRealm()); + j.jenkins.setAuthorizationStrategy(new MockAuthorizationStrategy().grant(Jenkins.ADMINISTER).everywhere().to("alice").grant(Jenkins.READ, View.READ).everywhere().to("bob")); + + final String secretText = "t0ps3cr3td4t4_view"; + final Secret encryptedSecret = Secret.fromString(secretText); + final String encryptedSecretText = encryptedSecret.getEncryptedValue(); + + final ListView v = new ListView("security-3496"); + v.getProperties().add(new ViewPropertyWithSecret(encryptedSecret)); + j.jenkins.addView(v); + + assertThat(readString(new File(j.jenkins.getRootDir(), "config.xml").toPath()), containsString(encryptedSecretText)); + + + { // admin can see encrypted value + var command = new GetViewCommand(); + try (JenkinsRule.WebClient wc = j.createWebClient().login("alice")) { + final Page page = wc.goTo(v.getUrl() + "config.xml", "application/xml"); + final String content = page.getWebResponse().getContentAsString(); + + assertThat(content, not(containsString(secretText))); + assertThat(content, containsString(encryptedSecretText)); + assertThat(content, containsString("" + encryptedSecretText + "")); + + var baos = new ByteArrayOutputStream(); + try (var unused = ACL.as(User.get("alice", true, Map.of()))) { + command.setTransportAuth2(Jenkins.getAuthentication2()); + command.main(List.of(v.getViewName()), Locale.US, System.in, new PrintStream(baos), System.err); + } + assertEquals(content, baos.toString(page.getWebResponse().getContentCharset())); + } + } + + { // extended reader gets only redacted value + var command = new GetViewCommand(); + try (JenkinsRule.WebClient wc = j.createWebClient().login("bob")) { + final Page page = wc.goTo(v.getUrl() + "config.xml", "application/xml"); + final String content = page.getWebResponse().getContentAsString(); + + assertThat(content, not(containsString(secretText))); + assertThat(content, not(containsString(encryptedSecretText))); + assertThat(content, containsString("********")); + + var baos = new ByteArrayOutputStream(); + try (var unused = ACL.as(User.get("bob", true, Map.of()))) { + command.setTransportAuth2(Jenkins.getAuthentication2()); + command.main(List.of(v.getViewName()), Locale.US, System.in, new PrintStream(baos), System.err); + } + assertEquals(content, baos.toString(page.getWebResponse().getContentCharset())); + } + } + } + + public static class ViewPropertyWithSecret extends ViewProperty { + private final Secret secret; + + public ViewPropertyWithSecret(Secret secret) { + this.secret = secret; + } + + public Secret getSecret() { + return secret; + } + } + @Issue({"SECURITY-266", "SECURITY-304"}) @Test @For(ExtendedReadSecretRedaction.class) diff --git a/test/src/test/java/lib/layout/AjaxTest.java b/test/src/test/java/lib/layout/AjaxTest.java index d25b4e34cd..f039d488df 100644 --- a/test/src/test/java/lib/layout/AjaxTest.java +++ b/test/src/test/java/lib/layout/AjaxTest.java @@ -61,7 +61,7 @@ public class AjaxTest { @Test @Issue("JENKINS-65288") public void ajaxPageRenderingPossibleWithoutJellyTrace() throws Exception { - JenkinsRule.WebClient wc = r.createWebClient(); + JenkinsRule.WebClient wc = r.createWebClient().withJavaScriptEnabled(false); HtmlPage htmlPage = wc.goTo(getExecutorsWidgetAjaxViewUrl()); r.assertGoodStatus(htmlPage); } @@ -76,7 +76,7 @@ public class AjaxTest { try { JellyFacet.TRACE = true; - JenkinsRule.WebClient wc = r.createWebClient(); + JenkinsRule.WebClient wc = r.createWebClient().withJavaScriptEnabled(false); HtmlPage htmlPage = wc.goTo(getExecutorsWidgetAjaxViewUrl()); r.assertGoodStatus(htmlPage); } finally { diff --git a/test/src/test/resources/plugins/jenkins-50336.hpi b/test/src/test/resources/plugins/jenkins-50336.hpi deleted file mode 100644 index 03578c6c8c..0000000000 Binary files a/test/src/test/resources/plugins/jenkins-50336.hpi and /dev/null differ diff --git a/test/src/test/resources/plugins/variant.hpi b/test/src/test/resources/plugins/variant.hpi deleted file mode 100644 index 9f49430df0..0000000000 Binary files a/test/src/test/resources/plugins/variant.hpi and /dev/null differ diff --git a/war/pom.xml b/war/pom.xml index 765fd8abc4..3afd42a7bd 100644 --- a/war/pom.xml +++ b/war/pom.xml @@ -344,7 +344,7 @@ THE SOFTWARE. io.jenkins.plugins checks-api - 2.2.3 + 367.v18b_7f530e54a_ hpi @@ -360,14 +360,14 @@ THE SOFTWARE. org.jenkins-ci.plugins jackson2-api - 2.17.0-389.va_5c7e45cd806 + 2.18.3-402.v74c4eb_f122b_2 hpi io.jenkins.plugins echarts-api - 5.5.1-6 + 5.6.0-2 hpi @@ -420,7 +420,7 @@ THE SOFTWARE. org.jenkins-ci.plugins scm-api - 703.v72ff4b_259600 + 704.v3ce5c542825a_ hpi @@ -455,7 +455,7 @@ THE SOFTWARE. io.jenkins.plugins jaxb - 2.3.9-1 + 2.3.9-133.vb_ec76a_73f706 hpi @@ -469,7 +469,7 @@ THE SOFTWARE. org.jenkins-ci.plugins trilead-api - 2.190.v1ed19f8137f8 + 2.192.vc50a_d147e369 hpi @@ -490,7 +490,7 @@ THE SOFTWARE. org.jenkins-ci.modules instance-identity - 201.vd2a_b_5a_468a_a_6 + 203.v15e81a_1b_7a_38 hpi diff --git a/yarn.lock b/yarn.lock index 4be9571e00..af7846e32b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1137,33 +1137,33 @@ __metadata: languageName: node linkType: hard -"@csstools/color-helpers@npm:^5.0.1": - version: 5.0.1 - resolution: "@csstools/color-helpers@npm:5.0.1" - checksum: 10c0/77fa3b7236eaa3f36dea24708ac0d5e53168903624ac5aed54615752a0730cd20773fda50e742ce868012eca8c000cc39688e05869e79f34714230ab6968d1e6 +"@csstools/color-helpers@npm:^5.0.2": + version: 5.0.2 + resolution: "@csstools/color-helpers@npm:5.0.2" + checksum: 10c0/bebaddb28b9eb58b0449edd5d0c0318fa88f3cb079602ee27e88c9118070d666dcc4e09a5aa936aba2fde6ba419922ade07b7b506af97dd7051abd08dfb2959b languageName: node linkType: hard -"@csstools/css-calc@npm:^2.1.1": - version: 2.1.1 - resolution: "@csstools/css-calc@npm:2.1.1" +"@csstools/css-calc@npm:^2.1.2": + version: 2.1.2 + resolution: "@csstools/css-calc@npm:2.1.2" peerDependencies: "@csstools/css-parser-algorithms": ^3.0.4 "@csstools/css-tokenizer": ^3.0.3 - checksum: 10c0/857c8dac40eb6ba8810408dad141bbcad060b28bce69dfd3bcf095a060fcaa23d5c4dbf52be88fcb57e12ce32c666e855dc68de1d8020851f6b432e3f9b29950 + checksum: 10c0/34ced30553968ef5d5f9e00e3b90b48c47480cf130e282e99d57ec9b09f803aab8bc06325683e72a1518b5e7180a3da8b533f1b462062757c21989a53b482e1a languageName: node linkType: hard -"@csstools/css-color-parser@npm:^3.0.7": - version: 3.0.7 - resolution: "@csstools/css-color-parser@npm:3.0.7" +"@csstools/css-color-parser@npm:^3.0.8": + version: 3.0.8 + resolution: "@csstools/css-color-parser@npm:3.0.8" dependencies: - "@csstools/color-helpers": "npm:^5.0.1" - "@csstools/css-calc": "npm:^2.1.1" + "@csstools/color-helpers": "npm:^5.0.2" + "@csstools/css-calc": "npm:^2.1.2" peerDependencies: "@csstools/css-parser-algorithms": ^3.0.4 "@csstools/css-tokenizer": ^3.0.3 - checksum: 10c0/b81780e6c50f0b0605776bd39bbd6203780231a561601853a9835cc70788560e7a281d0fbfe47ebe8affcb07dd64b0b1dcd4b67552520cfbe0e5088df158f12c + checksum: 10c0/90722c5a62ca94e9d578ddf59be604a76400b932bd3d4bd23cb1ae9b7ace8fcf83c06995d2b31f96f4afef24a7cefba79beb11ed7ee4999d7ecfec3869368359 languageName: node linkType: hard @@ -1205,33 +1205,33 @@ __metadata: languageName: node linkType: hard -"@csstools/postcss-color-function@npm:^4.0.7": - version: 4.0.7 - resolution: "@csstools/postcss-color-function@npm:4.0.7" +"@csstools/postcss-color-function@npm:^4.0.8": + version: 4.0.8 + resolution: "@csstools/postcss-color-function@npm:4.0.8" dependencies: - "@csstools/css-color-parser": "npm:^3.0.7" + "@csstools/css-color-parser": "npm:^3.0.8" "@csstools/css-parser-algorithms": "npm:^3.0.4" "@csstools/css-tokenizer": "npm:^3.0.3" "@csstools/postcss-progressive-custom-properties": "npm:^4.0.0" "@csstools/utilities": "npm:^2.0.0" peerDependencies: postcss: ^8.4 - checksum: 10c0/0f466e1d8863800f6428d7801e2134a834c9ea4b8098f84df41379cd3c3ba84f62588b46e03b26cf13c7d61b1112d22bdfd72adbcec7f5cb27f1149e855cd3ab + checksum: 10c0/d52c65bb4ed28f62b3fc9c0b2ce068e58395345dcead797ed8f7e4f5f469a9311607d39dd409c571ccc94d6c5c84171aff62d51d4f53fdcf6e1cca23fc31d4f1 languageName: node linkType: hard -"@csstools/postcss-color-mix-function@npm:^3.0.7": - version: 3.0.7 - resolution: "@csstools/postcss-color-mix-function@npm:3.0.7" +"@csstools/postcss-color-mix-function@npm:^3.0.8": + version: 3.0.8 + resolution: "@csstools/postcss-color-mix-function@npm:3.0.8" dependencies: - "@csstools/css-color-parser": "npm:^3.0.7" + "@csstools/css-color-parser": "npm:^3.0.8" "@csstools/css-parser-algorithms": "npm:^3.0.4" "@csstools/css-tokenizer": "npm:^3.0.3" "@csstools/postcss-progressive-custom-properties": "npm:^4.0.0" "@csstools/utilities": "npm:^2.0.0" peerDependencies: postcss: ^8.4 - checksum: 10c0/e663615c7fde6effe9888c049bf74373c55d7d69e36c239eb1343c4aa86810b2407baebedd9fd67c6374fbecc32b4b96d11cdba6099473e4551ce7a1e9613513 + checksum: 10c0/3fe7093b38f2b469462fa942af5a54a1ad68b07cd33267288e5c9e865d3a871c04774463136e4af24955316f40560dda1371d02cfd5595475a742afae13a37ba languageName: node linkType: hard @@ -1249,16 +1249,16 @@ __metadata: languageName: node linkType: hard -"@csstools/postcss-exponential-functions@npm:^2.0.6": - version: 2.0.6 - resolution: "@csstools/postcss-exponential-functions@npm:2.0.6" +"@csstools/postcss-exponential-functions@npm:^2.0.7": + version: 2.0.7 + resolution: "@csstools/postcss-exponential-functions@npm:2.0.7" dependencies: - "@csstools/css-calc": "npm:^2.1.1" + "@csstools/css-calc": "npm:^2.1.2" "@csstools/css-parser-algorithms": "npm:^3.0.4" "@csstools/css-tokenizer": "npm:^3.0.3" peerDependencies: postcss: ^8.4 - checksum: 10c0/e8b5bdde8e60cdd628c6654f2336921fa0df1a9468ce3b7cd40c9f27457cd1f8a2ffc9c6380487d55c6188bed6e772679cefa4dfa5ba90229957a030df9403ce + checksum: 10c0/9d02076135ee9bf82bf911f577c9fda42bf00347f3c519fa83e32e83f5b8a98649b97e13ba3a42ed906467729d7b69574595556dfb9e865c86d3bbae5ffbc918 languageName: node linkType: hard @@ -1274,46 +1274,46 @@ __metadata: languageName: node linkType: hard -"@csstools/postcss-gamut-mapping@npm:^2.0.7": - version: 2.0.7 - resolution: "@csstools/postcss-gamut-mapping@npm:2.0.7" +"@csstools/postcss-gamut-mapping@npm:^2.0.8": + version: 2.0.8 + resolution: "@csstools/postcss-gamut-mapping@npm:2.0.8" dependencies: - "@csstools/css-color-parser": "npm:^3.0.7" + "@csstools/css-color-parser": "npm:^3.0.8" "@csstools/css-parser-algorithms": "npm:^3.0.4" "@csstools/css-tokenizer": "npm:^3.0.3" peerDependencies: postcss: ^8.4 - checksum: 10c0/823603b1083ce2372ccbb2c25b744739cec8371ce593460a85896163fc8eb2b8e992497611af22dd765c2fccd8998b3d683732d61579d40bda0d3f21e6d74e06 + checksum: 10c0/81daaba0e774ed3ab97e2c7c93dcae16d1e8447a27f0e82ddf8a176e8f1e93b444f463284105fd312c6234d4210372d6d69d96efcfb05bc5b6adfba6fcfd6f44 languageName: node linkType: hard -"@csstools/postcss-gradients-interpolation-method@npm:^5.0.7": - version: 5.0.7 - resolution: "@csstools/postcss-gradients-interpolation-method@npm:5.0.7" +"@csstools/postcss-gradients-interpolation-method@npm:^5.0.8": + version: 5.0.8 + resolution: "@csstools/postcss-gradients-interpolation-method@npm:5.0.8" dependencies: - "@csstools/css-color-parser": "npm:^3.0.7" + "@csstools/css-color-parser": "npm:^3.0.8" "@csstools/css-parser-algorithms": "npm:^3.0.4" "@csstools/css-tokenizer": "npm:^3.0.3" "@csstools/postcss-progressive-custom-properties": "npm:^4.0.0" "@csstools/utilities": "npm:^2.0.0" peerDependencies: postcss: ^8.4 - checksum: 10c0/2998d28cd27ecf052da08679ca2fc5c8fcee68ade96cc32db4b4ae44f2b364954804e1e182cb547d6e8e4b5c84d6269763e12e3dfe1fd47c165c539c423b2ea0 + checksum: 10c0/832bfb663b334be9783f49c354cbeec3cede1830a576b91a101456db33207e9651f97624f0df92e5d01a39b68a215ad4b20621ee229b92b51607e889093bc590 languageName: node linkType: hard -"@csstools/postcss-hwb-function@npm:^4.0.7": - version: 4.0.7 - resolution: "@csstools/postcss-hwb-function@npm:4.0.7" +"@csstools/postcss-hwb-function@npm:^4.0.8": + version: 4.0.8 + resolution: "@csstools/postcss-hwb-function@npm:4.0.8" dependencies: - "@csstools/css-color-parser": "npm:^3.0.7" + "@csstools/css-color-parser": "npm:^3.0.8" "@csstools/css-parser-algorithms": "npm:^3.0.4" "@csstools/css-tokenizer": "npm:^3.0.3" "@csstools/postcss-progressive-custom-properties": "npm:^4.0.0" "@csstools/utilities": "npm:^2.0.0" peerDependencies: postcss: ^8.4 - checksum: 10c0/fa8e329ec92a9a04ba8d41d6640e39ea109c8d9ea1c90eaa141e303ae9bc41eca2c7bec72e4211f79a48b7e6746d754a66045b10da04ca9953c8a394a3bc1099 + checksum: 10c0/d6196e2acfc0a6fd61fe254385049fb784abb862c724543940dbba8ffe29bbdbedd83985a517132a21073435445486f918da170fb0f710dbe40a798b9abc41e7 languageName: node linkType: hard @@ -1415,17 +1415,17 @@ __metadata: languageName: node linkType: hard -"@csstools/postcss-media-minmax@npm:^2.0.6": - version: 2.0.6 - resolution: "@csstools/postcss-media-minmax@npm:2.0.6" +"@csstools/postcss-media-minmax@npm:^2.0.7": + version: 2.0.7 + resolution: "@csstools/postcss-media-minmax@npm:2.0.7" dependencies: - "@csstools/css-calc": "npm:^2.1.1" + "@csstools/css-calc": "npm:^2.1.2" "@csstools/css-parser-algorithms": "npm:^3.0.4" "@csstools/css-tokenizer": "npm:^3.0.3" "@csstools/media-query-list-parser": "npm:^4.0.2" peerDependencies: postcss: ^8.4 - checksum: 10c0/9cae49dcbba3f6818b679490665b48f13ab6c57f323a7e614e53a850503b6c5957a0de8dfff7184332c82f6f8570846283a96698791afb367457e4b24a4437f9 + checksum: 10c0/03b7a5603437d5be17e9c0d951ca0b7b3b6f437fd4e24e3ac3f70ed9d573ef67641821fe209b5764c54aa36e841c830a5d8cf3a3dd97fd2fa774b7ceba7ba038 languageName: node linkType: hard @@ -1465,18 +1465,18 @@ __metadata: languageName: node linkType: hard -"@csstools/postcss-oklab-function@npm:^4.0.7": - version: 4.0.7 - resolution: "@csstools/postcss-oklab-function@npm:4.0.7" +"@csstools/postcss-oklab-function@npm:^4.0.8": + version: 4.0.8 + resolution: "@csstools/postcss-oklab-function@npm:4.0.8" dependencies: - "@csstools/css-color-parser": "npm:^3.0.7" + "@csstools/css-color-parser": "npm:^3.0.8" "@csstools/css-parser-algorithms": "npm:^3.0.4" "@csstools/css-tokenizer": "npm:^3.0.3" "@csstools/postcss-progressive-custom-properties": "npm:^4.0.0" "@csstools/utilities": "npm:^2.0.0" peerDependencies: postcss: ^8.4 - checksum: 10c0/16d438aee2340dedd27249b540e261ea07bad56ceba507f6118e3eb44c693a977a374b554a1006a14c5d6d024f62d7cc468d7f4351a1c4e04e3a58142a3026a3 + checksum: 10c0/8a62f3875bb9026c95758a0b834e876a8f07dd1a5ba36c3967e230565fbd9afd21ec714c8590cb4ea594fd214e68f2ccf58456ed6e919a47d2ed17d5b63a925a languageName: node linkType: hard @@ -1491,31 +1491,31 @@ __metadata: languageName: node linkType: hard -"@csstools/postcss-random-function@npm:^1.0.2": - version: 1.0.2 - resolution: "@csstools/postcss-random-function@npm:1.0.2" +"@csstools/postcss-random-function@npm:^1.0.3": + version: 1.0.3 + resolution: "@csstools/postcss-random-function@npm:1.0.3" dependencies: - "@csstools/css-calc": "npm:^2.1.1" + "@csstools/css-calc": "npm:^2.1.2" "@csstools/css-parser-algorithms": "npm:^3.0.4" "@csstools/css-tokenizer": "npm:^3.0.3" peerDependencies: postcss: ^8.4 - checksum: 10c0/b0bc235999685045ca91f8f18eb56ced68747aec6e8b7ff57ac86b1c385d6c2526a528fde5fd32e0987b4387b22a75c73e2d2ebd57974c4ca32d3d60a1eb093a + checksum: 10c0/c3bf319a6f79c0e372e4754e7888a4cd3a97b81e480662b1d1cb193949670bbcd5995c42483390a996e66d6dd81c9ad753836cc617aac2e3acbd542faa56f907 languageName: node linkType: hard -"@csstools/postcss-relative-color-syntax@npm:^3.0.7": - version: 3.0.7 - resolution: "@csstools/postcss-relative-color-syntax@npm:3.0.7" +"@csstools/postcss-relative-color-syntax@npm:^3.0.8": + version: 3.0.8 + resolution: "@csstools/postcss-relative-color-syntax@npm:3.0.8" dependencies: - "@csstools/css-color-parser": "npm:^3.0.7" + "@csstools/css-color-parser": "npm:^3.0.8" "@csstools/css-parser-algorithms": "npm:^3.0.4" "@csstools/css-tokenizer": "npm:^3.0.3" "@csstools/postcss-progressive-custom-properties": "npm:^4.0.0" "@csstools/utilities": "npm:^2.0.0" peerDependencies: postcss: ^8.4 - checksum: 10c0/8741e3c337e5f321569fd41dac2442641390716bc67175faa3301bbbeaf23fe5b722b3b0b8f133ad0b927f32a2ed5fb73565fa8ee88685239d781f1826142405 + checksum: 10c0/fcd14fb1c3f103dbaaf88afa2540f9946313d48515fa24fffcde4200e7dc4aa767d186ecf2e12bb0501dd946a824f118cd4ad5d44899c8d6d9d8d9d9b99a123e languageName: node linkType: hard @@ -1530,54 +1530,54 @@ __metadata: languageName: node linkType: hard -"@csstools/postcss-sign-functions@npm:^1.1.1": - version: 1.1.1 - resolution: "@csstools/postcss-sign-functions@npm:1.1.1" +"@csstools/postcss-sign-functions@npm:^1.1.2": + version: 1.1.2 + resolution: "@csstools/postcss-sign-functions@npm:1.1.2" dependencies: - "@csstools/css-calc": "npm:^2.1.1" + "@csstools/css-calc": "npm:^2.1.2" "@csstools/css-parser-algorithms": "npm:^3.0.4" "@csstools/css-tokenizer": "npm:^3.0.3" peerDependencies: postcss: ^8.4 - checksum: 10c0/d9ebbbba833307aba0f490e527dd88a4796e94cc3ae0ba8ad1ada2830cdadfd3f9c9c37e18152903277905f847b71dac2ac1f9f78752aabc4c03a5c5c10157b1 + checksum: 10c0/15a1c434c3059ab884634d32374d53265c0ea5b5d1f6cb979dcfef18903edbafbf334fcbabd5b24869356db93792adfe95d88efef998b7d6b4c6f4b8393faca1 languageName: node linkType: hard -"@csstools/postcss-stepped-value-functions@npm:^4.0.6": - version: 4.0.6 - resolution: "@csstools/postcss-stepped-value-functions@npm:4.0.6" +"@csstools/postcss-stepped-value-functions@npm:^4.0.7": + version: 4.0.7 + resolution: "@csstools/postcss-stepped-value-functions@npm:4.0.7" dependencies: - "@csstools/css-calc": "npm:^2.1.1" + "@csstools/css-calc": "npm:^2.1.2" "@csstools/css-parser-algorithms": "npm:^3.0.4" "@csstools/css-tokenizer": "npm:^3.0.3" peerDependencies: postcss: ^8.4 - checksum: 10c0/9da91f2ca041a4c4a3118c3ac92b9c9ae244442423bb2d20f6861c5e8225af8f7e05b0d794ae0600dd7a23ca565f7714e066e7a3ea180350534dc0a30ae0d7f4 + checksum: 10c0/1e664f0b169abe0e8ad832844ff06b219702ba7e6af795801109bd2e90403295d5cdb2e27c17f92e60d9704b30726b4564da79e0bf66dec852d50704a8813053 languageName: node linkType: hard -"@csstools/postcss-text-decoration-shorthand@npm:^4.0.1": - version: 4.0.1 - resolution: "@csstools/postcss-text-decoration-shorthand@npm:4.0.1" +"@csstools/postcss-text-decoration-shorthand@npm:^4.0.2": + version: 4.0.2 + resolution: "@csstools/postcss-text-decoration-shorthand@npm:4.0.2" dependencies: - "@csstools/color-helpers": "npm:^5.0.1" + "@csstools/color-helpers": "npm:^5.0.2" postcss-value-parser: "npm:^4.2.0" peerDependencies: postcss: ^8.4 - checksum: 10c0/81950e248d6019c0066353895e0fa2a5c684b754c9af349218cb919534f5ebf79e5e9c7a10b3af1e9c56de2f246968de3b87a00d8c4102e5f88e0f05c04f9889 + checksum: 10c0/01e2f3717e7a42224dc1a746491c55a381cf208cb7588f0308eeefe730675be4c7bb56c0cc557e75999c981e67da7d0b0bb68610635752c89ef251ee435b9cac languageName: node linkType: hard -"@csstools/postcss-trigonometric-functions@npm:^4.0.6": - version: 4.0.6 - resolution: "@csstools/postcss-trigonometric-functions@npm:4.0.6" +"@csstools/postcss-trigonometric-functions@npm:^4.0.7": + version: 4.0.7 + resolution: "@csstools/postcss-trigonometric-functions@npm:4.0.7" dependencies: - "@csstools/css-calc": "npm:^2.1.1" + "@csstools/css-calc": "npm:^2.1.2" "@csstools/css-parser-algorithms": "npm:^3.0.4" "@csstools/css-tokenizer": "npm:^3.0.3" peerDependencies: postcss: ^8.4 - checksum: 10c0/b5aae978bbdca94c4a0f292ab5ec01750d8aeb62d68e0f9326b49ce94166886dfbfb48f169c9a053e874c9df78243053a30ceec91270a6022396d541d8c44ce9 + checksum: 10c0/2b01608a9f7dba6f73febfdd75269f6f88eb2a653de38a0adc6e81de57de4248bedd39b3e8b219cc49ce73b99118e285a870711953a553ddddb0bd5b2f9a5852 languageName: node linkType: hard @@ -1660,6 +1660,13 @@ __metadata: languageName: node linkType: hard +"@eslint/config-helpers@npm:^0.1.0": + version: 0.1.0 + resolution: "@eslint/config-helpers@npm:0.1.0" + checksum: 10c0/3562b5325f42740fc83b0b92b7d13a61b383f8db064915143eec36184f09a09fad73eca6c2955ab6c248b0d04fa03c140f9af2f2c4c06770781a6b79f300a01e + languageName: node + linkType: hard + "@eslint/core@npm:^0.12.0": version: 0.12.0 resolution: "@eslint/core@npm:0.12.0" @@ -1686,10 +1693,10 @@ __metadata: languageName: node linkType: hard -"@eslint/js@npm:9.21.0": - version: 9.21.0 - resolution: "@eslint/js@npm:9.21.0" - checksum: 10c0/86c24a2668808995037e3f40c758335df2ae277c553ac0cf84381a1a8698f3099d8a22dd9c388947e6b7f93fcc1142f62406072faaa2b83c43ca79993fc01bb3 +"@eslint/js@npm:9.22.0": + version: 9.22.0 + resolution: "@eslint/js@npm:9.22.0" + checksum: 10c0/5bcd009bb579dc6c6ed760703bdd741e08a48cd9decd677aa2cf67fe66236658cb09a00185a0369f3904e5cffba9e6e0f2ff4d9ba4fdf598fcd81d34c49213a5 languageName: node linkType: hard @@ -2618,16 +2625,15 @@ __metadata: languageName: node linkType: hard -"babel-loader@npm:9.2.1": - version: 9.2.1 - resolution: "babel-loader@npm:9.2.1" +"babel-loader@npm:10.0.0": + version: 10.0.0 + resolution: "babel-loader@npm:10.0.0" dependencies: - find-cache-dir: "npm:^4.0.0" - schema-utils: "npm:^4.0.0" + find-up: "npm:^5.0.0" peerDependencies: "@babel/core": ^7.12.0 - webpack: ">=5" - checksum: 10c0/efb82faff4c7c27e9c15bb28bf11c73200e61cf365118a9514e8d74dd489d0afc2a0d5aaa62cb4254eefc2ab631579224d95a03fd245410f28ea75e24de54ba4 + webpack: ">=5.61.0" + checksum: 10c0/882dfacde3ee24b432ad57e468832cd0821e2a410f6c5b75ff945f069a8956592b28c6c357df5bb03db73d2741ec3db5febb106ac0bb3591c3d4288f2cf4df0e languageName: node linkType: hard @@ -2788,13 +2794,13 @@ __metadata: languageName: node linkType: hard -"cacheable@npm:^1.8.7": - version: 1.8.7 - resolution: "cacheable@npm:1.8.7" +"cacheable@npm:^1.8.8": + version: 1.8.8 + resolution: "cacheable@npm:1.8.8" dependencies: - hookified: "npm:^1.6.0" + hookified: "npm:^1.7.0" keyv: "npm:^5.2.3" - checksum: 10c0/3403c2afe9f8bb804b2371e2aa45f10c1f817945ce20b3da2338d2f0b099c9a9d28e59939592e832ba0e6b1d932f8555cc0a0d815ca7bd68a086e1ad131ae40a + checksum: 10c0/24e0f93782015be75b1ec9fe3fb151b2921f61c282091b873f78a0efeb141e95a21d8aa5f4c6bd99a8acb0b485deb5801aa32b4ecf4b666efa7446739368588b languageName: node linkType: hard @@ -2963,13 +2969,6 @@ __metadata: languageName: node linkType: hard -"common-path-prefix@npm:^3.0.0": - version: 3.0.0 - resolution: "common-path-prefix@npm:3.0.0" - checksum: 10c0/c4a74294e1b1570f4a8ab435285d185a03976c323caa16359053e749db4fde44e3e6586c29cd051100335e11895767cbbd27ea389108e327d62f38daf4548fdb - languageName: node - linkType: hard - "concat-map@npm:0.0.1": version: 0.0.1 resolution: "concat-map@npm:0.0.1" @@ -3085,14 +3084,14 @@ __metadata: languageName: node linkType: hard -"css-minimizer-webpack-plugin@npm:7.0.0": - version: 7.0.0 - resolution: "css-minimizer-webpack-plugin@npm:7.0.0" +"css-minimizer-webpack-plugin@npm:7.0.2": + version: 7.0.2 + resolution: "css-minimizer-webpack-plugin@npm:7.0.2" dependencies: "@jridgewell/trace-mapping": "npm:^0.3.25" - cssnano: "npm:^7.0.1" + cssnano: "npm:^7.0.4" jest-worker: "npm:^29.7.0" - postcss: "npm:^8.4.38" + postcss: "npm:^8.4.40" schema-utils: "npm:^4.2.0" serialize-javascript: "npm:^6.0.2" peerDependencies: @@ -3110,7 +3109,7 @@ __metadata: optional: true lightningcss: optional: true - checksum: 10c0/607258ea16b753b42cbcf88b0b20c99406d7f162ad3a4da50ec3e23d1fb652d1304815c0d0c577944256c76dff3df64e1708e5c5e255318694ba8aaba7820ca3 + checksum: 10c0/da12f3214220cec8240af7206477f1d1a0036943b704ae36c75617bda8f1905b4d0f71869eda70dfbb184d3e200be541fc5b8b65781294bde71ede1660406a5b languageName: node linkType: hard @@ -3238,7 +3237,7 @@ __metadata: languageName: node linkType: hard -"cssnano@npm:^7.0.1": +"cssnano@npm:^7.0.4": version: 7.0.6 resolution: "cssnano@npm:7.0.6" dependencies: @@ -3463,14 +3462,14 @@ __metadata: languageName: node linkType: hard -"eslint-config-prettier@npm:10.0.1": - version: 10.0.1 - resolution: "eslint-config-prettier@npm:10.0.1" +"eslint-config-prettier@npm:10.1.1": + version: 10.1.1 + resolution: "eslint-config-prettier@npm:10.1.1" peerDependencies: eslint: ">=7.0.0" bin: - eslint-config-prettier: build/bin/cli.js - checksum: 10c0/e2434931669d211663c0493f2c1640a670a02ba4503a68f056a7eda133f383acbbb983a4a7bd0ad6cb3b2bc4d5731c3be8b32fe28e35087a76fea45f7061ae70 + eslint-config-prettier: bin/cli.js + checksum: 10c0/3dbfdf6495dd62e2e1644ea9e8e978100dabcd8740fd264df1222d130001a1e8de05d6ed6c67d3a60727386a07507f067d1ca79af6d546910414beab19e7966e languageName: node linkType: hard @@ -3491,13 +3490,13 @@ __metadata: languageName: node linkType: hard -"eslint-scope@npm:^8.2.0": - version: 8.2.0 - resolution: "eslint-scope@npm:8.2.0" +"eslint-scope@npm:^8.3.0": + version: 8.3.0 + resolution: "eslint-scope@npm:8.3.0" dependencies: esrecurse: "npm:^4.3.0" estraverse: "npm:^5.2.0" - checksum: 10c0/8d2d58e2136d548ac7e0099b1a90d9fab56f990d86eb518de1247a7066d38c908be2f3df477a79cf60d70b30ba18735d6c6e70e9914dca2ee515a729975d70d6 + checksum: 10c0/23bf54345573201fdf06d29efa345ab508b355492f6c6cc9e2b9f6d02b896f369b6dd5315205be94b8853809776c4d13353b85c6b531997b164ff6c3328ecf5b languageName: node linkType: hard @@ -3515,16 +3514,17 @@ __metadata: languageName: node linkType: hard -"eslint@npm:9.21.0": - version: 9.21.0 - resolution: "eslint@npm:9.21.0" +"eslint@npm:9.22.0": + version: 9.22.0 + resolution: "eslint@npm:9.22.0" dependencies: "@eslint-community/eslint-utils": "npm:^4.2.0" "@eslint-community/regexpp": "npm:^4.12.1" "@eslint/config-array": "npm:^0.19.2" + "@eslint/config-helpers": "npm:^0.1.0" "@eslint/core": "npm:^0.12.0" "@eslint/eslintrc": "npm:^3.3.0" - "@eslint/js": "npm:9.21.0" + "@eslint/js": "npm:9.22.0" "@eslint/plugin-kit": "npm:^0.2.7" "@humanfs/node": "npm:^0.16.6" "@humanwhocodes/module-importer": "npm:^1.0.1" @@ -3536,7 +3536,7 @@ __metadata: cross-spawn: "npm:^7.0.6" debug: "npm:^4.3.2" escape-string-regexp: "npm:^4.0.0" - eslint-scope: "npm:^8.2.0" + eslint-scope: "npm:^8.3.0" eslint-visitor-keys: "npm:^4.2.0" espree: "npm:^10.3.0" esquery: "npm:^1.5.0" @@ -3560,7 +3560,7 @@ __metadata: optional: true bin: eslint: bin/eslint.js - checksum: 10c0/558edb25b440cd51825d66fed3e84f1081bd6f4cb2cf994e60ece4c5978fa0583e88b75faf187c1fc21688c4ff7072f12bf5f6d1be1e09a4d6af78cff39dc520 + checksum: 10c0/7b5ab6f2365971c16efe97349565f75d8343347562fb23f12734c6ab2cd5e35301373a0d51e194789ddcfdfca21db7b62ff481b03d524b8169896c305b65ff48 languageName: node linkType: hard @@ -3702,12 +3702,12 @@ __metadata: languageName: node linkType: hard -"file-entry-cache@npm:^10.0.5": - version: 10.0.5 - resolution: "file-entry-cache@npm:10.0.5" +"file-entry-cache@npm:^10.0.6": + version: 10.0.6 + resolution: "file-entry-cache@npm:10.0.6" dependencies: - flat-cache: "npm:^6.1.5" - checksum: 10c0/c7d9bd537d1862db5357ecc688a22dc9287c2590e9eba04e85e0601d6143905eaa5edc5534f1dd92e24713f230ee69d99e150d7b6120c6d940f40beccdd5caf3 + flat-cache: "npm:^6.1.6" + checksum: 10c0/4e7226a5dbe7b5130c848c5fd3a352bb16e4ddb1de10cb4b3ea8375f6ab6085ed10da4db2db8119c61fc7e56fc59a40eeb837a4ae1a3a7c8357a17e69004f113 languageName: node linkType: hard @@ -3729,16 +3729,6 @@ __metadata: languageName: node linkType: hard -"find-cache-dir@npm:^4.0.0": - version: 4.0.0 - resolution: "find-cache-dir@npm:4.0.0" - dependencies: - common-path-prefix: "npm:^3.0.0" - pkg-dir: "npm:^7.0.0" - checksum: 10c0/0faa7956974726c8769671de696d24c643ca1e5b8f7a2401283caa9e07a5da093293e0a0f4bd18c920ec981d2ef945c7f5b946cde268dfc9077d833ad0293cff - languageName: node - linkType: hard - "find-up@npm:^4.0.0": version: 4.1.0 resolution: "find-up@npm:4.1.0" @@ -3759,16 +3749,6 @@ __metadata: languageName: node linkType: hard -"find-up@npm:^6.3.0": - version: 6.3.0 - resolution: "find-up@npm:6.3.0" - dependencies: - locate-path: "npm:^7.1.0" - path-exists: "npm:^5.0.0" - checksum: 10c0/07e0314362d316b2b13f7f11ea4692d5191e718ca3f7264110127520f3347996349bf9e16805abae3e196805814bc66ef4bff2b8904dc4a6476085fc9b0eba07 - languageName: node - linkType: hard - "flat-cache@npm:^4.0.0": version: 4.0.1 resolution: "flat-cache@npm:4.0.1" @@ -3779,14 +3759,14 @@ __metadata: languageName: node linkType: hard -"flat-cache@npm:^6.1.5": - version: 6.1.5 - resolution: "flat-cache@npm:6.1.5" +"flat-cache@npm:^6.1.6": + version: 6.1.6 + resolution: "flat-cache@npm:6.1.6" dependencies: - cacheable: "npm:^1.8.7" + cacheable: "npm:^1.8.8" flatted: "npm:^3.3.2" - hookified: "npm:^1.6.0" - checksum: 10c0/b107edc1c24a8fabe89645001fb8b1479016177d92be45dc6ba739e688131fe35fffbf13516d2f7bb3dc5c1256ff8721f16ec1858c4ee77e46f93c9093c53689 + hookified: "npm:^1.7.0" + checksum: 10c0/2aeba555b61d32d7f0803e6b6b3ba959610cdc0e5b591ed0f80a3ad70c4e80e81afb6853c495cafdcbc3a02386d76a1522babcf04e50c4a1e81df2decfd02e9f languageName: node linkType: hard @@ -4071,10 +4051,10 @@ __metadata: languageName: node linkType: hard -"hookified@npm:^1.6.0": - version: 1.6.0 - resolution: "hookified@npm:1.6.0" - checksum: 10c0/00ae9502315a21b559c2d057e02d1418a812e015df701b89f590ddbce135e422058e6e97b1047ef4b0b96477bf08e36f3f572fe247c8f3370a126555800ae46f +"hookified@npm:^1.7.0": + version: 1.7.1 + resolution: "hookified@npm:1.7.1" + checksum: 10c0/779cb2f912d19f9cf00ec081d2fb07068553093e8cfaab7fb536b45a04b5743ac836e7fd4d09f1473f82c105338aa2539a104e5fb28e55f4ec1ce0be28ea9acc languageName: node linkType: hard @@ -4372,13 +4352,13 @@ __metadata: "@babel/cli": "npm:7.26.4" "@babel/core": "npm:7.26.9" "@babel/preset-env": "npm:7.26.9" - "@eslint/js": "npm:9.21.0" - babel-loader: "npm:9.2.1" + "@eslint/js": "npm:9.22.0" + babel-loader: "npm:10.0.0" clean-webpack-plugin: "npm:4.0.0" css-loader: "npm:7.1.2" - css-minimizer-webpack-plugin: "npm:7.0.0" - eslint: "npm:9.21.0" - eslint-config-prettier: "npm:10.0.1" + css-minimizer-webpack-plugin: "npm:7.0.2" + eslint: "npm:9.22.0" + eslint-config-prettier: "npm:10.1.1" eslint-formatter-checkstyle: "npm:8.40.0" globals: "npm:16.0.0" handlebars: "npm:4.7.8" @@ -4389,14 +4369,14 @@ __metadata: mini-css-extract-plugin: "npm:2.9.2" postcss: "npm:8.5.3" postcss-loader: "npm:8.1.1" - postcss-preset-env: "npm:10.1.4" + postcss-preset-env: "npm:10.1.5" postcss-scss: "npm:4.0.9" - prettier: "npm:3.5.1" - sass: "npm:1.85.0" + prettier: "npm:3.5.3" + sass: "npm:1.85.1" sass-loader: "npm:16.0.5" sortablejs: "npm:1.15.6" style-loader: "npm:4.0.0" - stylelint: "npm:16.14.1" + stylelint: "npm:16.15.0" stylelint-checkstyle-reporter: "npm:1.0.0" stylelint-config-standard-scss: "npm:14.0.0" tippy.js: "npm:6.3.7" @@ -4662,15 +4642,6 @@ __metadata: languageName: node linkType: hard -"locate-path@npm:^7.1.0": - version: 7.2.0 - resolution: "locate-path@npm:7.2.0" - dependencies: - p-locate: "npm:^6.0.0" - checksum: 10c0/139e8a7fe11cfbd7f20db03923cacfa5db9e14fa14887ea121345597472b4a63c1a42a8a5187defeeff6acf98fd568da7382aa39682d38f0af27433953a97751 - languageName: node - linkType: hard - "lodash.debounce@npm:^4.0.8": version: 4.0.8 resolution: "lodash.debounce@npm:4.0.8" @@ -5118,15 +5089,6 @@ __metadata: languageName: node linkType: hard -"p-limit@npm:^4.0.0": - version: 4.0.0 - resolution: "p-limit@npm:4.0.0" - dependencies: - yocto-queue: "npm:^1.0.0" - checksum: 10c0/a56af34a77f8df2ff61ddfb29431044557fcbcb7642d5a3233143ebba805fc7306ac1d448de724352861cb99de934bc9ab74f0d16fe6a5460bdbdf938de875ad - languageName: node - linkType: hard - "p-locate@npm:^4.1.0": version: 4.1.0 resolution: "p-locate@npm:4.1.0" @@ -5145,15 +5107,6 @@ __metadata: languageName: node linkType: hard -"p-locate@npm:^6.0.0": - version: 6.0.0 - resolution: "p-locate@npm:6.0.0" - dependencies: - p-limit: "npm:^4.0.0" - checksum: 10c0/d72fa2f41adce59c198270aa4d3c832536c87a1806e0f69dffb7c1a7ca998fb053915ca833d90f166a8c082d3859eabfed95f01698a3214c20df6bb8de046312 - languageName: node - linkType: hard - "p-map@npm:^2.0.0": version: 2.1.0 resolution: "p-map@npm:2.1.0" @@ -5210,13 +5163,6 @@ __metadata: languageName: node linkType: hard -"path-exists@npm:^5.0.0": - version: 5.0.0 - resolution: "path-exists@npm:5.0.0" - checksum: 10c0/b170f3060b31604cde93eefdb7392b89d832dfbc1bed717c9718cbe0f230c1669b7e75f87e19901da2250b84d092989a0f9e44d2ef41deb09aa3ad28e691a40a - languageName: node - linkType: hard - "path-is-absolute@npm:^1.0.0": version: 1.0.1 resolution: "path-is-absolute@npm:1.0.1" @@ -5315,15 +5261,6 @@ __metadata: languageName: node linkType: hard -"pkg-dir@npm:^7.0.0": - version: 7.0.0 - resolution: "pkg-dir@npm:7.0.0" - dependencies: - find-up: "npm:^6.3.0" - checksum: 10c0/1afb23d2efb1ec9d8b2c4a0c37bf146822ad2774f074cb05b853be5dca1b40815c5960dd126df30ab8908349262a266f31b771e877235870a3b8fd313beebec5 - languageName: node - linkType: hard - "postcss-attribute-case-insensitive@npm:^7.0.1": version: 7.0.1 resolution: "postcss-attribute-case-insensitive@npm:7.0.1" @@ -5358,18 +5295,18 @@ __metadata: languageName: node linkType: hard -"postcss-color-functional-notation@npm:^7.0.7": - version: 7.0.7 - resolution: "postcss-color-functional-notation@npm:7.0.7" +"postcss-color-functional-notation@npm:^7.0.8": + version: 7.0.8 + resolution: "postcss-color-functional-notation@npm:7.0.8" dependencies: - "@csstools/css-color-parser": "npm:^3.0.7" + "@csstools/css-color-parser": "npm:^3.0.8" "@csstools/css-parser-algorithms": "npm:^3.0.4" "@csstools/css-tokenizer": "npm:^3.0.3" "@csstools/postcss-progressive-custom-properties": "npm:^4.0.0" "@csstools/utilities": "npm:^2.0.0" peerDependencies: postcss: ^8.4 - checksum: 10c0/d9f64abb54da1e2e2641d68548e5fe3685e1b5f85cd39059f1e9312f9c897eef80a76d1e9b9271d4700a5954fc0c0b6494fc8ed4a25fe64a25525b3973be4a36 + checksum: 10c0/4180e2f6ee9c925d6c47e727cfc50de2186d4a5cfda6e1ccf28f60e5536b418ddd90f9cc5f9cbcd1900f74098101bca8f844867e16b591e66760300e34257e47 languageName: node linkType: hard @@ -5580,18 +5517,18 @@ __metadata: languageName: node linkType: hard -"postcss-lab-function@npm:^7.0.7": - version: 7.0.7 - resolution: "postcss-lab-function@npm:7.0.7" +"postcss-lab-function@npm:^7.0.8": + version: 7.0.8 + resolution: "postcss-lab-function@npm:7.0.8" dependencies: - "@csstools/css-color-parser": "npm:^3.0.7" + "@csstools/css-color-parser": "npm:^3.0.8" "@csstools/css-parser-algorithms": "npm:^3.0.4" "@csstools/css-tokenizer": "npm:^3.0.3" "@csstools/postcss-progressive-custom-properties": "npm:^4.0.0" "@csstools/utilities": "npm:^2.0.0" peerDependencies: postcss: ^8.4 - checksum: 10c0/bee0f50c3f59da04b219b528cdd63b8f2600fd9bbaa02ba101593f29f4cd090c25acc40254a41a11e3db221cc98b7a1b2600fd3abe3065262c2cb5c7501a3dba + checksum: 10c0/5f7b6f95cb3d1aa099c16dcdd89c575f112387600f30949f74c205e0846c9303ca851be794fad9fd56825859d38ac811f972cc34bbc2dfcf71371c640165ddfb languageName: node linkType: hard @@ -5615,14 +5552,14 @@ __metadata: languageName: node linkType: hard -"postcss-logical@npm:^8.0.0": - version: 8.0.0 - resolution: "postcss-logical@npm:8.0.0" +"postcss-logical@npm:^8.1.0": + version: 8.1.0 + resolution: "postcss-logical@npm:8.1.0" dependencies: postcss-value-parser: "npm:^4.2.0" peerDependencies: postcss: ^8.4 - checksum: 10c0/2caa04e45227ab9dec728416ccde47514e1c347ee72aac58e13ecee3bc7fbc8b53e3fe4f1e2e4396432feb1d54e70a1f06ec5a74d60e84bafff05ab82f196475 + checksum: 10c0/0e2e9e901d8a550db7f682d46b1f7e4f363c1ada061dc8e4548e2b563c5e39f3684a2d7c3f11fe061188782bca37874e34967fc6179fa6d98a49ff66a0076d27 languageName: node linkType: hard @@ -5915,19 +5852,19 @@ __metadata: languageName: node linkType: hard -"postcss-preset-env@npm:10.1.4": - version: 10.1.4 - resolution: "postcss-preset-env@npm:10.1.4" +"postcss-preset-env@npm:10.1.5": + version: 10.1.5 + resolution: "postcss-preset-env@npm:10.1.5" dependencies: "@csstools/postcss-cascade-layers": "npm:^5.0.1" - "@csstools/postcss-color-function": "npm:^4.0.7" - "@csstools/postcss-color-mix-function": "npm:^3.0.7" + "@csstools/postcss-color-function": "npm:^4.0.8" + "@csstools/postcss-color-mix-function": "npm:^3.0.8" "@csstools/postcss-content-alt-text": "npm:^2.0.4" - "@csstools/postcss-exponential-functions": "npm:^2.0.6" + "@csstools/postcss-exponential-functions": "npm:^2.0.7" "@csstools/postcss-font-format-keywords": "npm:^4.0.0" - "@csstools/postcss-gamut-mapping": "npm:^2.0.7" - "@csstools/postcss-gradients-interpolation-method": "npm:^5.0.7" - "@csstools/postcss-hwb-function": "npm:^4.0.7" + "@csstools/postcss-gamut-mapping": "npm:^2.0.8" + "@csstools/postcss-gradients-interpolation-method": "npm:^5.0.8" + "@csstools/postcss-hwb-function": "npm:^4.0.8" "@csstools/postcss-ic-unit": "npm:^4.0.0" "@csstools/postcss-initial": "npm:^2.0.1" "@csstools/postcss-is-pseudo-class": "npm:^5.0.1" @@ -5937,19 +5874,19 @@ __metadata: "@csstools/postcss-logical-overscroll-behavior": "npm:^2.0.0" "@csstools/postcss-logical-resize": "npm:^3.0.0" "@csstools/postcss-logical-viewport-units": "npm:^3.0.3" - "@csstools/postcss-media-minmax": "npm:^2.0.6" + "@csstools/postcss-media-minmax": "npm:^2.0.7" "@csstools/postcss-media-queries-aspect-ratio-number-values": "npm:^3.0.4" "@csstools/postcss-nested-calc": "npm:^4.0.0" "@csstools/postcss-normalize-display-values": "npm:^4.0.0" - "@csstools/postcss-oklab-function": "npm:^4.0.7" + "@csstools/postcss-oklab-function": "npm:^4.0.8" "@csstools/postcss-progressive-custom-properties": "npm:^4.0.0" - "@csstools/postcss-random-function": "npm:^1.0.2" - "@csstools/postcss-relative-color-syntax": "npm:^3.0.7" + "@csstools/postcss-random-function": "npm:^1.0.3" + "@csstools/postcss-relative-color-syntax": "npm:^3.0.8" "@csstools/postcss-scope-pseudo-class": "npm:^4.0.1" - "@csstools/postcss-sign-functions": "npm:^1.1.1" - "@csstools/postcss-stepped-value-functions": "npm:^4.0.6" - "@csstools/postcss-text-decoration-shorthand": "npm:^4.0.1" - "@csstools/postcss-trigonometric-functions": "npm:^4.0.6" + "@csstools/postcss-sign-functions": "npm:^1.1.2" + "@csstools/postcss-stepped-value-functions": "npm:^4.0.7" + "@csstools/postcss-text-decoration-shorthand": "npm:^4.0.2" + "@csstools/postcss-trigonometric-functions": "npm:^4.0.7" "@csstools/postcss-unset-value": "npm:^4.0.0" autoprefixer: "npm:^10.4.19" browserslist: "npm:^4.24.4" @@ -5959,7 +5896,7 @@ __metadata: cssdb: "npm:^8.2.3" postcss-attribute-case-insensitive: "npm:^7.0.1" postcss-clamp: "npm:^4.1.0" - postcss-color-functional-notation: "npm:^7.0.7" + postcss-color-functional-notation: "npm:^7.0.8" postcss-color-hex-alpha: "npm:^10.0.0" postcss-color-rebeccapurple: "npm:^10.0.0" postcss-custom-media: "npm:^11.0.5" @@ -5972,8 +5909,8 @@ __metadata: postcss-font-variant: "npm:^5.0.0" postcss-gap-properties: "npm:^6.0.0" postcss-image-set-function: "npm:^7.0.0" - postcss-lab-function: "npm:^7.0.7" - postcss-logical: "npm:^8.0.0" + postcss-lab-function: "npm:^7.0.8" + postcss-logical: "npm:^8.1.0" postcss-nesting: "npm:^13.0.1" postcss-opacity-percentage: "npm:^3.0.0" postcss-overflow-shorthand: "npm:^6.0.0" @@ -5984,7 +5921,7 @@ __metadata: postcss-selector-not: "npm:^8.0.1" peerDependencies: postcss: ^8.4 - checksum: 10c0/bd3ad0a32fa30e53f4fdfb980b77d4e86a53ad40681dd8f7d2d9fb00728730b426cd7d039b57f0849261014e4c9e5a55f72edfda1c70165c96a52f246d273ebb + checksum: 10c0/5ed5aeb7c9718230742a56d9b49e05a90135bc4bb77f97d9978bdb0b999d36a2d6175d99360c966cb7a307c9efe4b8792f4c0b79ec99a233f9e1c1ebae4244f0 languageName: node linkType: hard @@ -6077,13 +6014,13 @@ __metadata: languageName: node linkType: hard -"postcss-selector-parser@npm:^7.0.0": - version: 7.0.0 - resolution: "postcss-selector-parser@npm:7.0.0" +"postcss-selector-parser@npm:^7.0.0, postcss-selector-parser@npm:^7.1.0": + version: 7.1.0 + resolution: "postcss-selector-parser@npm:7.1.0" dependencies: cssesc: "npm:^3.0.0" util-deprecate: "npm:^1.0.2" - checksum: 10c0/e96e096afcce70bf5c97789f5ea09d7415ae5eb701d82b05b5e8532885d31363b484fcb1ca9488c9a331f30508d9e5bb6c3109eb2eb5067ef3d3919f9928cd9d + checksum: 10c0/0fef257cfd1c0fe93c18a3f8a6e739b4438b527054fd77e9a62730a89b2d0ded1b59314a7e4aaa55bc256204f40830fecd2eb50f20f8cb7ab3a10b52aa06c8aa languageName: node linkType: hard @@ -6117,7 +6054,7 @@ __metadata: languageName: node linkType: hard -"postcss@npm:8.5.3, postcss@npm:^8.4.33, postcss@npm:^8.4.38, postcss@npm:^8.5.1": +"postcss@npm:8.5.3, postcss@npm:^8.4.33, postcss@npm:^8.4.40, postcss@npm:^8.5.3": version: 8.5.3 resolution: "postcss@npm:8.5.3" dependencies: @@ -6135,12 +6072,12 @@ __metadata: languageName: node linkType: hard -"prettier@npm:3.5.1": - version: 3.5.1 - resolution: "prettier@npm:3.5.1" +"prettier@npm:3.5.3": + version: 3.5.3 + resolution: "prettier@npm:3.5.3" bin: prettier: bin/prettier.cjs - checksum: 10c0/9f6f810eae455d6e4213845151a484a2338f2e0d6a8b84ee8e13a83af8a2421ef6c1e31e61e4b135671fb57b9541f6624648880cc2061ac803e243ac898c0123 + checksum: 10c0/3880cb90b9dc0635819ab52ff571518c35bd7f15a6e80a2054c05dbc8a3aa6e74f135519e91197de63705bcb38388ded7e7230e2178432a1468005406238b877 languageName: node linkType: hard @@ -6414,9 +6351,9 @@ __metadata: languageName: node linkType: hard -"sass@npm:1.85.0": - version: 1.85.0 - resolution: "sass@npm:1.85.0" +"sass@npm:1.85.1": + version: 1.85.1 + resolution: "sass@npm:1.85.1" dependencies: "@parcel/watcher": "npm:^2.4.1" chokidar: "npm:^4.0.0" @@ -6427,7 +6364,7 @@ __metadata: optional: true bin: sass: sass.js - checksum: 10c0/a1af0c0596ae1904f66337d0c70a684db6e12210f97be4326cc3dcf18b0f956d7bc45ab2bcc7a8422d433d3eb3c9cb2cc8e60b2dafbdd01fb1ae5a23f5424690 + checksum: 10c0/f843aa1df1dca2f0e9cb2fb247e4939fd514ae4c182cdd1900a0622c0d71b40dfb1c4225f78b78e165a318287ca137ec597695db3e496408bd16a921a2bc2b3f languageName: node linkType: hard @@ -6769,9 +6706,9 @@ __metadata: languageName: node linkType: hard -"stylelint@npm:16.14.1": - version: 16.14.1 - resolution: "stylelint@npm:16.14.1" +"stylelint@npm:16.15.0": + version: 16.15.0 + resolution: "stylelint@npm:16.15.0" dependencies: "@csstools/css-parser-algorithms": "npm:^3.0.4" "@csstools/css-tokenizer": "npm:^3.0.3" @@ -6786,7 +6723,7 @@ __metadata: debug: "npm:^4.3.7" fast-glob: "npm:^3.3.3" fastest-levenshtein: "npm:^1.0.16" - file-entry-cache: "npm:^10.0.5" + file-entry-cache: "npm:^10.0.6" global-modules: "npm:^2.0.0" globby: "npm:^11.1.0" globjoin: "npm:^0.1.4" @@ -6800,20 +6737,20 @@ __metadata: micromatch: "npm:^4.0.8" normalize-path: "npm:^3.0.0" picocolors: "npm:^1.1.1" - postcss: "npm:^8.5.1" + postcss: "npm:^8.5.3" postcss-resolve-nested-selector: "npm:^0.1.6" postcss-safe-parser: "npm:^7.0.1" - postcss-selector-parser: "npm:^7.0.0" + postcss-selector-parser: "npm:^7.1.0" postcss-value-parser: "npm:^4.2.0" resolve-from: "npm:^5.0.0" string-width: "npm:^4.2.3" - supports-hyperlinks: "npm:^3.1.0" + supports-hyperlinks: "npm:^3.2.0" svg-tags: "npm:^1.0.0" table: "npm:^6.9.0" write-file-atomic: "npm:^5.0.1" bin: stylelint: bin/stylelint.mjs - checksum: 10c0/cce94374dc721d491d955f548ee81ba835d4955fa37d58a11323454f9f3721e5644fa89a04c14f85bdfa12790bdd043a41be2001a99cb0bfe23b38eb933199d7 + checksum: 10c0/32dee221fe8980e08adcdc7c48ddab51eac263784b0cc2f4eb9c029e3589ad6ce7dae710ec2aadb57bdbfecec579326c75918686f1563576715cbd3095c5dd12 languageName: node linkType: hard @@ -6835,13 +6772,13 @@ __metadata: languageName: node linkType: hard -"supports-hyperlinks@npm:^3.1.0": - version: 3.1.0 - resolution: "supports-hyperlinks@npm:3.1.0" +"supports-hyperlinks@npm:^3.2.0": + version: 3.2.0 + resolution: "supports-hyperlinks@npm:3.2.0" dependencies: has-flag: "npm:^4.0.0" supports-color: "npm:^7.0.0" - checksum: 10c0/78cc3e17eb27e6846fa355a8ebf343befe36272899cd409e45317a06c1997e95c23ff99d91080a517bd8c96508d4fa456e6ceb338c02ba5d7544277dbec0f10f + checksum: 10c0/bca527f38d4c45bc95d6a24225944675746c515ddb91e2456d00ae0b5c537658e9dd8155b996b191941b0c19036195a098251304b9082bbe00cd1781f3cd838e languageName: node linkType: hard @@ -7312,10 +7249,3 @@ __metadata: checksum: 10c0/dceb44c28578b31641e13695d200d34ec4ab3966a5729814d5445b194933c096b7ced71494ce53a0e8820685d1d010df8b2422e5bf2cdea7e469d97ffbea306f languageName: node linkType: hard - -"yocto-queue@npm:^1.0.0": - version: 1.1.1 - resolution: "yocto-queue@npm:1.1.1" - checksum: 10c0/cb287fe5e6acfa82690acb43c283de34e945c571a78a939774f6eaba7c285bacdf6c90fbc16ce530060863984c906d2b4c6ceb069c94d1e0a06d5f2b458e2a92 - languageName: node - linkType: hard