Copyright (c) 2014, David Kitchen <david@buro9.com> All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the organisation (Microcosm) nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

package bluemonday

import (
	
	
	
)
Policy encapsulates the whitelist of HTML elements and attributes that will be applied to the sanitised HTML. You should use bluemonday.NewPolicy() to create a blank policy as the unexported fields contain maps that need to be initialized.
type Policy struct {
Declares whether the maps have been initialized, used as a cheap check to ensure that those using Policy{} directly won't cause nil pointer exceptions
If true then we add spaces when stripping tags, specifically the closing tag is replaced by a space character.
When true, add rel="nofollow" to HTML anchors
When true, add rel="nofollow" to HTML anchors Will add for href="http://foo" Will skip for href="/foo" or href="foo"
When true add target="_blank" to fully qualified links Will add for href="http://foo" Will skip for href="/foo" or href="foo"
When true, URLs must be parseable by "net/url" url.Parse()
When true, u, _ := url.Parse("url"); !u.IsAbs() is permitted
When true, allow data attributes.
map[htmlElementName]map[htmlAttributeName]attrPolicy
map[htmlAttributeName]attrPolicy
If urlPolicy is nil, all URLs with matching schema are allowed. Otherwise, only the URLs with matching schema and urlPolicy(url) returning true are allowed.
If an element has had all attributes removed as a result of a policy being applied, then the element would be removed from the output. However some elements are valid and have strong layout meaning without any attributes, i.e. <table>. To prevent those being removed we maintain a list of elements that are allowed to have no attributes and that will be maintained in the output HTML.
optional pattern to match, when not nil the regexp needs to match otherwise the attribute is removed
init initializes the maps if this has not been done already
NewPolicy returns a blank policy with nothing whitelisted or permitted. This is the recommended way to start building a policy and you should now use AllowAttrs() and/or AllowElements() to construct the whitelist of HTML elements and attributes.
AllowAttrs takes a range of HTML attribute names and returns an attribute policy builder that allows you to specify the pattern and scope of the whitelisted attribute. The attribute policy is only added to the core policy when either Globally() or OnElements(...) are called.
func ( *Policy) ( ...string) *attrPolicyBuilder {

	.init()

	 := attrPolicyBuilder{
		p:          ,
		allowEmpty: false,
	}

	for ,  := range  {
		.attrNames = append(.attrNames, strings.ToLower())
	}

	return &
}
AllowDataAttributes whitelists all data attributes. We can't specify the name of each attribute exactly as they are customized. NOTE: These values are not sanitized and applications that evaluate or process them without checking and verification of the input may be at risk if this option is enabled. This is a 'caveat emptor' option and the person enabling this option needs to fully understand the potential impact with regards to whatever application will be consuming the sanitized HTML afterwards, i.e. if you know you put a link in a data attribute and use that to automatically load some new window then you're giving the author of a HTML fragment the means to open a malicious destination automatically. Use with care!
AllowNoAttrs says that attributes on element are optional. The attribute policy is only added to the core policy when OnElements(...) are called.
func ( *Policy) () *attrPolicyBuilder {

	.init()

	 := attrPolicyBuilder{
		p:          ,
		allowEmpty: true,
	}
	return &
}
AllowNoAttrs says that attributes on element are optional. The attribute policy is only added to the core policy when OnElements(...) are called.
func ( *attrPolicyBuilder) () *attrPolicyBuilder {

	.allowEmpty = true

	return 
}
Matching allows a regular expression to be applied to a nascent attribute policy, and returns the attribute policy. Calling this more than once will replace the existing regexp.
func ( *attrPolicyBuilder) ( *regexp.Regexp) *attrPolicyBuilder {

	.regexp = 

	return 
}
OnElements will bind an attribute policy to a given range of HTML elements and return the updated policy
func ( *attrPolicyBuilder) ( ...string) *Policy {

	for ,  := range  {
		 = strings.ToLower()

		for ,  := range .attrNames {

			if ,  := .p.elsAndAttrs[]; ! {
				.p.elsAndAttrs[] = make(map[string]attrPolicy)
			}

			 := attrPolicy{}
			if .regexp != nil {
				.regexp = .regexp
			}

			.p.elsAndAttrs[][] = 
		}

		if .allowEmpty {
			.p.setOfElementsAllowedWithoutAttrs[] = struct{}{}

			if ,  := .p.elsAndAttrs[]; ! {
				.p.elsAndAttrs[] = make(map[string]attrPolicy)
			}
		}
	}

	return .p
}
Globally will bind an attribute policy to all HTML elements and return the updated policy
func ( *attrPolicyBuilder) () *Policy {

	for ,  := range .attrNames {
		if ,  := .p.globalAttrs[]; ! {
			.p.globalAttrs[] = attrPolicy{}
		}

		 := attrPolicy{}
		if .regexp != nil {
			.regexp = .regexp
		}

		.p.globalAttrs[] = 
	}

	return .p
}
AllowElements will append HTML elements to the whitelist without applying an attribute policy to those elements (the elements are permitted sans-attributes)
func ( *Policy) ( ...string) *Policy {
	.init()

	for ,  := range  {
		 = strings.ToLower()

		if ,  := .elsAndAttrs[]; ! {
			.elsAndAttrs[] = make(map[string]attrPolicy)
		}
	}

	return 
}
RequireNoFollowOnLinks will result in all <a> tags having a rel="nofollow" added to them if one does not already exist Note: This requires p.RequireParseableURLs(true) and will enable it.
func ( *Policy) ( bool) *Policy {

	.requireNoFollow = 
	.requireParseableURLs = true

	return 
}
RequireNoFollowOnFullyQualifiedLinks will result in all <a> tags that point to a non-local destination (i.e. starts with a protocol and has a host) having a rel="nofollow" added to them if one does not already exist Note: This requires p.RequireParseableURLs(true) and will enable it.
AddTargetBlankToFullyQualifiedLinks will result in all <a> tags that point to a non-local destination (i.e. starts with a protocol and has a host) having a target="_blank" added to them if one does not already exist Note: This requires p.RequireParseableURLs(true) and will enable it.
RequireParseableURLs will result in all URLs requiring that they be parseable by "net/url" url.Parse() This applies to: - a.href - area.href - blockquote.cite - img.src - link.href - script.src
func ( *Policy) ( bool) *Policy {

	.requireParseableURLs = 

	return 
}
AllowRelativeURLs enables RequireParseableURLs and then permits URLs that are parseable, have no schema information and url.IsAbs() returns false This permits local URLs
func ( *Policy) ( bool) *Policy {

	.RequireParseableURLs(true)
	.allowRelativeURLs = 

	return 
}
AllowURLSchemes will append URL schemes to the whitelist Example: p.AllowURLSchemes("mailto", "http", "https")
func ( *Policy) ( ...string) *Policy {
	.init()

	.RequireParseableURLs(true)

	for ,  := range  {
		 = strings.ToLower()
Allow all URLs with matching scheme.
		.allowURLSchemes[] = nil
	}

	return 
}
AllowURLSchemeWithCustomPolicy will append URL schemes with a custom URL policy to the whitelist. Only the URLs with matching schema and urlPolicy(url) returning true will be allowed.
func ( *Policy) (
	 string,
	 func( *url.URL) ( bool),
) *Policy {

	.init()

	.RequireParseableURLs(true)

	 = strings.ToLower()

	.allowURLSchemes[] = 

	return 
}
AddSpaceWhenStrippingTag states whether to add a single space " " when removing tags that are not whitelisted by the policy. This is useful if you expect to strip tags in dense markup and may lose the value of whitespace. For example: "<p>Hello</p><p>World</p>"" would be sanitized to "HelloWorld" with the default value of false, but you may wish to sanitize this to " Hello World " by setting AddSpaceWhenStrippingTag to true as this would retain the intent of the text.
func ( *Policy) ( bool) *Policy {

	.addSpaces = 

	return 
}
SkipElementsContent adds the HTML elements whose tags is needed to be removed with its content.
func ( *Policy) ( ...string) *Policy {

	.init()

	for ,  := range  {
		 = strings.ToLower()

		if ,  := .setOfElementsToSkipContent[]; ! {
			.setOfElementsToSkipContent[] = struct{}{}
		}
	}

	return 
}
AllowElementsContent marks the HTML elements whose content should be retained after removing the tag.
func ( *Policy) ( ...string) *Policy {

	.init()

	for ,  := range  {
		delete(.setOfElementsToSkipContent, strings.ToLower())
	}

	return 
}
addDefaultElementsWithoutAttrs adds the HTML elements that we know are valid without any attributes to an internal map. i.e. we know that <table> is valid, but <bdo> isn't valid as the "dir" attr is mandatory
func ( *Policy) () {
	.init()

	.setOfElementsAllowedWithoutAttrs["abbr"] = struct{}{}
	.setOfElementsAllowedWithoutAttrs["acronym"] = struct{}{}
	.setOfElementsAllowedWithoutAttrs["address"] = struct{}{}
	.setOfElementsAllowedWithoutAttrs["article"] = struct{}{}
	.setOfElementsAllowedWithoutAttrs["aside"] = struct{}{}
	.setOfElementsAllowedWithoutAttrs["audio"] = struct{}{}
	.setOfElementsAllowedWithoutAttrs["b"] = struct{}{}
	.setOfElementsAllowedWithoutAttrs["bdi"] = struct{}{}
	.setOfElementsAllowedWithoutAttrs["blockquote"] = struct{}{}
	.setOfElementsAllowedWithoutAttrs["body"] = struct{}{}
	.setOfElementsAllowedWithoutAttrs["br"] = struct{}{}
	.setOfElementsAllowedWithoutAttrs["button"] = struct{}{}
	.setOfElementsAllowedWithoutAttrs["canvas"] = struct{}{}
	.setOfElementsAllowedWithoutAttrs["caption"] = struct{}{}
	.setOfElementsAllowedWithoutAttrs["center"] = struct{}{}
	.setOfElementsAllowedWithoutAttrs["cite"] = struct{}{}
	.setOfElementsAllowedWithoutAttrs["code"] = struct{}{}
	.setOfElementsAllowedWithoutAttrs["col"] = struct{}{}
	.setOfElementsAllowedWithoutAttrs["colgroup"] = struct{}{}
	.setOfElementsAllowedWithoutAttrs["datalist"] = struct{}{}
	.setOfElementsAllowedWithoutAttrs["dd"] = struct{}{}
	.setOfElementsAllowedWithoutAttrs["del"] = struct{}{}
	.setOfElementsAllowedWithoutAttrs["details"] = struct{}{}
	.setOfElementsAllowedWithoutAttrs["dfn"] = struct{}{}
	.setOfElementsAllowedWithoutAttrs["div"] = struct{}{}
	.setOfElementsAllowedWithoutAttrs["dl"] = struct{}{}
	.setOfElementsAllowedWithoutAttrs["dt"] = struct{}{}
	.setOfElementsAllowedWithoutAttrs["em"] = struct{}{}
	.setOfElementsAllowedWithoutAttrs["fieldset"] = struct{}{}
	.setOfElementsAllowedWithoutAttrs["figcaption"] = struct{}{}
	.setOfElementsAllowedWithoutAttrs["figure"] = struct{}{}
	.setOfElementsAllowedWithoutAttrs["footer"] = struct{}{}
	.setOfElementsAllowedWithoutAttrs["h1"] = struct{}{}
	.setOfElementsAllowedWithoutAttrs["h2"] = struct{}{}
	.setOfElementsAllowedWithoutAttrs["h3"] = struct{}{}
	.setOfElementsAllowedWithoutAttrs["h4"] = struct{}{}
	.setOfElementsAllowedWithoutAttrs["h5"] = struct{}{}
	.setOfElementsAllowedWithoutAttrs["h6"] = struct{}{}
	.setOfElementsAllowedWithoutAttrs["head"] = struct{}{}
	.setOfElementsAllowedWithoutAttrs["header"] = struct{}{}
	.setOfElementsAllowedWithoutAttrs["hgroup"] = struct{}{}
	.setOfElementsAllowedWithoutAttrs["hr"] = struct{}{}
	.setOfElementsAllowedWithoutAttrs["html"] = struct{}{}
	.setOfElementsAllowedWithoutAttrs["i"] = struct{}{}
	.setOfElementsAllowedWithoutAttrs["ins"] = struct{}{}
	.setOfElementsAllowedWithoutAttrs["kbd"] = struct{}{}
	.setOfElementsAllowedWithoutAttrs["li"] = struct{}{}
	.setOfElementsAllowedWithoutAttrs["mark"] = struct{}{}
	.setOfElementsAllowedWithoutAttrs["marquee"] = struct{}{}
	.setOfElementsAllowedWithoutAttrs["nav"] = struct{}{}
	.setOfElementsAllowedWithoutAttrs["ol"] = struct{}{}
	.setOfElementsAllowedWithoutAttrs["optgroup"] = struct{}{}
	.setOfElementsAllowedWithoutAttrs["option"] = struct{}{}
	.setOfElementsAllowedWithoutAttrs["p"] = struct{}{}
	.setOfElementsAllowedWithoutAttrs["pre"] = struct{}{}
	.setOfElementsAllowedWithoutAttrs["q"] = struct{}{}
	.setOfElementsAllowedWithoutAttrs["rp"] = struct{}{}
	.setOfElementsAllowedWithoutAttrs["rt"] = struct{}{}
	.setOfElementsAllowedWithoutAttrs["ruby"] = struct{}{}
	.setOfElementsAllowedWithoutAttrs["s"] = struct{}{}
	.setOfElementsAllowedWithoutAttrs["samp"] = struct{}{}
	.setOfElementsAllowedWithoutAttrs["script"] = struct{}{}
	.setOfElementsAllowedWithoutAttrs["section"] = struct{}{}
	.setOfElementsAllowedWithoutAttrs["select"] = struct{}{}
	.setOfElementsAllowedWithoutAttrs["small"] = struct{}{}
	.setOfElementsAllowedWithoutAttrs["span"] = struct{}{}
	.setOfElementsAllowedWithoutAttrs["strike"] = struct{}{}
	.setOfElementsAllowedWithoutAttrs["strong"] = struct{}{}
	.setOfElementsAllowedWithoutAttrs["style"] = struct{}{}
	.setOfElementsAllowedWithoutAttrs["sub"] = struct{}{}
	.setOfElementsAllowedWithoutAttrs["summary"] = struct{}{}
	.setOfElementsAllowedWithoutAttrs["sup"] = struct{}{}
	.setOfElementsAllowedWithoutAttrs["svg"] = struct{}{}
	.setOfElementsAllowedWithoutAttrs["table"] = struct{}{}
	.setOfElementsAllowedWithoutAttrs["tbody"] = struct{}{}
	.setOfElementsAllowedWithoutAttrs["td"] = struct{}{}
	.setOfElementsAllowedWithoutAttrs["textarea"] = struct{}{}
	.setOfElementsAllowedWithoutAttrs["tfoot"] = struct{}{}
	.setOfElementsAllowedWithoutAttrs["th"] = struct{}{}
	.setOfElementsAllowedWithoutAttrs["thead"] = struct{}{}
	.setOfElementsAllowedWithoutAttrs["title"] = struct{}{}
	.setOfElementsAllowedWithoutAttrs["time"] = struct{}{}
	.setOfElementsAllowedWithoutAttrs["tr"] = struct{}{}
	.setOfElementsAllowedWithoutAttrs["tt"] = struct{}{}
	.setOfElementsAllowedWithoutAttrs["u"] = struct{}{}
	.setOfElementsAllowedWithoutAttrs["ul"] = struct{}{}
	.setOfElementsAllowedWithoutAttrs["var"] = struct{}{}
	.setOfElementsAllowedWithoutAttrs["video"] = struct{}{}
	.setOfElementsAllowedWithoutAttrs["wbr"] = struct{}{}

}
addDefaultSkipElementContent adds the HTML elements that we should skip rendering the character content of, if the element itself is not allowed. This is all character data that the end user would not normally see. i.e. if we exclude a <script> tag then we shouldn't render the JavaScript or anything else until we encounter the closing </script> tag.
func ( *Policy) () {
	.init()

	.setOfElementsToSkipContent["frame"] = struct{}{}
	.setOfElementsToSkipContent["frameset"] = struct{}{}
	.setOfElementsToSkipContent["iframe"] = struct{}{}
	.setOfElementsToSkipContent["noembed"] = struct{}{}
	.setOfElementsToSkipContent["noframes"] = struct{}{}
	.setOfElementsToSkipContent["noscript"] = struct{}{}
	.setOfElementsToSkipContent["nostyle"] = struct{}{}
	.setOfElementsToSkipContent["object"] = struct{}{}
	.setOfElementsToSkipContent["script"] = struct{}{}
	.setOfElementsToSkipContent["style"] = struct{}{}
	.setOfElementsToSkipContent["title"] = struct{}{}