Utils/t/xss_test.cpp
///////////////////////////////////////////////////////////////////////////////
//
// 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("XSS")"));
TEST(!r.valid_property("a","href","javascript:"
"alert('XSS')"));
TEST(!r.valid_property("a","href","javascript:al"
"ert('XSS')"));
TEST(!r.valid_property("a","href","javascript: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&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'm"));
TEST(!r.valid_property("a","href","jav
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 & not to be",r));
TEST(!validate_simple("to be ∨ not to be",r,"to be not to be","to be &or; not to be"));
TEST(!validate_simple("∨ not to be",r));
TEST(!validate_simple("to be ∨",r));
TEST(!validate_simple("& not to be",r,"","&amp not to be"));
TEST(!validate_simple("to be &",r,"to be ","to be &amp"));
r.add_entity("or");
TEST(validate_simple("to be ∨ not to be",r));
TEST(validate_simple("∨ not to be",r));
TEST(validate_simple("to be ∨",r));
TEST(!validate_simple("@",r));
TEST(!validate_simple("<",r));
TEST(!validate_simple("<",r));
r.numeric_entities_allowed(true);
TEST(validate_simple("@",r));
TEST(validate_simple("<",r));
TEST(validate_simple("<",r));
TEST(!validate_simple("",r));
TEST(validate_simple("
",r));
TEST(!validate_simple("",r));
TEST(!validate_simple("",r));
TEST(!validate_simple("�",r));
TEST(validate_simple("",r));
TEST(!validate_simple("�",r));
TEST(!validate_simple("�",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,"","<hr"));
TEST(!validate_simple("hr>",r,"hr","hr>"));
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&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 <!-- test --> world"));
r.comments_allowed(true);
TEST(validate_simple("Hello <!-- test --> world",r));
TEST(!validate_simple("Hello < !-- test --> world",r,"Hello world","Hello < !-- test --> 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;
}