001    /**
002     * Copyright (c) 2000-2013 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.StringPool;
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                            SystemProperties.getArray(
065                                    PropsKeys.LOG_SANITIZER_WHITELIST_CHARACTERS));
066    
067                    for (int whitelistCharacter : whitelistCharacters) {
068                            if ((whitelistCharacter >= 0) &&
069                                    (whitelistCharacter < _whitelistCharacters.length)) {
070    
071                                    _whitelistCharacters[whitelistCharacter] = 0;
072                            }
073                            else {
074                                    System.err.println(
075                                            "Unable to register log whitelist character " +
076                                                    whitelistCharacter);
077                            }
078                    }
079            }
080    
081            public static boolean isEnabled() {
082                    return _LOG_SANITIZER_ENABLED;
083            }
084    
085            public SanitizerLogWrapper(Log log) {
086                    super(log);
087    
088                    setLogWrapperClassName(SanitizerLogWrapper.class.getName());
089            }
090    
091            @Override
092            public void debug(Object msg) {
093                    super.debug(sanitize(msg));
094            }
095    
096            @Override
097            public void debug(Object msg, Throwable t) {
098                    super.debug(sanitize(msg), sanitize(t));
099            }
100    
101            @Override
102            public void debug(Throwable t) {
103                    super.debug(sanitize(t));
104            }
105    
106            @Override
107            public void error(Object msg) {
108                    super.error(sanitize(msg));
109            }
110    
111            @Override
112            public void error(Object msg, Throwable t) {
113                    super.error(sanitize(msg), sanitize(t));
114            }
115    
116            @Override
117            public void error(Throwable t) {
118                    super.error(sanitize(t));
119            }
120    
121            @Override
122            public void fatal(Object msg) {
123                    super.fatal(sanitize(msg));
124            }
125    
126            @Override
127            public void fatal(Object msg, Throwable t) {
128                    super.fatal(sanitize(msg), sanitize(t));
129            }
130    
131            @Override
132            public void fatal(Throwable t) {
133                    super.fatal(sanitize(t));
134            }
135    
136            @Override
137            public void info(Object msg) {
138                    super.info(sanitize(msg));
139            }
140    
141            @Override
142            public void info(Object msg, Throwable t) {
143                    super.info(sanitize(msg), sanitize(t));
144            }
145    
146            @Override
147            public void info(Throwable t) {
148                    super.info(sanitize(t));
149            }
150    
151            @Override
152            public void trace(Object msg) {
153                    super.trace(sanitize(msg));
154            }
155    
156            @Override
157            public void trace(Object msg, Throwable t) {
158                    super.trace(sanitize(msg), sanitize(t));
159            }
160    
161            @Override
162            public void trace(Throwable t) {
163                    super.trace(sanitize(t));
164            }
165    
166            @Override
167            public void warn(Object msg) {
168                    super.warn(sanitize(msg));
169            }
170    
171            @Override
172            public void warn(Object msg, Throwable t) {
173                    super.warn(sanitize(msg), sanitize(t));
174            }
175    
176            @Override
177            public void warn(Throwable t) {
178                    super.warn(sanitize(t));
179            }
180    
181            protected String sanitize(Object obj) {
182                    if (obj == null) {
183                            return null;
184                    }
185    
186                    String message = obj.toString();
187    
188                    return sanitize(message, message);
189            }
190    
191            protected String sanitize(String message, String defaultResult) {
192                    if (message == null) {
193                            return null;
194                    }
195    
196                    char[] chars = message.toCharArray();
197                    boolean hasCRLF = false;
198                    boolean hasLessThanCharacter = false;
199                    boolean sanitized = false;
200    
201                    for (int i = 0; i < chars.length; i++) {
202                            int c = chars[i];
203    
204                            if (_allowCRLF &&
205                                    ((c == CharPool.NEW_LINE) || (c == CharPool.RETURN))) {
206    
207                                    hasCRLF = true;
208    
209                                    continue;
210                            }
211    
212                            if ((c >= 0) && (c < _whitelistCharacters.length) &&
213                                    (_whitelistCharacters[c] != 0)) {
214    
215                                    chars[i] = _LOG_SANITIZER_REPLACEMENT_CHARACTER;
216                                    sanitized = true;
217                            }
218    
219                            if (c == CharPool.LESS_THAN) {
220                                    hasLessThanCharacter = true;
221                            }
222                    }
223    
224                    boolean escapeHTML = false;
225    
226                    if (_LOG_SANITIZER_ESCAPE_HTML_ENABLED && hasLessThanCharacter) {
227                            escapeHTML = true;
228                    }
229    
230                    if (sanitized || escapeHTML || hasCRLF) {
231                            String sanitizedMessage = new String(chars);
232    
233                            if (escapeHTML) {
234                                    sanitizedMessage = sanitizedMessage.replaceAll(
235                                            StringPool.LESS_THAN, _LESS_THAN_ESCAPED);
236                            }
237    
238                            if (sanitized) {
239                                    sanitizedMessage = sanitizedMessage.concat(_SANITIZED);
240                            }
241    
242                            if (hasCRLF) {
243                                    sanitizedMessage = CRLF_WARNING.concat(sanitizedMessage);
244                            }
245    
246                            return sanitizedMessage;
247                    }
248    
249                    return defaultResult;
250            }
251    
252            protected Throwable sanitize(Throwable throwable) {
253                    List<Throwable> throwables = new ArrayList<Throwable>();
254    
255                    Throwable tempThrowable = throwable;
256    
257                    while (tempThrowable != null) {
258                            throwables.add(tempThrowable);
259    
260                            tempThrowable = tempThrowable.getCause();
261                    }
262    
263                    Throwable resultThrowable = null;
264    
265                    boolean sanitized = false;
266    
267                    for (int i = throwables.size() - 1; i > - 1; i--) {
268                            Throwable curThrowable = throwables.get(i);
269    
270                            String message = curThrowable.toString();
271    
272                            String sanitizedMessage = sanitize(message, null);
273    
274                            if (!sanitized && (sanitizedMessage == null)) {
275                                    resultThrowable = curThrowable;
276    
277                                    continue;
278                            }
279    
280                            if (sanitizedMessage == null) {
281                                    sanitizedMessage = message;
282                            }
283    
284                            sanitized = true;
285    
286                            resultThrowable = new LogSanitizerException(
287                                    sanitizedMessage, curThrowable.getStackTrace(),
288                                    resultThrowable);
289                    }
290    
291                    return resultThrowable;
292            }
293    
294            protected static final String CRLF_WARNING =
295                    "SanitizerLogWrapper warning: Following message contains CRLF " +
296                            "characters\n";
297    
298            private static final String _LESS_THAN_ESCAPED = "&lt;";
299    
300            private static final String _SANITIZED = " [Sanitized]";
301    
302            private static boolean _LOG_SANITIZER_ENABLED = GetterUtil.getBoolean(
303                    SystemProperties.get(PropsKeys.LOG_SANITIZER_ENABLED));
304    
305            private static boolean _LOG_SANITIZER_ESCAPE_HTML_ENABLED = false;
306    
307            private static char _LOG_SANITIZER_REPLACEMENT_CHARACTER =
308                    CharPool.UNDERLINE;
309    
310            private static int[] _whitelistCharacters = new int[128];
311    
312            private boolean _allowCRLF;
313    
314    }