mirror of
https://gitlab.com/apparmor/apparmor
synced 2025-08-28 12:58:07 +00:00
Add support for --show-matching-path and xattrs
The new option --show-matching-path shows a path that matches in the host filesystem, to prove that the profile is indeed used. Also, profiles' xattrs are now parsed into a dict and are taken in consideration when looking for matching profiles. Signed-off-by: Maxime Bélair <maxime.belair@canonical.com>
This commit is contained in:
parent
db376c0458
commit
b46f7a426c
@ -30,26 +30,35 @@ _ = init_translation()
|
|||||||
MAX_RECURSION = 10
|
MAX_RECURSION = 10
|
||||||
|
|
||||||
|
|
||||||
def has_matching_file(pattern):
|
def has_matching_file(pattern, xattrs=None):
|
||||||
for pat in expand_braces(pattern):
|
for p in expand_braces(pattern):
|
||||||
if any(glob.iglob(pat, recursive=True)):
|
for path in glob.iglob(p, recursive=True):
|
||||||
return True
|
if os.path.realpath(path) != os.path.abspath(path): # remove symlinks
|
||||||
return False
|
continue
|
||||||
|
if not xattrs or all(os.getxattr(path, name) == val for name, val in xattrs.items()):
|
||||||
|
return path
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
def display_profile_text(used, unused):
|
def display_profile_text(used, unused, show_matching_path):
|
||||||
if used:
|
if used:
|
||||||
print(_('Used profiles:'))
|
print(_('Used profiles:'))
|
||||||
for (name, attach, path) in used:
|
for (name, attach, path, match) in used:
|
||||||
print(_(' Profile {} for {} ({})').format(name, attach, path))
|
print(_(' Profile {} for {} ({}) {}').format(name, attach, path, ('→ ' + match) if show_matching_path else ''))
|
||||||
if unused:
|
if unused:
|
||||||
print(_('Unused profiles:'))
|
print(_('Unused profiles:'))
|
||||||
for (name, attach, path) in unused:
|
for (name, attach, path, match) in unused:
|
||||||
print(_(' Profile {} for {} ({}) ').format(name, attach, path))
|
print(_(' Profile {} for {} ({}) ').format(name, attach, path))
|
||||||
|
|
||||||
|
|
||||||
def profiles_to_json(profiles):
|
def profiles_to_json(profiles):
|
||||||
return [{'name': profile_name, 'attach': attach, 'path': path} for profile_name, attach, path in profiles]
|
result = []
|
||||||
|
for profile_name, attach, path, matching_path in profiles:
|
||||||
|
entry = {'name': profile_name, 'attach': attach, 'path': path}
|
||||||
|
if matching_path:
|
||||||
|
entry['matching_path'] = matching_path
|
||||||
|
result.append(entry)
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
def display_profile_json(used, unused):
|
def display_profile_json(used, unused):
|
||||||
@ -90,15 +99,16 @@ def get_used_profiles(args, prof_filter):
|
|||||||
|
|
||||||
var_dict = aa.active_profiles.get_all_merged_variables(filename, aa.include_list_recursive(aa.active_profiles.files[filename], True))
|
var_dict = aa.active_profiles.get_all_merged_variables(filename, aa.include_list_recursive(aa.active_profiles.files[filename], True))
|
||||||
resolved = resolve_variables(a, var_dict)
|
resolved = resolve_variables(a, var_dict)
|
||||||
found = False
|
matching_path = None
|
||||||
for entry in resolved:
|
for entry in resolved:
|
||||||
if has_matching_file(entry):
|
matching_path = has_matching_file(entry)
|
||||||
found = True
|
if matching_path:
|
||||||
|
break
|
||||||
|
|
||||||
if found and args.show_type != 'unused':
|
if matching_path and args.show_type != 'unused':
|
||||||
used.append((profile_name, a, filename))
|
used.append((profile_name, a, filename, matching_path))
|
||||||
if not found and args.show_type != 'used':
|
if not matching_path and args.show_type != 'used':
|
||||||
unused.append((profile_name, a, filename))
|
unused.append((profile_name, a, filename, matching_path))
|
||||||
|
|
||||||
return used, unused
|
return used, unused
|
||||||
|
|
||||||
@ -108,6 +118,7 @@ def main():
|
|||||||
parser.add_argument('-s', '--show-type', type=str, default='all', choices=['all', 'used', 'unused'], help=_('Type of profiles to show'))
|
parser.add_argument('-s', '--show-type', type=str, default='all', choices=['all', 'used', 'unused'], help=_('Type of profiles to show'))
|
||||||
parser.add_argument('-j', '--json', action='store_true', help=_('Output in JSON'))
|
parser.add_argument('-j', '--json', action='store_true', help=_('Output in JSON'))
|
||||||
parser.add_argument('-d', '--dir', type=str, help=_('Path to profiles'))
|
parser.add_argument('-d', '--dir', type=str, help=_('Path to profiles'))
|
||||||
|
parser.add_argument('--show-matching-path', action='store_true', help=_('Show the path of a file matching the profile'))
|
||||||
parser.add_argument('--configdir', type=str, help=argparse.SUPPRESS)
|
parser.add_argument('--configdir', type=str, help=argparse.SUPPRESS)
|
||||||
|
|
||||||
filter_group = parser.add_argument_group(_('Filtering options'),
|
filter_group = parser.add_argument_group(_('Filtering options'),
|
||||||
@ -137,7 +148,7 @@ def main():
|
|||||||
if args.json:
|
if args.json:
|
||||||
display_profile_json(used, unused)
|
display_profile_json(used, unused)
|
||||||
else:
|
else:
|
||||||
display_profile_text(used, unused)
|
display_profile_text(used, unused, args.show_matching_path)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
@ -50,6 +50,10 @@ Output in JSON
|
|||||||
|
|
||||||
Path to profiles
|
Path to profiles
|
||||||
|
|
||||||
|
=item --show-matching-path
|
||||||
|
|
||||||
|
Show the path of a file matching the profile. Only the first matching path of an executable is shown (not the whole list).
|
||||||
|
|
||||||
=back
|
=back
|
||||||
|
|
||||||
=head1 FILTERING OPTIONS
|
=head1 FILTERING OPTIONS
|
||||||
|
@ -13,9 +13,10 @@
|
|||||||
#
|
#
|
||||||
# ----------------------------------------------------------------------
|
# ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
from types import NoneType
|
||||||
|
|
||||||
from apparmor.common import AppArmorBug, AppArmorException
|
from apparmor.common import AppArmorBug, AppArmorException
|
||||||
from apparmor.regex import parse_profile_start_line
|
from apparmor.regex import parse_profile_start_line, re_print_dict
|
||||||
from apparmor.rule import quote_if_needed
|
from apparmor.rule import quote_if_needed
|
||||||
from apparmor.rule.abi import AbiRule, AbiRuleset
|
from apparmor.rule.abi import AbiRule, AbiRuleset
|
||||||
from apparmor.rule.all import AllRule, AllRuleset
|
from apparmor.rule.all import AllRule, AllRuleset
|
||||||
@ -79,7 +80,7 @@ class ProfileStorage:
|
|||||||
data['parent'] = '' # parent profile, or '' for top-level profiles and external hats
|
data['parent'] = '' # parent profile, or '' for top-level profiles and external hats
|
||||||
data['name'] = ''
|
data['name'] = ''
|
||||||
data['attachment'] = ''
|
data['attachment'] = ''
|
||||||
data['xattrs'] = ''
|
data['xattrs'] = {}
|
||||||
data['flags'] = ''
|
data['flags'] = ''
|
||||||
data['external'] = False
|
data['external'] = False
|
||||||
data['header_comment'] = '' # comment in the profile/hat start line
|
data['header_comment'] = '' # comment in the profile/hat start line
|
||||||
@ -100,28 +101,16 @@ class ProfileStorage:
|
|||||||
if key not in self.data:
|
if key not in self.data:
|
||||||
raise AppArmorBug('attempt to set unknown key %s' % key)
|
raise AppArmorBug('attempt to set unknown key %s' % key)
|
||||||
|
|
||||||
# allow writing bool values
|
allowed_types = {bool, str, dict, None, NoneType}
|
||||||
if isinstance(self.data[key], bool):
|
old_type = type(self.data[key])
|
||||||
if isinstance(value, bool):
|
if old_type in allowed_types:
|
||||||
|
if key in {'flags', 'filename'} and type(value) in {str, NoneType}:
|
||||||
|
self.data[key] = value
|
||||||
|
elif isinstance(value, old_type):
|
||||||
self.data[key] = value
|
self.data[key] = value
|
||||||
else:
|
else:
|
||||||
raise AppArmorBug('Attempt to change type of "%s" from %s to %s, value %s' % (key, type(self.data[key]), type(value), value))
|
raise AppArmorBug('Attempt to change type of "%s" from %s to %s, value %s' % (key, old_type, type(value), value))
|
||||||
|
|
||||||
# allow writing str or None to some keys
|
|
||||||
elif key in ('flags', 'filename'):
|
|
||||||
if isinstance(value, str) or value is None:
|
|
||||||
self.data[key] = value
|
self.data[key] = value
|
||||||
else:
|
|
||||||
raise AppArmorBug('Attempt to change type of "%s" from %s to %s, value %s' % (key, type(self.data[key]), type(value), value))
|
|
||||||
|
|
||||||
# allow writing str values
|
|
||||||
elif isinstance(self.data[key], str):
|
|
||||||
if isinstance(value, str):
|
|
||||||
self.data[key] = value
|
|
||||||
else:
|
|
||||||
raise AppArmorBug('Attempt to change type of "%s" from %s to %s, value %s' % (key, type(self.data[key]), type(value), value))
|
|
||||||
|
|
||||||
# don't allow overwriting of other types
|
|
||||||
else:
|
else:
|
||||||
raise AppArmorBug('Attempt to overwrite "%s" with %s, type %s' % (key, value, type(value)))
|
raise AppArmorBug('Attempt to overwrite "%s" with %s, type %s' % (key, value, type(value)))
|
||||||
|
|
||||||
@ -168,7 +157,7 @@ class ProfileStorage:
|
|||||||
|
|
||||||
xattrs = ''
|
xattrs = ''
|
||||||
if self.data['xattrs']:
|
if self.data['xattrs']:
|
||||||
xattrs = ' xattrs=(%s)' % self.data['xattrs']
|
xattrs = ' xattrs=(%s)' % re_print_dict(self.data['xattrs'])
|
||||||
|
|
||||||
flags = ''
|
flags = ''
|
||||||
if self.data['flags']:
|
if self.data['flags']:
|
||||||
@ -263,7 +252,7 @@ class ProfileStorage:
|
|||||||
else:
|
else:
|
||||||
prof_storage['profile_keyword'] = matches['profile_keyword']
|
prof_storage['profile_keyword'] = matches['profile_keyword']
|
||||||
prof_storage['attachment'] = matches['attachment'] or ''
|
prof_storage['attachment'] = matches['attachment'] or ''
|
||||||
prof_storage['xattrs'] = matches['xattrs'] or ''
|
prof_storage['xattrs'] = matches['xattrs'] or {}
|
||||||
|
|
||||||
return (profile, hat, prof_storage)
|
return (profile, hat, prof_storage)
|
||||||
|
|
||||||
|
@ -29,10 +29,11 @@ RE_COMMA_EOL = r'\s*,' + RE_EOL # optional whitespace, comma + RE_EOL
|
|||||||
RE_PROFILE_NAME = r'(?P<%s>(\S+|"[^"]+"))' # string without spaces, or quoted string. %s is the match group name
|
RE_PROFILE_NAME = r'(?P<%s>(\S+|"[^"]+"))' # string without spaces, or quoted string. %s is the match group name
|
||||||
RE_PATH = r'/\S*|"/[^"]*"' # filename (starting with '/') without spaces, or quoted filename.
|
RE_PATH = r'/\S*|"/[^"]*"' # filename (starting with '/') without spaces, or quoted filename.
|
||||||
RE_VAR = r'@{[^}\s]+}'
|
RE_VAR = r'@{[^}\s]+}'
|
||||||
|
RE_DICT_ENTRY = r'\s*(?P<key>[^,\s=]+)(?:=(?P<value>[^,\s=]+))?\s*'
|
||||||
RE_PROFILE_PATH = '(?P<%s>(' + RE_PATH + '))' # quoted or unquoted filename. %s is the match group name
|
RE_PROFILE_PATH = '(?P<%s>(' + RE_PATH + '))' # quoted or unquoted filename. %s is the match group name
|
||||||
RE_PROFILE_PATH_OR_VAR = '(?P<%s>(' + RE_PATH + '|' + RE_VAR + r'\S*|"' + RE_VAR + '[^"]*"))' # quoted or unquoted filename or variable. %s is the match group name
|
RE_PROFILE_PATH_OR_VAR = '(?P<%s>(' + RE_PATH + '|' + RE_VAR + r'\S*|"' + RE_VAR + '[^"]*"))' # quoted or unquoted filename or variable. %s is the match group name
|
||||||
RE_SAFE_OR_UNSAFE = '(?P<execmode>(safe|unsafe))'
|
RE_SAFE_OR_UNSAFE = '(?P<execmode>(safe|unsafe))'
|
||||||
RE_XATTRS = r'(\s+xattrs\s*=\s*\((?P<xattrs>([^)=]+(=[^)=]+)?\s?)+)\)\s*)?'
|
RE_XATTRS = r'(\s+xattrs\s*=\s*\((?P<xattrs>([^)=]+(=[^)=]+)?\s?)*)\)\s*)?'
|
||||||
RE_FLAGS = r'(\s+(flags\s*=\s*)?\((?P<flags>[^)]+)\))?'
|
RE_FLAGS = r'(\s+(flags\s*=\s*)?\((?P<flags>[^)]+)\))?'
|
||||||
|
|
||||||
RE_VARIABLE = re.compile(RE_VAR)
|
RE_VARIABLE = re.compile(RE_VAR)
|
||||||
@ -166,6 +167,10 @@ def parse_profile_start_line(line, filename):
|
|||||||
else:
|
else:
|
||||||
result['profile'] = result['namedprofile']
|
result['profile'] = result['namedprofile']
|
||||||
result['profile_keyword'] = True
|
result['profile_keyword'] = True
|
||||||
|
if 'xattrs' in result:
|
||||||
|
result['xattrs'] = re_parse_dict(result['xattrs'])
|
||||||
|
else:
|
||||||
|
result['xattrs'] = {}
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
@ -239,6 +244,30 @@ def re_match_include(line):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def re_parse_dict(raw):
|
||||||
|
"""returns a dict where entries are comma or space separated"""
|
||||||
|
result = {}
|
||||||
|
if not raw:
|
||||||
|
return result
|
||||||
|
|
||||||
|
for key, value in re.findall(RE_DICT_ENTRY, raw):
|
||||||
|
if value == '':
|
||||||
|
value = None
|
||||||
|
result[key] = value
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def re_print_dict(d):
|
||||||
|
parts = []
|
||||||
|
for k, v in sorted(d.items()):
|
||||||
|
if v:
|
||||||
|
parts.append("{}={}".format(k, v))
|
||||||
|
else:
|
||||||
|
parts.append(k)
|
||||||
|
return " ".join(parts)
|
||||||
|
|
||||||
|
|
||||||
def strip_parenthesis(data):
|
def strip_parenthesis(data):
|
||||||
"""strips parenthesis from the given string and returns the strip()ped result.
|
"""strips parenthesis from the given string and returns the strip()ped result.
|
||||||
The parenthesis must be the first and last char, otherwise they won't be removed.
|
The parenthesis must be the first and last char, otherwise they won't be removed.
|
||||||
|
@ -28,7 +28,8 @@ class AAShowUsageTest(AATest):
|
|||||||
|
|
||||||
expected_output_1 = \
|
expected_output_1 = \
|
||||||
'''usage: aa-show-usage [-h] [-s {all,used,unused}] [-j] [-d DIR]
|
'''usage: aa-show-usage [-h] [-s {all,used,unused}] [-j] [-d DIR]
|
||||||
[--filter.flags FLAGS] [--filter.profile_name PROFILE_NAME]
|
[--show-matching-path] [--filter.flags FLAGS]
|
||||||
|
[--filter.profile_name PROFILE_NAME]
|
||||||
[--filter.profile_attach PROFILE_ATTACH]
|
[--filter.profile_attach PROFILE_ATTACH]
|
||||||
[--filter.profile_path PROFILE_PATH]
|
[--filter.profile_path PROFILE_PATH]
|
||||||
|
|
||||||
@ -42,6 +43,7 @@ Check which profiles are used
|
|||||||
Type of profiles to show
|
Type of profiles to show
|
||||||
-j, --json Output in JSON
|
-j, --json Output in JSON
|
||||||
-d, --dir DIR Path to profiles
|
-d, --dir DIR Path to profiles
|
||||||
|
--show-matching-path Show the path of a file matching the profile
|
||||||
|
|
||||||
Filtering options:
|
Filtering options:
|
||||||
Filters are used to reduce the output of information to only those entries
|
Filters are used to reduce the output of information to only those entries
|
||||||
|
@ -552,7 +552,7 @@ class AaTest_parse_profile_data(AATest):
|
|||||||
self.assertEqual(prof['/foo']['name'], '/foo')
|
self.assertEqual(prof['/foo']['name'], '/foo')
|
||||||
self.assertEqual(prof['/foo']['filename'], 'somefile')
|
self.assertEqual(prof['/foo']['filename'], 'somefile')
|
||||||
self.assertEqual(prof['/foo']['flags'], None)
|
self.assertEqual(prof['/foo']['flags'], None)
|
||||||
self.assertEqual(prof['/foo']['xattrs'], 'user.bar=bar')
|
self.assertEqual(prof['/foo']['xattrs'], {'user.bar': 'bar'})
|
||||||
|
|
||||||
def test_parse_xattrs_02(self):
|
def test_parse_xattrs_02(self):
|
||||||
prof = parse_profile_data('/foo xattrs=(user.bar=bar user.foo=*) {\n}\n'.split(), 'somefile', False, False)
|
prof = parse_profile_data('/foo xattrs=(user.bar=bar user.foo=*) {\n}\n'.split(), 'somefile', False, False)
|
||||||
@ -561,7 +561,7 @@ class AaTest_parse_profile_data(AATest):
|
|||||||
self.assertEqual(prof['/foo']['name'], '/foo')
|
self.assertEqual(prof['/foo']['name'], '/foo')
|
||||||
self.assertEqual(prof['/foo']['filename'], 'somefile')
|
self.assertEqual(prof['/foo']['filename'], 'somefile')
|
||||||
self.assertEqual(prof['/foo']['flags'], None)
|
self.assertEqual(prof['/foo']['flags'], None)
|
||||||
self.assertEqual(prof['/foo']['xattrs'], 'user.bar=bar user.foo=*')
|
self.assertEqual(prof['/foo']['xattrs'], {'user.bar': 'bar', 'user.foo': '*'})
|
||||||
|
|
||||||
def test_parse_xattrs_03(self):
|
def test_parse_xattrs_03(self):
|
||||||
d = '/foo xattrs=(user.bar=bar) flags=(complain) {\n}\n'
|
d = '/foo xattrs=(user.bar=bar) flags=(complain) {\n}\n'
|
||||||
@ -571,7 +571,7 @@ class AaTest_parse_profile_data(AATest):
|
|||||||
self.assertEqual(prof['/foo']['name'], '/foo')
|
self.assertEqual(prof['/foo']['name'], '/foo')
|
||||||
self.assertEqual(prof['/foo']['filename'], 'somefile')
|
self.assertEqual(prof['/foo']['filename'], 'somefile')
|
||||||
self.assertEqual(prof['/foo']['flags'], 'complain')
|
self.assertEqual(prof['/foo']['flags'], 'complain')
|
||||||
self.assertEqual(prof['/foo']['xattrs'], 'user.bar=bar')
|
self.assertEqual(prof['/foo']['xattrs'], {'user.bar': 'bar'})
|
||||||
|
|
||||||
def test_parse_xattrs_04(self):
|
def test_parse_xattrs_04(self):
|
||||||
with self.assertRaises(AppArmorException):
|
with self.assertRaises(AppArmorException):
|
||||||
|
@ -41,30 +41,30 @@ class TestUnknownKey(AATest):
|
|||||||
class AaTest_get_header(AATest):
|
class AaTest_get_header(AATest):
|
||||||
tests = (
|
tests = (
|
||||||
# name embedded_hat depth flags attachment xattrs prof.keyw. comment expected
|
# name embedded_hat depth flags attachment xattrs prof.keyw. comment expected
|
||||||
(('/foo', False, 1, 'complain', '', '', False, ''), ' /foo flags=(complain) {'),
|
(('/foo', False, 1, 'complain', '', {}, False, ''), ' /foo flags=(complain) {'),
|
||||||
(('/foo', True, 1, 'complain', '', '', False, ''), ' profile /foo flags=(complain) {'),
|
(('/foo', True, 1, 'complain', '', {}, False, ''), ' profile /foo flags=(complain) {'),
|
||||||
(('/foo sp', False, 2, 'complain', '', '', False, ''), ' "/foo sp" flags=(complain) {'),
|
(('/foo sp', False, 2, 'complain', '', {}, False, ''), ' "/foo sp" flags=(complain) {'),
|
||||||
(('/foo', True, 2, 'complain', '', '', False, ''), ' profile /foo flags=(complain) {'),
|
(('/foo', True, 2, 'complain', '', {}, False, ''), ' profile /foo flags=(complain) {'),
|
||||||
(('/foo', False, 0, None, '', '', False, ''), '/foo {'),
|
(('/foo', False, 0, None, '', {}, False, ''), '/foo {'),
|
||||||
(('/foo', False, 0, None, '', 'user.foo=bar', False, ''), '/foo xattrs=(user.foo=bar) {'),
|
(('/foo', False, 0, None, '', {'user.foo': 'bar'}, False, ''), '/foo xattrs=(user.foo=bar) {'),
|
||||||
(('/foo', True, 0, None, '', '', False, ''), 'profile /foo {'),
|
(('/foo', True, 0, None, '', {}, False, ''), 'profile /foo {'),
|
||||||
(('bar', False, 1, 'complain', '', '', False, ''), ' profile bar flags=(complain) {'),
|
(('bar', False, 1, 'complain', '', {}, False, ''), ' profile bar flags=(complain) {'),
|
||||||
(('bar', False, 1, 'complain', '/foo', '', False, ''), ' profile bar /foo flags=(complain) {'),
|
(('bar', False, 1, 'complain', '/foo', {}, False, ''), ' profile bar /foo flags=(complain) {'),
|
||||||
(('bar', True, 1, 'complain', '/foo', '', False, ''), ' profile bar /foo flags=(complain) {'),
|
(('bar', True, 1, 'complain', '/foo', {}, False, ''), ' profile bar /foo flags=(complain) {'),
|
||||||
(('bar baz', False, 1, None, '/foo', '', False, ''), ' profile "bar baz" /foo {'),
|
(('bar baz', False, 1, None, '/foo', {}, False, ''), ' profile "bar baz" /foo {'),
|
||||||
(('bar', True, 1, None, '/foo', '', False, ''), ' profile bar /foo {'),
|
(('bar', True, 1, None, '/foo', {}, False, ''), ' profile bar /foo {'),
|
||||||
(('bar baz', False, 1, 'complain', '/foo sp', '', False, ''), ' profile "bar baz" "/foo sp" flags=(complain) {'),
|
(('bar baz', False, 1, 'complain', '/foo sp', {}, False, ''), ' profile "bar baz" "/foo sp" flags=(complain) {'),
|
||||||
(('bar baz', False, 1, 'complain', '/foo sp', 'user.foo=bar', False, ''), ' profile "bar baz" "/foo sp" xattrs=(user.foo=bar) flags=(complain) {'),
|
(('bar baz', False, 1, 'complain', '/foo sp', {'user.foo': 'bar'}, False, ''), ' profile "bar baz" "/foo sp" xattrs=(user.foo=bar) flags=(complain) {'),
|
||||||
(('^foo', False, 1, 'complain', '', '', False, ''), ' profile ^foo flags=(complain) {'),
|
(('^foo', False, 1, 'complain', '', {}, False, ''), ' profile ^foo flags=(complain) {'),
|
||||||
(('^foo', True, 1, 'complain', '', '', False, ''), ' ^foo flags=(complain) {'),
|
(('^foo', True, 1, 'complain', '', {}, False, ''), ' ^foo flags=(complain) {'),
|
||||||
(('^foo', True, 1.5, 'complain', '', '', False, ''), ' ^foo flags=(complain) {'),
|
(('^foo', True, 1.5, 'complain', '', {}, False, ''), ' ^foo flags=(complain) {'),
|
||||||
(('^foo', True, 1.3, 'complain', '', '', False, ''), ' ^foo flags=(complain) {'),
|
(('^foo', True, 1.3, 'complain', '', {}, False, ''), ' ^foo flags=(complain) {'),
|
||||||
(('/foo', False, 1, 'complain', '', '', True, ''), ' profile /foo flags=(complain) {'),
|
(('/foo', False, 1, 'complain', '', {}, True, ''), ' profile /foo flags=(complain) {'),
|
||||||
(('/foo', True, 1, 'complain', '', '', True, ''), ' profile /foo flags=(complain) {'),
|
(('/foo', True, 1, 'complain', '', {}, True, ''), ' profile /foo flags=(complain) {'),
|
||||||
(('/foo', False, 1, 'complain', '', '', False, '# x'), ' /foo flags=(complain) { # x'),
|
(('/foo', False, 1, 'complain', '', {}, False, '# x'), ' /foo flags=(complain) { # x'),
|
||||||
(('/foo', True, 1, None, '', '', False, '# x'), ' profile /foo { # x'),
|
(('/foo', True, 1, None, '', {}, False, '# x'), ' profile /foo { # x'),
|
||||||
(('/foo', False, 1, None, '', '', True, '# x'), ' profile /foo { # x'),
|
(('/foo', False, 1, None, '', {}, True, '# x'), ' profile /foo { # x'),
|
||||||
(('/foo', True, 1, 'complain', '', '', True, '# x'), ' profile /foo flags=(complain) { # x'),
|
(('/foo', True, 1, 'complain', '', {}, True, '# x'), ' profile /foo flags=(complain) { # x'),
|
||||||
)
|
)
|
||||||
|
|
||||||
def _run_test(self, params, expected):
|
def _run_test(self, params, expected):
|
||||||
@ -88,8 +88,10 @@ class AaTest_get_header_01(AATest):
|
|||||||
({'name': '/foo', 'depth': 1, 'flags': 'complain'}, ' /foo flags=(complain) {'),
|
({'name': '/foo', 'depth': 1, 'flags': 'complain'}, ' /foo flags=(complain) {'),
|
||||||
({'name': '/foo', 'depth': 1, 'flags': 'complain', 'profile_keyword': True}, ' profile /foo flags=(complain) {'),
|
({'name': '/foo', 'depth': 1, 'flags': 'complain', 'profile_keyword': True}, ' profile /foo flags=(complain) {'),
|
||||||
({'name': '/foo', 'flags': 'complain'}, '/foo flags=(complain) {'),
|
({'name': '/foo', 'flags': 'complain'}, '/foo flags=(complain) {'),
|
||||||
({'name': '/foo', 'xattrs': 'user.foo=bar', 'flags': 'complain'}, '/foo xattrs=(user.foo=bar) flags=(complain) {'),
|
({'name': '/foo', 'xattrs': {'user.foo': 'bar'}, 'flags': 'complain'}, '/foo xattrs=(user.foo=bar) flags=(complain) {'),
|
||||||
({'name': '/foo', 'xattrs': 'user.foo=bar', 'embedded_hat': True}, 'profile /foo xattrs=(user.foo=bar) {'),
|
({'name': '/foo', 'xattrs': {'user.foo': 'bar'}, 'embedded_hat': True}, 'profile /foo xattrs=(user.foo=bar) {'),
|
||||||
|
({'name': '/foo', 'xattrs': {'user.foo': None}, 'embedded_hat': True}, 'profile /foo xattrs=(user.foo) {'),
|
||||||
|
({'name': '/foo', 'xattrs': {'user.foo': None, 'user.bar': None}, 'embedded_hat': True}, 'profile /foo xattrs=(user.bar user.foo) {'),
|
||||||
)
|
)
|
||||||
|
|
||||||
def _run_test(self, params, expected):
|
def _run_test(self, params, expected):
|
||||||
@ -178,6 +180,7 @@ class TestSetInvalid(AATest):
|
|||||||
(('attachment', None), AppArmorBug),
|
(('attachment', None), AppArmorBug),
|
||||||
(('filename', True), AppArmorBug), # expects string or None
|
(('filename', True), AppArmorBug), # expects string or None
|
||||||
(('allow', None), AppArmorBug), # doesn't allow overwriting at all
|
(('allow', None), AppArmorBug), # doesn't allow overwriting at all
|
||||||
|
(('xattrs', 0), AppArmorBug), # Invalid type
|
||||||
)
|
)
|
||||||
|
|
||||||
def _run_test(self, params, expected):
|
def _run_test(self, params, expected):
|
||||||
@ -196,7 +199,7 @@ class AaTest_repr(AATest):
|
|||||||
def testRepr(self):
|
def testRepr(self):
|
||||||
prof_storage = ProfileStorage('foo', 'hat', 'TEST')
|
prof_storage = ProfileStorage('foo', 'hat', 'TEST')
|
||||||
prof_storage['name'] = 'foo'
|
prof_storage['name'] = 'foo'
|
||||||
prof_storage['xattrs'] = 'user.bar=bar'
|
prof_storage['xattrs'] = {'user.bar': 'bar'}
|
||||||
prof_storage['capability'].add(CapabilityRule('dac_override'))
|
prof_storage['capability'].add(CapabilityRule('dac_override'))
|
||||||
|
|
||||||
self.assertEqual(str(prof_storage), '\n<ProfileStorage>\nprofile foo xattrs=(user.bar=bar) {\n capability dac_override,\n\n}\n</ProfileStorage>\n')
|
self.assertEqual(str(prof_storage), '\n<ProfileStorage>\nprofile foo xattrs=(user.bar=bar) {\n capability dac_override,\n\n}\n</ProfileStorage>\n')
|
||||||
@ -205,15 +208,21 @@ class AaTest_repr(AATest):
|
|||||||
class AaTest_parse_profile_start(AATest):
|
class AaTest_parse_profile_start(AATest):
|
||||||
tests = (
|
tests = (
|
||||||
# profile start line profile hat parent name profile hat attachment xattrs flags pps_set_hat_external
|
# profile start line profile hat parent name profile hat attachment xattrs flags pps_set_hat_external
|
||||||
(('/foo {', None, None), ('', '/foo', '/foo', '/foo', '', '', None, False)),
|
(('/foo {', None, None), ('', '/foo', '/foo', '/foo', '', {}, None, False)),
|
||||||
(('/foo (complain) {', None, None), ('', '/foo', '/foo', '/foo', '', '', 'complain', False)),
|
(('/foo (complain) {', None, None), ('', '/foo', '/foo', '/foo', '', {}, 'complain', False)),
|
||||||
(('profile foo /foo {', None, None), ('', 'foo', 'foo', 'foo', '/foo', '', None, False)), # named profile
|
(('profile foo /foo {', None, None), ('', 'foo', 'foo', 'foo', '/foo', {}, None, False)), # named profile
|
||||||
(('profile /foo {', '/bar', None), ('/bar', '/foo', '/bar', '/foo', '', '', None, False)), # child profile
|
(('profile /foo {', '/bar', None), ('/bar', '/foo', '/bar', '/foo', '', {}, None, False)), # child profile
|
||||||
(('/foo//bar {', None, None), ('/foo', '/foo//bar', '/foo', 'bar', '', '', None, True)), # external hat
|
(('profile /foo xattrs=() {', '/bar', None), ('/bar', '/foo', '/bar', '/foo', '', {}, None, False)),
|
||||||
(('profile "/foo" (complain) {', None, None), ('', '/foo', '/foo', '/foo', '', '', 'complain', False)),
|
(('/foo//bar {', None, None), ('/foo', '/foo//bar', '/foo', 'bar', '', {}, None, True)), # external hat
|
||||||
(('profile "/foo" xattrs=(user.bar=bar) {', None, None), ('', '/foo', '/foo', '/foo', '', 'user.bar=bar', None, False)),
|
(('profile "/foo" (complain) {', None, None), ('', '/foo', '/foo', '/foo', '', {}, 'complain', False)),
|
||||||
(('profile "/foo" xattrs=(user.bar=bar user.foo=*) {', None, None), ('', '/foo', '/foo', '/foo', '', 'user.bar=bar user.foo=*', None, False)),
|
(('profile "/foo" xattrs=() {', None, None), ('', '/foo', '/foo', '/foo', '', {}, None, False)),
|
||||||
(('/usr/bin/xattrs-test xattrs=(myvalue="foo.bar") {', None, None), ('', '/usr/bin/xattrs-test', '/usr/bin/xattrs-test', '/usr/bin/xattrs-test', '', 'myvalue="foo.bar"', None, False)),
|
(('profile "/foo" xattrs=(user.bar) {', None, None), ('', '/foo', '/foo', '/foo', '', {'user.bar': None}, None, False)),
|
||||||
|
(('profile "/foo" xattrs=(user.foo user.bar) {', None, None), ('', '/foo', '/foo', '/foo', '', {'user.bar': None, 'user.foo': None},
|
||||||
|
None, False)), # noqa: E127
|
||||||
|
(('profile "/foo" xattrs=(user.bar=bar) {', None, None), ('', '/foo', '/foo', '/foo', '', {'user.bar': 'bar'}, None, False)),
|
||||||
|
(('profile "/foo" xattrs=(user.bar=bar user.foo=*) {', None, None), ('', '/foo', '/foo', '/foo', '', {'user.bar': 'bar', 'user.foo': '*'},
|
||||||
|
None, False)), # noqa: E127
|
||||||
|
(('/usr/bin/xattrs-test xattrs=(myvalue="foo.bar") {', None, None), ('', '/usr/bin/xattrs-test', '/usr/bin/xattrs-test', '/usr/bin/xattrs-test', '', {'myvalue': '"foo.bar"'}, None, False)),
|
||||||
)
|
)
|
||||||
|
|
||||||
def _run_test(self, params, expected):
|
def _run_test(self, params, expected):
|
||||||
|
@ -18,7 +18,7 @@ from apparmor.regex import (
|
|||||||
RE_PROFILE_START, parse_profile_start_line, re_match_include, RE_PROFILE_UNIX,
|
RE_PROFILE_START, parse_profile_start_line, re_match_include, RE_PROFILE_UNIX,
|
||||||
RE_PROFILE_PIVOT_ROOT,
|
RE_PROFILE_PIVOT_ROOT,
|
||||||
re_match_include_parse, strip_parenthesis, strip_quotes, resolve_variables, expand_braces,
|
re_match_include_parse, strip_parenthesis, strip_quotes, resolve_variables, expand_braces,
|
||||||
expand_var, expand_string)
|
expand_var, expand_string, re_print_dict, re_parse_dict)
|
||||||
from common_test import AATest, setup_aa, setup_all_loops
|
from common_test import AATest, setup_aa, setup_all_loops
|
||||||
|
|
||||||
|
|
||||||
@ -857,6 +857,27 @@ class TestInvalidExpandString(AATest):
|
|||||||
expand_string(var, var_dict, seen_vars)
|
expand_string(var, var_dict, seen_vars)
|
||||||
|
|
||||||
|
|
||||||
|
class TestRePrintDict(AATest):
|
||||||
|
tests = (
|
||||||
|
({'a': 'b'}, 'a=b'),
|
||||||
|
({'a': 'b', 'bb': 'cc'}, 'a=b bb=cc'),
|
||||||
|
({'z': 'c', 'y': 'b', 'x': 'a'}, 'x=a y=b z=c'),
|
||||||
|
)
|
||||||
|
|
||||||
|
def _run_test(self, params, expected):
|
||||||
|
self.assertEqual(re_print_dict(params), expected)
|
||||||
|
|
||||||
|
|
||||||
|
class TestReParseDict(AATest):
|
||||||
|
tests = (
|
||||||
|
('a=b', {'a': 'b'}),
|
||||||
|
(' a=bbb bb=cc', {'a': 'bbb', 'bb': 'cc'}),
|
||||||
|
)
|
||||||
|
|
||||||
|
def _run_test(self, params, expected):
|
||||||
|
self.assertEqual(re_parse_dict(params), expected)
|
||||||
|
|
||||||
|
|
||||||
setup_aa(aa)
|
setup_aa(aa)
|
||||||
setup_all_loops(__name__)
|
setup_all_loops(__name__)
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
Loading…
x
Reference in New Issue
Block a user