StringUtils.java

1
package com.renomad.minum.utils;
2
3
import java.net.URLDecoder;
4
import java.net.URLEncoder;
5
import java.security.SecureRandom;
6
import java.util.List;
7
import java.util.stream.Collectors;
8
import java.util.stream.IntStream;
9
10
import static com.renomad.minum.utils.Invariants.mustNotBeNull;
11
import static java.nio.charset.StandardCharsets.UTF_8;
12
13
/**
14
 * Some helper methods for Strings.
15
 */
16
public final class StringUtils {
17
18
    private StringUtils() {
19
        // making this private to be clearer it isn't supposed to be instantiated.
20
    }
21
22
23
    /**
24
     * Returns text that has three symbols replaced -
25
     * the less-than, greater-than, and ampersand.
26
     * See <a href="https://www.w3.org/International/questions/qa-escapes#use">...</a>
27
     * <br>
28
     * <pre>{@code
29
     * This will protect against something like <div>$USERNAME</div> allowing
30
     * a username of
31
     *      <script>alert(1)</script>
32
     * becoming
33
     *      <div><script>alert(1)</script</div>
34
     * and instead becomes
35
     *      <div>&lt;script&gt;alert(1)&lt;/script&gt;</div>
36
     * }</pre>
37
     * If the text is going inside an attribute (e.g. {@code <div class="TEXT_GOES_HERE">} )
38
     * Then you need to escape slightly differently. In that case see [safeAttr]
39
     */
40
    public static String safeHtml(String input) {
41 1 1. safeHtml : negated conditional → KILLED
        if (input == null) {
42
            return "";
43
        }
44 1 1. safeHtml : replaced return value with "" for com/renomad/minum/utils/StringUtils::safeHtml → KILLED
        return input.replace("&", "&amp;")
45
            .replace("<", "&lt;")
46
            .replace(">", "&gt;");
47
    }
48
49
    /**
50
     * Replace dangerous text that would go inside an HTML attribute.
51
     * See {@link #safeHtml(String)}
52
     * <br><br>
53
     * If we get a null string, just return an empty string
54
     * <br><br>
55
     * <pre>{@code
56
     * example:
57
     *   Given
58
     *      alert('XSS Attack')
59
     *   Get
60
     *      alert(&apos;XSS Attack&apos;)
61
     * }</pre>
62
     */
63
    public static String safeAttr(String input) {
64 1 1. safeAttr : negated conditional → KILLED
        if (input == null) {
65
            return "";
66
        }
67 1 1. safeAttr : replaced return value with "" for com/renomad/minum/utils/StringUtils::safeAttr → KILLED
        return input.replace("&", "&amp;")
68
            .replace("<", "&lt;")
69
            .replace("\"", "&quot;")
70
            .replace("'", "&apos;");
71
    }
72
73
    /**
74
     * Encodes UTF-8 text using URL-encoding
75
     */
76
    public static String encode(String str) {
77 1 1. encode : negated conditional → KILLED
        if (str == null) {
78 1 1. encode : replaced return value with "" for com/renomad/minum/utils/StringUtils::encode → KILLED
            return "%NULL%";
79
        }
80 1 1. encode : replaced return value with "" for com/renomad/minum/utils/StringUtils::encode → KILLED
        return URLEncoder.encode(str, UTF_8);
81
    }
82
83
    /**
84
     * Decodes URL-encoded UTF-8 text, except that we
85
     * first check if the string value is the token %NULL%,
86
     * which is our way to signify null.
87
     */
88
    public static String decode(String str) {
89
        mustNotBeNull(str);
90 1 1. decode : negated conditional → KILLED
        if (str.equals("%NULL%")) {
91 1 1. decode : replaced return value with "" for com/renomad/minum/utils/StringUtils::decode → KILLED
            return null;
92
        }
93 1 1. decode : replaced return value with "" for com/renomad/minum/utils/StringUtils::decode → KILLED
        return URLDecoder.decode(str, UTF_8);
94
    }
95
96
    public static String generateSecureRandomString(int length) {
97
        final var allowedChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
98
        final var sr = new SecureRandom();
99
100 2 1. generateSecureRandomString : Replaced integer addition with subtraction → TIMED_OUT
2. generateSecureRandomString : replaced return value with "" for com/renomad/minum/utils/StringUtils::generateSecureRandomString → KILLED
        return IntStream.range(1, length+1)
101 1 1. lambda$generateSecureRandomString$0 : replaced Character return value with 0 for com/renomad/minum/utils/StringUtils::lambda$generateSecureRandomString$0 → KILLED
                .mapToObj (x -> allowedChars.charAt(sr.nextInt(allowedChars.length())))
102
                .map(Object::toString)
103
                .collect(Collectors.joining());
104
    }
105
106
    /**
107
     * Converts a list of bytes to a string. Returns null if the input is null.
108
     */
109
    public static String byteListToString(List<Byte> byteList) {
110 2 1. byteListToString : negated conditional → KILLED
2. byteListToString : replaced return value with "" for com/renomad/minum/utils/StringUtils::byteListToString → KILLED
        if (byteList == null) return null;
111
        final int size = byteList.size();
112
        final var buf = new byte[size];
113 2 1. byteListToString : changed conditional boundary → KILLED
2. byteListToString : negated conditional → KILLED
        for (int i = 0; i < size; i++) {
114
            buf[i] = byteList.get(i);
115
        }
116 1 1. byteListToString : replaced return value with "" for com/renomad/minum/utils/StringUtils::byteListToString → KILLED
        return new String(buf, UTF_8);
117
    }
118
119
    /**
120
     * Converts an array of bytes to a string. Returns null if the input is null.
121
     */
122
    public static String byteArrayToString(byte[] byteArray) {
123 2 1. byteArrayToString : negated conditional → KILLED
2. byteArrayToString : replaced return value with "" for com/renomad/minum/utils/StringUtils::byteArrayToString → KILLED
        if (byteArray == null) return null;
124 1 1. byteArrayToString : replaced return value with "" for com/renomad/minum/utils/StringUtils::byteArrayToString → KILLED
        return new String(byteArray, UTF_8);
125
    }
126
127
128
}
129

