Skip to content

CWE-80: Cross-Site Scripting (XSS) - Java

Overview

Cross-Site Scripting (CWE-80) occurs when untrusted data is included in web pages without proper encoding. Attackers inject malicious scripts that execute in victim browsers, leading to session theft, credential harvesting, or defacement. Java applications must encode all user-controlled output using context-appropriate methods.

Primary Defence: Encode untrusted output for the exact browser context before rendering it. Use JSTL's <c:out> tag for ordinary JSP HTML text and quoted attributes, framework-native escaped bindings such as Thymeleaf th:text, and OWASP Java Encoder methods such as Encode.forHtml(), Encode.forJavaScript(), and Encode.forCssString() for servlet output and mixed contexts. Treat CSP and input validation as supporting controls, not substitutes for contextual output encoding.

Common Vulnerable Patterns

Direct Output to JSP Without Encoding

<%-- VULNERABLE - scriptlet with no encoding --%>
<div>Welcome, <%= request.getParameter("username") %></div>

<%-- VULNERABLE - EL without c:out --%>
<p>Comment: ${param.comment}</p>

<%-- VULNERABLE - attribute without encoding --%>
<input type="text" value="<%= request.getParameter("search") %>">

Attack Examples:

username=<script>alert(document.cookie)</script>
comment=<img src=x onerror=alert('XSS')>
search="><script>alert(1)</script>

Servlet PrintWriter Without Encoding

// VULNERABLE - Direct output
public void doGet(HttpServletRequest request, HttpServletResponse response) 
    throws ServletException, IOException {
    String name = request.getParameter("name");
    response.setContentType("text/html");
    PrintWriter out = response.getWriter();
    out.println("<h1>Welcome " + name + "!</h1>");  // NO ENCODING
}

// VULNERABLE - StringBuilder concatenation
String comment = request.getParameter("comment");
StringBuilder html = new StringBuilder();
html.append("<div class='comment'>");
html.append(comment);  // NO ENCODING
html.append("</div>");
response.getWriter().write(html.toString());

JavaScript Context Without Encoding

// VULNERABLE - User data in JavaScript
String userName = request.getParameter("user");
out.println("<script>");
out.println("var currentUser = '" + userName + "';");  // NO JS ENCODING
out.println("</script>");

// Attack: user='; alert(document.cookie); //
// Result: var currentUser = ''; alert(document.cookie); //';

URL Context Without Encoding

// VULNERABLE - Unencoded URL parameters
String redirect = request.getParameter("returnUrl");
out.println("<a href=\"" + redirect + "\">Continue</a>");  // NO URL ENCODING

// Attack: returnUrl=javascript:alert('XSS')

Thymeleaf th:utext (Unescaped)

<!-- VULNERABLE - th:utext bypasses escaping -->
<div th:utext="${userInput}">Content</div>

<!-- VULNERABLE - Using raw HTML from user -->
<p th:utext="${request.getParameter('content')}"></p>

JSON Responses with Manual Construction

// VULNERABLE - Manual JSON construction
public void doGet(HttpServletRequest request, HttpServletResponse response) {
    String name = request.getParameter("name");
    String json = "{\"userName\":\"" + name + "\"}";
    response.setContentType("application/json");
    response.getWriter().write(json);  // NO ESCAPING
}

// Attack: name=test","admin":true,"x":"y
// Result: {"userName":"test","admin":true,"x":"y"}

Secure Patterns

JSTL c:out in JSP

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<!-- SAFE - c:out auto-escapes HTML -->
<div>Welcome, <c:out value="${param.username}"/></div>

<!-- SAFE - Attribute context -->
<input type="text" value="<c:out value='${param.search}'/>"/>

<!-- SAFE - With default value -->
<p><c:out value="${user.bio}" default="No bio available"/></p>

<!-- SAFE - Disable escaping only for trusted content -->
<c:out value="${trustedHtml}" escapeXml="false"/>  <!-- USE WITH CAUTION -->

