| OLD | NEW |
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include <algorithm> | 5 #include <algorithm> |
| 6 #include <cctype> | 6 #include <cctype> |
| 7 | 7 |
| 8 #include <windows.h> | 8 #include <windows.h> |
| 9 #include <winioctl.h> | 9 #include <winioctl.h> |
| 10 | 10 |
| (...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 55 return SBOX_TEST_SUCCEEDED; | 55 return SBOX_TEST_SUCCEEDED; |
| 56 return SBOX_TEST_DENIED; | 56 return SBOX_TEST_DENIED; |
| 57 } | 57 } |
| 58 } | 58 } |
| 59 | 59 |
| 60 SBOX_TESTS_COMMAND int File_Win32Create(int argc, wchar_t **argv) { | 60 SBOX_TESTS_COMMAND int File_Win32Create(int argc, wchar_t **argv) { |
| 61 if (argc != 1) { | 61 if (argc != 1) { |
| 62 SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; | 62 SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; |
| 63 } | 63 } |
| 64 | 64 |
| 65 std::wstring full_path = MakePathToSys(argv[0], false); | 65 base::string16 full_path = MakePathToSys(argv[0], false); |
| 66 if (full_path.empty()) { | 66 if (full_path.empty()) { |
| 67 return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; | 67 return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; |
| 68 } | 68 } |
| 69 | 69 |
| 70 HANDLE file = ::CreateFileW(full_path.c_str(), GENERIC_READ, kSharing, | 70 HANDLE file = ::CreateFileW(full_path.c_str(), GENERIC_READ, kSharing, |
| 71 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); | 71 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); |
| 72 | 72 |
| 73 if (INVALID_HANDLE_VALUE != file) { | 73 if (INVALID_HANDLE_VALUE != file) { |
| 74 ::CloseHandle(file); | 74 ::CloseHandle(file); |
| 75 return SBOX_TEST_SUCCEEDED; | 75 return SBOX_TEST_SUCCEEDED; |
| (...skipping 11 matching lines...) Expand all Loading... |
| 87 // call succeeded or not. | 87 // call succeeded or not. |
| 88 SBOX_TESTS_COMMAND int File_CreateSys32(int argc, wchar_t **argv) { | 88 SBOX_TESTS_COMMAND int File_CreateSys32(int argc, wchar_t **argv) { |
| 89 BINDNTDLL(NtCreateFile); | 89 BINDNTDLL(NtCreateFile); |
| 90 BINDNTDLL(RtlInitUnicodeString); | 90 BINDNTDLL(RtlInitUnicodeString); |
| 91 if (!NtCreateFile || !RtlInitUnicodeString) | 91 if (!NtCreateFile || !RtlInitUnicodeString) |
| 92 return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; | 92 return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; |
| 93 | 93 |
| 94 if (argc != 1) | 94 if (argc != 1) |
| 95 return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; | 95 return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; |
| 96 | 96 |
| 97 std::wstring file(argv[0]); | 97 base::string16 file(argv[0]); |
| 98 if (0 != _wcsnicmp(file.c_str(), kNTObjManPrefix, kNTObjManPrefixLen)) | 98 if (0 != _wcsnicmp(file.c_str(), kNTObjManPrefix, kNTObjManPrefixLen)) |
| 99 file = MakePathToSys(argv[0], true); | 99 file = MakePathToSys(argv[0], true); |
| 100 | 100 |
| 101 UNICODE_STRING object_name; | 101 UNICODE_STRING object_name; |
| 102 RtlInitUnicodeString(&object_name, file.c_str()); | 102 RtlInitUnicodeString(&object_name, file.c_str()); |
| 103 | 103 |
| 104 OBJECT_ATTRIBUTES obj_attributes = {0}; | 104 OBJECT_ATTRIBUTES obj_attributes = {0}; |
| 105 InitializeObjectAttributes(&obj_attributes, &object_name, | 105 InitializeObjectAttributes(&obj_attributes, &object_name, |
| 106 OBJ_CASE_INSENSITIVE, NULL, NULL); | 106 OBJ_CASE_INSENSITIVE, NULL, NULL); |
| 107 | 107 |
| (...skipping 17 matching lines...) Expand all Loading... |
| 125 // call succeeded or not. | 125 // call succeeded or not. |
| 126 SBOX_TESTS_COMMAND int File_OpenSys32(int argc, wchar_t **argv) { | 126 SBOX_TESTS_COMMAND int File_OpenSys32(int argc, wchar_t **argv) { |
| 127 BINDNTDLL(NtOpenFile); | 127 BINDNTDLL(NtOpenFile); |
| 128 BINDNTDLL(RtlInitUnicodeString); | 128 BINDNTDLL(RtlInitUnicodeString); |
| 129 if (!NtOpenFile || !RtlInitUnicodeString) | 129 if (!NtOpenFile || !RtlInitUnicodeString) |
| 130 return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; | 130 return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; |
| 131 | 131 |
| 132 if (argc != 1) | 132 if (argc != 1) |
| 133 return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; | 133 return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; |
| 134 | 134 |
| 135 std::wstring file = MakePathToSys(argv[0], true); | 135 base::string16 file = MakePathToSys(argv[0], true); |
| 136 UNICODE_STRING object_name; | 136 UNICODE_STRING object_name; |
| 137 RtlInitUnicodeString(&object_name, file.c_str()); | 137 RtlInitUnicodeString(&object_name, file.c_str()); |
| 138 | 138 |
| 139 OBJECT_ATTRIBUTES obj_attributes = {0}; | 139 OBJECT_ATTRIBUTES obj_attributes = {0}; |
| 140 InitializeObjectAttributes(&obj_attributes, &object_name, | 140 InitializeObjectAttributes(&obj_attributes, &object_name, |
| 141 OBJ_CASE_INSENSITIVE, NULL, NULL); | 141 OBJ_CASE_INSENSITIVE, NULL, NULL); |
| 142 | 142 |
| 143 HANDLE handle; | 143 HANDLE handle; |
| 144 IO_STATUS_BLOCK io_block = {0}; | 144 IO_STATUS_BLOCK io_block = {0}; |
| 145 NTSTATUS status = NtOpenFile(&handle, FILE_READ_DATA, &obj_attributes, | 145 NTSTATUS status = NtOpenFile(&handle, FILE_READ_DATA, &obj_attributes, |
| 146 &io_block, kSharing, 0); | 146 &io_block, kSharing, 0); |
| 147 if (NT_SUCCESS(status)) { | 147 if (NT_SUCCESS(status)) { |
| 148 ::CloseHandle(handle); | 148 ::CloseHandle(handle); |
| 149 return SBOX_TEST_SUCCEEDED; | 149 return SBOX_TEST_SUCCEEDED; |
| 150 } else if (STATUS_ACCESS_DENIED == status) { | 150 } else if (STATUS_ACCESS_DENIED == status) { |
| 151 return SBOX_TEST_DENIED; | 151 return SBOX_TEST_DENIED; |
| 152 } else if (STATUS_OBJECT_NAME_NOT_FOUND == status) { | 152 } else if (STATUS_OBJECT_NAME_NOT_FOUND == status) { |
| 153 return SBOX_TEST_NOT_FOUND; | 153 return SBOX_TEST_NOT_FOUND; |
| 154 } | 154 } |
| 155 return SBOX_TEST_FAILED; | 155 return SBOX_TEST_FAILED; |
| 156 } | 156 } |
| 157 | 157 |
| 158 SBOX_TESTS_COMMAND int File_GetDiskSpace(int argc, wchar_t **argv) { | 158 SBOX_TESTS_COMMAND int File_GetDiskSpace(int argc, wchar_t **argv) { |
| 159 std::wstring sys_path = MakePathToSys(L"", false); | 159 base::string16 sys_path = MakePathToSys(L"", false); |
| 160 if (sys_path.empty()) { | 160 if (sys_path.empty()) { |
| 161 return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; | 161 return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; |
| 162 } | 162 } |
| 163 ULARGE_INTEGER free_user = {0}; | 163 ULARGE_INTEGER free_user = {0}; |
| 164 ULARGE_INTEGER total = {0}; | 164 ULARGE_INTEGER total = {0}; |
| 165 ULARGE_INTEGER free_total = {0}; | 165 ULARGE_INTEGER free_total = {0}; |
| 166 if (::GetDiskFreeSpaceExW(sys_path.c_str(), &free_user, &total, | 166 if (::GetDiskFreeSpaceExW(sys_path.c_str(), &free_user, &total, |
| 167 &free_total)) { | 167 &free_total)) { |
| 168 if ((total.QuadPart != 0) && (free_total.QuadPart !=0)) { | 168 if ((total.QuadPart != 0) && (free_total.QuadPart !=0)) { |
| 169 return SBOX_TEST_SUCCEEDED; | 169 return SBOX_TEST_SUCCEEDED; |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 205 if (!NtQueryAttributesFile || !NtQueryFullAttributesFile || | 205 if (!NtQueryAttributesFile || !NtQueryFullAttributesFile || |
| 206 !RtlInitUnicodeString) | 206 !RtlInitUnicodeString) |
| 207 return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; | 207 return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; |
| 208 | 208 |
| 209 if (argc != 2) | 209 if (argc != 2) |
| 210 return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; | 210 return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; |
| 211 | 211 |
| 212 bool expect_directory = (L'd' == argv[1][0]); | 212 bool expect_directory = (L'd' == argv[1][0]); |
| 213 | 213 |
| 214 UNICODE_STRING object_name; | 214 UNICODE_STRING object_name; |
| 215 std::wstring file = MakePathToSys(argv[0], true); | 215 base::string16 file = MakePathToSys(argv[0], true); |
| 216 RtlInitUnicodeString(&object_name, file.c_str()); | 216 RtlInitUnicodeString(&object_name, file.c_str()); |
| 217 | 217 |
| 218 OBJECT_ATTRIBUTES obj_attributes = {0}; | 218 OBJECT_ATTRIBUTES obj_attributes = {0}; |
| 219 InitializeObjectAttributes(&obj_attributes, &object_name, | 219 InitializeObjectAttributes(&obj_attributes, &object_name, |
| 220 OBJ_CASE_INSENSITIVE, NULL, NULL); | 220 OBJ_CASE_INSENSITIVE, NULL, NULL); |
| 221 | 221 |
| 222 FILE_BASIC_INFORMATION info = {0}; | 222 FILE_BASIC_INFORMATION info = {0}; |
| 223 FILE_NETWORK_OPEN_INFORMATION full_info = {0}; | 223 FILE_NETWORK_OPEN_INFORMATION full_info = {0}; |
| 224 NTSTATUS status1 = NtQueryAttributesFile(&obj_attributes, &info); | 224 NTSTATUS status1 = NtQueryAttributesFile(&obj_attributes, &info); |
| 225 NTSTATUS status2 = NtQueryFullAttributesFile(&obj_attributes, &full_info); | 225 NTSTATUS status2 = NtQueryFullAttributesFile(&obj_attributes, &full_info); |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 258 TestRunner runner; | 258 TestRunner runner; |
| 259 EXPECT_TRUE(runner.AddRuleSys32(TargetPolicy::FILES_ALLOW_ANY, L"calc.exe")); | 259 EXPECT_TRUE(runner.AddRuleSys32(TargetPolicy::FILES_ALLOW_ANY, L"calc.exe")); |
| 260 | 260 |
| 261 EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"File_CreateSys32 calc.exe")); | 261 EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"File_CreateSys32 calc.exe")); |
| 262 | 262 |
| 263 runner.SetTestState(BEFORE_REVERT); | 263 runner.SetTestState(BEFORE_REVERT); |
| 264 EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"File_CreateSys32 calc.exe")); | 264 EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"File_CreateSys32 calc.exe")); |
| 265 } | 265 } |
| 266 | 266 |
| 267 TEST(FilePolicyTest, AllowNtCreateWithNativePath) { | 267 TEST(FilePolicyTest, AllowNtCreateWithNativePath) { |
| 268 std::wstring calc = MakePathToSys(L"calc.exe", false); | 268 base::string16 calc = MakePathToSys(L"calc.exe", false); |
| 269 std::wstring nt_path; | 269 base::string16 nt_path; |
| 270 ASSERT_TRUE(GetNtPathFromWin32Path(calc, &nt_path)); | 270 ASSERT_TRUE(GetNtPathFromWin32Path(calc, &nt_path)); |
| 271 TestRunner runner; | 271 TestRunner runner; |
| 272 runner.AddFsRule(TargetPolicy::FILES_ALLOW_READONLY, nt_path.c_str()); | 272 runner.AddFsRule(TargetPolicy::FILES_ALLOW_READONLY, nt_path.c_str()); |
| 273 | 273 |
| 274 wchar_t buff[MAX_PATH]; | 274 wchar_t buff[MAX_PATH]; |
| 275 ::wsprintfW(buff, L"File_CreateSys32 %s", nt_path.c_str()); | 275 ::wsprintfW(buff, L"File_CreateSys32 %s", nt_path.c_str()); |
| 276 EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(buff)); | 276 EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(buff)); |
| 277 | 277 |
| 278 std::transform(nt_path.begin(), nt_path.end(), nt_path.begin(), std::tolower); | 278 std::transform(nt_path.begin(), nt_path.end(), nt_path.begin(), std::tolower); |
| 279 ::wsprintfW(buff, L"File_CreateSys32 %s", nt_path.c_str()); | 279 ::wsprintfW(buff, L"File_CreateSys32 %s", nt_path.c_str()); |
| (...skipping 246 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 526 wchar_t temp_directory[MAX_PATH]; | 526 wchar_t temp_directory[MAX_PATH]; |
| 527 wchar_t temp_file_name[MAX_PATH]; | 527 wchar_t temp_file_name[MAX_PATH]; |
| 528 ASSERT_NE(::GetTempPath(MAX_PATH, temp_directory), 0u); | 528 ASSERT_NE(::GetTempPath(MAX_PATH, temp_directory), 0u); |
| 529 ASSERT_NE(::GetTempFileName(temp_directory, L"test", 0, temp_file_name), 0u); | 529 ASSERT_NE(::GetTempFileName(temp_directory, L"test", 0, temp_file_name), 0u); |
| 530 | 530 |
| 531 // Delete the file and create a directory instead. | 531 // Delete the file and create a directory instead. |
| 532 ASSERT_TRUE(::DeleteFile(temp_file_name)); | 532 ASSERT_TRUE(::DeleteFile(temp_file_name)); |
| 533 ASSERT_TRUE(::CreateDirectory(temp_file_name, NULL)); | 533 ASSERT_TRUE(::CreateDirectory(temp_file_name, NULL)); |
| 534 | 534 |
| 535 // Create a temporary file in the subfolder. | 535 // Create a temporary file in the subfolder. |
| 536 std::wstring subfolder = temp_file_name; | 536 base::string16 subfolder = temp_file_name; |
| 537 std::wstring temp_file_title = subfolder.substr(subfolder.rfind(L"\\") + 1); | 537 base::string16 temp_file_title = subfolder.substr(subfolder.rfind(L"\\") + 1); |
| 538 std::wstring temp_file = subfolder + L"\\file_" + temp_file_title; | 538 base::string16 temp_file = subfolder + L"\\file_" + temp_file_title; |
| 539 | 539 |
| 540 HANDLE file = ::CreateFile(temp_file.c_str(), FILE_ALL_ACCESS, | 540 HANDLE file = ::CreateFile(temp_file.c_str(), FILE_ALL_ACCESS, |
| 541 FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, | 541 FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, |
| 542 CREATE_ALWAYS, 0, NULL); | 542 CREATE_ALWAYS, 0, NULL); |
| 543 ASSERT_TRUE(INVALID_HANDLE_VALUE != file); | 543 ASSERT_TRUE(INVALID_HANDLE_VALUE != file); |
| 544 ASSERT_TRUE(::CloseHandle(file)); | 544 ASSERT_TRUE(::CloseHandle(file)); |
| 545 | 545 |
| 546 // Create a temporary file in the temp directory. | 546 // Create a temporary file in the temp directory. |
| 547 std::wstring temp_dir = temp_directory; | 547 base::string16 temp_dir = temp_directory; |
| 548 std::wstring temp_file_in_temp = temp_dir + L"file_" + temp_file_title; | 548 base::string16 temp_file_in_temp = temp_dir + L"file_" + temp_file_title; |
| 549 file = ::CreateFile(temp_file_in_temp.c_str(), FILE_ALL_ACCESS, | 549 file = ::CreateFile(temp_file_in_temp.c_str(), FILE_ALL_ACCESS, |
| 550 FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, | 550 FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, |
| 551 CREATE_ALWAYS, 0, NULL); | 551 CREATE_ALWAYS, 0, NULL); |
| 552 ASSERT_TRUE(file != NULL); | 552 ASSERT_TRUE(file != NULL); |
| 553 ASSERT_TRUE(::CloseHandle(file)); | 553 ASSERT_TRUE(::CloseHandle(file)); |
| 554 | 554 |
| 555 // Give write access to the temp directory. | 555 // Give write access to the temp directory. |
| 556 std::wstring temp_dir_wildcard = temp_dir + L"*"; | 556 base::string16 temp_dir_wildcard = temp_dir + L"*"; |
| 557 EXPECT_TRUE(runner.AddFsRule(TargetPolicy::FILES_ALLOW_ANY, | 557 EXPECT_TRUE(runner.AddFsRule(TargetPolicy::FILES_ALLOW_ANY, |
| 558 temp_dir_wildcard.c_str())); | 558 temp_dir_wildcard.c_str())); |
| 559 | 559 |
| 560 // Prepare the command to execute. | 560 // Prepare the command to execute. |
| 561 std::wstring command_write; | 561 base::string16 command_write; |
| 562 command_write += L"File_Create Write \""; | 562 command_write += L"File_Create Write \""; |
| 563 command_write += temp_file; | 563 command_write += temp_file; |
| 564 command_write += L"\""; | 564 command_write += L"\""; |
| 565 | 565 |
| 566 // Verify that we have write access to the original file | 566 // Verify that we have write access to the original file |
| 567 EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(command_write.c_str())); | 567 EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(command_write.c_str())); |
| 568 | 568 |
| 569 // Replace the subfolder by a reparse point to %temp%. | 569 // Replace the subfolder by a reparse point to %temp%. |
| 570 ::DeleteFile(temp_file.c_str()); | 570 ::DeleteFile(temp_file.c_str()); |
| 571 HANDLE dir = ::CreateFile(subfolder.c_str(), FILE_ALL_ACCESS, | 571 HANDLE dir = ::CreateFile(subfolder.c_str(), FILE_ALL_ACCESS, |
| 572 FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, | 572 FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, |
| 573 OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); | 573 OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); |
| 574 EXPECT_TRUE(INVALID_HANDLE_VALUE != dir); | 574 EXPECT_TRUE(INVALID_HANDLE_VALUE != dir); |
| 575 | 575 |
| 576 std::wstring temp_dir_nt; | 576 base::string16 temp_dir_nt; |
| 577 temp_dir_nt += L"\\??\\"; | 577 temp_dir_nt += L"\\??\\"; |
| 578 temp_dir_nt += temp_dir; | 578 temp_dir_nt += temp_dir; |
| 579 EXPECT_TRUE(SetReparsePoint(dir, temp_dir_nt.c_str())); | 579 EXPECT_TRUE(SetReparsePoint(dir, temp_dir_nt.c_str())); |
| 580 EXPECT_TRUE(::CloseHandle(dir)); | 580 EXPECT_TRUE(::CloseHandle(dir)); |
| 581 | 581 |
| 582 // Try to open the file again. | 582 // Try to open the file again. |
| 583 EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(command_write.c_str())); | 583 EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(command_write.c_str())); |
| 584 | 584 |
| 585 // Remove the reparse point. | 585 // Remove the reparse point. |
| 586 dir = ::CreateFile(subfolder.c_str(), FILE_ALL_ACCESS, | 586 dir = ::CreateFile(subfolder.c_str(), FILE_ALL_ACCESS, |
| 587 FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, | 587 FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, |
| 588 FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, | 588 FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, |
| 589 NULL); | 589 NULL); |
| 590 EXPECT_TRUE(INVALID_HANDLE_VALUE != dir); | 590 EXPECT_TRUE(INVALID_HANDLE_VALUE != dir); |
| 591 EXPECT_TRUE(DeleteReparsePoint(dir)); | 591 EXPECT_TRUE(DeleteReparsePoint(dir)); |
| 592 EXPECT_TRUE(::CloseHandle(dir)); | 592 EXPECT_TRUE(::CloseHandle(dir)); |
| 593 | 593 |
| 594 // Cleanup. | 594 // Cleanup. |
| 595 EXPECT_TRUE(::DeleteFile(temp_file_in_temp.c_str())); | 595 EXPECT_TRUE(::DeleteFile(temp_file_in_temp.c_str())); |
| 596 EXPECT_TRUE(::RemoveDirectory(subfolder.c_str())); | 596 EXPECT_TRUE(::RemoveDirectory(subfolder.c_str())); |
| 597 } | 597 } |
| 598 | 598 |
| 599 } // namespace sandbox | 599 } // namespace sandbox |
| OLD | NEW |