[apparmor] RFC - generate regression tests from stdin

John Johansen john.johansen at canonical.com
Thu Apr 5 10:52:05 UTC 2012


The following patch is wip on an alternate way to auto generate profiles for
the regression test suite. I am not proposing replacing the older short hand
just providing a new more flexible way to specify test profiles when needed.

It came about as I have been kicking about extending the test suite for all
kind of things that aren't supported atm, (mount rules, owner, audit, named
transitions, child profiles, namespaces, ..)

It uses stdin and the shell variables and a few special apparmorish variables,
to represent where/what auto generation should be done.

the special apparmorish variables are
@{gen_elf name} - generate rules for elf binaries
@{gen_bin name} - generate rules for a binary
@{gen_def} - generate default rules
@{gen name} - do @{gen_def} @{gen_bin name}

To generate a profile you do

genprofile --stdin <<EOF
/profile/name {
@{gen /profile/name}
}
EOF

eg. to generate the equivalent of
  genprofile
you would do
  genprofile --stdin <<EOF
  $test {
  @{gen $test}
  }
EOF

and the equiv of
  genprofile $file:rw
would be
  genprofile --stdin <<EOF
  $test {
  @{gen $test}
  $file rw,
  }


while it takes a little more to generate a base profile than the old syntax, it
use the actual profile language (augmented with the special variables), it is a
lot more flexible, and a lot easier to expand when new rule types are added.

eg. of something not possible with the current auto generation
    Generate a profile with a child profile and hat and a trailing profile

genprofile --stdin <<EOF
$test {
@{gen $test}

  profile $bin/open {
@{gen $bin/open}
  }

  ^hatfoo {
     $file rw,
  }
}
profile $bin/exec {
@{gen $bin/exec}
}
EOF


The majority of the patch is just moving the mkprofile.pl's body into the
fn gen_from_args() and the new functionality lies in gen_from_stdin()


---

=== modified file 'tests/regression/apparmor/mkprofile.pl'
--- tests/regression/apparmor/mkprofile.pl	2012-03-09 12:23:25 +0000
+++ tests/regression/apparmor/mkprofile.pl	2012-04-05 10:18:55 +0000
@@ -16,6 +16,7 @@
 my $nodefault;
 my $noimage;
 my $escape = '';
+my $usestdin = '';
 my %output_rules;
 my $hat = "__no_hat";
 my %flags;
@@ -26,19 +27,22 @@
   'help|h' => \$help,
   'nodefault|N' => \$nodefault,
   'noimage|I' => \$noimage,
+  'stdin' => \$usestdin,
 );
 
 sub usage {
   print STDERR "$__VERSION__\n";
   print STDERR "Usage $0 [--nowarn|--escape] execname [rules]\n";
   print STDERR "      $0 --help\n";
+  print STDERR "      $0 --stdin\n";
   print STDERR "  nowarn:      don't warn if execname does not exist\n";
   print STDERR "  nodefault:   don't include default rules/ldd output\n";
   print STDERR "  escape:      escape stuff that would be treated as regexs\n";
   print STDERR "  help:        print this message\n";
 }
 
-&usage && exit 0 if ($help || @ARGV < 1);
+# genprofile passes in $bin:w as default rule atm
+&usage && exit 0 if ($help || (!$usestdin && @ARGV < 1) || ($usestdin && @ARGV != 2));
 
 sub head ($) {
   my $file = shift;
@@ -214,34 +218,6 @@
   }
 }
 
-my $bin = shift @ARGV;
-!(-e $bin || $nowarn) && print STDERR "Warning: execname '$bin': no such file or directory\n";
-
-unless ($nodefault) {
-  gen_default_rules();
-  gen_binary($bin);
-}
-
-for my $rule (@ARGV) {
-  #($fn, @rules) = split (/:/, $rule);
-  if ($rule =~ /^(tcp|udp)/) {
-    # netdomain rules
-    gen_netdomain($rule);
-  } elsif ($rule =~ /^network:/) {
-    gen_network($rule);
-  } elsif ($rule =~ /^cap:/) {
-    gen_cap($rule);
-  } elsif ($rule =~ /^flag:/) {
-    gen_flag($rule);
-  } elsif ($rule =~ /^hat:/) {
-    gen_hat($rule);
-  } elsif ($rule =~ /^addimage:/) {
-    gen_addimage($rule);
-  } else {
-    gen_file($rule);
-  }
-}
-
 sub emit_flags($) {
   my $hat = shift;
 
@@ -255,26 +231,99 @@
   }
 }
 