CRITICAL: The JSTL taglib must be available, either via:

  1. Per-file declaration (recommended): <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> at the top of each JSP
  2. Global web.xml configuration:

    web.xml
    <jsp-config>
        <taglib>
        <taglib-uri>http://java.sun.com/jsp/jstl/core</taglib-uri>
        <taglib-location>/WEB-INF/tld/c.tld</taglib-location>
        </taglib>
    </jsp-config>
    
  3. Auto-discovery: Some containers auto-discover taglibs from JARs in WEB-INF/lib (unreliable, avoid)

Without a valid taglib configuration, behavior depends on the JSP container and configuration: the page may fail translation, or the tag may not execute as intended. In either case, do not assume <c:out> is protecting the output unless the taglib is declared and the page is verified. Always use explicit per-file declarations for clarity and reliability.

Why this works: The <c:out> tag from JSTL XML/HTML-escapes output by default (when escapeXml is true or unspecified), converting dangerous characters like <, >, &, and quotes into entity form. This prevents browsers from interpreting user input as HTML markup or JavaScript code in ordinary HTML body and quoted attribute contexts. The encoding happens at the output stage, ensuring that malicious content like <script>alert(1)</script> renders as text instead of executing. This is a strong JSP default, but it is not a JavaScript, CSS, or URL validator; use context-specific encoders for those sinks and validate unsafe URL schemes.

import org.owasp.encoder.Encode;

public void doGet(HttpServletRequest request, HttpServletResponse response) 
    throws ServletException, IOException {

    String name = request.getParameter("name");
    String comment = request.getParameter("comment");

    response.setContentType("text/html; charset=UTF-8");
    PrintWriter out = response.getWriter();

    // SAFE - HTML context
    out.println("<h1>Welcome " + Encode.forHtml(name) + "!</h1>");

    // SAFE - HTML attribute context
    out.println("<div class=\"" + Encode.forHtmlAttribute(comment) + "\">");

    // SAFE - JavaScript context
    out.println("<script>");
    out.println("var user = '" + Encode.forJavaScript(name) + "';");
    out.println("</script>");

    // SAFE - URL context
    String search = request.getParameter("q");
    String url = "/search?q=" + Encode.forUriComponent(search);
    out.println("<a href=\"" + Encode.forHtmlAttribute(url) + "\">Search</a>");
}

Why this works: OWASP Java Encoder provides context-aware encoding methods that apply the correct transformations for each output context. Encode.forHtml() converts characters like <>\"'& to HTML entities, preventing tag injection in HTML body content. Encode.forHtmlAttribute() additionally handles quote escaping for safe use in attributes. Encode.forJavaScript() uses Unicode escaping (\\xHH or \\uHHHH) to neutralize special characters in JavaScript strings, preventing script injection even when user input contains quotes or control characters. Encode.forUriComponent() applies percent-encoding for URL parameters. Each method is specifically designed for its context, making it impossible for attackers to break out of the intended data context and inject malicious code. This library is battle-tested, actively maintained, and recommended by OWASP for production use.

Spring HtmlUtils

import org.springframework.web.util.HtmlUtils;

@GetMapping("/profile")
public String showProfile(@RequestParam String name, Model model) {
    // SAFE - Escape before adding to model
    String safeName = HtmlUtils.htmlEscape(name);
    model.addAttribute("userName", safeName);
    return "profile";
}

// Or in template:
<%@ page import="org.springframework.web.util.HtmlUtils" %>
<div><%= HtmlUtils.htmlEscape(request.getParameter("name")) %></div>

