invicnaper/MWF

View on GitHub
Utils/t/xss_test.cpp

Summary

Maintainability
Test Coverage
///////////////////////////////////////////////////////////////////////////////
//                                                                             
//  Copyright (C) 2008-2012  Artyom Beilis (Tonkikh) <artyomtnk@yahoo.com>     
//                                                                             
//  See accompanying file COPYING.TXT file for licensing details.
//
///////////////////////////////////////////////////////////////////////////////
#include <cppcms/xss.h>
#include <cppcms/json.h>
#include <booster/locale/encoding.h>
#include "test.h"
#include "c_string.h"
#include <iostream>
#include <string.h>
#include <fstream>
#include <cstdlib>

void test_rules()
{
    std::cout << "- Testing rules" << std::endl;
    using namespace cppcms::xss;
    
    {
        std::cout << "-- Testing basic properties" << std::endl;
        rules r;
        TEST(r.html() == rules::xhtml_input);
        r.html(rules::html_input);
        TEST(r.html() == rules::html_input);
        r.html(rules::xhtml_input);
        TEST(r.html() == rules::xhtml_input);
        TEST(!r.comments_allowed());
        r.comments_allowed(true);
        TEST(r.comments_allowed());
        TEST(!r.numeric_entities_allowed());
        r.numeric_entities_allowed(true);
        TEST(r.numeric_entities_allowed());
    }
    {
        std::cout << "-- Testing entities" << std::endl;
        rules r;
        TEST(r.valid_entity(details::c_string("amp")));
        TEST(!r.valid_entity(details::c_string("Amp")));
        TEST(!r.valid_entity(details::c_string("foo")));
        r.html(rules::html_input);
        TEST(!r.valid_entity(details::c_string("Amp")));
        r.add_entity("foo");
        TEST(r.valid_entity(details::c_string("foo")));
        TEST(!r.valid_entity(details::c_string("bar")));

    }
    {
        std::cout <<"-- Testing tags" << std::endl;
        std::cout <<"--- XHTML" << std::endl;
        {
            rules r;
            TEST(r.valid_tag("a")==rules::invalid_tag);
            TEST(r.valid_tag("hr")==rules::invalid_tag);
            r.add_tag("a",rules::any_tag);
            TEST(r.valid_tag("A")==rules::invalid_tag);
            TEST(r.valid_tag("a")==rules::any_tag);
            r.add_tag("hr",rules::stand_alone);
            TEST(r.valid_tag("hr")==rules::stand_alone);
            TEST(r.valid_tag("HR")==rules::invalid_tag);
            TEST(r.valid_tag("Hr")==rules::invalid_tag);
        }
        std::cout <<"--- HTML" << std::endl;
        {
            rules r;
            r.html(rules::html_input);
            TEST(r.valid_tag("a")==rules::invalid_tag);
            TEST(r.valid_tag("hr")==rules::invalid_tag);
            r.add_tag("a",rules::any_tag);
            TEST(r.valid_tag("A")==rules::any_tag);
            TEST(r.valid_tag("a")==rules::any_tag);
            r.add_tag("hr",rules::stand_alone);
            TEST(r.valid_tag("hr")==rules::stand_alone);
            TEST(r.valid_tag("HR")==rules::stand_alone);
            TEST(r.valid_tag("Hr")==rules::stand_alone);
        }
    }
    {
        std::cout <<"-- Testing properties" << std::endl;
        std::cout <<"--- XHTML" << std::endl;
        {
            rules r;
            r.add_tag("a",rules::opening_and_closing);
            r.add_tag("b",rules::opening_and_closing);
            TEST(!r.valid_property("a","src","javascript:alert('XSS')"));
            TEST(!r.valid_boolean_property("a","src"));
            r.add_property("a","src",booster::regex(".*"));
            TEST(r.valid_property("a","src","javascript:alert('XSS')"));
            TEST(r.valid_property("a","src","javascript:alert('XSS')"));
            TEST(!r.valid_property("b","src","javascript:alert('XSS')"));
            r.add_property("a","src",booster::regex("(http|ftp|https)://.*"));
            TEST(!r.valid_property("a","src","javascript:alert('XSS')"));
            TEST(!r.valid_property("a","src","javascript:alert('XSS') http://foo"));
            TEST(r.valid_property("a","src","http://foo"));
            TEST(!r.valid_property("a","Src","http://foo"));
            r.add_boolean_property("a","disabled");
            TEST(r.valid_property("a","disabled","disabled"));
            TEST(!r.valid_boolean_property("a","disabled"));
            r.add_integer_property("a","border");
            TEST(r.valid_property("a","border","0"));
            TEST(r.valid_property("a","border","3415423452452454235234523"));
            TEST(r.valid_property("a","border","-3415423452452454235234523"));
            TEST(!r.valid_property("a","border","-"));
            TEST(!r.valid_property("a","border","1.3"));
            TEST(!r.valid_property("a","border",""));

        }
        std::cout <<"--- HTML" << std::endl;
        {
            rules r;
            r.html(rules::html_input);
            r.add_tag("a",rules::opening_and_closing);
            r.add_tag("b",rules::opening_and_closing);
            TEST(!r.valid_property("a","src","javascript:alert('XSS')"));
            TEST(!r.valid_boolean_property("a","src"));
            r.add_property("a","src",booster::regex(".*"));
            TEST(r.valid_property("a","src","javascript:alert('XSS')"));
            TEST(r.valid_property("a","src","javascript:alert('XSS')"));
            TEST(!r.valid_property("b","src","javascript:alert('XSS')"));
            r.add_property("a","src",booster::regex("(http|ftp|https)://.*"));
            TEST(!r.valid_property("a","src","javascript:alert('XSS')"));
            TEST(!r.valid_property("a","src","javascript:alert('XSS') http://foo"));
            TEST(r.valid_property("a","src","http://foo"));
            TEST(r.valid_property("a","Src","http://foo"));
            r.add_boolean_property("a","disabled");
            TEST(!r.valid_property("a","disabled","disabled"));
            TEST(r.valid_boolean_property("a","disabled"));
            r.add_integer_property("a","border");
            TEST(r.valid_property("a","border","0"));
            TEST(r.valid_property("a","border","3415423452452454235234523"));
            TEST(r.valid_property("a","border","-3415423452452454235234523"));
            TEST(!r.valid_property("a","border","-"));
            TEST(!r.valid_property("a","border","1.3"));
            TEST(!r.valid_property("a","border",""));
        }
        std::cout <<"--- URI-Filter" << std::endl;
        {
            rules r;
            r.add_tag("a",rules::opening_and_closing);
            r.add_tag("img",rules::stand_alone);
            r.add_uri_property("a","href");
            r.add_uri_property("img","src","(http|https)");
            TEST(!r.valid_property("a","href","javascript:alert('XSS')"));
            TEST(!r.valid_property("a","href","javascript:alert(&quot;XSS&quot;)"));
            TEST(!r.valid_property("a","href","&#106;&#97;&#118;&#97;&#115;&#99;&#114;&#105;&#112;&#116;&#58;"
                              "&#97;&#108;&#101;&#114;&#116;&#40;&#39;&#88;&#83;&#83;&#39;&#41;"));
            TEST(!r.valid_property("a","href","&#x6A&#x61&#x76&#x61&#x73&#x63&#x72&#x69&#x70&#x74&#x3A&#x61&#x6C"
                              "&#x65&#x72&#x74&#x28&#x27&#x58&#x53&#x53&#x27&#x29"));
            TEST(!r.valid_property("a","href","jav&#x61;script:alert('XSS');"));
            
            TEST(!r.valid_property("a","href","javascript:alert(\"XSS\")"));
            TEST(!r.valid_property("a","href","javascript:test()"));
            TEST(!r.valid_property("a","href","http://thisisatest.com/@\"onmouseover=\"alert('test xss')\"/"));

            TEST(r.valid_property("a","href","test.html"));
            TEST(r.valid_property("a","href","/foo/test.html"));
            TEST(r.valid_property("a","href","https://127.0.0.1/foo/test.html"));
            TEST(r.valid_property("a","href","http://localhost/foo/test.html"));
            TEST(r.valid_property("a","href","http://foo:bar@localhost/foo/test.html"));
            TEST(r.valid_property("a","href","https://example.com/foo/test.html"));
            TEST(r.valid_property("a","href","https://example.com/foo/test.html#There"));
            TEST(r.valid_property("a","href","https://example.com/foo/test.html?x=a&amp;y=b#There"));
            TEST(!r.valid_property("a","href","https://example.com/foo/test.html?x=a&y=b#There"));
            TEST(r.valid_property("a","href","http://en.wikipedia.org/wiki/I'm"));
            TEST(r.valid_property("a","href","http://en.wikipedia.org/wiki/I&apos;m"));
            TEST(!r.valid_property("a","href","jav&#x0A;ascript:alert('XSS');"));
            TEST(r.valid_property("a","href","http://he.wikipedia.org/wiki/%D7%A9%D7%9C%D7%95%D7%9D?x=%D7#x"));
            TEST(r.valid_property("a","href","/wiki/%D7%A9%D7%9C%D7%95%D7%9D?x=%D7#x"));
            TEST(r.valid_property("a","href","http://en.wikipedia.org/wiki/C_(programming_language)"));
            TEST(r.valid_property("a","href","ftp://myftp/"));
            TEST(!r.valid_property("img","src","ftp://myftp/"));
            TEST(r.valid_property("img","src","http://myftp/"));
        }
    }

    
}

