github.com/tschmi5/nomad@v0.11.8/helper/funcs_test.go (about) 1 package helper 2 3 import ( 4 "fmt" 5 "path/filepath" 6 "reflect" 7 "sort" 8 "testing" 9 10 "github.com/stretchr/testify/require" 11 ) 12 13 func TestSliceStringIsSubset(t *testing.T) { 14 l := []string{"a", "b", "c"} 15 s := []string{"d"} 16 17 sub, offending := SliceStringIsSubset(l, l[:1]) 18 if !sub || len(offending) != 0 { 19 t.Fatalf("bad %v %v", sub, offending) 20 } 21 22 sub, offending = SliceStringIsSubset(l, s) 23 if sub || len(offending) == 0 || offending[0] != "d" { 24 t.Fatalf("bad %v %v", sub, offending) 25 } 26 } 27 28 func TestCompareSliceSetString(t *testing.T) { 29 cases := []struct { 30 A []string 31 B []string 32 Result bool 33 }{ 34 { 35 A: []string{}, 36 B: []string{}, 37 Result: true, 38 }, 39 { 40 A: []string{}, 41 B: []string{"a"}, 42 Result: false, 43 }, 44 { 45 A: []string{"a"}, 46 B: []string{"a"}, 47 Result: true, 48 }, 49 { 50 A: []string{"a"}, 51 B: []string{"b"}, 52 Result: false, 53 }, 54 { 55 A: []string{"a", "b"}, 56 B: []string{"b"}, 57 Result: false, 58 }, 59 { 60 A: []string{"a", "b"}, 61 B: []string{"a"}, 62 Result: false, 63 }, 64 { 65 A: []string{"a", "b"}, 66 B: []string{"a", "b"}, 67 Result: true, 68 }, 69 { 70 A: []string{"a", "b"}, 71 B: []string{"b", "a"}, 72 Result: true, 73 }, 74 } 75 76 for i, tc := range cases { 77 tc := tc 78 t.Run(fmt.Sprintf("case-%da", i), func(t *testing.T) { 79 if res := CompareSliceSetString(tc.A, tc.B); res != tc.Result { 80 t.Fatalf("expected %t but CompareSliceSetString(%v, %v) -> %t", 81 tc.Result, tc.A, tc.B, res, 82 ) 83 } 84 }) 85 86 // Function is commutative so compare B and A 87 t.Run(fmt.Sprintf("case-%db", i), func(t *testing.T) { 88 if res := CompareSliceSetString(tc.B, tc.A); res != tc.Result { 89 t.Fatalf("expected %t but CompareSliceSetString(%v, %v) -> %t", 90 tc.Result, tc.B, tc.A, res, 91 ) 92 } 93 }) 94 } 95 } 96 97 func TestMapStringStringSliceValueSet(t *testing.T) { 98 m := map[string][]string{ 99 "foo": {"1", "2"}, 100 "bar": {"3"}, 101 "baz": nil, 102 } 103 104 act := MapStringStringSliceValueSet(m) 105 exp := []string{"1", "2", "3"} 106 sort.Strings(act) 107 if !reflect.DeepEqual(act, exp) { 108 t.Fatalf("Bad; got %v; want %v", act, exp) 109 } 110 } 111 112 func TestCopyMapStringSliceString(t *testing.T) { 113 m := map[string][]string{ 114 "x": {"a", "b", "c"}, 115 "y": {"1", "2", "3"}, 116 "z": nil, 117 } 118 119 c := CopyMapStringSliceString(m) 120 if !reflect.DeepEqual(c, m) { 121 t.Fatalf("%#v != %#v", m, c) 122 } 123 124 c["x"][1] = "---" 125 if reflect.DeepEqual(c, m) { 126 t.Fatalf("Shared slices: %#v == %#v", m["x"], c["x"]) 127 } 128 } 129 130 func TestClearEnvVar(t *testing.T) { 131 type testCase struct { 132 input string 133 expected string 134 } 135 cases := []testCase{ 136 {"asdf", "asdf"}, 137 {"ASDF", "ASDF"}, 138 {"0sdf", "_sdf"}, 139 {"asd0", "asd0"}, 140 {"_asd", "_asd"}, 141 {"-asd", "_asd"}, 142 {"asd.fgh", "asd.fgh"}, 143 {"A~!@#$%^&*()_+-={}[]|\\;:'\"<,>?/Z", "A______________________________Z"}, 144 {"A\U0001f4a9Z", "A____Z"}, 145 } 146 for _, c := range cases { 147 if output := CleanEnvVar(c.input, '_'); output != c.expected { 148 t.Errorf("CleanEnvVar(%q, '_') -> %q != %q", c.input, output, c.expected) 149 } 150 } 151 } 152 153 func BenchmarkCleanEnvVar(b *testing.B) { 154 in := "NOMAD_ADDR_redis-cache" 155 replacement := byte('_') 156 b.SetBytes(int64(len(in))) 157 b.ReportAllocs() 158 b.ResetTimer() 159 for i := 0; i < b.N; i++ { 160 CleanEnvVar(in, replacement) 161 } 162 } 163 164 func TestPathEscapesSandbox(t *testing.T) { 165 cases := []struct { 166 name string 167 path string 168 dir string 169 expected bool 170 }{ 171 { 172 // this is the ${NOMAD_SECRETS_DIR} case 173 name: "ok joined absolute path inside sandbox", 174 path: filepath.Join("/alloc", "/secrets"), 175 dir: "/alloc", 176 expected: false, 177 }, 178 { 179 name: "fail unjoined absolute path outside sandbox", 180 path: "/secrets", 181 dir: "/alloc", 182 expected: true, 183 }, 184 { 185 name: "ok joined relative path inside sandbox", 186 path: filepath.Join("/alloc", "./safe"), 187 dir: "/alloc", 188 expected: false, 189 }, 190 { 191 name: "fail unjoined relative path outside sandbox", 192 path: "./safe", 193 dir: "/alloc", 194 expected: true, 195 }, 196 { 197 name: "ok relative path traversal constrained to sandbox", 198 path: filepath.Join("/alloc", "../../alloc/safe"), 199 dir: "/alloc", 200 expected: false, 201 }, 202 { 203 name: "ok unjoined absolute path traversal constrained to sandbox", 204 path: filepath.Join("/alloc", "/../alloc/safe"), 205 dir: "/alloc", 206 expected: false, 207 }, 208 { 209 name: "ok unjoined absolute path traversal constrained to sandbox", 210 path: "/../alloc/safe", 211 dir: "/alloc", 212 expected: false, 213 }, 214 { 215 name: "fail joined relative path traverses outside sandbox", 216 path: filepath.Join("/alloc", "../../../unsafe"), 217 dir: "/alloc", 218 expected: true, 219 }, 220 { 221 name: "fail unjoined relative path traverses outside sandbox", 222 path: "../../../unsafe", 223 dir: "/alloc", 224 expected: true, 225 }, 226 { 227 name: "fail joined absolute path tries to transverse outside sandbox", 228 path: filepath.Join("/alloc", "/alloc/../../unsafe"), 229 dir: "/alloc", 230 expected: true, 231 }, 232 { 233 name: "fail unjoined absolute path tries to transverse outside sandbox", 234 path: "/alloc/../../unsafe", 235 dir: "/alloc", 236 expected: true, 237 }, 238 } 239 240 for _, tc := range cases { 241 t.Run(tc.name, func(t *testing.T) { 242 caseMsg := fmt.Sprintf("path: %v\ndir: %v", tc.path, tc.dir) 243 escapes := PathEscapesSandbox(tc.dir, tc.path) 244 require.Equal(t, tc.expected, escapes, caseMsg) 245 }) 246 } 247 }