1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include <stddef.h>
6 
7 #include <algorithm>
8 
9 #include "base/files/file_util.h"
10 #include "base/files/scoped_temp_dir.h"
11 #include "base/path_service.h"
12 #include "build/build_config.h"
13 #include "components/crx_file/id_util.h"
14 #include "extensions/common/constants.h"
15 #include "extensions/common/extension_paths.h"
16 #include "extensions/common/extension_resource.h"
17 #include "testing/gtest/include/gtest/gtest.h"
18 #include "ui/base/l10n/l10n_util.h"
19 
20 namespace extensions {
21 
TEST(ExtensionResourceTest,CreateEmptyResource)22 TEST(ExtensionResourceTest, CreateEmptyResource) {
23   ExtensionResource resource;
24 
25   EXPECT_TRUE(resource.extension_root().empty());
26   EXPECT_TRUE(resource.relative_path().empty());
27   EXPECT_TRUE(resource.GetFilePath().empty());
28 }
29 
ToLower(const base::FilePath::StringType & in_str)30 const base::FilePath::StringType ToLower(
31     const base::FilePath::StringType& in_str) {
32   base::FilePath::StringType str(in_str);
33   std::transform(str.begin(), str.end(), str.begin(), tolower);
34   return str;
35 }
36 
TEST(ExtensionResourceTest,CreateWithMissingResourceOnDisk)37 TEST(ExtensionResourceTest, CreateWithMissingResourceOnDisk) {
38   base::FilePath root_path;
39   ASSERT_TRUE(base::PathService::Get(DIR_TEST_DATA, &root_path));
40   base::FilePath relative_path;
41   relative_path = relative_path.AppendASCII("cira.js");
42   std::string extension_id = crx_file::id_util::GenerateId("test");
43   ExtensionResource resource(extension_id, root_path, relative_path);
44 
45   // The path doesn't exist on disk, we will be returned an empty path.
46   EXPECT_EQ(root_path.value(), resource.extension_root().value());
47   EXPECT_EQ(relative_path.value(), resource.relative_path().value());
48   EXPECT_TRUE(resource.GetFilePath().empty());
49 }
50 
TEST(ExtensionResourceTest,ResourcesOutsideOfPath)51 TEST(ExtensionResourceTest, ResourcesOutsideOfPath) {
52   base::ScopedTempDir temp;
53   ASSERT_TRUE(temp.CreateUniqueTempDir());
54 
55   base::FilePath inner_dir = temp.GetPath().AppendASCII("directory");
56   ASSERT_TRUE(base::CreateDirectory(inner_dir));
57   base::FilePath sub_dir = inner_dir.AppendASCII("subdir");
58   ASSERT_TRUE(base::CreateDirectory(sub_dir));
59   base::FilePath inner_file = inner_dir.AppendASCII("inner");
60   base::FilePath outer_file = temp.GetPath().AppendASCII("outer");
61   ASSERT_EQ(1, base::WriteFile(outer_file, "X", 1));
62   ASSERT_EQ(1, base::WriteFile(inner_file, "X", 1));
63   std::string extension_id = crx_file::id_util::GenerateId("test");
64 
65 #if defined(OS_POSIX)
66   base::FilePath symlink_file = inner_dir.AppendASCII("symlink");
67   base::CreateSymbolicLink(
68       base::FilePath().AppendASCII("..").AppendASCII("outer"),
69       symlink_file);
70 #endif
71 
72   // A non-packing extension should be able to access the file within the
73   // directory.
74   ExtensionResource r1(extension_id, inner_dir,
75                        base::FilePath().AppendASCII("inner"));
76   EXPECT_FALSE(r1.GetFilePath().empty());
77 
78   // ... but not a relative path that walks out of |inner_dir|.
79   ExtensionResource r2(extension_id, inner_dir,
80                        base::FilePath().AppendASCII("..").AppendASCII("outer"));
81   EXPECT_TRUE(r2.GetFilePath().empty());
82 
83   // A packing extension should also be able to access the file within the
84   // directory.
85   ExtensionResource r3(extension_id, inner_dir,
86                        base::FilePath().AppendASCII("inner"));
87   r3.set_follow_symlinks_anywhere();
88   EXPECT_FALSE(r3.GetFilePath().empty());
89 
90   // ... but, again, not a relative path that walks out of |inner_dir|.
91   ExtensionResource r4(extension_id, inner_dir,
92                        base::FilePath().AppendASCII("..").AppendASCII("outer"));
93   r4.set_follow_symlinks_anywhere();
94   EXPECT_TRUE(r4.GetFilePath().empty());
95 
96   // ... and not even when clever current-directory syntax is present. Note
97   // that the path for this test case can't start with the current directory
98   // component due to quirks in FilePath::Append(), and the path must exist.
99   ExtensionResource r4a(
100       extension_id, inner_dir,
101       base::FilePath().AppendASCII("subdir").AppendASCII(".").AppendASCII("..").
102       AppendASCII("..").AppendASCII("outer"));
103   r4a.set_follow_symlinks_anywhere();
104   EXPECT_TRUE(r4a.GetFilePath().empty());
105 
106 #if defined(OS_POSIX)
107   // The non-packing extension should also not be able to access a resource that
108   // symlinks out of the directory.
109   ExtensionResource r5(extension_id, inner_dir,
110                        base::FilePath().AppendASCII("symlink"));
111   EXPECT_TRUE(r5.GetFilePath().empty());
112 
113   // ... but a packing extension can.
114   ExtensionResource r6(extension_id, inner_dir,
115                        base::FilePath().AppendASCII("symlink"));
116   r6.set_follow_symlinks_anywhere();
117   EXPECT_FALSE(r6.GetFilePath().empty());
118 #endif
119 }
120 
TEST(ExtensionResourceTest,CreateWithAllResourcesOnDisk)121 TEST(ExtensionResourceTest, CreateWithAllResourcesOnDisk) {
122   base::ScopedTempDir temp;
123   ASSERT_TRUE(temp.CreateUniqueTempDir());
124 
125   // Create resource in the extension root.
126   const char* filename = "res.ico";
127   base::FilePath root_resource = temp.GetPath().AppendASCII(filename);
128   std::string data = "some foo";
129   ASSERT_EQ(static_cast<int>(data.length()),
130             base::WriteFile(root_resource, data.c_str(), data.length()));
131 
132   // Create l10n resources (for current locale and its parents).
133   base::FilePath l10n_path = temp.GetPath().Append(kLocaleFolder);
134   ASSERT_TRUE(base::CreateDirectory(l10n_path));
135 
136   std::vector<std::string> locales;
137   l10n_util::GetParentLocales(l10n_util::GetApplicationLocale(std::string()),
138                               &locales);
139   ASSERT_FALSE(locales.empty());
140   for (size_t i = 0; i < locales.size(); i++) {
141     base::FilePath make_path;
142     make_path = l10n_path.AppendASCII(locales[i]);
143     ASSERT_TRUE(base::CreateDirectory(make_path));
144     ASSERT_EQ(static_cast<int>(data.length()),
145               base::WriteFile(make_path.AppendASCII(filename), data.c_str(),
146                               data.length()));
147   }
148 
149   base::FilePath path;
150   std::string extension_id = crx_file::id_util::GenerateId("test");
151   ExtensionResource resource(extension_id, temp.GetPath(),
152                              base::FilePath().AppendASCII(filename));
153   const base::FilePath& resolved_path = resource.GetFilePath();
154 
155   base::FilePath expected_path;
156   // Expect default path only, since fallback logic is disabled.
157   // See http://crbug.com/27359.
158   expected_path = base::MakeAbsoluteFilePath(root_resource);
159   ASSERT_FALSE(expected_path.empty());
160 
161   EXPECT_EQ(ToLower(expected_path.value()), ToLower(resolved_path.value()));
162   EXPECT_EQ(ToLower(temp.GetPath().value()),
163             ToLower(resource.extension_root().value()));
164   EXPECT_EQ(ToLower(base::FilePath().AppendASCII(filename).value()),
165             ToLower(resource.relative_path().value()));
166 }
167 
168 }  // namespace extensions
169