bool validate_simple(char const *s,cppcms::xss::rules const &r,char const *rm=0,char const *esc=0)
{
    bool res = cppcms::xss::validate(s,s+strlen(s),r);
    std::string tmp1,tmp2;
    bool res2 = cppcms::xss::validate_and_filter_if_invalid(s,s+strlen(s),r,tmp1,cppcms::xss::remove_invalid);
    bool res3 = cppcms::xss::validate_and_filter_if_invalid(s,s+strlen(s),r,tmp2,cppcms::xss::escape_invalid);
    TEST(res==res2);
    TEST(res==res3);
    if(!res) {
        if(rm) {
            TEST(tmp1==rm);
        }
        if(esc)
            TEST(tmp2==esc);
        TEST(tmp1!=s);
        TEST(tmp2!=s);
        TEST(tmp1!=tmp2);
        TEST(tmp1.size() < strlen(s));
        TEST(cppcms::xss::validate(tmp1.c_str(),tmp1.c_str()+tmp1.size(),r));
        TEST(cppcms::xss::validate(tmp2.c_str(),tmp2.c_str()+tmp2.size(),r));
    }
    return res;
}

void test_validation()
{
    std::cout << "- Testing filter" << std::endl;
    using namespace cppcms::xss;
    {
        rules r;
        std::cout << "-- Testing entities" << std::endl;
        TEST(validate_simple("to be or not to be",r));
        TEST(validate_simple("to be &amp; not to be",r));
        TEST(!validate_simple("to be &or; not to be",r,"to be  not to be","to be &amp;or; not to be"));
        TEST(!validate_simple("&or; not to be",r));
        TEST(!validate_simple("to be &or;",r));
        TEST(!validate_simple("&amp not to be",r,"","&amp;amp not to be"));
        TEST(!validate_simple("to be &amp",r,"to be ","to be &amp;amp"));
        r.add_entity("or");
        TEST(validate_simple("to be &or; not to be",r));
        TEST(validate_simple("&or; not to be",r));
        TEST(validate_simple("to be &or;",r));

        TEST(!validate_simple("&#64;",r));
        TEST(!validate_simple("&#x3c;",r));
        TEST(!validate_simple("&#X3c;",r));
        r.numeric_entities_allowed(true);
        TEST(validate_simple("&#64;",r));
        TEST(validate_simple("&#x3c;",r));
        TEST(validate_simple("&#X3c;",r));
        TEST(!validate_simple("&#x3;",r));
        TEST(validate_simple("&#x0A;",r));
        TEST(!validate_simple("&#xFFFF;",r));
        TEST(!validate_simple("&#xFFFE;",r));
        TEST(!validate_simple("&#xDB01;",r));
        TEST(validate_simple("&#x10FFFF;",r));
        TEST(!validate_simple("&#x110000;",r));
        TEST(!validate_simple("&#10000000000000;",r));
        TEST(!validate_simple("&#-45;",r));
    }
    {
        rules r;
        std::cout << "-- Testing basic tags" << std::endl;
        TEST(!validate_simple("<hr />",r));
        r.add_tag("hr",rules::stand_alone);
        TEST(validate_simple("<hr />",r));
        TEST(validate_simple("<hr/>",r));
        TEST(!validate_simple("< hr/>",r));
        TEST(!validate_simple("<hr / >",r));
        TEST(!validate_simple("<hr/ >",r));

        TEST(validate_simple("xxx<hr/>xxx",r));
        TEST(!validate_simple("<hr>",r));
        TEST(!validate_simple("</hr>",r));
        TEST(!validate_simple("<hr></hr>",r));
        TEST(!validate_simple("<hr",r,"","&lt;hr"));
        TEST(!validate_simple("hr>",r,"hr","hr&gt;"));
        r.add_tag("a",rules::opening_and_closing);
        TEST(!validate_simple("<a/>",r));
        TEST(validate_simple("<a></a>",r));
        r.add_tag("p",rules::any_tag);
        TEST(validate_simple("<p/>",r));
        TEST(validate_simple("<p></p>",r));
        TEST(validate_simple("<p></p>",r));
    }
    {
        std::cout << "-- Testing properties" << std::endl;
        rules r;
        r.add_tag("hr",rules::stand_alone);
        r.add_tag("a",rules::opening_and_closing);
        TEST(!validate_simple("<hr test='test' />",r));
        r.add_boolean_property("hr","test");
        r.add_integer_property("hr","size");
        r.add_uri_property("a","href");
        TEST(validate_simple("<hr size='1' />",r));
        TEST(validate_simple("<hr test='test' />",r));
        TEST(validate_simple("<hr test='test' size=\"1\" />",r));
        TEST(!validate_simple("<hr test='test' size=\"1\" >",r));
        TEST(!validate_simple("<hr test='test' size=1 />",r));
        TEST(!validate_simple("<hr test='test' size='x'/>",r));
        TEST(!validate_simple("<hr test='test' size='1' size='2' />",r));
        TEST(!validate_simple("<hr size/>",r));
        TEST(!validate_simple("<hr size />",r));
        TEST(!validate_simple("<hr size ='10'  />",r));
        TEST(!validate_simple("<hr size= '10'  />",r));
        TEST(!validate_simple("<hr size='10'test='test'  />",r));
        TEST(validate_simple("<a href='http://google.com'>x</a>",r));
        TEST(!validate_simple("<a href='javascript:xss()'>x</a>",r,"x"));
        TEST(!validate_simple("<a>x</a href='http://google.com'>",r,"x"));
        TEST(validate_simple("<a href='http://google.com/foo?x=y&amp;bar=x'>x</a>",r));
        TEST(validate_simple("<a href=\"http://cppcms.sourceforge.net/banner-small.png\">x</a>",r));
        TEST(validate_simple("<a href=\"#Are+you+crazy+or+masochist?\">x</a>",r));
    }
    {
        std::cout << "-- Testing comments" << std::endl;
        rules r;
        TEST(!validate_simple("Hello <!-- test --> world",r,"Hello  world","Hello &lt;!-- test --&gt; world"));
        r.comments_allowed(true);
        TEST(validate_simple("Hello <!-- test --> world",r));
        TEST(!validate_simple("Hello < !-- test --> world",r,"Hello  world","Hello &lt; !-- test --&gt; world"));
        TEST(!validate_simple("Hello <!- - test --> world",r));
        TEST(!validate_simple("Hello <!-- ---> world",r));
        TEST(!validate_simple("Hello <!-- -- -> world",r));
        TEST(validate_simple("Hello <!-- - --> world",r));
        TEST(!validate_simple("Hello <!-- -- --> world",r));
        TEST(!validate_simple("Hello <!-- <test /> --> world",r,"Hello  world"));
        TEST(!validate_simple("Hello <!-- < --> world",r,"Hello  world"));
        TEST(!validate_simple("Hello <!-- > --> world",r,"Hello  world"));
        TEST(validate_simple("Hello <!----> world",r));
        TEST(validate_simple("<!-- test -->",r));
        TEST(validate_simple("<!-- test -->x",r));
        TEST(validate_simple("x<!-- test -->",r));
        TEST(!validate_simple("<!-- test --",r,""));
        TEST(!validate_simple("<!-- test -",r,""));
        TEST(!validate_simple("< !-- test -->",r,""));
        TEST(!validate_simple("a<!--[if gte IE 4]><SCRIPT>alert('XSS');</SCRIPT><![endif]-->b",r,"ab"));
    }
    {
        std::cout << "-- Test nesting" << std::endl;
        {
            rules r;
            std::cout << "--- XHTML" << std::endl;
            r.add_tag("input",rules::any_tag);
            r.add_tag("ul",rules::opening_and_closing);
            r.add_tag("ol",rules::opening_and_closing);
            r.add_tag("hr",rules::stand_alone);
            TEST(validate_simple("<ul><input /><input></input><ol><hr/></ol></ul>",r));
            TEST(!validate_simple("<ul><input /><input></input><ol><hr/></ol></ol>",r,
                        "<input /><input></input><ol><hr/></ol>"));
            TEST(!validate_simple("<ul><input /><input></input><ol><hr/></ul></ul>",r,
                        "<ul><input /><input></input><hr/></ul>"));
            TEST(!validate_simple("<ul><input /><input></input><ul><hr/></ol></ol>",r,
                        "<input /><input></input><hr/>"
                        ));
            TEST(!validate_simple("<ul><input /><input></input><ol><hr/></ul></ol>",r,
                        "<input /><input></input><hr/>"
                        ));
            TEST(!validate_simple("<ul><input /><input></input><ol><hr>x</hr></ol></ul>",r,
                        "<ul><input /><input></input><ol>x</ol></ul>"
                        ));
        }
        {
            rules r;
            r.html(rules::html_input);
            std::cout << "--- HTML" << std::endl;
            r.add_tag("p",rules::any_tag);
            r.add_tag("b",rules::opening_and_closing);
            r.add_tag("i",rules::opening_and_closing);
            r.add_tag("hr",rules::stand_alone);
            TEST(validate_simple("<p>x<p>y<p>z",r));
            TEST(validate_simple("<p>x</p>y<p>z</p>",r));
            TEST(validate_simple("<b> y<p>z</B>",r));
            TEST(validate_simple("<i><b> y<p>z<hr></B></i>",r));
            TEST(!validate_simple("<b><i> y<p>z</B></i>",r,"<b> y<p>z</B>"));
            TEST(!validate_simple("<i><b> y<p>z<hr></hr></B></i>",r,"<i><b> y<p>z</B></i>"));
        }
    }    
}

