package com.iamberry.wechat.autiXSS;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.oro.text.regex.PatternCompiler;
import org.apache.oro.text.regex.PatternMatcher;
import org.apache.oro.text.regex.Perl5Compiler;
import org.apache.oro.text.regex.Perl5Matcher;
import org.apache.oro.text.regex.Perl5Substitution;
import org.apache.oro.text.regex.Util;
import org.owasp.validator.html.AntiSamy;
import org.owasp.validator.html.CleanResults;
import org.owasp.validator.html.Policy;
import org.owasp.validator.html.PolicyException;
/**
 * description : 富文本校验器，凡是使用了富文本，必须使用此类进行过滤，保证防止XSS攻击，
 * 但不是说明，使用此类，就可以完全屏蔽XSS，只是缩小攻击范围
 * @author 何秀刚
 * create date : 2015年11月12日
 */
public class AntiXSS {
	
	private final Pattern SCRIPT_TAG_PATTERN = Pattern.compile("<script[^>]*>.*</script[^>]*>", Pattern.CASE_INSENSITIVE);
    private final String IMGURL_REG = "<img.*src=(.*?)[^>]*?>";  
    private final String IMGSRC_REG = "http:\"?(.*?)(\"|>|\\s+)";
    // 站外的图片资源 被替换资源
    private final PatternCompiler pc = new Perl5Compiler();
    private final PatternMatcher matcher = new Perl5Matcher();
    private final String replace_img_url = "";
    /** anti-samy过滤规则 */
	public static Policy policy = null;
	public static AntiSamy antiSamy = new AntiSamy();
    static {
		// antisamy过滤规则
    	String path = AntiXSS.class.getClassLoader().getResource("antisamy-config.xml").getFile();
		try {
			policy = Policy.getInstance(path);
		} catch (PolicyException e) {
			e.printStackTrace();
		}
    }
    public static void main(String[] args) {
		System.out.println(new AntiXSS()._antiXSS("http://n.sinaimg.cn/translate/20160622/gVDw-fxtfrrc4094328.jpg<p><br /></p><p style=\"font-size: 16.0px;\"> <img src=\"http://n.sinaimg.cn/translate/20160622/gVDw-fxtfrrc4094328.jpg\" /> </p><p style=\"font-size: 16.0px;\">中新网6月21日电 人人都坐过滑梯，但你尝试过在300米高空，乘坐透明玻璃滑梯的刺激感受吗？日前国外摄影师就拍下了人们乘坐洛杉矶市联邦银行大厦高空透明滑梯时的场景。<img src=\"http://n.sinaimg.cn/translate/20160622/poKn-fxthapu9457231.jpg\" /> </p><p style=\"font-size: 16.0px;\">在美国洛杉矶市联邦银行大厦(U.S.Bank Tower)的建筑外围，有一个特殊的滑梯。这个滑梯名叫&ldquo;天空滑梯(Skyslide)&rdquo;，建在联邦银行大厦的外立面，由金属框架固定和支撑，位于这座摩天大楼的69到70层处，大概距离地面300米。<img src=\"http://n.sinaimg.cn/translate/20160622/spBn-fxtfrrc4094370.jpg\" /> </p><p style=\"font-size: 16.0px;\">	这个滑梯是全封闭全透明的，其整体结构由玻璃打造，玻璃的厚度约合3.2厘米，玻璃坡道宽1.2米，长14米，游客是从70层的高度滑到69层。<img src=\"http://n.sinaimg.cn/translate/20160622/a3gq-fxtfrrc4094374.jpg\" /> </p><p style=\"font-size: 16.0px;\">虽然距离不长，但考虑到滑梯的高度，再加上透明玻璃让地面景色一览无余，想必坐上这样的滑梯也是惊险刺激。啊</p><h2><span style=\"background-color: rgb(229,102,0);\" /><em> </em>	</h2>"));
	}
	public String antiXSS(String content) {
		return _antiXSS(content);
	}

