1*1fa6dee9SAndroid Build Coastguard Worker// Copyright 2015 Google Inc. All rights reserved. 2*1fa6dee9SAndroid Build Coastguard Worker// 3*1fa6dee9SAndroid Build Coastguard Worker// Licensed under the Apache License, Version 2.0 (the "License"); 4*1fa6dee9SAndroid Build Coastguard Worker// you may not use this file except in compliance with the License. 5*1fa6dee9SAndroid Build Coastguard Worker// You may obtain a copy of the License at 6*1fa6dee9SAndroid Build Coastguard Worker// 7*1fa6dee9SAndroid Build Coastguard Worker// http://www.apache.org/licenses/LICENSE-2.0 8*1fa6dee9SAndroid Build Coastguard Worker// 9*1fa6dee9SAndroid Build Coastguard Worker// Unless required by applicable law or agreed to in writing, software 10*1fa6dee9SAndroid Build Coastguard Worker// distributed under the License is distributed on an "AS IS" BASIS, 11*1fa6dee9SAndroid Build Coastguard Worker// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12*1fa6dee9SAndroid Build Coastguard Worker// See the License for the specific language governing permissions and 13*1fa6dee9SAndroid Build Coastguard Worker// limitations under the License. 14*1fa6dee9SAndroid Build Coastguard Worker 15*1fa6dee9SAndroid Build Coastguard Workerpackage proptools 16*1fa6dee9SAndroid Build Coastguard Worker 17*1fa6dee9SAndroid Build Coastguard Workerimport ( 18*1fa6dee9SAndroid Build Coastguard Worker "bytes" 19*1fa6dee9SAndroid Build Coastguard Worker "os/exec" 20*1fa6dee9SAndroid Build Coastguard Worker "reflect" 21*1fa6dee9SAndroid Build Coastguard Worker "slices" 22*1fa6dee9SAndroid Build Coastguard Worker "testing" 23*1fa6dee9SAndroid Build Coastguard Worker "unsafe" 24*1fa6dee9SAndroid Build Coastguard Worker) 25*1fa6dee9SAndroid Build Coastguard Worker 26*1fa6dee9SAndroid Build Coastguard Workertype escapeTestCase struct { 27*1fa6dee9SAndroid Build Coastguard Worker name string 28*1fa6dee9SAndroid Build Coastguard Worker in string 29*1fa6dee9SAndroid Build Coastguard Worker out string 30*1fa6dee9SAndroid Build Coastguard Worker} 31*1fa6dee9SAndroid Build Coastguard Worker 32*1fa6dee9SAndroid Build Coastguard Workervar ninjaEscapeTestCase = []escapeTestCase{ 33*1fa6dee9SAndroid Build Coastguard Worker { 34*1fa6dee9SAndroid Build Coastguard Worker name: "no escaping", 35*1fa6dee9SAndroid Build Coastguard Worker in: `test`, 36*1fa6dee9SAndroid Build Coastguard Worker out: `test`, 37*1fa6dee9SAndroid Build Coastguard Worker }, 38*1fa6dee9SAndroid Build Coastguard Worker { 39*1fa6dee9SAndroid Build Coastguard Worker name: "leading $", 40*1fa6dee9SAndroid Build Coastguard Worker in: `$test`, 41*1fa6dee9SAndroid Build Coastguard Worker out: `$$test`, 42*1fa6dee9SAndroid Build Coastguard Worker }, 43*1fa6dee9SAndroid Build Coastguard Worker { 44*1fa6dee9SAndroid Build Coastguard Worker name: "trailing $", 45*1fa6dee9SAndroid Build Coastguard Worker in: `test$`, 46*1fa6dee9SAndroid Build Coastguard Worker out: `test$$`, 47*1fa6dee9SAndroid Build Coastguard Worker }, 48*1fa6dee9SAndroid Build Coastguard Worker { 49*1fa6dee9SAndroid Build Coastguard Worker name: "leading and trailing $", 50*1fa6dee9SAndroid Build Coastguard Worker in: `$test$`, 51*1fa6dee9SAndroid Build Coastguard Worker out: `$$test$$`, 52*1fa6dee9SAndroid Build Coastguard Worker }, 53*1fa6dee9SAndroid Build Coastguard Worker} 54*1fa6dee9SAndroid Build Coastguard Worker 55*1fa6dee9SAndroid Build Coastguard Workervar shellEscapeTestCase = []escapeTestCase{ 56*1fa6dee9SAndroid Build Coastguard Worker { 57*1fa6dee9SAndroid Build Coastguard Worker name: "no escaping", 58*1fa6dee9SAndroid Build Coastguard Worker in: `test`, 59*1fa6dee9SAndroid Build Coastguard Worker out: `test`, 60*1fa6dee9SAndroid Build Coastguard Worker }, 61*1fa6dee9SAndroid Build Coastguard Worker { 62*1fa6dee9SAndroid Build Coastguard Worker name: "leading $", 63*1fa6dee9SAndroid Build Coastguard Worker in: `$test`, 64*1fa6dee9SAndroid Build Coastguard Worker out: `'$test'`, 65*1fa6dee9SAndroid Build Coastguard Worker }, 66*1fa6dee9SAndroid Build Coastguard Worker { 67*1fa6dee9SAndroid Build Coastguard Worker name: "trailing $", 68*1fa6dee9SAndroid Build Coastguard Worker in: `test$`, 69*1fa6dee9SAndroid Build Coastguard Worker out: `'test$'`, 70*1fa6dee9SAndroid Build Coastguard Worker }, 71*1fa6dee9SAndroid Build Coastguard Worker { 72*1fa6dee9SAndroid Build Coastguard Worker name: "leading and trailing $", 73*1fa6dee9SAndroid Build Coastguard Worker in: `$test$`, 74*1fa6dee9SAndroid Build Coastguard Worker out: `'$test$'`, 75*1fa6dee9SAndroid Build Coastguard Worker }, 76*1fa6dee9SAndroid Build Coastguard Worker { 77*1fa6dee9SAndroid Build Coastguard Worker name: "single quote", 78*1fa6dee9SAndroid Build Coastguard Worker in: `'`, 79*1fa6dee9SAndroid Build Coastguard Worker out: `''\'''`, 80*1fa6dee9SAndroid Build Coastguard Worker }, 81*1fa6dee9SAndroid Build Coastguard Worker { 82*1fa6dee9SAndroid Build Coastguard Worker name: "multiple single quote", 83*1fa6dee9SAndroid Build Coastguard Worker in: `''`, 84*1fa6dee9SAndroid Build Coastguard Worker out: `''\'''\'''`, 85*1fa6dee9SAndroid Build Coastguard Worker }, 86*1fa6dee9SAndroid Build Coastguard Worker { 87*1fa6dee9SAndroid Build Coastguard Worker name: "double quote", 88*1fa6dee9SAndroid Build Coastguard Worker in: `""`, 89*1fa6dee9SAndroid Build Coastguard Worker out: `'""'`, 90*1fa6dee9SAndroid Build Coastguard Worker }, 91*1fa6dee9SAndroid Build Coastguard Worker { 92*1fa6dee9SAndroid Build Coastguard Worker name: "ORIGIN", 93*1fa6dee9SAndroid Build Coastguard Worker in: `-Wl,--rpath,${ORIGIN}/../bionic-loader-test-libs`, 94*1fa6dee9SAndroid Build Coastguard Worker out: `'-Wl,--rpath,${ORIGIN}/../bionic-loader-test-libs'`, 95*1fa6dee9SAndroid Build Coastguard Worker }, 96*1fa6dee9SAndroid Build Coastguard Worker} 97*1fa6dee9SAndroid Build Coastguard Worker 98*1fa6dee9SAndroid Build Coastguard Workervar shellEscapeIncludingSpacesTestCase = []escapeTestCase{ 99*1fa6dee9SAndroid Build Coastguard Worker { 100*1fa6dee9SAndroid Build Coastguard Worker name: "no escaping", 101*1fa6dee9SAndroid Build Coastguard Worker in: `test`, 102*1fa6dee9SAndroid Build Coastguard Worker out: `test`, 103*1fa6dee9SAndroid Build Coastguard Worker }, 104*1fa6dee9SAndroid Build Coastguard Worker { 105*1fa6dee9SAndroid Build Coastguard Worker name: "spacing", 106*1fa6dee9SAndroid Build Coastguard Worker in: `arg1 arg2`, 107*1fa6dee9SAndroid Build Coastguard Worker out: `'arg1 arg2'`, 108*1fa6dee9SAndroid Build Coastguard Worker }, 109*1fa6dee9SAndroid Build Coastguard Worker { 110*1fa6dee9SAndroid Build Coastguard Worker name: "single quote", 111*1fa6dee9SAndroid Build Coastguard Worker in: `'arg'`, 112*1fa6dee9SAndroid Build Coastguard Worker out: `''\''arg'\'''`, 113*1fa6dee9SAndroid Build Coastguard Worker }, 114*1fa6dee9SAndroid Build Coastguard Worker} 115*1fa6dee9SAndroid Build Coastguard Worker 116*1fa6dee9SAndroid Build Coastguard Workerfunc TestNinjaEscaping(t *testing.T) { 117*1fa6dee9SAndroid Build Coastguard Worker for _, testCase := range ninjaEscapeTestCase { 118*1fa6dee9SAndroid Build Coastguard Worker got := NinjaEscape(testCase.in) 119*1fa6dee9SAndroid Build Coastguard Worker if got != testCase.out { 120*1fa6dee9SAndroid Build Coastguard Worker t.Errorf("%s: expected `%s` got `%s`", testCase.name, testCase.out, got) 121*1fa6dee9SAndroid Build Coastguard Worker } 122*1fa6dee9SAndroid Build Coastguard Worker } 123*1fa6dee9SAndroid Build Coastguard Worker} 124*1fa6dee9SAndroid Build Coastguard Worker 125*1fa6dee9SAndroid Build Coastguard Workerfunc TestShellEscaping(t *testing.T) { 126*1fa6dee9SAndroid Build Coastguard Worker for _, testCase := range shellEscapeTestCase { 127*1fa6dee9SAndroid Build Coastguard Worker got := ShellEscape(testCase.in) 128*1fa6dee9SAndroid Build Coastguard Worker if got != testCase.out { 129*1fa6dee9SAndroid Build Coastguard Worker t.Errorf("%s: expected `%s` got `%s`", testCase.name, testCase.out, got) 130*1fa6dee9SAndroid Build Coastguard Worker } 131*1fa6dee9SAndroid Build Coastguard Worker } 132*1fa6dee9SAndroid Build Coastguard Worker} 133*1fa6dee9SAndroid Build Coastguard Worker 134*1fa6dee9SAndroid Build Coastguard Workerfunc TestShellEscapeIncludingSpaces(t *testing.T) { 135*1fa6dee9SAndroid Build Coastguard Worker for _, testCase := range shellEscapeIncludingSpacesTestCase { 136*1fa6dee9SAndroid Build Coastguard Worker got := ShellEscapeIncludingSpaces(testCase.in) 137*1fa6dee9SAndroid Build Coastguard Worker if got != testCase.out { 138*1fa6dee9SAndroid Build Coastguard Worker t.Errorf("%s: expected `%s` got `%s`", testCase.name, testCase.out, got) 139*1fa6dee9SAndroid Build Coastguard Worker } 140*1fa6dee9SAndroid Build Coastguard Worker } 141*1fa6dee9SAndroid Build Coastguard Worker} 142*1fa6dee9SAndroid Build Coastguard Worker 143*1fa6dee9SAndroid Build Coastguard Workerfunc TestExternalShellEscaping(t *testing.T) { 144*1fa6dee9SAndroid Build Coastguard Worker if testing.Short() { 145*1fa6dee9SAndroid Build Coastguard Worker return 146*1fa6dee9SAndroid Build Coastguard Worker } 147*1fa6dee9SAndroid Build Coastguard Worker for _, testCase := range shellEscapeTestCase { 148*1fa6dee9SAndroid Build Coastguard Worker cmd := "echo " + ShellEscape(testCase.in) 149*1fa6dee9SAndroid Build Coastguard Worker got, err := exec.Command("/bin/sh", "-c", cmd).Output() 150*1fa6dee9SAndroid Build Coastguard Worker got = bytes.TrimSuffix(got, []byte("\n")) 151*1fa6dee9SAndroid Build Coastguard Worker if err != nil { 152*1fa6dee9SAndroid Build Coastguard Worker t.Error(err) 153*1fa6dee9SAndroid Build Coastguard Worker } 154*1fa6dee9SAndroid Build Coastguard Worker if string(got) != testCase.in { 155*1fa6dee9SAndroid Build Coastguard Worker t.Errorf("%s: expected `%s` got `%s`", testCase.name, testCase.in, got) 156*1fa6dee9SAndroid Build Coastguard Worker } 157*1fa6dee9SAndroid Build Coastguard Worker } 158*1fa6dee9SAndroid Build Coastguard Worker} 159*1fa6dee9SAndroid Build Coastguard Worker 160*1fa6dee9SAndroid Build Coastguard Workerfunc TestExternalShellEscapeIncludingSpaces(t *testing.T) { 161*1fa6dee9SAndroid Build Coastguard Worker if testing.Short() { 162*1fa6dee9SAndroid Build Coastguard Worker return 163*1fa6dee9SAndroid Build Coastguard Worker } 164*1fa6dee9SAndroid Build Coastguard Worker for _, testCase := range shellEscapeIncludingSpacesTestCase { 165*1fa6dee9SAndroid Build Coastguard Worker cmd := "echo " + ShellEscapeIncludingSpaces(testCase.in) 166*1fa6dee9SAndroid Build Coastguard Worker got, err := exec.Command("/bin/sh", "-c", cmd).Output() 167*1fa6dee9SAndroid Build Coastguard Worker got = bytes.TrimSuffix(got, []byte("\n")) 168*1fa6dee9SAndroid Build Coastguard Worker if err != nil { 169*1fa6dee9SAndroid Build Coastguard Worker t.Error(err) 170*1fa6dee9SAndroid Build Coastguard Worker } 171*1fa6dee9SAndroid Build Coastguard Worker if string(got) != testCase.in { 172*1fa6dee9SAndroid Build Coastguard Worker t.Errorf("%s: expected `%s` got `%s`", testCase.name, testCase.in, got) 173*1fa6dee9SAndroid Build Coastguard Worker } 174*1fa6dee9SAndroid Build Coastguard Worker } 175*1fa6dee9SAndroid Build Coastguard Worker} 176*1fa6dee9SAndroid Build Coastguard Worker 177*1fa6dee9SAndroid Build Coastguard Workerfunc TestNinjaEscapeList(t *testing.T) { 178*1fa6dee9SAndroid Build Coastguard Worker type testCase struct { 179*1fa6dee9SAndroid Build Coastguard Worker name string 180*1fa6dee9SAndroid Build Coastguard Worker in []string 181*1fa6dee9SAndroid Build Coastguard Worker ninjaEscaped []string 182*1fa6dee9SAndroid Build Coastguard Worker shellEscaped []string 183*1fa6dee9SAndroid Build Coastguard Worker ninjaAndShellEscaped []string 184*1fa6dee9SAndroid Build Coastguard Worker sameSlice bool 185*1fa6dee9SAndroid Build Coastguard Worker } 186*1fa6dee9SAndroid Build Coastguard Worker testCases := []testCase{ 187*1fa6dee9SAndroid Build Coastguard Worker { 188*1fa6dee9SAndroid Build Coastguard Worker name: "empty", 189*1fa6dee9SAndroid Build Coastguard Worker in: []string{}, 190*1fa6dee9SAndroid Build Coastguard Worker sameSlice: true, 191*1fa6dee9SAndroid Build Coastguard Worker }, 192*1fa6dee9SAndroid Build Coastguard Worker { 193*1fa6dee9SAndroid Build Coastguard Worker name: "nil", 194*1fa6dee9SAndroid Build Coastguard Worker in: nil, 195*1fa6dee9SAndroid Build Coastguard Worker sameSlice: true, 196*1fa6dee9SAndroid Build Coastguard Worker }, 197*1fa6dee9SAndroid Build Coastguard Worker { 198*1fa6dee9SAndroid Build Coastguard Worker name: "no escaping", 199*1fa6dee9SAndroid Build Coastguard Worker in: []string{"abc", "def", "ghi"}, 200*1fa6dee9SAndroid Build Coastguard Worker sameSlice: true, 201*1fa6dee9SAndroid Build Coastguard Worker }, 202*1fa6dee9SAndroid Build Coastguard Worker { 203*1fa6dee9SAndroid Build Coastguard Worker name: "escape first", 204*1fa6dee9SAndroid Build Coastguard Worker in: []string{`$\abc`, "def", "ghi"}, 205*1fa6dee9SAndroid Build Coastguard Worker ninjaEscaped: []string{`$$\abc`, "def", "ghi"}, 206*1fa6dee9SAndroid Build Coastguard Worker shellEscaped: []string{`'$\abc'`, "def", "ghi"}, 207*1fa6dee9SAndroid Build Coastguard Worker ninjaAndShellEscaped: []string{`'$$\abc'`, "def", "ghi"}, 208*1fa6dee9SAndroid Build Coastguard Worker }, 209*1fa6dee9SAndroid Build Coastguard Worker { 210*1fa6dee9SAndroid Build Coastguard Worker name: "escape middle", 211*1fa6dee9SAndroid Build Coastguard Worker in: []string{"abc", `$\def`, "ghi"}, 212*1fa6dee9SAndroid Build Coastguard Worker ninjaEscaped: []string{"abc", `$$\def`, "ghi"}, 213*1fa6dee9SAndroid Build Coastguard Worker shellEscaped: []string{"abc", `'$\def'`, "ghi"}, 214*1fa6dee9SAndroid Build Coastguard Worker ninjaAndShellEscaped: []string{"abc", `'$$\def'`, "ghi"}, 215*1fa6dee9SAndroid Build Coastguard Worker }, 216*1fa6dee9SAndroid Build Coastguard Worker { 217*1fa6dee9SAndroid Build Coastguard Worker name: "escape last", 218*1fa6dee9SAndroid Build Coastguard Worker in: []string{"abc", "def", `$\ghi`}, 219*1fa6dee9SAndroid Build Coastguard Worker ninjaEscaped: []string{"abc", "def", `$$\ghi`}, 220*1fa6dee9SAndroid Build Coastguard Worker shellEscaped: []string{"abc", "def", `'$\ghi'`}, 221*1fa6dee9SAndroid Build Coastguard Worker ninjaAndShellEscaped: []string{"abc", "def", `'$$\ghi'`}, 222*1fa6dee9SAndroid Build Coastguard Worker }, 223*1fa6dee9SAndroid Build Coastguard Worker } 224*1fa6dee9SAndroid Build Coastguard Worker 225*1fa6dee9SAndroid Build Coastguard Worker testFuncs := []struct { 226*1fa6dee9SAndroid Build Coastguard Worker name string 227*1fa6dee9SAndroid Build Coastguard Worker f func([]string) []string 228*1fa6dee9SAndroid Build Coastguard Worker expected func(tt testCase) []string 229*1fa6dee9SAndroid Build Coastguard Worker }{ 230*1fa6dee9SAndroid Build Coastguard Worker {name: "NinjaEscapeList", f: NinjaEscapeList, expected: func(tt testCase) []string { return tt.ninjaEscaped }}, 231*1fa6dee9SAndroid Build Coastguard Worker {name: "ShellEscapeList", f: ShellEscapeList, expected: func(tt testCase) []string { return tt.shellEscaped }}, 232*1fa6dee9SAndroid Build Coastguard Worker {name: "NinjaAndShellEscapeList", f: NinjaAndShellEscapeList, expected: func(tt testCase) []string { return tt.ninjaAndShellEscaped }}, 233*1fa6dee9SAndroid Build Coastguard Worker } 234*1fa6dee9SAndroid Build Coastguard Worker 235*1fa6dee9SAndroid Build Coastguard Worker for _, tf := range testFuncs { 236*1fa6dee9SAndroid Build Coastguard Worker t.Run(tf.name, func(t *testing.T) { 237*1fa6dee9SAndroid Build Coastguard Worker for _, tt := range testCases { 238*1fa6dee9SAndroid Build Coastguard Worker t.Run(tt.name, func(t *testing.T) { 239*1fa6dee9SAndroid Build Coastguard Worker inCopy := slices.Clone(tt.in) 240*1fa6dee9SAndroid Build Coastguard Worker 241*1fa6dee9SAndroid Build Coastguard Worker got := tf.f(tt.in) 242*1fa6dee9SAndroid Build Coastguard Worker 243*1fa6dee9SAndroid Build Coastguard Worker want := tf.expected(tt) 244*1fa6dee9SAndroid Build Coastguard Worker if tt.sameSlice { 245*1fa6dee9SAndroid Build Coastguard Worker want = tt.in 246*1fa6dee9SAndroid Build Coastguard Worker } 247*1fa6dee9SAndroid Build Coastguard Worker 248*1fa6dee9SAndroid Build Coastguard Worker if !reflect.DeepEqual(got, want) { 249*1fa6dee9SAndroid Build Coastguard Worker t.Errorf("incorrect output, want %q got %q", want, got) 250*1fa6dee9SAndroid Build Coastguard Worker } 251*1fa6dee9SAndroid Build Coastguard Worker if len(inCopy) != len(tt.in) && (len(tt.in) == 0 || !reflect.DeepEqual(inCopy, tt.in)) { 252*1fa6dee9SAndroid Build Coastguard Worker t.Errorf("input modified, want %#v, got %#v", inCopy, tt.in) 253*1fa6dee9SAndroid Build Coastguard Worker } 254*1fa6dee9SAndroid Build Coastguard Worker 255*1fa6dee9SAndroid Build Coastguard Worker if (unsafe.SliceData(tt.in) == unsafe.SliceData(got)) != tt.sameSlice { 256*1fa6dee9SAndroid Build Coastguard Worker if tt.sameSlice { 257*1fa6dee9SAndroid Build Coastguard Worker t.Errorf("expected input and output slices to have the same backing arrays") 258*1fa6dee9SAndroid Build Coastguard Worker } else { 259*1fa6dee9SAndroid Build Coastguard Worker t.Errorf("expected input and output slices to have different backing arrays") 260*1fa6dee9SAndroid Build Coastguard Worker } 261*1fa6dee9SAndroid Build Coastguard Worker } 262*1fa6dee9SAndroid Build Coastguard Worker }) 263*1fa6dee9SAndroid Build Coastguard Worker } 264*1fa6dee9SAndroid Build Coastguard Worker }) 265*1fa6dee9SAndroid Build Coastguard Worker } 266*1fa6dee9SAndroid Build Coastguard Worker} 267