From 308479dd767e162e0d8e16018cf19563ff9b6203 Mon Sep 17 00:00:00 2001 From: Francisco Ferrari Bihurriet Date: Wed, 9 Apr 2025 19:13:55 +0200 Subject: [PATCH 1/8] 8352728: InternalError loading java.security due to Windows parent folder permissions --- .../share/classes/java/security/Security.java | 16 ++-- .../ConfigFileTestDirPermissions.java | 87 +++++++++++++++++++ 2 files changed, 98 insertions(+), 5 deletions(-) create mode 100644 test/jdk/java/security/Security/ConfigFileTestDirPermissions.java diff --git a/src/java.base/share/classes/java/security/Security.java b/src/java.base/share/classes/java/security/Security.java index 6969fe8a8e142..277a620bcb449 100644 --- a/src/java.base/share/classes/java/security/Security.java +++ b/src/java.base/share/classes/java/security/Security.java @@ -265,13 +265,19 @@ static void loadInclude(String propFile) { private static void loadFromPath(Path path, LoadingMode mode) throws IOException { boolean isRegularFile = Files.isRegularFile(path); - if (isRegularFile) { - path = path.toRealPath(); - } else if (Files.isDirectory(path)) { + if (!isRegularFile && Files.isDirectory(path)) { throw new IOException("Is a directory"); - } else { - path = path.toAbsolutePath(); } + // For path canonicalization, we prefer + // java.io.File::getCanonicalPath over + // java.nio.file.Path::toRealPath because of the following reasons: + // 1. In Windows, File::getCanonicalPath handles restricted + // permissions in parent directories. Contrarily, + // Path::toRealPath fails with AccessDeniedException. + // 2. In Linux, File::getCanonicalPath handles non-regular files + // (e.g. /dev/stdin). Contrarily, Path::toRealPath fails with + // NoSuchFileException. + path = Path.of(path.toFile().getCanonicalPath()); if (activePaths.contains(path)) { throw new InternalError("Cyclic include of '" + path + "'"); } diff --git a/test/jdk/java/security/Security/ConfigFileTestDirPermissions.java b/test/jdk/java/security/Security/ConfigFileTestDirPermissions.java new file mode 100644 index 0000000000000..111f7e7039109 --- /dev/null +++ b/test/jdk/java/security/Security/ConfigFileTestDirPermissions.java @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2025, Red Hat, Inc. + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import jdk.test.lib.process.ProcessTools; +import jdk.test.lib.util.FileUtils; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.attribute.AclEntry; +import java.nio.file.attribute.AclEntryType; +import java.nio.file.attribute.AclFileAttributeView; +import java.util.List; + +/* + * @test + * @summary Ensures java.security is loadable in Windows, even when the user + * does not have permissions on one of the parent directories. + * @bug 8352728 + * @requires os.family == "windows" + * @library /test/lib + * @run main ConfigFileTestDirPermissions + */ + +public class ConfigFileTestDirPermissions { + public static void main(String[] args) throws Exception { + Path temp = Files.createTempDirectory("JDK-8352728-tmp-"); + try { + // Copy the jdk to a different directory + Path originalJdk = Path.of(System.getProperty("test.jdk")); + Path jdk = temp.resolve("jdk-parent-dir", "jdk"); + Files.createDirectories(jdk); + FileUtils.copyDirectory(originalJdk, jdk); + + // Remove current user permissions from jdk-parent-dir + Path parent = jdk.getParent(); + AclFileAttributeView view = Files.getFileAttributeView(parent, + AclFileAttributeView.class); + List originalAcl = List.copyOf(view.getAcl()); + view.setAcl(List.of(AclEntry.newBuilder().setType(AclEntryType.DENY) + .setPrincipal(Files.getOwner(parent)).build())); + + try { + // Make sure the permissions are affecting the current user + try { + jdk.toRealPath(); + throw new jtreg.SkippedException("Must run non-elevated!"); + } catch (IOException expected) { } + + // Execute the copied jdk, ensuring java.security.Security is + // loaded (i.e. use -XshowSettings:security:properties) + ProcessTools.executeProcess(new ProcessBuilder( + List.of(jdk.resolve("bin", "java.exe").toString(), + "-Djava.security.debug=properties", + "-XshowSettings:security:properties", + "-version"))).shouldHaveExitValue(0); + } finally { + view.setAcl(originalAcl); + } + } finally { + FileUtils.deleteFileTreeUnchecked(temp); + } + + System.out.println("TEST PASS - OK"); + } +} From a8c5ca745298a7efdef23bc7416bf2aacd284143 Mon Sep 17 00:00:00 2001 From: Francisco Ferrari Bihurriet Date: Tue, 15 Apr 2025 20:31:58 +0200 Subject: [PATCH 2/8] Do minor adjustments Update copyright year, improve comments and use File::toPath to convert back to Path. --- .../share/classes/java/security/Security.java | 21 +++++++++---------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/src/java.base/share/classes/java/security/Security.java b/src/java.base/share/classes/java/security/Security.java index 277a620bcb449..1675d69bd4af2 100644 --- a/src/java.base/share/classes/java/security/Security.java +++ b/src/java.base/share/classes/java/security/Security.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -253,6 +253,10 @@ static void loadInclude(String propFile) { "from a non-regular properties file " + "(e.g. HTTP served file)"); } + // Inside loadFromPath, we have performed symlinks + // resolution on currentFile under the rationale that + // the original file writer is the one who decided + // where the relative includes should resolve. path = currentPath.resolveSibling(path); } loadFromPath(path, LoadingMode.APPEND); @@ -268,16 +272,11 @@ private static void loadFromPath(Path path, LoadingMode mode) if (!isRegularFile && Files.isDirectory(path)) { throw new IOException("Is a directory"); } - // For path canonicalization, we prefer - // java.io.File::getCanonicalPath over - // java.nio.file.Path::toRealPath because of the following reasons: - // 1. In Windows, File::getCanonicalPath handles restricted - // permissions in parent directories. Contrarily, - // Path::toRealPath fails with AccessDeniedException. - // 2. In Linux, File::getCanonicalPath handles non-regular files - // (e.g. /dev/stdin). Contrarily, Path::toRealPath fails with - // NoSuchFileException. - path = Path.of(path.toFile().getCanonicalPath()); + // JDK-8352728: we prefer java.io.File::getCanonicalFile over + // java.nio.file.Path::toRealPath because the former is more + // fault-tolerant, since the canonical form of a pathname is + // specified to exist even for nonexistent/inaccessible files. + path = path.toFile().getCanonicalFile().toPath(); if (activePaths.contains(path)) { throw new InternalError("Cyclic include of '" + path + "'"); } From 7abb62c069ad35f4ec34f6cd9b9f6d05febceecc Mon Sep 17 00:00:00 2001 From: Francisco Ferrari Bihurriet Date: Fri, 25 Apr 2025 01:09:34 +0200 Subject: [PATCH 3/8] Reintroduce links test using directory junctions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Junctions do not require elevation, so this is a way of testing soft-links are resolved without requiring elevation. This is useful because we need to avoid elevation in order to reproduce the parent directories permission issue. This is testing directories structure: 📁 JDK-8352728-tmp-*/ ├─🔒 jdk-parent-dir/ (ACL with REMOVED-PERMISSIONS) │ └─📁 jdk/ │ ├─📁 conf/ │ │ ├─📁 security/ │ │ │ ├─📄 java.security │ │ │ │ 📝 include link-to-other-dir/other.properties │ │ │ ├─🔗 link-to-other-dir/ ⟹ 📁 JDK-8352728-tmp-*/other-dir │ │ │ └─... (JUNCTION) │ │ └─... │ └─... ├─📁 other-dir/ │ └─📄 other.properties │ 📝 include ../relatively.included.properties └─📄 relatively.included.properties 📝 test.property.name=test_property_value --- .../ConfigFileTestDirPermissions.java | 68 +++++++++++++++++-- 1 file changed, 64 insertions(+), 4 deletions(-) diff --git a/test/jdk/java/security/Security/ConfigFileTestDirPermissions.java b/test/jdk/java/security/Security/ConfigFileTestDirPermissions.java index 111f7e7039109..754507791f0bb 100644 --- a/test/jdk/java/security/Security/ConfigFileTestDirPermissions.java +++ b/test/jdk/java/security/Security/ConfigFileTestDirPermissions.java @@ -33,10 +33,13 @@ import java.nio.file.attribute.AclFileAttributeView; import java.util.List; +import static java.nio.file.StandardOpenOption.APPEND; + /* * @test - * @summary Ensures java.security is loadable in Windows, even when the user - * does not have permissions on one of the parent directories. + * @summary Ensures java.security is loadable in Windows and filesystem + * soft-links are resolved, even when the user does not have permissions + * on one of the parent directories. * @bug 8352728 * @requires os.family == "windows" * @library /test/lib @@ -44,8 +47,44 @@ */ public class ConfigFileTestDirPermissions { + private static final String LF = System.lineSeparator(); + private static final String TEST_PROPERTY = + "test.property.name=test_property_value"; + + // Unlike symbolic links, directory junctions do not require elevation + private static void createJunction(Path target, Path link) + throws IOException, InterruptedException { + if (!Files.isDirectory(target)) { + throw new IOException("The target must be a directory: " + target); + } + int exitCode = + new ProcessBuilder("cmd", "/c", "MKLINK", "/J", link.toString(), + target.toString()).inheritIO().start().waitFor(); + if (exitCode != 0) { + throw new IOException("Unexpected exit code: " + exitCode); + } + } + public static void main(String[] args) throws Exception { Path temp = Files.createTempDirectory("JDK-8352728-tmp-"); + // We will create the following directories structure: + // + // 📁 JDK-8352728-tmp-*/ + // ├─🔒 jdk-parent-dir/ (ACL with REMOVED-PERMISSIONS) + // │ └─📁 jdk/ + // │ ├─📁 conf/ + // │ │ ├─📁 security/ + // │ │ │ ├─📄 java.security + // │ │ │ │ 📝 include link-to-other-dir/other.properties + // │ │ │ ├─🔗 link-to-other-dir/ ⟹ 📁 JDK-8352728-tmp-*/other-dir + // │ │ │ └─... (JUNCTION) + // │ │ └─... + // │ └─... + // ├─📁 other-dir/ + // │ └─📄 other.properties + // │ 📝 include ../relatively.included.properties + // └─📄 relatively.included.properties + // 📝 test.property.name=test_property_value try { // Copy the jdk to a different directory Path originalJdk = Path.of(System.getProperty("test.jdk")); @@ -53,6 +92,25 @@ public static void main(String[] args) throws Exception { Files.createDirectories(jdk); FileUtils.copyDirectory(originalJdk, jdk); + // Create a properties file with a relative include in it + Path otherDir = temp.resolve("other-dir"); + Files.createDirectories(otherDir); + Path other = otherDir.resolve("other.properties"); + Path included = temp.resolve("relatively.included.properties"); + Files.writeString(included, TEST_PROPERTY + LF); + Files.writeString(other, + "include ../" + included.getFileName() + LF); + + // Create a junction to the properties file dir, from the jdk dir + Path javaSec = jdk.resolve("conf", "security", "java.security"); + Path linkDir = javaSec.resolveSibling("link-to-other-dir"); + createJunction(otherDir, linkDir); + + // Include the properties file from java.security (through the link) + Files.writeString(javaSec, + LF + "include " + linkDir.getFileName() + "/" + + other.getFileName() + LF, APPEND); + // Remove current user permissions from jdk-parent-dir Path parent = jdk.getParent(); AclFileAttributeView view = Files.getFileAttributeView(parent, @@ -71,10 +129,12 @@ public static void main(String[] args) throws Exception { // Execute the copied jdk, ensuring java.security.Security is // loaded (i.e. use -XshowSettings:security:properties) ProcessTools.executeProcess(new ProcessBuilder( - List.of(jdk.resolve("bin", "java.exe").toString(), + jdk.resolve("bin", "java.exe").toString(), "-Djava.security.debug=properties", "-XshowSettings:security:properties", - "-version"))).shouldHaveExitValue(0); + "-version")) + .shouldHaveExitValue(0) + .shouldContain(TEST_PROPERTY); } finally { view.setAcl(originalAcl); } From 35933475549a36b19ce196410d93450d48471cba Mon Sep 17 00:00:00 2001 From: Francisco Ferrari Bihurriet Date: Thu, 24 Jul 2025 01:54:48 +0200 Subject: [PATCH 4/8] Fix typo --- src/java.base/share/classes/java/security/Security.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/java.base/share/classes/java/security/Security.java b/src/java.base/share/classes/java/security/Security.java index 1675d69bd4af2..384f9fc99b887 100644 --- a/src/java.base/share/classes/java/security/Security.java +++ b/src/java.base/share/classes/java/security/Security.java @@ -254,7 +254,7 @@ static void loadInclude(String propFile) { "(e.g. HTTP served file)"); } // Inside loadFromPath, we have performed symlinks - // resolution on currentFile under the rationale that + // resolution on currentPath under the rationale that // the original file writer is the one who decided // where the relative includes should resolve. path = currentPath.resolveSibling(path); From 71718e522feaf8e6404c526588b400d3073b26c2 Mon Sep 17 00:00:00 2001 From: Francisco Ferrari Bihurriet Date: Mon, 28 Jul 2025 19:26:21 +0200 Subject: [PATCH 5/8] Introduce a shell test for anonymous pipes This use case has been discussed and analyzed in the pull request, but we didn't have a test case for it. By introducing a test, we make sure we don't have regressions in this area, regardless of the alternative we choose to advance with for this fix. --- .../Security/ConfigFileTestAnonymousPipes.sh | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 test/jdk/java/security/Security/ConfigFileTestAnonymousPipes.sh diff --git a/test/jdk/java/security/Security/ConfigFileTestAnonymousPipes.sh b/test/jdk/java/security/Security/ConfigFileTestAnonymousPipes.sh new file mode 100644 index 0000000000000..518a304ba65f4 --- /dev/null +++ b/test/jdk/java/security/Security/ConfigFileTestAnonymousPipes.sh @@ -0,0 +1,61 @@ +# +# Copyright (c) 2025, Red Hat, Inc. +# +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +# or visit www.oracle.com if you need additional information or have any +# questions. +# + +# @test +# @summary Ensures the java executable is able to load extra security +# properties files from anonymous pipes (non-regular files). +# @bug 8352728 +# @requires os.family == "linux" +# @run shell/timeout=30 ConfigFileTestAnonymousPipes.sh + +if [ -z "${TESTJAVA}" ]; then + JAVA=java +else + JAVA="${TESTJAVA}/bin/java" +fi + +TEST_PROP="ConfigFileTestAnonymousPipes.property.name=PROPERTY_VALUE" + +function check_java() { + local java_output java_exit_code + java_output="$("${JAVA}" ${TESTVMOPTS} ${TESTJAVAOPTS} -XshowSettings:security:properties \ + -Djava.security.debug=properties "$@" -version 2>&1)" + java_exit_code=$? + if [ ${java_exit_code} -ne 0 ] || ! grep -qF "${TEST_PROP}" <<<"${java_output}"; then + echo "TEST FAILED (java exit code: ${java_exit_code})" + echo "${java_output}" + exit 1 + fi +} + +# https://www.gnu.org/software/bash/manual/bash.html#Pipelines +echo "Extra properties from pipeline" +echo "${TEST_PROP}" | check_java -Djava.security.properties=/dev/stdin || exit 1 + +# https://www.gnu.org/software/bash/manual/bash.html#Process-Substitution +echo "Extra properties from process-substitution, include other from pipeline" +echo "${TEST_PROP}" | check_java -Djava.security.properties=<(echo "include /dev/stdin") || exit 1 + +echo "TEST PASS - OK" +exit 0 From a8d865c4985e6660655b27df28e76882855b2087 Mon Sep 17 00:00:00 2001 From: Francisco Ferrari Bihurriet Date: Thu, 30 Oct 2025 21:22:36 +0100 Subject: [PATCH 6/8] Detect cyclic includes with Files::isSameFile MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit checkCyclicInclude() is invoked after we successfully get an InputStream for the path to avoid skipping the same IOException several times inside checkCyclicInclude() if the path doesn't exist. Also, perform symlinks resolution only in the following cases: • When we need to resolve a relative include • For clarity to the user in logging messages • For clarity to the user in exception messages In the first case, the resolution is a requirement, in the last two cases it is a nice-to-have. But given the last two are exceptional cases anyway, we let any resolution error bubble up. --- .../share/classes/java/security/Security.java | 47 +++++++++++++------ 1 file changed, 32 insertions(+), 15 deletions(-) diff --git a/src/java.base/share/classes/java/security/Security.java b/src/java.base/share/classes/java/security/Security.java index 384f9fc99b887..cfae40df77ecc 100644 --- a/src/java.base/share/classes/java/security/Security.java +++ b/src/java.base/share/classes/java/security/Security.java @@ -34,6 +34,7 @@ import java.nio.file.Files; import java.nio.file.InvalidPathException; import java.nio.file.Path; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Enumeration; @@ -112,7 +113,7 @@ private enum LoadingMode {OVERRIDE, APPEND} private static Path currentPath; - private static final Set activePaths = new HashSet<>(); + private static final List activePaths = new ArrayList<>(); static void loadAll() { // first load the master properties file to @@ -253,11 +254,10 @@ static void loadInclude(String propFile) { "from a non-regular properties file " + "(e.g. HTTP served file)"); } - // Inside loadFromPath, we have performed symlinks - // resolution on currentPath under the rationale that - // the original file writer is the one who decided - // where the relative includes should resolve. - path = currentPath.resolveSibling(path); + // We perform symlinks resolution on currentPath under the + // rationale that the original file writer is the one who + // decided where the relative includes should resolve. + path = resolve(currentPath).resolveSibling(path); } loadFromPath(path, LoadingMode.APPEND); } catch (IOException | InvalidPathException e) { @@ -266,21 +266,37 @@ static void loadInclude(String propFile) { } } + private static Path resolve(Path path) { + // JDK-8352728: we prefer java.io.File::getCanonicalFile over + // java.nio.file.Path::toRealPath because the former is more + // fault-tolerant, since the canonical form of a pathname is + // specified to exist even for nonexistent/inaccessible files. + try { + return path.toFile().getCanonicalFile().toPath(); + } catch (IOException e) { + throw new InternalError("Cannot resolve path", e); + } + } + + private static void checkCyclicInclude(Path path) { + for (Path activePath : activePaths) { + try { + if (Files.isSameFile(path, activePath)) { + throw new InternalError( + "Cyclic include of '" + resolve(path) + "'"); + } + } catch (IOException ignore) {} + } + } + private static void loadFromPath(Path path, LoadingMode mode) throws IOException { boolean isRegularFile = Files.isRegularFile(path); if (!isRegularFile && Files.isDirectory(path)) { throw new IOException("Is a directory"); } - // JDK-8352728: we prefer java.io.File::getCanonicalFile over - // java.nio.file.Path::toRealPath because the former is more - // fault-tolerant, since the canonical form of a pathname is - // specified to exist even for nonexistent/inaccessible files. - path = path.toFile().getCanonicalFile().toPath(); - if (activePaths.contains(path)) { - throw new InternalError("Cyclic include of '" + path + "'"); - } try (InputStream is = Files.newInputStream(path)) { + checkCyclicInclude(path); reset(mode); Path previousPath = currentPath; currentPath = isRegularFile ? path : null; @@ -290,7 +306,7 @@ private static void loadFromPath(Path path, LoadingMode mode) props.load(is); debugLoad(false, path); } finally { - activePaths.remove(path); + activePaths.removeLast(); currentPath = previousPath; } } @@ -309,6 +325,7 @@ private static void loadFromUrl(URL url, LoadingMode mode) private static void debugLoad(boolean start, Object source) { if (sdebug != null) { int level = activePaths.isEmpty() ? 1 : activePaths.size(); + source = source instanceof Path path ? resolve(path) : source; sdebug.println((start ? ">".repeat(level) + " starting to process " : "<".repeat(level) + " finished processing ") + source); From 40bc832ef22670a6ba2481fb53d2ebe4b3bbd434 Mon Sep 17 00:00:00 2001 From: Francisco Ferrari Bihurriet Date: Thu, 13 Nov 2025 00:58:47 +0100 Subject: [PATCH 7/8] Remove path resolution from exception message --- src/java.base/share/classes/java/security/Security.java | 2 +- test/jdk/java/security/Security/ConfigFileTest.java | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/java.base/share/classes/java/security/Security.java b/src/java.base/share/classes/java/security/Security.java index cfae40df77ecc..1152849046d22 100644 --- a/src/java.base/share/classes/java/security/Security.java +++ b/src/java.base/share/classes/java/security/Security.java @@ -283,7 +283,7 @@ private static void checkCyclicInclude(Path path) { try { if (Files.isSameFile(path, activePath)) { throw new InternalError( - "Cyclic include of '" + resolve(path) + "'"); + "Cyclic include of '" + path + "'"); } } catch (IOException ignore) {} } diff --git a/test/jdk/java/security/Security/ConfigFileTest.java b/test/jdk/java/security/Security/ConfigFileTest.java index caf657005e1ba..9da3391764bdd 100644 --- a/test/jdk/java/security/Security/ConfigFileTest.java +++ b/test/jdk/java/security/Security/ConfigFileTest.java @@ -394,8 +394,9 @@ static void testCannotIncludeCycles(Executor ex, FilesManager filesMgr) masterFile.addRelativeInclude(file0); ex.setMasterFile(masterFile); - ex.assertError( - "InternalError: Cyclic include of '" + masterFile.path + "'"); + ex.assertError("Cyclic include"); + ex.getOutputAnalyzer().stderrMatches("\\QInternalError: Cyclic " + + "include of '\\E[^']+\\Q" + masterFile.fileName + "'\\E"); } static void testCannotIncludeURL(Executor ex, FilesManager filesMgr) From 4483469e453100259aa28d900943c34a4ab994b4 Mon Sep 17 00:00:00 2001 From: Francisco Ferrari Bihurriet Date: Thu, 13 Nov 2025 15:42:00 +0100 Subject: [PATCH 8/8] Use the right OutputAnalyzer method OutputAnalyzer::stderrMatches returns a boolean while OutputAnalyzer::stderrShouldMatch performs the check. --- test/jdk/java/security/Security/ConfigFileTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/jdk/java/security/Security/ConfigFileTest.java b/test/jdk/java/security/Security/ConfigFileTest.java index 9da3391764bdd..d30bb0a7dff90 100644 --- a/test/jdk/java/security/Security/ConfigFileTest.java +++ b/test/jdk/java/security/Security/ConfigFileTest.java @@ -395,7 +395,7 @@ static void testCannotIncludeCycles(Executor ex, FilesManager filesMgr) ex.setMasterFile(masterFile); ex.assertError("Cyclic include"); - ex.getOutputAnalyzer().stderrMatches("\\QInternalError: Cyclic " + + ex.getOutputAnalyzer().stderrShouldMatch("\\QInternalError: Cyclic " + "include of '\\E[^']+\\Q" + masterFile.fileName + "'\\E"); }