def test_parse_filter_statement(self): """Accept valid filter statements.""" self.assertEqual( self.parser.parse_filter_statement( self._tokenize('read: arg0 == 0')), parser.ParsedFilterStatement((parser.Syscall('read', 0), ), [ parser.Filter([[parser.Atom(0, '==', 0)]], bpf.Allow()), ])) self.assertEqual( self.parser.parse_filter_statement( self._tokenize('{read, write}: arg0 == 0')), parser.ParsedFilterStatement(( parser.Syscall('read', 0), parser.Syscall('write', 1), ), [ parser.Filter([[parser.Atom(0, '==', 0)]], bpf.Allow()), ])) self.assertEqual( self.parser.parse_filter_statement( self._tokenize('io@libc: arg0 == 0')), parser.ParsedFilterStatement(( parser.Syscall('read', 0), parser.Syscall('write', 1), ), [ parser.Filter([[parser.Atom(0, '==', 0)]], bpf.Allow()), ])) self.assertEqual( self.parser.parse_filter_statement( self._tokenize('file-io@systemd: arg0 == 0')), parser.ParsedFilterStatement(( parser.Syscall('read', 0), parser.Syscall('write', 1), ), [ parser.Filter([[parser.Atom(0, '==', 0)]], bpf.Allow()), ]))
def test_parse_metadata(self): """Accept valid filter statements with metadata.""" self.assertEqualIgnoringToken( self.parser.parse_filter_statement( self._tokenize('read[arch=test]: arg0 == 0')), parser.ParsedFilterStatement( syscalls=( parser.Syscall('read', 0), ), filters=[ parser.Filter([[parser.Atom(0, '==', 0)]], bpf.Allow()), ], token=None)) self.assertEqualIgnoringToken( self.parser.parse_filter_statement( self._tokenize( '{read, nonexistent[arch=nonexistent]}: arg0 == 0')), parser.ParsedFilterStatement( syscalls=( parser.Syscall('read', 0), ), filters=[ parser.Filter([[parser.Atom(0, '==', 0)]], bpf.Allow()), ], token=None))
def test_parse_filter_statement(self): """Accept valid filter statements.""" self.assertEqualIgnoringToken( self.parser.parse_filter_statement( self._tokenize('read: arg0 == 0')), parser.ParsedFilterStatement( syscalls=(parser.Syscall('read', 0), ), filters=[ parser.Filter([[parser.Atom(0, '==', 0)]], bpf.Allow()), ], token=None)) self.assertEqualIgnoringToken( self.parser.parse_filter_statement( self._tokenize('{read, write}: arg0 == 0')), parser.ParsedFilterStatement(syscalls=( parser.Syscall('read', 0), parser.Syscall('write', 1), ), filters=[ parser.Filter( [[parser.Atom(0, '==', 0)]], bpf.Allow()), ], token=None)) self.assertEqualIgnoringToken( self.parser.parse_filter_statement( self._tokenize('io@libc: arg0 == 0')), parser.ParsedFilterStatement(syscalls=( parser.Syscall('read', 0), parser.Syscall('write', 1), ), filters=[ parser.Filter( [[parser.Atom(0, '==', 0)]], bpf.Allow()), ], token=None)) self.assertEqualIgnoringToken( self.parser.parse_filter_statement( self._tokenize('file-io@systemd: arg0 == 0')), parser.ParsedFilterStatement(syscalls=( parser.Syscall('read', 0), parser.Syscall('write', 1), ), filters=[ parser.Filter( [[parser.Atom(0, '==', 0)]], bpf.Allow()), ], token=None)) self.assertEqualIgnoringToken( self.parser.parse_filter_statement( self._tokenize('kill: arg0 == 0')), parser.ParsedFilterStatement( syscalls=(parser.Syscall('kill', 62), ), filters=[ parser.Filter([[parser.Atom(0, '==', 0)]], bpf.Allow()), ], token=None))
def test_parse_argument_expression(self): """Accept valid argument expressions.""" self.assertEqual( self.parser.parse_argument_expression( self._tokenize( 'arg0 in 0xffff || arg0 == PROT_EXEC && arg1 == PROT_WRITE' )), [ [parser.Atom(0, 'in', 0xffff)], [parser.Atom(0, '==', 4), parser.Atom(1, '==', 2)], ])
def test_parse_filter(self): """Accept valid filters.""" self.assertEqual( self.parser.parse_filter(self._tokenize('arg0 == 0')), [ parser.Filter([[parser.Atom(0, '==', 0)]], bpf.Allow()), ]) self.assertEqual( self.parser.parse_filter(self._tokenize('kill-process')), [ parser.Filter(None, bpf.KillProcess()), ]) self.assertEqual( self.parser.parse_filter(self._tokenize('kill-thread')), [ parser.Filter(None, bpf.KillThread()), ]) self.assertEqual( self.parser.parse_filter(self._tokenize('trap')), [ parser.Filter(None, bpf.Trap()), ]) self.assertEqual( self.parser.parse_filter(self._tokenize('return ENOSYS')), [ parser.Filter(None, bpf.ReturnErrno(self.arch.constants['ENOSYS'])), ]) self.assertEqual( self.parser.parse_filter(self._tokenize('trace')), [ parser.Filter(None, bpf.Trace()), ]) self.assertEqual( self.parser.parse_filter(self._tokenize('user-notify')), [ parser.Filter(None, bpf.UserNotify()), ]) self.assertEqual( self.parser.parse_filter(self._tokenize('log')), [ parser.Filter(None, bpf.Log()), ]) self.assertEqual( self.parser.parse_filter(self._tokenize('allow')), [ parser.Filter(None, bpf.Allow()), ]) self.assertEqual( self.parser.parse_filter(self._tokenize('1')), [ parser.Filter(None, bpf.Allow()), ]) self.assertEqual( self.parser.parse_filter( self._tokenize( '{ arg0 == 0, arg0 == 1; return ENOSYS, trap }')), [ parser.Filter([[parser.Atom(0, '==', 0)]], bpf.Allow()), parser.Filter([[parser.Atom(0, '==', 1)]], bpf.ReturnErrno(self.arch.constants['ENOSYS'])), parser.Filter(None, bpf.Trap()), ])
def test_parse_simple_with_arg(self): """Allow simple denylist policy files.""" path = self._write_file( 'test.policy', """ # Comment. @denylist read: return ENOSYS write: arg0 == 0 ; return ENOSYS """) self.assertEqual( self.parser.parse_file(path), parser.ParsedPolicy( default_action=bpf.Allow(), filter_statements=[ parser.FilterStatement( syscall=parser.Syscall('read', 0), frequency=1, filters=[ parser.Filter( None, bpf.ReturnErrno( self.arch.constants['ENOSYS'])), ]), parser.FilterStatement( syscall=parser.Syscall('write', 1), frequency=1, filters=[ parser.Filter([[parser.Atom(0, '==', 0)]], bpf.ReturnErrno( self.arch.constants['ENOSYS'])), parser.Filter(None, bpf.Allow()), ]), ]))
def test_parse_filter(self): """Accept only filters that return an errno.""" self.assertEqual( self.parser.parse_filter( self._tokenize('arg0 == 0; return ENOSYS')), [ parser.Filter([[parser.Atom(0, '==', 0)]], bpf.ReturnErrno(self.arch.constants['ENOSYS'])), ])
def test_parse_number_argument_expression(self): """Accept valid argument expressions with any octal/decimal/hex number.""" # 4607 == 010777 == 0x11ff self.assertEqual( self.parser.parse_argument_expression( self._tokenize('arg0 in 4607')), [ [parser.Atom(0, 'in', 4607)], ]) self.assertEqual( self.parser.parse_argument_expression( self._tokenize('arg0 in 010777')), [ [parser.Atom(0, 'in', 4607)], ]) self.assertEqual( self.parser.parse_argument_expression( self._tokenize('arg0 in 0x11ff')), [ [parser.Atom(0, 'in', 4607)], ])
def test_parse_include(self): """Allow including policy files.""" path = self._write_file( 'test.include.policy', """ {read, write}: arg0 == 0; allow """) path = self._write_file( 'test.policy', """ @include ./test.include.policy read: return ENOSYS """) self.assertEqual( self.parser.parse_file(path), parser.ParsedPolicy( default_action=bpf.KillProcess(), filter_statements=[ parser.FilterStatement( syscall=parser.Syscall('read', 0), frequency=1, filters=[ parser.Filter([[parser.Atom(0, '==', 0)]], bpf.Allow()), parser.Filter( None, bpf.ReturnErrno( self.arch.constants['ENOSYS'])), ]), parser.FilterStatement( syscall=parser.Syscall('write', 1), frequency=1, filters=[ parser.Filter([[parser.Atom(0, '==', 0)]], bpf.Allow()), parser.Filter(None, bpf.KillProcess()), ]), ]))