blob: e85f5dda9350eb132e74ea45dfc8fd4ea42a8897 [file] [log] [blame]
// 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_test
import (
"fmt"
"regexp"
"strings"
"github.com/microcosm-cc/bluemonday"
)
func Example() {
// Create a new policy
p := bluemonday.NewPolicy()
// Add elements to a policy without attributes
p.AllowElements("b", "strong")
// Add elements as a virtue of adding an attribute
p.AllowAttrs("nowrap").OnElements("td", "th")
// Attributes can either be added to all elements
p.AllowAttrs("dir").Globally()
//Or attributes can be added to specific elements
p.AllowAttrs("value").OnElements("li")
// It is ALWAYS recommended that an attribute be made to match a pattern
// XSS in HTML attributes is a very easy attack vector
// \p{L} matches unicode letters, \p{N} matches unicode numbers
p.AllowAttrs("title").Matching(regexp.MustCompile(`[\p{L}\p{N}\s\-_',:\[\]!\./\\\(\)&]*`)).Globally()
// You can stop at any time and call .Sanitize()
// Assumes that string htmlIn was passed in from a HTTP POST and contains
// untrusted user generated content
htmlIn := `untrusted user generated content <body onload="alert('XSS')">`
fmt.Println(p.Sanitize(htmlIn))
// And you can take any existing policy and extend it
p = bluemonday.UGCPolicy()
p.AllowElements("fieldset", "select", "option")
// Links are complex beasts and one of the biggest attack vectors for
// malicious content so we have included features specifically to help here.
// This is not recommended:
p = bluemonday.NewPolicy()
p.AllowAttrs("href").Matching(regexp.MustCompile(`(?i)mailto|https?`)).OnElements("a")
// The regexp is insufficient in this case to have prevented a malformed
// value doing something unexpected.
// This will ensure that URLs are not considered invalid by Go's net/url
// package.
p.RequireParseableURLs(true)
// If you have enabled parseable URLs then the following option will allow
// relative URLs. By default this is disabled and will prevent all local and
// schema relative URLs (i.e. `href="//www.google.com"` is schema relative).
p.AllowRelativeURLs(true)
// If you have enabled parseable URLs then you can whitelist the schemas
// that are permitted. Bear in mind that allowing relative URLs in the above
// option allows for blank schemas.
p.AllowURLSchemes("mailto", "http", "https")
// Regardless of whether you have enabled parseable URLs, you can force all
// URLs to have a rel="nofollow" attribute. This will be added if it does
// not exist.
// This applies to "a" "area" "link" elements that have a "href" attribute
p.RequireNoFollowOnLinks(true)
// We provide a convenience function that applies all of the above, but you
// will still need to whitelist the linkable elements:
p = bluemonday.NewPolicy()
p.AllowStandardURLs()
p.AllowAttrs("cite").OnElements("blockquote")
p.AllowAttrs("href").OnElements("a", "area")
p.AllowAttrs("src").OnElements("img")
// Policy Building Helpers
// If you've got this far and you're bored already, we also bundle some
// other convenience functions
p = bluemonday.NewPolicy()
p.AllowStandardAttributes()
p.AllowImages()
p.AllowLists()
p.AllowTables()
}
func ExampleNewPolicy() {
// NewPolicy is a blank policy and we need to explicitly whitelist anything
// that we wish to allow through
p := bluemonday.NewPolicy()
// We ensure any URLs are parseable and have rel="nofollow" where applicable
p.AllowStandardURLs()
// AllowStandardURLs already ensures that the href will be valid, and so we
// can skip the .Matching()
p.AllowAttrs("href").OnElements("a")
// We allow paragraphs too
p.AllowElements("p")
html := p.Sanitize(
`<p><a onblur="alert(secret)" href="http://www.google.com">Google</a></p>`,
)
fmt.Println(html)
// Output:
//<p><a href="http://www.google.com" rel="nofollow">Google</a></p>
}
func ExampleStrictPolicy() {
// StrictPolicy is equivalent to NewPolicy and as nothing else is declared
// we are stripping all elements (and their attributes)
p := bluemonday.StrictPolicy()
html := p.Sanitize(
`Goodbye <a onblur="alert(secret)" href="http://en.wikipedia.org/wiki/Goodbye_Cruel_World_(Pink_Floyd_song)">Cruel</a> World`,
)
fmt.Println(html)
// Output:
//Goodbye Cruel World
}
func ExampleUGCPolicy() {
// UGCPolicy is a convenience policy for user generated content.
p := bluemonday.UGCPolicy()
html := p.Sanitize(
`<a onblur="alert(secret)" href="http://www.google.com">Google</a>`,
)
fmt.Println(html)
// Output:
//<a href="http://www.google.com" rel="nofollow">Google</a>
}
func ExamplePolicy_AllowAttrs() {
p := bluemonday.NewPolicy()
// Allow the 'title' attribute on every HTML element that has been
// whitelisted
p.AllowAttrs("title").Matching(bluemonday.Paragraph).Globally()
// Allow the 'abbr' attribute on only the 'td' and 'th' elements.
p.AllowAttrs("abbr").Matching(bluemonday.Paragraph).OnElements("td", "th")
// Allow the 'colspan' and 'rowspan' attributes, matching a positive integer
// pattern, on only the 'td' and 'th' elements.
p.AllowAttrs("colspan", "rowspan").Matching(
bluemonday.Integer,
).OnElements("td", "th")
}
func ExamplePolicy_AllowElements() {
p := bluemonday.NewPolicy()
// Allow styling elements without attributes
p.AllowElements("br", "div", "hr", "p", "span")
}
func ExamplePolicy_Sanitize() {
// UGCPolicy is a convenience policy for user generated content.
p := bluemonday.UGCPolicy()
// string in, string out
html := p.Sanitize(`<a onblur="alert(secret)" href="http://www.google.com">Google</a>`)
fmt.Println(html)
// Output:
//<a href="http://www.google.com" rel="nofollow">Google</a>
}
func ExamplePolicy_SanitizeBytes() {
// UGCPolicy is a convenience policy for user generated content.
p := bluemonday.UGCPolicy()
// []byte in, []byte out
b := []byte(`<a onblur="alert(secret)" href="http://www.google.com">Google</a>`)
b = p.SanitizeBytes(b)
fmt.Println(string(b))
// Output:
//<a href="http://www.google.com" rel="nofollow">Google</a>
}
func ExamplePolicy_SanitizeReader() {
// UGCPolicy is a convenience policy for user generated content.
p := bluemonday.UGCPolicy()
// io.Reader in, bytes.Buffer out
r := strings.NewReader(`<a onblur="alert(secret)" href="http://www.google.com">Google</a>`)
buf := p.SanitizeReader(r)
fmt.Println(buf.String())
// Output:
//<a href="http://www.google.com" rel="nofollow">Google</a>
}