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><script>alert(1)</script></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("&", "&") |
45 | .replace("<", "<") | |
46 | .replace(">", ">"); | |
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('XSS Attack') | |
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("&", "&") |
68 | .replace("<", "<") | |
69 | .replace("\"", """) | |
70 | .replace("'", "'"); | |
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 → KILLED 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 |
|
44 |
1.1 |
|
64 |
1.1 |
|
67 |
1.1 |
|
77 |
1.1 |
|
78 |
1.1 |
|
80 |
1.1 |
|
90 |
1.1 |
|
91 |
1.1 |
|
93 |
1.1 |
|
100 |
1.1 2.2 |
|
101 |
1.1 |
|
110 |
1.1 2.2 |
|
113 |
1.1 2.2 |
|
116 |
1.1 |
|
123 |
1.1 2.2 |
|
124 |
1.1 |