	// 过滤调用的方法， content是用户输入的数据
	private String _antiXSS(String content) {
		try {
			String html = getImageUrl(content);
			html = stripEvent(html);
			html = stripScriptTag(html);
			final CleanResults cr = antiSamy.scan(html, policy);
			// 安全的HTML输出
			return cr.getCleanHTML();
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}

	// 过滤script标签
	private String stripScriptTag(String content) {
		Matcher m = SCRIPT_TAG_PATTERN.matcher(content);
		content = m.replaceAll("");
		return content;
	}

	// 所有的JavaScript事件，必须经过过滤
	private String stripEvent(String content) throws Exception {
		String[] events = { "onmouseover", "onmouseout", "onmousedown",
				"onmouseup", "onmousemove", "onclick", "ondblclick",
				"onkeypress", "onkeydown", "onkeyup", "ondragstart",
				"onerrorupdate", "onhelp", "onreadystatechange", "onrowenter",
				"onrowexit", "onselectstart", "onload", "onunload",
				"onbeforeunload", "onblur", "onerror", "onfocus", "onresize",
				"onscroll", "oncontextmenu" };
		for (String event : events) {
			org.apache.oro.text.regex.Pattern p = pc.compile("(<[^>]*)("+ event + ")([^>]*>)", Perl5Compiler.CASE_INSENSITIVE_MASK);
			if (null != p)
				content = Util.substitute(matcher, p, new Perl5Substitution("$1" + event.substring(2) + "$3"), content, Util.SUBSTITUTE_ALL);
		}
		return content;
	}

	// 过滤 美国信息交换标准代码 、 十六进制记录
	private String stripAsciiAndHex(String content) throws Exception {
		// filter &# \00xx
		org.apache.oro.text.regex.Pattern p = pc.compile("(<[^>]*)(&#|\\\\00)([^>]*>)", Perl5Compiler.CASE_INSENSITIVE_MASK);
		if (null != p)
			content = Util.substitute(matcher, p, new Perl5Substitution("$1$3"), content, Util.SUBSTITUTE_ALL);
		return content;
	}
	
	public static String xssEncode(String s) {
        if (s == null || s.equals("")) {
            return s;
        }
        
       return s.replaceAll("'", "’").replaceAll(">", "＞").replaceAll("<", "＜").replaceAll("\"", "”");
    }

	// 过滤CSS表达式
	private String stripCssExpression(String content) throws Exception {
		org.apache.oro.text.regex.Pattern p = pc.compile("(<[^>]*style=.*)/\\*.*\\*/([^>]*>)", Perl5Compiler.CASE_INSENSITIVE_MASK);
		if (null != p)
			content = Util.substitute(matcher, p, new Perl5Substitution("$1$2"), content, Util.SUBSTITUTE_ALL);

		p = pc.compile("(<[^>]*style=[^>]+)(expression|javascript|vbscript|-moz-binding)([^>]*>)", Perl5Compiler.CASE_INSENSITIVE_MASK);
		if (null != p)
			content = Util.substitute(matcher, p, new Perl5Substitution("$1$3"), content, Util.SUBSTITUTE_ALL);

		p = pc.compile("(<style[^>]*>.*)/\\*.*\\*/(.*</style[^>]*>)", Perl5Compiler.CASE_INSENSITIVE_MASK);
		if (null != p)
			content = Util.substitute(matcher, p, new Perl5Substitution("$1$2"), content, Util.SUBSTITUTE_ALL);

		p = pc.compile("(<style[^>]*>[^>]+)(expression|javascript|vbscript|-moz-binding)(.*</style[^>]*>)", Perl5Compiler.CASE_INSENSITIVE_MASK);
		if (null != p)
			content = Util.substitute(matcher, p, new Perl5Substitution("$1$3"), content, Util.SUBSTITUTE_ALL);
		return content;
	}
	
	// 危险标签，必须经过过滤
	private String stripProtocol(String content) throws Exception {
		String[] protocols = { "javascript", "vbscript", "livescript", "ms-its", "mhtml", "data", "firefoxurl", "mocha" };
		for (String protocol : protocols) {
			org.apache.oro.text.regex.Pattern p = pc.compile("(<[^>]*)" + protocol + ":([^>]*>)", Perl5Compiler.CASE_INSENSITIVE_MASK);
			if (null != p)
				content = Util.substitute(matcher, p, new Perl5Substitution("$1/$2"), content, Util.SUBSTITUTE_ALL);
		}
		return content;
	}
	
	// 防止flash
	private String stripAllowScriptAccess(String content) throws Exception {
		org.apache.oro.text.regex.Pattern p = pc.compile("(<[^>]*)AllowScriptAccess([^>]*>)", Perl5Compiler.CASE_INSENSITIVE_MASK);
		if (null != p)
			content = Util.substitute(matcher, p, new Perl5Substitution("$1Allow_Script_Access$2"), content, Util.SUBSTITUTE_ALL);
		return content;
	}
	
	// 过滤站外图片
    private String getImageUrl(String HTML) {
        Matcher matcher = Pattern.compile(IMGURL_REG, Pattern.CASE_INSENSITIVE | Pattern.DOTALL).matcher(HTML);  
        while (matcher.find()) {
            Matcher matcher1 = Pattern.compile(IMGSRC_REG, Pattern.CASE_INSENSITIVE | Pattern.DOTALL).matcher(matcher.group());
            String temp = matcher.group();
            String tempReplace = matcher.group();
            while (matcher1.find()) {
            	// 图片违法，进行替换
            	String imgSrc = matcher1.group().substring(0, matcher1.group().length() - 1);
            	if (imgSrc.startsWith("http")) {
            		if (!imgSrc.startsWith("http://h5.acoolrat.com:")) {
            			temp = temp.replace(imgSrc, replace_img_url);
            		}
             	} else {
             		temp = temp.replace(imgSrc, replace_img_url);
            	}
            }
            HTML = HTML.replace(tempReplace, temp);
        }
        return HTML;
    }  
}