package com.eactive.eai.rms.common.filter;

import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

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

import org.apache.commons.lang3.StringUtils;

class RequestWrapper extends HttpServletRequestWrapper {

	// GS인증 크로스사이트스크립트 보안성 문제로 XSS 공격을 방지하기 위한 악성 스크립트 패턴 정의.
	// 2025.05.27 패턴 추가
	private static final Pattern[] PATTERNS = new Pattern[] {
			Pattern.compile("<script>(.*?)</script>", Pattern.CASE_INSENSITIVE),
			Pattern.compile("src[\r\n]*=[\r\n]*\\\'(.*?)\\\'",
									Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL),
			Pattern.compile("src[\r\n]*=[\r\n]*\\\"(.*?)\\\"",
									Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL),
			Pattern.compile("</script>", Pattern.CASE_INSENSITIVE),
			Pattern.compile("<script(.*?)>",
									Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL),
			Pattern.compile("eval\\((.*?)\\)",
									Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL),
			Pattern.compile("expression\\((.*?)\\)",
									Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL),
			Pattern.compile("javascript:", Pattern.CASE_INSENSITIVE),
			Pattern.compile("vbscript:", Pattern.CASE_INSENSITIVE),
			Pattern.compile("onload(.*?)=",
									Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL),
			Pattern.compile("onmouseover(.*?)=",
									Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL),
			Pattern.compile("onmouseout(.*?)=",
									Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL),
			Pattern.compile("onclick(.*?)=",
									Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL),
			Pattern.compile("onfocus(.*?)=",
									Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL),
			Pattern.compile("onblur(.*?)=",
									Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL),
			Pattern.compile("onerror(.*?)=",
									Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL)
	};


	public RequestWrapper(HttpServletRequest servletRequest) {
		super(servletRequest);
	}

	@Override
	public String[] getParameterValues(String parameter) {
		String[] values = super.getParameterValues(parameter);

		if (values == null) { return values; }

		int count = values.length;
		String[] encodedValues = new String[count];

		for (int i = 0; i < count; i++) {
			encodedValues[i] = cleanXSS(values[i]);
		}

		return encodedValues;
	}

	@Override
	public Map<String, String[]> getParameterMap() {
		Map<String, String[]> map = super.getParameterMap();
		Map<String, String[]> clonedMap = new HashMap<>();

		for (Entry<String, String[]> entry : map.entrySet()) {
			String[] values = entry.getValue();

			if (values != null && values.length > 0) {
				String[] cloneValues = new String[values.length];
				for (int idx = 0; idx < values.length; idx++) {
					cloneValues[idx] = cleanXSS(values[idx]);
				}
				clonedMap.put(entry.getKey(), cloneValues);
			}
		}

		return clonedMap;
	}

	@Override
	public String getParameter(String parameter) {
		String value = super.getParameter(parameter);

		if (value == null || StringUtils.equals(parameter, "gridData")) {
			return value;
		}

		return cleanXSS(value);
	}

	@Override
	public String getHeader(String name) {
		String value = super.getHeader(name);

		if (value == null || StringUtils.equals(name, "gridData")) {
			return value;
		}

		return cleanXSS(value);
	}

	private String cleanXSS(String value) {
		if (value == null) { return null; }

		// 추가적인 정규 표현식을 이용한 필터링
		for (Pattern scriptPattern : PATTERNS) {
			Matcher matcher = scriptPattern.matcher(value);
			value = matcher.replaceAll("");
		}

		// <script> 태그를 제거
		value = value.replaceAll("(?i)<script.*?>.*?</script>", "");

		// script 문자열을 완전히 제거
		value = value.replaceAll("(?i)\\bscript\\b", "");
		StringBuilder sb = new StringBuilder();

		value = convertChars(value, sb);

		// eval, javascript 등 스크립트 관련 문자열 제거
		value = value.replaceAll("(?i)eval\\((.*?)\\)", "");
		value = value.replaceAll("[\\\"\\\'][\\s]*javascript:(.*?)\\)", "\"\"");
		return value;
	}

	// '(', ')', '"' 문자는 getMethod()가 "get"일 때만 변환됨.
	// 이는 gridData에서 사용되는 큰따옴표(")와 변환 함수에서 사용하는 괄호('(', ')')의 처리를 위함.
	private String convertChars(String value, StringBuilder sb) {

		for (char c : value.toCharArray()) {

			switch (c) {
			case '<':
				sb.append("&lt;");
				break;
			case '>':
				sb.append("&gt;");
				break;
			case '(':
				convertGetMethod(c, "&#40;", sb);
				break;
			case ')':
				convertGetMethod(c, "&#41;", sb);
				break;
			case '"':
				convertGetMethod(c, "&quot;", sb);
				break;
			case '#':
				sb.append("&#35;");
				break;
			case '&':
				sb.append("&#38;");
				break;
			case '\'':
				sb.append("&#39;");
				break;
			default:
				sb.append(c);
			}

		}

		return sb.toString();
	}

	private void convertGetMethod(char value, String convertValue,
							StringBuilder sb) {

		if (StringUtils.equalsIgnoreCase(getMethod(), "get")) {
			sb.append(convertValue);
		} else {
			sb.append(value);
		}

	}
}
