<gh_stars>0
#! /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

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 contents_only == False:
        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(object):
    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 not 'template_variables' 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 <tunables/global>

###VAR###

###PROFILEATTACH### {
  #include <abstractions/base>

  ###ABSTRACTIONS###

  ###POLICYGROUPS###

  ###READS###

  ###WRITES###
}

''' % (self.test_template)
        open(os.path.join(self.tmpdir, 'templates', self.test_template), 'w').write(contents)

        # Create a test policygroup
        self.test_policygroup = "test-policygroup"
        contents = '''
  # %s
  #include <abstractions/gnome>
  #include <abstractions/nameservice>
''' % (self.test_policygroup)
        open(os.path.join(self.tmpdir, 'policygroups', self.test_policygroup), 'w').write(contents)

        # setup our conffile
        self.conffile = os.path.join(self.tmpdir, 'easyprof.conf')
        contents = '''
POLICYGROUPS_DIR="%s/policygroups"
TEMPLATES_DIR="%s/templates"
''' % (self.tmpdir, self.tmpdir)
        open(self.conffile, 'w').write(contents)

        self.binary = "/opt/bin/foo"
        self.full_args = ['-c', self.conffile, self.binary]

        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_%s" % os.path.basename(f)))

    def tearDown(self):
        '''Teardown for tests'''
        if os.path.exists(self.tmpdir):
            if debugging:
                sys.stdout.write("%s\n" % self.tmpdir)
            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="%s/templates"
''' % (self.tmpdir)

        open(self.conffile, 'w').write(contents)
        try:
            easyprof.AppArmorEasyProfile(self.binary, self.options)
        except easyprof.AppArmorException:
            return
        except Exception:
            raise

        raise Exception ("File should have been invalid")

    def test_configuration_file_p_empty(self):
        '''Test config parsing (empty POLICYGROUPS_DIR)'''
        contents = '''
POLICYGROUPS_DIR="%s"
TEMPLATES_DIR="%s/templates"
''' % ('', self.tmpdir)

        open(self.conffile, 'w').write(contents)
        try:
            easyprof.AppArmorEasyProfile(self.binary, self.options)
        except easyprof.AppArmorException:
            return
        except Exception:
            raise

        raise Exception ("File should have been invalid")

    def test_configuration_file_p_nonexistent(self):
        '''Test config parsing (nonexistent POLICYGROUPS_DIR)'''
        contents = '''
POLICYGROUPS_DIR="%s/policygroups"
TEMPLATES_DIR="%s/templates"
''' % ('/nonexistent', self.tmpdir)

        open(self.conffile, 'w').write(contents)
        try:
            easyprof.AppArmorEasyProfile(self.binary, self.options)
        except easyprof.AppArmorException:
            return
        except Exception:
            raise

        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=%s' % 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: %s\nActual dir: %s" % (rel, str(easyp.dirs['policygroups'])))
        self.assertFalse(easyp.get_policy_groups() == 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=%s' % 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() != 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=%s' % 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() == 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=%s' % 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() == None, "Could not find policy-groups")
        for f in easyp.get_policy_groups():
            self.assertFalse(os.path.basename(f) == vendor, "Found '%s' in %s" % (vendor, f))

    def test_configuration_file_t_invalid(self):
        '''Test config parsing (invalid TEMPLATES_DIR)'''
        contents = '''
TEMPLATES_DIR=
POLICYGROUPS_DIR="%s/templates"
''' % (self.tmpdir)

        open(self.conffile, 'w').write(contents)
        try:
            easyprof.AppArmorEasyProfile(self.binary, self.options)
        except easyprof.AppArmorException:
            return
        except Exception:
            raise

        raise Exception ("File should have been invalid")

    def test_configuration_file_t_empty(self):
        '''Test config parsing (empty TEMPLATES_DIR)'''
        contents = '''
TEMPLATES_DIR="%s"
POLICYGROUPS_DIR="%s/templates"
''' % ('', self.tmpdir)

        open(self.conffile, 'w').write(contents)
        try:
            easyprof.AppArmorEasyProfile(self.binary, self.options)
        except easyprof.AppArmorException:
            return
        except Exception:
            raise

        raise Exception ("File should have been invalid")

    def test_configuration_file_t_nonexistent(self):
        '''Test config parsing (nonexistent TEMPLATES_DIR)'''
        contents = '''
TEMPLATES_DIR="%s/policygroups"
POLICYGROUPS_DIR="%s/templates"
''' % ('/nonexistent', self.tmpdir)

        open(self.conffile, 'w').write(contents)
        try:
            easyprof.AppArmorEasyProfile(self.binary, self.options)
        except easyprof.AppArmorException:
            return
        except Exception:
            raise

        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=%s' % 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: %s\nActual dir: %s" % (rel, str(easyp.dirs['templates'])))
        self.assertFalse(easyp.get_templates() == 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=%s' % 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() != 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=%s' % 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() == 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=%s' % 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() == None, "Could not find templates")
        for f in easyp.get_templates():
            self.assertFalse(os.path.basename(f) == vendor, "Found '%s' in %s" % (vendor, f))

#
# Binary file tests
#
    def test_binary_without_profile_name(self):
        '''Test binary (<binary> { })'''
        easyprof.AppArmorEasyProfile(self.ls, self.options)

    def test_binary_with_profile_name(self):
        '''Test binary (profile <name> <binary> { })'''
        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 <name> { })'''
        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 easyprof.AppArmorException:
            return
        except Exception:
            raise
        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 easyprof.AppArmorException:
            return
        except Exception:
            raise
        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 '%s'" % i)

    def test_templates_show(self):
        '''Test templates (show)'''
        files = []
        for f in glob.glob("%s/templates/*" % self.tmpdir):
            files.append(f)

        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 '%s'" % path)
            open(path).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=%s' %
                    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: %s" % inc_templates)

        for i in inc_templates:
            self.assertTrue(os.path.exists(i), "Could not find '%s'" % i)

    def test_templates_show_include(self):
        '''Test templates (show with --include-templates-dir)'''
        files = []
        for f in glob.glob("%s/templates/*" % self.test_include_dir):
            files.append(f)

        for f in files:
            args = self.full_args
            args += ['--show-template',
                     '--template', f,
                     '--include-templates-dir=%s' %
                     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 '%s'" % path)
            open(path).read()

            bn = os.path.basename(f)
            # setup() copies everything in the include prefixed with inc_
            self.assertTrue(bn.startswith('inc_'),
                            "'%s' does not start with 'inc_'" % 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 '%s'" % i)

    def test_policygroups_show(self):
        '''Test policygroups (show)'''
        files = []
        for f in glob.glob("%s/policygroups/*" % self.tmpdir):
            files.append(f)

        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 '%s'" % path)
            open(path).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=%s' %
                    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: %s" % inc_policy_groups)

        for i in inc_policy_groups:
            self.assertTrue(os.path.exists(i), "Could not find '%s'" % i)

    def test_policygroups_show_include(self):
        '''Test policygroups (show with --include-policy-groups-dir)'''
        files = []
        for f in glob.glob("%s/policygroups/*" % self.test_include_dir):
            files.append(f)

        for f in files:
            args = self.full_args
            args += ['--show-policy-group',
                     '--policy-groups', os.path.basename(f),
                     '--include-policy-groups-dir=%s' %
                     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 '%s'" % path)
            open(path).read()

            bn = os.path.basename(f)
            # setup() copies everything in the include prefixed with inc_
            self.assertTrue(bn.startswith('inc_'),
                            "'%s' does not start with 'inc_'" % 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"}}}
'''
        open(self.manifest, 'w').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"}}}
'''
        open(self.manifest, 'w').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="%s and manifest arguments did not " \
                                    "raise a parse error" % opt)

        # 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="%s and manifest arguments did not " \
                                    "raise a parse error" % opt)

    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=[]):
        '''Generate a policy'''
        # Build up our args
        args = self.full_args

        if template == None:
            args.append('--template=%s' % self.test_template)
        else:
            args.append('--template=%s' % template)

        if name != None:
            args.append('--name=%s' % name)

        if len(extra_args) > 0:
            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 != None:
            search_terms.append(name)

        if template == None:
            search_terms.append(self.test_template)

        for s in search_terms:
            self.assertTrue(s in p, "Could not find '%s' in:\n%s" % (s, p))

        # ###NAME### should be replaced with self.binary or 'name'. Check for that
        inv_s = '###NAME###'
        self.assertFalse(inv_s in p, "Found '%s' in :\n%s" % (inv_s, p))

        if debugging:
            sys.stdout.write("%s\n" % p)

        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 '%s' in :\n%s" % (inv_s, p))

        if debugging:
            sys.stdout.write("%s\n" % p)

        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), "'%s' should be bad" %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)
        contents = open(template).read()
        test_string = "#teststring"
        open(template, 'w').write(contents + "\n%s\n" % test_string)

        p = self._gen_policy(template=template)

        for s in [self.test_template, test_string]:
            self.assertTrue(s in p, "Could not find '%s' in:\n%s" % (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 easyprof.AppArmorException:
            return
        except Exception:
            raise
        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' % s])
        self.assertTrue(s in p, "Could not find '%s' in:\n%s" % (s, p))
        inv_s = '###COMMENT###'
        self.assertFalse(inv_s in p, "Found '%s' in :\n%s" % (inv_s, p))

    def test_genpolicy_author(self):
        '''Test genpolicy (author)'''
        s = "<NAME>"
        p = self._gen_policy(extra_args=['--author=%s' % s])
        self.assertTrue(s in p, "Could not find '%s' in:\n%s" % (s, p))
        inv_s = '###AUTHOR###'
        self.assertFalse(inv_s in p, "Found '%s' in :\n%s" % (inv_s, p))

    def test_genpolicy_copyright(self):
        '''Test genpolicy (copyright)'''
        s = "2112/01/01"
        p = self._gen_policy(extra_args=['--copyright=%s' % s])
        self.assertTrue(s in p, "Could not find '%s' in:\n%s" % (s, p))
        inv_s = '###COPYRIGHT###'
        self.assertFalse(inv_s in p, "Found '%s' in :\n%s" % (inv_s, p))

    def test_genpolicy_abstractions(self):
        '''Test genpolicy (single abstraction)'''
        s = "nameservice"
        p = self._gen_policy(extra_args=['--abstractions=%s' % s])
        search = "#include <abstractions/%s>" % s
        self.assertTrue(search in p, "Could not find '%s' in:\n%s" % (search, p))
        inv_s = '###ABSTRACTIONS###'
        self.assertFalse(inv_s in p, "Found '%s' in :\n%s" % (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=%s' % abstractions])
        for s in abstractions.split(','):
            search = "#include <abstractions/%s>" % s
            self.assertTrue(search in p, "Could not find '%s' in:\n%s" % (search, p))
        inv_s = '###ABSTRACTIONS###'
        self.assertFalse(inv_s in p, "Found '%s' in :\n%s" % (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' % s])
            except easyprof.AppArmorException:
                continue
            except Exception:
                raise
            raise Exception ("abstraction '%s' should be invalid" % s)

    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' % s])
            except easyprof.AppArmorException:
                continue
            except Exception:
                raise
            raise Exception ("profile_name '%s' should be invalid" % 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' % s])
            except easyprof.AppArmorException:
                continue
            except Exception:
                raise
            raise Exception ("policy group '%s' should be invalid" % s)

    def test_genpolicy_policygroups(self):
        '''Test genpolicy (single policygroup)'''
        groups = self.test_policygroup
        p = self._gen_policy(extra_args=['--policy-groups=%s' % groups])

        for s in ['#include <abstractions/nameservice>', '#include <abstractions/gnome>']:
            self.assertTrue(s in p, "Could not find '%s' in:\n%s" % (s, p))
        inv_s = '###POLICYGROUPS###'
        self.assertFalse(inv_s in p, "Found '%s' in :\n%s" % (inv_s, p))

    def test_genpolicy_policygroups_multiple(self):
        '''Test genpolicy (multiple policygroups)'''
        test_policygroup2 = "test-policygroup2"
        contents = '''
  # %s
  #include <abstractions/kde>
  #include <abstractions/openssl>
''' % (self.test_policygroup)
        open(os.path.join(self.tmpdir, 'policygroups', test_policygroup2), 'w').write(contents)

        groups = "%s,%s" % (self.test_policygroup, test_policygroup2)
        p = self._gen_policy(extra_args=['--policy-groups=%s' % groups])

        for s in ['#include <abstractions/nameservice>',
                  '#include <abstractions/gnome>',
                  '#include <abstractions/kde>',
                  '#include <abstractions/openssl>']:
            self.assertTrue(s in p, "Could not find '%s' in:\n%s" % (s, p))
        inv_s = '###POLICYGROUPS###'
        self.assertFalse(inv_s in p, "Found '%s' in :\n%s" % (inv_s, p))

    def test_genpolicy_policygroups_nonexistent(self):
        '''Test genpolicy (nonexistent policygroup)'''
        try:
            self._gen_policy(extra_args=['--policy-groups=nonexistent'])
        except easyprof.AppArmorException:
            return
        except Exception:
            raise
        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' % s])
        search = "%s rk," % s
        self.assertTrue(search in p, "Could not find '%s' in:\n%s" % (search, p))
        inv_s = '###READPATH###'
        self.assertFalse(inv_s in p, "Found '%s' in :\n%s" % (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' % s])
        search = "owner %s rk," % s
        self.assertTrue(search in p, "Could not find '%s' in:\n%s" % (search, p))
        inv_s = '###READPATH###'
        self.assertFalse(inv_s in p, "Found '%s' in :\n%s" % (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' % s])
        search = "owner %s rk," % s
        self.assertTrue(search in p, "Could not find '%s' in:\n%s" % (search, p))
        inv_s = '###READPATH###'
        self.assertFalse(inv_s in p, "Found '%s' in :\n%s" % (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' % s])
        search = "owner %s rk," % s
        self.assertTrue(search in p, "Could not find '%s' in:\n%s" % (search, p))
        inv_s = '###READPATH###'
        self.assertFalse(inv_s in p, "Found '%s' in :\n%s" % (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' % s])
        search_terms = ["%s rk," % s, "%s** rk," % s]
        for search in search_terms:
            self.assertTrue(search in p, "Could not find '%s' in:\n%s" % (search, p))
        inv_s = '###READPATH###'
        self.assertFalse(inv_s in p, "Found '%s' in :\n%s" % (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' % s])
        search_terms = ["%s rk," % os.path.dirname(s), "%s rk," % s]
        for search in search_terms:
            self.assertTrue(search in p, "Could not find '%s' in:\n%s" % (search, p))
        inv_s = '###READPATH###'
        self.assertFalse(inv_s in p, "Found '%s' in :\n%s" % (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' % s])
        search_terms = ["%s rk," % os.path.dirname(s), "%s rk," % s]
        for search in search_terms:
            self.assertTrue(search in p, "Could not find '%s' in:\n%s" % (search, p))
        inv_s = '###READPATH###'
        self.assertFalse(inv_s in p, "Found '%s' in :\n%s" % (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' % s)
            # This mimics easyprof.gen_path_rule()
            owner = ""
            if s.startswith('/home/') or s.startswith("@{HOME"):
                owner = "owner "
            if s.endswith('/'):
                search_terms.append("%s rk," % (s))
                search_terms.append("%s%s** rk," % (owner, s))
            elif s.endswith('/**') or s.endswith('/*'):
                search_terms.append("%s rk," % (os.path.dirname(s)))
                search_terms.append("%s%s rk," % (owner, s))
            else:
                search_terms.append("%s%s rk," % (owner, s))

        p = self._gen_policy(extra_args=args)
        for search in search_terms:
            self.assertTrue(search in p, "Could not find '%s' in:\n%s" % (search, p))
        inv_s = '###READPATH###'
        self.assertFalse(inv_s in p, "Found '%s' in :\n%s" % (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' % s])
        except easyprof.AppArmorException:
            return
        except Exception:
            raise
        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' % s])
        search = "%s rwk," % s
        self.assertTrue(search in p, "Could not find '%s' in:\n%s" % (search, p))
        inv_s = '###READPATH###'
        self.assertFalse(inv_s in p, "Found '%s' in :\n%s" % (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' % s])
        search = "owner %s rwk," % s
        self.assertTrue(search in p, "Could not find '%s' in:\n%s" % (search, p))
        inv_s = '###READPATH###'
        self.assertFalse(inv_s in p, "Found '%s' in :\n%s" % (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' % s])
        search = "owner %s rwk," % s
        self.assertTrue(search in p, "Could not find '%s' in:\n%s" % (search, p))
        inv_s = '###READPATH###'
        self.assertFalse(inv_s in p, "Found '%s' in :\n%s" % (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' % s])
        search = "owner %s rwk," % s
        self.assertTrue(search in p, "Could not find '%s' in:\n%s" % (search, p))
        inv_s = '###READPATH###'
        self.assertFalse(inv_s in p, "Found '%s' in :\n%s" % (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' % s])
        search_terms = ["%s rwk," % s, "%s** rwk," % s]
        for search in search_terms:
            self.assertTrue(search in p, "Could not find '%s' in:\n%s" % (search, p))
        inv_s = '###READPATH###'
        self.assertFalse(inv_s in p, "Found '%s' in :\n%s" % (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' % s])
        search_terms = ["%s rwk," % os.path.dirname(s), "%s rwk," % s]
        for search in search_terms:
            self.assertTrue(search in p, "Could not find '%s' in:\n%s" % (search, p))
        inv_s = '###READPATH###'
        self.assertFalse(inv_s in p, "Found '%s' in :\n%s" % (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' % s])
        search_terms = ["%s rwk," % os.path.dirname(s), "%s rwk," % s]
        for search in search_terms:
            self.assertTrue(search in p, "Could not find '%s' in:\n%s" % (search, p))
        inv_s = '###READPATH###'
        self.assertFalse(inv_s in p, "Found '%s' in :\n%s" % (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' % s)
            # This mimics easyprof.gen_path_rule()
            owner = ""
            if s.startswith('/home/') or s.startswith("@{HOME"):
                owner = "owner "
            if s.endswith('/'):
                search_terms.append("%s rwk," % (s))
                search_terms.append("%s%s** rwk," % (owner, s))
            elif s.endswith('/**') or s.endswith('/*'):
                search_terms.append("%s rwk," % (os.path.dirname(s)))
                search_terms.append("%s%s rwk," % (owner, s))
            else:
                search_terms.append("%s%s rwk," % (owner, s))

        p = self._gen_policy(extra_args=args)
        for search in search_terms:
            self.assertTrue(search in p, "Could not find '%s' in:\n%s" % (search, p))
        inv_s = '###READPATH###'
        self.assertFalse(inv_s in p, "Found '%s' in :\n%s" % (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' % s])
        except easyprof.AppArmorException:
            return
        except Exception:
            raise
        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' % s])
        k, v = s.split('=')
        s = '%s="%s"' % (k, v)
        self.assertTrue(s in p, "Could not find '%s' in:\n%s" % (s, p))
        inv_s = '###TEMPLATEVAR###'
        self.assertFalse(inv_s in p, "Found '%s' in :\n%s" % (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' % s)

        p = self._gen_policy(extra_args=args)
        for s in variables:
            k, v = s.split('=')
            s = '%s="%s"' % (k, v)
            self.assertTrue(s in p, "Could not find '%s' in:\n%s" % (s, p))
            inv_s = '###TEMPLATEVAR###'
            self.assertFalse(inv_s in p, "Found '%s' in :\n%s" % (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' % s])
            except easyprof.AppArmorException:
                continue
            except Exception:
                raise
            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)
        contents = open(template).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"
        open(template, 'w').write(bad_pol)
        try:
            self._gen_policy(template=template)
        except easyprof.AppArmorException:
            return
        except Exception:
            raise
        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 easyprof.AppArmorException:
            return
        except Exception:
            raise
        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=%s' % profile_name])
        s = 'profile "%s" "%s" {' % (profile_name, self.binary)
        self.assertTrue(s in p, "Could not find '%s' in:\n%s" % (s, p))
        inv_s = '###PROFILEATTACH###'
        self.assertFalse(inv_s in p, "Found '%s' in :\n%s" % (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 '%s' in:\n%s" % (s, p))
        inv_s = '###PROFILEATTACH###'
        self.assertFalse(inv_s in p, "Found '%s' in :\n%s" % (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=%s' % 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 '%s' in:\n%s" % (s, p))
        inv_s = '###PROFILEATTACH###'
        self.assertFalse(inv_s in p, "Found '%s' in :\n%s" % (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 easyprof.AppArmorException:
            return
        except Exception:
            raise
        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 easyprof.AppArmorException:
            return
        except Exception:
            raise
        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 easyprof.AppArmorException:
            return
        except Exception:
            raise
        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 '%s' in:\n%s" % (s, p))
        inv_s = '###COMMENT###'
        self.assertFalse(inv_s in p, "Found '%s' in :\n%s" % (inv_s, p))

    def test_gen_manifest_policy_author(self):
        '''Test gen manifest policy (author)'''
        s = "<NAME>"
        m = Manifest("test_gen_manifest_policy")
        m.add_author(s)
        p = self._gen_manifest_policy(m)
        self.assertTrue(s in p, "Could not find '%s' in:\n%s" % (s, p))
        inv_s = '###AUTHOR###'
        self.assertFalse(inv_s in p, "Found '%s' in :\n%s" % (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 '%s' in:\n%s" % (s, p))
        inv_s = '###COPYRIGHT###'
        self.assertFalse(inv_s in p, "Found '%s' in :\n%s" % (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 <abstractions/nameservice>', '#include <abstractions/gnome>']:
            self.assertTrue(s in p, "Could not find '%s' in:\n%s" % (s, p))
        inv_s = '###POLICYGROUPS###'
        self.assertFalse(inv_s in p, "Found '%s' in :\n%s" % (inv_s, p))

    def test_gen_manifest_policy_policygroups_multiple(self):
        '''Test genpolicy (multiple policygroups)'''
        test_policygroup2 = "test-policygroup2"
        contents = '''
  # %s
  #include <abstractions/kde>
  #include <abstractions/openssl>
''' % (self.test_policygroup)
        open(os.path.join(self.tmpdir, 'policygroups', test_policygroup2), 'w').write(contents)

        groups = "%s,%s" % (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 <abstractions/nameservice>',
                  '#include <abstractions/gnome>',
                  '#include <abstractions/kde>',
                  '#include <abstractions/openssl>']:
            self.assertTrue(s in p, "Could not find '%s' in:\n%s" % (s, p))
        inv_s = '###POLICYGROUPS###'
        self.assertFalse(inv_s in p, "Found '%s' in :\n%s" % (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 easyprof.AppArmorException:
            return
        except Exception:
            raise
        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 '%s' in:\n%s" % (s, p))
        inv_s = '###TEMPLATEVAR###'
        self.assertFalse(inv_s in p, "Found '%s' in :\n%s" % (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 '%s' in:\n%s" % (str_s, p))
            inv_s = '###TEMPLATEVAR###'
            self.assertFalse(inv_s in p, "Found '%s' in :\n%s" % (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 easyprof.AppArmorException:
                continue
            raise Exception ("'%s' should be invalid" % k)

    def test_gen_manifest(self):
        '''Test gen_manifest'''
        #  this should come from manpage
        m = '''{
  "security": {
    "profiles": {
      "com.example.foo": {
        "abstractions": [
          "audio",
          "gnome"
        ],
        "author": "<NAME>",
        "binary": "/opt/foo/**",
        "comment": "Unstructured single-line comment",
        "copyright": "Unstructured single-line copyright statement",
        "name": "<NAME> 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.assertEquals(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.assertEquals(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 easyprof.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 easyprof.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": "<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 easyprof.AppArmorException:
            if invalid:
                return
            raise
        params = easyprof.gen_policy_params(binary, options)
        if expected:
            self.assertTrue(easyprof.verify_manifest(params, args), "params=%s\nmanifest=%s" % (params,m))
        else:
            self.assertFalse(easyprof.verify_manifest(params, args), "params=%s\nmanifest=%s" % (params,m))

    def test_verify_manifest_full(self):
        '''Test verify_manifest (full)'''
        m = '''{
  "security": {
    "profiles": {
      "com.example.foo": {
        "abstractions": [
          "base"
        ],
        "author": "<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": "<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 easyprof.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 easyprof.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 = "%s/%s" % (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 = "%s/%s" % (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=%s" % policy_version)
        args.append("--policy-vendor=%s" % 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), \
                            "'%s' does not start with '%s'" % (t, tdir))

        pdir = os.path.join(self.tmpdir, 'policygroups', policy_subdir)
        for p in easyp.get_policy_groups():
            self.assertTrue(p.startswith(pdir), \
                            "'%s' does not start with '%s'" % (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=%s" % policy_version)
        args.append("--policy-vendor=%s" % 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 easyprof.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=%s" % 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 easyprof.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=%s" % 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 easyprof.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": "<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 '%s'" % 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": "<NAME>",
      "binary": "/opt/foo/**",
      "comment": "Unstructured single-line comment",
      "copyright": "Unstructured single-line copyright statement",
      "name": "<NAME>",
      "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 '%s'" % 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 easyprof.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 easyprof.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 easyprof.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=%s' % self.test_template)
        args.append('--name=%s' % '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 '%s'" % 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), "'%s' should be valid" % 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?',
                 'foo\/',
                 'foo,',
                 '_foo',
                ]
        for n in names:
            self.assertFalse(easyprof.valid_profile_name(n), "'%s' should be invalid" % 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), "'%s' should be valid" % n)
        for n in names_rel:
            self.assertTrue(easyprof.valid_path(n, relative_ok=True), "'%s' should be valid" % 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), "'%s' should be invalid" % n)
        for n in names_rel:
            self.assertFalse(easyprof.valid_path(n, relative_ok=True), "'%s' should be invalid" % n)


#
# End test class
#

#
# Main
#
if __name__ == '__main__':
    def cleanup(files):
        for f in files:
            if os.path.exists(f):
                os.unlink(f)

    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

    created = []

    # Create the necessary files to import aa-easyprof
    init = os.path.join(os.path.dirname(absfn), '__init__.py')
    if not os.path.exists(init):
        open(init, 'a').close()
        created.append(init)

    symlink = os.path.join(os.path.dirname(absfn), 'easyprof.py')
    if not os.path.exists(symlink):
        os.symlink(os.path.join(topdir, 'apparmor', 'easyprof.py'), symlink)
        created.append(symlink)
        created.append(symlink + 'c')

    # Now that we have everything we need, import aa-easyprof
    import easyprof

    # run the tests
    suite = unittest.TestSuite()
    suite.addTest(unittest.TestLoader().loadTestsFromTestCase(T))
    rc = unittest.TextTestRunner(verbosity=2).run(suite)

    cleanup(created)

    if not rc.wasSuccessful():
        sys.exit(1)

#! /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

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 contents_only == False:
        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(object):
    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 not 'template_variables' 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 <tunables/global>

###VAR###

###PROFILEATTACH### {
  #include <abstractions/base>

  ###ABSTRACTIONS###

  ###POLICYGROUPS###

  ###READS###

  ###WRITES###
}

''' % (self.test_template)
        open(os.path.join(self.tmpdir, 'templates', self.test_template), 'w').write(contents)

        # Create a test policygroup
        self.test_policygroup = "test-policygroup"
        contents = '''
  # %s
  #include <abstractions/gnome>
  #include <abstractions/nameservice>
''' % (self.test_policygroup)
        open(os.path.join(self.tmpdir, 'policygroups', self.test_policygroup), 'w').write(contents)

        # setup our conffile
        self.conffile = os.path.join(self.tmpdir, 'easyprof.conf')
        contents = '''
POLICYGROUPS_DIR="%s/policygroups"
TEMPLATES_DIR="%s/templates"
''' % (self.tmpdir, self.tmpdir)
        open(self.conffile, 'w').write(contents)

        self.binary = "/opt/bin/foo"
        self.full_args = ['-c', self.conffile, self.binary]

        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_%s" % os.path.basename(f)))

    def tearDown(self):
        '''Teardown for tests'''
        if os.path.exists(self.tmpdir):
            if debugging:
                sys.stdout.write("%s\n" % self.tmpdir)
            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="%s/templates"
''' % (self.tmpdir)

        open(self.conffile, 'w').write(contents)
        try:
            easyprof.AppArmorEasyProfile(self.binary, self.options)
        except easyprof.AppArmorException:
            return
        except Exception:
            raise

        raise Exception ("File should have been invalid")

    def test_configuration_file_p_empty(self):
        '''Test config parsing (empty POLICYGROUPS_DIR)'''
        contents = '''
POLICYGROUPS_DIR="%s"
TEMPLATES_DIR="%s/templates"
''' % ('', self.tmpdir)

        open(self.conffile, 'w').write(contents)
        try:
            easyprof.AppArmorEasyProfile(self.binary, self.options)
        except easyprof.AppArmorException:
            return
        except Exception:
            raise

        raise Exception ("File should have been invalid")

    def test_configuration_file_p_nonexistent(self):
        '''Test config parsing (nonexistent POLICYGROUPS_DIR)'''
        contents = '''
POLICYGROUPS_DIR="%s/policygroups"
TEMPLATES_DIR="%s/templates"
''' % ('/nonexistent', self.tmpdir)

        open(self.conffile, 'w').write(contents)
        try:
            easyprof.AppArmorEasyProfile(self.binary, self.options)
        except easyprof.AppArmorException:
            return
        except Exception:
            raise

        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=%s' % 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: %s\nActual dir: %s" % (rel, str(easyp.dirs['policygroups'])))
        self.assertFalse(easyp.get_policy_groups() == 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=%s' % 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() != 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=%s' % 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() == 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=%s' % 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() == None, "Could not find policy-groups")
        for f in easyp.get_policy_groups():
            self.assertFalse(os.path.basename(f) == vendor, "Found '%s' in %s" % (vendor, f))

    def test_configuration_file_t_invalid(self):
        '''Test config parsing (invalid TEMPLATES_DIR)'''
        contents = '''
TEMPLATES_DIR=
POLICYGROUPS_DIR="%s/templates"
''' % (self.tmpdir)

        open(self.conffile, 'w').write(contents)
        try:
            easyprof.AppArmorEasyProfile(self.binary, self.options)
        except easyprof.AppArmorException:
            return
        except Exception:
            raise

        raise Exception ("File should have been invalid")

    def test_configuration_file_t_empty(self):
        '''Test config parsing (empty TEMPLATES_DIR)'''
        contents = '''
TEMPLATES_DIR="%s"
POLICYGROUPS_DIR="%s/templates"
''' % ('', self.tmpdir)

        open(self.conffile, 'w').write(contents)
        try:
            easyprof.AppArmorEasyProfile(self.binary, self.options)
        except easyprof.AppArmorException:
            return
        except Exception:
            raise

        raise Exception ("File should have been invalid")

    def test_configuration_file_t_nonexistent(self):
        '''Test config parsing (nonexistent TEMPLATES_DIR)'''
        contents = '''
TEMPLATES_DIR="%s/policygroups"
POLICYGROUPS_DIR="%s/templates"
''' % ('/nonexistent', self.tmpdir)

        open(self.conffile, 'w').write(contents)
        try:
            easyprof.AppArmorEasyProfile(self.binary, self.options)
        except easyprof.AppArmorException:
            return
        except Exception:
            raise

        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=%s' % 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: %s\nActual dir: %s" % (rel, str(easyp.dirs['templates'])))
        self.assertFalse(easyp.get_templates() == 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=%s' % 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() != 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=%s' % 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() == 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=%s' % 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() == None, "Could not find templates")
        for f in easyp.get_templates():
            self.assertFalse(os.path.basename(f) == vendor, "Found '%s' in %s" % (vendor, f))

#
# Binary file tests
#
    def test_binary_without_profile_name(self):
        '''Test binary (<binary> { })'''
        easyprof.AppArmorEasyProfile(self.ls, self.options)

    def test_binary_with_profile_name(self):
        '''Test binary (profile <name> <binary> { })'''
        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 <name> { })'''
        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 easyprof.AppArmorException:
            return
        except Exception:
            raise
        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 easyprof.AppArmorException:
            return
        except Exception:
            raise
        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 '%s'" % i)

    def test_templates_show(self):
        '''Test templates (show)'''
        files = []
        for f in glob.glob("%s/templates/*" % self.tmpdir):
            files.append(f)

        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 '%s'" % path)
            open(path).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=%s' %
                    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: %s" % inc_templates)

        for i in inc_templates:
            self.assertTrue(os.path.exists(i), "Could not find '%s'" % i)

    def test_templates_show_include(self):
        '''Test templates (show with --include-templates-dir)'''
        files = []
        for f in glob.glob("%s/templates/*" % self.test_include_dir):
            files.append(f)

        for f in files:
            args = self.full_args
            args += ['--show-template',
                     '--template', f,
                     '--include-templates-dir=%s' %
                     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 '%s'" % path)
            open(path).read()

            bn = os.path.basename(f)
            # setup() copies everything in the include prefixed with inc_
            self.assertTrue(bn.startswith('inc_'),
                            "'%s' does not start with 'inc_'" % 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 '%s'" % i)

    def test_policygroups_show(self):
        '''Test policygroups (show)'''
        files = []
        for f in glob.glob("%s/policygroups/*" % self.tmpdir):
            files.append(f)

        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 '%s'" % path)
            open(path).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=%s' %
                    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: %s" % inc_policy_groups)

        for i in inc_policy_groups:
            self.assertTrue(os.path.exists(i), "Could not find '%s'" % i)

    def test_policygroups_show_include(self):
        '''Test policygroups (show with --include-policy-groups-dir)'''
        files = []
        for f in glob.glob("%s/policygroups/*" % self.test_include_dir):
            files.append(f)

        for f in files:
            args = self.full_args
            args += ['--show-policy-group',
                     '--policy-groups', os.path.basename(f),
                     '--include-policy-groups-dir=%s' %
                     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 '%s'" % path)
            open(path).read()

            bn = os.path.basename(f)
            # setup() copies everything in the include prefixed with inc_
            self.assertTrue(bn.startswith('inc_'),
                            "'%s' does not start with 'inc_'" % 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"}}}
'''
        open(self.manifest, 'w').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"}}}
'''
        open(self.manifest, 'w').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="%s and manifest arguments did not " \
                                    "raise a parse error" % opt)

        # 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="%s and manifest arguments did not " \
                                    "raise a parse error" % opt)

    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=[]):
        '''Generate a policy'''
        # Build up our args
        args = self.full_args

        if template == None:
            args.append('--template=%s' % self.test_template)
        else:
            args.append('--template=%s' % template)

        if name != None:
            args.append('--name=%s' % name)

        if len(extra_args) > 0:
            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 != None:
            search_terms.append(name)

        if template == None:
            search_terms.append(self.test_template)

        for s in search_terms:
            self.assertTrue(s in p, "Could not find '%s' in:\n%s" % (s, p))

        # ###NAME### should be replaced with self.binary or 'name'. Check for that
        inv_s = '###NAME###'
        self.assertFalse(inv_s in p, "Found '%s' in :\n%s" % (inv_s, p))

        if debugging:
            sys.stdout.write("%s\n" % p)

        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 '%s' in :\n%s" % (inv_s, p))

        if debugging:
            sys.stdout.write("%s\n" % p)

        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), "'%s' should be bad" %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)
        contents = open(template).read()
        test_string = "#teststring"
        open(template, 'w').write(contents + "\n%s\n" % test_string)

        p = self._gen_policy(template=template)

        for s in [self.test_template, test_string]:
            self.assertTrue(s in p, "Could not find '%s' in:\n%s" % (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 easyprof.AppArmorException:
            return
        except Exception:
            raise
        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' % s])
        self.assertTrue(s in p, "Could not find '%s' in:\n%s" % (s, p))
        inv_s = '###COMMENT###'
        self.assertFalse(inv_s in p, "Found '%s' in :\n%s" % (inv_s, p))

    def test_genpolicy_author(self):
        '''Test genpolicy (author)'''
        s = "<NAME>"
        p = self._gen_policy(extra_args=['--author=%s' % s])
        self.assertTrue(s in p, "Could not find '%s' in:\n%s" % (s, p))
        inv_s = '###AUTHOR###'
        self.assertFalse(inv_s in p, "Found '%s' in :\n%s" % (inv_s, p))

    def test_genpolicy_copyright(self):
        '''Test genpolicy (copyright)'''
        s = "2112/01/01"
        p = self._gen_policy(extra_args=['--copyright=%s' % s])
        self.assertTrue(s in p, "Could not find '%s' in:\n%s" % (s, p))
        inv_s = '###COPYRIGHT###'
        self.assertFalse(inv_s in p, "Found '%s' in :\n%s" % (inv_s, p))

    def test_genpolicy_abstractions(self):
        '''Test genpolicy (single abstraction)'''
        s = "nameservice"
        p = self._gen_policy(extra_args=['--abstractions=%s' % s])
        search = "#include <abstractions/%s>" % s
        self.assertTrue(search in p, "Could not find '%s' in:\n%s" % (search, p))
        inv_s = '###ABSTRACTIONS###'
        self.assertFalse(inv_s in p, "Found '%s' in :\n%s" % (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=%s' % abstractions])
        for s in abstractions.split(','):
            search = "#include <abstractions/%s>" % s
            self.assertTrue(search in p, "Could not find '%s' in:\n%s" % (search, p))
        inv_s = '###ABSTRACTIONS###'
        self.assertFalse(inv_s in p, "Found '%s' in :\n%s" % (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' % s])
            except easyprof.AppArmorException:
                continue
            except Exception:
                raise
            raise Exception ("abstraction '%s' should be invalid" % s)

    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' % s])
            except easyprof.AppArmorException:
                continue
            except Exception:
                raise
            raise Exception ("profile_name '%s' should be invalid" % 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' % s])
            except easyprof.AppArmorException:
                continue
            except Exception:
                raise
            raise Exception ("policy group '%s' should be invalid" % s)

    def test_genpolicy_policygroups(self):
        '''Test genpolicy (single policygroup)'''
        groups = self.test_policygroup
        p = self._gen_policy(extra_args=['--policy-groups=%s' % groups])

        for s in ['#include <abstractions/nameservice>', '#include <abstractions/gnome>']:
            self.assertTrue(s in p, "Could not find '%s' in:\n%s" % (s, p))
        inv_s = '###POLICYGROUPS###'
        self.assertFalse(inv_s in p, "Found '%s' in :\n%s" % (inv_s, p))

    def test_genpolicy_policygroups_multiple(self):
        '''Test genpolicy (multiple policygroups)'''
        test_policygroup2 = "test-policygroup2"
        contents = '''
  # %s
  #include <abstractions/kde>
  #include <abstractions/openssl>
''' % (self.test_policygroup)
        open(os.path.join(self.tmpdir, 'policygroups', test_policygroup2), 'w').write(contents)

        groups = "%s,%s" % (self.test_policygroup, test_policygroup2)
        p = self._gen_policy(extra_args=['--policy-groups=%s' % groups])

        for s in ['#include <abstractions/nameservice>',
                  '#include <abstractions/gnome>',
                  '#include <abstractions/kde>',
                  '#include <abstractions/openssl>']:
            self.assertTrue(s in p, "Could not find '%s' in:\n%s" % (s, p))
        inv_s = '###POLICYGROUPS###'
        self.assertFalse(inv_s in p, "Found '%s' in :\n%s" % (inv_s, p))

    def test_genpolicy_policygroups_nonexistent(self):
        '''Test genpolicy (nonexistent policygroup)'''
        try:
            self._gen_policy(extra_args=['--policy-groups=nonexistent'])
        except easyprof.AppArmorException:
            return
        except Exception:
            raise
        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' % s])
        search = "%s rk," % s
        self.assertTrue(search in p, "Could not find '%s' in:\n%s" % (search, p))
        inv_s = '###READPATH###'
        self.assertFalse(inv_s in p, "Found '%s' in :\n%s" % (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' % s])
        search = "owner %s rk," % s
        self.assertTrue(search in p, "Could not find '%s' in:\n%s" % (search, p))
        inv_s = '###READPATH###'
        self.assertFalse(inv_s in p, "Found '%s' in :\n%s" % (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' % s])
        search = "owner %s rk," % s
        self.assertTrue(search in p, "Could not find '%s' in:\n%s" % (search, p))
        inv_s = '###READPATH###'
        self.assertFalse(inv_s in p, "Found '%s' in :\n%s" % (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' % s])
        search = "owner %s rk," % s
        self.assertTrue(search in p, "Could not find '%s' in:\n%s" % (search, p))
        inv_s = '###READPATH###'
        self.assertFalse(inv_s in p, "Found '%s' in :\n%s" % (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' % s])
        search_terms = ["%s rk," % s, "%s** rk," % s]
        for search in search_terms:
            self.assertTrue(search in p, "Could not find '%s' in:\n%s" % (search, p))
        inv_s = '###READPATH###'
        self.assertFalse(inv_s in p, "Found '%s' in :\n%s" % (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' % s])
        search_terms = ["%s rk," % os.path.dirname(s), "%s rk," % s]
        for search in search_terms:
            self.assertTrue(search in p, "Could not find '%s' in:\n%s" % (search, p))
        inv_s = '###READPATH###'
        self.assertFalse(inv_s in p, "Found '%s' in :\n%s" % (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' % s])
        search_terms = ["%s rk," % os.path.dirname(s), "%s rk," % s]
        for search in search_terms:
            self.assertTrue(search in p, "Could not find '%s' in:\n%s" % (search, p))
        inv_s = '###READPATH###'
        self.assertFalse(inv_s in p, "Found '%s' in :\n%s" % (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' % s)
            # This mimics easyprof.gen_path_rule()
            owner = ""
            if s.startswith('/home/') or s.startswith("@{HOME"):
                owner = "owner "
            if s.endswith('/'):
                search_terms.append("%s rk," % (s))
                search_terms.append("%s%s** rk," % (owner, s))
            elif s.endswith('/**') or s.endswith('/*'):
                search_terms.append("%s rk," % (os.path.dirname(s)))
                search_terms.append("%s%s rk," % (owner, s))
            else:
                search_terms.append("%s%s rk," % (owner, s))

        p = self._gen_policy(extra_args=args)
        for search in search_terms:
            self.assertTrue(search in p, "Could not find '%s' in:\n%s" % (search, p))
        inv_s = '###READPATH###'
        self.assertFalse(inv_s in p, "Found '%s' in :\n%s" % (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' % s])
        except easyprof.AppArmorException:
            return
        except Exception:
            raise
        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' % s])
        search = "%s rwk," % s
        self.assertTrue(search in p, "Could not find '%s' in:\n%s" % (search, p))
        inv_s = '###READPATH###'
        self.assertFalse(inv_s in p, "Found '%s' in :\n%s" % (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' % s])
        search = "owner %s rwk," % s
        self.assertTrue(search in p, "Could not find '%s' in:\n%s" % (search, p))
        inv_s = '###READPATH###'
        self.assertFalse(inv_s in p, "Found '%s' in :\n%s" % (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' % s])
        search = "owner %s rwk," % s
        self.assertTrue(search in p, "Could not find '%s' in:\n%s" % (search, p))
        inv_s = '###READPATH###'
        self.assertFalse(inv_s in p, "Found '%s' in :\n%s" % (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' % s])
        search = "owner %s rwk," % s
        self.assertTrue(search in p, "Could not find '%s' in:\n%s" % (search, p))
        inv_s = '###READPATH###'
        self.assertFalse(inv_s in p, "Found '%s' in :\n%s" % (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' % s])
        search_terms = ["%s rwk," % s, "%s** rwk," % s]
        for search in search_terms:
            self.assertTrue(search in p, "Could not find '%s' in:\n%s" % (search, p))
        inv_s = '###READPATH###'
        self.assertFalse(inv_s in p, "Found '%s' in :\n%s" % (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' % s])
        search_terms = ["%s rwk," % os.path.dirname(s), "%s rwk," % s]
        for search in search_terms:
            self.assertTrue(search in p, "Could not find '%s' in:\n%s" % (search, p))
        inv_s = '###READPATH###'
        self.assertFalse(inv_s in p, "Found '%s' in :\n%s" % (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' % s])
        search_terms = ["%s rwk," % os.path.dirname(s), "%s rwk," % s]
        for search in search_terms:
            self.assertTrue(search in p, "Could not find '%s' in:\n%s" % (search, p))
        inv_s = '###READPATH###'
        self.assertFalse(inv_s in p, "Found '%s' in :\n%s" % (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' % s)
            # This mimics easyprof.gen_path_rule()
            owner = ""
            if s.startswith('/home/') or s.startswith("@{HOME"):
                owner = "owner "
            if s.endswith('/'):
                search_terms.append("%s rwk," % (s))
                search_terms.append("%s%s** rwk," % (owner, s))
            elif s.endswith('/**') or s.endswith('/*'):
                search_terms.append("%s rwk," % (os.path.dirname(s)))
                search_terms.append("%s%s rwk," % (owner, s))
            else:
                search_terms.append("%s%s rwk," % (owner, s))

        p = self._gen_policy(extra_args=args)
        for search in search_terms:
            self.assertTrue(search in p, "Could not find '%s' in:\n%s" % (search, p))
        inv_s = '###READPATH###'
        self.assertFalse(inv_s in p, "Found '%s' in :\n%s" % (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' % s])
        except easyprof.AppArmorException:
            return
        except Exception:
            raise
        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' % s])
        k, v = s.split('=')
        s = '%s="%s"' % (k, v)
        self.assertTrue(s in p, "Could not find '%s' in:\n%s" % (s, p))
        inv_s = '###TEMPLATEVAR###'
        self.assertFalse(inv_s in p, "Found '%s' in :\n%s" % (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' % s)

        p = self._gen_policy(extra_args=args)
        for s in variables:
            k, v = s.split('=')
            s = '%s="%s"' % (k, v)
            self.assertTrue(s in p, "Could not find '%s' in:\n%s" % (s, p))
            inv_s = '###TEMPLATEVAR###'
            self.assertFalse(inv_s in p, "Found '%s' in :\n%s" % (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' % s])
            except easyprof.AppArmorException:
                continue
            except Exception:
                raise
            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)
        contents = open(template).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"
        open(template, 'w').write(bad_pol)
        try:
            self._gen_policy(template=template)
        except easyprof.AppArmorException:
            return
        except Exception:
            raise
        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 easyprof.AppArmorException:
            return
        except Exception:
            raise
        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=%s' % profile_name])
        s = 'profile "%s" "%s" {' % (profile_name, self.binary)
        self.assertTrue(s in p, "Could not find '%s' in:\n%s" % (s, p))
        inv_s = '###PROFILEATTACH###'
        self.assertFalse(inv_s in p, "Found '%s' in :\n%s" % (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 '%s' in:\n%s" % (s, p))
        inv_s = '###PROFILEATTACH###'
        self.assertFalse(inv_s in p, "Found '%s' in :\n%s" % (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=%s' % 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 '%s' in:\n%s" % (s, p))
        inv_s = '###PROFILEATTACH###'
        self.assertFalse(inv_s in p, "Found '%s' in :\n%s" % (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 easyprof.AppArmorException:
            return
        except Exception:
            raise
        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 easyprof.AppArmorException:
            return
        except Exception:
            raise
        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 easyprof.AppArmorException:
            return
        except Exception:
            raise
        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 '%s' in:\n%s" % (s, p))
        inv_s = '###COMMENT###'
        self.assertFalse(inv_s in p, "Found '%s' in :\n%s" % (inv_s, p))

    def test_gen_manifest_policy_author(self):
        '''Test gen manifest policy (author)'''
        s = "<NAME>"
        m = Manifest("test_gen_manifest_policy")
        m.add_author(s)
        p = self._gen_manifest_policy(m)
        self.assertTrue(s in p, "Could not find '%s' in:\n%s" % (s, p))
        inv_s = '###AUTHOR###'
        self.assertFalse(inv_s in p, "Found '%s' in :\n%s" % (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 '%s' in:\n%s" % (s, p))
        inv_s = '###COPYRIGHT###'
        self.assertFalse(inv_s in p, "Found '%s' in :\n%s" % (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 <abstractions/nameservice>', '#include <abstractions/gnome>']:
            self.assertTrue(s in p, "Could not find '%s' in:\n%s" % (s, p))
        inv_s = '###POLICYGROUPS###'
        self.assertFalse(inv_s in p, "Found '%s' in :\n%s" % (inv_s, p))

    def test_gen_manifest_policy_policygroups_multiple(self):
        '''Test genpolicy (multiple policygroups)'''
        test_policygroup2 = "test-policygroup2"
        contents = '''
  # %s
  #include <abstractions/kde>
  #include <abstractions/openssl>
''' % (self.test_policygroup)
        open(os.path.join(self.tmpdir, 'policygroups', test_policygroup2), 'w').write(contents)

        groups = "%s,%s" % (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 <abstractions/nameservice>',
                  '#include <abstractions/gnome>',
                  '#include <abstractions/kde>',
                  '#include <abstractions/openssl>']:
            self.assertTrue(s in p, "Could not find '%s' in:\n%s" % (s, p))
        inv_s = '###POLICYGROUPS###'
        self.assertFalse(inv_s in p, "Found '%s' in :\n%s" % (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 easyprof.AppArmorException:
            return
        except Exception:
            raise
        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 '%s' in:\n%s" % (s, p))
        inv_s = '###TEMPLATEVAR###'
        self.assertFalse(inv_s in p, "Found '%s' in :\n%s" % (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 '%s' in:\n%s" % (str_s, p))
            inv_s = '###TEMPLATEVAR###'
            self.assertFalse(inv_s in p, "Found '%s' in :\n%s" % (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 easyprof.AppArmorException:
                continue
            raise Exception ("'%s' should be invalid" % k)

    def test_gen_manifest(self):
        '''Test gen_manifest'''
        #  this should come from manpage
        m = '''{
  "security": {
    "profiles": {
      "com.example.foo": {
        "abstractions": [
          "audio",
          "gnome"
        ],
        "author": "<NAME>",
        "binary": "/opt/foo/**",
        "comment": "Unstructured single-line comment",
        "copyright": "Unstructured single-line copyright statement",
        "name": "<NAME> 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.assertEquals(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.assertEquals(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 easyprof.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 easyprof.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": "<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 easyprof.AppArmorException:
            if invalid:
                return
            raise
        params = easyprof.gen_policy_params(binary, options)
        if expected:
            self.assertTrue(easyprof.verify_manifest(params, args), "params=%s\nmanifest=%s" % (params,m))
        else:
            self.assertFalse(easyprof.verify_manifest(params, args), "params=%s\nmanifest=%s" % (params,m))

    def test_verify_manifest_full(self):
        '''Test verify_manifest (full)'''
        m = '''{
  "security": {
    "profiles": {
      "com.example.foo": {
        "abstractions": [
          "base"
        ],
        "author": "<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": "<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 easyprof.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 easyprof.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 = "%s/%s" % (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 = "%s/%s" % (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=%s" % policy_version)
        args.append("--policy-vendor=%s" % 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), \
                            "'%s' does not start with '%s'" % (t, tdir))

        pdir = os.path.join(self.tmpdir, 'policygroups', policy_subdir)
        for p in easyp.get_policy_groups():
            self.assertTrue(p.startswith(pdir), \
                            "'%s' does not start with '%s'" % (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=%s" % policy_version)
        args.append("--policy-vendor=%s" % 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 easyprof.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=%s" % 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 easyprof.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=%s" % 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 easyprof.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": "<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 '%s'" % 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": "<NAME>",
      "binary": "/opt/foo/**",
      "comment": "Unstructured single-line comment",
      "copyright": "Unstructured single-line copyright statement",
      "name": "<NAME>",
      "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 '%s'" % 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 easyprof.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 easyprof.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 easyprof.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=%s' % self.test_template)
        args.append('--name=%s' % '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 '%s'" % 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), "'%s' should be valid" % 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?',
                 'foo\/',
                 'foo,',
                 '_foo',
                ]
        for n in names:
            self.assertFalse(easyprof.valid_profile_name(n), "'%s' should be invalid" % 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), "'%s' should be valid" % n)
        for n in names_rel:
            self.assertTrue(easyprof.valid_path(n, relative_ok=True), "'%s' should be valid" % 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), "'%s' should be invalid" % n)
        for n in names_rel:
            self.assertFalse(easyprof.valid_path(n, relative_ok=True), "'%s' should be invalid" % n)


#
# End test class
#

#
# Main
#
if __name__ == '__main__':
    def cleanup(files):
        for f in files:
            if os.path.exists(f):
                os.unlink(f)

    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

    created = []

    # Create the necessary files to import aa-easyprof
    init = os.path.join(os.path.dirname(absfn), '__init__.py')
    if not os.path.exists(init):
        open(init, 'a').close()
        created.append(init)

    symlink = os.path.join(os.path.dirname(absfn), 'easyprof.py')
    if not os.path.exists(symlink):
        os.symlink(os.path.join(topdir, 'apparmor', 'easyprof.py'), symlink)
        created.append(symlink)
        created.append(symlink + 'c')

    # Now that we have everything we need, import aa-easyprof
    import easyprof

    # run the tests
    suite = unittest.TestSuite()
    suite.addTest(unittest.TestLoader().loadTestsFromTestCase(T))
    rc = unittest.TextTestRunner(verbosity=2).run(suite)

    cleanup(created)

    if not rc.wasSuccessful():
        sys.exit(1)
#! /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. # # ------------------------------------------------------------------ recursively remove directory # From Lib/test/test_optparse.py from python 2.7.4 # # Our test class # # work around UsrMove Setup for tests # Copy everything into place # Create a test template # vim:syntax=apparmor # %s # AppArmor policy for ###NAME### # ###AUTHOR### # ###COPYRIGHT### # ###COMMENT###  #include <tunables/global>  ###VAR###  ###PROFILEATTACH### {   #include <abstractions/base>    ###ABSTRACTIONS###    ###POLICYGROUPS###    ###READS###    ###WRITES### } # Create a test policygroup # %s   #include <abstractions/gnome>   #include <abstractions/nameservice> # setup our conffile POLICYGROUPS_DIR="%s/policygroups" TEMPLATES_DIR="%s/templates" # Now create some differently prefixed files in the include-dir Teardown for tests # # config file tests # Test config parsing (invalid POLICYGROUPS_DIR) POLICYGROUPS_DIR= TEMPLATES_DIR="%s/templates" Test config parsing (empty POLICYGROUPS_DIR) POLICYGROUPS_DIR="%s" TEMPLATES_DIR="%s/templates" Test config parsing (nonexistent POLICYGROUPS_DIR) POLICYGROUPS_DIR="%s/policygroups" TEMPLATES_DIR="%s/templates" Test --policy-groups-dir (relative DIR) # no fallback Test --policy-groups-dir (nonexistent DIR) # test if using fallback # test fallback Test --policy-groups-dir (valid DIR) # no fallback Test --policy-groups-dir (valid DIR with vendor) Test config parsing (invalid TEMPLATES_DIR) TEMPLATES_DIR= POLICYGROUPS_DIR="%s/templates" Test config parsing (empty TEMPLATES_DIR) TEMPLATES_DIR="%s" POLICYGROUPS_DIR="%s/templates" Test config parsing (nonexistent TEMPLATES_DIR) TEMPLATES_DIR="%s/policygroups" POLICYGROUPS_DIR="%s/templates" Test --templates-dir (relative DIR) # no fallback Test --templates-dir (nonexistent DIR) # test if using fallback # test fallback Test --templates-dir (valid DIR) # no fallback Test --templates-dir (valid DIR with vendor) # # Binary file tests # Test binary (<binary> { }) Test binary (profile <name> <binary> { }) Test binary (profile <name> { }) Test binary (nonexistent) Test binary (relative) Test binary (symlink) # # Templates tests # Test templates (list) Test templates (show) Test templates (list with --include-templates-dir) Test templates (show with --include-templates-dir) # setup() copies everything in the include prefixed with inc_ # # Policygroups tests # Test policygroups (list) Test policygroups (show) Test policygroups (list with --include-policy-groups-dir) Test policygroups (show with --include-policy-groups-dir) # setup() copies everything in the include prefixed with inc_ # # Manifest file argument tests # Test manifest argument # setup our manifest {"security": {"domain.reverse.appname": {"name": "simple-app"}}} Helper for conflicts tests # setup our manifest {"security": {"domain.reverse.appname": {"binary": /nonexistent"}}} # opt first # manifest first Test manifest arg conflicts with profile_name arg Test manifest arg conflicts with copyright arg Test manifest arg conflicts with author arg Test manifest arg conflicts with comment arg Test manifest arg conflicts with abstractions arg Test manifest arg conflicts with read-path arg Test manifest arg conflicts with write-path arg Test manifest arg conflicts with policy-groups arg Test manifest arg conflicts with name arg Test manifest arg conflicts with template-var arg Test manifest arg conflicts with policy-version arg Test manifest arg conflicts with policy-vendor arg # # Test genpolicy # Generate a policy # Build up our args # Now parse our args # We always need to check for these # ###NAME### should be replaced with self.binary or 'name'. Check for that ##NAME###' # Build up our args # ###NAME### should be replaced with self.binary or 'name'. Check for that ##NAME###' Test _is_safe() Test genpolicy (abspath to template) # create a new template Test genpolicy (system template) Test genpolicy (nonexistent template) Test genpolicy (name) Test genpolicy (comment) ##COMMENT###' Test genpolicy (author) ##AUTHOR###' Test genpolicy (copyright) ##COPYRIGHT###' Test genpolicy (single abstraction) ##ABSTRACTIONS###' Test genpolicy (multiple abstractions) ##ABSTRACTIONS###' Test genpolicy (abstractions - bad values) Test genpolicy (profile name - bad values) Test genpolicy (policy group - bad values) Test genpolicy (single policygroup) ##POLICYGROUPS###' Test genpolicy (multiple policygroups) # %s   #include <abstractions/kde>   #include <abstractions/openssl> ##POLICYGROUPS###' Test genpolicy (nonexistent policygroup) Test genpolicy (read-path file) ##READPATH###' Test genpolicy (read-path file in /home) ##READPATH###' Test genpolicy (read-path file in @{HOME}) ##READPATH###' Test genpolicy (read-path file in @{HOMEDIRS}) ##READPATH###' Test genpolicy (read-path directory/) ##READPATH###' Test genpolicy (read-path directory/*) ##READPATH###' Test genpolicy (read-path directory/**) ##READPATH###' Test genpolicy (read-path multiple) # This mimics easyprof.gen_path_rule() ##READPATH###' Test genpolicy (read-path bad) Test genpolicy (write-path file) ##READPATH###' Test genpolicy (write-path file in /home) ##READPATH###' Test genpolicy (write-path file in @{HOME}) ##READPATH###' Test genpolicy (write-path file in @{HOMEDIRS}) ##READPATH###' Test genpolicy (write-path directory/) ##READPATH###' Test genpolicy (write-path directory/*) ##READPATH###' Test genpolicy (write-path directory/**) ##READPATH###' Test genpolicy (write-path multiple) # This mimics easyprof.gen_path_rule() ##READPATH###' Test genpolicy (write-path bad) Test genpolicy (template-var single) ##TEMPLATEVAR###' Test genpolicy (template-var multiple) ##TEMPLATEVAR###' Test genpolicy (template-var - bad values) Test genpolicy (invalid template policy) # create a new template Test genpolicy (no binary with no profile name) Test genpolicy (binary with profile name) ##PROFILEATTACH###' Test genpolicy (binary without profile name) ##PROFILEATTACH###' Test genpolicy (no binary with profile name) ##PROFILEATTACH###' # manifest tests Test gen_manifest_policy (binary with profile name) Test gen_manifest_policy (no binary with profile name) Test gen_manifest_policy (system template) Test gen_manifest_policy (system template, no security prefix) Test gen_manifest_policy (abs path template) Test gen_manifest_policy (esc path template) Test gen manifest policy (nonexistent template) Test gen manifest policy (comment) ##COMMENT###' Test gen manifest policy (author) ##AUTHOR###' Test genpolicy (copyright) ##COPYRIGHT###' Test gen manifest policy (single policygroup) ##POLICYGROUPS###' Test genpolicy (multiple policygroups) # %s   #include <abstractions/kde>   #include <abstractions/openssl> ##POLICYGROUPS###' Test gen manifest policy (nonexistent policygroup) Test gen manifest policy (template-var single) ##TEMPLATEVAR###' Test gen manifest policy (template-var multiple) ##TEMPLATEVAR###' Test gen manifest policy (invalid keys) Test gen_manifest #  this should come from manpage {   "security": {     "profiles": {       "com.example.foo": {         "abstractions": [           "audio",           "gnome"         ],         "author": "<NAME>",         "binary": "/opt/foo/**",         "comment": "Unstructured single-line comment",         "copyright": "Unstructured single-line copyright statement",         "name": "<NAME> 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/"         ]       }     }   } } # verify we get the same manifest back Test gen_manifest (ubuntu) # this should be based on the manpage (but use existing policy_groups # and template {   "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"         }       }     }   } } # verify we get the same manifest back Test parse_manifest (vendor with no version) #  this should come from manpage {   "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"         }       }     }   } } Test parse_manifest (version with no vendor) #  this should come from manpage {   "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"         }       }     }   } } Test parse_manifest_multiple {   "security": {     "profiles": {       "com.example.foo": {         "abstractions": [           "audio",           "gnome"         ],         "author": "<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"         }       }     }   } } # verify manifest tests Test verify_manifest (full) {   "security": {     "profiles": {       "com.example.foo": {         "abstractions": [           "base"         ],         "author": "<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"         }       }     }   } } Test verify_manifest (full bad) {   "security": {     "profiles": {       "/com.example.foo": {          "abstractions": [           "audio",           "gnome"         ],         "author": "<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/"         ]       }     }   } } Test verify_manifest (binary in /usr) {   "security": {    "profiles": {     "com.example.foo": {      "binary": "/usr/foo/**",      "template": "user-application"     }    }   } } Test verify_manifest (bad profile_name) {   "security": {    "profiles": {     "/foo": {      "binary": "/opt/com.example/foo/**",      "template": "user-application"     }    }   } } {   "security": {    "profiles": {     "bin/*": {      "binary": "/opt/com.example/foo/**",      "template": "user-application"     }    }   } } Test verify_manifest (profile_name) {   "security": {    "profiles": {     "com.example.foo": {      "binary": "/opt/com.example/foo/**",      "template": "user-application"     }    }   } } Test verify_manifest (abstractions) {   "security": {    "profiles": {     "com.example.foo": {      "binary": "/opt/com.example/foo/**",      "template": "user-application",      "abstractions": [        "base"      ]     }    }   } } Test verify_manifest (bad abstractions) {   "security": {    "profiles": {     "com.example.foo": {      "binary": "/opt/com.example/foo/**",      "template": "user-application",      "abstractions": [        "user-tmp"      ]     }    }   } } Test verify_manifest (good template_var) {   "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"      }     }    }   } } Test verify_manifest (bad template_var) # this is valid, but potentially unsafe {   "security": {    "profiles": {     "com.example.foo": {      "binary": "/opt/com.example/foo/**",     "template": "user-application",      "template_variables": {        %s      }     }    }   } } Test invalid manifest (parse error) {   "security": {    "com.example.foo": {     "binary": "/opt/com.example/foo/**",     "template": "user-application",     "abstractions": [       "base"     ] } Test invalid manifest (profile_name is not key) {   "security": {     "binary": "/opt/com.example/foo/**",     "template": "user-application",     "abstractions": [       "base"     ]   } } Test invalid manifest (profile_name in dict) {   "security": {     "binary": "/opt/com.example/foo/**",     "template": "user-application",     "abstractions": [       "base"     ],     "profile_name": "com.example.foo"   } } Test invalid manifest (bad path in template var) {   "security": {    "profiles": {     "com.example.foo": {      "binary": "/opt/com.example/foo/**",      "template": "user-application",      "template_variables": {        %s      }     }    }   } } # policy version tests Test policy vendor via manifest (nonexistent) {   "security": {    "profiles": {     "com.example.foo": {      "policy_vendor": "nonexistent",      "policy_version": 1.0,      "binary": "/opt/com.example/foo/**",      "template": "user-application"     }    }   } } # Build up our args Test policy version via manifest (good) {   "security": {    "profiles": {     "com.example.foo": {      "policy_vendor": "%s",      "policy_version": %s,      "binary": "/opt/com.example/foo/**",      "template": "user-application"     }    }   } } # Build up our args Test policy vendor and version via command line args (good) # Create the directories # Build up our args Test policy vendor via command line args (nonexistent) Test policy version via command line args (bad) Test policy vendor via command line args (bad) # output_directory tests Test output_directory (multiple) {   "security": {    "profiles": {     "%s": {       "abstractions": [         "audio",         "gnome"       ],       "author": "<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"       }     }    }   } } Test output_directory (single) {   "security": {    "profiles": {     "%s": {       "abstractions": [         "audio",         "gnome"       ],       "author": "<NAME>",       "binary": "/opt/foo/**",       "comment": "Unstructured single-line comment",       "copyright": "Unstructured single-line copyright statement",       "name": "<NAME>",       "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/"       ]     }    }   } } Test output_directory (output directory exists as file) {   "security": {     "profiles": {       "%s": {         "abstractions": [           "gnome"         ],         "policy_groups": [           "user-application"         ],         "template_variables": {           "APPNAME": "baz"         }       }     }   } } Test output_directory (no binary or profile_name) {   "security": {     "profiles": {       "%s": {         "abstractions": [           "gnome"         ],         "policy_groups": [           "user-application"         ],         "template_variables": {           "APPNAME": "baz"         }       }     }   } } Test output_directory (profile exists) {   "security": {     "profiles": {       "%s": {         "abstractions": [           "gnome"         ],         "policy_groups": [           "user-application"         ],         "template_variables": {           "APPNAME": "baz"         }       }     }   } } Test output_directory (args) # Build up our args # Now parse our args # # utility classes # Test valid_profile_name Test valid_profile_name (invalid) #', Test valid_path Test valid_path (invalid) # # End test class # # # Main # # Create the necessary files to import aa-easyprof # Now that we have everything we need, import aa-easyprof # run the tests