Why this works: Spring's HtmlUtils.htmlEscape() performs HTML entity encoding by converting special characters (<, >, &, ", ') into their HTML entity equivalents (&lt;, &gt;, &amp;, &quot;, &#39;). This transformation ensures that even if user input contains HTML tags or JavaScript, browsers will render it as plain text rather than interpreting it as executable code. The method is part of the Spring Framework's core utilities and is specifically designed for web output encoding. It handles both ISO-8859-1 and UTF-8 character sets correctly. When used in Spring MVC controllers, encoding the data before adding it to the model ensures that XSS payloads are neutralized before reaching the view layer, providing defense in depth even if the view template has vulnerabilities.

Thymeleaf (Spring Boot) - Auto-Escaping

<!-- SAFE - th:text auto-escapes -->
<div th:text="${userInput}">Default</div>

<!-- SAFE - Attribute binding -->
<input type="text" th:value="${searchTerm}"/>

<!-- SAFE - URL parameters -->
<a th:href="@{/search(q=${query})}">Search</a>

<!-- SAFE - Multiple attributes -->
<div th:attr="data-user=${userName}, data-id=${userId}"></div>

Why this works: Thymeleaf's th:text attribute automatically HTML-encodes all content before rendering, preventing XSS by converting dangerous characters to HTML entities. The th:attr and attribute-specific directives (like th:href, th:value) provide context-aware encoding: they ensure values are properly escaped for their specific context (HTML content, attributes, URLs, etc.). The @{...} URL syntax automatically encodes query parameters. This auto-escaping is enabled by default in Spring Boot and cannot be accidentally bypassed unless you explicitly use th:utext (unescaped text), making it a secure-by-default approach.

JSF (JavaServer Faces)

<!-- SAFE - h:outputText escapes by default -->
<h:outputText value="#{userBean.name}"/>

<!-- SAFE - escape=true (explicit) -->
<h:outputText value="#{userBean.comment}" escape="true"/>

<!-- SAFE - Input components auto-escape -->
<h:inputText value="#{userBean.search}"/>

Why this works: JavaServer Faces (JSF) components like <h:outputText> automatically HTML-encode output by default through their escape="true" attribute (which is the default behavior when escape is not specified). This built-in encoding converts special HTML characters to entities before rendering, preventing XSS. The encoding is context-aware and happens during the Render Response phase of the JSF lifecycle, ensuring that data bound via Expression Language (EL) like #{userBean.name} is always safe. JSF input components (<h:inputText>, <h:inputTextarea>, etc.) also automatically escape values when rendering, protecting against reflected XSS. This secure-by-default behavior makes JSF applications resistant to XSS as long as developers don't explicitly set escape="false" on user-controlled data.

Jackson for JSON (REST APIs)

import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

@RestController
public class UserController {

    @GetMapping("/api/user")
    public ResponseEntity<Map<String, String>> getUser(@RequestParam String name) {
        // SAFE - Jackson serializes values as JSON string data
        Map<String, String> response = new HashMap<>();
        response.put("name", name);  // No manual escaping needed
        response.put("timestamp", Instant.now().toString());

        return ResponseEntity.ok(response);  // SAFE JSON
    }

    // SAFE - Explicit content type enforcement
    @GetMapping(value = "/api/profile", produces = MediaType.APPLICATION_JSON_VALUE)
    public User getUserProfile(@RequestParam String id) {
        // produces ensures response is encoded as JSON
        // Jackson serialization provides automatic escaping
        User user = userService.findById(id);
        return user;  // Jackson handles escaping automatically
    }
}

// How it works:
// - Jackson serializes objects to JSON and escapes JSON syntax characters
// - produces = APPLICATION_JSON_VALUE forces JSON content type
// - Prevents content-type confusion attacks
// - User input like: name = "<script>alert(1)</script>"
//   remains JSON string data, not HTML

Why this works: Jackson serializes Java objects to JSON and escapes JSON syntax characters such as quotes, backslashes, and control characters. It does not replace output encoding for HTML sinks, and characters such as <script> may remain literal JSON string data. The critical security features are: (1) Content-Type is set to application/json, reducing the chance that browsers interpret the response as HTML, and (2) JSON string encoding prevents quote and backslash injection into the JSON structure. The produces = MediaType.APPLICATION_JSON_VALUE annotation enforces JSON output for the endpoint. Add X-Content-Type-Options: nosniff at the application level, and ensure clients render JSON values with safe DOM APIs or auto-escaping frameworks.

JAX-RS (Java EE/Jakarta EE)

import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;

@Path("/api")
public class UserResource {

    // SAFE - @Produces forces JSON serialization
    @GET
    @Path("/user")
    @Produces(MediaType.APPLICATION_JSON)  // Ensures JSON encoding
    public User getUser(@QueryParam("name") String name) {
        // JSON-B or Jackson serializer provides automatic escaping
        User user = new User();
        user.setName(name);  // No manual escaping needed
        return user;
    }

    // SAFE - Explicit Response with JSON content type
    @POST
    @Path("/profile")
    @Consumes(MediaType.APPLICATION_JSON)
    @Produces(MediaType.APPLICATION_JSON)  // Forces JSON output encoding
    public Response createProfile(UserRequest request) {
        Map<String, String> response = new HashMap<>();
        response.put("username", request.getUsername());
        response.put("status", "created");

        // JSON provider handles escaping automatically
        return Response.ok(response).build();
    }
}

// How it works:
// - @Produces(MediaType.APPLICATION_JSON) tells JAX-RS to use JSON serializer
// - JSON provider (Jackson, JSON-B, Gson) escapes JSON syntax characters
// - Prevents XSS by ensuring output is valid, escaped JSON
// - Content-Type header set to application/json automatically

Why this works: The @Produces(MediaType.APPLICATION_JSON) annotation tells JAX-RS to serialize the return value as JSON using a JSON provider (Jackson, JSON-B, or Gson). These JSON libraries escape JSON syntax characters like quotes, backslashes, and control characters according to the JSON specification. The annotation also sets the Content-Type: application/json header, which tells clients to treat the response as data rather than HTML. If user input contains <script> tags, they remain JSON string data; they become dangerous only if a client later injects that value into HTML with an unsafe API such as innerHTML.

Context-Specific Encoding

Context-specific encoding ensures that user input is properly encoded based on where it appears in the output.

HTML Body Context

import org.owasp.encoder.Encode;

String userInput = getUserInput();
out.println("<p>" + Encode.forHtml(userInput) + "</p>");

HTML Attribute Context

String userInput = getUserInput();
out.println("<div class=\"" + Encode.forHtmlAttribute(userInput) + "\">");

JavaScript Context

String userName = getUserName();
out.println("<script>");
out.println("var currentUser = '" + Encode.forJavaScript(userName) + "';");
out.println("</script>");

URL Parameter Context

String searchTerm = getSearchTerm();
String url = "/search?q=" + Encode.forUriComponent(searchTerm);
out.println("<a href=\"" + Encode.forHtmlAttribute(url) + "\">Search</a>");

CSS Context (Avoid if Possible)

// WARNING: CSS context is complex - avoid user input in CSS
String color = getUserColor();
if (!color.matches("^[a-zA-Z0-9#]+$")) {
    throw new SecurityException("Invalid color");
}
out.println("<div style=\"color: " + color + "\">");

Why this works: Each output context (HTML body, attributes, JavaScript, URLs, CSS) has different parsing rules and special characters, requiring context-specific encoding. Using Encode.forHtml() in HTML body context converts <>\"'& to entities, preventing tag injection. Encode.forHtmlAttribute() handles both entity encoding and quote escaping for safe attribute values. Encode.forJavaScript() uses Unicode escape sequences (\\xHH) to neutralize special characters in JavaScript string literals, preventing quote-based injection like '; alert(1)//. Encode.forUriComponent() percent-encodes reserved URL characters to prevent breaking out of the parameter context. Using the wrong encoder (e.g., HTML encoding in JavaScript context) leaves vulnerabilities because each context interprets characters differently. Context-aware encoding is critical - HTML encoding alone won't protect JavaScript contexts where </script> can close a script tag even when HTML-encoded.

Alternative Encoding Libraries

While OWASP Java Encoder, Spring HtmlUtils, and framework-specific methods (JSTL, Thymeleaf, JSF) are recommended, these alternatives may be useful in specific contexts:

OWASP ESAPI Encoder (legacy applications):

import org.owasp.esapi.ESAPI;
String safe = ESAPI.encoder().encodeForHTML(userInput);
// Maven: org.owasp.esapi:esapi

Why this works: These alternative libraries provide XSS protection for applications where the primary recommended libraries are unavailable or incompatible. OWASP ESAPI offers comprehensive security controls including context-aware encoding methods, though it's considered legacy and has a heavier footprint than modern alternatives. Apache Commons Text provides XML-specific escaping useful for XML generation scenarios. Framework-specific utilities (GWT, Liferay, Android) are optimized for their respective platforms and integrate seamlessly with framework conventions. While these alternatives work, prefer OWASP Java Encoder or framework-native solutions when possible for better performance and maintenance.

Apache Commons Text (XML-focused):

import org.apache.commons.text.StringEscapeUtils;
String safeXml = StringEscapeUtils.escapeXml10(userInput);
// Maven: org.apache.commons:commons-text

Framework-Specific Utilities:

  • GWT: SafeHtmlUtils.htmlEscape(userInput)
  • Liferay: HtmlUtil.escape(userInput)
  • Android: Uri.encode(userInput)

Content Security Policy (CSP)

Servlet Filter

@WebFilter("/*")
public class CSPFilter implements Filter {
    public void doFilter(ServletRequest request, ServletResponse response, 
                        FilterChain chain) throws IOException, ServletException {
        HttpServletResponse httpResponse = (HttpServletResponse) response;

        httpResponse.setHeader("Content-Security-Policy",
            "default-src 'self'; " +
            "script-src 'self'; " +
            "style-src 'self'; " +
            "img-src 'self' https://trusted-cdn.com; " +
            "connect-src 'self'; " +
            "frame-ancestors 'none';"
        );

        chain.doFilter(request, response);
    }
}

Spring Security Configuration

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http.headers(headers -> headers
            .contentSecurityPolicy(csp -> csp.policyDirectives(
                "default-src 'self'; " +
                "script-src 'self'; " +
                "object-src 'none'; " +
                "base-uri 'self'; " +
                "frame-ancestors 'none';"
            ))
            .contentTypeOptions(Customizer.withDefaults()));

        return http.build();
    }
}

