1
0
Fork 0
mirror of https://github.com/documize/community.git synced 2025-07-25 16:19:46 +02:00

Bump version to 5.11.0

This commit is contained in:
Harvey Kandola 2024-01-10 14:47:40 -05:00
parent a32510b8e6
commit 510e1bd0bd
370 changed files with 18825 additions and 5454 deletions

View file

@ -10,6 +10,7 @@ Third-party patches are essential for keeping bluemonday secure and offering the
1. Do not vendor dependencies. As a security package, were we to vendor dependencies the projects that then vendor bluemonday may not receive the latest security updates to the dependencies. By not vendoring dependencies the project that implements bluemonday will vendor the latest version of any dependent packages. Vendoring is a project problem, not a package problem. bluemonday will be tested against the latest version of dependencies periodically and during any PR/merge.
2. I do not care about spelling mistakes or whitespace and I do not believe that you should either. PRs therefore must be functional in their nature or be substantial and impactful if documentation or examples.
3. This module does not participate in hacktober, please make your contributions meaningful.
## Submitting an Issue
@ -45,8 +46,6 @@ We haven't gone for the formal "Sign a Contributor Licence Agreement" thing that
But we do need to know that we can accept and merge your contributions, so for now the act of contributing a pull request should be considered equivalent to agreeing to a contributor licence agreement, specifically:
You accept that the act of submitting code to the bluemonday project is to grant a copyright licence to the project that is perpetual, worldwide, non-exclusive, no-charge, royalty free and irrevocable.
You accept that all who comply with the licence of the project (BSD 3-clause) are permitted to use your contributions to the project.
You accept, and by submitting code do declare, that you have the legal right to grant such a licence to the project and that each of the contributions is your own original creation.
* You accept that the act of submitting code to the bluemonday project is to grant a copyright licence to the project that is perpetual, worldwide, non-exclusive, no-charge, royalty free and irrevocable.
* You accept that all who comply with the licence of the project (BSD 3-clause) are permitted to use your contributions to the project.
* You accept, and by submitting code do declare, that you have the legal right to grant such a licence to the project and that each of the contributions is your own original creation.

View file

@ -1,3 +1,6 @@
SPDX short identifier: BSD-3-Clause
https://opensource.org/licenses/BSD-3-Clause
Copyright (c) 2014, David Kitchen <david@buro9.com>
All rights reserved.

View file

@ -6,7 +6,7 @@
# fmt-check: Check if the source files are formated
# build: Builds the code locally
# vet: Vets the code
# lint: Runs lint over the code (you do not need to fix everything)
# staticcheck: Runs staticcheck over the code
# test: Runs the tests
# cover: Gives you the URL to a nice test coverage report
#
@ -33,8 +33,8 @@ build:
vet:
@go vet
lint:
@golint *.go
staticcheck:
@staticcheck ./...
test:
@go test -v ./...
@ -42,7 +42,7 @@ test:
cover: COVERAGE_FILE := coverage.out
cover:
@go test -coverprofile=$(COVERAGE_FILE) && \
cover -html=$(COVERAGE_FILE) && rm $(COVERAGE_FILE)
go tool cover -html=$(COVERAGE_FILE) && rm $(COVERAGE_FILE)
install:
@go install ./...

View file

@ -1,4 +1,4 @@
# bluemonday [![Build Status](https://travis-ci.org/microcosm-cc/bluemonday.svg?branch=master)](https://travis-ci.org/microcosm-cc/bluemonday) [![GoDoc](https://godoc.org/github.com/microcosm-cc/bluemonday?status.png)](https://godoc.org/github.com/microcosm-cc/bluemonday) [![Sourcegraph](https://sourcegraph.com/github.com/microcosm-cc/bluemonday/-/badge.svg)](https://sourcegraph.com/github.com/microcosm-cc/bluemonday?badge)
# bluemonday [![GoDoc](https://godoc.org/github.com/microcosm-cc/bluemonday?status.png)](https://godoc.org/github.com/microcosm-cc/bluemonday) [![Sourcegraph](https://sourcegraph.com/github.com/microcosm-cc/bluemonday/-/badge.svg)](https://sourcegraph.com/github.com/microcosm-cc/bluemonday?badge)
bluemonday is a HTML sanitizer implemented in Go. It is fast and highly configurable.

View file

