mirror of
				https://gitea.com/gitea/tea.git
				synced 2025-10-31 09:15:26 +01:00 
			
		
		
		
	Update Vendors (#250)
update go min version Update Vendors: * code.gitea.io/gitea-vet v0.2.0 -> v0.2.1 * code.gitea.io/sdk/gitea v0.13.0 -> v0.13.1 * github.com/AlecAivazis/survey v2.1.1 -> v2.2.2 * github.com/adrg/xdg v0.2.1 -> v0.2.2 * github.com/araddon/dateparse d820a6159ab1 -> 8aadafed4dc4 * github.com/go-git/go-git v5.1.0 -> v5.2.0 * github.com/muesli/termenv v0.7.2 -> v0.7.4 * github.com/stretchr/testify v1.5.1 -> v1.6.1 * github.com/urfave/cli v2.2.0 -> v2.3.0 Co-authored-by: 6543 <6543@obermui.de> Reviewed-on: https://gitea.com/gitea/tea/pulls/250 Reviewed-by: Andrew Thornton <art27@cantab.net> Reviewed-by: mrsdizzie <info@mrsdizzie.com> Co-Authored-By: 6543 <6543@noreply.gitea.io> Co-Committed-By: 6543 <6543@noreply.gitea.io>
This commit is contained in:
		
							
								
								
									
										15
									
								
								vendor/github.com/microcosm-cc/bluemonday/.gitignore
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								vendor/github.com/microcosm-cc/bluemonday/.gitignore
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,15 @@ | ||||
|  # Binaries for programs and plugins | ||||
| *.exe | ||||
| *.exe~ | ||||
| *.dll | ||||
| *.so | ||||
| *.dylib | ||||
|  | ||||
| # Test binary, built with `go test -c` | ||||
| *.test | ||||
|  | ||||
| # Output of the go coverage tool, specifically when used with LiteIDE | ||||
| *.out | ||||
|  | ||||
| # goland idea folder | ||||
| *.idea | ||||
							
								
								
									
										2
									
								
								vendor/github.com/microcosm-cc/bluemonday/.travis.yml
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								vendor/github.com/microcosm-cc/bluemonday/.travis.yml
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -1,6 +1,5 @@ | ||||
| language: go | ||||
| go: | ||||
|   - 1.1.x | ||||
|   - 1.2.x | ||||
|   - 1.3.x | ||||
|   - 1.4.x | ||||
| @@ -11,6 +10,7 @@ go: | ||||
|   - 1.9.x | ||||
|   - 1.10.x | ||||
|   - 1.11.x | ||||
|   - 1.12.x | ||||
|   - tip | ||||
| matrix: | ||||
|   allow_failures: | ||||
|   | ||||
							
								
								
									
										7
									
								
								vendor/github.com/microcosm-cc/bluemonday/CREDITS.md
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										7
									
								
								vendor/github.com/microcosm-cc/bluemonday/CREDITS.md
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -1,6 +1,7 @@ | ||||
| 1. Andrew Krasichkov @buglloc https://github.com/buglloc | ||||
| 1. John Graham-Cumming http://jgc.org/ | ||||
| 1. Mohammad Gufran https://github.com/Gufran | ||||
| 1. Steven Gutzwiller https://github.com/StevenGutzwiller | ||||
| 1. Andrew Krasichkov @buglloc https://github.com/buglloc | ||||
| 1. Mike Samuel mikesamuel@gmail.com | ||||
| 1. Dmitri Shuralyov shurcooL@gmail.com | ||||
| 1. https://github.com/opennota | ||||
| 1. https://github.com/Gufran | ||||
| 1. https://github.com/opennota | ||||
							
								
								
									
										71
									
								
								vendor/github.com/microcosm-cc/bluemonday/README.md
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										71
									
								
								vendor/github.com/microcosm-cc/bluemonday/README.md
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -58,10 +58,12 @@ We expect to be supplied with well-formatted HTML (closing elements for every ap | ||||
|  | ||||
| ### Supported Go Versions | ||||
|  | ||||
| bluemonday is tested against Go 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, and tip. | ||||
| bluemonday is tested against Go 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 1.10, 1.11, 1.12, and tip. | ||||
|  | ||||
| We do not support Go 1.0 as we depend on `golang.org/x/net/html` which includes a reference to `io.ErrNoProgress` which did not exist in Go 1.0. | ||||
|  | ||||
| We support Go 1.1 but Travis no longer tests against it. | ||||
|  | ||||
| ## Is it production ready? | ||||
|  | ||||
| *Yes* | ||||
| @@ -90,7 +92,7 @@ func main() { | ||||
| 	// Do this once for each unique policy, and use the policy for the life of the program | ||||
| 	// Policy creation/editing is not safe to use in multiple goroutines | ||||
| 	p := bluemonday.UGCPolicy() | ||||
| 	 | ||||
|  | ||||
| 	// The policy can then be used to sanitize lots of input and it is safe to use the policy in multiple goroutines | ||||
| 	html := p.Sanitize( | ||||
| 		`<a onblur="alert(secret)" href="http://www.google.com">Google</a>`, | ||||
| @@ -167,12 +169,26 @@ To add elements to a policy either add just the elements: | ||||
| p.AllowElements("b", "strong") | ||||
| ``` | ||||
|  | ||||
| Or using a regex: | ||||
|  | ||||
| _Note: if an element is added by name as shown above, any matching regex will be ignored_ | ||||
|  | ||||
| It is also recommended to ensure multiple patterns don't overlap as order of execution is not guaranteed and can result in some rules being missed. | ||||
| ```go | ||||
| p.AllowElementsMatching(regex.MustCompile(`^my-element-`)) | ||||
| ``` | ||||
|  | ||||
| Or add elements as a virtue of adding an attribute: | ||||
| ```go | ||||
| // Not the recommended pattern, see the recommendation on using .Matching() below | ||||
| p.AllowAttrs("nowrap").OnElements("td", "th") | ||||
| ``` | ||||
|  | ||||
| Again, this also supports a regex pattern match alternative: | ||||
| ```go | ||||
| p.AllowAttrs("nowrap").OnElementsMatching(regex.MustCompile(`^my-element-`)) | ||||
| ``` | ||||
|  | ||||
| Attributes can either be added to all elements: | ||||
| ```go | ||||
| p.AllowAttrs("dir").Matching(regexp.MustCompile("(?i)rtl|ltr")).Globally() | ||||
| @@ -202,6 +218,49 @@ p := bluemonday.UGCPolicy() | ||||
| p.AllowElements("fieldset", "select", "option") | ||||
| ``` | ||||
|  | ||||
| ### Inline CSS | ||||
|  | ||||
| Although it's possible to handle inline CSS using `AllowAttrs` with a `Matching` rule, writing a single monolithic regular expression to safely process all inline CSS which you wish to allow is not a trivial task.  Instead of attempting to do so, you can whitelist the `style` attribute on whichever element(s) you desire and use style policies to control and sanitize inline styles. | ||||
|  | ||||
| It is suggested that you use `Matching` (with a suitable regular expression) | ||||
| `MatchingEnum`, or `MatchingHandler` to ensure each style matches your needs, | ||||
| but default handlers are supplied for most widely used styles. | ||||
|  | ||||
| Similar to attributes, you can allow specific CSS properties to be set inline: | ||||
| ```go | ||||
| p.AllowAttrs("style").OnElements("span", "p") | ||||
| // Allow the 'color' property with valid RGB(A) hex values only (on any element allowed a 'style' attribute) | ||||
| p.AllowStyles("color").Matching(regexp.MustCompile("(?i)^#([0-9a-f]{3,4}|[0-9a-f]{6}|[0-9a-f]{8})$")).Globally() | ||||
| ``` | ||||
|  | ||||
| Additionally, you can allow a CSS property to be set only to an allowed value: | ||||
| ```go | ||||
| p.AllowAttrs("style").OnElements("span", "p") | ||||
| // Allow the 'text-decoration' property to be set to 'underline', 'line-through' or 'none' | ||||
| // on 'span' elements only | ||||
| p.AllowStyles("text-decoration").MatchingEnum("underline", "line-through", "none").OnElements("span") | ||||
| ``` | ||||
|  | ||||
| Or you can specify elements based on a regex patterm match: | ||||
| ```go | ||||
| p.AllowAttrs("style").OnElementsMatching(regex.MustCompile(`^my-element-`)) | ||||
| // Allow the 'text-decoration' property to be set to 'underline', 'line-through' or 'none' | ||||
| // on 'span' elements only | ||||
| p.AllowStyles("text-decoration").MatchingEnum("underline", "line-through", "none").OnElementsMatching(regex.MustCompile(`^my-element-`)) | ||||
| ``` | ||||
|  | ||||
| If you need more specific checking, you can create a handler that takes in a string and returns a bool to | ||||
| validate the values for a given property. The string parameter has been | ||||
| converted to lowercase and unicode code points have been converted. | ||||
| ```go | ||||
| myHandler := func(value string) bool{ | ||||
| 	return true | ||||
| } | ||||
| p.AllowAttrs("style").OnElements("span", "p") | ||||
| // Allow the 'color' property with values validated by the handler (on any element allowed a 'style' attribute) | ||||
| p.AllowStyles("color").MatchingHandler(myHandler).Globally() | ||||
| ``` | ||||
|  | ||||
| ### Links | ||||
|  | ||||
| Links are difficult beasts to sanitise safely and also one of the biggest attack vectors for malicious content. | ||||
| @@ -236,6 +295,13 @@ Regardless of whether you have enabled parseable URLs, you can force all URLs to | ||||
| p.RequireNoFollowOnLinks(true) | ||||
| ``` | ||||
|  | ||||
| Similarly, you can force all URLs to have "noreferrer" in their rel attribute. | ||||
| ```go | ||||
| // This applies to "a" "area" "link" elements that have a "href" attribute | ||||
| p.RequireNoReferrerOnLinks(true) | ||||
| ``` | ||||
|  | ||||
|  | ||||
| We provide a convenience method that applies all of the above, but you will still need to whitelist the linkable elements for the URL rules to be applied to: | ||||
| ```go | ||||
| p.AllowStandardURLs() | ||||
| @@ -316,7 +382,6 @@ It is not the job of bluemonday to fix your bad HTML, it is merely the job of bl | ||||
|  | ||||
| ## TODO | ||||
|  | ||||
| * Add support for CSS sanitisation to allow some CSS properties based on a whitelist, possibly using the [Gorilla CSS3 scanner](http://www.gorillatoolkit.org/pkg/css/scanner) - PRs welcome so long as testing covers XSS and demonstrates safety first | ||||
| * Investigate whether devs want to blacklist elements and attributes. This would allow devs to take an existing policy (such as the `bluemonday.UGCPolicy()` ) that encapsulates 90% of what they're looking for but does more than they need, and to remove the extra things they do not want to make it 100% what they want | ||||
| * Investigate whether devs want a validating HTML mode, in which the HTML elements are not just transformed into a balanced tree (every start tag has a closing tag at the correct depth) but also that elements and character data appear only in their allowed context (i.e. that a `table` element isn't a descendent of a `caption`, that `colgroup`, `thead`, `tbody`, `tfoot` and `tr` are permitted, and that character data is not permitted) | ||||
|  | ||||
|   | ||||
							
								
								
									
										7
									
								
								vendor/github.com/microcosm-cc/bluemonday/go.mod
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										7
									
								
								vendor/github.com/microcosm-cc/bluemonday/go.mod
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -2,4 +2,9 @@ module github.com/microcosm-cc/bluemonday | ||||
|  | ||||
| go 1.9 | ||||
|  | ||||
| require golang.org/x/net v0.0.0-20181220203305-927f97764cc3 | ||||
| require ( | ||||
| 	github.com/aymerick/douceur v0.2.0 // indirect | ||||
| 	github.com/chris-ramon/douceur v0.2.0 | ||||
| 	github.com/gorilla/css v1.0.0 // indirect | ||||
| 	golang.org/x/net v0.0.0-20181220203305-927f97764cc3 | ||||
| ) | ||||
|   | ||||
							
								
								
									
										6
									
								
								vendor/github.com/microcosm-cc/bluemonday/go.sum
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								vendor/github.com/microcosm-cc/bluemonday/go.sum
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -1,2 +1,8 @@ | ||||
| github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk= | ||||
| github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4= | ||||
| github.com/chris-ramon/douceur v0.2.0 h1:IDMEdxlEUUBYBKE4z/mJnFyVXox+MjuEVDJNN27glkU= | ||||
| github.com/chris-ramon/douceur v0.2.0/go.mod h1:wDW5xjJdeoMm1mRt4sD4c/LbF/mWdEpRXQKjTR8nIBE= | ||||
| github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY= | ||||
| github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c= | ||||
| golang.org/x/net v0.0.0-20181220203305-927f97764cc3 h1:eH6Eip3UpmR+yM/qI9Ijluzb1bNv/cAU/n+6l8tRSis= | ||||
| golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= | ||||
|   | ||||
							
								
								
									
										2085
									
								
								vendor/github.com/microcosm-cc/bluemonday/handlers.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										2085
									
								
								vendor/github.com/microcosm-cc/bluemonday/handlers.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										2
									
								
								vendor/github.com/microcosm-cc/bluemonday/helpers.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								vendor/github.com/microcosm-cc/bluemonday/helpers.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -135,7 +135,7 @@ func (p *Policy) AllowStandardURLs() { | ||||
| 	// Most common URL schemes only | ||||
| 	p.AllowURLSchemes("mailto", "http", "https") | ||||
|  | ||||
| 	// For all anchors we will add rel="nofollow" if it does not already exist | ||||
| 	// For linking elements we will add rel="nofollow" if it does not already exist | ||||
| 	// This applies to "a" "area" "link" | ||||
| 	p.RequireNoFollowOnLinks(true) | ||||
| } | ||||
|   | ||||
							
								
								
									
										271
									
								
								vendor/github.com/microcosm-cc/bluemonday/policy.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										271
									
								
								vendor/github.com/microcosm-cc/bluemonday/policy.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -29,6 +29,8 @@ | ||||
|  | ||||
| package bluemonday | ||||
|  | ||||
| //TODO sgutzwiller create map of styles to default handlers | ||||
| //TODO sgutzwiller create handlers for various attributes | ||||
| import ( | ||||
| 	"net/url" | ||||
| 	"regexp" | ||||
| @@ -51,14 +53,22 @@ type Policy struct { | ||||
| 	// tag is replaced by a space character. | ||||
| 	addSpaces bool | ||||
|  | ||||
| 	// When true, add rel="nofollow" to HTML anchors | ||||
| 	// When true, add rel="nofollow" to HTML a, area, and link tags | ||||
| 	requireNoFollow bool | ||||
|  | ||||
| 	// When true, add rel="nofollow" to HTML anchors | ||||
| 	// When true, add rel="nofollow" to HTML a, area, and link tags | ||||
| 	// Will add for href="http://foo" | ||||
| 	// Will skip for href="/foo" or href="foo" | ||||
| 	requireNoFollowFullyQualifiedLinks bool | ||||
|  | ||||
| 	// When true, add rel="noreferrer" to HTML a, area, and link tags | ||||
| 	requireNoReferrer bool | ||||
|  | ||||
| 	// When true, add rel="noreferrer" to HTML a, area, and link tags | ||||
| 	// Will add for href="http://foo" | ||||
| 	// Will skip for href="/foo" or href="foo" | ||||
| 	requireNoReferrerFullyQualifiedLinks bool | ||||
|  | ||||
| 	// When true add target="_blank" to fully qualified links | ||||
| 	// Will add for href="http://foo" | ||||
| 	// Will skip for href="/foo" or href="foo" | ||||
| @@ -76,9 +86,21 @@ type Policy struct { | ||||
| 	// map[htmlElementName]map[htmlAttributeName]attrPolicy | ||||
| 	elsAndAttrs map[string]map[string]attrPolicy | ||||
|  | ||||
| 	// elsMatchingAndAttrs stores regex based element matches along with attributes | ||||
| 	elsMatchingAndAttrs map[*regexp.Regexp]map[string]attrPolicy | ||||
|  | ||||
| 	// map[htmlAttributeName]attrPolicy | ||||
| 	globalAttrs map[string]attrPolicy | ||||
|  | ||||
| 	// map[htmlElementName]map[cssPropertyName]stylePolicy | ||||
| 	elsAndStyles map[string]map[string]stylePolicy | ||||
|  | ||||
| 	// map[regex]map[cssPropertyName]stylePolicy | ||||
| 	elsMatchingAndStyles map[*regexp.Regexp]map[string]stylePolicy | ||||
|  | ||||
| 	// map[cssPropertyName]stylePolicy | ||||
| 	globalStyles map[string]stylePolicy | ||||
|  | ||||
| 	// 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. | ||||
| @@ -93,6 +115,16 @@ type Policy struct { | ||||
| 	// be maintained in the output HTML. | ||||
| 	setOfElementsAllowedWithoutAttrs map[string]struct{} | ||||
|  | ||||
| 	// 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>. | ||||
| 	// | ||||
| 	// In this case, any element matching a regular expression will be accepted without | ||||
| 	// attributes added. | ||||
| 	setOfElementsMatchingAllowedWithoutAttrs []*regexp.Regexp | ||||
|  | ||||
| 	setOfElementsToSkipContent map[string]struct{} | ||||
| } | ||||
|  | ||||
| @@ -103,6 +135,20 @@ type attrPolicy struct { | ||||
| 	regexp *regexp.Regexp | ||||
| } | ||||
|  | ||||
| type stylePolicy struct { | ||||
| 	// handler to validate | ||||
| 	handler func(string) bool | ||||
|  | ||||
| 	// optional pattern to match, when not nil the regexp needs to match | ||||
| 	// otherwise the property is removed | ||||
| 	regexp *regexp.Regexp | ||||
|  | ||||
| 	// optional list of allowed property values, for properties which | ||||
| 	// have a defined list of allowed values; property will be removed | ||||
| 	// if the value is not allowed | ||||
| 	enum []string | ||||
| } | ||||
|  | ||||
| type attrPolicyBuilder struct { | ||||
| 	p *Policy | ||||
|  | ||||
| @@ -111,13 +157,26 @@ type attrPolicyBuilder struct { | ||||
| 	allowEmpty bool | ||||
| } | ||||
|  | ||||
| type stylePolicyBuilder struct { | ||||
| 	p *Policy | ||||
|  | ||||
| 	propertyNames []string | ||||
| 	regexp        *regexp.Regexp | ||||
| 	enum          []string | ||||
| 	handler       func(string) bool | ||||
| } | ||||
|  | ||||
| type urlPolicy func(url *url.URL) (allowUrl bool) | ||||
|  | ||||
| // init initializes the maps if this has not been done already | ||||
| func (p *Policy) init() { | ||||
| 	if !p.initialized { | ||||
| 		p.elsAndAttrs = make(map[string]map[string]attrPolicy) | ||||
| 		p.elsMatchingAndAttrs = make(map[*regexp.Regexp]map[string]attrPolicy) | ||||
| 		p.globalAttrs = make(map[string]attrPolicy) | ||||
| 		p.elsAndStyles = make(map[string]map[string]stylePolicy) | ||||
| 		p.elsMatchingAndStyles = make(map[*regexp.Regexp]map[string]stylePolicy) | ||||
| 		p.globalStyles = make(map[string]stylePolicy) | ||||
| 		p.allowURLSchemes = make(map[string]urlPolicy) | ||||
| 		p.setOfElementsAllowedWithoutAttrs = make(map[string]struct{}) | ||||
| 		p.setOfElementsToSkipContent = make(map[string]struct{}) | ||||
| @@ -245,6 +304,30 @@ func (abp *attrPolicyBuilder) OnElements(elements ...string) *Policy { | ||||
| 	return abp.p | ||||
| } | ||||
|  | ||||
| // OnElementsMatching will bind an attribute policy to all elements matching a given regex | ||||
| // and return the updated policy | ||||
| func (abp *attrPolicyBuilder) OnElementsMatching(regex *regexp.Regexp) *Policy { | ||||
| 	for _, attr := range abp.attrNames { | ||||
| 		if _, ok := abp.p.elsMatchingAndAttrs[regex]; !ok { | ||||
| 			abp.p.elsMatchingAndAttrs[regex] = make(map[string]attrPolicy) | ||||
| 		} | ||||
| 		ap := attrPolicy{} | ||||
| 		if abp.regexp != nil { | ||||
| 			ap.regexp = abp.regexp | ||||
| 		} | ||||
| 		abp.p.elsMatchingAndAttrs[regex][attr] = ap | ||||
| 	} | ||||
|  | ||||
| 	if abp.allowEmpty { | ||||
| 		abp.p.setOfElementsMatchingAllowedWithoutAttrs = append(abp.p.setOfElementsMatchingAllowedWithoutAttrs, regex) | ||||
| 		if _, ok := abp.p.elsMatchingAndAttrs[regex]; !ok { | ||||
| 			abp.p.elsMatchingAndAttrs[regex] = make(map[string]attrPolicy) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return abp.p | ||||
| } | ||||
|  | ||||
| // Globally will bind an attribute policy to all HTML elements and return the | ||||
| // updated policy | ||||
| func (abp *attrPolicyBuilder) Globally() *Policy { | ||||
| @@ -265,6 +348,139 @@ func (abp *attrPolicyBuilder) Globally() *Policy { | ||||
| 	return abp.p | ||||
| } | ||||
|  | ||||
| // AllowStyles takes a range of CSS property names and returns a | ||||
| // style policy builder that allows you to specify the pattern and scope of | ||||
| // the whitelisted property. | ||||
| // | ||||
| // The style policy is only added to the core policy when either Globally() | ||||
| // or OnElements(...) are called. | ||||
| func (p *Policy) AllowStyles(propertyNames ...string) *stylePolicyBuilder { | ||||
|  | ||||
| 	p.init() | ||||
|  | ||||
| 	abp := stylePolicyBuilder{ | ||||
| 		p: p, | ||||
| 	} | ||||
|  | ||||
| 	for _, propertyName := range propertyNames { | ||||
| 		abp.propertyNames = append(abp.propertyNames, strings.ToLower(propertyName)) | ||||
| 	} | ||||
|  | ||||
| 	return &abp | ||||
| } | ||||
|  | ||||
| // Matching allows a regular expression to be applied to a nascent style | ||||
| // policy, and returns the style policy. Calling this more than once will | ||||
| // replace the existing regexp. | ||||
| func (spb *stylePolicyBuilder) Matching(regex *regexp.Regexp) *stylePolicyBuilder { | ||||
|  | ||||
| 	spb.regexp = regex | ||||
|  | ||||
| 	return spb | ||||
| } | ||||
|  | ||||
| // MatchingEnum allows a list of allowed values to be applied to a nascent style | ||||
| // policy, and returns the style policy. Calling this more than once will | ||||
| // replace the existing list of allowed values. | ||||
| func (spb *stylePolicyBuilder) MatchingEnum(enum ...string) *stylePolicyBuilder { | ||||
|  | ||||
| 	spb.enum = enum | ||||
|  | ||||
| 	return spb | ||||
| } | ||||
|  | ||||
| // MatchingHandler allows a handler to be applied to a nascent style | ||||
| // policy, and returns the style policy. Calling this more than once will | ||||
| // replace the existing handler. | ||||
| func (spb *stylePolicyBuilder) MatchingHandler(handler func(string) bool) *stylePolicyBuilder { | ||||
|  | ||||
| 	spb.handler = handler | ||||
|  | ||||
| 	return spb | ||||
| } | ||||
|  | ||||
| // OnElements will bind a style policy to a given range of HTML elements | ||||
| // and return the updated policy | ||||
| func (spb *stylePolicyBuilder) OnElements(elements ...string) *Policy { | ||||
|  | ||||
| 	for _, element := range elements { | ||||
| 		element = strings.ToLower(element) | ||||
|  | ||||
| 		for _, attr := range spb.propertyNames { | ||||
|  | ||||
| 			if _, ok := spb.p.elsAndStyles[element]; !ok { | ||||
| 				spb.p.elsAndStyles[element] = make(map[string]stylePolicy) | ||||
| 			} | ||||
|  | ||||
| 			sp := stylePolicy{} | ||||
| 			if spb.handler != nil { | ||||
| 				sp.handler = spb.handler | ||||
| 			} else if len(spb.enum) > 0 { | ||||
| 				sp.enum = spb.enum | ||||
| 			} else if spb.regexp != nil { | ||||
| 				sp.regexp = spb.regexp | ||||
| 			} else { | ||||
| 				sp.handler = getDefaultHandler(attr) | ||||
| 			} | ||||
| 			spb.p.elsAndStyles[element][attr] = sp | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return spb.p | ||||
| } | ||||
|  | ||||
| // OnElementsMatching will bind a style policy to any HTML elements matching the pattern | ||||
| // and return the updated policy | ||||
| func (spb *stylePolicyBuilder) OnElementsMatching(regex *regexp.Regexp) *Policy { | ||||
|  | ||||
| 		for _, attr := range spb.propertyNames { | ||||
|  | ||||
| 			if _, ok := spb.p.elsMatchingAndStyles[regex]; !ok { | ||||
| 				spb.p.elsMatchingAndStyles[regex] = make(map[string]stylePolicy) | ||||
| 			} | ||||
|  | ||||
| 			sp := stylePolicy{} | ||||
| 			if spb.handler != nil { | ||||
| 				sp.handler = spb.handler | ||||
| 			} else if len(spb.enum) > 0 { | ||||
| 				sp.enum = spb.enum | ||||
| 			} else if spb.regexp != nil { | ||||
| 				sp.regexp = spb.regexp | ||||
| 			} else { | ||||
| 				sp.handler = getDefaultHandler(attr) | ||||
| 			} | ||||
| 			spb.p.elsMatchingAndStyles[regex][attr] = sp | ||||
| 		} | ||||
|  | ||||
| 	return spb.p | ||||
| } | ||||
|  | ||||
| // Globally will bind a style policy to all HTML elements and return the | ||||
| // updated policy | ||||
| func (spb *stylePolicyBuilder) Globally() *Policy { | ||||
|  | ||||
| 	for _, attr := range spb.propertyNames { | ||||
| 		if _, ok := spb.p.globalStyles[attr]; !ok { | ||||
| 			spb.p.globalStyles[attr] = stylePolicy{} | ||||
| 		} | ||||
|  | ||||
| 		// Use only one strategy for validating styles, fallback to default | ||||
| 		sp := stylePolicy{} | ||||
| 		if spb.handler != nil { | ||||
| 			sp.handler = spb.handler | ||||
| 		} else if len(spb.enum) > 0 { | ||||
| 			sp.enum = spb.enum | ||||
| 		} else if spb.regexp != nil { | ||||
| 			sp.regexp = spb.regexp | ||||
| 		} else { | ||||
| 			sp.handler = getDefaultHandler(attr) | ||||
| 		} | ||||
| 		spb.p.globalStyles[attr] = sp | ||||
| 	} | ||||
|  | ||||
| 	return spb.p | ||||
| } | ||||
|  | ||||
| // AllowElements will append HTML elements to the whitelist without applying an | ||||
| // attribute policy to those elements (the elements are permitted | ||||
| // sans-attributes) | ||||
| @@ -282,8 +498,16 @@ func (p *Policy) AllowElements(names ...string) *Policy { | ||||
| 	return p | ||||
| } | ||||
|  | ||||
| // RequireNoFollowOnLinks will result in all <a> tags having a rel="nofollow" | ||||
| // added to them if one does not already exist | ||||
| func (p *Policy) AllowElementsMatching(regex *regexp.Regexp) *Policy { | ||||
| 	p.init() | ||||
| 	if _, ok := p.elsMatchingAndAttrs[regex]; !ok { | ||||
| 		p.elsMatchingAndAttrs[regex] = make(map[string]attrPolicy) | ||||
| 	} | ||||
| 	return p | ||||
| } | ||||
|  | ||||
| // RequireNoFollowOnLinks will result in all a, area, link 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 (p *Policy) RequireNoFollowOnLinks(require bool) *Policy { | ||||
| @@ -294,9 +518,10 @@ func (p *Policy) RequireNoFollowOnLinks(require bool) *Policy { | ||||
| 	return p | ||||
| } | ||||
|  | ||||
| // 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 | ||||
| // RequireNoFollowOnFullyQualifiedLinks will result in all a, area, and link | ||||
| // 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. | ||||
| func (p *Policy) RequireNoFollowOnFullyQualifiedLinks(require bool) *Policy { | ||||
| @@ -307,9 +532,35 @@ func (p *Policy) RequireNoFollowOnFullyQualifiedLinks(require bool) *Policy { | ||||
| 	return p | ||||
| } | ||||
|  | ||||
| // 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 | ||||
| // RequireNoReferrerOnLinks will result in all a, area, and link tags having a | ||||
| // rel="noreferrrer" added to them if one does not already exist | ||||
| // | ||||
| // Note: This requires p.RequireParseableURLs(true) and will enable it. | ||||
| func (p *Policy) RequireNoReferrerOnLinks(require bool) *Policy { | ||||
|  | ||||
| 	p.requireNoReferrer = require | ||||
| 	p.requireParseableURLs = true | ||||
|  | ||||
| 	return p | ||||
| } | ||||
|  | ||||
| // RequireNoReferrerOnFullyQualifiedLinks will result in all a, area, and link | ||||
| // tags that point to a non-local destination (i.e. starts with a protocol and | ||||
| // has a host) having a rel="noreferrer" added to them if one does not already | ||||
| // exist | ||||
| // | ||||
| // Note: This requires p.RequireParseableURLs(true) and will enable it. | ||||
| func (p *Policy) RequireNoReferrerOnFullyQualifiedLinks(require bool) *Policy { | ||||
|  | ||||
| 	p.requireNoReferrerFullyQualifiedLinks = require | ||||
| 	p.requireParseableURLs = true | ||||
|  | ||||
| 	return p | ||||
| } | ||||
|  | ||||
| // AddTargetBlankToFullyQualifiedLinks will result in all a, area and link 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. | ||||
| func (p *Policy) AddTargetBlankToFullyQualifiedLinks(require bool) *Policy { | ||||
|   | ||||
							
								
								
									
										376
									
								
								vendor/github.com/microcosm-cc/bluemonday/sanitize.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										376
									
								
								vendor/github.com/microcosm-cc/bluemonday/sanitize.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -34,15 +34,19 @@ import ( | ||||
| 	"io" | ||||
| 	"net/url" | ||||
| 	"regexp" | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
|  | ||||
| 	"golang.org/x/net/html" | ||||
|  | ||||
| 	cssparser "github.com/chris-ramon/douceur/parser" | ||||
| ) | ||||
|  | ||||
| var ( | ||||
| 	dataAttribute             = regexp.MustCompile("^data-.+") | ||||
| 	dataAttributeXMLPrefix    = regexp.MustCompile("^xml.+") | ||||
| 	dataAttributeInvalidChars = regexp.MustCompile("[A-Z;]+") | ||||
| 	cssUnicodeChar            = regexp.MustCompile(`\\[0-9a-f]{1,6} ?`) | ||||
| ) | ||||
|  | ||||
| // Sanitize takes a string that contains a HTML fragment or document and applies | ||||
| @@ -82,6 +86,98 @@ func (p *Policy) SanitizeReader(r io.Reader) *bytes.Buffer { | ||||
| 	return p.sanitize(r) | ||||
| } | ||||
|  | ||||
| const escapedURLChars = "'<>\"\r" | ||||
|  | ||||
| func escapeUrlComponent(val string) string { | ||||
| 	w := bytes.NewBufferString("") | ||||
| 	i := strings.IndexAny(val, escapedURLChars) | ||||
| 	for i != -1 { | ||||
| 		if _, err := w.WriteString(val[:i]); err != nil { | ||||
| 			return w.String() | ||||
| 		} | ||||
| 		var esc string | ||||
| 		switch val[i] { | ||||
| 		case '\'': | ||||
| 			// "'" is shorter than "'" and apos was not in HTML until HTML5. | ||||
| 			esc = "'" | ||||
| 		case '<': | ||||
| 			esc = "<" | ||||
| 		case '>': | ||||
| 			esc = ">" | ||||
| 		case '"': | ||||
| 			// """ is shorter than """. | ||||
| 			esc = """ | ||||
| 		case '\r': | ||||
| 			esc = "
" | ||||
| 		default: | ||||
| 			panic("unrecognized escape character") | ||||
| 		} | ||||
| 		val = val[i+1:] | ||||
| 		if _, err := w.WriteString(esc); err != nil { | ||||
| 			return w.String() | ||||
| 		} | ||||
| 		i = strings.IndexAny(val, escapedURLChars) | ||||
| 	} | ||||
| 	w.WriteString(val) | ||||
| 	return w.String() | ||||
| } | ||||
|  | ||||
| func sanitizedUrl(val string) (string, error) { | ||||
| 	u, err := url.Parse(val) | ||||
| 	if err != nil { | ||||
| 		return "", err | ||||
| 	} | ||||
| 	// sanitize the url query params | ||||
| 	sanitizedQueryValues := make(url.Values, 0) | ||||
| 	queryValues := u.Query() | ||||
| 	for k, vals := range queryValues { | ||||
| 		sk := html.EscapeString(k) | ||||
| 		for _, v := range vals { | ||||
| 			sv := v | ||||
| 			sanitizedQueryValues.Add(sk, sv) | ||||
| 		} | ||||
| 	} | ||||
| 	u.RawQuery = sanitizedQueryValues.Encode() | ||||
| 	// u.String() will also sanitize host/scheme/user/pass | ||||
| 	return u.String(), nil | ||||
| } | ||||
|  | ||||
| func (p *Policy) writeLinkableBuf(buff *bytes.Buffer, token *html.Token) { | ||||
| 	// do not escape multiple query parameters | ||||
| 	tokenBuff := bytes.NewBufferString("") | ||||
| 	tokenBuff.WriteString("<") | ||||
| 	tokenBuff.WriteString(token.Data) | ||||
| 	for _, attr := range token.Attr { | ||||
| 		tokenBuff.WriteByte(' ') | ||||
| 		tokenBuff.WriteString(attr.Key) | ||||
| 		tokenBuff.WriteString(`="`) | ||||
| 		switch attr.Key { | ||||
| 		case "href", "src": | ||||
| 			u, ok := p.validURL(attr.Val) | ||||
| 			if !ok { | ||||
| 				tokenBuff.WriteString(html.EscapeString(attr.Val)) | ||||
| 				continue | ||||
| 			} | ||||
| 			u, err := sanitizedUrl(u) | ||||
| 			if err == nil { | ||||
| 				tokenBuff.WriteString(u) | ||||
| 			} else { | ||||
| 				// fallthrough | ||||
| 				tokenBuff.WriteString(html.EscapeString(attr.Val)) | ||||
| 			} | ||||
| 		default: | ||||
| 			// re-apply | ||||
| 			tokenBuff.WriteString(html.EscapeString(attr.Val)) | ||||
| 		} | ||||
| 		tokenBuff.WriteByte('"') | ||||
| 	} | ||||
| 	if token.Type == html.SelfClosingTagToken { | ||||
| 		tokenBuff.WriteString("/") | ||||
| 	} | ||||
| 	tokenBuff.WriteString(">") | ||||
| 	buff.WriteString(tokenBuff.String()) | ||||
| } | ||||
|  | ||||
| // Performs the actual sanitization process. | ||||
| func (p *Policy) sanitize(r io.Reader) *bytes.Buffer { | ||||
|  | ||||
| @@ -133,20 +229,23 @@ func (p *Policy) sanitize(r io.Reader) *bytes.Buffer { | ||||
|  | ||||
| 		case html.StartTagToken: | ||||
|  | ||||
| 			mostRecentlyStartedToken = token.Data | ||||
| 			mostRecentlyStartedToken = strings.ToLower(token.Data) | ||||
|  | ||||
| 			aps, ok := p.elsAndAttrs[token.Data] | ||||
| 			if !ok { | ||||
| 				if _, ok := p.setOfElementsToSkipContent[token.Data]; ok { | ||||
| 					skipElementContent = true | ||||
| 					skippingElementsCount++ | ||||
| 				aa, matched := p.matchRegex(token.Data) | ||||
| 				if !matched { | ||||
| 					if _, ok := p.setOfElementsToSkipContent[token.Data]; ok { | ||||
| 						skipElementContent = true | ||||
| 						skippingElementsCount++ | ||||
| 					} | ||||
| 					if p.addSpaces { | ||||
| 						buff.WriteString(" ") | ||||
| 					} | ||||
| 					break | ||||
| 				} | ||||
| 				if p.addSpaces { | ||||
| 					buff.WriteString(" ") | ||||
| 				} | ||||
| 				break | ||||
| 				aps = aa | ||||
| 			} | ||||
|  | ||||
| 			if len(token.Attr) != 0 { | ||||
| 				token.Attr = p.sanitizeAttrs(token.Data, token.Attr, aps) | ||||
| 			} | ||||
| @@ -163,12 +262,17 @@ func (p *Policy) sanitize(r io.Reader) *bytes.Buffer { | ||||
| 			} | ||||
|  | ||||
| 			if !skipElementContent { | ||||
| 				buff.WriteString(token.String()) | ||||
| 				// do not escape multiple query parameters | ||||
| 				if linkable(token.Data) { | ||||
| 					p.writeLinkableBuf(&buff, &token) | ||||
| 				} else { | ||||
| 					buff.WriteString(token.String()) | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 		case html.EndTagToken: | ||||
|  | ||||
| 			if mostRecentlyStartedToken == token.Data { | ||||
| 			if mostRecentlyStartedToken == strings.ToLower(token.Data) { | ||||
| 				mostRecentlyStartedToken = "" | ||||
| 			} | ||||
|  | ||||
| @@ -182,18 +286,27 @@ func (p *Policy) sanitize(r io.Reader) *bytes.Buffer { | ||||
| 				} | ||||
| 				break | ||||
| 			} | ||||
|  | ||||
| 			if _, ok := p.elsAndAttrs[token.Data]; !ok { | ||||
| 				if _, ok := p.setOfElementsToSkipContent[token.Data]; ok { | ||||
| 				match := false | ||||
| 				for regex := range p.elsMatchingAndAttrs { | ||||
| 					if regex.MatchString(token.Data) { | ||||
| 						skipElementContent = false | ||||
| 						match = true | ||||
| 						break | ||||
| 					} | ||||
| 				} | ||||
| 				if _, ok := p.setOfElementsToSkipContent[token.Data]; ok && !match { | ||||
| 					skippingElementsCount-- | ||||
| 					if skippingElementsCount == 0 { | ||||
| 						skipElementContent = false | ||||
| 					} | ||||
| 				} | ||||
| 				if p.addSpaces { | ||||
| 					buff.WriteString(" ") | ||||
| 				if !match { | ||||
| 					if p.addSpaces { | ||||
| 						buff.WriteString(" ") | ||||
| 					} | ||||
| 					break | ||||
| 				} | ||||
| 				break | ||||
| 			} | ||||
|  | ||||
| 			if !skipElementContent { | ||||
| @@ -204,10 +317,14 @@ func (p *Policy) sanitize(r io.Reader) *bytes.Buffer { | ||||
|  | ||||
| 			aps, ok := p.elsAndAttrs[token.Data] | ||||
| 			if !ok { | ||||
| 				if p.addSpaces { | ||||
| 					buff.WriteString(" ") | ||||
| 				aa, matched := p.matchRegex(token.Data) | ||||
| 				if !matched { | ||||
| 					if p.addSpaces && !matched { | ||||
| 						buff.WriteString(" ") | ||||
| 					} | ||||
| 					break | ||||
| 				} | ||||
| 				break | ||||
| 				aps = aa | ||||
| 			} | ||||
|  | ||||
| 			if len(token.Attr) != 0 { | ||||
| @@ -217,12 +334,16 @@ func (p *Policy) sanitize(r io.Reader) *bytes.Buffer { | ||||
| 			if len(token.Attr) == 0 && !p.allowNoAttrs(token.Data) { | ||||
| 				if p.addSpaces { | ||||
| 					buff.WriteString(" ") | ||||
| 					break | ||||
| 				} | ||||
| 				break | ||||
| 			} | ||||
|  | ||||
| 			if !skipElementContent { | ||||
| 				buff.WriteString(token.String()) | ||||
| 				// do not escape multiple query parameters | ||||
| 				if linkable(token.Data) { | ||||
| 					p.writeLinkableBuf(&buff, &token) | ||||
| 				} else { | ||||
| 					buff.WriteString(token.String()) | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 		case html.TextToken: | ||||
| @@ -242,6 +363,7 @@ func (p *Policy) sanitize(r io.Reader) *bytes.Buffer { | ||||
| 					buff.WriteString(token.String()) | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 		default: | ||||
| 			// A token that didn't exist in the html package when we wrote this | ||||
| 			return &bytes.Buffer{} | ||||
| @@ -262,6 +384,23 @@ func (p *Policy) sanitizeAttrs( | ||||
| 		return attrs | ||||
| 	} | ||||
|  | ||||
| 	hasStylePolicies := false | ||||
| 	sps, elementHasStylePolicies := p.elsAndStyles[elementName] | ||||
| 	if len(p.globalStyles) > 0 || (elementHasStylePolicies && len(sps) > 0) { | ||||
| 		hasStylePolicies = true | ||||
| 	} | ||||
| 	// no specific element policy found, look for a pattern match | ||||
| 	if !hasStylePolicies { | ||||
| 		for k, v := range p.elsMatchingAndStyles { | ||||
| 			if k.MatchString(elementName) { | ||||
| 				if len(v) > 0 { | ||||
| 					hasStylePolicies = true | ||||
| 					break | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Builds a new attribute slice based on the whether the attribute has been | ||||
| 	// whitelisted explicitly or globally. | ||||
| 	cleanAttrs := []html.Attribute{} | ||||
| @@ -273,6 +412,19 @@ func (p *Policy) sanitizeAttrs( | ||||
| 				continue | ||||
| 			} | ||||
| 		} | ||||
| 		// Is this a "style" attribute, and if so, do we need to sanitize it? | ||||
| 		if htmlAttr.Key == "style" && hasStylePolicies { | ||||
| 			htmlAttr = p.sanitizeStyles(htmlAttr, elementName) | ||||
| 			if htmlAttr.Val == "" { | ||||
| 				// We've sanitized away any and all styles; don't bother to | ||||
| 				// output the style attribute (even if it's allowed) | ||||
| 				continue | ||||
| 			} else { | ||||
| 				cleanAttrs = append(cleanAttrs, htmlAttr) | ||||
| 				continue | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		// Is there an element specific attribute policy that applies? | ||||
| 		if ap, ok := aps[htmlAttr.Key]; ok { | ||||
| 			if ap.regexp != nil { | ||||
| @@ -354,6 +506,8 @@ func (p *Policy) sanitizeAttrs( | ||||
|  | ||||
| 		if (p.requireNoFollow || | ||||
| 			p.requireNoFollowFullyQualifiedLinks || | ||||
| 			p.requireNoReferrer || | ||||
| 			p.requireNoReferrerFullyQualifiedLinks || | ||||
| 			p.addTargetBlankToFullyQualifiedLinks) && | ||||
| 			len(cleanAttrs) > 0 { | ||||
|  | ||||
| @@ -381,12 +535,16 @@ func (p *Policy) sanitizeAttrs( | ||||
| 				if hrefFound { | ||||
| 					var ( | ||||
| 						noFollowFound    bool | ||||
| 						noReferrerFound  bool | ||||
| 						targetBlankFound bool | ||||
| 					) | ||||
|  | ||||
| 					addNoFollow := (p.requireNoFollow || | ||||
| 						externalLink && p.requireNoFollowFullyQualifiedLinks) | ||||
|  | ||||
| 					addNoReferrer := (p.requireNoReferrer || | ||||
| 						externalLink && p.requireNoReferrerFullyQualifiedLinks) | ||||
|  | ||||
| 					addTargetBlank := (externalLink && | ||||
| 						p.addTargetBlankToFullyQualifiedLinks) | ||||
|  | ||||
| @@ -394,18 +552,18 @@ func (p *Policy) sanitizeAttrs( | ||||
| 					for _, htmlAttr := range cleanAttrs { | ||||
|  | ||||
| 						var appended bool | ||||
| 						if htmlAttr.Key == "rel" && addNoFollow { | ||||
| 						if htmlAttr.Key == "rel" && (addNoFollow || addNoReferrer) { | ||||
|  | ||||
| 							if strings.Contains(htmlAttr.Val, "nofollow") { | ||||
| 								noFollowFound = true | ||||
| 								tmpAttrs = append(tmpAttrs, htmlAttr) | ||||
| 								appended = true | ||||
| 							} else { | ||||
| 							if addNoFollow && !strings.Contains(htmlAttr.Val, "nofollow") { | ||||
| 								htmlAttr.Val += " nofollow" | ||||
| 								noFollowFound = true | ||||
| 								tmpAttrs = append(tmpAttrs, htmlAttr) | ||||
| 								appended = true | ||||
| 							} | ||||
| 							if addNoReferrer && !strings.Contains(htmlAttr.Val, "noreferrer") { | ||||
| 								htmlAttr.Val += " noreferrer" | ||||
| 							} | ||||
| 							noFollowFound = addNoFollow | ||||
| 							noReferrerFound = addNoReferrer | ||||
| 							tmpAttrs = append(tmpAttrs, htmlAttr) | ||||
| 							appended = true | ||||
| 						} | ||||
|  | ||||
| 						if elementName == "a" && htmlAttr.Key == "target" { | ||||
| @@ -424,14 +582,22 @@ func (p *Policy) sanitizeAttrs( | ||||
| 							tmpAttrs = append(tmpAttrs, htmlAttr) | ||||
| 						} | ||||
| 					} | ||||
| 					if noFollowFound || targetBlankFound { | ||||
| 					if noFollowFound || noReferrerFound || targetBlankFound { | ||||
| 						cleanAttrs = tmpAttrs | ||||
| 					} | ||||
|  | ||||
| 					if addNoFollow && !noFollowFound { | ||||
| 					if (addNoFollow && !noFollowFound) || (addNoReferrer && !noReferrerFound) { | ||||
| 						rel := html.Attribute{} | ||||
| 						rel.Key = "rel" | ||||
| 						rel.Val = "nofollow" | ||||
| 						if addNoFollow { | ||||
| 							rel.Val = "nofollow" | ||||
| 						} | ||||
| 						if addNoReferrer { | ||||
| 							if rel.Val != "" { | ||||
| 								rel.Val += " " | ||||
| 							} | ||||
| 							rel.Val += "noreferrer" | ||||
| 						} | ||||
| 						cleanAttrs = append(cleanAttrs, rel) | ||||
| 					} | ||||
|  | ||||
| @@ -501,8 +667,95 @@ func (p *Policy) sanitizeAttrs( | ||||
| 	return cleanAttrs | ||||
| } | ||||
|  | ||||
| func (p *Policy) sanitizeStyles(attr html.Attribute, elementName string) html.Attribute { | ||||
| 	sps := p.elsAndStyles[elementName] | ||||
| 	if len(sps) == 0 { | ||||
| 		sps = map[string]stylePolicy{} | ||||
| 		// check for any matching elements, if we don't already have a policy found | ||||
| 		// if multiple matches are found they will be overwritten, it's best | ||||
| 		// to not have overlapping matchers | ||||
| 		for regex, policies := range p.elsMatchingAndStyles { | ||||
| 			if regex.MatchString(elementName) { | ||||
| 				for k, v := range policies { | ||||
| 					sps[k] = v | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	//Add semi-colon to end to fix parsing issue | ||||
| 	if len(attr.Val) > 0 && attr.Val[len(attr.Val)-1] != ';' { | ||||
| 		attr.Val = attr.Val + ";" | ||||
| 	} | ||||
| 	decs, err := cssparser.ParseDeclarations(attr.Val) | ||||
| 	if err != nil { | ||||
| 		attr.Val = "" | ||||
| 		return attr | ||||
| 	} | ||||
| 	clean := []string{} | ||||
| 	prefixes := []string{"-webkit-", "-moz-", "-ms-", "-o-", "mso-", "-xv-", "-atsc-", "-wap-", "-khtml-", "prince-", "-ah-", "-hp-", "-ro-", "-rim-", "-tc-"} | ||||
|  | ||||
| 	for _, dec := range decs { | ||||
| 		addedProperty := false | ||||
| 		tempProperty := strings.ToLower(dec.Property) | ||||
| 		tempValue := removeUnicode(strings.ToLower(dec.Value)) | ||||
| 		for _, i := range prefixes { | ||||
| 			tempProperty = strings.TrimPrefix(tempProperty, i) | ||||
| 		} | ||||
| 		if sp, ok := sps[tempProperty]; ok { | ||||
| 			if sp.handler != nil { | ||||
| 				if sp.handler(tempValue) { | ||||
| 					clean = append(clean, dec.Property+": "+dec.Value) | ||||
| 					addedProperty = true | ||||
| 				} | ||||
| 			} else if len(sp.enum) > 0 { | ||||
| 				if stringInSlice(tempValue, sp.enum) { | ||||
| 					clean = append(clean, dec.Property+": "+dec.Value) | ||||
| 					addedProperty = true | ||||
| 				} | ||||
| 			} else if sp.regexp != nil { | ||||
| 				if sp.regexp.MatchString(tempValue) { | ||||
| 					clean = append(clean, dec.Property+": "+dec.Value) | ||||
| 					addedProperty = true | ||||
| 				} | ||||
| 				continue | ||||
| 			} | ||||
| 		} | ||||
| 		if sp, ok := p.globalStyles[tempProperty]; ok && !addedProperty { | ||||
| 			if sp.handler != nil { | ||||
| 				if sp.handler(tempValue) { | ||||
| 					clean = append(clean, dec.Property+": "+dec.Value) | ||||
| 				} | ||||
| 			} else if len(sp.enum) > 0 { | ||||
| 				if stringInSlice(tempValue, sp.enum) { | ||||
| 					clean = append(clean, dec.Property+": "+dec.Value) | ||||
| 				} | ||||
| 			} else if sp.regexp != nil { | ||||
| 				if sp.regexp.MatchString(tempValue) { | ||||
| 					clean = append(clean, dec.Property+": "+dec.Value) | ||||
| 				} | ||||
| 				continue | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	if len(clean) > 0 { | ||||
| 		attr.Val = strings.Join(clean, "; ") | ||||
| 	} else { | ||||
| 		attr.Val = "" | ||||
| 	} | ||||
| 	return attr | ||||
| } | ||||
|  | ||||
| func (p *Policy) allowNoAttrs(elementName string) bool { | ||||
| 	_, ok := p.setOfElementsAllowedWithoutAttrs[elementName] | ||||
| 	if !ok { | ||||
| 		for _, r := range p.setOfElementsMatchingAllowedWithoutAttrs { | ||||
| 			if r.MatchString(elementName) { | ||||
| 				ok = true | ||||
| 				break | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	return ok | ||||
| } | ||||
|  | ||||
| @@ -561,6 +814,16 @@ func linkable(elementName string) bool { | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // stringInSlice returns true if needle exists in haystack | ||||
| func stringInSlice(needle string, haystack []string) bool { | ||||
| 	for _, straw := range haystack { | ||||
| 		if strings.ToLower(straw) == strings.ToLower(needle) { | ||||
| 			return true | ||||
| 		} | ||||
| 	} | ||||
| 	return false | ||||
| } | ||||
|  | ||||
| func isDataAttribute(val string) bool { | ||||
| 	if !dataAttribute.MatchString(val) { | ||||
| 		return false | ||||
| @@ -579,3 +842,48 @@ func isDataAttribute(val string) bool { | ||||
| 	} | ||||
| 	return true | ||||
| } | ||||
|  | ||||
| func removeUnicode(value string) string { | ||||
| 	substitutedValue := value | ||||
| 	currentLoc := cssUnicodeChar.FindStringIndex(substitutedValue) | ||||
| 	for currentLoc != nil { | ||||
|  | ||||
| 		character := substitutedValue[currentLoc[0]+1 : currentLoc[1]] | ||||
| 		character = strings.TrimSpace(character) | ||||
| 		if len(character) < 4 { | ||||
| 			character = strings.Repeat("0", 4-len(character)) + character | ||||
| 		} else { | ||||
| 			for len(character) > 4 { | ||||
| 				if character[0] != '0' { | ||||
| 					character = "" | ||||
| 					break | ||||
| 				} else { | ||||
| 					character = character[1:] | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		character = "\\u" + character | ||||
| 		translatedChar, err := strconv.Unquote(`"` + character + `"`) | ||||
| 		translatedChar = strings.TrimSpace(translatedChar) | ||||
| 		if err != nil { | ||||
| 			return "" | ||||
| 		} | ||||
| 		substitutedValue = substitutedValue[0:currentLoc[0]] + translatedChar + substitutedValue[currentLoc[1]:] | ||||
| 		currentLoc = cssUnicodeChar.FindStringIndex(substitutedValue) | ||||
| 	} | ||||
| 	return substitutedValue | ||||
| } | ||||
|  | ||||
| func (p *Policy) matchRegex(elementName string) (map[string]attrPolicy, bool) { | ||||
| 	aps := make(map[string]attrPolicy, 0) | ||||
| 	matched := false | ||||
| 	for regex, attrs := range p.elsMatchingAndAttrs { | ||||
| 		if regex.MatchString(elementName) { | ||||
| 			matched = true | ||||
| 			for k, v := range attrs { | ||||
| 				aps[k] = v | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	return aps, matched | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 6543
					6543