StringUtils.java

  1. package com.renomad.minum.utils;

  2. import java.net.URLDecoder;
  3. import java.net.URLEncoder;
  4. import java.security.SecureRandom;
  5. import java.util.List;
  6. import java.util.stream.Collectors;
  7. import java.util.stream.IntStream;

  8. import static com.renomad.minum.utils.Invariants.mustNotBeNull;
  9. import static java.nio.charset.StandardCharsets.UTF_8;

  10. /**
  11.  * Some helper methods for Strings.
  12.  */
  13. public final class StringUtils {

  14.     private StringUtils() {
  15.         // making this private to be clearer it isn't supposed to be instantiated.
  16.     }


  17.     /**
  18.      * Returns text that has three symbols replaced -
  19.      * the less-than, greater-than, and ampersand.
  20.      * See <a href="https://www.w3.org/International/questions/qa-escapes#use">...</a>
  21.      * <br>
  22.      * <pre>{@code
  23.      * This will protect against something like <div>$USERNAME</div> allowing
  24.      * a username of
  25.      *      <script>alert(1)</script>
  26.      * becoming
  27.      *      <div><script>alert(1)</script</div>
  28.      * and instead becomes
  29.      *      <div>&lt;script&gt;alert(1)&lt;/script&gt;</div>
  30.      * }</pre>
  31.      * If the text is going inside an attribute (e.g. {@code <div class="TEXT_GOES_HERE">} )
  32.      * Then you need to escape slightly differently. In that case see [safeAttr]
  33.      */
  34.     public static String safeHtml(String input) {
  35.         if (input == null) {
  36.             return "";
  37.         }
  38.         return input.replace("&", "&amp;")
  39.             .replace("<", "&lt;")
  40.             .replace(">", "&gt;");
  41.     }

  42.     /**
  43.      * Replace dangerous text that would go inside an HTML attribute.
  44.      * See {@link #safeHtml(String)}
  45.      * <br><br>
  46.      * If we get a null string, just return an empty string
  47.      * <br><br>
  48.      * <pre>{@code
  49.      * example:
  50.      *   Given
  51.      *      alert('XSS Attack')
  52.      *   Get
  53.      *      alert(&apos;XSS Attack&apos;)
  54.      * }</pre>
  55.      */
  56.     public static String safeAttr(String input) {
  57.         if (input == null) {
  58.             return "";
  59.         }
  60.         return input.replace("&", "&amp;")
  61.             .replace("<", "&lt;")
  62.             .replace("\"", "&quot;")
  63.             .replace("'", "&apos;");
  64.     }

  65.     /**
  66.      * Encodes UTF-8 text using URL-encoding
  67.      */
  68.     public static String encode(String str) {
  69.         if (str == null) {
  70.             return "%NULL%";
  71.         }
  72.         return URLEncoder.encode(str, UTF_8);
  73.     }

  74.     /**
  75.      * Decodes URL-encoded UTF-8 text, except that we
  76.      * first check if the string value is the token %NULL%,
  77.      * which is our way to signify null.
  78.      */
  79.     public static String decode(String str) {
  80.         mustNotBeNull(str);
  81.         if (str.equals("%NULL%")) {
  82.             return null;
  83.         }
  84.         return URLDecoder.decode(str, UTF_8);
  85.     }

  86.     public static String generateSecureRandomString(int length) {
  87.         final var allowedChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
  88.         final var sr = new SecureRandom();

  89.         return IntStream.range(1, length+1)
  90.                 .mapToObj (x -> allowedChars.charAt(sr.nextInt(allowedChars.length())))
  91.                 .map(Object::toString)
  92.                 .collect(Collectors.joining());
  93.     }

  94.     /**
  95.      * Converts a list of bytes to a string. Returns null if the input is null.
  96.      */
  97.     public static String byteListToString(List<Byte> byteList) {
  98.         if (byteList == null) return null;
  99.         final int size = byteList.size();
  100.         final var buf = new byte[size];
  101.         for (int i = 0; i < size; i++) {
  102.             buf[i] = byteList.get(i);
  103.         }
  104.         return new String(buf, UTF_8);
  105.     }

  106.     /**
  107.      * Converts an array of bytes to a string. Returns null if the input is null.
  108.      */
  109.     public static String byteArrayToString(byte[] byteArray) {
  110.         if (byteArray == null) return null;
  111.         return new String(byteArray, UTF_8);
  112.     }


  113. }