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  }