Mutations

41

1.1
Location : safeHtml
Killed by : com.renomad.minum.utils.StringUtilsTests.test_CleanHtml_Null(com.renomad.minum.utils.StringUtilsTests)
negated conditional → KILLED

44

1.1
Location : safeHtml
Killed by : com.renomad.minum.utils.StringUtilsTests.test_CleanHtml(com.renomad.minum.utils.StringUtilsTests)
replaced return value with "" for com/renomad/minum/utils/StringUtils::safeHtml → KILLED

64

1.1
Location : safeAttr
Killed by : com.renomad.minum.utils.StringUtilsTests.test_CleanAttributes_Null(com.renomad.minum.utils.StringUtilsTests)
negated conditional → KILLED

67

1.1
Location : safeAttr
Killed by : com.renomad.minum.utils.StringUtilsTests.test_CleanAttributes(com.renomad.minum.utils.StringUtilsTests)
replaced return value with "" for com/renomad/minum/utils/StringUtils::safeAttr → KILLED

77

1.1
Location : encode
Killed by : com.renomad.minum.utils.SerializationUtilsTests.testSerializationHelper(com.renomad.minum.utils.SerializationUtilsTests)
negated conditional → KILLED

78

1.1
Location : encode
Killed by : com.renomad.minum.utils.SerializationUtilsTests.testSerializationHelper(com.renomad.minum.utils.SerializationUtilsTests)
replaced return value with "" for com/renomad/minum/utils/StringUtils::encode → KILLED

80

1.1
Location : encode
Killed by : com.renomad.minum.utils.SerializationUtilsTests.testSerializationHelper(com.renomad.minum.utils.SerializationUtilsTests)
replaced return value with "" for com/renomad/minum/utils/StringUtils::encode → KILLED

90

1.1
Location : decode
Killed by : com.renomad.minum.database.DbTests.test_Serialization_SimpleCase(com.renomad.minum.database.DbTests)
negated conditional → KILLED

91

1.1
Location : decode
Killed by : com.renomad.minum.database.DbTests.test_Serialization_Null(com.renomad.minum.database.DbTests)
replaced return value with "" for com/renomad/minum/utils/StringUtils::decode → KILLED

93

1.1
Location : decode
Killed by : com.renomad.minum.database.DbTests.test_Serialization_SimpleCase(com.renomad.minum.database.DbTests)
replaced return value with "" for com/renomad/minum/utils/StringUtils::decode → KILLED

100

1.1
Location : generateSecureRandomString
Killed by : none
Replaced integer addition with subtraction → TIMED_OUT

2.2
Location : generateSecureRandomString
Killed by : com.renomad.minum.FunctionalTests
replaced return value with "" for com/renomad/minum/utils/StringUtils::generateSecureRandomString → KILLED

101

1.1
Location : lambda$generateSecureRandomString$0
Killed by : com.renomad.minum.FunctionalTests
replaced Character return value with 0 for com/renomad/minum/utils/StringUtils::lambda$generateSecureRandomString$0 → KILLED

110

1.1
Location : byteListToString
Killed by : com.renomad.minum.utils.StringUtilsTests.test_BytesListToString(com.renomad.minum.utils.StringUtilsTests)
negated conditional → KILLED

2.2
Location : byteListToString
Killed by : com.renomad.minum.utils.StringUtilsTests.test_BytesListToString(com.renomad.minum.utils.StringUtilsTests)
replaced return value with "" for com/renomad/minum/utils/StringUtils::byteListToString → KILLED

113

1.1
Location : byteListToString
Killed by : com.renomad.minum.utils.StringUtilsTests.test_BytesListToString(com.renomad.minum.utils.StringUtilsTests)
changed conditional boundary → KILLED

2.2
Location : byteListToString
Killed by : com.renomad.minum.utils.StringUtilsTests.test_BytesListToString(com.renomad.minum.utils.StringUtilsTests)
negated conditional → KILLED

116

1.1
Location : byteListToString
Killed by : com.renomad.minum.utils.StringUtilsTests.test_BytesListToString(com.renomad.minum.utils.StringUtilsTests)
replaced return value with "" for com/renomad/minum/utils/StringUtils::byteListToString → KILLED

123

1.1
Location : byteArrayToString
Killed by : com.renomad.minum.utils.StringUtilsTests.test_ByteArrayToString(com.renomad.minum.utils.StringUtilsTests)
negated conditional → KILLED

2.2
Location : byteArrayToString
Killed by : com.renomad.minum.utils.StringUtilsTests.test_ByteArrayToString(com.renomad.minum.utils.StringUtilsTests)
replaced return value with "" for com/renomad/minum/utils/StringUtils::byteArrayToString → KILLED

124

1.1
Location : byteArrayToString
Killed by : com.renomad.minum.web.BodyProcessorTests
replaced return value with "" for com/renomad/minum/utils/StringUtils::byteArrayToString → KILLED

Active mutators

Tests examined


Report generated by PIT 1.17.0