Input Validation (Defense in Depth)

import org.owasp.validator.html.*;

public class InputValidator {

    public String validateUserInput(String input) {
        // Length check
        if (input.length() > 1000) {
            throw new IllegalArgumentException("Input too long");
        }

        // Allowlist pattern for specific use cases
        if (!input.matches("^[a-zA-Z0-9 .,!?'-]+$")) {
            throw new IllegalArgumentException("Invalid characters");
        }

        return input;
    }

    public String sanitizeHTML(String input) throws ScanException, PolicyException {
        // Use OWASP AntiSamy for rich HTML input
        Policy policy = Policy.getInstance(
            getClass().getResourceAsStream("/antisamy-policy.xml")
        );

        AntiSamy antiSamy = new AntiSamy();
        CleanResults results = antiSamy.scan(input, policy);

        return results.getCleanHTML();
    }
}

JSON/API Responses

Jackson (Automatic Escaping)

import com.fasterxml.jackson.databind.ObjectMapper;

@RestController
public class ApiController {

    @GetMapping("/api/user")
    public User getUser(@RequestParam String name) {
        User user = new User();
        user.setName(name);  // Jackson serializes this as JSON string data
        return user;  // SAFE - JSON is properly escaped
    }
}

Manual JSON Construction (Avoid)

