| // +build linux |
| |
| package seccomp // import "github.com/docker/docker/profiles/seccomp" |
| |
| import ( |
| "encoding/json" |
| "io/ioutil" |
| "strings" |
| "testing" |
| |
| "github.com/opencontainers/runtime-spec/specs-go" |
| "gotest.tools/v3/assert" |
| ) |
| |
| func TestLoadProfile(t *testing.T) { |
| f, err := ioutil.ReadFile("fixtures/example.json") |
| if err != nil { |
| t.Fatal(err) |
| } |
| rs := createSpec() |
| if _, err := LoadProfile(string(f), &rs); err != nil { |
| t.Fatal(err) |
| } |
| } |
| |
| // TestLoadLegacyProfile tests loading a seccomp profile in the old format |
| // (before https://github.com/docker/docker/pull/24510) |
| func TestLoadLegacyProfile(t *testing.T) { |
| f, err := ioutil.ReadFile("fixtures/default-old-format.json") |
| if err != nil { |
| t.Fatal(err) |
| } |
| rs := createSpec() |
| if _, err := LoadProfile(string(f), &rs); err != nil { |
| t.Fatal(err) |
| } |
| } |
| |
| func TestLoadDefaultProfile(t *testing.T) { |
| f, err := ioutil.ReadFile("default.json") |
| if err != nil { |
| t.Fatal(err) |
| } |
| rs := createSpec() |
| if _, err := LoadProfile(string(f), &rs); err != nil { |
| t.Fatal(err) |
| } |
| } |
| |
| func TestUnmarshalDefaultProfile(t *testing.T) { |
| expected := DefaultProfile() |
| if expected == nil { |
| t.Skip("seccomp not supported") |
| } |
| |
| f, err := ioutil.ReadFile("default.json") |
| if err != nil { |
| t.Fatal(err) |
| } |
| var profile Seccomp |
| err = json.Unmarshal(f, &profile) |
| if err != nil { |
| t.Fatal(err) |
| } |
| assert.DeepEqual(t, expected.Architectures, profile.Architectures) |
| assert.DeepEqual(t, expected.ArchMap, profile.ArchMap) |
| assert.DeepEqual(t, expected.DefaultAction, profile.DefaultAction) |
| assert.DeepEqual(t, expected.Syscalls, profile.Syscalls) |
| } |
| |
| func TestMarshalUnmarshalFilter(t *testing.T) { |
| t.Parallel() |
| tests := []struct { |
| in string |
| out string |
| error bool |
| }{ |
| {in: `{"arches":["s390x"],"minKernel":3}`, error: true}, |
| {in: `{"arches":["s390x"],"minKernel":3.12}`, error: true}, |
| {in: `{"arches":["s390x"],"minKernel":true}`, error: true}, |
| {in: `{"arches":["s390x"],"minKernel":"0.0"}`, error: true}, |
| {in: `{"arches":["s390x"],"minKernel":"3"}`, error: true}, |
| {in: `{"arches":["s390x"],"minKernel":".3"}`, error: true}, |
| {in: `{"arches":["s390x"],"minKernel":"3."}`, error: true}, |
| {in: `{"arches":["s390x"],"minKernel":"true"}`, error: true}, |
| {in: `{"arches":["s390x"],"minKernel":"3.12.1\""}`, error: true}, |
| {in: `{"arches":["s390x"],"minKernel":"4.15abc"}`, error: true}, |
| {in: `{"arches":["s390x"],"minKernel":null}`, out: `{"arches":["s390x"]}`}, |
| {in: `{"arches":["s390x"],"minKernel":""}`, out: `{"arches":["s390x"],"minKernel":""}`}, // FIXME: try to fix omitempty for this |
| {in: `{"arches":["s390x"],"minKernel":"0.5"}`, out: `{"arches":["s390x"],"minKernel":"0.5"}`}, |
| {in: `{"arches":["s390x"],"minKernel":"0.50"}`, out: `{"arches":["s390x"],"minKernel":"0.50"}`}, |
| {in: `{"arches":["s390x"],"minKernel":"5.0"}`, out: `{"arches":["s390x"],"minKernel":"5.0"}`}, |
| {in: `{"arches":["s390x"],"minKernel":"50.0"}`, out: `{"arches":["s390x"],"minKernel":"50.0"}`}, |
| {in: `{"arches":["s390x"],"minKernel":"4.15"}`, out: `{"arches":["s390x"],"minKernel":"4.15"}`}, |
| } |
| for _, tc := range tests { |
| tc := tc |
| t.Run(tc.in, func(t *testing.T) { |
| var filter Filter |
| err := json.Unmarshal([]byte(tc.in), &filter) |
| if tc.error { |
| if err == nil { |
| t.Fatal("expected an error") |
| } else if !strings.Contains(err.Error(), "invalid kernel version") { |
| t.Fatal("unexpected error:", err) |
| } |
| return |
| } |
| if err != nil { |
| t.Fatal(err) |
| } |
| out, err := json.Marshal(filter) |
| if err != nil { |
| t.Fatal(err) |
| } |
| if string(out) != tc.out { |
| t.Fatalf("expected %s, got %s", tc.out, string(out)) |
| } |
| }) |
| } |
| } |
| |
| func TestLoadConditional(t *testing.T) { |
| f, err := ioutil.ReadFile("fixtures/conditional_include.json") |
| if err != nil { |
| t.Fatal(err) |
| } |
| tests := []struct { |
| doc string |
| cap string |
| expected []string |
| }{ |
| {doc: "no caps", expected: []string{"chmod", "ptrace"}}, |
| {doc: "with syslog", cap: "CAP_SYSLOG", expected: []string{"chmod", "syslog", "ptrace"}}, |
| {doc: "no ptrace", cap: "CAP_SYS_ADMIN", expected: []string{"chmod"}}, |
| } |
| |
| for _, tc := range tests { |
| tc := tc |
| t.Run(tc.doc, func(t *testing.T) { |
| rs := createSpec(tc.cap) |
| p, err := LoadProfile(string(f), &rs) |
| if err != nil { |
| t.Fatal(err) |
| } |
| if len(p.Syscalls) != len(tc.expected) { |
| t.Fatalf("expected %d syscalls in profile, have %d", len(tc.expected), len(p.Syscalls)) |
| } |
| for i, v := range p.Syscalls { |
| if v.Names[0] != tc.expected[i] { |
| t.Fatalf("expected %s syscall, have %s", tc.expected[i], v.Names[0]) |
| } |
| } |
| }) |
| } |
| } |
| |
| // createSpec() creates a minimum spec for testing |
| func createSpec(caps ...string) specs.Spec { |
| rs := specs.Spec{ |
| Process: &specs.Process{ |
| Capabilities: &specs.LinuxCapabilities{}, |
| }, |
| } |
| if caps != nil { |
| rs.Process.Capabilities.Bounding = append(rs.Process.Capabilities.Bounding, caps...) |
| } |
| return rs |
| } |