/*
 * Copyright 2017-2018 Baidu Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.baidu.openrasp.plugin.checker.local;

import com.baidu.openrasp.HookHandler;
import com.baidu.openrasp.config.Config;
import com.baidu.openrasp.plugin.checker.CheckParameter;
import com.baidu.openrasp.plugin.info.AttackInfo;
import com.baidu.openrasp.plugin.info.EventInfo;
import com.google.gson.JsonObject;

import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class XssChecker extends ConfigurableChecker {
    private static final String CONFIG_KEY_XSS_USER_INPUT = "xss_userinput";
    private static final String EXCEED_LENGTH_COUNT = "max_detection_num";
    private static final String XSS_PARAMETER_LENGTH = "min_length";
    private static final String XSS_REGEX = "filter_regex";
    private static final int DEFAULT_MIN_LENGTH = 15;
    private static final int DEFAULT_MAX_DETECTION_NUM = 10;
    private static final String DEFAULT_XSS_REGEX = "<![\\-\\[A-Za-z]|<([A-Za-z]{1,12})[\\/ >]";

    @Override
    public List<EventInfo> checkParam(CheckParameter checkParameter) {
        JsonObject config = Config.getConfig().getAlgorithmConfig();
        String action = getActionElement(config, CONFIG_KEY_XSS_USER_INPUT);
        LinkedList<EventInfo> result = new LinkedList<EventInfo>();
        String content = String.valueOf(checkParameter.getParam("html_body"));
        if (!EventInfo.CHECK_ACTION_IGNORE.equals(action)) {
            if (HookHandler.requestCache.get() != null && content != null) {
                Map<String, String[]> parameterMap = HookHandler.requestCache.get().getParameterMap();
                String regex = getStringElement(config, CONFIG_KEY_XSS_USER_INPUT, XSS_REGEX);
                if (regex == null) {
                    regex = DEFAULT_XSS_REGEX;
                }
                Pattern pattern = Pattern.compile(regex);

                int xssParameterLength = getIntElement(config, CONFIG_KEY_XSS_USER_INPUT, XSS_PARAMETER_LENGTH);
                if (xssParameterLength < 0) {
                    xssParameterLength = DEFAULT_MIN_LENGTH;
                }
                int exceedLengthCount = getIntElement(config, CONFIG_KEY_XSS_USER_INPUT, EXCEED_LENGTH_COUNT);
                if (exceedLengthCount < 0) {
                    exceedLengthCount = DEFAULT_MAX_DETECTION_NUM;
                }
                int count = 0;
                for (Map.Entry<String, String[]> entry : parameterMap.entrySet()) {
                    for (String value : entry.getValue()) {
                        Matcher matcher = pattern.matcher(value);
                        boolean isMatch = matcher.find();
                        if (value.length() >= xssParameterLength && isMatch) {
                            count++;
                            if (content.contains(value)) {
                                String message = "请求参数: " + entry.getKey() + " 参数值为: " + value + " 存在XSS攻击风险";
                                result.add(AttackInfo.createLocalAttackInfo(checkParameter, EventInfo.CHECK_ACTION_BLOCK, message, CONFIG_KEY_XSS_USER_INPUT));
                                return result;
                            }
                            if (count > exceedLengthCount) {
                                String message = "请求参数长度大于等于" + xssParameterLength + "并且匹配XSS正则的数量超过了" + exceedLengthCount;
                                result.add(AttackInfo.createLocalAttackInfo(checkParameter, EventInfo.CHECK_ACTION_BLOCK, message, CONFIG_KEY_XSS_USER_INPUT));
                                return result;
                            }
                        }
                    }
                }
            }
        }
        return result;
    }
}

@@ -3484,489 +3484,1105 @@
  = %22
-%E8%AF%B7%E6%B1%82%E5%8F%82%E6%95%B0: %22 + entry.getKey() + %22 %E5%8F%82%E6%95%B0%E5%80%BC%E4%B8%BA: %22 + value + %22 %E5%AD%98%E5%9C%A8XSS%E6%94%BB%E5%87%BB%E9%A3%8E%E9%99%A9%22;%0A                                result.add(AttackInfo.createLocalAttackInfo(checkParameter, EventInfo.CHECK_ACTION_BLOCK, message, CONFIG_KEY_XSS_USER_INPUT));%0A                                return result;%0A                            %7D%0A                            if (count %3E exceedLengthCount) %7B%0A                                String message = %22%E8%AF%B7%E6%B1%82%E5%8F%82%E6%95%B0%E9%95%BF%E5%BA%A6%E5%A4%A7%E4%BA%8E%E7%AD%89%E4%BA%8E%22 + xssParameterLength + %22%E5%B9%B6%E4%B8%94%E5%8C%B9%E9%85%8DXSS%E6%AD%A3%E5%88%99%E7%9A%84%E6%95%B0%E9%87%8F%E8%B6%85%E8%BF%87%E4%BA%86%22 + exceedLengthCount
+Reflected XSS attack detected, parameter name: %22 + entry.getKey();%0A                                Map%3CString, Object%3E params = (Map%3CString, Object%3E) checkParameter.getParams();%0A                                params.remove(%22html_body%22);%0A                                params.put(%22name%22, entry.getKey());%0A                                params.put(%22value%22, value);%0A                                result.add(AttackInfo.createLocalAttackInfo(checkParameter, EventInfo.CHECK_ACTION_BLOCK, message, CONFIG_KEY_XSS_USER_INPUT));%0A                                return result;%0A                            %7D%0A                            if (count %3E exceedLengthCount) %7B%0A                                String message = %22Reflected XSS attack detected: more than %22 + exceedLengthCount + %22 html tags detected in userinput%22;%0A                                Map%3CString, Object%3E params = (Map%3CString, Object%3E) checkParameter.getParams();%0A                                params.remove(%22html_body%22);%0A                                params.put(%22name%22, %22%22);%0A                                params.put(%22value%22, %22%22)
 ;%0A  

modify xss message