diff options
| author | Taras Madan <tarasmadan@google.com> | 2023-02-22 22:16:50 +0100 |
|---|---|---|
| committer | Taras Madan <tarasmadan@google.com> | 2023-02-24 12:47:23 +0100 |
| commit | 4165372ec8fd142475a4e35fd0cf4f8042132208 (patch) | |
| tree | 21cd62211b4dd80bee469054c5b65db77342333c /vendor/github.com/google/safehtml/stylesheet.go | |
| parent | 2b3ed821a493b8936c8bacfa6f8b4f1c90a00855 (diff) | |
dependencies: update
set go min requirements to 1.19
update dependencies
update vendor
Diffstat (limited to 'vendor/github.com/google/safehtml/stylesheet.go')
| -rw-r--r-- | vendor/github.com/google/safehtml/stylesheet.go | 111 |
1 files changed, 111 insertions, 0 deletions
diff --git a/vendor/github.com/google/safehtml/stylesheet.go b/vendor/github.com/google/safehtml/stylesheet.go new file mode 100644 index 000000000..17de8a517 --- /dev/null +++ b/vendor/github.com/google/safehtml/stylesheet.go @@ -0,0 +1,111 @@ +// Copyright (c) 2017 The Go Authors. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file or at +// https://developers.google.com/open-source/licenses/bsd + +package safehtml + +import ( + "container/list" + "fmt" + "regexp" + "strings" +) + +// A StyleSheet is an immutable string-like type which represents a CSS +// style sheet and guarantees that its value, as a string, will not cause +// untrusted script execution (cross-site scripting) when evaluated as CSS +// in a browser. +// +// StyleSheet's string representation can safely be interpolated as the +// content of a style element within HTML. The StyleSheet string should +// not be escaped before interpolation. +type StyleSheet struct { + // We declare a StyleSheet not as a string but as a struct wrapping a string + // to prevent construction of StyleSheet values through string conversion. + str string +} + +// StyleSheetFromConstant constructs a StyleSheet with the +// underlying stylesheet set to the given styleSheet, which must be an untyped string +// constant. +// +// No runtime validation or sanitization is performed on script; being under +// application control, it is simply assumed to comply with the StyleSheet +// contract. +func StyleSheetFromConstant(styleSheet stringConstant) StyleSheet { + return StyleSheet{string(styleSheet)} +} + +// CSSRule constructs a StyleSheet containng a CSS rule of the form: +// selector{style} +// It returns an error if selector contains disallowed characters or unbalanced +// brackets. +// +// The constructed StyleSheet value is guaranteed to fulfill its type contract, +// but is not guaranteed to be semantically valid CSS. +func CSSRule(selector string, style Style) (StyleSheet, error) { + if strings.ContainsRune(selector, '<') { + return StyleSheet{}, fmt.Errorf("selector %q contains '<'", selector) + } + selectorWithoutStrings := cssStringPattern.ReplaceAllString(selector, "") + if matches := invalidCSSSelectorRune.FindStringSubmatch(selectorWithoutStrings); matches != nil { + return StyleSheet{}, fmt.Errorf("selector %q contains %q, which is disallowed outside of CSS strings", selector, matches[0]) + } + if !hasBalancedBrackets(selectorWithoutStrings) { + return StyleSheet{}, fmt.Errorf("selector %q contains unbalanced () or [] brackets", selector) + } + return StyleSheet{fmt.Sprintf("%s{%s}", selector, style.String())}, nil +} + +var ( + // cssStringPattern matches a single- or double-quoted CSS string. + cssStringPattern = regexp.MustCompile( + `"([^"\r\n\f\\]|\\[\s\S])*"|` + // Double-quoted string literal + `'([^'\r\n\f\\]|\\[\s\S])*'`) // Single-quoted string literal + + // invalidCSSSelectorRune matches a rune that is not allowed in a CSS3 + // selector that does not contain string literals. + // See https://w3.org/TR/css3-selectors/#selectors. + invalidCSSSelectorRune = regexp.MustCompile(`[^-_a-zA-Z0-9#.:* ,>+~[\]()=^$|]`) +) + +// hasBalancedBrackets returns whether s has balanced () and [] brackets. +func hasBalancedBrackets(s string) bool { + stack := list.New() + for i := 0; i < len(s); i++ { + c := s[i] + if expected, ok := matchingBrackets[c]; ok { + e := stack.Back() + if e == nil { + return false + } + // Skip success check for this type assertion since it is trivial to + // see that only bytes are pushed onto this stack. + if v := e.Value.(byte); v != expected { + return false + } + stack.Remove(e) + continue + } + for _, openBracket := range matchingBrackets { + if c == openBracket { + stack.PushBack(c) + break + } + } + } + return stack.Len() == 0 +} + +// matchingBrackets[x] is the opening bracket that matches closing bracket x. +var matchingBrackets = map[byte]byte{ + ')': '(', + ']': '[', +} + +// String returns the string form of the StyleSheet. +func (s StyleSheet) String() string { + return s.str +} |