// VULNERABLE - manual JSON construction
String json = "{\"name\":\"" + userName + "\"}";

// SAFE - use JSON library
ObjectMapper mapper = new ObjectMapper();
Map<String, String> data = new HashMap<>();
data.put("name", userName);
String json = mapper.writeValueAsString(data);

Remediation Steps

  1. Trace the finding from the untrusted source to the rendering sink: JSP expression, servlet PrintWriter, template binding, JSON response, JavaScript block, URL, attribute, or CSS value.
  2. Identify the exact browser context where the value appears and choose the matching encoder or escaped framework binding.
  3. Replace raw JSP scriptlets, raw EL output, string-concatenated servlet HTML, and unescaped template attributes with c:out, Thymeleaf escaped bindings, JSF escaped components, or OWASP Java Encoder calls.
  4. For intentionally allowed rich HTML, sanitize with a strict server-side policy and keep raw-rendering opt-outs narrow and reviewed.
  5. Serve API data with Content-Type: application/json through Jackson, JSON-B, or another serializer, and make sure clients render returned values with safe DOM APIs.
  6. Add CSP, X-Content-Type-Options: nosniff, input validation, and scanner/code-review checks after the output encoding fix is in place.

Testing

Security testing requires multiple approaches - unit tests alone are insufficient.

