1 // Copyright (c) 2012 Jeff Ichnowski 2 // All rights reserved. 3 // 4 // Redistribution and use in source and binary forms, with or without 5 // modification, are permitted provided that the following conditions 6 // are met: 7 // 8 // * Redistributions of source code must retain the above 9 // copyright notice, this list of conditions and the following 10 // disclaimer. 11 // 12 // * Redistributions in binary form must reproduce the above 13 // copyright notice, this list of conditions and the following 14 // disclaimer in the documentation and/or other materials 15 // provided with the distribution. 16 // 17 // * Neither the name of the OWASP nor the names of its 18 // contributors may be used to endorse or promote products 19 // derived from this software without specific prior written 20 // permission. 21 // 22 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 23 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 24 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 25 // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 26 // COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 27 // INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 28 // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 29 // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 31 // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 32 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 33 // OF THE POSSIBILITY OF SUCH DAMAGE. 34 35 package org.owasp.encoder.esapi; 36 37 import java.io.IOException; 38 import java.net.URI; 39 import org.owasp.encoder.Encode; 40 import org.owasp.esapi.Encoder; 41 import org.owasp.esapi.codecs.Codec; 42 import org.owasp.esapi.errors.EncodingException; 43 import org.owasp.esapi.reference.DefaultEncoder; 44 45 /** 46 * ESAPIEncoder is a singleton implementation of the ESAPI Encoder API. It 47 * is meant to allow quick and easy drop-in replacement of the default 48 * encoder included with the ESAPI library, as the Encoder libraries are 49 * faster and use less memory thus cause fewer garbage collections. 50 * 51 * <p>Please note that the OWASP Java Encoders does not implement all 52 * the encodings of the ESAPI Encoder API. In such situations this 53 * implementation will fallback onto the default reference implementation 54 * included with ESAPI. Thus you should see the performance benefit from 55 * the methods included in the Encoder, but still maintain compatibility 56 * with all methods from ESAPI Encoder.</p> 57 * 58 * <p>For clarity, the reason the OWASP Java Encoders do not include some 59 * of the ESAPI library is that the Encoders library is specifically focused 60 * on <i>encoding</i>, and thus does not include:</p> 61 * 62 * <ul> 63 * <li>Input validation/normalization methods: 64 * {@link org.owasp.esapi.Encoder#canonicalize(String)}, 65 * {@link org.owasp.esapi.Encoder#canonicalize(String, boolean)}, 66 * {@link org.owasp.esapi.Encoder#canonicalize(String, boolean, boolean)} 67 * {@link org.owasp.esapi.Encoder#getCanonicalizedURI(URI)}</li> 68 * 69 * <li>Decoding methods: 70 * {@link org.owasp.esapi.Encoder#decodeForHTML(String)}, 71 * {@link org.owasp.esapi.Encoder#decodeFromURL(String)}</li> 72 * 73 * <li>Binary-to-text/text-to-binary: 74 * {@link org.owasp.esapi.Encoder#encodeForBase64(byte[], boolean)}, 75 * {@link org.owasp.esapi.Encoder#decodeFromBase64(String)}.</li> 76 * 77 * <li>Bind-able APIs (such as {@link java.sql.PreparedStatement}: 78 * {@link org.owasp.esapi.Encoder#encodeForSQL(org.owasp.esapi.codecs.Codec, String)}, 79 * {@link org.owasp.esapi.Encoder#encodeForXPath(String)}, 80 * {@link org.owasp.esapi.Encoder#encodeForOS(org.owasp.esapi.codecs.Codec, String)}</li> 81 * 82 * <li>Rarely-used or alternate compatible encoding: 83 * {@link org.owasp.esapi.Encoder#encodeForVBScript(String)}, 84 * {@link org.owasp.esapi.Encoder#encodeForLDAP(String)}, 85 * {@link org.owasp.esapi.Encoder#encodeForLDAP(String, boolean)}, 86 * {@link org.owasp.esapi.Encoder#encodeForDN(String)}</li> 87 * </ul> 88 * 89 * <p>(Please note that with sufficient feedback from the user base, the above 90 * mentioned methods may be implemented in future releases of the OWASP 91 * Java Encoders, if/when that happens, this shim class will be updated to 92 * call out to the new methods.)</p> 93 * 94 * <p>You may notice that this class does not actually implement Encoder 95 * itself. Instead it simply provides a {@link #getInstance()} method that 96 * does. This allows the implementation details maximum flexibility by not 97 * creating a any public API that would restrict changes later</p> 98 * 99 * @author jeffi 100 */ 101 public final class ESAPIEncoder { 102 103 /** No instances. */ ESAPIEncoder()104 private ESAPIEncoder() {} 105 106 /** 107 * Returns an instance of the Encoder. This method is the only supported 108 * mechanism by which an ESAPIEncoder instance should be obtained. The 109 * returned implementation is guaranteed to be thread-safe for the methods 110 * that the OWASP Java Encoders implement (see class documentation). 111 * Though not a requirement of the ESAPI Encoder API, the returned value 112 * is also serializable. 113 * 114 * @return An encoder implementation that uses the OWASP Java Encoders 115 * for most of the common encoding methods. 116 */ getInstance()117 public static Encoder getInstance() { 118 return Impl.INSTANCE; 119 } 120 121 /** 122 * This is the private singleton that implements the ESAPI Encoder shim. 123 * It is implemented as a single-value enum to get all the "free" singleton 124 * properties associated with enums--such as serialization, and on-demand 125 * initialization. 126 * 127 * <p>The implementation is intentionally private to avoid any API baggage. 128 * The instance should be obtained using 129 * {@link org.owasp.encoder.esapi.ESAPIEncoder#getInstance()}.</p> 130 */ 131 private enum Impl implements Encoder { 132 /** 133 * The singleton instance. 134 */ 135 INSTANCE; 136 137 /** 138 * The reference encoder from ESAPI. Any ESAPI method without an 139 * OWASP Java Encoder equivalent is delegated to this instance. 140 */ 141 private final Encoder _referenceEncoder = DefaultEncoder.getInstance(); 142 143 /** {@inheritDoc} */ canonicalize(String s)144 public String canonicalize(String s) { 145 return _referenceEncoder.canonicalize(s); 146 } 147 148 /** {@inheritDoc} */ canonicalize(String s, boolean strict)149 public String canonicalize(String s, boolean strict) { 150 return _referenceEncoder.canonicalize(s, strict); 151 } 152 153 /** {@inheritDoc} */ canonicalize(String s, boolean restrictMultiple, boolean restrictMixed)154 public String canonicalize(String s, boolean restrictMultiple, boolean restrictMixed) { 155 return _referenceEncoder.canonicalize(s, restrictMultiple, restrictMixed); 156 } 157 158 /** {@inheritDoc} */ getCanonicalizedURI(URI dirtyUri)159 public String getCanonicalizedURI(URI dirtyUri) { 160 return _referenceEncoder.getCanonicalizedURI(dirtyUri); 161 } 162 163 /** {@inheritDoc} */ encodeForCSS(String s)164 public String encodeForCSS(String s) { 165 return Encode.forCssString(s); 166 } 167 168 /** {@inheritDoc} */ encodeForHTML(String s)169 public String encodeForHTML(String s) { 170 return Encode.forHtml(s); 171 } 172 173 /** {@inheritDoc} */ decodeForHTML(String s)174 public String decodeForHTML(String s) { 175 return _referenceEncoder.decodeForHTML(s); 176 } 177 178 /** {@inheritDoc} */ encodeForHTMLAttribute(String s)179 public String encodeForHTMLAttribute(String s) { 180 return Encode.forHtmlAttribute(s); 181 } 182 183 /** {@inheritDoc} */ encodeForJavaScript(String s)184 public String encodeForJavaScript(String s) { 185 return Encode.forJavaScript(s); 186 } 187 188 /** {@inheritDoc} */ encodeForVBScript(String s)189 public String encodeForVBScript(String s) { 190 return _referenceEncoder.encodeForVBScript(s); 191 } 192 193 /** {@inheritDoc} */ encodeForSQL(Codec codec, String s)194 public String encodeForSQL(Codec codec, String s) { 195 return _referenceEncoder.encodeForSQL(codec, s); 196 } 197 198 /** {@inheritDoc} */ encodeForOS(Codec codec, String s)199 public String encodeForOS(Codec codec, String s) { 200 return _referenceEncoder.encodeForOS(codec, s); 201 } 202 203 /** {@inheritDoc} */ encodeForLDAP(String s)204 public String encodeForLDAP(String s) { 205 return _referenceEncoder.encodeForLDAP(s); 206 } 207 208 /** {@inheritDoc} */ encodeForLDAP(String s, boolean b)209 public String encodeForLDAP(String s, boolean b) { 210 return _referenceEncoder.encodeForLDAP(s, b); 211 } 212 213 /** {@inheritDoc} */ encodeForDN(String s)214 public String encodeForDN(String s) { 215 return _referenceEncoder.encodeForDN(s); 216 } 217 218 /** {@inheritDoc} */ encodeForXPath(String s)219 public String encodeForXPath(String s) { 220 return _referenceEncoder.encodeForXPath(s); 221 } 222 223 /** {@inheritDoc} */ encodeForXML(String s)224 public String encodeForXML(String s) { 225 return Encode.forXml(s); 226 } 227 228 /** {@inheritDoc} */ encodeForXMLAttribute(String s)229 public String encodeForXMLAttribute(String s) { 230 return Encode.forXmlAttribute(s); 231 } 232 233 /** {@inheritDoc} */ encodeForURL(String s)234 public String encodeForURL(String s) throws EncodingException { 235 return Encode.forUri(s); 236 } 237 238 /** {@inheritDoc} */ decodeFromURL(String s)239 public String decodeFromURL(String s) throws EncodingException { 240 return _referenceEncoder.decodeFromURL(s); 241 } 242 243 /** {@inheritDoc} */ encodeForBase64(byte[] bytes, boolean wrap)244 public String encodeForBase64(byte[] bytes, boolean wrap) { 245 return _referenceEncoder.encodeForBase64(bytes, wrap); 246 } 247 248 /** {@inheritDoc} */ decodeFromBase64(String s)249 public byte[] decodeFromBase64(String s) throws IOException { 250 return _referenceEncoder.decodeFromBase64(s); 251 } 252 253 } 254 } 255