BaseRule:
- add logprof_header() - sets the 'Qualifier' (audit, allow/deny) header
if a qualifier is specified, calls logprof_header_localvars() and then
returns an array of headers to display in aa-logprof and aa-mergeprof
- add logprof_header_localvars() - dummy function that needs to be
implemented in the child classes
NetworkRule: add logprof_header_localvars() - adds 'Network Family'
and 'Socket Type' to the headers
CapabilityRule: add logprof_header_localvars() - adds 'Capability' to
the headers
Also change aa-mergeprof to use rule_obj.logprof_header() for network
and capability rules. This means deleting lots of lines (that moved to
the *Rule classes) and also deleting the last differences between
capabiltiy and network rules.
Finally add tests for the newly added functions.
Acked-by: Steve Beattie <steve@nxnw.org>
Also implement handling for the special capability value '__ALL__' in
severity.py, which is used for 'capability,' rules (aa-mergeprof might
need to display the severity for such a rule).
Finally, add some tests for severity() in test-capability.py and a test
for '__ALL__' in test-severity.py.
Acked-by: Steve Beattie <steve@nxnw.org>
severity() will, surprise!, return the severity of a rule, or
sev_db.NOT_IMPLEMENTED if a *Rule class doesn't implement the severity()
function.
Also add the NOT_IMPLEMENTED constant to severity.py, and a test to
test-baserule.py that checks the return value in BaseRule.
Acked-by: Steve Beattie <steve@nxnw.org>
I decided to use a "small" solution for now, which basically means
s/unittest.TestCase/AATest/, cleanup of some setUp() and renaming the
remaining setUp() functions to AASetup().
This doesn't mean an instant win (like in test-severity.py), but allows
to add tests with a tests[] array.
Acked-by: Steve Beattie <steve@nxnw.org>
To be able to distinguish between severity 10 and unknown severity,
change AASetup to specify 'unknown' as default rank, and change the
expected result to 'unknown' where it's expected.
Also change the "expected rank %d" to "%s" because it can be a string
now, and add a test that contains directories with different severity
in one variable.
After these changes, handle_variable_rank() errors out with
TypeError: unorderable types: str() > int()
so fix it by
- initializing rank with the default rank (instead of none)
- explicitely check that rank and rank_new are != the default rank before
doing a comparison
A side effect is another bugfix - '@{HOME}/sys/@{PROC}/overcommit_memory'
is severity 4, not 10 or unknown (confirmed by reading severity.db).
Acked-by: Steve Beattie <steve@nxnw.org>
This simplifies test-severity.py a lot:
- lots of test functions are replaced with tests[] arrays
- tempdir handling and cleanup is now done automagically
Even if test-severity.py shrunk by 65 lines, all tests are still there.
There's even an addition - SeverityTestCap now additionally verifies the
result of rank_capability().
Acked-by: Steve Beattie <steve@nxnw.org>
Add a check to parse_profile_data() to detect if a file contains two
profiles with the same name.
Note: Two profiles with the same name, but in different files, won't be
detected by this check.
Also add basic tests to ensure that a valid profile gets parsed, and two
profiles with the same name inside the same file raise an exception.
(Sidenote: these simple tests improve aa.py coverage from 9% to 12%,
which also confirms the function is too long ;-)
Acked-by: Steve Beattie <steve@nxnw.org>
Add writeTmpfile() to AATest to write a file into the tmpdir. If no
tmpdir exists yet, automatically create one.
createTmpdir() is a separate function so that it's possible to manually
create the tmpdir (for example, if a test needs an empty tmpdir).
Also add a tearDown() function to delete the tmpdir again. This function
calls self.AATeardown() to avoid the need for super() in child classes.
Finally, simplify AaTestWithTempdir in test-aa.py to use createTmpdir()
and add an example for AATeardown() to test-example.py.
Acked-by: Steve Beattie <steve@nxnw.org>
It did this in the old 2.8 code, but didn't in 2.9.x (first there was a
broken hat regex, then I commented out the hat handling to avoid
breakage caused by the broken regex).
This patch makes sure the hat flags get set when setting the flags for
the main profile.
Also change RE_PROFILE_HAT_DEF to use more named matches
(leadingwhitespace and hat_keyword). Luckily all code that uses the
regex uses named matches already, which means adding another (...) pair
doesn't hurt.
Finally adjust the tests:
- change _test_set_flags to accept another optional parameter
expected_more_rules (used to specify the expected hat definition)
- add tests for hats (with '^foobar' and 'hat foobar' syntax)
- add tests for child profiles, one of them commented out (see below)
Remaining known issues (also added as TODO notes):
- The hat and child profile flags are *overwritten* with the flags used
for the main profile. (That's well-known behaviour from 2.8 :-/ but we
have more flags now, which makes this more annoying.)
The correct behaviour would be to add or remove the specified flag,
while keeping other flags unchanged.
- Child profiles are not handled/changed if you specify the 'program'
parameter. This means:
- 'aa-complain smbldap-useradd' or 'aa-complain /usr/sbin/smbldap-useradd'
_will not_ change the flags for the nscd child profile
- 'aa-complain /etc/apparmor.d/usr.sbin.smbldap-useradd' _will_ change
the flags for the nscd child profile (and any other profile and
child profile in that file)
Even with those remaining issues (which need bigger changes in
set_profile_flags() and maybe also in the whole flags handling), the
patch improves things and fixes the regression from the 2.8 code.
Acked-by: Steve Beattie <steve@nxnw.org> for trunk and 2.9
(might get re-used later ;-)
Also add two tests for profile names not starting with / - the quoted
version wasn't catched as invalid before, so this change is actually
also a bugfix.
Acked-by: Steve Beattie <steve@nxnw.org> for trunk and 2.9.
Add setUp() to AATest that sets "self.maxDiff = None" (unlimited).
This gives us unlimited array diffs everywhere where AATest is used.
Also rename several setUp() functions in test-regex_matches.py to
AASetup() to avoid that the shiny new AATest setUp() gets overwritten.
Acked-by: Steve Beattie <steve@nxnw.org>
As requested by Steve, also add an example AASetup() to test-example.py.
Replace usage of RE_PROFILE_CAP and RE_PROFILE_NETWORK with
CapabilityRule.match() and NetworkRule.match() calls.
This also means aa.py doesn't need to import those regexes anymore.
As a side effect of this change, test-regex_matches.py needs a small
fix because it imported RE_PROFILE_CAP from apparmor.aa instead of
apparmor.regex.
Acked-by: Seth Arnold <seth.arnold@canonical.com>
Add match() and _match() class methods to rule classes:
- _match() returns a regex match object for the given raw_rule
- match() converts the _match() result to True or False
The primary usage is to get an answer to the question "is this raw_rule
your job?". (For a moment, I thought about naming the function
*Rule.myjob() instead of *Rule.match() ;-)
My next patch will change aa.py to use *Rule.match() instead of directly
using RE_*, which will make the import list much shorter and hide
another implementation detail inside the rule classes.
Also change _parse() to use _match() instead of the regex, and add some
tests for match() and _match().
Acked-by: Seth Arnold <seth.arnold@canonical.com>
Add utils/test/test-network.py with tests for NetworkRule and
NetworkRuleset.
The tests are hopefully self-explaining, so let me just mention the most
important things:
- I started to play with namedtuple, which looks very useful (see "exp")
- the test loops make the tests much more readable (compare with
test-capability.py!) and make it easy to add some more tests
- 100% coverage :-)
Acked-by: Kshitij Gupta <kgupta8592@gmail.com>
Implement in-profile de-duplication in BaseRuleset (currently affects
"only" CapabilityRuleset, but will also work for all future *Ruleset
classes).
Also change 'deleted' to be a simple counter and add some tests that
verify the in-profile deduplication.
Acked-by: Seth Arnold <seth.arnold@canonical.com>
test_parse_modifiers_invalid() uses a hand-broken ;-) regex to parse
only the allow/deny/audit keywords. This test applies to all rule types
and doesn't contain anything specific to capability or other rules,
therefore it should live in test-baserule.py
Moving that test also means to move the imports for parse_modifiers and
re around (nothing else in test-capability.py needs them).
Acked-by: Kshitij Gupta <kgupta8592@gmail.com>
Add some tests for the Baserule class to cover the 3 functions that must
be re-implemented in each rule class. This means we finally get 100%
test coverage for apparmor/rule/__init__.py ;-)
Acked-by: Kshitij Gupta <kgupta8592@gmail.com>
Ensure nosetests sees all tests in the tests[] tuples. This requires
some name changes because nosetests thinks all function names containing
"test" are tests. (A "not a test" docorator would be an alternative, but
that would require some try/except magic to avoid a dependency on nose.)
To avoid nosetests thinks the functions are a test,
- rename setup_all_tests() to setup_all_loops()
- rename regex_test() to _regex_test() (in test-regex_matches.py)
Also add the module_name as parameter to setup_all_loops and always run
it (not only if __name__ == '__main__').
Known issue: nosetests errors out with
ValueError: no such test method in <class ...>: stub_test
when trying to run a single test generated out of tests[].
(debugging hint: stub_test is the name used in setup_test_loop().)
But that's still an improvement over not seeing those tests at all ;-)
Acked-by: Steve Beattie <steve@nxnw.org> for trunk and 2.9.
the LibreOffice profile uncovered that handling of @{var} += is broken:
File ".../utils/apparmor/aa.py", line 3272, in store_list_var
var[list_var] = set(var[list_var] + vlist)
TypeError: unsupported operand type(s) for +: 'set' and 'list'
This patch fixes it:
- change separate_vars() to use and return a set instead of a list
(FYI: separate_vars() is only called by store_list_var())
- adoptstore_list_var() to expect a set
- remove some old comments in these functions
- explain the less-intuitive parameters of store_list_var()
Also add some tests for separate_vars() and store_list_var().
The tests were developed based on the old code, but not all of them
succeed with the old code.
As usual, the tests uncovered some interesting[tm] behaviour in
separate_vars() (see the XXX comments and tell me what the really
expected behaviour is ;-)
Acked-by: Steve Beattie <steve@nxnw.org> for trunk and 2.9
Move the code that does the c -> a and d -> w replacement in denied_mask
and requested_mask so that it only runs for path and exec events, but not
for other events (like dbus and ptrace). The validate_log_mode() and
log_str_to_mode() calls are also moved.
Technically, this means moving code from parse_event() to the path
and exec sections in add_event_to_tree().
This also means aa-logprof no longer crashes if it hits a ptrace or
dbus event in the log.
The "if dmask:" and "if rmask:" checks are removed - if a path event
doesn't have these two, it is totally broken and worth a aa-logprof
crash ;-)
Also adjust the parse_event() tests to expect the "raw" mask instead of
a set.
This patch fixes
https://bugs.launchpad.net/apparmor/+bug/1426651 and
https://bugs.launchpad.net/apparmor/+bug/1243932
I manually tested that
- c and d log events are still converted to a and w
- aa-logprof handles exec events correctly
- ptrace events no longer crash aa-logprof
Note: add_event_to_tree() is not covered by tests.
Acked-by: Steve Beattie <steve@nxnw.org> for trunk and 2.9
"capability foo".is_covered("deny capability foo") should return False
even if check_allow_deny is False.
Also add some tests with check_allow_deny=False.
Acked-by: Steve Beattie <steve@nxnw.org>
Change serialize_parse_profile_start() to use parse_profile_start()
instead of using duplicated code.
The behaviour is mostly kept, with the exception that the function is
more strict now and raises exceptions instead of ignoring errors.
In practise, this won't change anything because the profiles are parsed
with parse_profile() (which calls parse_profile_start()) - and that
already errors out.
The tests are updated to match the more strict behaviour.
The next step would be to drop serialize_parse_profile_start()
completely, but this isn't urgent and can/should be done when we have
test coverage for serialize_profile_from_old_profile() one day ;-)
Acked-by: Steve Beattie <steve@nxnw.org>
Fix is_skippable_dir() - the regex also matched things like
/etc/apparmor.d/dont_disable, while it should match on the full
directory name.
Also add some tests based on a real-world aa-logprof run (with "print (path)"
in is_skippable_dir()) and some additional "funny"[tm] dirs.
Needless to say that the tests
('dont_disable', False),
('/etc/apparmor.d/cache_foo', False),
will fail with the old is_skippable_dir().
Acked-by: Steve Beattie <steve@nxnw.org>
Replace RE_PROFILE_START with RE_PROFILE_START_2 and adjust all
code sections that used RE_PROFILE_START_2.
The only real change is that test_get_flags_invalid_01 and
test_get_flags_invalid_02 now expect AppArmorException instead of
AppArmorBug.
Acked-by: Steve Beattie <steve@nxnw.org> for trunk
This patch implements attachment handling - aa-logprof now works with
profiles that have an attachment defined, instead of ignoring audit.log
entries for those profiles.
Changes:
- parse_profile_start_line(): remove workaround that merged the
attachment into the profile name
- parse_profile_data(): store attachment when parsing a profile
- update test_parse_profile_start_03, test_serialize_parse_profile_start_03,
test_set_flags_nochange_09 and some parse_profile_start_line() tests -
they now expect correct attachment handling
Acked-by: Steve Beattie <steve@nxnw.org>
this patch makes set_profile_flags more strict:
- raise AppArmorBug if newflags contains only whitespace
- raise AppArmorBug if the file doesn't contain the specified profile or
no profile at all
The tests are adjusted to expect AppArmorBug instead of a silent
failure. Also, some tests are added for profile=None, which means to
change the flags for all profiles in a file.
- test_set_flags_08 is now test_set_flags_invalid_04
- test_set_flags_invalid_03 is changed to only contain one reason for a
failure, not two ;-)
Acked-by: Steve Beattie <steve@nxnw.org>
Changes in set_profile_flags():
- rewrite set_profile_flags to use parse_profile_start_line() and
write_header().
- replace the silent failure for non-existing files with a proper
exception (using lazy programming - the check is done by removing the
"if os.path.isfile()" check, open_file_read then raises the
exception ;-)
- comment out regex_hat_flag and the code that was supposed to handle
hat flags, which were totally broken. We'll need another patch to fix
it, and we also need to decide if we want to do that because it
introduces a behaviour change (currently, aa-complain etc. don't
change hat flags).
The tests for set_profile_flags() are also updated:
- prepend a space to comments because write_header always adds a space
between '{' and the comment
- remove a test with superfluous quotes that are no longer kept (that's
just a profile cleanup, so dropping that test is the easiest way)
- update test_set_flags_10 and test_set_flags_12 to use the correct
profile name
- enable the tests for invalid (empty) flags
- update the test for a non-existing file
Note: test_set_flags_10, test_set_flags_12 and test_set_flags_nochange_09
will fail with this patch applied. The next patch will fix that.
Acked-by: Steve Beattie <steve@nxnw.org>
Change the write_header tests so that the 'profile_keyword' and
'header_comment' parameters can be (and are) tested:
- add a None for both to the existing tests
- add some tests that come with the profile keyword and/or a comment
Acked-by: Steve Beattie <steve@nxnw.org>
- add support for prof_data['header_comment'] (comment after '{')
and prof_data['profile_keyword'] (to force the 'profile' keyword, even
if it isn't needed) to write_header().
(set_profile_flags() will be the only user of these two for now)
- fix a crash if depth is not an integer - for example,
len(' ')/2 # 3 spaces = 1.5
would cause a crash.
Also add a test for 1.5 and 1.3 spaces.
- rewrite the handling of flags to avoid we have to maintain two
different template lines.
- update the tests to set 'profile_keyword' and 'header_comment' to None.
This avoids big changes in the test code. I'll send another patch that
makes sure profile_keyword and header_comment are tested ;-)
Acked-by: Steve Beattie <steve@nxnw.org>
Add the attachment to the parse_profile_start() and
serialize_parse_profile_start() return values, and adjust the functions
calling the *parse_profile_start() functions to save the attachment in
the "attachment" variable (which isn't used yet).
Also adjust the tests for the added return value.
(Sorry for not getting the resultset right from the beginning!)
Acked-by: Steve Beattie <steve@nxnw.org>
Also fix a little bug that added the profile keyword if the path needed
quotes (profile "/foo bar" - but "/foo bar" is enough). This was caused
by a regex that always matched on quoted paths (hint: "/ matches
^[^/] ;-)
Also add some tests with attachments and update the test for the bugfix
mentioned above.
Now the remaining part is to make sure that prof_data['attachment'] gets
set when parsing the profiles :-)
Acked-by: Steve Beattie <steve@nxnw.org>
Also add loop support to test-aa.py.
BTW: In case you wonder - the need to replace unittest.TestCase with
AATest is intentional. It might look annoying, but it makes sure that
a test-*.py file doesn't contain a test class where tests = [...] is
ignored because it's still unittest.TestCase.
(Technically, setup_all_tests() will error out if a test class doesn't
contain tests = [...] - either explicit or via its parent AATest.)
Acked-by: Steve Beattie <steve@nxnw.org>
Add various tests for set_profile_flags, and document various
interesting[tm] things I discovered while writing the tests (see
the inline comments for details).
Also adds a read_file() function to common_test.py.
The most interesting[tm] thing I found is:
regex_hat_flag = re.compile('^([a-z]*)\s+([A-Z]*)\s*(#.*)?$')
which matches various unexpected things - but not a hat :-/
(see mailinglist for all funny details)
Acked-by: Steve Beattie <steve@nxnw.org>
Convert serialize_parse_profile_start() to use
parse_profile_start_line(), and adjust a test to expect an AppArmorBug
instead of an AttributeError exception.
Also add two tests (they succeed with the old and the new code).
Note that these tests document interesting[tm] behaviour - I tend to
think that those cases should raise an exception, but I'm not sure about
this because serialize_profile_from_old_profile() is a good example for
interesting[tm] code :-/
I couldn't come up with a real-world test profile that would hit those
cases without erroring out aa-logprof earlier - maybe the (more
sane-looking) parse_profiles() / serialize_parse_profile_start()
protects us from hitting this interesting[tm] behaviour.
Acked-by: Steve Beattie <steve@nxnw.org>
The commit message for r2976 says:
[...]
The patch also adds test-example.py, which is
- a demo of the code added to common_test.py
- a template file that we can copy for future test-*.py
Acked-by: Steve Beattie <steve@nxnw.org>
but I forgot to add test-example.py to bzr, which I hereby do.
The previous patch slightly changed the behaviour of parse_profile_start()
and get_profile_flags() - they raise AppArmorBug instead of
AppArmorException when specifying a line that is not the start of a
profile and therefore doesn't match RE_PROFILE_START_2.
This patch updates test-aa.py to expect the correct exceptions, and adds
another test with quoted profile name to ensure that stripping the
quotes works as expected.
Acked-by: Steve Beattie <steve@nxnw.org>
Add better support for looping over a tests[] array to common_test.py:
- class AATest - a base class we can use for all tests, and that will
probably get more features in the future (for example tempdir
handling)
- setup_all_tests() - a function that iterates over all classes in the
given file and calls setup_test_loops() for each of them
- setup_tests_loop() - a function that creates tests based on tests[]
in the given class. Those tests call the class' _run_test() method for
each test specified in tests[] (inspired by setup_regex_tests() ;-)
This means we can get rid of the manually maintained tests list in
test-regex_matches.py and just need to call setup_all_tests() once in
each file.
The patch also adds test-example.py, which is
- a demo of the code added to common_test.py
- a template file that we can copy for future test-*.py
Acked-by: Steve Beattie <steve@nxnw.org>
As a follow-up to the logparser.py change that converts disconnected
path events to an error, add a testcase to test-logparser.py.
Acked-by: Steve Beattie <steve@nxnw.org> for both trunk and 2.9.
The upcoming function parse_profile_start() (which is a wrapper around
the updated RE_PROFILE_START, and will live in regex.py) needs
strip_profile(), but importing it from aa.py fails with an import loop.
Therefore this patch moves strip_quotes() from aa.py to regex.py and
re-imports it into aa.py.
As a bonus, the patch also adds some tests for strip_quotes() ;-)
Also add TestStripQuotes to the test_suite list because it won't run
otherwise.
Acked-by: Steve Beattie <steve@nxnw.org> for both trunk and 2.9
Move the code for parsing the profile start ("/foo {") from aa.py
parse_profile_data() to a separate function parse_profile_start().
Most of the changes are just moving around code, with some small
exceptions:
- instead of handing over profile_data to parse_profile_start() to
modify it, it sets two variables (pps_set_profile and
pps_set_hat_external) as part of its return value, which are then
used in parse_profile_data() to set the flags in profile_data.
- existing_profiles[profile] = file is executed later, which means
it used the strip_quotes() version of profile now
- whitespace / tab level changes
The patch also adds some tests for the parse_profile_start() function.
Acked-by: Steve Beattie <steve@nxnw.org>
Also adds a check to get_profile_flags() to catch an invalid syntax:
/foo ( ) {
was accepted by get_profile_flags, while
/foo () {
failed.
When testing with the parser, both result in a syntax error, therefore
the patch makes sure it also fails in get_profile_flags().
Acked-by: Steve Beattie <steve@nxnw.org>
This means that aa-logprof will ignore the event instead of crashing with
AppArmorException: 'Unexpected rank input: var/run/nscd/passwd'
Note that I made the check as specific as possible to be sure it doesn't
hide other events.
References: https://bugzilla.opensuse.org/show_bug.cgi?id=918787
Acked-by: Steve Beattie <steve@nxnw.org>
Also update test-capability.py - it contains a test that needs
'error_code': 0,
added to avoid a failure.
Patch by: Steve Beattie <steve@nxnw.org>
Acked-by: Christian Boltz <apparmor@cboltz.de>
libapparmor _aa_is_blacklisted() - some extensions were missing in the
python code.
Also make the code more readable and add some testcases.
Notes:
- the original code additionally ignored *.swp. I didn't include that -
*.swp looks like vim swap files which are also dot files
- the python code ignores README files, but the C code doesn't
(do we need to add README in the C code?)
Acked-by: Kshitij Gupta <kgupta8592@gmail.com> for 2.9 and trunk
Acked-by: Steve Beattie <steve@nxnw.org>
string or if a mode_char is not in MODE_HASH.
Also update the testcase for "asdf42" (which raises AppArmorBug now)
and add a test that simulates MODE_HASH and MODE_MAP_SET getting out
of sync (tests the second part of the if condition).
Acked-by: Steve Beattie <steve@nxnw.org>