Prevent invalid paths being added to mock file system

Bug: 182885307
Test: m nothing
Change-Id: Ie9f60fd02e2a2bc44811dbcadf0eada4e52c9749
diff --git a/android/fixture.go b/android/fixture.go
index 4e445c0..a372534 100644
--- a/android/fixture.go
+++ b/android/fixture.go
@@ -16,6 +16,7 @@
 
 import (
 	"fmt"
+	"strings"
 	"testing"
 )
 
@@ -241,6 +242,7 @@
 // Fails if the supplied map files with the same paths are present in both of them.
 func (fs MockFS) Merge(extra map[string][]byte) {
 	for p, c := range extra {
+		validateFixtureMockFSPath(p)
 		if _, ok := fs[p]; ok {
 			panic(fmt.Errorf("attempted to add file %s to the mock filesystem but it already exists", p))
 		}
@@ -248,6 +250,27 @@
 	}
 }
 
+// Ensure that tests cannot add paths into the mock file system which would not be allowed in the
+// runtime, e.g. absolute paths, paths relative to the 'out/' directory.
+func validateFixtureMockFSPath(path string) {
+	// This uses validateSafePath rather than validatePath because the latter prevents adding files
+	// that include a $ but there are tests that allow files with a $ to be used, albeit only by
+	// globbing.
+	validatedPath, err := validateSafePath(path)
+	if err != nil {
+		panic(err)
+	}
+
+	// Make sure that the path is canonical.
+	if validatedPath != path {
+		panic(fmt.Errorf("path %q is not a canonical path, use %q instead", path, validatedPath))
+	}
+
+	if path == "out" || strings.HasPrefix(path, "out/") {
+		panic(fmt.Errorf("cannot add output path %q to the mock file system", path))
+	}
+}
+
 func (fs MockFS) AddToFixture() FixturePreparer {
 	return FixtureMergeMockFs(fs)
 }
@@ -281,6 +304,11 @@
 func FixtureModifyMockFS(mutator func(fs MockFS)) FixturePreparer {
 	return newSimpleFixturePreparer(func(f *fixture) {
 		mutator(f.mockFS)
+
+		// Make sure that invalid paths were not added to the mock filesystem.
+		for p, _ := range f.mockFS {
+			validateFixtureMockFSPath(p)
+		}
 	})
 }
 
@@ -298,6 +326,7 @@
 // Fail if the filesystem already contains a file with that path, use FixtureOverrideFile instead.
 func FixtureAddFile(path string, contents []byte) FixturePreparer {
 	return FixtureModifyMockFS(func(fs MockFS) {
+		validateFixtureMockFSPath(path)
 		if _, ok := fs[path]; ok {
 			panic(fmt.Errorf("attempted to add file %s to the mock filesystem but it already exists, use FixtureOverride*File instead", path))
 		}
diff --git a/android/fixture_test.go b/android/fixture_test.go
index 0042e5b..209bee6 100644
--- a/android/fixture_test.go
+++ b/android/fixture_test.go
@@ -14,7 +14,9 @@
 
 package android
 
-import "testing"
+import (
+	"testing"
+)
 
 // Make sure that FixturePreparer instances are only called once per fixture and in the order in
 // which they were added.
@@ -47,3 +49,38 @@
 	AssertDeepEquals(t, "preparers called in wrong order",
 		[]string{"preparer1", "preparer2", "preparer4", "preparer3"}, list)
 }
+
+func TestFixtureValidateMockFS(t *testing.T) {
+	buildDir := "<unused>"
+	factory := NewFixtureFactory(&buildDir)
+
+	t.Run("absolute path", func(t *testing.T) {
+		AssertPanicMessageContains(t, "source path validation failed", "Path is outside directory: /abs/path/Android.bp", func() {
+			factory.Fixture(t, FixtureAddFile("/abs/path/Android.bp", nil))
+		})
+	})
+	t.Run("not canonical", func(t *testing.T) {
+		AssertPanicMessageContains(t, "source path validation failed", `path "path/with/../in/it/Android.bp" is not a canonical path, use "path/in/it/Android.bp" instead`, func() {
+			factory.Fixture(t, FixtureAddFile("path/with/../in/it/Android.bp", nil))
+		})
+	})
+	t.Run("FixtureAddFile", func(t *testing.T) {
+		AssertPanicMessageContains(t, "source path validation failed", `cannot add output path "out/Android.bp" to the mock file system`, func() {
+			factory.Fixture(t, FixtureAddFile("out/Android.bp", nil))
+		})
+	})
+	t.Run("FixtureMergeMockFs", func(t *testing.T) {
+		AssertPanicMessageContains(t, "source path validation failed", `cannot add output path "out/Android.bp" to the mock file system`, func() {
+			factory.Fixture(t, FixtureMergeMockFs(MockFS{
+				"out/Android.bp": nil,
+			}))
+		})
+	})
+	t.Run("FixtureModifyMockFS", func(t *testing.T) {
+		AssertPanicMessageContains(t, "source path validation failed", `cannot add output path "out/Android.bp" to the mock file system`, func() {
+			factory.Fixture(t, FixtureModifyMockFS(func(fs MockFS) {
+				fs["out/Android.bp"] = nil
+			}))
+		})
+	})
+}