gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

policy.go (32399B)


      1 // Copyright (c) 2014, David Kitchen <david@buro9.com>
      2 //
      3 // All rights reserved.
      4 //
      5 // Redistribution and use in source and binary forms, with or without
      6 // modification, are permitted provided that the following conditions are met:
      7 //
      8 // * Redistributions of source code must retain the above copyright notice, this
      9 //   list of conditions and the following disclaimer.
     10 //
     11 // * Redistributions in binary form must reproduce the above copyright notice,
     12 //   this list of conditions and the following disclaimer in the documentation
     13 //   and/or other materials provided with the distribution.
     14 //
     15 // * Neither the name of the organisation (Microcosm) nor the names of its
     16 //   contributors may be used to endorse or promote products derived from
     17 //   this software without specific prior written permission.
     18 //
     19 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
     20 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     21 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
     22 // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
     23 // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     24 // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
     25 // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
     26 // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
     27 // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     28 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     29 
     30 package bluemonday
     31 
     32 //TODO sgutzwiller create map of styles to default handlers
     33 //TODO sgutzwiller create handlers for various attributes
     34 import (
     35 	"net/url"
     36 	"regexp"
     37 	"strings"
     38 
     39 	"github.com/microcosm-cc/bluemonday/css"
     40 )
     41 
     42 // Policy encapsulates the allowlist of HTML elements and attributes that will
     43 // be applied to the sanitised HTML.
     44 //
     45 // You should use bluemonday.NewPolicy() to create a blank policy as the
     46 // unexported fields contain maps that need to be initialized.
     47 type Policy struct {
     48 
     49 	// Declares whether the maps have been initialized, used as a cheap check to
     50 	// ensure that those using Policy{} directly won't cause nil pointer
     51 	// exceptions
     52 	initialized bool
     53 
     54 	// If true then we add spaces when stripping tags, specifically the closing
     55 	// tag is replaced by a space character.
     56 	addSpaces bool
     57 
     58 	// When true, add rel="nofollow" to HTML a, area, and link tags
     59 	requireNoFollow bool
     60 
     61 	// When true, add rel="nofollow" to HTML a, area, and link tags
     62 	// Will add for href="http://foo"
     63 	// Will skip for href="/foo" or href="foo"
     64 	requireNoFollowFullyQualifiedLinks bool
     65 
     66 	// When true, add rel="noreferrer" to HTML a, area, and link tags
     67 	requireNoReferrer bool
     68 
     69 	// When true, add rel="noreferrer" to HTML a, area, and link tags
     70 	// Will add for href="http://foo"
     71 	// Will skip for href="/foo" or href="foo"
     72 	requireNoReferrerFullyQualifiedLinks bool
     73 
     74 	// When true, add crossorigin="anonymous" to HTML audio, img, link, script, and video tags
     75 	requireCrossOriginAnonymous bool
     76 
     77 	// When true, add and filter sandbox attribute on iframe tags
     78 	requireSandboxOnIFrame map[string]bool
     79 
     80 	// When true add target="_blank" to fully qualified links
     81 	// Will add for href="http://foo"
     82 	// Will skip for href="/foo" or href="foo"
     83 	addTargetBlankToFullyQualifiedLinks bool
     84 
     85 	// When true, URLs must be parseable by "net/url" url.Parse()
     86 	requireParseableURLs bool
     87 
     88 	// When true, u, _ := url.Parse("url"); !u.IsAbs() is permitted
     89 	allowRelativeURLs bool
     90 
     91 	// When true, allow data attributes.
     92 	allowDataAttributes bool
     93 
     94 	// When true, allow comments.
     95 	allowComments bool
     96 
     97 	// map[htmlElementName]map[htmlAttributeName][]attrPolicy
     98 	elsAndAttrs map[string]map[string][]attrPolicy
     99 
    100 	// elsMatchingAndAttrs stores regex based element matches along with attributes
    101 	elsMatchingAndAttrs map[*regexp.Regexp]map[string][]attrPolicy
    102 
    103 	// map[htmlAttributeName][]attrPolicy
    104 	globalAttrs map[string][]attrPolicy
    105 
    106 	// map[htmlElementName]map[cssPropertyName][]stylePolicy
    107 	elsAndStyles map[string]map[string][]stylePolicy
    108 
    109 	// map[regex]map[cssPropertyName][]stylePolicy
    110 	elsMatchingAndStyles map[*regexp.Regexp]map[string][]stylePolicy
    111 
    112 	// map[cssPropertyName][]stylePolicy
    113 	globalStyles map[string][]stylePolicy
    114 
    115 	// If urlPolicy is nil, all URLs with matching schema are allowed.
    116 	// Otherwise, only the URLs with matching schema and urlPolicy(url)
    117 	// returning true are allowed.
    118 	allowURLSchemes map[string][]urlPolicy
    119 
    120 	// These regexps are used to match allowed URL schemes, for example
    121 	// if one would want to allow all URL schemes, they would add `.+`
    122 	allowURLSchemeRegexps []*regexp.Regexp
    123 
    124 	// If an element has had all attributes removed as a result of a policy
    125 	// being applied, then the element would be removed from the output.
    126 	//
    127 	// However some elements are valid and have strong layout meaning without
    128 	// any attributes, i.e. <table>. To prevent those being removed we maintain
    129 	// a list of elements that are allowed to have no attributes and that will
    130 	// be maintained in the output HTML.
    131 	setOfElementsAllowedWithoutAttrs map[string]struct{}
    132 
    133 	// If an element has had all attributes removed as a result of a policy
    134 	// being applied, then the element would be removed from the output.
    135 	//
    136 	// However some elements are valid and have strong layout meaning without
    137 	// any attributes, i.e. <table>.
    138 	//
    139 	// In this case, any element matching a regular expression will be accepted without
    140 	// attributes added.
    141 	setOfElementsMatchingAllowedWithoutAttrs []*regexp.Regexp
    142 
    143 	setOfElementsToSkipContent map[string]struct{}
    144 
    145 	// Permits fundamentally unsafe elements.
    146 	//
    147 	// If false (default) then elements such as `style` and `script` will not be
    148 	// permitted even if declared in a policy. These elements when combined with
    149 	// untrusted input cannot be safely handled by bluemonday at this point in
    150 	// time.
    151 	//
    152 	// If true then `style` and `script` would be permitted by bluemonday if a
    153 	// policy declares them. However this is not recommended under any circumstance
    154 	// and can lead to XSS being rendered thus defeating the purpose of using a
    155 	// HTML sanitizer.
    156 	allowUnsafe bool
    157 }
    158 
    159 type attrPolicy struct {
    160 
    161 	// optional pattern to match, when not nil the regexp needs to match
    162 	// otherwise the attribute is removed
    163 	regexp *regexp.Regexp
    164 }
    165 
    166 type stylePolicy struct {
    167 	// handler to validate
    168 	handler func(string) bool
    169 
    170 	// optional pattern to match, when not nil the regexp needs to match
    171 	// otherwise the property is removed
    172 	regexp *regexp.Regexp
    173 
    174 	// optional list of allowed property values, for properties which
    175 	// have a defined list of allowed values; property will be removed
    176 	// if the value is not allowed
    177 	enum []string
    178 }
    179 
    180 type attrPolicyBuilder struct {
    181 	p *Policy
    182 
    183 	attrNames  []string
    184 	regexp     *regexp.Regexp
    185 	allowEmpty bool
    186 }
    187 
    188 type stylePolicyBuilder struct {
    189 	p *Policy
    190 
    191 	propertyNames []string
    192 	regexp        *regexp.Regexp
    193 	enum          []string
    194 	handler       func(string) bool
    195 }
    196 
    197 type urlPolicy func(url *url.URL) (allowUrl bool)
    198 
    199 type SandboxValue int64
    200 
    201 const (
    202 	SandboxAllowDownloads SandboxValue = iota
    203 	SandboxAllowDownloadsWithoutUserActivation
    204 	SandboxAllowForms
    205 	SandboxAllowModals
    206 	SandboxAllowOrientationLock
    207 	SandboxAllowPointerLock
    208 	SandboxAllowPopups
    209 	SandboxAllowPopupsToEscapeSandbox
    210 	SandboxAllowPresentation
    211 	SandboxAllowSameOrigin
    212 	SandboxAllowScripts
    213 	SandboxAllowStorageAccessByUserActivation
    214 	SandboxAllowTopNavigation
    215 	SandboxAllowTopNavigationByUserActivation
    216 )
    217 
    218 // init initializes the maps if this has not been done already
    219 func (p *Policy) init() {
    220 	if !p.initialized {
    221 		p.elsAndAttrs = make(map[string]map[string][]attrPolicy)
    222 		p.elsMatchingAndAttrs = make(map[*regexp.Regexp]map[string][]attrPolicy)
    223 		p.globalAttrs = make(map[string][]attrPolicy)
    224 		p.elsAndStyles = make(map[string]map[string][]stylePolicy)
    225 		p.elsMatchingAndStyles = make(map[*regexp.Regexp]map[string][]stylePolicy)
    226 		p.globalStyles = make(map[string][]stylePolicy)
    227 		p.allowURLSchemes = make(map[string][]urlPolicy)
    228 		p.allowURLSchemeRegexps = make([]*regexp.Regexp, 0)
    229 		p.setOfElementsAllowedWithoutAttrs = make(map[string]struct{})
    230 		p.setOfElementsToSkipContent = make(map[string]struct{})
    231 		p.initialized = true
    232 	}
    233 }
    234 
    235 // NewPolicy returns a blank policy with nothing allowed or permitted. This
    236 // is the recommended way to start building a policy and you should now use
    237 // AllowAttrs() and/or AllowElements() to construct the allowlist of HTML
    238 // elements and attributes.
    239 func NewPolicy() *Policy {
    240 
    241 	p := Policy{}
    242 
    243 	p.addDefaultElementsWithoutAttrs()
    244 	p.addDefaultSkipElementContent()
    245 
    246 	return &p
    247 }
    248 
    249 // AllowAttrs takes a range of HTML attribute names and returns an
    250 // attribute policy builder that allows you to specify the pattern and scope of
    251 // the allowed attribute.
    252 //
    253 // The attribute policy is only added to the core policy when either Globally()
    254 // or OnElements(...) are called.
    255 func (p *Policy) AllowAttrs(attrNames ...string) *attrPolicyBuilder {
    256 
    257 	p.init()
    258 
    259 	abp := attrPolicyBuilder{
    260 		p:          p,
    261 		allowEmpty: false,
    262 	}
    263 
    264 	for _, attrName := range attrNames {
    265 		abp.attrNames = append(abp.attrNames, strings.ToLower(attrName))
    266 	}
    267 
    268 	return &abp
    269 }
    270 
    271 // AllowDataAttributes permits all data attributes. We can't specify the name
    272 // of each attribute exactly as they are customized.
    273 //
    274 // NOTE: These values are not sanitized and applications that evaluate or process
    275 // them without checking and verification of the input may be at risk if this option
    276 // is enabled. This is a 'caveat emptor' option and the person enabling this option
    277 // needs to fully understand the potential impact with regards to whatever application
    278 // will be consuming the sanitized HTML afterwards, i.e. if you know you put a link in a
    279 // data attribute and use that to automatically load some new window then you're giving
    280 // the author of a HTML fragment the means to open a malicious destination automatically.
    281 // Use with care!
    282 func (p *Policy) AllowDataAttributes() {
    283 	p.allowDataAttributes = true
    284 }
    285 
    286 // AllowComments allows comments.
    287 //
    288 // Please note that only one type of comment will be allowed by this, this is the
    289 // the standard HTML comment <!-- --> which includes the use of that to permit
    290 // conditionals as per https://docs.microsoft.com/en-us/previous-versions/windows/internet-explorer/ie-developer/compatibility/ms537512(v=vs.85)?redirectedfrom=MSDN
    291 //
    292 // What is not permitted are CDATA XML comments, as the x/net/html package we depend
    293 // on does not handle this fully and we are not choosing to take on that work:
    294 // https://pkg.go.dev/golang.org/x/net/html#Tokenizer.AllowCDATA . If the x/net/html
    295 // package changes this then these will be considered, otherwise if you AllowComments
    296 // but provide a CDATA comment, then as per the documentation in x/net/html this will
    297 // be treated as a plain HTML comment.
    298 func (p *Policy) AllowComments() {
    299 	p.allowComments = true
    300 }
    301 
    302 // AllowNoAttrs says that attributes on element are optional.
    303 //
    304 // The attribute policy is only added to the core policy when OnElements(...)
    305 // are called.
    306 func (p *Policy) AllowNoAttrs() *attrPolicyBuilder {
    307 
    308 	p.init()
    309 
    310 	abp := attrPolicyBuilder{
    311 		p:          p,
    312 		allowEmpty: true,
    313 	}
    314 	return &abp
    315 }
    316 
    317 // AllowNoAttrs says that attributes on element are optional.
    318 //
    319 // The attribute policy is only added to the core policy when OnElements(...)
    320 // are called.
    321 func (abp *attrPolicyBuilder) AllowNoAttrs() *attrPolicyBuilder {
    322 
    323 	abp.allowEmpty = true
    324 
    325 	return abp
    326 }
    327 
    328 // Matching allows a regular expression to be applied to a nascent attribute
    329 // policy, and returns the attribute policy.
    330 func (abp *attrPolicyBuilder) Matching(regex *regexp.Regexp) *attrPolicyBuilder {
    331 
    332 	abp.regexp = regex
    333 
    334 	return abp
    335 }
    336 
    337 // OnElements will bind an attribute policy to a given range of HTML elements
    338 // and return the updated policy
    339 func (abp *attrPolicyBuilder) OnElements(elements ...string) *Policy {
    340 
    341 	for _, element := range elements {
    342 		element = strings.ToLower(element)
    343 
    344 		for _, attr := range abp.attrNames {
    345 
    346 			if _, ok := abp.p.elsAndAttrs[element]; !ok {
    347 				abp.p.elsAndAttrs[element] = make(map[string][]attrPolicy)
    348 			}
    349 
    350 			ap := attrPolicy{}
    351 			if abp.regexp != nil {
    352 				ap.regexp = abp.regexp
    353 			}
    354 
    355 			abp.p.elsAndAttrs[element][attr] = append(abp.p.elsAndAttrs[element][attr], ap)
    356 		}
    357 
    358 		if abp.allowEmpty {
    359 			abp.p.setOfElementsAllowedWithoutAttrs[element] = struct{}{}
    360 
    361 			if _, ok := abp.p.elsAndAttrs[element]; !ok {
    362 				abp.p.elsAndAttrs[element] = make(map[string][]attrPolicy)
    363 			}
    364 		}
    365 	}
    366 
    367 	return abp.p
    368 }
    369 
    370 // OnElementsMatching will bind an attribute policy to all elements matching a given regex
    371 // and return the updated policy
    372 func (abp *attrPolicyBuilder) OnElementsMatching(regex *regexp.Regexp) *Policy {
    373 	for _, attr := range abp.attrNames {
    374 		if _, ok := abp.p.elsMatchingAndAttrs[regex]; !ok {
    375 			abp.p.elsMatchingAndAttrs[regex] = make(map[string][]attrPolicy)
    376 		}
    377 		ap := attrPolicy{}
    378 		if abp.regexp != nil {
    379 			ap.regexp = abp.regexp
    380 		}
    381 		abp.p.elsMatchingAndAttrs[regex][attr] = append(abp.p.elsMatchingAndAttrs[regex][attr], ap)
    382 	}
    383 
    384 	if abp.allowEmpty {
    385 		abp.p.setOfElementsMatchingAllowedWithoutAttrs = append(abp.p.setOfElementsMatchingAllowedWithoutAttrs, regex)
    386 		if _, ok := abp.p.elsMatchingAndAttrs[regex]; !ok {
    387 			abp.p.elsMatchingAndAttrs[regex] = make(map[string][]attrPolicy)
    388 		}
    389 	}
    390 
    391 	return abp.p
    392 }
    393 
    394 // Globally will bind an attribute policy to all HTML elements and return the
    395 // updated policy
    396 func (abp *attrPolicyBuilder) Globally() *Policy {
    397 
    398 	for _, attr := range abp.attrNames {
    399 		if _, ok := abp.p.globalAttrs[attr]; !ok {
    400 			abp.p.globalAttrs[attr] = []attrPolicy{}
    401 		}
    402 
    403 		ap := attrPolicy{}
    404 		if abp.regexp != nil {
    405 			ap.regexp = abp.regexp
    406 		}
    407 
    408 		abp.p.globalAttrs[attr] = append(abp.p.globalAttrs[attr], ap)
    409 	}
    410 
    411 	return abp.p
    412 }
    413 
    414 // AllowStyles takes a range of CSS property names and returns a
    415 // style policy builder that allows you to specify the pattern and scope of
    416 // the allowed property.
    417 //
    418 // The style policy is only added to the core policy when either Globally()
    419 // or OnElements(...) are called.
    420 func (p *Policy) AllowStyles(propertyNames ...string) *stylePolicyBuilder {
    421 
    422 	p.init()
    423 
    424 	abp := stylePolicyBuilder{
    425 		p: p,
    426 	}
    427 
    428 	for _, propertyName := range propertyNames {
    429 		abp.propertyNames = append(abp.propertyNames, strings.ToLower(propertyName))
    430 	}
    431 
    432 	return &abp
    433 }
    434 
    435 // Matching allows a regular expression to be applied to a nascent style
    436 // policy, and returns the style policy.
    437 func (spb *stylePolicyBuilder) Matching(regex *regexp.Regexp) *stylePolicyBuilder {
    438 
    439 	spb.regexp = regex
    440 
    441 	return spb
    442 }
    443 
    444 // MatchingEnum allows a list of allowed values to be applied to a nascent style
    445 // policy, and returns the style policy.
    446 func (spb *stylePolicyBuilder) MatchingEnum(enum ...string) *stylePolicyBuilder {
    447 
    448 	spb.enum = enum
    449 
    450 	return spb
    451 }
    452 
    453 // MatchingHandler allows a handler to be applied to a nascent style
    454 // policy, and returns the style policy.
    455 func (spb *stylePolicyBuilder) MatchingHandler(handler func(string) bool) *stylePolicyBuilder {
    456 
    457 	spb.handler = handler
    458 
    459 	return spb
    460 }
    461 
    462 // OnElements will bind a style policy to a given range of HTML elements
    463 // and return the updated policy
    464 func (spb *stylePolicyBuilder) OnElements(elements ...string) *Policy {
    465 
    466 	for _, element := range elements {
    467 		element = strings.ToLower(element)
    468 
    469 		for _, attr := range spb.propertyNames {
    470 
    471 			if _, ok := spb.p.elsAndStyles[element]; !ok {
    472 				spb.p.elsAndStyles[element] = make(map[string][]stylePolicy)
    473 			}
    474 
    475 			sp := stylePolicy{}
    476 			if spb.handler != nil {
    477 				sp.handler = spb.handler
    478 			} else if len(spb.enum) > 0 {
    479 				sp.enum = spb.enum
    480 			} else if spb.regexp != nil {
    481 				sp.regexp = spb.regexp
    482 			} else {
    483 				sp.handler = css.GetDefaultHandler(attr)
    484 			}
    485 			spb.p.elsAndStyles[element][attr] = append(spb.p.elsAndStyles[element][attr], sp)
    486 		}
    487 	}
    488 
    489 	return spb.p
    490 }
    491 
    492 // OnElementsMatching will bind a style policy to any HTML elements matching the pattern
    493 // and return the updated policy
    494 func (spb *stylePolicyBuilder) OnElementsMatching(regex *regexp.Regexp) *Policy {
    495 
    496 	for _, attr := range spb.propertyNames {
    497 
    498 		if _, ok := spb.p.elsMatchingAndStyles[regex]; !ok {
    499 			spb.p.elsMatchingAndStyles[regex] = make(map[string][]stylePolicy)
    500 		}
    501 
    502 		sp := stylePolicy{}
    503 		if spb.handler != nil {
    504 			sp.handler = spb.handler
    505 		} else if len(spb.enum) > 0 {
    506 			sp.enum = spb.enum
    507 		} else if spb.regexp != nil {
    508 			sp.regexp = spb.regexp
    509 		} else {
    510 			sp.handler = css.GetDefaultHandler(attr)
    511 		}
    512 		spb.p.elsMatchingAndStyles[regex][attr] = append(spb.p.elsMatchingAndStyles[regex][attr], sp)
    513 	}
    514 
    515 	return spb.p
    516 }
    517 
    518 // Globally will bind a style policy to all HTML elements and return the
    519 // updated policy
    520 func (spb *stylePolicyBuilder) Globally() *Policy {
    521 
    522 	for _, attr := range spb.propertyNames {
    523 		if _, ok := spb.p.globalStyles[attr]; !ok {
    524 			spb.p.globalStyles[attr] = []stylePolicy{}
    525 		}
    526 
    527 		// Use only one strategy for validating styles, fallback to default
    528 		sp := stylePolicy{}
    529 		if spb.handler != nil {
    530 			sp.handler = spb.handler
    531 		} else if len(spb.enum) > 0 {
    532 			sp.enum = spb.enum
    533 		} else if spb.regexp != nil {
    534 			sp.regexp = spb.regexp
    535 		} else {
    536 			sp.handler = css.GetDefaultHandler(attr)
    537 		}
    538 		spb.p.globalStyles[attr] = append(spb.p.globalStyles[attr], sp)
    539 	}
    540 
    541 	return spb.p
    542 }
    543 
    544 // AllowElements will append HTML elements to the allowlist without applying an
    545 // attribute policy to those elements (the elements are permitted
    546 // sans-attributes)
    547 func (p *Policy) AllowElements(names ...string) *Policy {
    548 	p.init()
    549 
    550 	for _, element := range names {
    551 		element = strings.ToLower(element)
    552 
    553 		if _, ok := p.elsAndAttrs[element]; !ok {
    554 			p.elsAndAttrs[element] = make(map[string][]attrPolicy)
    555 		}
    556 	}
    557 
    558 	return p
    559 }
    560 
    561 // AllowElementsMatching will append HTML elements to the allowlist if they
    562 // match a regexp.
    563 func (p *Policy) AllowElementsMatching(regex *regexp.Regexp) *Policy {
    564 	p.init()
    565 	if _, ok := p.elsMatchingAndAttrs[regex]; !ok {
    566 		p.elsMatchingAndAttrs[regex] = make(map[string][]attrPolicy)
    567 	}
    568 	return p
    569 }
    570 
    571 // AllowURLSchemesMatching will append URL schemes to the allowlist if they
    572 // match a regexp.
    573 func (p *Policy) AllowURLSchemesMatching(r *regexp.Regexp) *Policy {
    574 	p.allowURLSchemeRegexps = append(p.allowURLSchemeRegexps, r)
    575 	return p
    576 }
    577 
    578 // RequireNoFollowOnLinks will result in all a, area, link tags having a
    579 // rel="nofollow"added to them if one does not already exist
    580 //
    581 // Note: This requires p.RequireParseableURLs(true) and will enable it.
    582 func (p *Policy) RequireNoFollowOnLinks(require bool) *Policy {
    583 
    584 	p.requireNoFollow = require
    585 	p.requireParseableURLs = true
    586 
    587 	return p
    588 }
    589 
    590 // RequireNoFollowOnFullyQualifiedLinks will result in all a, area, and link
    591 // tags that point to a non-local destination (i.e. starts with a protocol and
    592 // has a host) having a rel="nofollow" added to them if one does not already
    593 // exist
    594 //
    595 // Note: This requires p.RequireParseableURLs(true) and will enable it.
    596 func (p *Policy) RequireNoFollowOnFullyQualifiedLinks(require bool) *Policy {
    597 
    598 	p.requireNoFollowFullyQualifiedLinks = require
    599 	p.requireParseableURLs = true
    600 
    601 	return p
    602 }
    603 
    604 // RequireNoReferrerOnLinks will result in all a, area, and link tags having a
    605 // rel="noreferrrer" added to them if one does not already exist
    606 //
    607 // Note: This requires p.RequireParseableURLs(true) and will enable it.
    608 func (p *Policy) RequireNoReferrerOnLinks(require bool) *Policy {
    609 
    610 	p.requireNoReferrer = require
    611 	p.requireParseableURLs = true
    612 
    613 	return p
    614 }
    615 
    616 // RequireNoReferrerOnFullyQualifiedLinks will result in all a, area, and link
    617 // tags that point to a non-local destination (i.e. starts with a protocol and
    618 // has a host) having a rel="noreferrer" added to them if one does not already
    619 // exist
    620 //
    621 // Note: This requires p.RequireParseableURLs(true) and will enable it.
    622 func (p *Policy) RequireNoReferrerOnFullyQualifiedLinks(require bool) *Policy {
    623 
    624 	p.requireNoReferrerFullyQualifiedLinks = require
    625 	p.requireParseableURLs = true
    626 
    627 	return p
    628 }
    629 
    630 // RequireCrossOriginAnonymous will result in all audio, img, link, script, and
    631 // video tags having a crossorigin="anonymous" added to them if one does not
    632 // already exist
    633 func (p *Policy) RequireCrossOriginAnonymous(require bool) *Policy {
    634 
    635 	p.requireCrossOriginAnonymous = require
    636 
    637 	return p
    638 }
    639 
    640 // AddTargetBlankToFullyQualifiedLinks will result in all a, area and link tags
    641 // that point to a non-local destination (i.e. starts with a protocol and has a
    642 // host) having a target="_blank" added to them if one does not already exist
    643 //
    644 // Note: This requires p.RequireParseableURLs(true) and will enable it.
    645 func (p *Policy) AddTargetBlankToFullyQualifiedLinks(require bool) *Policy {
    646 
    647 	p.addTargetBlankToFullyQualifiedLinks = require
    648 	p.requireParseableURLs = true
    649 
    650 	return p
    651 }
    652 
    653 // RequireParseableURLs will result in all URLs requiring that they be parseable
    654 // by "net/url" url.Parse()
    655 // This applies to:
    656 // - a.href
    657 // - area.href
    658 // - blockquote.cite
    659 // - img.src
    660 // - link.href
    661 // - script.src
    662 func (p *Policy) RequireParseableURLs(require bool) *Policy {
    663 
    664 	p.requireParseableURLs = require
    665 
    666 	return p
    667 }
    668 
    669 // AllowRelativeURLs enables RequireParseableURLs and then permits URLs that
    670 // are parseable, have no schema information and url.IsAbs() returns false
    671 // This permits local URLs
    672 func (p *Policy) AllowRelativeURLs(require bool) *Policy {
    673 
    674 	p.RequireParseableURLs(true)
    675 	p.allowRelativeURLs = require
    676 
    677 	return p
    678 }
    679 
    680 // AllowURLSchemes will append URL schemes to the allowlist
    681 // Example: p.AllowURLSchemes("mailto", "http", "https")
    682 func (p *Policy) AllowURLSchemes(schemes ...string) *Policy {
    683 	p.init()
    684 
    685 	p.RequireParseableURLs(true)
    686 
    687 	for _, scheme := range schemes {
    688 		scheme = strings.ToLower(scheme)
    689 
    690 		// Allow all URLs with matching scheme.
    691 		p.allowURLSchemes[scheme] = nil
    692 	}
    693 
    694 	return p
    695 }
    696 
    697 // AllowURLSchemeWithCustomPolicy will append URL schemes with
    698 // a custom URL policy to the allowlist.
    699 // Only the URLs with matching schema and urlPolicy(url)
    700 // returning true will be allowed.
    701 func (p *Policy) AllowURLSchemeWithCustomPolicy(
    702 	scheme string,
    703 	urlPolicy func(url *url.URL) (allowUrl bool),
    704 ) *Policy {
    705 
    706 	p.init()
    707 
    708 	p.RequireParseableURLs(true)
    709 
    710 	scheme = strings.ToLower(scheme)
    711 
    712 	p.allowURLSchemes[scheme] = append(p.allowURLSchemes[scheme], urlPolicy)
    713 
    714 	return p
    715 }
    716 
    717 // RequireSandboxOnIFrame will result in all iframe tags having a sandbox="" tag
    718 // Any sandbox values not specified here will be filtered from the generated HTML
    719 func (p *Policy) RequireSandboxOnIFrame(vals ...SandboxValue) {
    720 	p.requireSandboxOnIFrame = make(map[string]bool)
    721 
    722 	for _, val := range vals {
    723 		switch SandboxValue(val) {
    724 		case SandboxAllowDownloads:
    725 			p.requireSandboxOnIFrame["allow-downloads"] = true
    726 
    727 		case SandboxAllowDownloadsWithoutUserActivation:
    728 			p.requireSandboxOnIFrame["allow-downloads-without-user-activation"] = true
    729 
    730 		case SandboxAllowForms:
    731 			p.requireSandboxOnIFrame["allow-forms"] = true
    732 
    733 		case SandboxAllowModals:
    734 			p.requireSandboxOnIFrame["allow-modals"] = true
    735 
    736 		case SandboxAllowOrientationLock:
    737 			p.requireSandboxOnIFrame["allow-orientation-lock"] = true
    738 
    739 		case SandboxAllowPointerLock:
    740 			p.requireSandboxOnIFrame["allow-pointer-lock"] = true
    741 
    742 		case SandboxAllowPopups:
    743 			p.requireSandboxOnIFrame["allow-popups"] = true
    744 
    745 		case SandboxAllowPopupsToEscapeSandbox:
    746 			p.requireSandboxOnIFrame["allow-popups-to-escape-sandbox"] = true
    747 
    748 		case SandboxAllowPresentation:
    749 			p.requireSandboxOnIFrame["allow-presentation"] = true
    750 
    751 		case SandboxAllowSameOrigin:
    752 			p.requireSandboxOnIFrame["allow-same-origin"] = true
    753 
    754 		case SandboxAllowScripts:
    755 			p.requireSandboxOnIFrame["allow-scripts"] = true
    756 
    757 		case SandboxAllowStorageAccessByUserActivation:
    758 			p.requireSandboxOnIFrame["allow-storage-access-by-user-activation"] = true
    759 
    760 		case SandboxAllowTopNavigation:
    761 			p.requireSandboxOnIFrame["allow-top-navigation"] = true
    762 
    763 		case SandboxAllowTopNavigationByUserActivation:
    764 			p.requireSandboxOnIFrame["allow-top-navigation-by-user-activation"] = true
    765 		}
    766 	}
    767 }
    768 
    769 // AddSpaceWhenStrippingTag states whether to add a single space " " when
    770 // removing tags that are not allowed by the policy.
    771 //
    772 // This is useful if you expect to strip tags in dense markup and may lose the
    773 // value of whitespace.
    774 //
    775 // For example: "<p>Hello</p><p>World</p>"" would be sanitized to "HelloWorld"
    776 // with the default value of false, but you may wish to sanitize this to
    777 // " Hello  World " by setting AddSpaceWhenStrippingTag to true as this would
    778 // retain the intent of the text.
    779 func (p *Policy) AddSpaceWhenStrippingTag(allow bool) *Policy {
    780 
    781 	p.addSpaces = allow
    782 
    783 	return p
    784 }
    785 
    786 // SkipElementsContent adds the HTML elements whose tags is needed to be removed
    787 // with its content.
    788 func (p *Policy) SkipElementsContent(names ...string) *Policy {
    789 
    790 	p.init()
    791 
    792 	for _, element := range names {
    793 		element = strings.ToLower(element)
    794 
    795 		if _, ok := p.setOfElementsToSkipContent[element]; !ok {
    796 			p.setOfElementsToSkipContent[element] = struct{}{}
    797 		}
    798 	}
    799 
    800 	return p
    801 }
    802 
    803 // AllowElementsContent marks the HTML elements whose content should be
    804 // retained after removing the tag.
    805 func (p *Policy) AllowElementsContent(names ...string) *Policy {
    806 
    807 	p.init()
    808 
    809 	for _, element := range names {
    810 		delete(p.setOfElementsToSkipContent, strings.ToLower(element))
    811 	}
    812 
    813 	return p
    814 }
    815 
    816 // AllowUnsafe permits fundamentally unsafe elements.
    817 //
    818 // If false (default) then elements such as `style` and `script` will not be
    819 // permitted even if declared in a policy. These elements when combined with
    820 // untrusted input cannot be safely handled by bluemonday at this point in
    821 // time.
    822 //
    823 // If true then `style` and `script` would be permitted by bluemonday if a
    824 // policy declares them. However this is not recommended under any circumstance
    825 // and can lead to XSS being rendered thus defeating the purpose of using a
    826 // HTML sanitizer.
    827 func (p *Policy) AllowUnsafe(allowUnsafe bool) *Policy {
    828 	p.init()
    829 	p.allowUnsafe = allowUnsafe
    830 	return p
    831 }
    832 
    833 // addDefaultElementsWithoutAttrs adds the HTML elements that we know are valid
    834 // without any attributes to an internal map.
    835 // i.e. we know that <table> is valid, but <bdo> isn't valid as the "dir" attr
    836 // is mandatory
    837 func (p *Policy) addDefaultElementsWithoutAttrs() {
    838 	p.init()
    839 
    840 	p.setOfElementsAllowedWithoutAttrs["abbr"] = struct{}{}
    841 	p.setOfElementsAllowedWithoutAttrs["acronym"] = struct{}{}
    842 	p.setOfElementsAllowedWithoutAttrs["address"] = struct{}{}
    843 	p.setOfElementsAllowedWithoutAttrs["article"] = struct{}{}
    844 	p.setOfElementsAllowedWithoutAttrs["aside"] = struct{}{}
    845 	p.setOfElementsAllowedWithoutAttrs["audio"] = struct{}{}
    846 	p.setOfElementsAllowedWithoutAttrs["b"] = struct{}{}
    847 	p.setOfElementsAllowedWithoutAttrs["bdi"] = struct{}{}
    848 	p.setOfElementsAllowedWithoutAttrs["blockquote"] = struct{}{}
    849 	p.setOfElementsAllowedWithoutAttrs["body"] = struct{}{}
    850 	p.setOfElementsAllowedWithoutAttrs["br"] = struct{}{}
    851 	p.setOfElementsAllowedWithoutAttrs["button"] = struct{}{}
    852 	p.setOfElementsAllowedWithoutAttrs["canvas"] = struct{}{}
    853 	p.setOfElementsAllowedWithoutAttrs["caption"] = struct{}{}
    854 	p.setOfElementsAllowedWithoutAttrs["center"] = struct{}{}
    855 	p.setOfElementsAllowedWithoutAttrs["cite"] = struct{}{}
    856 	p.setOfElementsAllowedWithoutAttrs["code"] = struct{}{}
    857 	p.setOfElementsAllowedWithoutAttrs["col"] = struct{}{}
    858 	p.setOfElementsAllowedWithoutAttrs["colgroup"] = struct{}{}
    859 	p.setOfElementsAllowedWithoutAttrs["datalist"] = struct{}{}
    860 	p.setOfElementsAllowedWithoutAttrs["dd"] = struct{}{}
    861 	p.setOfElementsAllowedWithoutAttrs["del"] = struct{}{}
    862 	p.setOfElementsAllowedWithoutAttrs["details"] = struct{}{}
    863 	p.setOfElementsAllowedWithoutAttrs["dfn"] = struct{}{}
    864 	p.setOfElementsAllowedWithoutAttrs["div"] = struct{}{}
    865 	p.setOfElementsAllowedWithoutAttrs["dl"] = struct{}{}
    866 	p.setOfElementsAllowedWithoutAttrs["dt"] = struct{}{}
    867 	p.setOfElementsAllowedWithoutAttrs["em"] = struct{}{}
    868 	p.setOfElementsAllowedWithoutAttrs["fieldset"] = struct{}{}
    869 	p.setOfElementsAllowedWithoutAttrs["figcaption"] = struct{}{}
    870 	p.setOfElementsAllowedWithoutAttrs["figure"] = struct{}{}
    871 	p.setOfElementsAllowedWithoutAttrs["footer"] = struct{}{}
    872 	p.setOfElementsAllowedWithoutAttrs["h1"] = struct{}{}
    873 	p.setOfElementsAllowedWithoutAttrs["h2"] = struct{}{}
    874 	p.setOfElementsAllowedWithoutAttrs["h3"] = struct{}{}
    875 	p.setOfElementsAllowedWithoutAttrs["h4"] = struct{}{}
    876 	p.setOfElementsAllowedWithoutAttrs["h5"] = struct{}{}
    877 	p.setOfElementsAllowedWithoutAttrs["h6"] = struct{}{}
    878 	p.setOfElementsAllowedWithoutAttrs["head"] = struct{}{}
    879 	p.setOfElementsAllowedWithoutAttrs["header"] = struct{}{}
    880 	p.setOfElementsAllowedWithoutAttrs["hgroup"] = struct{}{}
    881 	p.setOfElementsAllowedWithoutAttrs["hr"] = struct{}{}
    882 	p.setOfElementsAllowedWithoutAttrs["html"] = struct{}{}
    883 	p.setOfElementsAllowedWithoutAttrs["i"] = struct{}{}
    884 	p.setOfElementsAllowedWithoutAttrs["ins"] = struct{}{}
    885 	p.setOfElementsAllowedWithoutAttrs["kbd"] = struct{}{}
    886 	p.setOfElementsAllowedWithoutAttrs["li"] = struct{}{}
    887 	p.setOfElementsAllowedWithoutAttrs["mark"] = struct{}{}
    888 	p.setOfElementsAllowedWithoutAttrs["marquee"] = struct{}{}
    889 	p.setOfElementsAllowedWithoutAttrs["nav"] = struct{}{}
    890 	p.setOfElementsAllowedWithoutAttrs["ol"] = struct{}{}
    891 	p.setOfElementsAllowedWithoutAttrs["optgroup"] = struct{}{}
    892 	p.setOfElementsAllowedWithoutAttrs["option"] = struct{}{}
    893 	p.setOfElementsAllowedWithoutAttrs["p"] = struct{}{}
    894 	p.setOfElementsAllowedWithoutAttrs["picture"] = struct{}{}
    895 	p.setOfElementsAllowedWithoutAttrs["pre"] = struct{}{}
    896 	p.setOfElementsAllowedWithoutAttrs["q"] = struct{}{}
    897 	p.setOfElementsAllowedWithoutAttrs["rp"] = struct{}{}
    898 	p.setOfElementsAllowedWithoutAttrs["rt"] = struct{}{}
    899 	p.setOfElementsAllowedWithoutAttrs["ruby"] = struct{}{}
    900 	p.setOfElementsAllowedWithoutAttrs["s"] = struct{}{}
    901 	p.setOfElementsAllowedWithoutAttrs["samp"] = struct{}{}
    902 	p.setOfElementsAllowedWithoutAttrs["script"] = struct{}{}
    903 	p.setOfElementsAllowedWithoutAttrs["section"] = struct{}{}
    904 	p.setOfElementsAllowedWithoutAttrs["select"] = struct{}{}
    905 	p.setOfElementsAllowedWithoutAttrs["small"] = struct{}{}
    906 	p.setOfElementsAllowedWithoutAttrs["span"] = struct{}{}
    907 	p.setOfElementsAllowedWithoutAttrs["strike"] = struct{}{}
    908 	p.setOfElementsAllowedWithoutAttrs["strong"] = struct{}{}
    909 	p.setOfElementsAllowedWithoutAttrs["style"] = struct{}{}
    910 	p.setOfElementsAllowedWithoutAttrs["sub"] = struct{}{}
    911 	p.setOfElementsAllowedWithoutAttrs["summary"] = struct{}{}
    912 	p.setOfElementsAllowedWithoutAttrs["sup"] = struct{}{}
    913 	p.setOfElementsAllowedWithoutAttrs["svg"] = struct{}{}
    914 	p.setOfElementsAllowedWithoutAttrs["table"] = struct{}{}
    915 	p.setOfElementsAllowedWithoutAttrs["tbody"] = struct{}{}
    916 	p.setOfElementsAllowedWithoutAttrs["td"] = struct{}{}
    917 	p.setOfElementsAllowedWithoutAttrs["textarea"] = struct{}{}
    918 	p.setOfElementsAllowedWithoutAttrs["tfoot"] = struct{}{}
    919 	p.setOfElementsAllowedWithoutAttrs["th"] = struct{}{}
    920 	p.setOfElementsAllowedWithoutAttrs["thead"] = struct{}{}
    921 	p.setOfElementsAllowedWithoutAttrs["title"] = struct{}{}
    922 	p.setOfElementsAllowedWithoutAttrs["time"] = struct{}{}
    923 	p.setOfElementsAllowedWithoutAttrs["tr"] = struct{}{}
    924 	p.setOfElementsAllowedWithoutAttrs["tt"] = struct{}{}
    925 	p.setOfElementsAllowedWithoutAttrs["u"] = struct{}{}
    926 	p.setOfElementsAllowedWithoutAttrs["ul"] = struct{}{}
    927 	p.setOfElementsAllowedWithoutAttrs["var"] = struct{}{}
    928 	p.setOfElementsAllowedWithoutAttrs["video"] = struct{}{}
    929 	p.setOfElementsAllowedWithoutAttrs["wbr"] = struct{}{}
    930 
    931 }
    932 
    933 // addDefaultSkipElementContent adds the HTML elements that we should skip
    934 // rendering the character content of, if the element itself is not allowed.
    935 // This is all character data that the end user would not normally see.
    936 // i.e. if we exclude a <script> tag then we shouldn't render the JavaScript or
    937 // anything else until we encounter the closing </script> tag.
    938 func (p *Policy) addDefaultSkipElementContent() {
    939 	p.init()
    940 
    941 	p.setOfElementsToSkipContent["frame"] = struct{}{}
    942 	p.setOfElementsToSkipContent["frameset"] = struct{}{}
    943 	p.setOfElementsToSkipContent["iframe"] = struct{}{}
    944 	p.setOfElementsToSkipContent["noembed"] = struct{}{}
    945 	p.setOfElementsToSkipContent["noframes"] = struct{}{}
    946 	p.setOfElementsToSkipContent["noscript"] = struct{}{}
    947 	p.setOfElementsToSkipContent["nostyle"] = struct{}{}
    948 	p.setOfElementsToSkipContent["object"] = struct{}{}
    949 	p.setOfElementsToSkipContent["script"] = struct{}{}
    950 	p.setOfElementsToSkipContent["style"] = struct{}{}
    951 	p.setOfElementsToSkipContent["title"] = struct{}{}
    952 }