#! /usr/bin/python3 # ------------------------------------------------------------------ # # Copyright (C) 2011-2015 Canonical Ltd. # # This program is free software; you can redistribute it and/or # modify it under the terms of version 2 of the GNU General Public # License published by the Free Software Foundation. # # ------------------------------------------------------------------ import glob import json import optparse import os import shutil import sys import tempfile import unittest import apparmor.easyprof as easyprof from apparmor.common import AppArmorException topdir = None debugging = False def recursive_rm(dirPath, contents_only=False): """recursively remove directory""" names = os.listdir(dirPath) for name in names: path = os.path.join(dirPath, name) if os.path.islink(path) or not os.path.isdir(path): os.unlink(path) else: recursive_rm(path) if not contents_only: os.rmdir(dirPath) # From Lib/test/test_optparse.py from python 2.7.4 class InterceptedError(Exception): def __init__(self, error_message=None, exit_status=None, exit_message=None): self.error_message = error_message self.exit_status = exit_status self.exit_message = exit_message def __str__(self): return self.error_message or self.exit_message or "intercepted error" class InterceptingOptionParser(optparse.OptionParser): def exit(self, status=0, msg=None): raise InterceptedError(exit_status=status, exit_message=msg) def error(self, msg): raise InterceptedError(error_message=msg) class Manifest: def __init__(self, profile_name): self.security = dict() self.security['profiles'] = dict() self.profile_name = profile_name self.security['profiles'][self.profile_name] = dict() def add_policygroups(self, policy_list): self.security['profiles'][self.profile_name]['policy_groups'] = policy_list.split(",") def add_author(self, author): self.security['profiles'][self.profile_name]['author'] = author def add_copyright(self, copyright): self.security['profiles'][self.profile_name]['copyright'] = copyright def add_comment(self, comment): self.security['profiles'][self.profile_name]['comment'] = comment def add_binary(self, binary): self.security['profiles'][self.profile_name]['binary'] = binary def add_template(self, template): self.security['profiles'][self.profile_name]['template'] = template def add_template_variable(self, name, value): if 'template_variables' not in self.security['profiles'][self.profile_name]: self.security['profiles'][self.profile_name]['template_variables'] = dict() self.security['profiles'][self.profile_name]['template_variables'][name] = value def emit_json(self, use_security_prefix=True): manifest = dict() manifest['security'] = self.security if use_security_prefix: dumpee = manifest else: dumpee = self.security return json.dumps(dumpee, indent=2) # # Our test class # class T(unittest.TestCase): # work around UsrMove ls = os.path.realpath('/bin/ls') def setUp(self): """Setup for tests""" global topdir self.tmpdir = os.path.realpath(tempfile.mkdtemp(prefix='test-aa-easyprof')) # Copy everything into place for d in ('easyprof/policygroups', 'easyprof/templates'): shutil.copytree(os.path.join(topdir, d), os.path.join(self.tmpdir, os.path.basename(d))) # Create a test template self.test_template = "test-template" contents = '''# vim:syntax=apparmor # %s # AppArmor policy for ###NAME### # ###AUTHOR### # ###COPYRIGHT### # ###COMMENT### #include ###VAR### ###PROFILEATTACH### { #include ###ABSTRACTIONS### ###POLICYGROUPS### ###READS### ###WRITES### } ''' % (self.test_template,) with open(os.path.join(self.tmpdir, 'templates', self.test_template), 'w') as f: f.write(contents) # Create a test policygroup self.test_policygroup = "test-policygroup" contents = ''' # {} #include #include '''.format(self.test_policygroup) with open(os.path.join(self.tmpdir, 'policygroups', self.test_policygroup), 'w') as f: f.write(contents) # setup our conffile self.conffile = os.path.join(self.tmpdir, 'easyprof.conf') contents = ''' POLICYGROUPS_DIR="{}/policygroups" TEMPLATES_DIR="{}/templates" '''.format(self.tmpdir, self.tmpdir) with open(self.conffile, 'w') as f: f.write(contents) self.binary = "/opt/bin/foo" self.full_args = ['-c', self.conffile, self.binary] # Check __AA_BASEDIR, which may be set by the Makefile, to see if # we should use a non-default base directory path to find # abstraction files # # NOTE: Individual tests can append another --base path to the # args list and override a base path set here base = os.getenv('__AA_BASEDIR') if base: self.full_args.append('--base=' + base) # Check __AA_PARSER, which may be set by the Makefile, to see if # we should use a non-default apparmor_parser path to verify # policy parser = os.getenv('__AA_PARSER') if parser: self.full_args.append('--parser=' + parser) if debugging: self.full_args.append('-d') (self.options, self.args) = easyprof.parse_args(self.full_args + [self.binary]) # Now create some differently prefixed files in the include-dir self.test_include_dir = os.path.join(self.tmpdir, 'include-dir') os.mkdir(self.test_include_dir) os.mkdir(os.path.join(self.test_include_dir, "templates")) os.mkdir(os.path.join(self.test_include_dir, "policygroups")) for d in ('policygroups', 'templates'): for f in easyprof.get_directory_contents(os.path.join( self.tmpdir, d)): shutil.copy(f, os.path.join(self.test_include_dir, d, "inc_" + os.path.basename(f))) def tearDown(self): """Teardown for tests""" if os.path.exists(self.tmpdir): if debugging: sys.stdout.write(self.tmpdir + "\n") else: recursive_rm(self.tmpdir) # # config file tests # def test_configuration_file_p_invalid(self): """Test config parsing (invalid POLICYGROUPS_DIR)""" contents = ''' POLICYGROUPS_DIR= TEMPLATES_DIR="{}/templates" '''.format(self.tmpdir) with open(self.conffile, 'w') as f: f.write(contents) try: easyprof.AppArmorEasyProfile(self.binary, self.options) except AppArmorException: return raise Exception("File should have been invalid") def test_configuration_file_p_empty(self): """Test config parsing (empty POLICYGROUPS_DIR)""" contents = ''' POLICYGROUPS_DIR="" TEMPLATES_DIR="{}/templates" '''.format(self.tmpdir) with open(self.conffile, 'w') as f: f.write(contents) try: easyprof.AppArmorEasyProfile(self.binary, self.options) except AppArmorException: return raise Exception("File should have been invalid") def test_configuration_file_p_nonexistent(self): """Test config parsing (nonexistent POLICYGROUPS_DIR)""" contents = ''' POLICYGROUPS_DIR="/nonexistent/policygroups" TEMPLATES_DIR="{}/templates" '''.format(self.tmpdir) with open(self.conffile, 'w') as f: f.write(contents) try: easyprof.AppArmorEasyProfile(self.binary, self.options) except AppArmorException: return raise Exception("File should have been invalid") def test_policygroups_dir_relative(self): """Test --policy-groups-dir (relative DIR)""" os.chdir(self.tmpdir) rel = os.path.join(self.tmpdir, 'relative') os.mkdir(rel) shutil.copy(os.path.join(self.tmpdir, 'policygroups', self.test_policygroup), os.path.join(rel, self.test_policygroup)) args = self.full_args args += ['--policy-groups-dir', './relative', '--show-policy-group', '--policy-groups=' + self.test_policygroup] (self.options, self.args) = easyprof.parse_args(args) easyp = easyprof.AppArmorEasyProfile(self.binary, self.options) # no fallback self.assertTrue(easyp.dirs['policygroups'] == rel, "Not using specified --policy-groups-dir\n" "Specified dir: {}\nActual dir: {}".format(rel, easyp.dirs['policygroups'])) self.assertFalse(easyp.get_policy_groups() is None, "Could not find policy-groups") def test_policygroups_dir_nonexistent(self): """Test --policy-groups-dir (nonexistent DIR)""" os.chdir(self.tmpdir) rel = os.path.join(self.tmpdir, 'nonexistent') args = self.full_args args += ['--policy-groups-dir', rel, '--show-policy-group', '--policy-groups=' + self.test_policygroup] (self.options, self.args) = easyprof.parse_args(args) easyp = easyprof.AppArmorEasyProfile(self.binary, self.options) # test if using fallback self.assertFalse(easyp.dirs['policygroups'] == rel, "Using nonexistent --policy-groups-dir") # test fallback self.assertTrue(easyp.get_policy_groups() is not None, "Found policy-groups when shouldn't have") def test_policygroups_dir_valid(self): """Test --policy-groups-dir (valid DIR)""" os.chdir(self.tmpdir) valid = os.path.join(self.tmpdir, 'valid') os.mkdir(valid) shutil.copy(os.path.join(self.tmpdir, 'policygroups', self.test_policygroup), os.path.join(valid, self.test_policygroup)) args = self.full_args args += ['--policy-groups-dir', valid, '--show-policy-group', '--policy-groups=' + self.test_policygroup] (self.options, self.args) = easyprof.parse_args(args) easyp = easyprof.AppArmorEasyProfile(self.binary, self.options) # no fallback self.assertTrue(easyp.dirs['policygroups'] == valid, "Not using specified --policy-groups-dir") self.assertFalse(easyp.get_policy_groups() is None, "Could not find policy-groups") def test_policygroups_dir_valid_with_vendor(self): """Test --policy-groups-dir (valid DIR with vendor)""" os.chdir(self.tmpdir) valid = os.path.join(self.tmpdir, 'valid') os.mkdir(valid) shutil.copy(os.path.join(self.tmpdir, 'policygroups', self.test_policygroup), os.path.join(valid, self.test_policygroup)) vendor = "ubuntu" version = "1.0" valid_distro = os.path.join(valid, vendor, version) os.mkdir(os.path.join(valid, vendor)) os.mkdir(valid_distro) shutil.copy(os.path.join(self.tmpdir, 'policygroups', self.test_policygroup), valid_distro) args = self.full_args args += ['--policy-groups-dir', valid, '--show-policy-group', '--policy-groups=' + self.test_policygroup] (self.options, self.args) = easyprof.parse_args(args) easyp = easyprof.AppArmorEasyProfile(self.binary, self.options) self.assertTrue(easyp.dirs['policygroups'] == valid, "Not using specified --policy-groups-dir") self.assertFalse(easyp.get_policy_groups() is None, "Could not find policy-groups") for f in easyp.get_policy_groups(): self.assertFalse(os.path.basename(f) == vendor, "Found '{}' in {}".format(vendor, f)) def test_configuration_file_t_invalid(self): """Test config parsing (invalid TEMPLATES_DIR)""" contents = ''' TEMPLATES_DIR= POLICYGROUPS_DIR="{}/templates" '''.format(self.tmpdir) with open(self.conffile, 'w') as f: f.write(contents) try: easyprof.AppArmorEasyProfile(self.binary, self.options) except AppArmorException: return raise Exception("File should have been invalid") def test_configuration_file_t_empty(self): """Test config parsing (empty TEMPLATES_DIR)""" contents = ''' TEMPLATES_DIR="" POLICYGROUPS_DIR="{}/templates" '''.format(self.tmpdir) with open(self.conffile, 'w') as f: f.write(contents) try: easyprof.AppArmorEasyProfile(self.binary, self.options) except AppArmorException: return raise Exception("File should have been invalid") def test_configuration_file_t_nonexistent(self): """Test config parsing (nonexistent TEMPLATES_DIR)""" contents = ''' TEMPLATES_DIR="/nonexistent/policygroups" POLICYGROUPS_DIR="{}/templates" '''.format(self.tmpdir) with open(self.conffile, 'w') as f: f.write(contents) try: easyprof.AppArmorEasyProfile(self.binary, self.options) except AppArmorException: return raise Exception("File should have been invalid") def test_templates_dir_relative(self): """Test --templates-dir (relative DIR)""" os.chdir(self.tmpdir) rel = os.path.join(self.tmpdir, 'relative') os.mkdir(rel) shutil.copy(os.path.join(self.tmpdir, 'templates', self.test_template), os.path.join(rel, self.test_template)) args = self.full_args args += ['--templates-dir', './relative', '--show-template', '--template=' + self.test_template] (self.options, self.args) = easyprof.parse_args(args) easyp = easyprof.AppArmorEasyProfile(self.binary, self.options) # no fallback self.assertTrue(easyp.dirs['templates'] == rel, "Not using specified --template-dir\n" "Specified dir: {}\nActual dir: {}".format(rel, easyp.dirs['templates'])) self.assertFalse(easyp.get_templates() is None, "Could not find templates") def test_templates_dir_nonexistent(self): """Test --templates-dir (nonexistent DIR)""" os.chdir(self.tmpdir) rel = os.path.join(self.tmpdir, 'nonexistent') args = self.full_args args += ['--templates-dir', rel, '--show-template', '--template=' + self.test_template] (self.options, self.args) = easyprof.parse_args(args) easyp = easyprof.AppArmorEasyProfile(self.binary, self.options) # test if using fallback self.assertFalse(easyp.dirs['templates'] == rel, "Using nonexistent --template-dir") # test fallback self.assertTrue(easyp.get_templates() is not None, "Found templates when shouldn't have") def test_templates_dir_valid(self): """Test --templates-dir (valid DIR)""" os.chdir(self.tmpdir) valid = os.path.join(self.tmpdir, 'valid') os.mkdir(valid) shutil.copy(os.path.join(self.tmpdir, 'templates', self.test_template), os.path.join(valid, self.test_template)) args = self.full_args args += ['--templates-dir', valid, '--show-template', '--template=' + self.test_template] (self.options, self.args) = easyprof.parse_args(args) easyp = easyprof.AppArmorEasyProfile(self.binary, self.options) # no fallback self.assertTrue(easyp.dirs['templates'] == valid, "Not using specified --template-dir") self.assertFalse(easyp.get_templates() is None, "Could not find templates") def test_templates_dir_valid_with_vendor(self): """Test --templates-dir (valid DIR with vendor)""" os.chdir(self.tmpdir) valid = os.path.join(self.tmpdir, 'valid') os.mkdir(valid) shutil.copy(os.path.join(self.tmpdir, 'templates', self.test_template), os.path.join(valid, self.test_template)) vendor = "ubuntu" version = "1.0" valid_distro = os.path.join(valid, vendor, version) os.mkdir(os.path.join(valid, vendor)) os.mkdir(valid_distro) shutil.copy(os.path.join(self.tmpdir, 'templates', self.test_template), valid_distro) args = self.full_args args += ['--templates-dir', valid, '--show-template', '--template=' + self.test_template] (self.options, self.args) = easyprof.parse_args(args) easyp = easyprof.AppArmorEasyProfile(self.binary, self.options) self.assertTrue(easyp.dirs['templates'] == valid, "Not using specified --template-dir") self.assertFalse(easyp.get_templates() is None, "Could not find templates") for f in easyp.get_templates(): self.assertFalse(os.path.basename(f) == vendor, "Found '{}' in {}".format(vendor, f)) # # Binary file tests # def test_binary_without_profile_name(self): """Test binary ( { })""" easyprof.AppArmorEasyProfile(self.ls, self.options) def test_binary_with_profile_name(self): """Test binary (profile { })""" args = self.full_args args += ['--profile-name=some-profile-name'] (self.options, self.args) = easyprof.parse_args(args) easyprof.AppArmorEasyProfile(self.ls, self.options) def test_binary_omitted_with_profile_name(self): """Test binary (profile { })""" args = self.full_args args += ['--profile-name=some-profile-name'] (self.options, self.args) = easyprof.parse_args(args) easyprof.AppArmorEasyProfile(None, self.options) def test_binary_nonexistent(self): """Test binary (nonexistent)""" easyprof.AppArmorEasyProfile(os.path.join(self.tmpdir, 'nonexistent'), self.options) def test_binary_relative(self): """Test binary (relative)""" try: easyprof.AppArmorEasyProfile('./foo', self.options) except AppArmorException: return raise Exception("Binary should have been invalid") def test_binary_symlink(self): """Test binary (symlink)""" exe = os.path.join(self.tmpdir, 'exe') open(exe, 'a').close() symlink = exe + ".lnk" os.symlink(exe, symlink) try: easyprof.AppArmorEasyProfile(symlink, self.options) except AppArmorException: return raise Exception("Binary should have been invalid") # # Templates tests # def test_templates_list(self): """Test templates (list)""" args = self.full_args args.append('--list-templates') (self.options, self.args) = easyprof.parse_args(args) easyp = easyprof.AppArmorEasyProfile(None, self.options) for i in easyp.get_templates(): self.assertTrue(os.path.exists(i), "Could not find '{}'".format(i)) def test_templates_show(self): """Test templates (show)""" files = glob.glob(self.tmpdir + "/templates/*") for f in files: args = self.full_args args += ['--show-template', '--template', f] (self.options, self.args) = easyprof.parse_args(args) easyp = easyprof.AppArmorEasyProfile(None, self.options) path = os.path.join(easyp.dirs['templates'], f) self.assertTrue(os.path.exists(path), "Could not find '{}'".format(path)) with open(path) as fd: fd.read() def test_templates_list_include(self): """Test templates (list with --include-templates-dir)""" args = self.full_args args.append('--list-templates') (self.options, self.args) = easyprof.parse_args(args) easyp = easyprof.AppArmorEasyProfile(None, self.options) orig_templates = easyp.get_templates() args = self.full_args args.append('--list-templates') args.append('--include-templates-dir=' + os.path.join(self.test_include_dir, 'templates')) (self.options, self.args) = easyprof.parse_args(args) easyp = easyprof.AppArmorEasyProfile(None, self.options) inc_templates = easyp.get_templates() self.assertTrue(len(inc_templates) == len(orig_templates) * 2, "templates missing: {}".format(inc_templates)) for i in inc_templates: self.assertTrue(os.path.exists(i), "Could not find '{}'".format(i)) def test_templates_show_include(self): """Test templates (show with --include-templates-dir)""" files = glob.glob(self.test_include_dir + "/templates/*") for f in files: args = self.full_args args += ['--show-template', '--template', f, '--include-templates-dir=' + os.path.join(self.test_include_dir, 'templates')] (self.options, self.args) = easyprof.parse_args(args) easyp = easyprof.AppArmorEasyProfile(None, self.options) path = os.path.join(easyp.dirs['templates_include'], f) self.assertTrue(os.path.exists(path), "Could not find '{}'".format(path)) with open(path) as fd: fd.read() bn = os.path.basename(f) # setup() copies everything in the include prefixed with inc_ self.assertTrue(bn.startswith('inc_'), "'{}' does not start with 'inc_'".format(bn)) # # Policygroups tests # def test_policygroups_list(self): """Test policygroups (list)""" args = self.full_args args.append('--list-policy-groups') (self.options, self.args) = easyprof.parse_args(args) easyp = easyprof.AppArmorEasyProfile(None, self.options) for i in easyp.get_policy_groups(): self.assertTrue(os.path.exists(i), "Could not find '{}'".format(i)) def test_policygroups_show(self): """Test policygroups (show)""" files = glob.glob(self.tmpdir + "/policygroups/*") for f in files: args = self.full_args args += ['--show-policy-group', '--policy-groups', os.path.basename(f)] (self.options, self.args) = easyprof.parse_args(args) easyp = easyprof.AppArmorEasyProfile(None, self.options) path = os.path.join(easyp.dirs['policygroups'], f) self.assertTrue(os.path.exists(path), "Could not find '{}'".format(path)) with open(path) as fd: fd.read() def test_policygroups_list_include(self): """Test policygroups (list with --include-policy-groups-dir)""" args = self.full_args args.append('--list-policy-groups') (self.options, self.args) = easyprof.parse_args(args) easyp = easyprof.AppArmorEasyProfile(None, self.options) orig_policy_groups = easyp.get_policy_groups() args = self.full_args args.append('--list-policy-groups') args.append('--include-policy-groups-dir=' + os.path.join(self.test_include_dir, 'policygroups')) (self.options, self.args) = easyprof.parse_args(args) easyp = easyprof.AppArmorEasyProfile(None, self.options) inc_policy_groups = easyp.get_policy_groups() self.assertTrue(len(inc_policy_groups) == len(orig_policy_groups) * 2, "policy_groups missing: {}".format(inc_policy_groups)) for i in inc_policy_groups: self.assertTrue(os.path.exists(i), "Could not find '{}'".format(i)) def test_policygroups_show_include(self): """Test policygroups (show with --include-policy-groups-dir)""" files = glob.glob(self.test_include_dir + "/policygroups/*") for f in files: args = self.full_args args += ['--show-policy-group', '--policy-groups', os.path.basename(f), '--include-policy-groups-dir=' + os.path.join(self.test_include_dir, 'policygroups')] (self.options, self.args) = easyprof.parse_args(args) easyp = easyprof.AppArmorEasyProfile(None, self.options) path = os.path.join(easyp.dirs['policygroups_include'], f) self.assertTrue(os.path.exists(path), "Could not find '{}'".format(path)) with open(path) as fd: fd.read() bn = os.path.basename(f) # setup() copies everything in the include prefixed with inc_ self.assertTrue(bn.startswith('inc_'), "'{}' does not start with 'inc_'".format(bn)) # # Manifest file argument tests # def test_manifest_argument(self): """Test manifest argument""" # setup our manifest self.manifest = os.path.join(self.tmpdir, 'manifest.json') contents = ''' {"security": {"domain.reverse.appname": {"name": "simple-app"}}} ''' with open(self.manifest, 'w') as f: f.write(contents) args = self.full_args args.extend(('--manifest', self.manifest)) easyprof.parse_args(args) def _manifest_conflicts(self, opt, value): """Helper for conflicts tests""" # setup our manifest self.manifest = os.path.join(self.tmpdir, 'manifest.json') contents = ''' {"security": {"domain.reverse.appname": {"binary": /nonexistent"}}} ''' with open(self.manifest, 'w') as f: f.write(contents) # opt first args = self.full_args args.extend((opt, value, '--manifest', self.manifest)) raised = False try: easyprof.parse_args(args, InterceptingOptionParser()) except InterceptedError: raised = True self.assertTrue(raised, msg=opt + " and manifest arguments did not " "raise a parse error") # manifest first args = self.full_args args.extend(('--manifest', self.manifest, opt, value)) raised = False try: easyprof.parse_args(args, InterceptingOptionParser()) except InterceptedError: raised = True self.assertTrue(raised, msg=opt + " and manifest arguments did not " "raise a parse error") def test_manifest_conflicts_profilename(self): """Test manifest arg conflicts with profile_name arg""" self._manifest_conflicts("--profile-name", "simple-app") def test_manifest_conflicts_copyright(self): """Test manifest arg conflicts with copyright arg""" self._manifest_conflicts("--copyright", "2013-01-01") def test_manifest_conflicts_author(self): """Test manifest arg conflicts with author arg""" self._manifest_conflicts("--author", "Foo Bar") def test_manifest_conflicts_comment(self): """Test manifest arg conflicts with comment arg""" self._manifest_conflicts("--comment", "some comment") def test_manifest_conflicts_abstractions(self): """Test manifest arg conflicts with abstractions arg""" self._manifest_conflicts("--abstractions", "base") def test_manifest_conflicts_read_path(self): """Test manifest arg conflicts with read-path arg""" self._manifest_conflicts("--read-path", "/etc/passwd") def test_manifest_conflicts_write_path(self): """Test manifest arg conflicts with write-path arg""" self._manifest_conflicts("--write-path", "/tmp/foo") def test_manifest_conflicts_policy_groups(self): """Test manifest arg conflicts with policy-groups arg""" self._manifest_conflicts("--policy-groups", "opt-application") def test_manifest_conflicts_name(self): """Test manifest arg conflicts with name arg""" self._manifest_conflicts("--name", "foo") def test_manifest_conflicts_template_var(self): """Test manifest arg conflicts with template-var arg""" self._manifest_conflicts("--template-var", "foo") def test_manifest_conflicts_policy_version(self): """Test manifest arg conflicts with policy-version arg""" self._manifest_conflicts("--policy-version", "1.0") def test_manifest_conflicts_policy_vendor(self): """Test manifest arg conflicts with policy-vendor arg""" self._manifest_conflicts("--policy-vendor", "somevendor") # # Test genpolicy # def _gen_policy(self, name=None, template=None, extra_args=None): """Generate a policy""" # Build up our args args = self.full_args if template is None: args.append('--template=' + self.test_template) else: args.append('--template=' + template) if name is not None: args.append('--name=' + name) if extra_args: args += extra_args args.append(self.binary) # Now parse our args (self.options, self.args) = easyprof.parse_args(args) easyp = easyprof.AppArmorEasyProfile(self.binary, self.options) params = easyprof.gen_policy_params(self.binary, self.options) p = easyp.gen_policy(**params) # We always need to check for these search_terms = [self.binary] if name is not None: search_terms.append(name) if template is None: search_terms.append(self.test_template) for s in search_terms: self.assertTrue(s in p, "Could not find '{}' in:\n{}".format(s, p)) # ###NAME### should be replaced with self.binary or 'name'. Check for that inv_s = '###NAME###' self.assertFalse(inv_s in p, "Found '{}' in :\n{}".format(inv_s, p)) if debugging: sys.stdout.write(p + "\n") return p def _gen_manifest_policy(self, manifest, use_security_prefix=True): # Build up our args args = self.full_args args.append("--manifest=/dev/null") (self.options, self.args) = easyprof.parse_args(args) (binary, self.options) = easyprof.parse_manifest(manifest.emit_json(use_security_prefix), self.options)[0] easyp = easyprof.AppArmorEasyProfile(binary, self.options) params = easyprof.gen_policy_params(binary, self.options) p = easyp.gen_policy(**params) # ###NAME### should be replaced with self.binary or 'name'. Check for that inv_s = '###NAME###' self.assertFalse(inv_s in p, "Found '{}' in :\n{}".format(inv_s, p)) if debugging: sys.stdout.write(p + "\n") return p def test__is_safe(self): """Test _is_safe()""" bad = ( "/../../../../etc/passwd", "abstraction with spaces", "semicolon;bad", "bad\x00baz", "foo/bar", "foo'bar", 'foo"bar', ) for s in bad: self.assertFalse(easyprof._is_safe(s), "'{}' should be bad".format(s)) def test_genpolicy_templates_abspath(self): """Test genpolicy (abspath to template)""" # create a new template template = os.path.join(self.tmpdir, "test-abspath-template") shutil.copy(os.path.join(self.tmpdir, 'templates', self.test_template), template) with open(template) as f: contents = f.read() test_string = "#teststring" with open(template, 'w') as f: f.write(contents + "\n{}\n".format(test_string)) p = self._gen_policy(template=template) for s in (self.test_template, test_string): self.assertTrue(s in p, "Could not find '{}' in:\n{}".format(s, p)) def test_genpolicy_templates_system(self): """Test genpolicy (system template)""" self._gen_policy() def test_genpolicy_templates_nonexistent(self): """Test genpolicy (nonexistent template)""" try: self._gen_policy(template=os.path.join(self.tmpdir, "/nonexistent")) except AppArmorException: return raise Exception("template should be invalid") def test_genpolicy_name(self): """Test genpolicy (name)""" self._gen_policy(name='test-foo') def test_genpolicy_comment(self): """Test genpolicy (comment)""" s = "test comment" p = self._gen_policy(extra_args=['--comment=' + s]) self.assertTrue(s in p, "Could not find '{}' in:\n{}".format(s, p)) inv_s = '###COMMENT###' self.assertFalse(inv_s in p, "Found '{}' in :\n{}".format(inv_s, p)) def test_genpolicy_author(self): """Test genpolicy (author)""" s = "Archibald Poindexter" p = self._gen_policy(extra_args=['--author=' + s]) self.assertTrue(s in p, "Could not find '{}' in:\n{}".format(s, p)) inv_s = '###AUTHOR###' self.assertFalse(inv_s in p, "Found '{}' in :\n{}".format(inv_s, p)) def test_genpolicy_copyright(self): """Test genpolicy (copyright)""" s = "2112/01/01" p = self._gen_policy(extra_args=['--copyright=' + s]) self.assertTrue(s in p, "Could not find '{}' in:\n{}".format(s, p)) inv_s = '###COPYRIGHT###' self.assertFalse(inv_s in p, "Found '{}' in :\n{}".format(inv_s, p)) def test_genpolicy_abstractions(self): """Test genpolicy (single abstraction)""" s = "nameservice" p = self._gen_policy(extra_args=['--abstractions=' + s]) search = "#include ".format(s) self.assertTrue(search in p, "Could not find '{}' in:\n{}".format(search, p)) inv_s = '###ABSTRACTIONS###' self.assertFalse(inv_s in p, "Found '{}' in :\n{}".format(inv_s, p)) def test_genpolicy_abstractions_multiple(self): """Test genpolicy (multiple abstractions)""" abstractions = "authentication,X,user-tmp" p = self._gen_policy(extra_args=['--abstractions=' + abstractions]) for s in abstractions.split(','): search = "#include ".format(s) self.assertTrue(search in p, "Could not find '{}' in:\n{}".format(search, p)) inv_s = '###ABSTRACTIONS###' self.assertFalse(inv_s in p, "Found '{}' in :\n{}".format(inv_s, p)) def test_genpolicy_abstractions_bad(self): """Test genpolicy (abstractions - bad values)""" bad = ( "nonexistent", "/../../../../etc/passwd", "abstraction with spaces", ) for s in bad: try: self._gen_policy(extra_args=['--abstractions=' + s]) except AppArmorException: continue raise Exception("abstraction '{}' should be invalid".format(s)) def _create_tmp_base_dir(self, prefix='', abstractions=(), tunables=()): """Create a temporary base dir layout""" base_name = 'apparmor.d' if prefix: base_name = '{}-{}'.format(prefix, base_name) base_dir = os.path.join(self.tmpdir, base_name) abstractions_dir = os.path.join(base_dir, 'abstractions') tunables_dir = os.path.join(base_dir, 'tunables') os.mkdir(base_dir) os.mkdir(abstractions_dir) os.mkdir(tunables_dir) for f in abstractions: contents = ''' # Abstraction file for testing /{} r, '''.format(f) with open(os.path.join(abstractions_dir, f), 'w') as fd: fd.write(contents) for f in tunables: contents = ''' # Tunable file for testing @{AA_TEST_%s}=foo ''' % (f,) with open(os.path.join(tunables_dir, f), 'w') as fd: fd.write(contents) return base_dir def test_genpolicy_abstractions_custom_base(self): """Test genpolicy (custom base dir)""" abstraction = "custom-base-dir-test-abstraction" # The default template #includes the base abstraction and global # tunable so we need to create placeholders base = self._create_tmp_base_dir(abstractions=['base', abstraction], tunables=['global']) args = ['--abstractions=' + abstraction, '--base=' + base] p = self._gen_policy(extra_args=args) search = "#include ".format(abstraction) self.assertTrue(search in p, "Could not find '{}' in:\n{}".format(search, p)) inv_s = '###ABSTRACTIONS###' self.assertFalse(inv_s in p, "Found '{}' in :\n{}".format(inv_s, p)) def test_genpolicy_abstractions_custom_base_bad(self): """Test genpolicy (custom base dir - bad base dirs)""" abstraction = "custom-base-dir-test-abstraction" bad = [None, '/etc/apparmor.d', '/'] for base in bad: try: args = ['--abstractions=' + abstraction] if base: args.append('--base={}'.format(base)) self._gen_policy(extra_args=args) except AppArmorException: continue raise Exception("abstraction '{}' should be invalid".format(abstraction)) def test_genpolicy_abstractions_custom_include(self): """Test genpolicy (custom include dir)""" abstraction = "custom-include-dir-test-abstraction" # No need to create placeholders for the base abstraction or global # tunable since we're not adjusting the base directory include = self._create_tmp_base_dir(abstractions=[abstraction]) args = ['--abstractions=' + abstraction, '--Include=' + include] p = self._gen_policy(extra_args=args) search = "#include ".format(abstraction) self.assertTrue(search in p, "Could not find '{}' in:\n{}".format(search, p)) inv_s = '###ABSTRACTIONS###' self.assertFalse(inv_s in p, "Found '{}' in :\n{}".format(inv_s, p)) def test_genpolicy_abstractions_custom_include_bad(self): """Test genpolicy (custom include dir - bad include dirs)""" abstraction = "custom-include-dir-test-abstraction" bad = [None, '/etc/apparmor.d', '/'] for include in bad: try: args = ['--abstractions=' + abstraction] if include: args.append('--Include={}'.format(include)) self._gen_policy(extra_args=args) except AppArmorException: continue raise Exception("abstraction '{}' should be invalid".format(abstraction)) def test_genpolicy_profile_name_bad(self): """Test genpolicy (profile name - bad values)""" bad = [ "/../../../../etc/passwd", "../../../../etc/passwd", "profile name with spaces", ] for s in bad: try: self._gen_policy(extra_args=['--profile-name=' + s]) except AppArmorException: continue raise Exception("profile_name '{}' should be invalid".format(s)) def test_genpolicy_policy_group_bad(self): """Test genpolicy (policy group - bad values)""" bad = [ "/../../../../etc/passwd", "../../../../etc/passwd", "profile name with spaces", ] for s in bad: try: self._gen_policy(extra_args=['--policy-groups=' + s]) except AppArmorException: continue raise Exception("policy group '{}' should be invalid".format(s)) def test_genpolicy_policygroups(self): """Test genpolicy (single policygroup)""" groups = self.test_policygroup p = self._gen_policy(extra_args=['--policy-groups=' + groups]) for s in ('#include ', '#include '): self.assertTrue(s in p, "Could not find '{}' in:\n{}".format(s, p)) inv_s = '###POLICYGROUPS###' self.assertFalse(inv_s in p, "Found '{}' in :\n{}".format(inv_s, p)) def test_genpolicy_policygroups_multiple(self): """Test genpolicy (multiple policygroups)""" test_policygroup2 = "test-policygroup2" contents = ''' # {} #include #include '''.format(self.test_policygroup) with open(os.path.join(self.tmpdir, 'policygroups', test_policygroup2), 'w') as f: f.write(contents) groups = "{},{}".format(self.test_policygroup, test_policygroup2) p = self._gen_policy(extra_args=['--policy-groups=' + groups]) for s in ('#include ', '#include ', '#include ', '#include '): self.assertTrue(s in p, "Could not find '{}' in:\n{}".format(s, p)) inv_s = '###POLICYGROUPS###' self.assertFalse(inv_s in p, "Found '{}' in :\n{}".format(inv_s, p)) def test_genpolicy_policygroups_nonexistent(self): """Test genpolicy (nonexistent policygroup)""" try: self._gen_policy(extra_args=['--policy-groups=nonexistent']) except AppArmorException: return raise Exception("policygroup should be invalid") def test_genpolicy_readpath_file(self): """Test genpolicy (read-path file)""" s = "/opt/test-foo" p = self._gen_policy(extra_args=['--read-path=' + s]) search = "{} rk,".format(s) self.assertTrue(search in p, "Could not find '{}' in:\n{}".format(search, p)) inv_s = '###READPATH###' self.assertFalse(inv_s in p, "Found '{}' in :\n{}".format(inv_s, p)) def test_genpolicy_readpath_home_file(self): """Test genpolicy (read-path file in /home)""" s = "/home/*/test-foo" p = self._gen_policy(extra_args=['--read-path=' + s]) search = "owner {} rk,".format(s) self.assertTrue(search in p, "Could not find '{}' in:\n{}".format(search, p)) inv_s = '###READPATH###' self.assertFalse(inv_s in p, "Found '{}' in :\n{}".format(inv_s, p)) def test_genpolicy_readpath_homevar_file(self): """Test genpolicy (read-path file in @{HOME})""" s = "@{HOME}/test-foo" p = self._gen_policy(extra_args=['--read-path=' + s]) search = "owner {} rk,".format(s) self.assertTrue(search in p, "Could not find '{}' in:\n{}".format(search, p)) inv_s = '###READPATH###' self.assertFalse(inv_s in p, "Found '{}' in :\n{}".format(inv_s, p)) def test_genpolicy_readpath_homedirs_file(self): """Test genpolicy (read-path file in @{HOMEDIRS})""" s = "@{HOMEDIRS}/test-foo" p = self._gen_policy(extra_args=['--read-path=' + s]) search = "owner {} rk,".format(s) self.assertTrue(search in p, "Could not find '{}' in:\n{}".format(search, p)) inv_s = '###READPATH###' self.assertFalse(inv_s in p, "Found '{}' in :\n{}".format(inv_s, p)) def test_genpolicy_readpath_dir(self): """Test genpolicy (read-path directory/)""" s = "/opt/test-foo-dir/" p = self._gen_policy(extra_args=['--read-path=' + s]) search_terms = ["{} rk,".format(s), "{}** rk,".format(s)] for search in search_terms: self.assertTrue(search in p, "Could not find '{}' in:\n{}".format(search, p)) inv_s = '###READPATH###' self.assertFalse(inv_s in p, "Found '{}' in :\n{}".format(inv_s, p)) def test_genpolicy_readpath_dir_glob(self): """Test genpolicy (read-path directory/*)""" s = "/opt/test-foo-dir/*" p = self._gen_policy(extra_args=['--read-path=' + s]) search_terms = ["{} rk,".format(os.path.dirname(s)), "{} rk,".format(s)] for search in search_terms: self.assertTrue(search in p, "Could not find '{}' in:\n{}".format(search, p)) inv_s = '###READPATH###' self.assertFalse(inv_s in p, "Found '{}' in :\n{}".format(inv_s, p)) def test_genpolicy_readpath_dir_glob_all(self): """Test genpolicy (read-path directory/**)""" s = "/opt/test-foo-dir/**" p = self._gen_policy(extra_args=['--read-path=' + s]) search_terms = ["{} rk,".format(os.path.dirname(s)), "{} rk,".format(s)] for search in search_terms: self.assertTrue(search in p, "Could not find '{}' in:\n{}".format(search, p)) inv_s = '###READPATH###' self.assertFalse(inv_s in p, "Found '{}' in :\n{}".format(inv_s, p)) def test_genpolicy_readpath_multiple(self): """Test genpolicy (read-path multiple)""" paths = [ "/opt/test-foo", "/home/*/test-foo", "@{HOME}/test-foo", "@{HOMEDIRS}/test-foo", "/opt/test-foo-dir/", "/opt/test-foo-dir/*", "/opt/test-foo-dir/**", ] args = [] search_terms = [] for s in paths: args.append('--read-path=' + s) # This mimics easyprof.gen_path_rule() owner = "" if s.startswith('/home/') or s.startswith("@{HOME"): owner = "owner " if s.endswith('/'): search_terms.append("{} rk,".format(s)) search_terms.append("{}{}** rk,".format(owner, s)) elif s.endswith('/**') or s.endswith('/*'): search_terms.append("{} rk,".format(os.path.dirname(s))) search_terms.append("{}{} rk,".format(owner, s)) else: search_terms.append("{}{} rk,".format(owner, s)) p = self._gen_policy(extra_args=args) for search in search_terms: self.assertTrue(search in p, "Could not find '{}' in:\n{}".format(search, p)) inv_s = '###READPATH###' self.assertFalse(inv_s in p, "Found '{}' in :\n{}".format(inv_s, p)) def test_genpolicy_readpath_bad(self): """Test genpolicy (read-path bad)""" s = "bar" try: self._gen_policy(extra_args=['--read-path=' + s]) except AppArmorException: return raise Exception("read-path should be invalid") def test_genpolicy_writepath_file(self): """Test genpolicy (write-path file)""" s = "/opt/test-foo" p = self._gen_policy(extra_args=['--write-path=' + s]) search = "{} rwk,".format(s) self.assertTrue(search in p, "Could not find '{}' in:\n{}".format(search, p)) inv_s = '###READPATH###' self.assertFalse(inv_s in p, "Found '{}' in :\n{}".format(inv_s, p)) def test_genpolicy_writepath_home_file(self): """Test genpolicy (write-path file in /home)""" s = "/home/*/test-foo" p = self._gen_policy(extra_args=['--write-path=' + s]) search = "owner {} rwk,".format(s) self.assertTrue(search in p, "Could not find '{}' in:\n{}".format(search, p)) inv_s = '###READPATH###' self.assertFalse(inv_s in p, "Found '{}' in :\n{}".format(inv_s, p)) def test_genpolicy_writepath_homevar_file(self): """Test genpolicy (write-path file in @{HOME})""" s = "@{HOME}/test-foo" p = self._gen_policy(extra_args=['--write-path=' + s]) search = "owner {} rwk,".format(s) self.assertTrue(search in p, "Could not find '{}' in:\n{}".format(search, p)) inv_s = '###READPATH###' self.assertFalse(inv_s in p, "Found '{}' in :\n{}".format(inv_s, p)) def test_genpolicy_writepath_homedirs_file(self): """Test genpolicy (write-path file in @{HOMEDIRS})""" s = "@{HOMEDIRS}/test-foo" p = self._gen_policy(extra_args=['--write-path=' + s]) search = "owner {} rwk,".format(s) self.assertTrue(search in p, "Could not find '{}' in:\n{}".format(search, p)) inv_s = '###READPATH###' self.assertFalse(inv_s in p, "Found '{}' in :\n{}".format(inv_s, p)) def test_genpolicy_writepath_dir(self): """Test genpolicy (write-path directory/)""" s = "/opt/test-foo-dir/" p = self._gen_policy(extra_args=['--write-path=' + s]) search_terms = ["{} rwk,".format(s), "{}** rwk,".format(s)] for search in search_terms: self.assertTrue(search in p, "Could not find '{}' in:\n{}".format(search, p)) inv_s = '###READPATH###' self.assertFalse(inv_s in p, "Found '{}' in :\n{}".format(inv_s, p)) def test_genpolicy_writepath_dir_glob(self): """Test genpolicy (write-path directory/*)""" s = "/opt/test-foo-dir/*" p = self._gen_policy(extra_args=['--write-path=' + s]) search_terms = ["{} rwk,".format(os.path.dirname(s)), "{} rwk,".format(s)] for search in search_terms: self.assertTrue(search in p, "Could not find '{}' in:\n{}".format(search, p)) inv_s = '###READPATH###' self.assertFalse(inv_s in p, "Found '{}' in :\n{}".format(inv_s, p)) def test_genpolicy_writepath_dir_glob_all(self): """Test genpolicy (write-path directory/**)""" s = "/opt/test-foo-dir/**" p = self._gen_policy(extra_args=['--write-path=' + s]) search_terms = ["{} rwk,".format(os.path.dirname(s)), "{} rwk,".format(s)] for search in search_terms: self.assertTrue(search in p, "Could not find '{}' in:\n{}".format(search, p)) inv_s = '###READPATH###' self.assertFalse(inv_s in p, "Found '{}' in :\n{}".format(inv_s, p)) def test_genpolicy_writepath_multiple(self): """Test genpolicy (write-path multiple)""" paths = [ "/opt/test-foo", "/home/*/test-foo", "@{HOME}/test-foo", "@{HOMEDIRS}/test-foo", "/opt/test-foo-dir/", "/opt/test-foo-dir/*", "/opt/test-foo-dir/**", ] args = [] search_terms = [] for s in paths: args.append('--write-path=' + s) # This mimics easyprof.gen_path_rule() owner = "" if s.startswith('/home/') or s.startswith("@{HOME"): owner = "owner " if s.endswith('/'): search_terms.append("{} rwk,".format(s)) search_terms.append("{}{}** rwk,".format(owner, s)) elif s.endswith('/**') or s.endswith('/*'): search_terms.append("{} rwk,".format(os.path.dirname(s))) search_terms.append("{}{} rwk,".format(owner, s)) else: search_terms.append("{}{} rwk,".format(owner, s)) p = self._gen_policy(extra_args=args) for search in search_terms: self.assertTrue(search in p, "Could not find '{}' in:\n{}".format(search, p)) inv_s = '###READPATH###' self.assertFalse(inv_s in p, "Found '{}' in :\n{}".format(inv_s, p)) def test_genpolicy_writepath_bad(self): """Test genpolicy (write-path bad)""" s = "bar" try: self._gen_policy(extra_args=['--write-path=' + s]) except AppArmorException: return raise Exception("write-path should be invalid") def test_genpolicy_templatevar(self): """Test genpolicy (template-var single)""" s = "@{FOO}=bar" p = self._gen_policy(extra_args=['--template-var=' + s]) k, v = s.split('=') s = '{}="{}"'.format(k, v) self.assertTrue(s in p, "Could not find '{}' in:\n{}".format(s, p)) inv_s = '###TEMPLATEVAR###' self.assertFalse(inv_s in p, "Found '{}' in :\n{}".format(inv_s, p)) def test_genpolicy_templatevar_multiple(self): """Test genpolicy (template-var multiple)""" variables = ['@{FOO}=bar', '@{BAR}=baz'] args = [] for s in variables: args.append('--template-var=' + s) p = self._gen_policy(extra_args=args) for s in variables: k, v = s.split('=') s = '{}="{}"'.format(k, v) self.assertTrue(s in p, "Could not find '{}' in:\n{}".format(s, p)) inv_s = '###TEMPLATEVAR###' self.assertFalse(inv_s in p, "Found '{}' in :\n{}".format(inv_s, p)) def test_genpolicy_templatevar_bad(self): """Test genpolicy (template-var - bad values)""" bad = [ "{FOO}=bar", "@FOO}=bar", "@{FOO=bar", "FOO=bar", "@FOO=bar", "@{FOO}=/../../../etc/passwd", "@{FOO}=bar=foo", "@{FOO;BAZ}=bar", '@{FOO}=bar"baz', ] for s in bad: try: self._gen_policy(extra_args=['--template-var=' + s]) except AppArmorException: continue raise Exception("template-var should be invalid") def test_genpolicy_invalid_template_policy(self): """Test genpolicy (invalid template policy)""" # create a new template template = os.path.join(self.tmpdir, "test-invalid-template") shutil.copy(os.path.join(self.tmpdir, 'templates', self.test_template), template) with open(template) as f: contents = f.read() bad_pol = "" bad_string = "bzzzt" for line in contents.splitlines(): if '}' in line: bad_pol += bad_string else: bad_pol += line bad_pol += "\n" with open(template, 'w') as f: f.write(bad_pol) try: self._gen_policy(template=template) except AppArmorException: return raise Exception("policy should be invalid") def test_genpolicy_no_binary_without_profile_name(self): """Test genpolicy (no binary with no profile name)""" try: easyprof.gen_policy_params(None, self.options) except AppArmorException: return raise Exception("No binary or profile name should have been invalid") def test_genpolicy_with_binary_with_profile_name(self): """Test genpolicy (binary with profile name)""" profile_name = "some-profile-name" p = self._gen_policy(extra_args=['--profile-name=' + profile_name]) s = 'profile "%s" "%s" {' % (profile_name, self.binary) self.assertTrue(s in p, "Could not find '{}' in:\n{}".format(s, p)) inv_s = '###PROFILEATTACH###' self.assertFalse(inv_s in p, "Found '{}' in :\n{}".format(inv_s, p)) def test_genpolicy_with_binary_without_profile_name(self): """Test genpolicy (binary without profile name)""" p = self._gen_policy() s = '"%s" {' % (self.binary,) self.assertTrue(s in p, "Could not find '{}' in:\n{}".format(s, p)) inv_s = '###PROFILEATTACH###' self.assertFalse(inv_s in p, "Found '{}' in :\n{}".format(inv_s, p)) def test_genpolicy_without_binary_with_profile_name(self): """Test genpolicy (no binary with profile name)""" profile_name = "some-profile-name" args = self.full_args args.append('--profile-name=' + profile_name) (self.options, self.args) = easyprof.parse_args(args) easyp = easyprof.AppArmorEasyProfile(None, self.options) params = easyprof.gen_policy_params(None, self.options) p = easyp.gen_policy(**params) s = 'profile "%s" {' % (profile_name,) self.assertTrue(s in p, "Could not find '{}' in:\n{}".format(s, p)) inv_s = '###PROFILEATTACH###' self.assertFalse(inv_s in p, "Found '{}' in :\n{}".format(inv_s, p)) # manifest tests def test_gen_manifest_policy_with_binary_with_profile_name(self): """Test gen_manifest_policy (binary with profile name)""" m = Manifest("test_gen_manifest_policy") m.add_binary(self.ls) self._gen_manifest_policy(m) def test_gen_manifest_policy_without_binary_with_profile_name(self): """Test gen_manifest_policy (no binary with profile name)""" m = Manifest("test_gen_manifest_policy") self._gen_manifest_policy(m) def test_gen_manifest_policy_templates_system(self): """Test gen_manifest_policy (system template)""" m = Manifest("test_gen_manifest_policy") m.add_template(self.test_template) self._gen_manifest_policy(m) def test_gen_manifest_policy_templates_system_noprefix(self): """Test gen_manifest_policy (system template, no security prefix)""" m = Manifest("test_gen_manifest_policy") m.add_template(self.test_template) self._gen_manifest_policy(m, use_security_prefix=False) def test_gen_manifest_abs_path_template(self): """Test gen_manifest_policy (abs path template)""" m = Manifest("test_gen_manifest_policy") m.add_template("/etc/shadow") try: self._gen_manifest_policy(m) except AppArmorException: return raise Exception("abs path template name should be invalid") def test_gen_manifest_escape_path_templates(self): """Test gen_manifest_policy (esc path template)""" m = Manifest("test_gen_manifest_policy") m.add_template("../../../../../../../../etc/shadow") try: self._gen_manifest_policy(m) except AppArmorException: return raise Exception("../ template name should be invalid") def test_gen_manifest_policy_templates_nonexistent(self): """Test gen manifest policy (nonexistent template)""" m = Manifest("test_gen_manifest_policy") m.add_template("nonexistent") try: self._gen_manifest_policy(m) except AppArmorException: return raise Exception("template should be invalid") def test_gen_manifest_policy_comment(self): """Test gen manifest policy (comment)""" s = "test comment" m = Manifest("test_gen_manifest_policy") m.add_comment(s) p = self._gen_manifest_policy(m) self.assertTrue(s in p, "Could not find '{}' in:\n{}".format(s, p)) inv_s = '###COMMENT###' self.assertFalse(inv_s in p, "Found '{}' in :\n{}".format(inv_s, p)) def test_gen_manifest_policy_author(self): """Test gen manifest policy (author)""" s = "Archibald Poindexter" m = Manifest("test_gen_manifest_policy") m.add_author(s) p = self._gen_manifest_policy(m) self.assertTrue(s in p, "Could not find '{}' in:\n{}".format(s, p)) inv_s = '###AUTHOR###' self.assertFalse(inv_s in p, "Found '{}' in :\n{}".format(inv_s, p)) def test_gen_manifest_policy_copyright(self): """Test genpolicy (copyright)""" s = "2112/01/01" m = Manifest("test_gen_manifest_policy") m.add_copyright(s) p = self._gen_manifest_policy(m) self.assertTrue(s in p, "Could not find '{}' in:\n{}".format(s, p)) inv_s = '###COPYRIGHT###' self.assertFalse(inv_s in p, "Found '{}' in :\n{}".format(inv_s, p)) def test_gen_manifest_policy_policygroups(self): """Test gen manifest policy (single policygroup)""" groups = self.test_policygroup m = Manifest("test_gen_manifest_policy") m.add_policygroups(groups) p = self._gen_manifest_policy(m) for s in ('#include ', '#include '): self.assertTrue(s in p, "Could not find '{}' in:\n{}".format(s, p)) inv_s = '###POLICYGROUPS###' self.assertFalse(inv_s in p, "Found '{}' in :\n{}".format(inv_s, p)) def test_gen_manifest_policy_policygroups_multiple(self): """Test genpolicy (multiple policygroups)""" test_policygroup2 = "test-policygroup2" contents = ''' # {} #include #include '''.format(self.test_policygroup) with open(os.path.join(self.tmpdir, 'policygroups', test_policygroup2), 'w') as f: f.write(contents) groups = "{},{}".format(self.test_policygroup, test_policygroup2) m = Manifest("test_gen_manifest_policy") m.add_policygroups(groups) p = self._gen_manifest_policy(m) for s in ('#include ', '#include ', '#include ', '#include '): self.assertTrue(s in p, "Could not find '{}' in:\n{}".format(s, p)) inv_s = '###POLICYGROUPS###' self.assertFalse(inv_s in p, "Found '{}' in :\n{}".format(inv_s, p)) def test_gen_manifest_policy_policygroups_nonexistent(self): """Test gen manifest policy (nonexistent policygroup)""" groups = "nonexistent" m = Manifest("test_gen_manifest_policy") m.add_policygroups(groups) try: self._gen_manifest_policy(m) except AppArmorException: return raise Exception("policygroup should be invalid") def test_gen_manifest_policy_templatevar(self): """Test gen manifest policy (template-var single)""" m = Manifest("test_gen_manifest_policy") m.add_template_variable("FOO", "bar") p = self._gen_manifest_policy(m) s = '@{FOO}="bar"' self.assertTrue(s in p, "Could not find '{}' in:\n{}".format(s, p)) inv_s = '###TEMPLATEVAR###' self.assertFalse(inv_s in p, "Found '{}' in :\n{}".format(inv_s, p)) def test_gen_manifest_policy_templatevar_multiple(self): """Test gen manifest policy (template-var multiple)""" variables = [["FOO", "bar"], ["BAR", "baz"]] m = Manifest("test_gen_manifest_policy") for s in variables: m.add_template_variable(s[0], s[1]) p = self._gen_manifest_policy(m) for s in variables: str_s = '@{%s}="%s"' % (s[0], s[1]) self.assertTrue(str_s in p, "Could not find '{}' in:\n{}".format(str_s, p)) inv_s = '###TEMPLATEVAR###' self.assertFalse(inv_s in p, "Found '{}' in :\n{}".format(inv_s, p)) def test_gen_manifest_policy_invalid_keys(self): """Test gen manifest policy (invalid keys)""" keys = [ 'config_file', 'debug', 'help', 'list-templates', 'list_templates', 'show-template', 'show_template', 'list-policy-groups', 'list_policy_groups', 'show-policy-group', 'show_policy_group', 'templates-dir', 'templates_dir', 'policy-groups-dir', 'policy_groups_dir', 'nonexistent', 'no_verify', ] args = self.full_args args.append("--manifest=/dev/null") (self.options, self.args) = easyprof.parse_args(args) for k in keys: security = dict() security["profile_name"] = "test-app" security[k] = "bad" j = json.dumps(security, indent=2) try: easyprof.parse_manifest(j, self.options) except AppArmorException: continue raise Exception("'{}' should be invalid".format(k)) def test_gen_manifest(self): """Test gen_manifest""" # this should come from manpage m = '''{ "security": { "profiles": { "com.example.foo": { "abstractions": [ "audio", "gnome" ], "author": "Your Name", "binary": "/opt/foo/**", "comment": "Unstructured single-line comment", "copyright": "Unstructured single-line copyright statement", "name": "My Foo App", "policy_groups": [ "opt-application", "user-application" ], "policy_vendor": "somevendor", "policy_version": 1.0, "read_path": [ "/tmp/foo_r", "/tmp/bar_r/" ], "template": "user-application", "template_variables": { "APPNAME": "foo", "VAR1": "bar", "VAR2": "baz" }, "write_path": [ "/tmp/foo_w", "/tmp/bar_w/" ] } } } }''' for d in ('policygroups', 'templates'): shutil.copytree(os.path.join(self.tmpdir, d), os.path.join(self.tmpdir, d, "somevendor/1.0")) args = self.full_args args.append("--manifest=/dev/null") (self.options, self.args) = easyprof.parse_args(args) (binary, self.options) = easyprof.parse_manifest(m, self.options)[0] easyp = easyprof.AppArmorEasyProfile(binary, self.options) params = easyprof.gen_policy_params(binary, self.options) # verify we get the same manifest back man_new = easyp.gen_manifest(params) self.assertEqual(m, man_new) def test_gen_manifest_ubuntu(self): """Test gen_manifest (ubuntu)""" # this should be based on the manpage (but use existing policy_groups # and template m = '''{ "security": { "profiles": { "com.ubuntu.developer.myusername.MyCoolApp": { "name": "MyCoolApp", "policy_groups": [ "opt-application", "user-application" ], "policy_vendor": "ubuntu", "policy_version": 1.0, "template": "user-application", "template_variables": { "APPNAME": "MyCoolApp", "APPVERSION": "0.1.2" } } } } }''' for d in ('policygroups', 'templates'): shutil.copytree(os.path.join(self.tmpdir, d), os.path.join(self.tmpdir, d, "ubuntu/1.0")) args = self.full_args args.append("--manifest=/dev/null") (self.options, self.args) = easyprof.parse_args(args) (binary, self.options) = easyprof.parse_manifest(m, self.options)[0] easyp = easyprof.AppArmorEasyProfile(binary, self.options) params = easyprof.gen_policy_params(binary, self.options) # verify we get the same manifest back man_new = easyp.gen_manifest(params) self.assertEqual(m, man_new) def test_parse_manifest_no_version(self): """Test parse_manifest (vendor with no version)""" # this should come from manpage m = '''{ "security": { "profiles": { "com.ubuntu.developer.myusername.MyCoolApp": { "policy_groups": [ "opt-application", "user-application" ], "policy_vendor": "ubuntu", "template": "user-application", "template_variables": { "APPNAME": "MyCoolApp", "APPVERSION": "0.1.2" } } } } }''' args = self.full_args args.append("--manifest=/dev/null") (self.options, self.args) = easyprof.parse_args(args) (binary, self.options) = easyprof.parse_manifest(m, self.options)[0] try: easyprof.AppArmorEasyProfile(binary, self.options) except AppArmorException: return raise Exception("Should have failed on missing version") def test_parse_manifest_no_vendor(self): """Test parse_manifest (version with no vendor)""" # this should come from manpage m = '''{ "security": { "profiles": { "com.ubuntu.developer.myusername.MyCoolApp": { "policy_groups": [ "opt-application", "user-application" ], "policy_version": 1.0, "template": "user-application", "template_variables": { "APPNAME": "MyCoolApp", "APPVERSION": "0.1.2" } } } } }''' args = self.full_args args.append("--manifest=/dev/null") (self.options, self.args) = easyprof.parse_args(args) (binary, self.options) = easyprof.parse_manifest(m, self.options)[0] try: easyprof.AppArmorEasyProfile(binary, self.options) except AppArmorException: return raise Exception("Should have failed on missing vendor") def test_parse_manifest_multiple(self): """Test parse_manifest_multiple""" m = '''{ "security": { "profiles": { "com.example.foo": { "abstractions": [ "audio", "gnome" ], "author": "Your Name", "binary": "/opt/foo/**", "comment": "Unstructured single-line comment", "copyright": "Unstructured single-line copyright statement", "name": "My Foo App", "policy_groups": [ "opt-application", "user-application" ], "read_path": [ "/tmp/foo_r", "/tmp/bar_r/" ], "template": "user-application", "template_variables": { "APPNAME": "foo", "VAR1": "bar", "VAR2": "baz" }, "write_path": [ "/tmp/foo_w", "/tmp/bar_w/" ] }, "com.ubuntu.developer.myusername.MyCoolApp": { "policy_groups": [ "opt-application" ], "policy_vendor": "ubuntu", "policy_version": 1.0, "template": "user-application", "template_variables": { "APPNAME": "MyCoolApp", "APPVERSION": "0.1.2" } } } } }''' for d in ('policygroups', 'templates'): shutil.copytree(os.path.join(self.tmpdir, d), os.path.join(self.tmpdir, d, "ubuntu/1.0")) args = self.full_args args.append("--manifest=/dev/null") (self.options, self.args) = easyprof.parse_args(args) profiles = easyprof.parse_manifest(m, self.options) for (binary, options) in profiles: easyp = easyprof.AppArmorEasyProfile(binary, options) params = easyprof.gen_policy_params(binary, options) easyp.gen_manifest(params) easyp.gen_policy(**params) # verify manifest tests def _verify_manifest(self, m, expected, invalid=False): args = self.full_args args.append("--manifest=/dev/null") (self.options, self.args) = easyprof.parse_args(args) try: (binary, options) = easyprof.parse_manifest(m, self.options)[0] except AppArmorException: if invalid: return raise params = easyprof.gen_policy_params(binary, options) if expected: self.assertTrue(easyprof.verify_manifest(params, args), "params={}\nmanifest={}".format(params, m)) else: self.assertFalse(easyprof.verify_manifest(params, args), "params={}\nmanifest={}".format(params, m)) def test_verify_manifest_full(self): """Test verify_manifest (full)""" m = '''{ "security": { "profiles": { "com.example.foo": { "abstractions": [ "base" ], "author": "Your Name", "binary": "/opt/com.example/foo/**", "comment": "some free-form single-line comment", "copyright": "Unstructured single-line copyright statement", "name": "foo", "policy_groups": [ "user-application", "opt-application" ], "template": "user-application", "template_variables": { "OK1": "foo", "OK2": "com.example.foo" } } } } }''' self._verify_manifest(m, expected=True) def test_verify_manifest_full_bad(self): """Test verify_manifest (full bad)""" m = '''{ "security": { "profiles": { "/com.example.foo": { "abstractions": [ "audio", "gnome" ], "author": "Your Name", "binary": "/usr/foo/**", "comment": "some free-form single-line comment", "copyright": "Unstructured single-line copyright statement", "name": "foo", "policy_groups": [ "user-application", "opt-application" ], "read_path": [ "/tmp/foo_r", "/tmp/bar_r/" ], "template": "user-application", "template_variables": { "VAR1": "f*o", "VAR2": "*foo", "VAR3": "fo*", "VAR4": "b{ar", "VAR5": "b{a,r}", "VAR6": "b}ar", "VAR7": "bar[0-9]", "VAR8": "b{ar", "VAR9": "/tmp/../etc/passwd" }, "write_path": [ "/tmp/foo_w", "/tmp/bar_w/" ] } } } }''' self._verify_manifest(m, expected=False, invalid=True) def test_verify_manifest_binary(self): """Test verify_manifest (binary in /usr)""" m = '''{ "security": { "profiles": { "com.example.foo": { "binary": "/usr/foo/**", "template": "user-application" } } } }''' self._verify_manifest(m, expected=True) def test_verify_manifest_profile_profile_name_bad(self): """Test verify_manifest (bad profile_name)""" m = '''{ "security": { "profiles": { "/foo": { "binary": "/opt/com.example/foo/**", "template": "user-application" } } } }''' self._verify_manifest(m, expected=False, invalid=True) m = '''{ "security": { "profiles": { "bin/*": { "binary": "/opt/com.example/foo/**", "template": "user-application" } } } }''' self._verify_manifest(m, expected=False) def test_verify_manifest_profile_profile_name(self): """Test verify_manifest (profile_name)""" m = '''{ "security": { "profiles": { "com.example.foo": { "binary": "/opt/com.example/foo/**", "template": "user-application" } } } }''' self._verify_manifest(m, expected=True) def test_verify_manifest_profile_abstractions(self): """Test verify_manifest (abstractions)""" m = '''{ "security": { "profiles": { "com.example.foo": { "binary": "/opt/com.example/foo/**", "template": "user-application", "abstractions": [ "base" ] } } } }''' self._verify_manifest(m, expected=True) def test_verify_manifest_profile_abstractions_bad(self): """Test verify_manifest (bad abstractions)""" m = '''{ "security": { "profiles": { "com.example.foo": { "binary": "/opt/com.example/foo/**", "template": "user-application", "abstractions": [ "user-tmp" ] } } } }''' self._verify_manifest(m, expected=False) def test_verify_manifest_profile_template_var(self): """Test verify_manifest (good template_var)""" m = '''{ "security": { "profiles": { "com.example.foo": { "binary": "/opt/com.example/something with spaces/**", "template": "user-application", "template_variables": { "OK1": "foo", "OK2": "com.example.foo", "OK3": "something with spaces" } } } } }''' self._verify_manifest(m, expected=True) def test_verify_manifest_profile_template_var_bad(self): """Test verify_manifest (bad template_var)""" for v in ('"VAR1": "f*o"', '"VAR2": "*foo"', '"VAR3": "fo*"', '"VAR4": "b{ar"', '"VAR5": "b{a,r}"', '"VAR6": "b}ar"', '"VAR7": "bar[0-9]"', '"VAR8": "b{ar"', '"VAR9": "foo/bar"' # this is valid, but potentially unsafe ): m = '''{ "security": { "profiles": { "com.example.foo": { "binary": "/opt/com.example/foo/**", "template": "user-application", "template_variables": { %s } } } } }''' % (v,) self._verify_manifest(m, expected=False) def test_manifest_invalid(self): """Test invalid manifest (parse error)""" m = '''{ "security": { "com.example.foo": { "binary": "/opt/com.example/foo/**", "template": "user-application", "abstractions": [ "base" ] }''' self._verify_manifest(m, expected=False, invalid=True) def test_manifest_invalid2(self): """Test invalid manifest (profile_name is not key)""" m = '''{ "security": { "binary": "/opt/com.example/foo/**", "template": "user-application", "abstractions": [ "base" ] } }''' self._verify_manifest(m, expected=False, invalid=True) def test_manifest_invalid3(self): """Test invalid manifest (profile_name in dict)""" m = '''{ "security": { "binary": "/opt/com.example/foo/**", "template": "user-application", "abstractions": [ "base" ], "profile_name": "com.example.foo" } }''' self._verify_manifest(m, expected=False, invalid=True) def test_manifest_invalid4(self): """Test invalid manifest (bad path in template var)""" for v in ('"VAR1": "/tmp/../etc/passwd"', '"VAR2": "./"', '"VAR3": "foo\"bar"', '"VAR4": "foo//bar"', ): m = '''{ "security": { "profiles": { "com.example.foo": { "binary": "/opt/com.example/foo/**", "template": "user-application", "template_variables": { %s } } } } }''' % (v,) args = self.full_args args.append("--manifest=/dev/null") (self.options, self.args) = easyprof.parse_args(args) (binary, options) = easyprof.parse_manifest(m, self.options)[0] params = easyprof.gen_policy_params(binary, options) try: easyprof.verify_manifest(params) except AppArmorException: return raise Exception("Should have failed with invalid variable declaration") # policy version tests def test_policy_vendor_manifest_nonexistent(self): """Test policy vendor via manifest (nonexistent)""" m = '''{ "security": { "profiles": { "com.example.foo": { "policy_vendor": "nonexistent", "policy_version": 1.0, "binary": "/opt/com.example/foo/**", "template": "user-application" } } } }''' # Build up our args args = self.full_args args.append("--manifest=/dev/null") (self.options, self.args) = easyprof.parse_args(args) (binary, self.options) = easyprof.parse_manifest(m, self.options)[0] try: easyprof.AppArmorEasyProfile(binary, self.options) except AppArmorException: return raise Exception("Should have failed with non-existent directory") def test_policy_version_manifest(self): """Test policy version via manifest (good)""" policy_vendor = "somevendor" policy_version = "1.0" policy_subdir = "{}/{}".format(policy_vendor, policy_version) m = '''{ "security": { "profiles": { "com.example.foo": { "policy_vendor": "%s", "policy_version": %s, "binary": "/opt/com.example/foo/**", "template": "user-application" } } } }''' % (policy_vendor, policy_version) for d in ('policygroups', 'templates'): shutil.copytree(os.path.join(self.tmpdir, d), os.path.join(self.tmpdir, d, policy_subdir)) # Build up our args args = self.full_args args.append("--manifest=/dev/null") (self.options, self.args) = easyprof.parse_args(args) (binary, self.options) = easyprof.parse_manifest(m, self.options)[0] easyp = easyprof.AppArmorEasyProfile(binary, self.options) tdir = os.path.join(self.tmpdir, 'templates', policy_subdir) for t in easyp.get_templates(): self.assertTrue(t.startswith(tdir)) pdir = os.path.join(self.tmpdir, 'policygroups', policy_subdir) for p in easyp.get_policy_groups(): self.assertTrue(p.startswith(pdir)) params = easyprof.gen_policy_params(binary, self.options) easyp.gen_policy(**params) def test_policy_vendor_version_args(self): """Test policy vendor and version via command line args (good)""" policy_version = "1.0" policy_vendor = "somevendor" policy_subdir = "{}/{}".format(policy_vendor, policy_version) # Create the directories for d in ('policygroups', 'templates'): shutil.copytree(os.path.join(self.tmpdir, d), os.path.join(self.tmpdir, d, policy_subdir)) # Build up our args args = self.full_args args.append("--policy-version=" + policy_version) args.append("--policy-vendor=" + policy_vendor) (self.options, self.args) = easyprof.parse_args(args) (self.options, self.args) = easyprof.parse_args(self.full_args + [self.binary]) easyp = easyprof.AppArmorEasyProfile(self.binary, self.options) tdir = os.path.join(self.tmpdir, 'templates', policy_subdir) for t in easyp.get_templates(): self.assertTrue(t.startswith(tdir), "'{}' does not start with '{}'".format(t, tdir)) pdir = os.path.join(self.tmpdir, 'policygroups', policy_subdir) for p in easyp.get_policy_groups(): self.assertTrue(p.startswith(pdir), "'{}' does not start with '{}'".format(p, pdir)) params = easyprof.gen_policy_params(self.binary, self.options) easyp.gen_policy(**params) def test_policy_vendor_args_nonexistent(self): """Test policy vendor via command line args (nonexistent)""" policy_vendor = "nonexistent" policy_version = "1.0" args = self.full_args args.append("--policy-version=" + policy_version) args.append("--policy-vendor=" + policy_vendor) (self.options, self.args) = easyprof.parse_args(args) (self.options, self.args) = easyprof.parse_args(self.full_args + [self.binary]) try: easyprof.AppArmorEasyProfile(self.binary, self.options) except AppArmorException: return raise Exception("Should have failed with non-existent directory") def test_policy_version_args_bad(self): """Test policy version via command line args (bad)""" bad = [ "../../../../../../etc", "notanumber", "v1.0a", "-1", ] for policy_version in bad: args = self.full_args args.append("--policy-version=" + policy_version) args.append("--policy-vendor=somevendor") (self.options, self.args) = easyprof.parse_args(args) (self.options, self.args) = easyprof.parse_args(self.full_args + [self.binary]) try: easyprof.AppArmorEasyProfile(self.binary, self.options) except AppArmorException: continue raise Exception("Should have failed with bad version") def test_policy_vendor_args_bad(self): """Test policy vendor via command line args (bad)""" bad = [ "../../../../../../etc", "vendor with space", "semicolon;isbad", ] for policy_vendor in bad: args = self.full_args args.append("--policy-vendor=" + policy_vendor) args.append("--policy-version=1.0") (self.options, self.args) = easyprof.parse_args(args) (self.options, self.args) = easyprof.parse_args(self.full_args + [self.binary]) try: easyprof.AppArmorEasyProfile(self.binary, self.options) except AppArmorException: continue raise Exception("Should have failed with bad vendor") # output_directory tests def test_output_directory_multiple(self): """Test output_directory (multiple)""" files = dict() files["com.example.foo"] = "com.example.foo" files["com.ubuntu.developer.myusername.MyCoolApp"] = "com.ubuntu.developer.myusername.MyCoolApp" files["usr.bin.baz"] = "/usr/bin/baz" m = '''{ "security": { "profiles": { "%s": { "abstractions": [ "audio", "gnome" ], "author": "Your Name", "binary": "/opt/foo/**", "comment": "Unstructured single-line comment", "copyright": "Unstructured single-line copyright statement", "name": "My Foo App", "policy_groups": [ "opt-application", "user-application" ], "read_path": [ "/tmp/foo_r", "/tmp/bar_r/" ], "template": "user-application", "template_variables": { "APPNAME": "foo", "VAR1": "bar", "VAR2": "baz" }, "write_path": [ "/tmp/foo_w", "/tmp/bar_w/" ] }, "%s": { "policy_groups": [ "opt-application", "user-application" ], "template": "user-application", "template_variables": { "APPNAME": "MyCoolApp", "APPVERSION": "0.1.2" } }, "%s": { "abstractions": [ "gnome" ], "policy_groups": [ "user-application" ], "template_variables": { "APPNAME": "baz" } } } } }''' % ( files["com.example.foo"], files["com.ubuntu.developer.myusername.MyCoolApp"], files["usr.bin.baz"] ) out_dir = os.path.join(self.tmpdir, "output") args = self.full_args args.append("--manifest=/dev/null") (self.options, self.args) = easyprof.parse_args(args) profiles = easyprof.parse_manifest(m, self.options) for (binary, options) in profiles: easyp = easyprof.AppArmorEasyProfile(binary, options) params = easyprof.gen_policy_params(binary, options) easyp.output_policy(params, dir=out_dir) for fn in files: f = os.path.join(out_dir, fn) self.assertTrue(os.path.exists(f), "Could not find '{}'".format(f)) def test_output_directory_single(self): """Test output_directory (single)""" files = dict() files["com.example.foo"] = "com.example.foo" m = '''{ "security": { "profiles": { "%s": { "abstractions": [ "audio", "gnome" ], "author": "Your Name", "binary": "/opt/foo/**", "comment": "Unstructured single-line comment", "copyright": "Unstructured single-line copyright statement", "name": "My Foo App", "policy_groups": [ "opt-application", "user-application" ], "read_path": [ "/tmp/foo_r", "/tmp/bar_r/" ], "template": "user-application", "template_variables": { "APPNAME": "foo", "VAR1": "bar", "VAR2": "baz" }, "write_path": [ "/tmp/foo_w", "/tmp/bar_w/" ] } } } }''' % (files["com.example.foo"],) out_dir = os.path.join(self.tmpdir, "output") args = self.full_args args.append("--manifest=/dev/null") (self.options, self.args) = easyprof.parse_args(args) profiles = easyprof.parse_manifest(m, self.options) for (binary, options) in profiles: easyp = easyprof.AppArmorEasyProfile(binary, options) params = easyprof.gen_policy_params(binary, options) easyp.output_policy(params, dir=out_dir) for fn in files: f = os.path.join(out_dir, fn) self.assertTrue(os.path.exists(f), "Could not find '{}'".format(f)) def test_output_directory_invalid(self): """Test output_directory (output directory exists as file)""" files = dict() files["usr.bin.baz"] = "/usr/bin/baz" m = '''{ "security": { "profiles": { "%s": { "abstractions": [ "gnome" ], "policy_groups": [ "user-application" ], "template_variables": { "APPNAME": "baz" } } } } }''' % (files["usr.bin.baz"],) out_dir = os.path.join(self.tmpdir, "output") open(out_dir, 'w').close() args = self.full_args args.append("--manifest=/dev/null") (self.options, self.args) = easyprof.parse_args(args) (binary, options) = easyprof.parse_manifest(m, self.options)[0] easyp = easyprof.AppArmorEasyProfile(binary, options) params = easyprof.gen_policy_params(binary, options) try: easyp.output_policy(params, dir=out_dir) except AppArmorException: return raise Exception("Should have failed with 'is not a directory'") def test_output_directory_invalid_params(self): """Test output_directory (no binary or profile_name)""" files = dict() files["usr.bin.baz"] = "/usr/bin/baz" m = '''{ "security": { "profiles": { "%s": { "abstractions": [ "gnome" ], "policy_groups": [ "user-application" ], "template_variables": { "APPNAME": "baz" } } } } }''' % (files["usr.bin.baz"],) out_dir = os.path.join(self.tmpdir, "output") args = self.full_args args.append("--manifest=/dev/null") (self.options, self.args) = easyprof.parse_args(args) (binary, options) = easyprof.parse_manifest(m, self.options)[0] easyp = easyprof.AppArmorEasyProfile(binary, options) params = easyprof.gen_policy_params(binary, options) del params['binary'] try: easyp.output_policy(params, dir=out_dir) except AppArmorException: return raise Exception("Should have failed with 'Must specify binary and/or profile name'") def test_output_directory_invalid2(self): """Test output_directory (profile exists)""" files = dict() files["usr.bin.baz"] = "/usr/bin/baz" m = '''{ "security": { "profiles": { "%s": { "abstractions": [ "gnome" ], "policy_groups": [ "user-application" ], "template_variables": { "APPNAME": "baz" } } } } }''' % (files["usr.bin.baz"],) out_dir = os.path.join(self.tmpdir, "output") os.mkdir(out_dir) open(os.path.join(out_dir, "usr.bin.baz"), 'w').close() args = self.full_args args.append("--manifest=/dev/null") (self.options, self.args) = easyprof.parse_args(args) (binary, options) = easyprof.parse_manifest(m, self.options)[0] easyp = easyprof.AppArmorEasyProfile(binary, options) params = easyprof.gen_policy_params(binary, options) try: easyp.output_policy(params, dir=out_dir) except AppArmorException: return raise Exception("Should have failed with 'already exists'") def test_output_directory_args(self): """Test output_directory (args)""" files = dict() files["usr.bin.baz"] = "/usr/bin/baz" # Build up our args args = self.full_args args.append('--template=' + self.test_template) args.append('--name=foo') args.append(files["usr.bin.baz"]) out_dir = os.path.join(self.tmpdir, "output") # Now parse our args (self.options, self.args) = easyprof.parse_args(args) easyp = easyprof.AppArmorEasyProfile(files["usr.bin.baz"], self.options) params = easyprof.gen_policy_params(files["usr.bin.baz"], self.options) easyp.output_policy(params, dir=out_dir) for fn in files: f = os.path.join(out_dir, fn) self.assertTrue(os.path.exists(f), "Could not find '{}'".format(f)) # # utility classes # def test_valid_profile_name(self): """Test valid_profile_name""" names = [ 'foo', 'com.example.foo', '/usr/bin/foo', 'com.example.app_myapp_1:2.3+ab12~foo', ] for n in names: self.assertTrue(easyprof.valid_profile_name(n), "'{}' should be valid".format(n)) def test_valid_profile_name_invalid(self): """Test valid_profile_name (invalid)""" names = [ 'fo/o', '/../../etc/passwd', '../../etc/passwd', './../etc/passwd', './etc/passwd', '/usr/bin//foo', '/usr/bin/./foo', 'foo`', 'foo!', 'foo@', 'foo$', 'foo#', 'foo%', 'foo^', 'foo&', 'foo*', 'foo(', 'foo)', 'foo=', 'foo{', 'foo}', 'foo[', 'foo]', 'foo|', 'foo/', 'foo\\', 'foo;', "foo'", 'foo"', 'foo<', 'foo>', 'foo?', r'foo\/', 'foo,', '_foo', ] for n in names: self.assertFalse(easyprof.valid_profile_name(n), "'{}' should be invalid".format(n)) def test_valid_path(self): """Test valid_path""" names = [ '/bin/bar', '/etc/apparmor.d/com.example.app_myapp_1:2.3+ab12~foo', ] names_rel = [ 'bin/bar', 'apparmor.d/com.example.app_myapp_1:2.3+ab12~foo', 'com.example.app_myapp_1:2.3+ab12~foo', ] for n in names: self.assertTrue(easyprof.valid_path(n), "'{}' should be valid".format(n)) for n in names_rel: self.assertTrue(easyprof.valid_path(n, relative_ok=True), "'{}' should be valid".format(n)) def test_zz_valid_path_invalid(self): """Test valid_path (invalid)""" names = [ '/bin//bar', 'bin/bar', '/../etc/passwd', './bin/bar', './', ] names_rel = [ 'bin/../bar', 'apparmor.d/../passwd', 'com.example.app_"myapp_1:2.3+ab12~foo', ] for n in names: self.assertFalse(easyprof.valid_path(n, relative_ok=False), "'{}' should be invalid".format(n)) for n in names_rel: self.assertFalse(easyprof.valid_path(n, relative_ok=True), "'{}' should be invalid".format(n)) # # End test class # # # Main # if __name__ == '__main__': absfn = os.path.abspath(sys.argv[0]) topdir = os.path.dirname(os.path.dirname(absfn)) if len(sys.argv) > 1 and (sys.argv[1] == '-d' or sys.argv[1] == '--debug'): debugging = True # run the tests suite = unittest.TestSuite() suite.addTest(unittest.TestLoader().loadTestsFromTestCase(T)) rc = unittest.TextTestRunner(verbosity=1).run(suite) if not rc.wasSuccessful(): sys.exit(1)