-print STDOUT "# Profile autogenerated by $__VERSION__\n";
-print STDOUT "$bin ";
-emit_flags('__no_hat');
-print STDOUT "{\n";
-foreach my $outrule (@{$output_rules{'__no_hat'}}) {
-  print STDOUT $outrule;
-}
-foreach my $hat (keys %output_rules) {
-  if (not $hat =~ /^__no_hat$/) {
-    print STDOUT "\n  ^$hat";
-    emit_flags($hat);
-    print STDOUT " {\n";
-    foreach my $outrule (@{$output_rules{$hat}}) {
-      print STDOUT "  $outrule";
-    }
-    print STDOUT "  }\n";
-  }
-}
-#foreach my $hat keys
-#foreach my $outrule (@output_rules) {
-#  print STDOUT $outrule;
-#}
-print STDOUT "}\n";
+# generate profiles based on cmd line arguments
+sub gen_from_args() {
+  my $bin = shift @ARGV;
+  !(-e $bin || $nowarn) && print STDERR "Warning: execname '$bin': no such file or directory\n";
+
+  unless ($nodefault) {
+    gen_default_rules();
+    gen_binary($bin);
+  }
+
+  for my $rule (@ARGV) {
+    #($fn, @rules) = split (/:/, $rule);
+    if ($rule =~ /^(tcp|udp)/) {
+      # netdomain rules
+      gen_netdomain($rule);
+    } elsif ($rule =~ /^network:/) {
+      gen_network($rule);
+    } elsif ($rule =~ /^cap:/) {
+      gen_cap($rule);
+    } elsif ($rule =~ /^flag:/) {
+      gen_flag($rule);
+    } elsif ($rule =~ /^hat:/) {
+      gen_hat($rule);
+    } elsif ($rule =~ /^addimage:/) {
+      gen_addimage($rule);
+    } else {
+      gen_file($rule);
+    }
+  }
+
+  print STDOUT "# Profile autogenerated by $__VERSION__\n";
+  print STDOUT "$bin ";
+  emit_flags('__no_hat');
+  print STDOUT "{\n";
+  foreach my $outrule (@{$output_rules{'__no_hat'}}) {
+    print STDOUT $outrule;
+  }
+  foreach my $hat (keys %output_rules) {
+    if (not $hat =~ /^__no_hat$/) {
+      print STDOUT "\n  ^$hat";
+      emit_flags($hat);
+      print STDOUT " {\n";
+      foreach my $outrule (@{$output_rules{$hat}}) {
+        print STDOUT "  $outrule";
+      }
+      print STDOUT "  }\n";
+    }
+  }
+  #foreach my $hat keys
+  #foreach my $outrule (@output_rules) {
+  #  print STDOUT $outrule;
+  #}
+  print STDOUT "}\n";
+}
+
+#generate the profiles from stdin, interpreting and replacing the following sequences
+# @{gen_elf name} - generate rules for elf binaries
+# @{gen_bin name} - generate rules for a binary
+# @{gen_def} - generate default rules
+# @{gen name} - do @{gen_def} @{gen_bin name}
+
+sub emit_and_clear_rules() {
+  foreach my $outrule (@{$output_rules{'__no_hat'}}) {
+    print STDOUT $outrule;
+  }
+
+  undef %output_rules;
+}
+
+sub gen_from_stdin() {
+  while(<STDIN>) {
+    chomp;
+    if ($_ =~ m/@\{gen_def}/) {
+      gen_default_rules();
+      emit_and_clear_rules();
+    } elsif ($_ =~ m/@\{gen_bin\s+(.+)\}/) {
+      gen_binary($1);
+      emit_and_clear_rules();
+    } elsif ($_ =~ m/@\{gen_elf\s+(.+)\}/) {
+      gen_elf_binary($1);
+      emit_and_clear_rules();
+    } elsif ($_ =~ m/@\{gen\s+(.+)\}/) {
+      gen_default_rules();
+      gen_binary($1);
+      emit_and_clear_rules();
+    } else {
+      print STDOUT "$_\n" ;
+    }
+  }
+}
+
+if ($usestdin) {
+  gen_from_stdin();
+} else {
+  gen_from_args();
+}




More information about the AppArmor mailing list