undefect. CWE-407 — 63 sites patched across 27 ecosystems
Authors: russell@unturf.com · brackishbert@gmail.com · foxhop.net · TimeHexOn.com Patches, unit tests, benchmarks, whitepaper, and outreach briefs. Public domain — no copyright claimed. Use freely.
This commit is contained in:
commit
0a580b313d
70422 changed files with 17213626 additions and 0 deletions
322
test/jdk/java/util/Properties/PropertiesStoreTest.java
Normal file
322
test/jdk/java/util/Properties/PropertiesStoreTest.java
Normal file
|
|
@ -0,0 +1,322 @@
|
|||
/*
|
||||
* Copyright (c) 2021, 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
|
||||
* 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 java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.Writer;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.format.DateTimeParseException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
import java.util.stream.Collectors;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.TestInstance;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @summary tests the order in which the Properties.store() method writes out the properties
|
||||
* @bug 8231640 8282023
|
||||
* @run junit/othervm PropertiesStoreTest
|
||||
*/
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
public class PropertiesStoreTest {
|
||||
|
||||
private static final String DATE_FORMAT_PATTERN = "EEE MMM dd HH:mm:ss zzz uuuu";
|
||||
// use Locale.US, since when the date comment was written by Properties.store(...),
|
||||
// it internally calls the Date.toString() which uses Locale.US for time zone names
|
||||
private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern(DATE_FORMAT_PATTERN, Locale.US);
|
||||
private static final Locale PREV_LOCALE = Locale.getDefault();
|
||||
|
||||
private Object[][] createProps() {
|
||||
final Properties simple = new Properties();
|
||||
simple.setProperty("1", "one");
|
||||
simple.setProperty("2", "two");
|
||||
simple.setProperty("10", "ten");
|
||||
simple.setProperty("02", "zero-two");
|
||||
simple.setProperty("3", "three");
|
||||
simple.setProperty("0", "zero");
|
||||
simple.setProperty("00", "zero-zero");
|
||||
simple.setProperty("0", "zero-again");
|
||||
|
||||
final Properties specialChars = new Properties();
|
||||
// some special chars
|
||||
simple.setProperty(" 1", "space-one");
|
||||
simple.setProperty("\t 3 7 \n", "tab-space-three-space-seven-space-newline");
|
||||
// add some simple chars
|
||||
simple.setProperty("3", "three");
|
||||
simple.setProperty("0", "zero");
|
||||
|
||||
final Properties overrideCallsSuper = new OverridesEntrySetCallsSuper();
|
||||
overrideCallsSuper.putAll(simple);
|
||||
|
||||
final OverridesEntrySet overridesEntrySet = new OverridesEntrySet();
|
||||
overridesEntrySet.putAll(simple);
|
||||
|
||||
final Properties doesNotOverrideEntrySet = new DoesNotOverrideEntrySet();
|
||||
doesNotOverrideEntrySet.putAll(simple);
|
||||
|
||||
return new Object[][]{
|
||||
{simple, naturalOrder(simple)},
|
||||
{specialChars, naturalOrder(specialChars)},
|
||||
{overrideCallsSuper, naturalOrder(overrideCallsSuper)},
|
||||
{overridesEntrySet, overridesEntrySet.expectedKeyOrder()},
|
||||
{doesNotOverrideEntrySet, naturalOrder(doesNotOverrideEntrySet)}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link Locale} to use for testing
|
||||
*/
|
||||
private Object[][] provideLocales() {
|
||||
// pick a non-english locale for testing
|
||||
Set<Locale> locales = Arrays.stream(Locale.getAvailableLocales())
|
||||
.filter(l -> !l.getLanguage().isEmpty() && !l.getLanguage().equals("en"))
|
||||
.limit(1)
|
||||
.collect(Collectors.toCollection(HashSet::new));
|
||||
locales.add(Locale.getDefault()); // always test the default locale
|
||||
locales.add(Locale.US); // guaranteed to be present
|
||||
locales.add(Locale.ROOT); // guaranteed to be present
|
||||
|
||||
// return the chosen locales
|
||||
return locales.stream()
|
||||
.map(m -> new Locale[] {m})
|
||||
.toArray(n -> new Object[n][0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that the {@link Properties#store(Writer, String)} API writes out the properties
|
||||
* in the expected order
|
||||
*/
|
||||
@ParameterizedTest
|
||||
@MethodSource("createProps")
|
||||
public void testStoreWriterKeyOrder(final Properties props, final String[] expectedOrder) throws Exception {
|
||||
// Properties.store(...) to a temp file
|
||||
final Path tmpFile = Files.createTempFile("8231640", "props");
|
||||
try (final Writer writer = Files.newBufferedWriter(tmpFile)) {
|
||||
props.store(writer, null);
|
||||
}
|
||||
testStoreKeyOrder(props, tmpFile, expectedOrder);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that the {@link Properties#store(OutputStream, String)} API writes out the properties
|
||||
* in the expected order
|
||||
*/
|
||||
@ParameterizedTest
|
||||
@MethodSource("createProps")
|
||||
public void testStoreOutputStreamKeyOrder(final Properties props, final String[] expectedOrder) throws Exception {
|
||||
// Properties.store(...) to a temp file
|
||||
final Path tmpFile = Files.createTempFile("8231640", "props");
|
||||
try (final OutputStream os = Files.newOutputStream(tmpFile)) {
|
||||
props.store(os, null);
|
||||
}
|
||||
testStoreKeyOrder(props, tmpFile, expectedOrder);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link Properties#load(InputStream) Loads a Properties instance} from the passed
|
||||
* {@code Path} and then verifies that:
|
||||
* - the loaded properties instance "equals" the passed (original) "props" instance
|
||||
* - the order in which the properties appear in the file represented by the path
|
||||
* is the same as the passed "expectedOrder"
|
||||
*/
|
||||
private void testStoreKeyOrder(final Properties props, final Path storedProps,
|
||||
final String[] expectedOrder) throws Exception {
|
||||
// Properties.load(...) from that stored file and verify that the loaded
|
||||
// Properties has expected content
|
||||
final Properties loaded = new Properties();
|
||||
try (final InputStream is = Files.newInputStream(storedProps)) {
|
||||
loaded.load(is);
|
||||
}
|
||||
Assertions.assertEquals(props, loaded, "Unexpected properties loaded from stored state");
|
||||
|
||||
// now read lines from the stored file and keep track of the order in which the keys were
|
||||
// found in that file. Compare that order with the expected store order of the keys.
|
||||
final List<String> actualOrder;
|
||||
try (final BufferedReader reader = Files.newBufferedReader(storedProps)) {
|
||||
actualOrder = readInOrder(reader);
|
||||
}
|
||||
Assertions.assertEquals(expectedOrder.length, actualOrder.size(),
|
||||
"Unexpected number of keys read from stored properties");
|
||||
if (!Arrays.equals(actualOrder.toArray(new String[0]), expectedOrder)) {
|
||||
Assertions.fail("Unexpected order of stored property keys. Expected order: " + Arrays.toString(expectedOrder)
|
||||
+ ", found order: " + actualOrder);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that {@link Properties#store(Writer, String)} writes out a proper date comment
|
||||
*/
|
||||
@ParameterizedTest
|
||||
@MethodSource("provideLocales")
|
||||
public void testStoreWriterDateComment(final Locale testLocale) throws Exception {
|
||||
// switch the default locale to the one being tested
|
||||
Locale.setDefault(testLocale);
|
||||
System.out.println("Using locale: " + testLocale + " for Properties#store(Writer) test");
|
||||
try {
|
||||
final Properties props = new Properties();
|
||||
props.setProperty("a", "b");
|
||||
final Path tmpFile = Files.createTempFile("8231640", "props");
|
||||
try (final Writer writer = Files.newBufferedWriter(tmpFile)) {
|
||||
props.store(writer, null);
|
||||
}
|
||||
testDateComment(tmpFile);
|
||||
} finally {
|
||||
// reset to the previous one
|
||||
Locale.setDefault(PREV_LOCALE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that {@link Properties#store(OutputStream, String)} writes out a proper date comment
|
||||
*/
|
||||
@ParameterizedTest
|
||||
@MethodSource("provideLocales")
|
||||
public void testStoreOutputStreamDateComment(final Locale testLocale) throws Exception {
|
||||
// switch the default locale to the one being tested
|
||||
Locale.setDefault(testLocale);
|
||||
System.out.println("Using locale: " + testLocale + " for Properties#store(OutputStream) test");
|
||||
try {
|
||||
final Properties props = new Properties();
|
||||
props.setProperty("a", "b");
|
||||
final Path tmpFile = Files.createTempFile("8231640", "props");
|
||||
try (final Writer writer = Files.newBufferedWriter(tmpFile)) {
|
||||
props.store(writer, null);
|
||||
}
|
||||
testDateComment(tmpFile);
|
||||
} finally {
|
||||
// reset to the previous one
|
||||
Locale.setDefault(PREV_LOCALE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads each line in the {@code file} and verifies that there is only one comment line
|
||||
* and that comment line can be parsed into a {@link java.util.Date}
|
||||
*/
|
||||
private void testDateComment(Path file) throws Exception {
|
||||
String comment = null;
|
||||
try (final BufferedReader reader = Files.newBufferedReader(file)) {
|
||||
String line = null;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
if (line.startsWith("#")) {
|
||||
if (comment != null) {
|
||||
Assertions.fail("More than one comment line found in the stored properties file " + file);
|
||||
}
|
||||
comment = line.substring(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (comment == null) {
|
||||
Assertions.fail("No comment line found in the stored properties file " + file);
|
||||
}
|
||||
try {
|
||||
FORMATTER.parse(comment);
|
||||
} catch (DateTimeParseException pe) {
|
||||
Assertions.fail("Unexpected date comment: " + comment, pe);
|
||||
}
|
||||
}
|
||||
|
||||
// returns the property keys in their natural order
|
||||
private static String[] naturalOrder(final Properties props) {
|
||||
return new TreeSet<>(props.stringPropertyNames()).toArray(new String[0]);
|
||||
}
|
||||
|
||||
// reads each non-comment line and keeps track of the order in which the property key lines
|
||||
// were read
|
||||
private static List<String> readInOrder(final BufferedReader reader) throws IOException {
|
||||
final List<String> readKeys = new ArrayList<>();
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
if (line.startsWith("#")) {
|
||||
continue;
|
||||
}
|
||||
final String key = line.substring(0, line.indexOf("="));
|
||||
// the Properties.store(...) APIs write out the keys in a specific format for certain
|
||||
// special characters. Our test uses some of the keys which have those special characters.
|
||||
// Here we handle such special character conversion (for only those characters that this test uses).
|
||||
// replace the backslash character followed by the t character with the tab character
|
||||
String replacedKey = key.replace("\\t", "\t");
|
||||
// replace the backslash character followed by the n character with the newline character
|
||||
replacedKey = replacedKey.replace("\\n", "\n");
|
||||
// replace backslash character followed by the space character with the space character
|
||||
replacedKey = replacedKey.replace("\\ ", " ");
|
||||
readKeys.add(replacedKey);
|
||||
}
|
||||
return readKeys;
|
||||
}
|
||||
|
||||
// Extends java.util.Properties and overrides entrySet() to return a reverse
|
||||
// sorted entries set
|
||||
private static class OverridesEntrySet extends Properties {
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public Set<Map.Entry<Object, Object>> entrySet() {
|
||||
// return a reverse sorted entries set
|
||||
var entries = super.entrySet();
|
||||
Comparator<Map.Entry<String, String>> comparator = Map.Entry.comparingByKey(Comparator.reverseOrder());
|
||||
TreeSet<Map.Entry<String, String>> reverseSorted = new TreeSet<>(comparator);
|
||||
reverseSorted.addAll((Set) entries);
|
||||
return (Set) reverseSorted;
|
||||
}
|
||||
|
||||
String[] expectedKeyOrder() {
|
||||
// returns in reverse order of the property keys' natural ordering
|
||||
var keys = new ArrayList<>(stringPropertyNames());
|
||||
keys.sort(Comparator.reverseOrder());
|
||||
return keys.toArray(new String[0]);
|
||||
}
|
||||
}
|
||||
|
||||
// Extends java.util.Properties and overrides entrySet() to just return "super.entrySet()"
|
||||
private static class OverridesEntrySetCallsSuper extends Properties {
|
||||
@Override
|
||||
public Set<Map.Entry<Object, Object>> entrySet() {
|
||||
return super.entrySet();
|
||||
}
|
||||
}
|
||||
|
||||
// Extends java.util.Properties but doesn't override entrySet() method
|
||||
private static class DoesNotOverrideEntrySet extends Properties {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "DoesNotOverrideEntrySet - " + super.toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue