-
Notifications
You must be signed in to change notification settings - Fork 21
Expand file tree
/
Copy pathscript.go
More file actions
90 lines (83 loc) · 3.75 KB
/
script.go
File metadata and controls
90 lines (83 loc) · 3.75 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
// 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 (
"encoding/json"
"fmt"
"regexp"
)
// A Script is an immutable string-like type which represents JavaScript
// code and guarantees that its value, as a string, will not cause execution
// of unconstrained attacker controlled code (cross-site scripting) when
// evaluated as JavaScript in a browser.
//
// Script's string representation can safely be interpolated as the
// content of a script element within HTML, and can safely be passed to DOM
// properties and functions which expect JavaScript. In these cases, the Script
// string should not be escaped. Script's string representation can also be safely
// used as the value for on* attribute handlers in HTML, though the Script string
// must be escaped before such use.
//
// Note that the Script might contain text that is attacker-controlled but
// that text should have been interpolated with appropriate escaping,
// sanitization and/or validation into the right location in the script, such
// that it is highly constrained in its effect (for example, it had to match a
// set of allowed words).
//
// In order to ensure that an attacker cannot influence the Script
// value, a Script can only be instantiated from compile-time
// constant string literals or security-reviewed unchecked conversions,
// but never from arbitrary string values potentially representing untrusted
// user input.
type Script struct {
// We declare a Script not as a string but as a struct wrapping a string
// to prevent construction of Script values through string conversion.
str string
}
// ScriptFromConstant constructs a Script with its underlying script set
// to the given script, 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 Script
// contract.
func ScriptFromConstant(script stringConstant) Script {
return Script{string(script)}
}
// ScriptFromDataAndConstant constructs a Script of the form
//
// var name = data; script
//
// where name is the supplied variable name, data is the supplied data value
// encoded as JSON using encoding/json.Marshal, and script is the supplied
// JavaScript statement or sequence of statements. The supplied name and script
// must both be untyped string constants. It returns an error if name is not a
// valid Javascript identifier or JSON encoding fails.
//
// No runtime validation or sanitization is performed on script; being under
// application control, it is simply assumed to comply with the Script
// contract.
func ScriptFromDataAndConstant(name stringConstant, data interface{}, script stringConstant) (Script, error) {
if !jsIdentifierPattern.MatchString(string(name)) {
return Script{}, fmt.Errorf("variable name %q is an invalid Javascript identifier", string(name))
}
json, err := json.Marshal(data)
if err != nil {
return Script{}, err
}
return Script{fmt.Sprintf("var %s = %s;\n%s", name, json, string(script))}, nil
}
// jsIdentifierPattern matches strings that are valid Javascript identifiers.
//
// This pattern accepts only a subset of valid identifiers defined in
// https://tc39.github.io/ecma262/#sec-names-and-keywords. In particular,
// it does not match identifiers that contain non-ASCII letters, Unicode
// escape sequences, and the Unicode format-control characters
// \u200C (zero-width non-joiner) and \u200D (zero-width joiner).
var jsIdentifierPattern = regexp.MustCompile(`^[$_a-zA-Z][$_a-zA-Z0-9]+$`)
// String returns the string form of the Script.
func (s Script) String() string {
return s.str
}