void printhex(std::string const &s)
{
    for(unsigned i=0;i<s.size();i++) {
        unsigned char c=s[i];
        std::cout << std::hex << "\\x" << unsigned(c);
    }
    std::cout << std::endl;
}

bool valenc(char const *s,cppcms::xss::rules const &r,char const *rm=0,char const *esc=0,char const *rmalt=0)
{
    bool res = cppcms::xss::validate(s,s+strlen(s),r);
    std::string tmp1,tmp2;
    bool res2 = cppcms::xss::validate_and_filter_if_invalid(s,s+strlen(s),r,tmp1,cppcms::xss::remove_invalid);
    bool res3 = cppcms::xss::validate_and_filter_if_invalid(s,s+strlen(s),r,tmp2,cppcms::xss::escape_invalid,'?');
    TEST(res == res2);
    TEST(res == res3);
    if(!res) {
        if(rm && rmalt) {
            TEST(tmp1==rm || tmp1==rmalt);
        }
        else if(rm) {
            TEST(tmp1==rm);
        }
        if(esc)
            TEST(tmp2==esc);
        TEST(tmp1!=s);
        TEST(tmp2!=s);

        if(rm && esc && strcmp(rm,esc)!=0) {
            // encoding sometimes always remove
            TEST(tmp1!=tmp2);
            TEST(tmp1.size() < strlen(s));
        }
        TEST(cppcms::xss::validate(tmp1.c_str(),tmp1.c_str()+tmp1.size(),r));
        TEST(cppcms::xss::validate(tmp2.c_str(),tmp2.c_str()+tmp2.size(),r));
    }
    return res;

}