Static Application Security Testing (SAST)

Use automated tools to analyze code for missing or incorrect encoding:

Commercial Tools:

  • Checkmarx - Comprehensive XSS detection for Java web applications
  • Fortify Static Code Analyzer - Data flow analysis from sources to sinks
  • Veracode - Cloud-based static analysis for Java
  • Snyk Code - Developer-focused security scanning

Open Source Tools:

  • SonarQube - Free community edition with XSS detection
  • SpotBugs with Find Security Bugs plugin
  • Semgrep - Fast, customizable pattern matching

Dynamic Application Security Testing (DAST)

Test running applications for XSS:

  • OWASP ZAP - Free automated scanner
  • Burp Suite Professional - Industry-standard web security testing
  • Acunetix - Comprehensive XSS detection
  • Arachni - Open-source web application security scanner

Code Review Checklist

Manually verify:

  • All JSP output uses <c:out> or proper encoding
  • Every JSP file using <c:out> has the JSTL taglib declaration, or global web.xml declaration
  • No raw <%= %> scriptlets without encoding
  • Servlets use context-appropriate encoding (Encode.forHtml(), Encode.forJavaScript(), etc.) for rendering all untrusted input
  • Thymeleaf uses th:text (not th:utext) for user data
  • No escapeXml="false" on user input
  • Context-appropriate encoding (HTML vs JS vs URL vs CSS)
  • JSON responses use proper serialization libraries
  • All input sources identified (params, headers, cookies, database)

Framework-Specific Tools

Spring Boot:

# OWASP Dependency Check
mvn org.owasp:dependency-check-maven:check

# Spring Security audits
mvn spring-boot:run -Dspring-boot.run.arguments=--debug

JSP/Servlet:

# Check for unescaped output
grep -r "<%= " src/main/webapp/
grep -r "escapeXml=\"false\"" src/main/webapp/

# Check for c:out usage without taglib declaration
# (Find JSP files using <c:out> but missing the taglib)
find src/main/webapp -name "*.jsp" -exec sh -c \
  'grep -l "<c:out" "$1" | xargs grep -L "taglib.*jstl/core"' _ {} \;

Limited Role of Unit Tests

Manual verification with XSS payloads:

// Test these payloads in your application:
String[] xssPayloads = {
    "<script>alert('XSS')</script>",
    "<img src=x onerror=alert('XSS')>",
    "javascript:alert('XSS')",
    "<svg onload=alert('XSS')>",
    "'><script>alert(String.fromCharCode(88,83,83))</script>"
};

for (String payload : xssPayloads) {
    // Submit payload via form/API
    // View the rendered output in browser
    // Verify payload is HTML-encoded, NOT executed as script
    // Example: "<script>" should render as "&lt;script&gt;"
}

Common Pitfalls

  • Using HTML encoding in JavaScript, CSS, URL, or unquoted attribute contexts.
  • Treating input validation or denylisted characters as the XSS fix. The output sink still needs context-specific encoding.
  • Disabling escaping with escapeXml="false", th:utext, escape="false", or raw JSP output for content that has not been sanitized.
  • Assuming JSON serialization is HTML encoding. JSON is safer when served as application/json, but client code must still render values safely.
  • Adding CSP while leaving raw output paths unchanged. CSP reduces impact; it does not remove the injection point.
  • URL-encoding a value but still allowing active schemes such as javascript: or data: in links.

Dependencies and Installation

<!-- OWASP Java Encoder -->
<dependency>
    <groupId>org.owasp.encoder</groupId>
    <artifactId>encoder</artifactId>
</dependency>

<!-- OWASP Java HTML Sanitizer (for rich HTML) -->
<dependency>
    <groupId>com.googlecode.owasp-java-html-sanitizer</groupId>
    <artifactId>owasp-java-html-sanitizer</artifactId>
</dependency>

<!-- Spring Framework (if using Spring) -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-web</artifactId>
</dependency>

<!-- Apache Commons Text -->
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-text</artifactId>
</dependency>

Additional Resources