aboutsummaryrefslogtreecommitdiffstats
path: root/vendor/github.com/google/safehtml/stylesheet.go
diff options
context:
space:
mode:
authorTaras Madan <tarasmadan@google.com>2023-02-22 22:16:50 +0100
committerTaras Madan <tarasmadan@google.com>2023-02-24 12:47:23 +0100
commit4165372ec8fd142475a4e35fd0cf4f8042132208 (patch)
tree21cd62211b4dd80bee469054c5b65db77342333c /vendor/github.com/google/safehtml/stylesheet.go
parent2b3ed821a493b8936c8bacfa6f8b4f1c90a00855 (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.go111
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
+}