github.com/Prakhar-Agarwal-byte/moby@v0.0.0-20231027092010-a14e3e8ab87e/profiles/seccomp/seccomp_test.go (about) 1 //go:build linux 2 3 package seccomp // import "github.com/Prakhar-Agarwal-byte/moby/profiles/seccomp" 4 5 import ( 6 "encoding/json" 7 "os" 8 "strings" 9 "testing" 10 11 "github.com/opencontainers/runtime-spec/specs-go" 12 "gotest.tools/v3/assert" 13 ) 14 15 func TestLoadProfile(t *testing.T) { 16 f, err := os.ReadFile("fixtures/example.json") 17 if err != nil { 18 t.Fatal(err) 19 } 20 rs := createSpec() 21 p, err := LoadProfile(string(f), &rs) 22 if err != nil { 23 t.Fatal(err) 24 } 25 var expectedErrno uint = 12345 26 var expectedDefaultErrno uint = 1 27 expected := specs.LinuxSeccomp{ 28 DefaultAction: specs.ActErrno, 29 DefaultErrnoRet: &expectedDefaultErrno, 30 Syscalls: []specs.LinuxSyscall{ 31 { 32 Names: []string{"clone"}, 33 Action: specs.ActAllow, 34 Args: []specs.LinuxSeccompArg{{ 35 Index: 0, 36 Value: 2114060288, 37 ValueTwo: 0, 38 Op: specs.OpMaskedEqual, 39 }}, 40 }, 41 { 42 Names: []string{"open"}, 43 Action: specs.ActAllow, 44 Args: []specs.LinuxSeccompArg{}, 45 }, 46 { 47 Names: []string{"close"}, 48 Action: specs.ActAllow, 49 Args: []specs.LinuxSeccompArg{}, 50 }, 51 { 52 Names: []string{"syslog"}, 53 Action: specs.ActErrno, 54 ErrnoRet: &expectedErrno, 55 Args: []specs.LinuxSeccompArg{}, 56 }, 57 }, 58 } 59 60 assert.DeepEqual(t, expected, *p) 61 } 62 63 func TestLoadProfileWithDefaultErrnoRet(t *testing.T) { 64 profile := []byte(`{ 65 "defaultAction": "SCMP_ACT_ERRNO", 66 "defaultErrnoRet": 6 67 }`) 68 rs := createSpec() 69 p, err := LoadProfile(string(profile), &rs) 70 if err != nil { 71 t.Fatal(err) 72 } 73 74 expectedErrnoRet := uint(6) 75 expected := specs.LinuxSeccomp{ 76 DefaultAction: specs.ActErrno, 77 DefaultErrnoRet: &expectedErrnoRet, 78 } 79 80 assert.DeepEqual(t, expected, *p) 81 } 82 83 func TestLoadProfileWithListenerPath(t *testing.T) { 84 profile := []byte(`{ 85 "defaultAction": "SCMP_ACT_ERRNO", 86 "listenerPath": "/var/run/seccompaget.sock", 87 "listenerMetadata": "opaque-metadata" 88 }`) 89 rs := createSpec() 90 p, err := LoadProfile(string(profile), &rs) 91 if err != nil { 92 t.Fatal(err) 93 } 94 95 expected := specs.LinuxSeccomp{ 96 DefaultAction: specs.ActErrno, 97 ListenerPath: "/var/run/seccompaget.sock", 98 ListenerMetadata: "opaque-metadata", 99 } 100 101 assert.DeepEqual(t, expected, *p) 102 } 103 104 func TestLoadProfileWithFlag(t *testing.T) { 105 profile := `{"defaultAction": "SCMP_ACT_ERRNO", "flags": ["SECCOMP_FILTER_FLAG_SPEC_ALLOW", "SECCOMP_FILTER_FLAG_LOG"]}` 106 expected := specs.LinuxSeccomp{ 107 DefaultAction: specs.ActErrno, 108 Flags: []specs.LinuxSeccompFlag{"SECCOMP_FILTER_FLAG_SPEC_ALLOW", "SECCOMP_FILTER_FLAG_LOG"}, 109 } 110 rs := createSpec() 111 p, err := LoadProfile(profile, &rs) 112 assert.NilError(t, err) 113 assert.DeepEqual(t, expected, *p) 114 } 115 116 // TestLoadProfileValidation tests that invalid profiles produce the correct error. 117 func TestLoadProfileValidation(t *testing.T) { 118 tests := []struct { 119 doc string 120 profile string 121 expected string 122 }{ 123 { 124 doc: "conflicting architectures and archMap", 125 profile: `{"defaultAction": "SCMP_ACT_ERRNO", "architectures": ["A", "B", "C"], "archMap": [{"architecture": "A", "subArchitectures": ["B", "C"]}]}`, 126 expected: `use either 'architectures' or 'archMap'`, 127 }, 128 { 129 doc: "conflicting syscall.name and syscall.names", 130 profile: `{"defaultAction": "SCMP_ACT_ERRNO", "syscalls": [{"name": "accept", "names": ["accept"], "action": "SCMP_ACT_ALLOW"}]}`, 131 expected: `use either 'name' or 'names'`, 132 }, 133 } 134 for _, tc := range tests { 135 tc := tc 136 rs := createSpec() 137 t.Run(tc.doc, func(t *testing.T) { 138 _, err := LoadProfile(tc.profile, &rs) 139 assert.ErrorContains(t, err, tc.expected) 140 }) 141 } 142 } 143 144 // TestLoadLegacyProfile tests loading a seccomp profile in the old format 145 // (before https://github.com/Prakhar-Agarwal-byte/moby/pull/24510) 146 func TestLoadLegacyProfile(t *testing.T) { 147 f, err := os.ReadFile("fixtures/default-old-format.json") 148 if err != nil { 149 t.Fatal(err) 150 } 151 rs := createSpec() 152 p, err := LoadProfile(string(f), &rs) 153 assert.NilError(t, err) 154 assert.Equal(t, p.DefaultAction, specs.ActErrno) 155 assert.DeepEqual(t, p.Architectures, []specs.Arch{"SCMP_ARCH_X86_64", "SCMP_ARCH_X86", "SCMP_ARCH_X32"}) 156 assert.Equal(t, len(p.Syscalls), 311) 157 expected := specs.LinuxSyscall{ 158 Names: []string{"accept"}, 159 Action: specs.ActAllow, 160 Args: []specs.LinuxSeccompArg{}, 161 } 162 assert.DeepEqual(t, p.Syscalls[0], expected) 163 } 164 165 func TestLoadDefaultProfile(t *testing.T) { 166 f, err := os.ReadFile("default.json") 167 if err != nil { 168 t.Fatal(err) 169 } 170 rs := createSpec() 171 if _, err := LoadProfile(string(f), &rs); err != nil { 172 t.Fatal(err) 173 } 174 } 175 176 func TestUnmarshalDefaultProfile(t *testing.T) { 177 expected := DefaultProfile() 178 if expected == nil { 179 t.Skip("seccomp not supported") 180 } 181 182 f, err := os.ReadFile("default.json") 183 if err != nil { 184 t.Fatal(err) 185 } 186 var profile Seccomp 187 err = json.Unmarshal(f, &profile) 188 if err != nil { 189 t.Fatal(err) 190 } 191 assert.DeepEqual(t, expected.Architectures, profile.Architectures) 192 assert.DeepEqual(t, expected.ArchMap, profile.ArchMap) 193 assert.DeepEqual(t, expected.DefaultAction, profile.DefaultAction) 194 assert.DeepEqual(t, expected.Syscalls, profile.Syscalls) 195 } 196 197 func TestMarshalUnmarshalFilter(t *testing.T) { 198 t.Parallel() 199 tests := []struct { 200 in string 201 out string 202 error bool 203 }{ 204 {in: `{"arches":["s390x"],"minKernel":3}`, error: true}, 205 {in: `{"arches":["s390x"],"minKernel":3.12}`, error: true}, 206 {in: `{"arches":["s390x"],"minKernel":true}`, error: true}, 207 {in: `{"arches":["s390x"],"minKernel":"0.0"}`, error: true}, 208 {in: `{"arches":["s390x"],"minKernel":"3"}`, error: true}, 209 {in: `{"arches":["s390x"],"minKernel":".3"}`, error: true}, 210 {in: `{"arches":["s390x"],"minKernel":"3."}`, error: true}, 211 {in: `{"arches":["s390x"],"minKernel":"true"}`, error: true}, 212 {in: `{"arches":["s390x"],"minKernel":"3.12.1\""}`, error: true}, 213 {in: `{"arches":["s390x"],"minKernel":"4.15abc"}`, error: true}, 214 {in: `{"arches":["s390x"],"minKernel":null}`, out: `{"arches":["s390x"]}`}, 215 {in: `{"arches":["s390x"],"minKernel":""}`, out: `{"arches":["s390x"],"minKernel":""}`}, // FIXME: try to fix omitempty for this 216 {in: `{"arches":["s390x"],"minKernel":"0.5"}`, out: `{"arches":["s390x"],"minKernel":"0.5"}`}, 217 {in: `{"arches":["s390x"],"minKernel":"0.50"}`, out: `{"arches":["s390x"],"minKernel":"0.50"}`}, 218 {in: `{"arches":["s390x"],"minKernel":"5.0"}`, out: `{"arches":["s390x"],"minKernel":"5.0"}`}, 219 {in: `{"arches":["s390x"],"minKernel":"50.0"}`, out: `{"arches":["s390x"],"minKernel":"50.0"}`}, 220 {in: `{"arches":["s390x"],"minKernel":"4.15"}`, out: `{"arches":["s390x"],"minKernel":"4.15"}`}, 221 } 222 for _, tc := range tests { 223 tc := tc 224 t.Run(tc.in, func(t *testing.T) { 225 var filter Filter 226 err := json.Unmarshal([]byte(tc.in), &filter) 227 if tc.error { 228 if err == nil { 229 t.Fatal("expected an error") 230 } else if !strings.Contains(err.Error(), "invalid kernel version") { 231 t.Fatal("unexpected error:", err) 232 } 233 return 234 } 235 if err != nil { 236 t.Fatal(err) 237 } 238 out, err := json.Marshal(filter) 239 if err != nil { 240 t.Fatal(err) 241 } 242 if string(out) != tc.out { 243 t.Fatalf("expected %s, got %s", tc.out, string(out)) 244 } 245 }) 246 } 247 } 248 249 func TestLoadConditional(t *testing.T) { 250 f, err := os.ReadFile("fixtures/conditional_include.json") 251 if err != nil { 252 t.Fatal(err) 253 } 254 tests := []struct { 255 doc string 256 cap string 257 expected []string 258 }{ 259 {doc: "no caps", expected: []string{"chmod", "ptrace"}}, 260 {doc: "with syslog", cap: "CAP_SYSLOG", expected: []string{"chmod", "syslog", "ptrace"}}, 261 {doc: "no ptrace", cap: "CAP_SYS_ADMIN", expected: []string{"chmod"}}, 262 } 263 264 for _, tc := range tests { 265 tc := tc 266 t.Run(tc.doc, func(t *testing.T) { 267 rs := createSpec(tc.cap) 268 p, err := LoadProfile(string(f), &rs) 269 if err != nil { 270 t.Fatal(err) 271 } 272 if len(p.Syscalls) != len(tc.expected) { 273 t.Fatalf("expected %d syscalls in profile, have %d", len(tc.expected), len(p.Syscalls)) 274 } 275 for i, v := range p.Syscalls { 276 if v.Names[0] != tc.expected[i] { 277 t.Fatalf("expected %s syscall, have %s", tc.expected[i], v.Names[0]) 278 } 279 } 280 }) 281 } 282 } 283 284 // createSpec() creates a minimum spec for testing 285 func createSpec(caps ...string) specs.Spec { 286 rs := specs.Spec{ 287 Process: &specs.Process{ 288 Capabilities: &specs.LinuxCapabilities{}, 289 }, 290 } 291 if caps != nil { 292 rs.Process.Capabilities.Bounding = append(rs.Process.Capabilities.Bounding, caps...) 293 } 294 return rs 295 }