1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * See LICENSE.txt included in this distribution for the specific 9 * language governing permissions and limitations under the License. 10 * 11 * When distributing Covered Code, include this CDDL HEADER in each 12 * file and include the License file at LICENSE.txt. 13 * If applicable, add the following below this CDDL HEADER, with the 14 * fields enclosed by brackets "[]" replaced with your own identifying 15 * information: Portions Copyright [yyyy] [name of copyright owner] 16 * 17 * CDDL HEADER END 18 */ 19 20 /* 21 * Copyright (c) 2020, Chris Fraire <cfraire@me.com>. 22 */ 23 package org.opengrok.indexer.web; 24 25 import java.util.HashMap; 26 import java.util.Map; 27 import java.util.stream.Collectors; 28 29 /** 30 * Represents a container for sanitizing methods for avoiding classifications as 31 * taint bugs. 32 */ 33 public class Laundromat { 34 35 private static final String ESC_N_R_T_F = "[\\n\\r\\t\\f]"; 36 private static final String ESG_N_R_T_F__1_n = ESC_N_R_T_F + "+"; 37 38 /** 39 * Sanitize {@code value} where it will be used in subsequent OpenGrok 40 * (non-logging) processing. 41 * @return {@code null} if null or else {@code value} with "pattern-breaking 42 * characters" (tabs, CR, LF, FF) replaced as underscores (one for one) 43 */ launderInput(String value)44 public static String launderInput(String value) { 45 return replaceAll(value, ESC_N_R_T_F, "_"); 46 } 47 48 /** 49 * Sanitize {@code value} where it will be used in a Lucene query. 50 * @return {@code null} if null or else {@code value} with "pattern-breaking 51 * characters" (tabs, CR, LF, FF) replaced as spaces. Contiguous matches are 52 * replaced with one space. 53 */ launderQuery(String value)54 public static String launderQuery(String value) { 55 return replaceAll(value, ESG_N_R_T_F__1_n, " "); 56 } 57 58 /** 59 * Sanitize {@code value} where it will be used in a log message only. 60 * @return {@code null} if null or else {@code value} with "pattern-breaking 61 * characters" tabs, CR, LF, and FF replaced as {@code "<TAB>"}, 62 * {@code "<CR>"}, {@code "<LF>"}, and {@code "<FF>"} resp. 63 */ launderLog(String value)64 public static String launderLog(String value) { 65 if (value == null) { 66 return null; 67 } 68 return value.replaceAll("\\n", "<LF>"). 69 replaceAll("\\r", "<CR>"). 70 replaceAll("\\t", "<TAB>"). 71 replaceAll("\\f", "<FF>"); 72 } 73 74 /** 75 * Sanitize {@code map} where it will be used in a log message only. 76 * @return {@code null} if null or else {@code map} with keys and values 77 * sanitized with {@link #launderLog(String)}. If the sanitizing causes key 78 * collisions, the colliding keys' values are combined. 79 */ launderLog(Map<String, String[]> map)80 public static Map<String, String[]> launderLog(Map<String, String[]> map) { 81 if (map == null) { 82 return null; 83 } 84 85 HashMap<String, String[]> safes = new HashMap<>(); 86 for (Map.Entry<String, String[]> entry : map.entrySet().stream().sorted( 87 Map.Entry.comparingByKey()).collect(Collectors.toList())) { 88 String key = launderLog(entry.getKey()); 89 String[] safeValues = safes.get(key); 90 String[] fullySafe = mergeLogArrays(entry.getValue(), safeValues); 91 safes.put(key, fullySafe); 92 } 93 return safes; 94 } 95 mergeLogArrays(String[] values, String[] safeValues)96 private static String[] mergeLogArrays(String[] values, String[] safeValues) { 97 if (values == null && safeValues == null) { 98 return null; 99 } 100 101 int n = (values != null ? values.length : 0) + 102 (safeValues != null ? safeValues.length : 0); 103 String[] result = new String[n]; 104 105 int i = 0; 106 if (values != null) { 107 for (; i < values.length; ++i) { 108 result[i] = launderLog(values[i]); 109 } 110 } 111 if (safeValues != null) { 112 System.arraycopy(safeValues, 0, result, i, safeValues.length); 113 } 114 return result; 115 } 116 replaceAll(String value, String regex, String replacement)117 private static String replaceAll(String value, String regex, String replacement) { 118 if (value == null) { 119 return null; 120 } 121 return value.replaceAll(regex, replacement); 122 } 123 124 /* private to enforce static */ Laundromat()125 private Laundromat() { 126 } 127 } 128