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 "base/command_line.h"
6#include "base/json/json_file_value_serializer.h"
7#include "base/memory/linked_ptr.h"
8#include "chrome/common/extensions/manifest_handlers/app_isolation_info.h"
9#include "chrome/common/extensions/manifest_tests/chrome_manifest_test.h"
10#include "extensions/common/error_utils.h"
11#include "extensions/common/manifest_constants.h"
12#include "extensions/common/manifest_handlers/csp_info.h"
13#include "extensions/common/manifest_handlers/incognito_info.h"
14#include "extensions/common/switches.h"
15#include "testing/gtest/include/gtest/gtest.h"
16
17namespace extensions {
18
19namespace errors = manifest_errors;
20
21class PlatformAppsManifestTest : public ChromeManifestTest {
22};
23
24TEST_F(PlatformAppsManifestTest, PlatformApps) {
25  scoped_refptr<Extension> extension =
26      LoadAndExpectSuccess("init_valid_platform_app.json");
27  EXPECT_TRUE(AppIsolationInfo::HasIsolatedStorage(extension.get()));
28  EXPECT_FALSE(IncognitoInfo::IsSplitMode(extension.get()));
29
30  extension =
31      LoadAndExpectSuccess("init_valid_platform_app_no_manifest_version.json");
32  EXPECT_EQ(2, extension->manifest_version());
33
34  extension = LoadAndExpectSuccess("incognito_valid_platform_app.json");
35  EXPECT_FALSE(IncognitoInfo::IsSplitMode(extension.get()));
36
37  Testcase error_testcases[] = {
38    Testcase("init_invalid_platform_app_2.json",
39             errors::kBackgroundRequiredForPlatformApps),
40    Testcase("init_invalid_platform_app_3.json",
41             ErrorUtils::FormatErrorMessage(
42                 errors::kInvalidManifestVersionOld, "2", "apps")),
43  };
44  RunTestcases(error_testcases, arraysize(error_testcases), EXPECT_TYPE_ERROR);
45
46  Testcase warning_testcases[] = {
47    Testcase(
48        "init_invalid_platform_app_1.json",
49        "'app.launch' is only allowed for hosted apps and legacy packaged "
50            "apps, but this is a packaged app."),
51    Testcase(
52        "init_invalid_platform_app_4.json",
53        "'background' is only allowed for extensions, hosted apps, and legacy "
54            "packaged apps, but this is a packaged app."),
55    Testcase(
56        "init_invalid_platform_app_5.json",
57        "'background' is only allowed for extensions, hosted apps, and legacy "
58            "packaged apps, but this is a packaged app."),
59    Testcase("incognito_invalid_platform_app.json",
60        "'incognito' is only allowed for extensions and legacy packaged apps, "
61            "but this is a packaged app."),
62  };
63  RunTestcases(
64      warning_testcases, arraysize(warning_testcases), EXPECT_TYPE_WARNING);
65}
66
67TEST_F(PlatformAppsManifestTest, PlatformAppContentSecurityPolicy) {
68  // Normal platform apps can't specify a CSP value.
69  Testcase warning_testcases[] = {
70    Testcase(
71        "init_platform_app_csp_warning_1.json",
72        "'content_security_policy' is only allowed for extensions and legacy "
73            "packaged apps, but this is a packaged app."),
74    Testcase(
75        "init_platform_app_csp_warning_2.json",
76        "'app.content_security_policy' is not allowed for specified extension "
77            "ID.")
78  };
79  RunTestcases(
80      warning_testcases, arraysize(warning_testcases), EXPECT_TYPE_WARNING);
81
82  // Whitelisted ones can (this is the ID corresponding to the base 64 encoded
83  // key in the init_platform_app_csp.json manifest.)
84  std::string test_id = "ahplfneplbnjcflhdgkkjeiglkkfeelb";
85  CommandLine::ForCurrentProcess()->AppendSwitchASCII(
86      switches::kWhitelistedExtensionID, test_id);
87  scoped_refptr<Extension> extension =
88      LoadAndExpectSuccess("init_platform_app_csp.json");
89  EXPECT_EQ(0U, extension->install_warnings().size())
90      << "Unexpected warning " << extension->install_warnings()[0].message;
91  EXPECT_TRUE(extension->is_platform_app());
92  EXPECT_EQ("default-src 'self' https://www.google.com",
93            CSPInfo::GetResourceContentSecurityPolicy(extension.get(),
94                                                      std::string()));
95
96  // But even whitelisted ones must specify a secure policy.
97  LoadAndExpectError(
98      "init_platform_app_csp_insecure.json",
99      errors::kInsecureContentSecurityPolicy);
100}
101
102TEST_F(PlatformAppsManifestTest, CertainApisRequirePlatformApps) {
103  // Put APIs here that should be restricted to platform apps, but that haven't
104  // yet graduated from experimental.
105  const char* kPlatformAppExperimentalApis[] = {
106    "dns",
107    "serial",
108  };
109  // TODO(miket): When the first platform-app API leaves experimental, write
110  // similar code that tests without the experimental flag.
111
112  // This manifest is a skeleton used to build more specific manifests for
113  // testing. The requirements are that (1) it be a valid platform app, and (2)
114  // it contain no permissions dictionary.
115  std::string error;
116  scoped_ptr<base::DictionaryValue> manifest(
117      LoadManifest("init_valid_platform_app.json", &error));
118
119  std::vector<linked_ptr<base::DictionaryValue> > manifests;
120  // Create each manifest.
121  for (size_t i = 0; i < arraysize(kPlatformAppExperimentalApis); ++i) {
122    const char* api_name = kPlatformAppExperimentalApis[i];
123
124    // DictionaryValue will take ownership of this ListValue.
125    base::ListValue *permissions = new base::ListValue();
126    permissions->Append(new base::StringValue("experimental"));
127    permissions->Append(new base::StringValue(api_name));
128    manifest->Set("permissions", permissions);
129    manifests.push_back(make_linked_ptr(manifest->DeepCopy()));
130  }
131
132  // First try to load without any flags. This should fail for every API.
133  for (size_t i = 0; i < arraysize(kPlatformAppExperimentalApis); ++i) {
134    LoadAndExpectError(ManifestData(manifests[i].get(), ""),
135                       errors::kExperimentalFlagRequired);
136  }
137
138  // Now try again with the experimental flag set.
139  CommandLine::ForCurrentProcess()->AppendSwitch(
140      switches::kEnableExperimentalExtensionApis);
141  for (size_t i = 0; i < arraysize(kPlatformAppExperimentalApis); ++i) {
142    LoadAndExpectSuccess(ManifestData(manifests[i].get(), ""));
143  }
144}
145
146}  // namespace extensions
147