void test_encoding()
{
    std::cout << "- Testing encoding validation" << std::endl;
    using namespace cppcms::xss;
    {
        rules r;
        r.encoding("UTF-8");
        std::cout << "-- Testing UTF-8" << std::endl;
        TEST(valenc("שלום עולם",r));
        TEST(!valenc("שלום \xFF" "עולם",r,"שלום עולם","שלום ?עולם"));
        TEST(!valenc("שלום \4עולם",r,"שלום עולם","שלום ?עולם"));
        TEST(!valenc("\xFF" "עולם",r,"עולם","?עולם"));
        TEST(!valenc("\4עולם",r,"עולם","?עולם"));
        TEST(!valenc("שלום \xFF",r,"שלום ","שלום ?"));
        TEST(!valenc("שלום \4",r,"שלום ","שלום ?"));
    }
    {
        rules r;
        r.encoding("ISO-8859-8");
        std::cout << "-- Testing ISO-8859-8" << std::endl;
        TEST(valenc("\xf9\xec\xe5\xed \xf2\xe5\xec\xed",r));
        TEST(!valenc("\xf9\xec\xe5\xed \xc8\xf2\xe5\xec\xed",r,"\xf9\xec\xe5\xed \xf2\xe5\xec\xed","\xf9\xec\xe5\xed ?\xf2\xe5\xec\xed"));
        TEST(!valenc("\xf9\xec\xe5\xed \4\xf2\xe5\xec\xed",r,"\xf9\xec\xe5\xed \xf2\xe5\xec\xed","\xf9\xec\xe5\xed ?\xf2\xe5\xec\xed"));
        TEST(!valenc("\xc8\xf9\xec\xe5\xed \xf2\xe5\xec\xed",r,"\xf9\xec\xe5\xed \xf2\xe5\xec\xed","?\xf9\xec\xe5\xed \xf2\xe5\xec\xed"));
        TEST(!valenc("\4\xf9\xec\xe5\xed \xf2\xe5\xec\xed",r,"\xf9\xec\xe5\xed \xf2\xe5\xec\xed","?\xf9\xec\xe5\xed \xf2\xe5\xec\xed"));
        TEST(!valenc("\xf9\xec\xe5\xed \xc8",r,"\xf9\xec\xe5\xed ","\xf9\xec\xe5\xed ?"));
        TEST(!valenc("\xf9\xec\xe5\xed \4",r,"\xf9\xec\xe5\xed ","\xf9\xec\xe5\xed ?"));
    }
    {
        try {
            rules r;
            r.encoding("windows-932");
            std::cout << "-- Testing windows-932" << std::endl;
            TEST(valenc("aX\xCB\x83\x71\x82\xd0",r));
            // Only remove
            // single invalid code point in the middle 0xa0
            TEST(!valenc("aX\xCB\x83\x71\xa0\x82\xd0",r,"aX\xCB\x83\x71\x82\xd0","aX\xCB\x83\x71\x82\xd0"));
            // single invalid code point in the end 0xa0
            TEST(!valenc("aX\xCB\x83\x71\xa0",r,"aX\xCB\x83\x71","aX\xCB\x83\x71"));
            // single invalid code point in the begin 0xa0
            TEST(!valenc("\xa0\x82\xd0",r,"\x82\xd0","\x82\xd0"));
            // incompete code point in the end 0x82xd0
            TEST(!valenc("aX\xCB\x83\x71\x82\xd0\x82",r,"aX\xCB\x83\x71\x82\xd0","aX\xCB\x83\x71\x82\xd0"));
            // single invalid code point in the middle 4
            TEST(!valenc("aX\xCB\x83\x71\4\x82\xd0",r,"aX\xCB\x83\x71\x82\xd0","aX\xCB\x83\x71\x82\xd0"));
            // single invalid code point in the end 4
            TEST(!valenc("aX\xCB\x83\x71\4",r,"aX\xCB\x83\x71","aX\xCB\x83\x71"));
            // single invalid code point in the begin 4
            TEST(!valenc("\4\x82\xd0",r,"\x82\xd0","\x82\xd0"));
            // single invalid code point in the middle 83 f0
            TEST(!valenc(    "aX\xCB\x83\x71\x83\xf0\x82\xd0",r,
                    "aX\xCB\x83\x71\x82\xd0",
                    0, // both are legal ways to interpret as xf0x82 + d0 and x82xd0 are valid code points
                    "aX\xCB\x83\x71\xf0\x82\xd0"));
            // single invalid code point in the end 83 f0
            TEST(!valenc("aX\xCB\x83\x71\x83\xf0",r,"aX\xCB\x83\x71","aX\xCB\x83\x71"));
            // single invalid code point in the begin 83 f0
            TEST(!valenc(    "\x83\xf0\x82\xd0",r,
                    "\x82\xd0", 
                    0, // both are legal ways to interpret as xf0x82 + d0 and x82xd0 are valid code points
                    "\xf0\x82\xd0"));
        }
        catch(booster::locale::conv::invalid_charset_error const &e) {
            std::cout << "--- windows-932 charset is not supported" << std::endl;
        }
    }
}

