diff options
Diffstat (limited to 'vendor/github.com/microcosm-cc/bluemonday/policy.go')
| -rw-r--r-- | vendor/github.com/microcosm-cc/bluemonday/policy.go | 952 |
1 files changed, 952 insertions, 0 deletions
diff --git a/vendor/github.com/microcosm-cc/bluemonday/policy.go b/vendor/github.com/microcosm-cc/bluemonday/policy.go new file mode 100644 index 0000000..995f46c --- /dev/null +++ b/vendor/github.com/microcosm-cc/bluemonday/policy.go | |||
| @@ -0,0 +1,952 @@ | |||
| 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 | } | ||
