[content shell] hook up testRunner.dumpEditingCallbacks
[chromium-blink-merge.git] / content / common / sandbox_mac_diraccess_unittest.mm
bloba0b1df8101e86172da60cbf1ca42c0480d3c2d68
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.
5 #import <Cocoa/Cocoa.h>
6 #include <dirent.h>
8 extern "C" {
9 #include <sandbox.h>
12 #include "base/file_util.h"
13 #include "base/file_path.h"
14 #include "base/test/multiprocess_test.h"
15 #include "base/sys_string_conversions.h"
16 #include "base/utf_string_conversions.h"
17 #include "content/common/sandbox_mac.h"
18 #include "testing/gtest/include/gtest/gtest.h"
19 #include "testing/multiprocess_func_list.h"
21 namespace {
23 static const char* kSandboxAccessPathKey = "sandbox_dir";
24 static const char* kDeniedSuffix = "_denied";
26 }  // namespace
28 // Tests need to be in the same namespace as the Sandbox class to be useable
29 // with FRIEND_TEST() declaration.
30 namespace content {
32 class MacDirAccessSandboxTest : public base::MultiProcessTest {
33  public:
34   bool CheckSandbox(const std::string& directory_to_try) {
35     setenv(kSandboxAccessPathKey, directory_to_try.c_str(), 1);
36     base::ProcessHandle child_process = SpawnChild("mac_sandbox_path_access",
37                                                    false);
38     if (child_process == base::kNullProcessHandle) {
39       LOG(WARNING) << "SpawnChild failed";
40       return false;
41     }
42     int code = -1;
43     if (!base::WaitForExitCode(child_process, &code)) {
44       LOG(WARNING) << "base::WaitForExitCode failed";
45       return false;
46     }
47     return code == 0;
48   }
51 TEST_F(MacDirAccessSandboxTest, StringEscape) {
52   const struct string_escape_test_data {
53   const char* to_escape;
54   const char* escaped;
55   } string_escape_cases[] = {
56     {"", ""},
57     {"\b\f\n\r\t\\\"", "\\b\\f\\n\\r\\t\\\\\\\""},
58     {"/'", "/'"},
59     {"sandwich", "sandwich"},
60     {"(sandwich)", "(sandwich)"},
61     {"^\u2135.\u2136$", "^\\u2135.\\u2136$"},
62   };
64   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(string_escape_cases); ++i) {
65     std::string out;
66     std::string in(string_escape_cases[i].to_escape);
67     EXPECT_TRUE(Sandbox::QuotePlainString(in, &out));
68     EXPECT_EQ(string_escape_cases[i].escaped, out);
69   }
72 TEST_F(MacDirAccessSandboxTest, RegexEscape) {
73   const std::string kSandboxEscapeSuffix("(/|$)");
74   const struct regex_test_data {
75     const wchar_t *to_escape;
76     const char* escaped;
77   } regex_cases[] = {
78     {L"", ""},
79     {L"/'", "/'"},  // / & ' characters don't need escaping.
80     {L"sandwich", "sandwich"},
81     {L"(sandwich)", "\\(sandwich\\)"},
82   };
84   // Check that all characters whose values are smaller than 32 [1F] are
85   // rejected by the regex escaping code.
86   {
87     std::string out;
88     char fail_string[] = {31, 0};
89     char ok_string[] = {32, 0};
90     EXPECT_FALSE(Sandbox::QuoteStringForRegex(fail_string, &out));
91     EXPECT_TRUE(Sandbox::QuoteStringForRegex(ok_string, &out));
92   }
94   // Check that all characters whose values are larger than 126 [7E] are
95   // rejected by the regex escaping code.
96   {
97     std::string out;
98     EXPECT_TRUE(Sandbox::QuoteStringForRegex("}", &out));   // } == 0x7D == 125
99     EXPECT_FALSE(Sandbox::QuoteStringForRegex("~", &out));  // ~ == 0x7E == 126
100     EXPECT_FALSE(
101         Sandbox::QuoteStringForRegex(WideToUTF8(L"^\u2135.\u2136$"), &out));
102   }
104   {
105     for (size_t i = 0; i < ARRAYSIZE_UNSAFE(regex_cases); ++i) {
106       std::string out;
107       std::string in = WideToUTF8(regex_cases[i].to_escape);
108       EXPECT_TRUE(Sandbox::QuoteStringForRegex(in, &out));
109       std::string expected("^");
110       expected.append(regex_cases[i].escaped);
111       expected.append(kSandboxEscapeSuffix);
112       EXPECT_EQ(expected, out);
113     }
114   }
116   {
117     std::string in_utf8("\\^.$|()[]*+?{}");
118     std::string expected;
119     expected.push_back('^');
120     for (size_t i = 0; i < in_utf8.length(); ++i) {
121       expected.push_back('\\');
122       expected.push_back(in_utf8[i]);
123     }
124     expected.append(kSandboxEscapeSuffix);
126     std::string out;
127     EXPECT_TRUE(Sandbox::QuoteStringForRegex(in_utf8, &out));
128     EXPECT_EQ(expected, out);
130   }
133 // A class to handle auto-deleting a directory.
134 class ScopedDirectoryDelete {
135  public:
136   inline void operator()(FilePath* x) const {
137     if (x) {
138       file_util::Delete(*x, true);
139     }
140   }
143 typedef scoped_ptr_malloc<FilePath, ScopedDirectoryDelete> ScopedDirectory;
145 TEST_F(MacDirAccessSandboxTest, SandboxAccess) {
146   using file_util::CreateDirectory;
148   FilePath tmp_dir;
149   ASSERT_TRUE(file_util::CreateNewTempDirectory("", &tmp_dir));
150   // This step is important on OS X since the sandbox only understands "real"
151   // paths and the paths CreateNewTempDirectory() returns are empirically in
152   // /var which is a symlink to /private/var .
153   tmp_dir = Sandbox::GetCanonicalSandboxPath(tmp_dir);
154   ScopedDirectory cleanup(&tmp_dir);
156   const char* sandbox_dir_cases[] = {
157     "simple_dir_name",
158     "^hello++ $",       // Regex.
159     "\\^.$|()[]*+?{}",  // All regex characters.
160   };
162   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(sandbox_dir_cases); ++i) {
163     const char* sandbox_dir_name = sandbox_dir_cases[i];
164     FilePath sandbox_dir = tmp_dir.Append(sandbox_dir_name);
165     ASSERT_TRUE(CreateDirectory(sandbox_dir));
166     ScopedDirectory cleanup_sandbox(&sandbox_dir);
168     // Create a sibling directory of the sandbox dir, whose name has sandbox dir
169     // as a substring but to which access is denied.
170     std::string sibling_sandbox_dir_name_denied =
171         std::string(sandbox_dir_cases[i]) + kDeniedSuffix;
172     FilePath sibling_sandbox_dir = tmp_dir.Append(
173                                       sibling_sandbox_dir_name_denied.c_str());
174     ASSERT_TRUE(CreateDirectory(sibling_sandbox_dir));
175     ScopedDirectory cleanup_sandbox_sibling(&sibling_sandbox_dir);
177     EXPECT_TRUE(CheckSandbox(sandbox_dir.value()));
178   }
181 MULTIPROCESS_TEST_MAIN(mac_sandbox_path_access) {
182   char *sandbox_allowed_dir = getenv(kSandboxAccessPathKey);
183   if (!sandbox_allowed_dir)
184     return -1;
186   // Build up a sandbox profile that only allows access to a single directory.
187   NSString *sandbox_profile =
188       @"(version 1)" \
189       "(deny default)" \
190       "(allow signal (target self))" \
191       "(allow sysctl-read)" \
192       ";ENABLE_DIRECTORY_ACCESS";
194   std::string allowed_dir(sandbox_allowed_dir);
195   Sandbox::SandboxVariableSubstitions substitutions;
196   NSString* allow_dir_sandbox_code =
197       Sandbox::BuildAllowDirectoryAccessSandboxString(
198           FilePath(sandbox_allowed_dir),
199           &substitutions);
200   sandbox_profile = [sandbox_profile
201       stringByReplacingOccurrencesOfString:@";ENABLE_DIRECTORY_ACCESS"
202                                 withString:allow_dir_sandbox_code];
204   std::string final_sandbox_profile_str;
205   if (!Sandbox::PostProcessSandboxProfile(sandbox_profile,
206                                           [NSArray array],
207                                           substitutions,
208                                           &final_sandbox_profile_str)) {
209     LOG(ERROR) << "Call to PostProcessSandboxProfile() failed";
210     return -1;
211   }
213   // Enable Sandbox.
214   char* error_buff = NULL;
215   int error = sandbox_init(final_sandbox_profile_str.c_str(), 0, &error_buff);
216   if (error == -1) {
217     LOG(ERROR) << "Failed to Initialize Sandbox: " << error_buff;
218     return -1;
219   }
220   sandbox_free_error(error_buff);
222   // Test Sandbox.
224   // We should be able to list the contents of the sandboxed directory.
225   DIR *file_list = NULL;
226   file_list = opendir(sandbox_allowed_dir);
227   if (!file_list) {
228     PLOG(ERROR) << "Sandbox overly restrictive: call to opendir("
229                 << sandbox_allowed_dir
230                 << ") failed";
231     return -1;
232   }
233   closedir(file_list);
235   // Test restrictions on accessing files.
236   FilePath allowed_dir_path(sandbox_allowed_dir);
237   FilePath allowed_file = allowed_dir_path.Append("ok_to_write");
238   FilePath denied_file1 =  allowed_dir_path.DirName().Append("cant_access");
240   // Try to write a file who's name has the same prefix as the directory we
241   // allow access to.
242   FilePath basename = allowed_dir_path.BaseName();
243   FilePath allowed_parent_dir = allowed_dir_path.DirName();
244   std::string tricky_filename = basename.value() + "123";
245   FilePath denied_file2 =  allowed_parent_dir.Append(tricky_filename);
247   if (open(allowed_file.value().c_str(), O_WRONLY | O_CREAT) <= 0) {
248     PLOG(ERROR) << "Sandbox overly restrictive: failed to write ("
249                 << allowed_file.value()
250                 << ")";
251     return -1;
252   }
254   // Test that we deny access to a sibling of the sandboxed directory whose
255   // name has the sandboxed directory name as a substring. e.g. if the sandbox
256   // directory is /foo/baz then test /foo/baz_denied.
257   {
258     struct stat tmp_stat_info;
259     std::string denied_sibling =
260         std::string(sandbox_allowed_dir) + kDeniedSuffix;
261     if (stat(denied_sibling.c_str(), &tmp_stat_info) > 0) {
262       PLOG(ERROR) << "Sandbox breach: was able to stat ("
263                   << denied_sibling.c_str()
264                   << ")";
265       return -1;
266     }
267   }
269   // Test that we can stat parent directories of the "allowed" directory.
270   {
271     struct stat tmp_stat_info;
272     if (stat(allowed_parent_dir.value().c_str(), &tmp_stat_info) != 0) {
273       PLOG(ERROR) << "Sandbox overly restrictive: unable to stat ("
274                   << allowed_parent_dir.value()
275                   << ")";
276       return -1;
277     }
278   }
280   // Test that we can't stat files outside the "allowed" directory.
281   {
282     struct stat tmp_stat_info;
283     if (stat(denied_file1.value().c_str(), &tmp_stat_info) > 0) {
284       PLOG(ERROR) << "Sandbox breach: was able to stat ("
285                   << denied_file1.value()
286                   << ")";
287       return -1;
288     }
289   }
291   if (open(denied_file1.value().c_str(), O_WRONLY | O_CREAT) > 0) {
292     PLOG(ERROR) << "Sandbox breach: was able to write ("
293                 << denied_file1.value()
294                 << ")";
295     return -1;
296   }
298   if (open(denied_file2.value().c_str(), O_WRONLY | O_CREAT) > 0) {
299     PLOG(ERROR) << "Sandbox breach: was able to write ("
300                 << denied_file2.value()
301                 << ")";
302     return -1;
303   }
305   return 0;
308 }  // namespace content