// 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 ( "fmt" "regexp" "sort" "strings" "flag" "github.com/google/safehtml/internal/safehtmlutil" ) // A TrustedResourceURL is an immutable string-like type referencing the // application’s own, trusted resources. It can be used to safely load scripts, // CSS and other sensitive resources without the risk of untrusted code execution. // For example, it is unsafe to insert a plain string in a // // // // context since the URL may originate from untrusted user input and the // script it is pointing to may thus be controlled by an attacker. It is, // however, safe to use a TrustedResourceURL since its value is known to never // have left application control. // // In order to ensure that an attacker cannot influence the TrustedResourceURL // value, a TrustedResourceURL can only be instantiated from compile-time // constant string literals, command-line flags or a combination of the two, // but never from arbitrary string values potentially representing untrusted user input. // // Additionally, TrustedResourceURLs can be serialized and passed along within // the application via protocol buffers. It is the application’s responsibility // to ensure that the protocol buffers originate from within the application // itself and not from an external entity outside its trust domain. // // Note that TrustedResourceURLs can also use absolute paths (starting with '/') // and relative paths. This allows the same binary to be used for different // hosts without hard-coding the hostname in a string literal. type TrustedResourceURL struct { // We declare a TrustedResourceURL not as a string but as a struct wrapping a string // to prevent construction of TrustedResourceURL values through string conversion. str string } // TrustedResourceURLWithParams constructs a new TrustedResourceURL with the // given key-value pairs added as query parameters. // // Map entries with empty keys or values are ignored. The order of appended // keys is guaranteed to be stable but may differ from the order in input. func TrustedResourceURLWithParams(t TrustedResourceURL, params map[string]string) TrustedResourceURL { url := t.str var fragment string if i := strings.IndexByte(url, '#'); i != -1 { // The fragment identifier component will always appear at the end // of the URL after the query segment. It is therefore safe to // trim the fragment from the tail of the URL and re-append it after // all query parameters have been added. // See https://tools.ietf.org/html/rfc3986#appendix-A. fragment = url[i:] url = url[:i] } sep := "?" if i := strings.IndexRune(url, '?'); i != -1 { // The first "?" in a URL indicates the start of the query component. // See https://tools.ietf.org/html/rfc3986#section-3.4 if i == len(url)-1 { sep = "" } else { sep = "&" } } stringParams := make([]string, 0, len(params)) for k, v := range params { if k == "" || v == "" { continue } stringParam := safehtmlutil.QueryEscapeURL(k) + "=" + safehtmlutil.QueryEscapeURL(v) stringParams = append(stringParams, stringParam) } if len(stringParams) > 0 { sort.Strings(stringParams) url += sep + strings.Join(stringParams, "&") } return TrustedResourceURL{url + fragment} } // TrustedResourceURLFromConstant constructs a TrustedResourceURL with its underlying // URL set to the given url, which must be an untyped string constant. // // No runtime validation or sanitization is performed on url; being under // application control, it is simply assumed to comply with the TrustedResourceURL type // contract. func TrustedResourceURLFromConstant(url stringConstant) TrustedResourceURL { return TrustedResourceURL{string(url)} } // TrustedResourceURLFormatFromConstant constructs a TrustedResourceURL from a // format string, which must be an untyped string constant, and string arguments. // // Arguments are specified as a map of labels, which must contain only alphanumeric // and '_' runes, to string values. Each `%{