def test_parse_simple_grouped(self): """Allow simple policy files.""" path = self._write_file( 'test.policy', """ # Comment. {read, write}: allow """) 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(None, bpf.Allow()), ]), parser.FilterStatement( syscall=parser.Syscall('write', 1), frequency=1, filters=[ parser.Filter(None, bpf.Allow()), ]), ]))
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_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_frequency(self): """Allow including frequency files.""" self._write_file( 'test.frequency', """ read: 2 write: 3 """) path = self._write_file( 'test.policy', """ @frequency ./test.frequency read: allow """) self.assertEqual( self.parser.parse_file(path), parser.ParsedPolicy( default_action=bpf.KillProcess(), filter_statements=[ parser.FilterStatement( syscall=parser.Syscall('read', 0), frequency=2, filters=[ 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_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()), ]), ]))
def test_parse_default(self): """Allow defining a default action.""" path = self._write_file( 'test.policy', """ @default kill-thread read: allow """) self.assertEqual( self.parser.parse_file(path), parser.ParsedPolicy(default_action=bpf.KillThread(), filter_statements=[ parser.FilterStatement( syscall=parser.Syscall('read', 0), frequency=1, filters=[ parser.Filter(None, bpf.Allow()), ]), ]))
def test_parse_other_arch(self): """Allow entries that only target another architecture.""" path = self._write_file( 'test.policy', """ # Comment. read[arch=nonexistent]: allow write: allow """) self.assertEqual( self.parser.parse_file(path), parser.ParsedPolicy(default_action=bpf.KillProcess(), filter_statements=[ parser.FilterStatement( syscall=parser.Syscall('write', 1), frequency=1, filters=[ parser.Filter(None, bpf.Allow()), ]), ]))
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()), ])