Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: - Redistributions of source code must + * retain the above copyright notice, this list of conditions and the following disclaimer. - + * Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided + * with the distribution. - Neither the name of Coverity, Inc. nor the names of its contributors + * may be used to endorse or promote products derived from this software without specific prior + * written permission from Coverity, Inc. + * + *
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND INFRINGEMENT ARE DISCLAIMED. IN NO EVENT + * SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + /** + * Escape is a small set of methods for escaping tainted data. These escaping methods are useful + * in transforming user-controlled ("tainted") data into forms that are safe from being + * interpreted as something other than data, such as JavaScript. + * + *
At this time most of these escaping routines focus on cross-site scripting mitigations. Each + * method is good for a different HTML context. For a primer on HTML contexts, see OWASP's XSS + * Prevention Cheat Sheet (note however that the escaping routines are not implemented exactly + * according to OWASP's recommendations) or the Coverity Security Advisor documentation. Also see + * the Coverity Security Research Laboratory blog on how to properly use each function. + * + *
While Coverity's static analysis product references these escaping routines as exemplars and + * understands their behavior, there is no dependency on Coverity products and these routines are + * completely standalone. Feel free to use them! Just make sure you use them correctly. + * + * @author Romain Gaucher + * @author Andy Chou + * @author Jon Passki + * @author Alex Kouzemtchenko + */ + private static class Escape { + + /** + * HTML entity escaping for text content and attributes. + * + *
HTML entity escaping that is appropriate for the most common HTML contexts: PCDATA and
+ * "normal" attributes (non-URI, non-event, and non-CSS attributes).
+ * Note that we do not recommend using non-quoted HTML attributes since the security obligations
+ * vary more between web browser. We recommend to always quote (single or double quotes) HTML
+ * attributes.
+ * This method is generic to HTML entity escaping, and therefore escapes more characters than
+ * usually necessary -- mostly to handle non-quoted attribute values. If this method is somehow
+ * too slow, such as you output megabytes of text with spaces, please use the {@link
+ * #htmlText(String)} method which only escape HTML text specific characters.
+ *
+ *
The following characters are escaped: + * + *
' (U+0022), " (U+0027), \ (U+005C)
+ * , / (U+002F), < (U+003C), > (U+003E)
+ * , & (U+0026)
+ * \t (U+0009), \n (U+000A),
+ * \f (U+000C), \r (U+000D), SPACE (U+0020)
+ * LS (U+2028), PS (U+2029)
+ * null if input is null
+ * @since 1.0
+ */
+ private static String html(String input) {
+ if (input == null) return null;
+
+ int length = input.length();
+ StringBuilder output = allocateStringBuilder(length);
+
+ for (int i = 0; i < length; i++) {
+ char c = input.charAt(i);
+ switch (c) {
+ // Control chars
+ case '\t':
+ output.append(" ");
+ break;
+ case '\n':
+ output.append("
");
+ break;
+ case '\f':
+ output.append("");
+ break;
+ case '\r':
+ output.append("
");
+ break;
+ // Chars that have a meaning for HTML
+ case '\'':
+ output.append("'");
+ break;
+ case '\\':
+ output.append("\");
+ break;
+ case ' ':
+ output.append(" ");
+ break;
+ case '/':
+ output.append("/");
+ break;
+ case '"':
+ output.append(""");
+ break;
+ case '<':
+ output.append("<");
+ break;
+ case '>':
+ output.append(">");
+ break;
+ case '&':
+ output.append("&");
+ break;
+ // Unicode new lines
+ case '\u2028':
+ output.append("
");
+ break;
+ case '\u2029':
+ output.append("
");
+ break;
+
+ default:
+ output.append(c);
+ break;
+ }
+ }
+ return output.toString();
+ }
+
+ /**
+ * URI encoder.
+ *
+ * URI encoding for query string values of the URI:
+ * /example/?name=URI_ENCODED_VALUE_HERE
+ * Note that this method is not sufficient to protect for cross-site scripting in a generic URI
+ * context, but only for query string values. If you need to escape a URI in an href
+ * attribute (for example), ensure that:
+ *
+ *
' (U+0022), " (U+0027), \ (U+005C)
+ * , / (U+002F), < (U+003C), > (U+003E)
+ * , & (U+0026), < (U+003C), > (U+003E)
+ * , ! (U+0021), # (U+0023), $ (U+0024),
+ * % (U+0025), ( (U+0028), ) (U+0029),
+ * * (U+002A), + (U+002B), , (U+002C), . (U+002E)
+ * , : (U+003A), ; (U+003B), = (U+003D),
+ * ? (U+003F), @ (U+0040), [ (U+005B),
+ * ] (U+005D)
+ * \t (U+0009), \n (U+000A),
+ * \f (U+000C), \r (U+000D), SPACE (U+0020)
+ * null if input is null
+ * @since 1.0
+ */
+ private static String uriParam(String input) {
+ if (input == null) return null;
+
+ int length = input.length();
+ StringBuilder output = allocateStringBuilder(length);
+
+ for (int i = 0; i < length; i++) {
+ char c = input.charAt(i);
+ switch (c) {
+ // Control chars
+ case '\t':
+ output.append("%09");
+ break;
+ case '\n':
+ output.append("%0A");
+ break;
+ case '\f':
+ output.append("%0C");
+ break;
+ case '\r':
+ output.append("%0D");
+ break;
+ // RFC chars to encode, plus % ' " < and >, and space
+ case ' ':
+ output.append("%20");
+ break;
+ case '!':
+ output.append("%21");
+ break;
+ case '"':
+ output.append("%22");
+ break;
+ case '#':
+ output.append("%23");
+ break;
+ case '$':
+ output.append("%24");
+ break;
+ case '%':
+ output.append("%25");
+ break;
+ case '&':
+ output.append("%26");
+ break;
+ case '\'':
+ output.append("%27");
+ break;
+ case '(':
+ output.append("%28");
+ break;
+ case ')':
+ output.append("%29");
+ break;
+ case '*':
+ output.append("%2A");
+ break;
+ case '+':
+ output.append("%2B");
+ break;
+ case ',':
+ output.append("%2C");
+ break;
+ case '.':
+ output.append("%2E");
+ break;
+ case '/':
+ output.append("%2F");
+ break;
+ case ':':
+ output.append("%3A");
+ break;
+ case ';':
+ output.append("%3B");
+ break;
+ case '<':
+ output.append("%3C");
+ break;
+ case '=':
+ output.append("%3D");
+ break;
+ case '>':
+ output.append("%3E");
+ break;
+ case '?':
+ output.append("%3F");
+ break;
+ case '@':
+ output.append("%40");
+ break;
+ case '[':
+ output.append("%5B");
+ break;
+ case ']':
+ output.append("%5D");
+ break;
+
+ default:
+ output.append(c);
+ break;
+ }
+ }
+ return output.toString();
+ }
+
+ /** Compute the allocation size of the StringBuilder based on the length. */
+ private static StringBuilder allocateStringBuilder(int length) {
+ // Allocate enough temporary buffer space to avoid reallocation in most
+ // cases. If you believe you will output large amount of data at once
+ // you might need to change the factor.
+ int buflen = length;
+ if (length * 2 > 0) buflen = length * 2;
+ return new StringBuilder(buflen);
+ }
+ }
}
diff --git a/src/main/java/io/github/pixee/security/ObjectInputFilters.java b/src/main/java/io/github/pixee/security/ObjectInputFilters.java
index 9f2d7ab..58b42ae 100644
--- a/src/main/java/io/github/pixee/security/ObjectInputFilters.java
+++ b/src/main/java/io/github/pixee/security/ObjectInputFilters.java
@@ -6,7 +6,6 @@
import java.io.ObjectInputStream;
import java.util.Objects;
import org.apache.commons.io.serialization.ValidatingObjectInputStream;
-import org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement;
/**
* This type exposes helper methods that will help defend against Java deserialization attacks.
@@ -15,7 +14,6 @@
* href="https://cheatsheetseries.owasp.org/cheatsheets/Deserialization_Cheat_Sheet.html">OWASP
* Cheat Sheet.
*/
-@IgnoreJRERequirement
public final class ObjectInputFilters {
private ObjectInputFilters() {}
@@ -80,7 +78,6 @@ public static ObjectInputFilter createCombinedHardenedObjectFilter(
return new CombinedObjectInputFilter(existingFilter);
}
- @IgnoreJRERequirement
private static class CombinedObjectInputFilter implements ObjectInputFilter {
private final ObjectInputFilter originalFilter;
diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java
new file mode 100644
index 0000000..38e9e30
--- /dev/null
+++ b/src/main/java/module-info.java
@@ -0,0 +1,10 @@
+/** Export our package so that it can be used by other modules. */
+open module io.github.pixee.security {
+ exports io.github.pixee.security;
+ exports io.github.pixee.security.jakarta;
+
+ requires org.apache.commons.io;
+ requires java.xml;
+ requires java.desktop;
+ requires java.base;
+}