@ -280,6 +280,49 @@ var (
"slategray", "slategrey", "snow", "springgreen", "steelblue", "tan",
"teal", "thistle", "tomato", "turquoise", "violet", "wheat", "white",
"whitesmoke", "yellow", "yellowgreen"}
Alpha = regexp.MustCompile(`^[a-z]+$`)
Blur = regexp.MustCompile(`^blur\([0-9]+px\)$`)
BrightnessCont = regexp.MustCompile(`^(brightness|contrast)\([0-9]+\%\)$`)
Count = regexp.MustCompile(`^[0-9]+[\.]?[0-9]*$`)
CubicBezier = regexp.MustCompile(`^cubic-bezier\(([ ]*(0(.[0-9]+)?|1(.0)?),){3}[ ]*(0(.[0-9]+)?|1)\)$`)
Digits = regexp.MustCompile(`^digits [2-4]$`)
DropShadow = regexp.MustCompile(`drop-shadow\(([-]?[0-9]+px) ([-]?[0-9]+px)( [-]?[0-9]+px)?( ([-]?[0-9]+px))?`)
Font = regexp.MustCompile(`^('[a-z \-]+'|[a-z \-]+)$`)
Grayscale = regexp.MustCompile(`^grayscale\(([0-9]{1,2}|100)%\)$`)
GridTemplateAreas = regexp.MustCompile(`^['"]?[a-z ]+['"]?$`)
HexRGB = regexp.MustCompile(`^#([0-9a-f]{3}|[0-9a-f]{6}|[0-9a-f]{8})$`)
HSL = regexp.MustCompile(`^hsl\([ ]*([012]?[0-9]{1,2}|3[0-5][0-9]|360),[ ]*([0-9]{0,2}|100)\%,[ ]*([0-9]{0,2}|100)\%\)$`)
HSLA = regexp.MustCompile(`^hsla\(([ ]*[012]?[0-9]{1,2}|3[0-5][0-9]|360),[ ]*([0-9]{0,2}|100)\%,[ ]*([0-9]{0,2}|100)\%,[ ]*(1|1\.0|0|(0\.[0-9]+))\)$`)
HueRotate = regexp.MustCompile(`^hue-rotate\(([12]?[0-9]{1,2}|3[0-5][0-9]|360)?\)$`)
Invert = regexp.MustCompile(`^invert\(([0-9]{1,2}|100)%\)$`)
Length = regexp.MustCompile(`^[\-]?([0-9]+|[0-9]*[\.][0-9]+)(%|cm|mm|in|px|pt|pc|em|ex|ch|rem|vw|vh|vmin|vmax|deg|rad|turn)?$`)
Matrix = regexp.MustCompile(`^matrix\(([ ]*[0-9]+[\.]?[0-9]*,){5}([ ]*[0-9]+[\.]?[0-9]*)\)$`)
Matrix3D = regexp.MustCompile(`^matrix3d\(([ ]*[0-9]+[\.]?[0-9]*,){15}([ ]*[0-9]+[\.]?[0-9]*)\)$`)
NegTime = regexp.MustCompile(`^[\-]?[0-9]+[\.]?[0-9]*(s|ms)?$`)
Numeric = regexp.MustCompile(`^[0-9]+$`)
NumericDecimal = regexp.MustCompile(`^[0-9\.]+$`)
Opactiy = regexp.MustCompile(`^opacity\(([0-9]{1,2}|100)%\)$`)
Perspective = regexp.MustCompile(`perspective\(`)
Position = regexp.MustCompile(`^[\-]*[0-9]+[cm|mm|in|px|pt|pc\%]* [[\-]*[0-9]+[cm|mm|in|px|pt|pc\%]*]*$`)
Opacity = regexp.MustCompile(`^(0[.]?[0-9]*)|(1.0)$`)
QuotedAlpha = regexp.MustCompile(`^["'][a-z]+["']$`)
Quotes = regexp.MustCompile(`^([ ]*["'][\x{0022}\x{0027}\x{2039}\x{2039}\x{203A}\x{00AB}\x{00BB}\x{2018}\x{2019}\x{201C}-\x{201E}]["'] ["'][\x{0022}\x{0027}\x{2039}\x{2039}\x{203A}\x{00AB}\x{00BB}\x{2018}\x{2019}\x{201C}-\x{201E}]["'])+$`)
Rect = regexp.MustCompile(`^rect\([0-9]+px,[ ]*[0-9]+px,[ ]*[0-9]+px,[ ]*[0-9]+px\)$`)
RGB = regexp.MustCompile(`^rgb\(([ ]*((([0-9]{1,2}|100)\%)|(([01]?[0-9]{1,2})|(2[0-4][0-9])|(25[0-5]))),){2}([ ]*((([0-9]{1,2}|100)\%)|(([01]?[0-9]{1,2})|(2[0-4][0-9])|(25[0-5]))))\)$`)
RGBA = regexp.MustCompile(`^rgba\(([ ]*((([0-9]{1,2}|100)\%)|(([01]?[0-9]{1,2})|(2[0-4][0-9])|(25[0-5]))),){3}[ ]*(1(\.0)?|0|(0\.[0-9]+))\)$`)
Rotate = regexp.MustCompile(`^rotate(x|y|z)?\(([12]?|3[0-5][0-9]|360)\)$`)
Rotate3D = regexp.MustCompile(`^rotate3d\(([ ]?(1(\.0)?|0\.[0-9]+),){3}([12]?|3[0-5][0-9]|360)\)$`)
Saturate = regexp.MustCompile(`^saturate\([0-9]+%\)$`)
Sepia = regexp.MustCompile(`^sepia\(([0-9]{1,2}|100)%\)$`)
Skew = regexp.MustCompile(`skew(x|y)?\(`)
Span = regexp.MustCompile(`^span [0-9]+$`)
Steps = regexp.MustCompile(`^steps\([ ]*[0-9]+([ ]*,[ ]*(start|end)?)\)$`)
Time = regexp.MustCompile(`^[0-9]+[\.]?[0-9]*(s|ms)?$`)
TransitionProp = regexp.MustCompile(`^([a-zA-Z]+,[ ]?)*[a-zA-Z]+$`)
TranslateScale = regexp.MustCompile(`(translate|translate3d|translatex|translatey|translatez|scale|scale3d|scalex|scaley|scalez)\(`)
URL = regexp.MustCompile(`^url\([\"\']?((https|http)[a-z0-9\.\\/_:]+[\"\']?)\)$`)
ZIndex = regexp.MustCompile(`^[\-]?[0-9]+$`)
)
func multiSplit(value string, seps ...string) []string {
@ -323,10 +366,11 @@ func in(value []string, arr []string) bool {
func splitValues(value string) []string {
values := strings.Split(value, ",")
newValues := []string{}
for _, strippedValue := range values {
strippedValue = strings.ToLower(strings.TrimSpace(strippedValue))
newValues = append(newValues, strings.ToLower(strings.TrimSpace(strippedValue)))
}
return values
return newValues
}
func GetDefaultHandler(attr string) func(string) bool {
@ -388,9 +432,7 @@ func AnimationHandler(value string) bool {
}
func AnimationDelayHandler(value string) bool {
reg := regexp.MustCompile(`[\-]?[0-9]+[\.]?[0-9]*[s|ms]?`)
reg.Longest()
if reg.FindString(value) == value && value != "" {
if NegTime.MatchString(value) {
return true
}
values := []string{"initial", "inherit"}
@ -405,9 +447,7 @@ func AnimationDirectionHandler(value string) bool {
}
func AnimationDurationHandler(value string) bool {
reg := regexp.MustCompile(`[0-9]+[\.]?[0-9]*[s|ms]?`)
reg.Longest()
if reg.FindString(value) == value && value != "" {
if Time.MatchString(value) {
return true
}
values := []string{"initial", "inherit"}
@ -422,9 +462,7 @@ func AnimationFillModeHandler(value string) bool {
}
func AnimationIterationCountHandler(value string) bool {
reg := regexp.MustCompile(`[0-9]+[\.]?[0-9]*`)
reg.Longest()
if reg.FindString(value) == value && value != "" {
if Count.MatchString(value) {
return true
}
values := []string{"infinite", "initial", "inherit"}
@ -433,9 +471,7 @@ func AnimationIterationCountHandler(value string) bool {
}
func AnimationNameHandler(value string) bool {
reg := regexp.MustCompile(`[a-z]+`)
reg.Longest()
return reg.FindString(value) == value && value != ""
return Alpha.MatchString(value)
}
func AnimationPlayStateHandler(value string) bool {
@ -450,14 +486,10 @@ func TimingFunctionHandler(value string) bool {
if in(splitVals, values) {
return true
}
reg := regexp.MustCompile(`cubic-bezier\(([ ]*(0(.[0-9]+)?|1(.0)?),){3}[ ]*(0(.[0-9]+)?|1)\)`)
reg.Longest()
if reg.FindString(value) == value && value != "" {
if CubicBezier.MatchString(value) {
return true
}
reg = regexp.MustCompile(`steps\([ ]*[0-9]+([ ]*,[ ]*(start|end)?)\)`)
reg.Longest()
return reg.FindString(value) == value && value != ""
return Steps.MatchString(value)
}
func BackfaceVisibilityHandler(value string) bool {
@ -518,9 +550,7 @@ func ImageHandler(value string) bool {
if in(splitVals, values) {
return true
}
reg := regexp.MustCompile(`url\([\"\']?((https|http)[a-z0-9\.\\/_:]+[\"\']?)\)`)
reg.Longest()
return reg.FindString(value) == value && value != ""
return URL.MatchString(value)
}
func BackgroundOriginHandler(value string) bool {
@ -535,12 +565,7 @@ func BackgroundPositionHandler(value string) bool {
if in(splitVals, values) {
return true
}
reg := regexp.MustCompile(`[\-]*[0-9]+[cm|mm|in|px|pt|pc\%]* [[\-]*[0-9]+[cm|mm|in|px|pt|pc\%]*]*`)
reg.Longest()
if reg.FindString(value) == value && value != "" {
return true
}
return false
return Position.MatchString(value)
}
func BackgroundRepeatHandler(value string) bool {
@ -816,31 +841,19 @@ func CaretColorHandler(value string) bool {
if in(splitVals, colorValues) {
return true
}
reg := regexp.MustCompile(`#[0-9abcdef]{6}`)
reg.Longest()
if reg.FindString(value) == value && value != "" {
if HexRGB.MatchString(value) {
return true
}
reg = regexp.MustCompile(`rgb\(([ ]*((([0-9]{1,2}|100)\%)|(([01]?[0-9]{1,2})|(2[0-4][0-9])|(25[0-5]))),){2}([ ]*((([0-9]{1,2}|100)\%)|(([01]?[0-9]{1,2})|(2[0-4][0-9])|(25[0-5]))))\)`)
reg.Longest()
if reg.FindString(value) == value && value != "" {
if RGB.MatchString(value) {
return true
}
reg = regexp.MustCompile(`rgba\(([ ]*((([0-9]{1,2}|100)\%)|(([01]?[0-9]{1,2})|(2[0-4][0-9])|(25[0-5]))),){3}[ ]*(1(\.0)?|0|(0\.[0-9]+))\)`)
reg.Longest()
if reg.FindString(value) == value && value != "" {
if RGBA.MatchString(value) {
return true
}
reg = regexp.MustCompile(`hsl\([ ]*([012]?[0-9]{1,2}|3[0-5][0-9]|360),[ ]*([0-9]{0,2}|100)\%,[ ]*([0-9]{0,2}|100)\%\)`)
if reg.FindString(value) == value && value != "" {
if HSL.MatchString(value) {
return true
}
reg = regexp.MustCompile(`hsla\(([ ]*[012]?[0-9]{1,2}|3[0-5][0-9]|360),[ ]*([0-9]{0,2}|100)\%,[ ]*([0-9]{0,2}|100)\%,[ ]*(1|1\.0|0|(0\.[0-9]+))\)`)
reg.Longest()
if reg.FindString(value) == value && value != "" {
return true
}
return false
return HSLA.MatchString(value)
}
func ClearHandler(value string) bool {
@ -850,9 +863,7 @@ func ClearHandler(value string) bool {
}
func ClipHandler(value string) bool {
reg := regexp.MustCompile(`rect\([0-9]+px,[ ]*[0-9]+px,[ ]*[0-9]+px,[ ]*[0-9]+px\)`)
reg.Longest()
if reg.FindString(value) == value && value != "" {
if Rect.MatchString(value) {
return true
}
values := []string{"auto", "initial", "inherit"}
@ -865,38 +876,23 @@ func ColorHandler(value string) bool {
if in(splitVals, colorValues) {
return true
}
reg := regexp.MustCompile(`#[0-9abcdef]{6}`)
reg.Longest()
if reg.FindString(value) == value && value != "" {
if HexRGB.MatchString(value) {
return true
}
reg = regexp.MustCompile(`rgb\(([ ]*((([0-9]{1,2}|100)\%)|(([01]?[0-9]{1,2})|(2[0-4][0-9])|(25[0-5]))),){2}([ ]*((([0-9]{1,2}|100)\%)|(([01]?[0-9]{1,2})|(2[0-4][0-9])|(25[0-5]))))\)`)
reg.Longest()
if reg.FindString(value) == value && value != "" {
if RGB.MatchString(value) {
return true
}
reg = regexp.MustCompile(`rgba\(([ ]*((([0-9]{1,2}|100)\%)|(([01]?[0-9]{1,2})|(2[0-4][0-9])|(25[0-5]))),){3}[ ]*(1(\.0)?|0|(0\.[0-9]+))\)`)
reg.Longest()
if reg.FindString(value) == value && value != "" {
if RGBA.MatchString(value) {
return true
}
reg = regexp.MustCompile(`hsl\([ ]*([012]?[0-9]{1,2}|3[0-5][0-9]|360),[ ]*([0-9]{0,2}|100)\%,[ ]*([0-9]{0,2}|100)\%\)`)
reg.Longest()
if reg.FindString(value) == value && value != "" {
if HSL.MatchString(value) {
return true
}
reg = regexp.MustCompile(`hsla\(([ ]*[012]?[0-9]{1,2}|3[0-5][0-9]|360),[ ]*([0-9]{0,2}|100)\%,[ ]*([0-9]{0,2}|100)\%,[ ]*(1|1\.0|0|(0\.[0-9]+))\)`)
reg.Longest()
if reg.FindString(value) == value && value != "" {
return true
}
return false
return HSLA.MatchString(value)
}
func ColumnCountHandler(value string) bool {
reg := regexp.MustCompile(`[0-9]+`)
reg.Longest()
if reg.FindString(value) == value && value != "" {
if Numeric.MatchString(value) {
return true
}
values := []string{"auto", "initial", "inherit"}
@ -1000,54 +996,35 @@ func FilterHandler(value string) bool {
if in(splitVals, values) {
return true
}
reg := regexp.MustCompile(`blur\([0-9]+px\)`)
reg.Longest()
if reg.FindString(value) == value && value != "" {
if Blur.MatchString(value) {
return true
}
reg = regexp.MustCompile(`(brightness|contrast)\([0-9]+\%\)`)
reg.Longest()
if reg.FindString(value) == value && value != "" {
if BrightnessCont.MatchString(value) {
return true
}
reg = regexp.MustCompile(`drop-shadow\(([-]?[0-9]+px) ([-]?[0-9]+px)( [-]?[0-9]+px)?( ([-]?[0-9]+px))?`)
reg.Longest()
colorValue := strings.TrimSuffix(string(reg.ReplaceAll([]byte(value), []byte{})), ")")
if DropShadow.MatchString(value) {
return true
}
colorValue := strings.TrimSuffix(string(DropShadow.ReplaceAll([]byte(value), []byte{})), ")")
if ColorHandler(colorValue) {
return true
}
reg = regexp.MustCompile(`grayscale\(([0-9]{1,2}|100)%\)`)
reg.Longest()
if reg.FindString(value) == value && value != "" {
if Grayscale.MatchString(value) {
return true
}
reg = regexp.MustCompile(`hue-rotate\(([12]?[0-9]{1,2}|3[0-5][0-9]|360)?\)`)
reg.Longest()
if reg.FindString(value) == value && value != "" {
if HueRotate.MatchString(value) {
return true
}
reg = regexp.MustCompile(`invert\(([0-9]{1,2}|100)%\)`)
reg.Longest()
if reg.FindString(value) == value && value != "" {
if Invert.MatchString(value) {
return true
}
reg = regexp.MustCompile(`opacity\(([0-9]{1,2}|100)%\)`)
reg.Longest()
if reg.FindString(value) == value && value != "" {
if Opacity.MatchString(value) {
return true
}
reg = regexp.MustCompile(`saturate\([0-9]+%\)`)
reg.Longest()
if reg.FindString(value) == value && value != "" {
if Saturate.MatchString(value) {
return true
}
reg = regexp.MustCompile(`sepia\(([0-9]{1,2}|100)%\)`)
reg.Longest()
if reg.FindString(value) == value && value != "" {
return true
}
//Not allowing URLs
return false
return Sepia.MatchString(value)
}
func FlexHandler(value string) bool {
@ -1092,9 +1069,7 @@ func FlexFlowHandler(value string) bool {
}
func FlexGrowHandler(value string) bool {
reg := regexp.MustCompile(`[0-9\.]+`)
reg.Longest()
if reg.FindString(value) == value && value != "" {
if NumericDecimal.MatchString(value) {
return true
}
splitVals := strings.Split(value, ";")
@ -1144,11 +1119,9 @@ func FontFamilyHandler(value string) bool {
if in(splitVals, values) {
return true
}
reg := regexp.MustCompile(`('[a-z \-]+'|[a-z \-]+)`)
reg.Longest()
for _, i := range splitVals {
i = strings.TrimSpace(i)
if reg.FindString(i) != i {
if Font.FindString(i) != i {
return false
}
}
@ -1162,9 +1135,7 @@ func FontKerningHandler(value string) bool {
}
func FontLanguageOverrideHandler(value string) bool {
reg := regexp.MustCompile(`[a-z]+`)
reg.Longest()
return reg.FindString(value) == value && value != ""
return Alpha.MatchString(value)
}
func FontSizeHandler(value string) bool {
@ -1177,9 +1148,7 @@ func FontSizeHandler(value string) bool {
}
func FontSizeAdjustHandler(value string) bool {
reg := regexp.MustCompile(`[0-9]+[\.]?[0-9]*`)
reg.Longest()
if reg.FindString(value) == value && value != "" {
if Count.MatchString(value) {
return true
}
values := []string{"auto", "initial", "inherit"}
@ -1298,9 +1267,7 @@ func GridColumnGapHandler(value string) bool {
}
func LengthHandler(value string) bool {
reg := regexp.MustCompile(`[\-]?[0-9]+[\.]?[0-9]*(%|cm|mm|in|px|pt|pc|em|ex|ch|rem|vw|vh|vmin|vmax|deg|rad|turn)?`)
reg.Longest()
return reg.FindString(value) == value && value != ""
return Length.MatchString(value)
}
func LineBreakHandler(value string) bool {
@ -1310,12 +1277,10 @@ func LineBreakHandler(value string) bool {
}
func GridAxisStartEndHandler(value string) bool {
reg := regexp.MustCompile(`[0-9]+`)
if reg.FindString(value) == value && value != "" {
if Numeric.MatchString(value) {
return true
}
reg = regexp.MustCompile(`span [0-9]+`)
if reg.FindString(value) == value && value != "" {
if Span.MatchString(value) {
return true
}
values := []string{"auto"}
@ -1366,9 +1331,7 @@ func GridTemplateAreasHandler(value string) bool {
if in([]string{value}, values) {
return true
}
reg := regexp.MustCompile(`['"]?[a-z ]+['"]?`)
reg.Longest()
return reg.FindString(value) == value && value != ""
return GridTemplateAreas.MatchString(value)
}
func GridTemplateColumnsHandler(value string) bool {
@ -1551,9 +1514,7 @@ func ObjectPositionHandler(value string) bool {
}
func OpacityHandler(value string) bool {
reg := regexp.MustCompile("(0[.]?[0-9]*)|(1.0)")
reg.Longest()
if reg.FindString(value) == value && value != "" {
if Opacity.MatchString(value) {
return true
}
values := []string{"initial", "inherit"}
@ -1562,9 +1523,7 @@ func OpacityHandler(value string) bool {
}
func OrderHandler(value string) bool {
reg := regexp.MustCompile("[0-9]+")
reg.Longest()
if reg.FindString(value) == value && value != "" {
if Numeric.MatchString(value) {
return true
}
values := []string{"initial", "inherit"}
@ -1629,9 +1588,7 @@ func OverflowWrapHandler(value string) bool {
}
func OrphansHandler(value string) bool {
reg := regexp.MustCompile(`[0-9]+`)
reg.Longest()
return reg.FindString(value) == value && value != ""
return Numeric.MatchString(value)
}
func PaddingHandler(value string) bool {
@ -1716,9 +1673,7 @@ func QuotesHandler(value string) bool {
if in(splitVals, values) {
return true
}
reg := regexp.MustCompile(`([ ]*["'][\x{0022}\x{0027}\x{2039}\x{2039}\x{203A}\x{00AB}\x{00BB}\x{2018}\x{2019}\x{201C}-\x{201E}]["'] ["'][\x{0022}\x{0027}\x{2039}\x{2039}\x{203A}\x{00AB}\x{00BB}\x{2018}\x{2019}\x{201C}-\x{201E}]["'])+`)
reg.Longest()
return reg.FindString(value) == value && value != ""
return Quotes.MatchString(value)
}
func ResizeHandler(value string) bool {
@ -1766,9 +1721,7 @@ func TextCombineUprightHandler(value string) bool {
if in(splitVals, values) {
return true
}
reg := regexp.MustCompile(`digits [2-4]`)
reg.Longest()
return reg.FindString(value) == value && value != ""
return Digits.MatchString(value)
}
func TextDecorationHandler(value string) bool {
@ -1813,9 +1766,7 @@ func TextJustifyHandler(value string) bool {
}
func TextOverflowHandler(value string) bool {
reg := regexp.MustCompile("[\"'][a-z]+[\"']")
reg.Longest()
if reg.FindString(value) == value && value != "" {
if QuotedAlpha.MatchString(value) {
return true
}
values := []string{"clip", "ellipsis", "initial", "inherit"}
@ -1868,18 +1819,13 @@ func TransformHandler(value string) bool {
if in([]string{value}, values) {
return true
}
reg := regexp.MustCompile(`matrix\(([ ]*[0-9]+[\.]?[0-9]*,){5}([ ]*[0-9]+[\.]?[0-9]*)\)`)
reg.Longest()
if reg.FindString(value) == value && value != "" {
if Matrix.MatchString(value) {
return true
}
reg = regexp.MustCompile(`matrix3d\(([ ]*[0-9]+[\.]?[0-9]*,){15}([ ]*[0-9]+[\.]?[0-9]*)\)`)
if reg.FindString(value) == value && value != "" {
if Matrix3D.MatchString(value) {
return true
}
reg = regexp.MustCompile(`(translate|translate3d|translatex|translatey|translatez|scale|scale3d|scalex|scaley|scalez)\(`)
reg.Longest()
subValue := string(reg.ReplaceAll([]byte(value), []byte{}))
subValue := string(TranslateScale.ReplaceAll([]byte(value), []byte{}))
trimValue := strings.Split(strings.TrimSuffix(subValue, ")"), ",")
valid := true
for _, i := range trimValue {
@ -1891,19 +1837,13 @@ func TransformHandler(value string) bool {
if valid && trimValue != nil {
return true
}
reg = regexp.MustCompile(`rotate(x|y|z)?\(([12]?|3[0-5][0-9]|360)\)`)
reg.Longest()
if reg.FindString(value) == value && value != "" {
if Rotate.MatchString(value) {
return true
}
reg = regexp.MustCompile(`rotate3d\(([ ]?(1(\.0)?|0\.[0-9]+),){3}([12]?|3[0-5][0-9]|360)\)`)
reg.Longest()
if reg.FindString(value) == value && value != "" {
if Rotate3D.MatchString(value) {
return true
}
reg = regexp.MustCompile(`skew(x|y)?\(`)
reg.Longest()
subValue = string(reg.ReplaceAll([]byte(value), []byte{}))
subValue = string(Skew.ReplaceAll([]byte(value), []byte{}))
subValue = strings.TrimSuffix(subValue, ")")
trimValue = strings.Split(subValue, ",")
valid = true
@ -1916,9 +1856,7 @@ func TransformHandler(value string) bool {
if valid {
return true
}
reg = regexp.MustCompile(`perspective\(`)
reg.Longest()
subValue = string(reg.ReplaceAll([]byte(value), []byte{}))
subValue = string(Perspective.ReplaceAll([]byte(value), []byte{}))
subValue = strings.TrimSuffix(subValue, ")")
return LengthHandler(subValue)
}
@ -1973,9 +1911,7 @@ func TransitionHandler(value string) bool {
}
func TransitionDelayHandler(value string) bool {
reg := regexp.MustCompile("[0-9]+[.]?[0-9]*(s|ms)?")
reg.Longest()
if reg.FindString(value) == value && value != "" {
if Time.MatchString(value) {
return true
}
values := []string{"initial", "inherit"}
@ -1984,9 +1920,7 @@ func TransitionDelayHandler(value string) bool {
}
func TransitionDurationHandler(value string) bool {
reg := regexp.MustCompile("[0-9]+[.]?[0-9]*(s|ms)?")
reg.Longest()
if reg.FindString(value) == value && value != "" {
if Time.MatchString(value) {
return true
}
values := []string{"initial", "inherit"}
@ -1995,9 +1929,7 @@ func TransitionDurationHandler(value string) bool {
}
func TransitionPropertyHandler(value string) bool {
reg := regexp.MustCompile("([a-zA-Z]+,[ ]?)*[a-zA-Z]+")
reg.Longest()
if reg.FindString(value) == value && value != "" {
if TransitionProp.MatchString(value) {
return true
}
values := []string{"none", "all", "initial", "inherit"}
@ -2075,9 +2007,7 @@ func WritingModeHandler(value string) bool {
}
func ZIndexHandler(value string) bool {
reg := regexp.MustCompile(`[\-]?[0-9]+`)
reg.Longest()
if reg.FindString(value) == value && value != "" {
if ZIndex.MatchString(value) {
return true
}
values := []string{"auto", "initial", "inherit"}

View file

@ -35,31 +35,31 @@ the allowlist will be stripped.
The default bluemonday.UGCPolicy().Sanitize() turns this:
Hello <STYLE>.XSS{background-image:url("javascript:alert('XSS')");}</STYLE><A CLASS=XSS></A>World
Hello <STYLE>.XSS{background-image:url("javascript:alert('XSS')");}</STYLE><A CLASS=XSS></A>World
Into the more harmless:
Hello World
Hello World
And it turns this:
<a href="javascript:alert('XSS1')" onmouseover="alert('XSS2')">XSS<a>
<a href="javascript:alert('XSS1')" onmouseover="alert('XSS2')">XSS<a>
Into this:
XSS
XSS
Whilst still allowing this:
<a href="http://www.google.com/">
<img src="https://ssl.gstatic.com/accounts/ui/logo_2x.png"/>
</a>
<a href="http://www.google.com/">
<img src="https://ssl.gstatic.com/accounts/ui/logo_2x.png"/>
</a>
To pass through mostly unaltered (it gained a rel="nofollow"):
<a href="http://www.google.com/" rel="nofollow">
<img src="https://ssl.gstatic.com/accounts/ui/logo_2x.png"/>
</a>
<a href="http://www.google.com/" rel="nofollow">
<img src="https://ssl.gstatic.com/accounts/ui/logo_2x.png"/>
</a>
The primary purpose of bluemonday is to take potentially unsafe user generated
content (from things like Markdown, HTML WYSIWYG tools, etc) and make it safe
@ -95,10 +95,10 @@ attributes are considered safe for your scenario. OWASP provide an XSS
prevention cheat sheet ( https://www.google.com/search?q=xss+prevention+cheat+sheet )
to help explain the risks, but essentially:
1. Avoid allowing anything other than plain HTML elements
2. Avoid allowing `script`, `style`, `iframe`, `object`, `embed`, `base`
elements
3. Avoid allowing anything other than plain HTML elements with simple
values that you can match to a regexp
1. Avoid allowing anything other than plain HTML elements
2. Avoid allowing `script`, `style`, `iframe`, `object`, `embed`, `base`
elements
3. Avoid allowing anything other than plain HTML elements with simple
values that you can match to a regexp
*/
package bluemonday

View file

@ -117,7 +117,7 @@ var (
// This is not exported as it's not useful by itself, and only has value
// within the AllowDataURIImages func
dataURIImagePrefix = regexp.MustCompile(
`^image/(gif|jpeg|png|webp);base64,`,
`^image/(gif|jpeg|png|svg\+xml|webp);base64,`,
)
)
@ -193,10 +193,11 @@ func (p *Policy) AllowImages() {
// http://en.wikipedia.org/wiki/Data_URI_scheme
//
// Images must have a mimetype matching:
// image/gif
// image/jpeg
// image/png
// image/webp
//
// image/gif
// image/jpeg
// image/png
// image/webp
//
// NOTE: There is a potential security risk to allowing data URIs and you should
// only permit them on content you already trust.
@ -221,11 +222,7 @@ func (p *Policy) AllowDataURIImages() {
}
_, err := base64.StdEncoding.DecodeString(url.Opaque[len(matched):])
if err != nil {
return false
}
return true
return err == nil
},
)
}

View file

@ -117,6 +117,19 @@ type Policy struct {
// returning true are allowed.
allowURLSchemes map[string][]urlPolicy
// These regexps are used to match allowed URL schemes, for example
// if one would want to allow all URL schemes, they would add `.+`.
// However pay attention as this can lead to XSS being rendered thus
// defeating the purpose of using a HTML sanitizer.
// The regexps are only considered if a schema was not explicitly
// handled by `AllowURLSchemes` or `AllowURLSchemeWithCustomPolicy`.
allowURLSchemeRegexps []*regexp.Regexp
// If srcRewriter is not nil, it is used to rewrite the src attribute
// of tags that download resources, such as <img> and <script>.
// It requires that the URL is parsable by "net/url" url.Parse().
srcRewriter urlRewriter
// 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.
//
@ -192,6 +205,8 @@ type stylePolicyBuilder struct {
type urlPolicy func(url *url.URL) (allowUrl bool)
type urlRewriter func(*url.URL)
type SandboxValue int64
const (
@ -221,6 +236,7 @@ func (p *Policy) init() {
p.elsMatchingAndStyles = make(map[*regexp.Regexp]map[string][]stylePolicy)
p.globalStyles = make(map[string][]stylePolicy)
p.allowURLSchemes = make(map[string][]urlPolicy)
p.allowURLSchemeRegexps = make([]*regexp.Regexp, 0)
p.setOfElementsAllowedWithoutAttrs = make(map[string]struct{})
p.setOfElementsToSkipContent = make(map[string]struct{})
p.initialized = true
@ -563,6 +579,40 @@ func (p *Policy) AllowElementsMatching(regex *regexp.Regexp) *Policy {
return p
}
// AllowURLSchemesMatching will append URL schemes to the allowlist if they
// match a regexp.
func (p *Policy) AllowURLSchemesMatching(r *regexp.Regexp) *Policy {
p.allowURLSchemeRegexps = append(p.allowURLSchemeRegexps, r)
return p
}
// RewriteSrc will rewrite the src attribute of a resource downloading tag
// (e.g. <img>, <script>, <iframe>) using the provided function.
//
// Typically the use case here is that if the content that we're sanitizing
// is untrusted then the content that is inlined is also untrusted.
// To prevent serving this content on the same domain as the content appears
// on it is good practise to proxy the content through an additional domain
// name as this will force the web client to consider the inline content as
// third party to the main content, thus providing browser isolation around
// the inline content.
//
// An example of this is a web mail provider like fastmail.com , when an
// email (user generated content) is displayed, the email text is shown on
// fastmail.com but the inline attachments and content are rendered from
// fastmailusercontent.com . This proxying of the external content on a
// domain that is different to the content domain forces the browser domain
// security model to kick in. Note that this only applies to differences
// below the suffix (as per the publix suffix list).
//
// This is a good practise to adopt as it prevents the content from being
// able to set cookies on the main domain and thus prevents the content on
// the main domain from being able to read those cookies.
func (p *Policy) RewriteSrc(fn urlRewriter) *Policy {
p.srcRewriter = fn
return p
}
// RequireNoFollowOnLinks will result in all a, area, link tags having a
// rel="nofollow"added to them if one does not already exist
//
@ -707,7 +757,7 @@ func (p *Policy) AllowURLSchemeWithCustomPolicy(
func (p *Policy) RequireSandboxOnIFrame(vals ...SandboxValue) {
p.requireSandboxOnIFrame = make(map[string]bool)
for val := range vals {
for _, val := range vals {
switch SandboxValue(val) {
case SandboxAllowDownloads:
p.requireSandboxOnIFrame["allow-downloads"] = true
@ -879,6 +929,7 @@ func (p *Policy) addDefaultElementsWithoutAttrs() {
p.setOfElementsAllowedWithoutAttrs["optgroup"] = struct{}{}
p.setOfElementsAllowedWithoutAttrs["option"] = struct{}{}
p.setOfElementsAllowedWithoutAttrs["p"] = struct{}{}
p.setOfElementsAllowedWithoutAttrs["picture"] = struct{}{}
p.setOfElementsAllowedWithoutAttrs["pre"] = struct{}{}
p.setOfElementsAllowedWithoutAttrs["q"] = struct{}{}
p.setOfElementsAllowedWithoutAttrs["rp"] = struct{}{}

View file

@ -95,41 +95,6 @@ func (p *Policy) SanitizeReaderToWriter(r io.Reader, w io.Writer) error {
return p.sanitize(r, w)
}
const escapedURLChars = "'<>\"\r"
func escapeUrlComponent(w stringWriterWriter, val string) error {
i := strings.IndexAny(val, escapedURLChars)
for i != -1 {
if _, err := w.WriteString(val[:i]); err != nil {
return err
}
var esc string
switch val[i] {
case '\'':
// "&#39;" is shorter than "&apos;" and apos was not in HTML until HTML5.
esc = "&#39;"
case '<':
esc = "&lt;"
case '>':
esc = "&gt;"
case '"':
// "&#34;" is shorter than "&quot;".
esc = "&#34;"
case '\r':
esc = "&#13;"
default:
panic("unrecognized escape character")
}
val = val[i+1:]
if _, err := w.WriteString(esc); err != nil {
return err
}
i = strings.IndexAny(val, escapedURLChars)
}
_, err := w.WriteString(val)
return err
}
// Query represents a single part of the query string, a query param
type Query struct {
Key string
@ -322,9 +287,7 @@ func (p *Policy) sanitize(r io.Reader, w io.Writer) error {
aps = aa
}
if len(token.Attr) != 0 {
token.Attr = escapeAttributes(
p.sanitizeAttrs(token.Data, token.Attr, aps),
)
token.Attr = p.sanitizeAttrs(token.Data, token.Attr, aps)
}
if len(token.Attr) == 0 {
@ -434,7 +397,7 @@ func (p *Policy) sanitize(r io.Reader, w io.Writer) error {
}
if len(token.Attr) != 0 {
token.Attr = escapeAttributes(p.sanitizeAttrs(token.Data, token.Attr, aps))
token.Attr = p.sanitizeAttrs(token.Data, token.Attr, aps)
}
if len(token.Attr) == 0 && !p.allowNoAttrs(token.Data) {
@ -442,8 +405,8 @@ func (p *Policy) sanitize(r io.Reader, w io.Writer) error {
if _, err := buff.WriteString(" "); err != nil {
return err
}
break
}
break
}
if !skipElementContent {
if _, err := buff.WriteString(token.String()); err != nil {
@ -565,11 +528,9 @@ attrsLoop:
for _, ap := range apl {
if ap.regexp != nil {
if ap.regexp.MatchString(htmlAttr.Val) {
htmlAttr.Val = escapeAttribute(htmlAttr.Val)
cleanAttrs = append(cleanAttrs, htmlAttr)
}
} else {
htmlAttr.Val = escapeAttribute(htmlAttr.Val)
cleanAttrs = append(cleanAttrs, htmlAttr)
}
}
@ -616,6 +577,14 @@ attrsLoop:
case "audio", "embed", "iframe", "img", "script", "source", "track", "video":
if htmlAttr.Key == "src" {
if u, ok := p.validURL(htmlAttr.Val); ok {
if p.srcRewriter != nil {
parsedURL, err := url.Parse(u)
if err != nil {
fmt.Println(err)
}
p.srcRewriter(parsedURL)
u = parsedURL.String()
}
htmlAttr.Val = u
tmpAttrs = append(tmpAttrs, htmlAttr)
}
@ -856,6 +825,7 @@ func (p *Policy) sanitizeStyles(attr html.Attribute, elementName string) html.At
}
//Add semi-colon to end to fix parsing issue
attr.Val = strings.TrimRight(attr.Val, " ")
if len(attr.Val) > 0 && attr.Val[len(attr.Val)-1] != ';' {
attr.Val = attr.Val + ";"
}
@ -973,9 +943,14 @@ func (p *Policy) validURL(rawurl string) (string, bool) {
}
if u.Scheme != "" {
urlPolicies, ok := p.allowURLSchemes[u.Scheme]
if !ok {
for _, r := range p.allowURLSchemeRegexps {
if r.MatchString(u.Scheme) {
return u.String(), true
}
}
return "", false
}
@ -984,7 +959,7 @@ func (p *Policy) validURL(rawurl string) (string, bool) {
}
for _, urlPolicy := range urlPolicies {
if urlPolicy(u) == true {
if urlPolicy(u) {
return u.String(), true
}
}
@ -1023,7 +998,7 @@ 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) {
if strings.EqualFold(straw, needle) {
return true
}
}
@ -1112,18 +1087,3 @@ func normaliseElementName(str string) string {
`"`,
)
}
func escapeAttributes(attrs []html.Attribute) []html.Attribute {
escapedAttrs := []html.Attribute{}
for _, attr := range attrs {
attr.Val = escapeAttribute(attr.Val)
escapedAttrs = append(escapedAttrs, attr)
}
return escapedAttrs
}
func escapeAttribute(val string) string {
val = strings.Replace(val, string([]rune{'\u00A0'}), `&nbsp;`, -1)
val = strings.Replace(val, `"`, `&quot;`, -1)
return val
}