Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
nahsra committed Sep 21, 2023
1 parent 6214dbb commit 24b1a5b
Show file tree
Hide file tree
Showing 4 changed files with 304 additions and 50 deletions.
52 changes: 7 additions & 45 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

<groupId>io.github.pixee</groupId>
<artifactId>java-security-toolkit</artifactId>
<version>1.0.7</version>
<version>1.0.8</version>

<name>java-security-toolkit</name>
<description>a library with common security controls</description>
Expand Down Expand Up @@ -63,12 +63,11 @@
</properties>

<dependencies>

<!-- needed for XSS escapers -->
<!-- needed for ValidatingObjectInputStream in deserialization -->
<dependency>
<groupId>com.coverity.security</groupId>
<artifactId>coverity-escapers</artifactId>
<version>1.1.1</version>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.13.0</version>
</dependency>

<!-- needed for command line parsers -->
Expand All @@ -78,18 +77,6 @@
<version>2.1</version>
</dependency>

<!-- needed for ValidatingObjectInputStream in deserialization -->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.11.0</version>
</dependency>
<dependency>
<groupId>org.codehaus.mojo</groupId>
<artifactId>animal-sniffer-annotations</artifactId>
<version>1.23</version>
<optional>true</optional>
</dependency>
<!-- needed for testing deserialization protection -->
<dependency>
<groupId>commons-fileupload</groupId>
Expand Down Expand Up @@ -162,30 +149,9 @@
<artifactId>maven-compiler-plugin</artifactId>
<version>${versions.maven-compiler-plugin}</version>
<configuration>
<source>8</source>
<target>8</target>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>animal-sniffer-maven-plugin</artifactId>
<version>1.23</version>
<configuration>
<signature>
<groupId>org.codehaus.mojo.signature</groupId>
<artifactId>java18</artifactId>
<version>1.0</version>
</signature>
<source>11</source>
<target>11</target>
</configuration>
<executions>
<execution>
<id>animal-sniffer</id>
<phase>test</phase>
<goals>
<goal>check</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.jacoco</groupId>
Expand Down Expand Up @@ -276,10 +242,6 @@
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>animal-sniffer-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
Expand Down
290 changes: 288 additions & 2 deletions src/main/java/io/github/pixee/security/HtmlEncoder.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
package io.github.pixee.security;

import com.coverity.security.Escape;

/**
* This type exposes helper methods that will help defend against XSS attacks with HTML encoding.
*
Expand All @@ -21,4 +19,292 @@ private HtmlEncoder() {}
public static String encode(final String s) {
return Escape.html(s);
}

/*
* This code was originally brought in as the BSD-2 licensed dependency from Coverity. However, it didn't publish any automatic module name, and we didn't really need any of the rest of it, so we just copied the code here, including the license.
*/

/**
* Copyright (c) 2012-2016, Coverity, Inc. All rights reserved.
*
* <p>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.
*
* <p>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.
*
* <p>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.
*
* <p>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.
*
* <p>HTML entity escaping that is appropriate for the most common HTML contexts: PCDATA and
* "normal" attributes (non-URI, non-event, and non-CSS attributes). <br>
* 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.<br>
* 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.
*
* <p>The following characters are escaped:
*
* <ul>
* <li>HTML characters: <code>' (U+0022)</code>, <code>" (U+0027)</code>, <code>\ (U+005C)
* </code>, <code>/ (U+002F)</code>, <code>&lt; (U+003C)</code>, <code>&gt; (U+003E)
* </code>, <code>&amp; (U+0026)</code>
* <li>Control characters: <code>\t (U+0009)</code>, <code>\n (U+000A)</code>, <code>
* \f (U+000C)</code>, <code>\r (U+000D)</code>, <code>SPACE (U+0020)</code>
* <li>Unicode newlines: <code>LS (U+2028)</code>, <code>PS (U+2029)</code>
* </ul>
*
* @param input the string to be escaped
* @return the HTML escaped string or <code>null</code> if <code>input</code> 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("&#x09;");
break;
case '\n':
output.append("&#x0A;");
break;
case '\f':
output.append("&#x0C;");
break;
case '\r':
output.append("&#x0D;");
break;
// Chars that have a meaning for HTML
case '\'':
output.append("&#39;");
break;
case '\\':
output.append("&#x5C;");
break;
case ' ':
output.append("&#x20;");
break;
case '/':
output.append("&#x2F;");
break;
case '"':
output.append("&quot;");
break;
case '<':
output.append("&lt;");
break;
case '>':
output.append("&gt;");
break;
case '&':
output.append("&amp;");
break;
// Unicode new lines
case '\u2028':
output.append("&#x2028;");
break;
case '\u2029':
output.append("&#x2029;");
break;

default:
output.append(c);
break;
}
}
return output.toString();
}

/**
* URI encoder.
*
* <p>URI encoding for query string values of the URI: <code>
* /example/?name=URI_ENCODED_VALUE_HERE</code> <br>
* 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 <code>href
* </code> attribute (for example), ensure that:
*
* <ul>
* <li>The scheme is allowed (restrict to http, https, or mailto)
* <li>Use the HTML escaper {@link #html(String)} on the entire URI
* </ul>
*
* This URI encoder processes the following characters:
*
* <ul>
* <li>URI characters: <code>' (U+0022)</code>, <code>" (U+0027)</code>, <code>\ (U+005C)
* </code>, <code>/ (U+002F)</code>, <code>&lt; (U+003C)</code>, <code>&gt; (U+003E)
* </code>, <code>&amp; (U+0026)</code>, <code>&lt; (U+003C)</code>, <code>&gt; (U+003E)
* </code>, <code>! (U+0021)</code>, <code># (U+0023)</code>, <code>$ (U+0024)</code>,
* <code>% (U+0025)</code>, <code>( (U+0028)</code>, <code>) (U+0029)</code>, <code>
* * (U+002A)</code>, <code>+ (U+002B)</code>, <code>, (U+002C)</code>, <code>. (U+002E)
* </code>, <code>: (U+003A)</code>, <code>; (U+003B)</code>, <code>= (U+003D)</code>,
* <code>? (U+003F)</code>, <code>@ (U+0040)</code>, <code>[ (U+005B)</code>, <code>
* ] (U+005D)</code>
* <li>Control characters: <code>\t (U+0009)</code>, <code>\n (U+000A)</code>, <code>
* \f (U+000C)</code>, <code>\r (U+000D)</code>, <code>SPACE (U+0020)</code>
* </ul>
*
* @param input the string to be escaped
* @return the URI encoded string or <code>null</code> if <code>input</code> 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);
}
}
}
Loading

0 comments on commit 24b1a5b

Please sign in to comment.