xref: /aosp_15_r20/external/minijail/tools/seccomp_policy_lint_unittest.py (revision 4b9c6d91573e8b3a96609339b46361b5476dd0f9)
1*4b9c6d91SCole Faust#!/usr/bin/env python3
2*4b9c6d91SCole Faust#
3*4b9c6d91SCole Faust# Copyright (C) 2021 The Android Open Source Project
4*4b9c6d91SCole Faust#
5*4b9c6d91SCole Faust# Licensed under the Apache License, Version 2.0 (the "License");
6*4b9c6d91SCole Faust# you may not use this file except in compliance with the License.
7*4b9c6d91SCole Faust# You may obtain a copy of the License at
8*4b9c6d91SCole Faust#
9*4b9c6d91SCole Faust#      http://www.apache.org/licenses/LICENSE-2.0
10*4b9c6d91SCole Faust#
11*4b9c6d91SCole Faust# Unless required by applicable law or agreed to in writing, software
12*4b9c6d91SCole Faust# distributed under the License is distributed on an "AS IS" BASIS,
13*4b9c6d91SCole Faust# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14*4b9c6d91SCole Faust# See the License for the specific language governing permissions and
15*4b9c6d91SCole Faust# limitations under the License.
16*4b9c6d91SCole Faust"""Unittests for the seccomp policy linter module."""
17*4b9c6d91SCole Faust
18*4b9c6d91SCole Faustfrom pathlib import Path
19*4b9c6d91SCole Faustimport tempfile
20*4b9c6d91SCole Faustimport unittest
21*4b9c6d91SCole Faust
22*4b9c6d91SCole Faustimport seccomp_policy_lint
23*4b9c6d91SCole Faust
24*4b9c6d91SCole Faustclass CheckSeccompPolicyTests(unittest.TestCase):
25*4b9c6d91SCole Faust    """Tests for check_seccomp_policy."""
26*4b9c6d91SCole Faust
27*4b9c6d91SCole Faust    def setUp(self):
28*4b9c6d91SCole Faust        self.tempdir = Path(tempfile.mkdtemp())
29*4b9c6d91SCole Faust
30*4b9c6d91SCole Faust    def _write_file(self, filename, contents):
31*4b9c6d91SCole Faust        """Helper to write out a file for testing."""
32*4b9c6d91SCole Faust        path = self.tempdir / filename
33*4b9c6d91SCole Faust        path.write_text(contents)
34*4b9c6d91SCole Faust        return path
35*4b9c6d91SCole Faust
36*4b9c6d91SCole Faust    def test_check_simple(self):
37*4b9c6d91SCole Faust        """Allow simple policy files."""
38*4b9c6d91SCole Faust        path = self._write_file(
39*4b9c6d91SCole Faust            'test.policy', """
40*4b9c6d91SCole Faust            # Comment.\n
41*4b9c6d91SCole Faust            read: 1\n
42*4b9c6d91SCole Faust            write: 1\n
43*4b9c6d91SCole Faust        """)
44*4b9c6d91SCole Faust
45*4b9c6d91SCole Faust        exp_out = seccomp_policy_lint.CheckPolicyReturn(
46*4b9c6d91SCole Faust                    f'seccomp: {path.resolve()} does not contain any dangerous'
47*4b9c6d91SCole Faust                    ' syscalls, so does not require review from'
48*4b9c6d91SCole Faust                    ' chromeos-security@',
49*4b9c6d91SCole Faust                    [])
50*4b9c6d91SCole Faust
51*4b9c6d91SCole Faust        with path.open('r', encoding='utf-8') as check_file:
52*4b9c6d91SCole Faust            self.assertEqual(seccomp_policy_lint.check_seccomp_policy(
53*4b9c6d91SCole Faust                    check_file, seccomp_policy_lint.DANGEROUS_SYSCALLS),
54*4b9c6d91SCole Faust                    exp_out)
55*4b9c6d91SCole Faust
56*4b9c6d91SCole Faust    def test_check_dangerous_comment(self):
57*4b9c6d91SCole Faust        """Dangerous syscalls must have a comment and need to be reviewed."""
58*4b9c6d91SCole Faust        path = self._write_file(
59*4b9c6d91SCole Faust            'test.policy', """
60*4b9c6d91SCole Faust            # Comment.\n\n\n
61*4b9c6d91SCole Faust            clone: 1\n
62*4b9c6d91SCole Faust            write: 1\n
63*4b9c6d91SCole Faust        """)
64*4b9c6d91SCole Faust
65*4b9c6d91SCole Faust        exp_out = seccomp_policy_lint.CheckPolicyReturn(
66*4b9c6d91SCole Faust                    f'seccomp: {path.resolve()} contains dangerous syscalls,'
67*4b9c6d91SCole Faust                    ' so requires review from chromeos-security@',
68*4b9c6d91SCole Faust                    [])
69*4b9c6d91SCole Faust
70*4b9c6d91SCole Faust        with path.open('r', encoding='utf-8') as check_file:
71*4b9c6d91SCole Faust            self.assertEqual(seccomp_policy_lint.check_seccomp_policy(
72*4b9c6d91SCole Faust                    check_file, seccomp_policy_lint.DANGEROUS_SYSCALLS),
73*4b9c6d91SCole Faust                    exp_out)
74*4b9c6d91SCole Faust
75*4b9c6d91SCole Faust    def test_check_dangerous_no_comment(self):
76*4b9c6d91SCole Faust        """Dangerous syscalls without a comment should cause an error."""
77*4b9c6d91SCole Faust        path = self._write_file(
78*4b9c6d91SCole Faust            'test.policy', """
79*4b9c6d91SCole Faust            # Comment.\n
80*4b9c6d91SCole Faust            mount: 1\n
81*4b9c6d91SCole Faust            clone: 1\n
82*4b9c6d91SCole Faust        """)
83*4b9c6d91SCole Faust
84*4b9c6d91SCole Faust        exp_out = seccomp_policy_lint.CheckPolicyReturn(
85*4b9c6d91SCole Faust                    f'seccomp: {path.resolve()} contains dangerous syscalls,'
86*4b9c6d91SCole Faust                    ' so requires review from chromeos-security@',
87*4b9c6d91SCole Faust                   [(f'{path.resolve()}, line 5: clone syscall is a dangerous '
88*4b9c6d91SCole Faust                   'syscall so requires a comment on the preceding line')])
89*4b9c6d91SCole Faust
90*4b9c6d91SCole Faust        with path.open('r', encoding='utf-8') as check_file:
91*4b9c6d91SCole Faust            self.assertEqual(seccomp_policy_lint.check_seccomp_policy(
92*4b9c6d91SCole Faust                    check_file, seccomp_policy_lint.DANGEROUS_SYSCALLS),
93*4b9c6d91SCole Faust                    exp_out)
94*4b9c6d91SCole Faust
95*4b9c6d91SCole Faust    def test_check_duplicate_syscall(self):
96*4b9c6d91SCole Faust        """Policy files cannot have duplicate syscalls.."""
97*4b9c6d91SCole Faust        path = self._write_file(
98*4b9c6d91SCole Faust            'test.policy', """
99*4b9c6d91SCole Faust            # Comment.\n
100*4b9c6d91SCole Faust            clone: 1\n
101*4b9c6d91SCole Faust            clone: arg0 == 3
102*4b9c6d91SCole Faust        """)
103*4b9c6d91SCole Faust
104*4b9c6d91SCole Faust        exp_out = seccomp_policy_lint.CheckPolicyReturn(
105*4b9c6d91SCole Faust                    f'seccomp: {path.resolve()} contains dangerous syscalls,'
106*4b9c6d91SCole Faust                    ' so requires review from chromeos-security@',
107*4b9c6d91SCole Faust                   [(f'{path.resolve()}, line 5: repeat syscall: clone')])
108*4b9c6d91SCole Faust
109*4b9c6d91SCole Faust        with path.open('r', encoding='utf-8') as check_file:
110*4b9c6d91SCole Faust            self.assertEqual(seccomp_policy_lint.check_seccomp_policy(
111*4b9c6d91SCole Faust                    check_file, seccomp_policy_lint.DANGEROUS_SYSCALLS),
112*4b9c6d91SCole Faust                    exp_out)
113*4b9c6d91SCole Faust
114*4b9c6d91SCole Faust
115*4b9c6d91SCole Faustif __name__ == '__main__':
116*4b9c6d91SCole Faust    unittest.main()
117