001    /**
002     * Copyright (c) 2000-present Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portal.kernel.log;
016    
017    import com.liferay.portal.kernel.util.CharPool;
018    import com.liferay.portal.kernel.util.GetterUtil;
019    import com.liferay.portal.kernel.util.PropsKeys;
020    import com.liferay.portal.kernel.util.StringUtil;
021    import com.liferay.portal.kernel.util.SystemProperties;
022    
023    import java.util.ArrayList;
024    import java.util.List;
025    
026    /**
027     * @author Tomas Polesovsky
028     * @author Raymond Aug??
029     */
030    public class SanitizerLogWrapper extends LogWrapper {
031    
032            public static Log allowCRLF(Log log) {
033                    if (!(log instanceof SanitizerLogWrapper)) {
034                            return log;
035                    }
036    
037                    SanitizerLogWrapper sanitizerLogWrapper = (SanitizerLogWrapper)log;
038    
039                    sanitizerLogWrapper = new SanitizerLogWrapper(
040                            sanitizerLogWrapper.getWrappedLog());
041    
042                    sanitizerLogWrapper._allowCRLF = true;
043    
044                    return sanitizerLogWrapper;
045            }
046    
047            public static void init() {
048                    if (!_LOG_SANITIZER_ENABLED) {
049                            return;
050                    }
051    
052                    _LOG_SANITIZER_ESCAPE_HTML_ENABLED = GetterUtil.getBoolean(
053                            SystemProperties.get(PropsKeys.LOG_SANITIZER_ESCAPE_HTML_ENABLED));
054    
055                    _LOG_SANITIZER_REPLACEMENT_CHARACTER = (char)GetterUtil.getInteger(
056                            SystemProperties.get(
057                                    PropsKeys.LOG_SANITIZER_REPLACEMENT_CHARACTER));
058    
059                    for (int i = 0; i < _whitelistCharacters.length; i++) {
060                            _whitelistCharacters[i] = 1;
061                    }
062    
063                    int[] whitelistCharacters = GetterUtil.getIntegerValues(
064                            StringUtil.split(
065                                    SystemProperties.get(
066                                            PropsKeys.LOG_SANITIZER_WHITELIST_CHARACTERS)));
067    
068                    for (int whitelistCharacter : whitelistCharacters) {
069                            if ((whitelistCharacter >= 0) &&
070                                    (whitelistCharacter < _whitelistCharacters.length)) {
071    
072                                    _whitelistCharacters[whitelistCharacter] = 0;
073                            }
074                            else {
075                                    System.err.println(
076                                            "Unable to register log whitelist character " +
077                                                    whitelistCharacter);
078                            }
079                    }
080            }
081    
082            public static boolean isEnabled() {
083                    return _LOG_SANITIZER_ENABLED;
084            }
085    
086            public SanitizerLogWrapper(Log log) {
087                    super(log);
088    
089                    setLogWrapperClassName(SanitizerLogWrapper.class.getName());
090            }
091    
092            @Override
093            public void debug(Object msg) {
094                    super.debug(sanitize(msg));
095            }
096    
097            @Override
098            public void debug(Object msg, Throwable t) {
099                    super.debug(sanitize(msg), sanitize(t));
100            }
101    
102            @Override
103            public void debug(Throwable t) {
104                    super.debug(sanitize(t));
105            }
106    
107            @Override
108            public void error(Object msg) {
109                    super.error(sanitize(msg));
110            }
111    
112            @Override
113            public void error(Object msg, Throwable t) {
114                    super.error(sanitize(msg), sanitize(t));
115            }
116    
117            @Override
118            public void error(Throwable t) {
119                    super.error(sanitize(t));
120            }
121    
122            @Override
123            public void fatal(Object msg) {
124                    super.fatal(sanitize(msg));
125            }
126    
127            @Override
128            public void fatal(Object msg, Throwable t) {
129                    super.fatal(sanitize(msg), sanitize(t));
130            }
131    
132            @Override
133            public void fatal(Throwable t) {
134                    super.fatal(sanitize(t));
135            }
136    
137            @Override
138            public void info(Object msg) {
139                    super.info(sanitize(msg));
140            }
141    
142            @Override
143            public void info(Object msg, Throwable t) {
144                    super.info(sanitize(msg), sanitize(t));
145            }
146    
147            @Override
148            public void info(Throwable t) {
149                    super.info(sanitize(t));
150            }
151    
152            @Override
153            public void trace(Object msg) {
154                    super.trace(sanitize(msg));
155            }
156    
157            @Override
158            public void trace(Object msg, Throwable t) {
159                    super.trace(sanitize(msg), sanitize(t));
160            }
161    
162            @Override
163            public void trace(Throwable t) {
164                    super.trace(sanitize(t));
165            }
166    
167            @Override
168            public void warn(Object msg) {
169                    super.warn(sanitize(msg));
170            }
171    
172            @Override
173            public void warn(Object msg, Throwable t) {
174                    super.warn(sanitize(msg), sanitize(t));
175            }
176    
177            @Override
178            public void warn(Throwable t) {
179                    super.warn(sanitize(t));
180            }
181    
182            protected String sanitize(Object obj) {
183                    if (obj == null) {
184                            return null;
185                    }
186    
187                    String message = obj.toString();
188    
189                    return sanitize(message, message);
190            }
191    
192            protected String sanitize(String message, String defaultResult) {
193                    if (message == null) {
194                            return null;
195                    }
196    
197                    char[] chars = message.toCharArray();
198                    boolean hasCRLF = false;
199                    boolean hasLessThanCharacter = false;
200                    boolean sanitized = false;
201    
202                    for (int i = 0; i < chars.length; i++) {
203                            int c = chars[i];
204    
205                            if (_allowCRLF &&
206                                    ((c == CharPool.NEW_LINE) || (c == CharPool.RETURN))) {
207    
208                                    hasCRLF = true;
209    
210                                    continue;
211                            }
212    
213                            if ((c >= 0) && (c < _whitelistCharacters.length) &&
214                                    (_whitelistCharacters[c] != 0)) {
215    
216                                    chars[i] = _LOG_SANITIZER_REPLACEMENT_CHARACTER;
217                                    sanitized = true;
218                            }
219    
220                            if (c == CharPool.LESS_THAN) {
221                                    hasLessThanCharacter = true;
222                            }
223                    }
224    
225                    boolean escapeHTML = false;
226    
227                    if (_LOG_SANITIZER_ESCAPE_HTML_ENABLED && hasLessThanCharacter) {
228                            escapeHTML = true;
229                    }
230    
231                    if (sanitized || escapeHTML || hasCRLF) {
232                            String sanitizedMessage = new String(chars);
233    
234                            if (escapeHTML) {
235                                    sanitizedMessage = StringUtil.replace(
236                                            sanitizedMessage, CharPool.LESS_THAN, _LESS_THAN_ESCAPED);
237                            }
238    
239                            if (sanitized) {
240                                    sanitizedMessage = sanitizedMessage.concat(_SANITIZED);
241                            }
242    
243                            if (hasCRLF) {
244                                    sanitizedMessage = CRLF_WARNING.concat(sanitizedMessage);
245                            }
246    
247                            return sanitizedMessage;
248                    }
249    
250                    return defaultResult;
251            }
252    
253            protected Throwable sanitize(Throwable throwable) {
254                    List<Throwable> throwables = new ArrayList<>();
255    
256                    Throwable tempThrowable = throwable;
257    
258                    while (tempThrowable != null) {
259                            throwables.add(tempThrowable);
260    
261                            tempThrowable = tempThrowable.getCause();
262                    }
263    
264                    Throwable resultThrowable = null;
265    
266                    boolean sanitized = false;
267    
268                    for (int i = throwables.size() - 1; i > - 1; i--) {
269                            Throwable curThrowable = throwables.get(i);
270    
271                            String message = curThrowable.toString();
272    
273                            String sanitizedMessage = sanitize(message, null);
274    
275                            if (!sanitized && (sanitizedMessage == null)) {
276                                    resultThrowable = curThrowable;
277    
278                                    continue;
279                            }
280    
281                            if (sanitizedMessage == null) {
282                                    sanitizedMessage = message;
283                            }
284    
285                            sanitized = true;
286    
287                            resultThrowable = new LogSanitizerException(
288                                    sanitizedMessage, curThrowable.getStackTrace(),
289                                    resultThrowable);
290                    }
291    
292                    return resultThrowable;
293            }
294    
295            protected static final String CRLF_WARNING =
296                    "SanitizerLogWrapper warning: Following message contains CRLF " +
297                            "characters\n";
298    
299            private static final String _LESS_THAN_ESCAPED = "&lt;";
300    
301            private static final boolean _LOG_SANITIZER_ENABLED = GetterUtil.getBoolean(
302                    SystemProperties.get(PropsKeys.LOG_SANITIZER_ENABLED));
303    
304            private static boolean _LOG_SANITIZER_ESCAPE_HTML_ENABLED;
305    
306            private static char _LOG_SANITIZER_REPLACEMENT_CHARACTER =
307                    CharPool.UNDERLINE;
308    
309            private static final String _SANITIZED = " [Sanitized]";
310    
311            private static final int[] _whitelistCharacters = new int[128];
312    
313            private boolean _allowCRLF;
314    
315    }