gitee.com/mysnapcore/mysnapd@v0.1.0/interfaces/apparmor/spec_test.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2016-2018 Canonical Ltd
     5   *
     6   * This program is free software: you can redistribute it and/or modify
     7   * it under the terms of the GNU General Public License version 3 as
     8   * published by the Free Software Foundation.
     9   *
    10   * This program is distributed in the hope that it will be useful,
    11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
    12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13   * GNU General Public License for more details.
    14   *
    15   * You should have received a copy of the GNU General Public License
    16   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
    17   *
    18   */
    19  
    20  package apparmor_test
    21  
    22  import (
    23  	"strings"
    24  
    25  	. "gopkg.in/check.v1"
    26  
    27  	"gitee.com/mysnapcore/mysnapd/interfaces"
    28  	"gitee.com/mysnapcore/mysnapd/interfaces/apparmor"
    29  	"gitee.com/mysnapcore/mysnapd/interfaces/ifacetest"
    30  	"gitee.com/mysnapcore/mysnapd/snap"
    31  	"gitee.com/mysnapcore/mysnapd/snap/snaptest"
    32  	"gitee.com/mysnapcore/mysnapd/testutil"
    33  )
    34  
    35  type specSuite struct {
    36  	testutil.BaseTest
    37  	iface    *ifacetest.TestInterface
    38  	spec     *apparmor.Specification
    39  	plugInfo *snap.PlugInfo
    40  	plug     *interfaces.ConnectedPlug
    41  	slotInfo *snap.SlotInfo
    42  	slot     *interfaces.ConnectedSlot
    43  }
    44  
    45  var _ = Suite(&specSuite{
    46  	iface: &ifacetest.TestInterface{
    47  		InterfaceName: "test",
    48  		AppArmorConnectedPlugCallback: func(spec *apparmor.Specification, plug *interfaces.ConnectedPlug, slot *interfaces.ConnectedSlot) error {
    49  			spec.AddSnippet("connected-plug")
    50  			return nil
    51  		},
    52  		AppArmorConnectedSlotCallback: func(spec *apparmor.Specification, plug *interfaces.ConnectedPlug, slot *interfaces.ConnectedSlot) error {
    53  			spec.AddSnippet("connected-slot")
    54  			return nil
    55  		},
    56  		AppArmorPermanentPlugCallback: func(spec *apparmor.Specification, plug *snap.PlugInfo) error {
    57  			spec.AddSnippet("permanent-plug")
    58  			return nil
    59  		},
    60  		AppArmorPermanentSlotCallback: func(spec *apparmor.Specification, slot *snap.SlotInfo) error {
    61  			spec.AddSnippet("permanent-slot")
    62  			return nil
    63  		},
    64  	},
    65  	plugInfo: &snap.PlugInfo{
    66  		Snap:      &snap.Info{SuggestedName: "snap1"},
    67  		Name:      "name",
    68  		Interface: "test",
    69  		Apps: map[string]*snap.AppInfo{
    70  			"app1": {
    71  				Snap: &snap.Info{
    72  					SuggestedName: "snap1",
    73  				},
    74  				Name: "app1"}},
    75  	},
    76  	slotInfo: &snap.SlotInfo{
    77  		Snap:      &snap.Info{SuggestedName: "snap2"},
    78  		Name:      "name",
    79  		Interface: "test",
    80  		Apps: map[string]*snap.AppInfo{
    81  			"app2": {
    82  				Snap: &snap.Info{
    83  					SuggestedName: "snap2",
    84  				},
    85  				Name: "app2"}},
    86  	},
    87  })
    88  
    89  func (s *specSuite) SetUpTest(c *C) {
    90  	s.BaseTest.SetUpTest(c)
    91  	s.BaseTest.AddCleanup(snap.MockSanitizePlugsSlots(func(snapInfo *snap.Info) {}))
    92  
    93  	s.spec = &apparmor.Specification{}
    94  	s.plug = interfaces.NewConnectedPlug(s.plugInfo, nil, nil)
    95  	s.slot = interfaces.NewConnectedSlot(s.slotInfo, nil, nil)
    96  }
    97  
    98  func (s *specSuite) TearDownTest(c *C) {
    99  	s.BaseTest.TearDownTest(c)
   100  }
   101  
   102  // The spec.Specification can be used through the interfaces.Specification interface
   103  func (s *specSuite) TestSpecificationIface(c *C) {
   104  	var r interfaces.Specification = s.spec
   105  	c.Assert(r.AddConnectedPlug(s.iface, s.plug, s.slot), IsNil)
   106  	c.Assert(r.AddConnectedSlot(s.iface, s.plug, s.slot), IsNil)
   107  	c.Assert(r.AddPermanentPlug(s.iface, s.plugInfo), IsNil)
   108  	c.Assert(r.AddPermanentSlot(s.iface, s.slotInfo), IsNil)
   109  	c.Assert(s.spec.Snippets(), DeepEquals, map[string][]string{
   110  		"snap.snap1.app1": {"connected-plug", "permanent-plug"},
   111  		"snap.snap2.app2": {"connected-slot", "permanent-slot"},
   112  	})
   113  }
   114  
   115  // AddSnippet adds a snippet for the given security tag.
   116  func (s *specSuite) TestAddSnippet(c *C) {
   117  	restore := apparmor.SetSpecScope(s.spec, []string{"snap.demo.command", "snap.demo.service"})
   118  	defer restore()
   119  
   120  	// Add two snippets in the context we are in.
   121  	s.spec.AddSnippet("snippet 1")
   122  	s.spec.AddSnippet("snippet 2")
   123  
   124  	// The snippets were recorded correctly.
   125  	c.Assert(s.spec.UpdateNS(), HasLen, 0)
   126  	c.Assert(s.spec.Snippets(), DeepEquals, map[string][]string{
   127  		"snap.demo.command": {"snippet 1", "snippet 2"},
   128  		"snap.demo.service": {"snippet 1", "snippet 2"},
   129  	})
   130  	c.Assert(s.spec.SnippetsForTag("snap.demo.command"), DeepEquals, []string{"snippet 1", "snippet 2"})
   131  	c.Assert(s.spec.SnippetForTag("snap.demo.command"), Equals, "snippet 1\nsnippet 2")
   132  	c.Assert(s.spec.SecurityTags(), DeepEquals, []string{"snap.demo.command", "snap.demo.service"})
   133  }
   134  
   135  // AddDeduplicatedSnippet adds a snippet for the given security tag.
   136  func (s *specSuite) TestAddDeduplicatedSnippet(c *C) {
   137  	restore := apparmor.SetSpecScope(s.spec, []string{"snap.demo.command", "snap.demo.service"})
   138  	defer restore()
   139  
   140  	// Add two snippets in the context we are in.
   141  	s.spec.AddDeduplicatedSnippet("dedup snippet 1")
   142  	s.spec.AddDeduplicatedSnippet("dedup snippet 1")
   143  	s.spec.AddDeduplicatedSnippet("dedup snippet 2")
   144  	s.spec.AddDeduplicatedSnippet("dedup snippet 2")
   145  
   146  	// The snippets were recorded correctly.
   147  	c.Assert(s.spec.UpdateNS(), HasLen, 0)
   148  	c.Assert(s.spec.Snippets(), DeepEquals, map[string][]string{
   149  		"snap.demo.command": {"dedup snippet 1", "dedup snippet 2"},
   150  		"snap.demo.service": {"dedup snippet 1", "dedup snippet 2"},
   151  	})
   152  	c.Assert(s.spec.SnippetsForTag("snap.demo.command"), DeepEquals, []string{"dedup snippet 1", "dedup snippet 2"})
   153  	c.Assert(s.spec.SnippetForTag("snap.demo.command"), Equals, "dedup snippet 1\ndedup snippet 2")
   154  	c.Assert(s.spec.SecurityTags(), DeepEquals, []string{"snap.demo.command", "snap.demo.service"})
   155  }
   156  
   157  func (s *specSuite) TestAddParametricSnippet(c *C) {
   158  	restore := apparmor.SetSpecScope(s.spec, []string{"snap.demo.command", "snap.demo.service"})
   159  	defer restore()
   160  
   161  	s.spec.AddParametricSnippet([]string{"prefix ", " postfix"}, "param1")
   162  	s.spec.AddParametricSnippet([]string{"prefix ", " postfix"}, "param1")
   163  	s.spec.AddParametricSnippet([]string{"prefix ", " postfix"}, "param2")
   164  	s.spec.AddParametricSnippet([]string{"prefix ", " postfix"}, "param2")
   165  	s.spec.AddParametricSnippet([]string{"other "}, "param")
   166  	c.Assert(s.spec.SnippetsForTag("snap.demo.command"), DeepEquals, []string{
   167  		"other param",
   168  		"prefix {param1,param2} postfix",
   169  	})
   170  	c.Assert(s.spec.SnippetForTag("snap.demo.command"), Equals, "other param\nprefix {param1,param2} postfix")
   171  	c.Assert(s.spec.Snippets(), DeepEquals, map[string][]string{
   172  		"snap.demo.command": {"other param", "prefix {param1,param2} postfix"},
   173  		"snap.demo.service": {"other param", "prefix {param1,param2} postfix"},
   174  	})
   175  }
   176  
   177  // All of AddSnippet, AddDeduplicatedSnippet, AddParameticSnippet work correctly together.
   178  func (s *specSuite) TestAddSnippetAndAddDeduplicatedAndParamSnippet(c *C) {
   179  	restore := apparmor.SetSpecScope(s.spec, []string{"snap.demo.command", "snap.demo.service"})
   180  	defer restore()
   181  
   182  	// Add three snippets in the context we are in.
   183  	s.spec.AddSnippet("normal")
   184  	s.spec.AddDeduplicatedSnippet("dedup")
   185  	s.spec.AddParametricSnippet([]string{""}, "param")
   186  
   187  	// The snippets were recorded correctly.
   188  	c.Assert(s.spec.UpdateNS(), HasLen, 0)
   189  	c.Assert(s.spec.Snippets(), DeepEquals, map[string][]string{
   190  		"snap.demo.command": {"normal", "dedup", "param"},
   191  		"snap.demo.service": {"normal", "dedup", "param"},
   192  	})
   193  	c.Assert(s.spec.SnippetsForTag("snap.demo.command"), DeepEquals, []string{"normal", "dedup", "param"})
   194  	c.Assert(s.spec.SnippetForTag("snap.demo.command"), Equals, "normal\ndedup\nparam")
   195  	c.Assert(s.spec.SecurityTags(), DeepEquals, []string{"snap.demo.command", "snap.demo.service"})
   196  }
   197  
   198  // Define tags but don't add any snippets.
   199  func (s *specSuite) TestTagsButNoSnippets(c *C) {
   200  	restore := apparmor.SetSpecScope(s.spec, []string{"snap.demo.command", "snap.demo.service"})
   201  	defer restore()
   202  
   203  	c.Assert(s.spec.UpdateNS(), HasLen, 0)
   204  	c.Assert(s.spec.Snippets(), DeepEquals, map[string][]string{})
   205  	c.Assert(s.spec.SnippetsForTag("snap.demo.command"), DeepEquals, []string(nil))
   206  	c.Assert(s.spec.SnippetForTag("snap.demo.command"), Equals, "")
   207  	c.Assert(s.spec.SecurityTags(), DeepEquals, []string(nil))
   208  }
   209  
   210  // Don't define any tags but add snippets.
   211  func (s *specSuite) TestNoTagsButWithSnippets(c *C) {
   212  	restore := apparmor.SetSpecScope(s.spec, []string{})
   213  	defer restore()
   214  
   215  	s.spec.AddSnippet("normal")
   216  	s.spec.AddDeduplicatedSnippet("dedup")
   217  	s.spec.AddParametricSnippet([]string{""}, "param")
   218  
   219  	c.Assert(s.spec.UpdateNS(), HasLen, 0)
   220  	c.Assert(s.spec.Snippets(), DeepEquals, map[string][]string{})
   221  	c.Assert(s.spec.SnippetsForTag("snap.demo.command"), DeepEquals, []string(nil))
   222  	c.Assert(s.spec.SnippetForTag("snap.demo.command"), Equals, "")
   223  	c.Assert(s.spec.SecurityTags(), DeepEquals, []string(nil))
   224  }
   225  
   226  // Don't define any tags or snippets.
   227  func (s *specSuite) TestsNoTagsOrSnippets(c *C) {
   228  	restore := apparmor.SetSpecScope(s.spec, []string{})
   229  	defer restore()
   230  
   231  	c.Assert(s.spec.UpdateNS(), HasLen, 0)
   232  	c.Assert(s.spec.Snippets(), DeepEquals, map[string][]string{})
   233  	c.Assert(s.spec.SnippetsForTag("snap.demo.command"), DeepEquals, []string(nil))
   234  	c.Assert(s.spec.SnippetForTag("snap.demo.command"), Equals, "")
   235  	c.Assert(s.spec.SecurityTags(), DeepEquals, []string(nil))
   236  }
   237  
   238  // AddUpdateNS adds a snippet for the snap-update-ns profile for a given snap.
   239  func (s *specSuite) TestAddUpdateNS(c *C) {
   240  	restore := apparmor.SetSpecScope(s.spec, []string{"snap.demo.command", "snap.demo.service"})
   241  	defer restore()
   242  
   243  	// Add a two snap-update-ns snippets in the context we are in.
   244  	s.spec.AddUpdateNS("s-u-n snippet 1")
   245  	s.spec.AddUpdateNS("s-u-n snippet 2")
   246  
   247  	// Check the order of the snippets can be retrieved.
   248  	idx, ok := s.spec.UpdateNSIndexOf("s-u-n snippet 2")
   249  	c.Assert(ok, Equals, true)
   250  	c.Check(idx, Equals, 1)
   251  
   252  	// The snippets were recorded correctly and in the right place.
   253  	c.Assert(s.spec.UpdateNS(), DeepEquals, []string{
   254  		"s-u-n snippet 1", "s-u-n snippet 2",
   255  	})
   256  	c.Assert(s.spec.SnippetForTag("snap.demo.command"), Equals, "")
   257  	c.Assert(s.spec.SecurityTags(), HasLen, 0)
   258  }
   259  
   260  const snapWithLayout = `
   261  name: vanguard
   262  version: 0
   263  apps:
   264    vanguard:
   265      command: vanguard
   266  layout:
   267    /usr/foo:
   268      bind: $SNAP/usr/foo
   269    /var/tmp:
   270      type: tmpfs
   271      mode: 1777
   272    /var/cache/mylink:
   273      symlink: $SNAP_DATA/link/target
   274    /etc/foo.conf:
   275      bind-file: $SNAP/foo.conf
   276  `
   277  
   278  func (s *specSuite) TestApparmorSnippetsFromLayout(c *C) {
   279  	snapInfo := snaptest.MockInfo(c, snapWithLayout, &snap.SideInfo{Revision: snap.R(42)})
   280  	restore := apparmor.SetSpecScope(s.spec, []string{"snap.vanguard.vanguard"})
   281  	defer restore()
   282  
   283  	s.spec.AddLayout(snapInfo)
   284  	c.Assert(s.spec.Snippets(), DeepEquals, map[string][]string{
   285  		"snap.vanguard.vanguard": {
   286  			"# Layout path: /etc/foo.conf\n\"/etc/foo.conf\" mrwklix,",
   287  			"# Layout path: /usr/foo\n\"/usr/foo{,/**}\" mrwklix,",
   288  			"# Layout path: /var/cache/mylink\n# (no extra permissions required for symlink)",
   289  			"# Layout path: /var/tmp\n\"/var/tmp{,/**}\" mrwklix,",
   290  		},
   291  	})
   292  	updateNS := s.spec.UpdateNS()
   293  
   294  	profile0 := `  # Layout /etc/foo.conf: bind-file $SNAP/foo.conf
   295    mount options=(bind, rw) "/snap/vanguard/42/foo.conf" -> "/etc/foo.conf",
   296    mount options=(rprivate) -> "/etc/foo.conf",
   297    umount "/etc/foo.conf",
   298    # Writable mimic /etc
   299    # .. permissions for traversing the prefix that is assumed to exist
   300    "/" r,
   301    # .. variant with mimic at /etc/
   302    # Allow reading the mimic directory, it must exist in the first place.
   303    "/etc/" r,
   304    # Allow setting the read-only directory aside via a bind mount.
   305    "/tmp/.snap/etc/" rw,
   306    mount options=(rbind, rw) "/etc/" -> "/tmp/.snap/etc/",
   307    # Allow mounting tmpfs over the read-only directory.
   308    mount fstype=tmpfs options=(rw) tmpfs -> "/etc/",
   309    # Allow creating empty files and directories for bind mounting things
   310    # to reconstruct the now-writable parent directory.
   311    "/tmp/.snap/etc/*/" rw,
   312    "/etc/*/" rw,
   313    mount options=(rbind, rw) "/tmp/.snap/etc/*/" -> "/etc/*/",
   314    "/tmp/.snap/etc/*" rw,
   315    "/etc/*" rw,
   316    mount options=(bind, rw) "/tmp/.snap/etc/*" -> "/etc/*",
   317    # Allow unmounting the auxiliary directory.
   318    # TODO: use fstype=tmpfs here for more strictness (LP: #1613403)
   319    mount options=(rprivate) -> "/tmp/.snap/etc/",
   320    umount "/tmp/.snap/etc/",
   321    # Allow unmounting the destination directory as well as anything
   322    # inside.  This lets us perform the undo plan in case the writable
   323    # mimic fails.
   324    mount options=(rprivate) -> "/etc/",
   325    mount options=(rprivate) -> "/etc/*",
   326    mount options=(rprivate) -> "/etc/*/",
   327    umount "/etc/",
   328    umount "/etc/*",
   329    umount "/etc/*/",
   330    # Writable mimic /snap/vanguard/42
   331    "/snap/" r,
   332    "/snap/vanguard/" r,
   333    # .. variant with mimic at /snap/vanguard/42/
   334    "/snap/vanguard/42/" r,
   335    "/tmp/.snap/snap/vanguard/42/" rw,
   336    mount options=(rbind, rw) "/snap/vanguard/42/" -> "/tmp/.snap/snap/vanguard/42/",
   337    mount fstype=tmpfs options=(rw) tmpfs -> "/snap/vanguard/42/",
   338    "/tmp/.snap/snap/vanguard/42/*/" rw,
   339    "/snap/vanguard/42/*/" rw,
   340    mount options=(rbind, rw) "/tmp/.snap/snap/vanguard/42/*/" -> "/snap/vanguard/42/*/",
   341    "/tmp/.snap/snap/vanguard/42/*" rw,
   342    "/snap/vanguard/42/*" rw,
   343    mount options=(bind, rw) "/tmp/.snap/snap/vanguard/42/*" -> "/snap/vanguard/42/*",
   344    mount options=(rprivate) -> "/tmp/.snap/snap/vanguard/42/",
   345    umount "/tmp/.snap/snap/vanguard/42/",
   346    mount options=(rprivate) -> "/snap/vanguard/42/",
   347    mount options=(rprivate) -> "/snap/vanguard/42/*",
   348    mount options=(rprivate) -> "/snap/vanguard/42/*/",
   349    umount "/snap/vanguard/42/",
   350    umount "/snap/vanguard/42/*",
   351    umount "/snap/vanguard/42/*/",
   352  `
   353  	// Find the slice that describes profile0 by looking for the first unique
   354  	// line of the next profile.
   355  	start := 0
   356  	end, _ := s.spec.UpdateNSIndexOf("  # Layout /usr/foo: bind $SNAP/usr/foo\n")
   357  	c.Assert(strings.Join(updateNS[start:end], ""), Equals, profile0)
   358  
   359  	profile1 := `  # Layout /usr/foo: bind $SNAP/usr/foo
   360    mount options=(rbind, rw) "/snap/vanguard/42/usr/foo/" -> "/usr/foo/",
   361    mount options=(rprivate) -> "/usr/foo/",
   362    umount "/usr/foo/",
   363    # Writable mimic /usr
   364    # .. variant with mimic at /usr/
   365    "/usr/" r,
   366    "/tmp/.snap/usr/" rw,
   367    mount options=(rbind, rw) "/usr/" -> "/tmp/.snap/usr/",
   368    mount fstype=tmpfs options=(rw) tmpfs -> "/usr/",
   369    "/tmp/.snap/usr/*/" rw,
   370    "/usr/*/" rw,
   371    mount options=(rbind, rw) "/tmp/.snap/usr/*/" -> "/usr/*/",
   372    "/tmp/.snap/usr/*" rw,
   373    "/usr/*" rw,
   374    mount options=(bind, rw) "/tmp/.snap/usr/*" -> "/usr/*",
   375    mount options=(rprivate) -> "/tmp/.snap/usr/",
   376    umount "/tmp/.snap/usr/",
   377    mount options=(rprivate) -> "/usr/",
   378    mount options=(rprivate) -> "/usr/*",
   379    mount options=(rprivate) -> "/usr/*/",
   380    umount "/usr/",
   381    umount "/usr/*",
   382    umount "/usr/*/",
   383    # Writable mimic /snap/vanguard/42/usr
   384    # .. variant with mimic at /snap/vanguard/42/usr/
   385    "/snap/vanguard/42/usr/" r,
   386    "/tmp/.snap/snap/vanguard/42/usr/" rw,
   387    mount options=(rbind, rw) "/snap/vanguard/42/usr/" -> "/tmp/.snap/snap/vanguard/42/usr/",
   388    mount fstype=tmpfs options=(rw) tmpfs -> "/snap/vanguard/42/usr/",
   389    "/tmp/.snap/snap/vanguard/42/usr/*/" rw,
   390    "/snap/vanguard/42/usr/*/" rw,
   391    mount options=(rbind, rw) "/tmp/.snap/snap/vanguard/42/usr/*/" -> "/snap/vanguard/42/usr/*/",
   392    "/tmp/.snap/snap/vanguard/42/usr/*" rw,
   393    "/snap/vanguard/42/usr/*" rw,
   394    mount options=(bind, rw) "/tmp/.snap/snap/vanguard/42/usr/*" -> "/snap/vanguard/42/usr/*",
   395    mount options=(rprivate) -> "/tmp/.snap/snap/vanguard/42/usr/",
   396    umount "/tmp/.snap/snap/vanguard/42/usr/",
   397    mount options=(rprivate) -> "/snap/vanguard/42/usr/",
   398    mount options=(rprivate) -> "/snap/vanguard/42/usr/*",
   399    mount options=(rprivate) -> "/snap/vanguard/42/usr/*/",
   400    umount "/snap/vanguard/42/usr/",
   401    umount "/snap/vanguard/42/usr/*",
   402    umount "/snap/vanguard/42/usr/*/",
   403  `
   404  	// Find the slice that describes profile1 by looking for the first unique
   405  	// line of the next profile.
   406  	start = end
   407  	end, _ = s.spec.UpdateNSIndexOf("  # Layout /var/cache/mylink: symlink $SNAP_DATA/link/target\n")
   408  	c.Assert(strings.Join(updateNS[start:end], ""), Equals, profile1)
   409  
   410  	profile2 := `  # Layout /var/cache/mylink: symlink $SNAP_DATA/link/target
   411    "/var/cache/mylink" rw,
   412    # Writable mimic /var/cache
   413    # .. variant with mimic at /var/
   414    "/var/" r,
   415    "/tmp/.snap/var/" rw,
   416    mount options=(rbind, rw) "/var/" -> "/tmp/.snap/var/",
   417    mount fstype=tmpfs options=(rw) tmpfs -> "/var/",
   418    "/tmp/.snap/var/*/" rw,
   419    "/var/*/" rw,
   420    mount options=(rbind, rw) "/tmp/.snap/var/*/" -> "/var/*/",
   421    "/tmp/.snap/var/*" rw,
   422    "/var/*" rw,
   423    mount options=(bind, rw) "/tmp/.snap/var/*" -> "/var/*",
   424    mount options=(rprivate) -> "/tmp/.snap/var/",
   425    umount "/tmp/.snap/var/",
   426    mount options=(rprivate) -> "/var/",
   427    mount options=(rprivate) -> "/var/*",
   428    mount options=(rprivate) -> "/var/*/",
   429    umount "/var/",
   430    umount "/var/*",
   431    umount "/var/*/",
   432    # .. variant with mimic at /var/cache/
   433    "/var/cache/" r,
   434    "/tmp/.snap/var/cache/" rw,
   435    mount options=(rbind, rw) "/var/cache/" -> "/tmp/.snap/var/cache/",
   436    mount fstype=tmpfs options=(rw) tmpfs -> "/var/cache/",
   437    "/tmp/.snap/var/cache/*/" rw,
   438    "/var/cache/*/" rw,
   439    mount options=(rbind, rw) "/tmp/.snap/var/cache/*/" -> "/var/cache/*/",
   440    "/tmp/.snap/var/cache/*" rw,
   441    "/var/cache/*" rw,
   442    mount options=(bind, rw) "/tmp/.snap/var/cache/*" -> "/var/cache/*",
   443    mount options=(rprivate) -> "/tmp/.snap/var/cache/",
   444    umount "/tmp/.snap/var/cache/",
   445    mount options=(rprivate) -> "/var/cache/",
   446    mount options=(rprivate) -> "/var/cache/*",
   447    mount options=(rprivate) -> "/var/cache/*/",
   448    umount "/var/cache/",
   449    umount "/var/cache/*",
   450    umount "/var/cache/*/",
   451  `
   452  	// Find the slice that describes profile2 by looking for the first unique
   453  	// line of the next profile.
   454  	start = end
   455  	end, _ = s.spec.UpdateNSIndexOf("  # Layout /var/tmp: type tmpfs, mode: 01777\n")
   456  	c.Assert(strings.Join(updateNS[start:end], ""), Equals, profile2)
   457  
   458  	profile3 := `  # Layout /var/tmp: type tmpfs, mode: 01777
   459    mount fstype=tmpfs tmpfs -> "/var/tmp/",
   460    mount options=(rprivate) -> "/var/tmp/",
   461    umount "/var/tmp/",
   462    # Writable mimic /var
   463  `
   464  	// Find the slice that describes profile2 by looking till the end of the list.
   465  	start = end
   466  	c.Assert(strings.Join(updateNS[start:], ""), Equals, profile3)
   467  	c.Assert(strings.Join(updateNS, ""), DeepEquals, strings.Join([]string{profile0, profile1, profile2, profile3}, ""))
   468  }
   469  
   470  const snapTrivial = `
   471  name: some-snap
   472  version: 0
   473  apps:
   474    app:
   475      command: app-command
   476  `
   477  
   478  func (s *specSuite) TestApparmorOvernameSnippetsNotInstanceKeyed(c *C) {
   479  	snapInfo := snaptest.MockInfo(c, snapTrivial, &snap.SideInfo{Revision: snap.R(42)})
   480  	restore := apparmor.SetSpecScope(s.spec, []string{"snap.some-snap.app"})
   481  	defer restore()
   482  
   483  	s.spec.AddOvername(snapInfo)
   484  	c.Assert(s.spec.Snippets(), HasLen, 0)
   485  	// non instance-keyed snaps require no extra snippets
   486  	c.Assert(s.spec.UpdateNS(), HasLen, 0)
   487  }
   488  
   489  func (s *specSuite) TestApparmorOvernameSnippets(c *C) {
   490  	snapInfo := snaptest.MockInfo(c, snapTrivial, &snap.SideInfo{Revision: snap.R(42)})
   491  	snapInfo.InstanceKey = "instance"
   492  
   493  	restore := apparmor.SetSpecScope(s.spec, []string{"snap.some-snap_instace.app"})
   494  	defer restore()
   495  
   496  	s.spec.AddOvername(snapInfo)
   497  	c.Assert(s.spec.Snippets(), HasLen, 0)
   498  
   499  	updateNS := s.spec.UpdateNS()
   500  	c.Assert(updateNS, HasLen, 1)
   501  
   502  	profile := `  # Allow parallel instance snap mount namespace adjustments
   503    mount options=(rw rbind) /snap/some-snap_instance/ -> /snap/some-snap/,
   504    mount options=(rw rbind) /var/snap/some-snap_instance/ -> /var/snap/some-snap/,
   505  `
   506  	c.Assert(updateNS[0], Equals, profile)
   507  }
   508  
   509  func (s *specSuite) TestApparmorExtraLayouts(c *C) {
   510  	snapInfo := snaptest.MockInfo(c, snapTrivial, &snap.SideInfo{Revision: snap.R(42)})
   511  	snapInfo.InstanceKey = "instance"
   512  
   513  	restore := apparmor.SetSpecScope(s.spec, []string{"snap.some-snap_instace.app"})
   514  	defer restore()
   515  
   516  	extraLayouts := []snap.Layout{
   517  		{
   518  			Path: "/test",
   519  			Bind: "/usr/home/test",
   520  			Mode: 0755,
   521  		},
   522  	}
   523  
   524  	s.spec.AddExtraLayouts(snapInfo, extraLayouts)
   525  
   526  	updateNS := s.spec.UpdateNS()
   527  
   528  	// verify that updateNS does indeed add all the additional layout
   529  	// lines. This just so happens to be 10 in this case because of reverse
   530  	// traversal for the path /usr/home/test
   531  	c.Assert(updateNS, HasLen, 10)
   532  
   533  	// make sure the extra layout is added
   534  	c.Assert(updateNS[0], Equals, "  # Layout /test: bind /usr/home/test\n")
   535  	c.Assert(updateNS[1], Equals, "  mount options=(rbind, rw) \"/usr/home/test/\" -> \"/test/\",\n")
   536  	c.Assert(updateNS[2], Equals, "  mount options=(rprivate) -> \"/test/\",\n")
   537  	// lines 3..9 is the traversal of the prefix for /usr/home/test
   538  }
   539  
   540  func (s *specSuite) TestUsesPtraceTrace(c *C) {
   541  	c.Assert(s.spec.UsesPtraceTrace(), Equals, false)
   542  	s.spec.SetUsesPtraceTrace()
   543  	c.Assert(s.spec.UsesPtraceTrace(), Equals, true)
   544  }
   545  
   546  func (s *specSuite) TestSuppressPtraceTrace(c *C) {
   547  	c.Assert(s.spec.SuppressPtraceTrace(), Equals, false)
   548  	s.spec.SetSuppressPtraceTrace()
   549  	c.Assert(s.spec.SuppressPtraceTrace(), Equals, true)
   550  }
   551  
   552  func (s *specSuite) TestSetSuppressHomeIx(c *C) {
   553  	c.Assert(s.spec.SuppressHomeIx(), Equals, false)
   554  	s.spec.SetSuppressHomeIx()
   555  	c.Assert(s.spec.SuppressHomeIx(), Equals, true)
   556  }