github.com/google/go-safeweb@v0.0.0-20231219055052-64d8cfc90fbb/safesql/safesql.go (about) 1 // Copyright 2020 Google LLC 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // https://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // Package safesql implements a safe version of the standard sql package while trying to keep the API as similar as 16 // possible to the original one. 17 // The concept of this package is to provide "safe by construction" SQL strings so that code that would accidentally introduce 18 // SQL injection vulnerabilities does not compile. 19 // If uncheckedconversions and legacyconversions are not used and the sql package is forbidden this package guarantees that only compile-time constants will be 20 // interpreted as SQL, thus preventing attacker-controlled strings to be accidentally executed. 21 // 22 // # Migration Examples 23 // 24 // Code like the following is trivial to migrate from sql to safesql: 25 // 26 // db.Query("SELECT ...", args...) 27 // 28 // The only change required would be to promote the string literal to a trusted string: 29 // 30 // db.Query(safesql.New("SELECT ..."), args...) 31 // 32 // For more complicated cases it might be needed to use the helper functions like Join and Concat. 33 // If the queries for the service are stored in a trusted runtime-only source that cannot be controlled by a user 34 // the uncheckedconversions package can be used to assert that those strings are under the programmer control. 35 // Note that unchecked conversions should be very limited, ideally never used, as they pose a security risk. 36 // 37 // Note on API documentation. 38 // 39 // For documentation on methods and types that wrap the standard ones please refer to the stdlib package doc instead, as 40 // all the types exported by this package are tiny wrappers around the standard ones and thus follow their behavior. 41 // The only relevant difference is that functions accept TrustedSQLString instances instead of plain "strings" and that some 42 // dangerous methods have been removed. 43 // 44 // # Explainer 45 // 46 // This package wraps the sql package and all methods that would normally take a string take a TrustedSQLString instead. 47 // The constructor for TrustedSQLString takes a stringConstant as an argument, which is an unexported type constituted by a named string. 48 // The only way for a package outside of safesql to construct a TrustedSQLString is thus to pass an untyped string (only const strings can be untyped) to the constructor. 49 package safesql 50 51 import ( 52 "strconv" 53 "strings" 54 55 "github.com/google/go-safeweb/safesql/internal/raw" 56 ) 57 58 func init() { 59 // Initialize the bypass mechanisms for unchecked and legacy conversions. 60 raw.TrustedSQLString = func(unsafe string) TrustedSQLString { return TrustedSQLString{unsafe} } 61 } 62 63 type stringConstant string 64 65 // TrustedSQLString is a string representing a SQL query that is known to be safe and not contain potentially malicious inputs. 66 type TrustedSQLString struct { 67 s string 68 } 69 70 // New constructs a TrustedSQLString from a compile-time constant string. 71 // Since the stringConstant type is unexported the only way to call this function outside of this package is to pass 72 // a string literal or an untyped string const. 73 func New(text stringConstant) TrustedSQLString { return TrustedSQLString{string(text)} } 74 75 // NewFromUint64 constructs a TrustedSQLString from a uint64. 76 func NewFromUint64(i uint64) TrustedSQLString { return TrustedSQLString{strconv.FormatUint(i, 10)} } 77 78 // TrustedSQLStringConcat concatenates the given trusted SQL strings into a trusted string. 79 // 80 // Note: this function should not be abused to create arbitrary queries from user input, it is just 81 // intended as a helper to compose queries at runtime to avoid redundant constants. 82 func TrustedSQLStringConcat(ss ...TrustedSQLString) TrustedSQLString { 83 return TrustedSQLStringJoin(ss, TrustedSQLString{}) 84 } 85 86 // TrustedSQLStringJoin joins the given trusted SQL with the given separator the same way strings.Join would. 87 // 88 // Note: this function should not be abused to create arbitrary queries from user input, it is just 89 // intended as a helper to compose queries at runtime to avoid redundant constants. 90 func TrustedSQLStringJoin(ss []TrustedSQLString, sep TrustedSQLString) TrustedSQLString { 91 accum := make([]string, 0, len(ss)) 92 for _, s := range ss { 93 accum = append(accum, s.s) 94 } 95 return TrustedSQLString{strings.Join(accum, sep.s)} 96 } 97 func (t TrustedSQLString) String() string { 98 return t.s 99 } 100 101 // TrustedSQLStringSplit functions as strings.Split but for TrustedSQLStrings. 102 func TrustedSQLStringSplit(s TrustedSQLString, sep TrustedSQLString) []TrustedSQLString { 103 spl := strings.Split(s.s, sep.s) 104 accum := make([]TrustedSQLString, 0, len(spl)) 105 for _, s := range spl { 106 accum = append(accum, TrustedSQLString{s}) 107 } 108 return accum 109 }