void test_json()
{
    std::cout << "- Testing json format" << std::endl;
    using namespace cppcms::xss;
    
    {
        std::cout << "-- Testing basic properties" << std::endl;
        {
            cppcms::json::value v;
            v["xhtml"]=true;
            v["comments"]=true;
            v["numeric_entities"]=true;
            rules r(v);
            TEST(r.html() == rules::xhtml_input);
            TEST(r.comments_allowed());
            TEST(r.numeric_entities_allowed());
        }
        {
            cppcms::json::value v;
            v["xhtml"]=false;
            v["comments"]=false;
            v["numeric_entities"]=false;
            rules r(v);
            TEST(r.html() == rules::html_input);
            TEST(!r.comments_allowed());
            TEST(!r.numeric_entities_allowed());
        }
        {
            cppcms::json::value v=cppcms::json::object();
            rules r(v);
            TEST(r.html() == rules::xhtml_input);
            TEST(!r.comments_allowed());
            TEST(!r.numeric_entities_allowed());
        }
    }
    {
        std::cout << "-- Testing entities" << std::endl;
        cppcms::json::value v;
        v["entities"][0]="copy";
        v["entities"][1]="nbsp";
        rules r(v);
        TEST(r.valid_entity(details::c_string("copy")));
        TEST(r.valid_entity(details::c_string("nbsp")));
        TEST(!r.valid_entity(details::c_string("foo")));
    }
    {
        std::cout <<"-- Testing tags" << std::endl;
        {
            cppcms::json::value v;
            v["tags"]["opening_and_closing"][0]="b";
            v["tags"]["opening_and_closing"][1]="i";
            v["tags"]["stand_alone"][0]="br";
            v["tags"]["stand_alone"][1]="hr";
            v["tags"]["any_tag"][0]="input";
            rules r(v);
            TEST(r.valid_tag("b")==rules::opening_and_closing);
            TEST(r.valid_tag("i")==rules::opening_and_closing);
            TEST(r.valid_tag("hr")==rules::stand_alone);
            TEST(r.valid_tag("br")==rules::stand_alone);
            TEST(r.valid_tag("input")==rules::any_tag);
        }
    }
    {
        std::cout <<"-- Testing properties" << std::endl;
        {
            std::cout <<"--- Testing properties setting" << std::endl;
            cppcms::json::value v;
            
            v["tags"]["opening_and_closing"][0]="b";
            v["tags"]["opening_and_closing"][1]="i";
            v["tags"]["opening_and_closing"][2]="a";
            
            v["attributes"][0]["tags"][0]="b";
            v["attributes"][0]["tags"][1]="i";
            
            v["attributes"][0]["attributes"][0]="class";
            v["attributes"][0]["attributes"][1]="id";
            
            v["attributes"][0]["pairs"][0]["tag"]="a";
            v["attributes"][0]["pairs"][0]["attr"]="name";
            
            v["attributes"][0]["pairs"][1]["tag"]="b";
            v["attributes"][0]["pairs"][1]["attr"]="foo";

            v["attributes"][0]["type"]="regex";
            v["attributes"][0]["expression"]="[a-z]+";

            rules r(v);
            TEST(r.valid_property("b","class","x"));
            TEST(r.valid_property("b","id","x"));

            TEST(r.valid_property("i","class","x"));
            TEST(r.valid_property("i","id","x"));


            TEST(!r.valid_property("a","class","x"));
            TEST(r.valid_property("a","name","x"));
            TEST(r.valid_property("b","foo","x"));
        }
        {
            std::cout <<"--- Testing properties options" << std::endl;
            cppcms::json::value basic;
            basic["xhtml"]=false;
            basic["tags"]["opening_and_closing"][0]="foo";
            basic["attributes"][0]["tags"][0]="foo";
            basic["attributes"][0]["attributes"][0]="bar";
            {
                cppcms::json::value v=basic;
                v["attributes"][0]["type"]="boolean";
                rules r(v);
                TEST(r.valid_boolean_property("foo","bar"));
            }
            {
                cppcms::json::value v=basic;
                v["attributes"][0]["type"]="integer";
                rules r(v);
                TEST(r.valid_property("foo","bar","10"));
                TEST(!r.valid_property("foo","bar","xxx"));
            }
            {
                cppcms::json::value v=basic;
                v["attributes"][0]["type"]="regex";
                v["attributes"][0]["expression"]="[0-9a-fA-F]+";
                rules r(v);
                TEST(r.valid_property("foo","bar","10f"));
                TEST(!r.valid_property("foo","bar","x"));
            }

            {
                cppcms::json::value v=basic;
                v["attributes"][0]["type"]="uri";
                rules r(v);
                TEST(r.valid_property("foo","bar","/foo"));
                TEST(r.valid_property("foo","bar","http://www.google.com/foo"));
                TEST(!r.valid_property("foo","bar","javascript://www.google.com/foo"));
            }
            {
                cppcms::json::value v=basic;
                v["attributes"][0]["type"]="uri";
                v["attributes"][0]["scheme"]="(http|https)";
                rules r(v);
                TEST(r.valid_property("foo","bar","http://www.google.com/foo"));
                TEST(r.valid_property("foo","bar","/foo"));
                TEST(!r.valid_property("foo","bar","ftp://www.google.com/foo"));
            }
            {
                cppcms::json::value v=basic;
                v["attributes"][0]["type"]="absolute_uri";
                rules r(v);
                TEST(r.valid_property("foo","bar","http://www.google.com/foo"));
                TEST(!r.valid_property("foo","bar","/foo"));
                TEST(!r.valid_property("foo","bar","javascript://www.google.com/foo"));
            }
            {
                cppcms::json::value v=basic;
                v["attributes"][0]["type"]="absolute_uri";
                v["attributes"][0]["scheme"]="(http|https)";
                rules r(v);
                TEST(r.valid_property("foo","bar","http://www.google.com/foo"));
                TEST(!r.valid_property("foo","bar","/foo"));
                TEST(!r.valid_property("foo","bar","javascript://www.google.com/foo"));
                TEST(!r.valid_property("foo","bar","ftp://www.google.com/foo"));
            }
            {
                cppcms::json::value v=basic;
                v["attributes"][0]["type"]="relative_uri";
                rules r(v);
                TEST(r.valid_property("foo","bar","/foo"));
                TEST(r.valid_property("foo","bar","foo.txt"));
                TEST(!r.valid_property("foo","bar","http://www.google.com/foo"));
                TEST(!r.valid_property("foo","bar","javascript://www.google.com/foo"));

            }

        }
        std::cout << "-- From file" << std::endl;
        {
            cppcms::json::value v;
            v["xhtml"]=false;
            v["comments"]=true;
            v["numeric_entities"]=true;
            std::ofstream f("test.txt");
            f << v;
            f.close();
            rules r(std::string("test.txt"));
            TEST(r.html() == rules::html_input);
            TEST(r.comments_allowed());
            TEST(r.numeric_entities_allowed());
            std::remove("test.txt");
        }
    }

}

int main()
{
    try {
        test_rules();
        test_validation();
        test_encoding();
        test_json();
    }
    catch(std::exception const &e){
        std::cerr << "Fail " << e.what() << std::endl;
        return 1;
    }
    std::cout << "Ok" << std::endl;
    return 0;
}