Commit d20be1e7 authored by Rayan Kanso's avatar Rayan Kanso Committed by Commit Bot

[gIRA] Enable checking for WebAPKs not controlled by the manifest.

The verification is done by checking the digital asset links of the
target WebAPK's origin, and making sure that the assets contain a
declaration that the page querying for the WebAPK has permission to
do so.

Bug: 1043970
Change-Id: Idd2c85769d6c487025a73e4c23f660156583c863
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2047164Reviewed-by: default avatarPeter Conn <peconn@chromium.org>
Reviewed-by: default avatarDavid Trainor <dtrainor@chromium.org>
Commit-Queue: Rayan Kanso <rayankans@chromium.org>
Cr-Commit-Position: refs/heads/master@{#740661}
parent 2c5cf427
......@@ -2682,6 +2682,7 @@ generate_jni("chrome_jni_headers") {
"java/src/org/chromium/chrome/browser/infobar/TranslateCompactInfoBar.java",
"java/src/org/chromium/chrome/browser/infobar/UpdatePasswordInfoBar.java",
"java/src/org/chromium/chrome/browser/init/NativeStartupBridge.java",
"java/src/org/chromium/chrome/browser/installedapp/InstalledAppProviderImpl.java",
"java/src/org/chromium/chrome/browser/installedapp/PackageHash.java",
"java/src/org/chromium/chrome/browser/instantapps/InstantAppsSettings.java",
"java/src/org/chromium/chrome/browser/locale/LocaleManager.java",
......@@ -2870,6 +2871,7 @@ android_library("native_java_unittests_java") {
"//base:jni_java",
"//base/test:test_support_java",
"//chrome/android:chrome_java",
"//chrome/android/public/profiles:java",
"//chrome/browser/flags:java",
"//chrome/browser/util:java",
"//chrome/test/android:chrome_java_test_support",
......
......@@ -13,11 +13,16 @@ import android.os.Bundle;
import android.util.Pair;
import org.junit.Assert;
import org.junit.Rule;
import org.chromium.base.Callback;
import org.chromium.base.annotations.CalledByNative;
import org.chromium.base.annotations.CalledByNativeJavaTest;
import org.chromium.base.test.util.JniMocker;
import org.chromium.chrome.browser.UnitTestUtils;
import org.chromium.chrome.browser.instantapps.InstantAppsHandler;
import org.chromium.chrome.browser.profiles.Profile;
import org.chromium.content_public.browser.test.util.TestThreadUtils;
import org.chromium.installedapp.mojom.InstalledAppProvider;
import org.chromium.installedapp.mojom.RelatedApplication;
import org.chromium.url.URI;
......@@ -57,6 +62,7 @@ public class InstalledAppProviderTest {
private static final String URL_ON_ORIGIN =
"https://example.com:8000/path/to/page.html?key=value#fragment";
private static final String MANIFEST_URL = "https://example.com:8000/manifest.json";
private static final String OTHER_MANIFEST_URL = "https://example2.com:8000/manifest.json";
private static final String ORIGIN_SYNTAX_ERROR = "https:{";
private static final String ORIGIN_MISSING_SCHEME = "path/only";
private static final String ORIGIN_MISSING_HOST = "file:///path/piece";
......@@ -70,6 +76,9 @@ public class InstalledAppProviderTest {
private InstalledAppProviderTestImpl mInstalledAppProvider;
private FakeInstantAppsHandler mFakeInstantAppsHandler;
@Rule
public JniMocker mocker = new JniMocker();
private static class FakePackageManager extends PackageManagerDelegate {
private Map<String, PackageInfo> mPackageInfo = new HashMap<>();
private Map<String, Resources> mResources = new HashMap<>();
......@@ -137,6 +146,27 @@ public class InstalledAppProviderTest {
public boolean isWebApkInstalled(String manifestUrl) {
return mFakePackageManager.isWebApkInstalled(manifestUrl);
}
@Override
protected Profile getProfile() {
return null;
}
}
private static class TestInstalledAppProviderImplJni
implements InstalledAppProviderImpl.Natives {
private static final Map<String, String> RELATION_MAP = new HashMap<>();
public static void addVerfication(String webDomain, String manifestUrl) {
RELATION_MAP.put(webDomain, manifestUrl);
}
@Override
public void checkDigitalAssetLinksRelationshipForWebApk(
Profile profile, String webDomain, String manifestUrl, Callback<Boolean> callback) {
callback.onResult(RELATION_MAP.containsKey(webDomain)
&& RELATION_MAP.get(webDomain).equals(manifestUrl));
}
}
/**
......@@ -354,7 +384,11 @@ public class InstalledAppProviderTest {
private InstalledAppProviderTest() {}
@CalledByNative
public void setUp() throws URISyntaxException {
public void setUp() throws Exception {
TestThreadUtils.setThreadAssertsDisabled(true);
mocker.mock(InstalledAppProviderImplJni.TEST_HOOKS, new TestInstalledAppProviderImplJni());
mFakePackageManager = new FakePackageManager();
mFrameUrlDelegate = new FakeFrameUrlDelegate(URL_ON_ORIGIN);
mFakeInstantAppsHandler = new FakeInstantAppsHandler();
......@@ -854,7 +888,7 @@ public class InstalledAppProviderTest {
// low 10 bits of the first two bytes of the result / 100.
Assert.assertEquals(5, mInstalledAppProvider.getLastDelayMillis());
// WebAPK.
// Own WebAPK.
manifestRelatedApps = new RelatedApplication[] {
createRelatedApplication(PLATFORM_WEBAPP, null, MANIFEST_URL)};
expectedInstalledRelatedApps = new RelatedApplication[] {};
......@@ -862,6 +896,15 @@ public class InstalledAppProviderTest {
// This expectation is based on HMAC_SHA256(salt, manifestUrl encoded in UTF-8), taking the
// low 10 bits of the first two bytes of the result / 100.
Assert.assertEquals(3, mInstalledAppProvider.getLastDelayMillis());
// Another WebAPK.
manifestRelatedApps = new RelatedApplication[] {
createRelatedApplication(PLATFORM_WEBAPP, null, OTHER_MANIFEST_URL)};
expectedInstalledRelatedApps = new RelatedApplication[] {};
verifyInstalledApps(manifestRelatedApps, expectedInstalledRelatedApps);
// This expectation is based on HMAC_SHA256(salt, manifestUrl encoded in UTF-8), taking the
// low 10 bits of the first two bytes of the result / 100.
Assert.assertEquals(8, mInstalledAppProvider.getLastDelayMillis());
}
@CalledByNativeJavaTest
......@@ -916,4 +959,33 @@ public class InstalledAppProviderTest {
RelatedApplication[] expectedInstalledRelatedApps = new RelatedApplication[] {webApk};
verifyInstalledApps(manifestRelatedApps, expectedInstalledRelatedApps);
}
/**
* Check that a website can find another WebAPK when installed & verfied.
*/
@CalledByNativeJavaTest
public void testInstalledWebApkForOtherWebsite() throws Exception {
RelatedApplication webApk =
createRelatedApplication(PLATFORM_WEBAPP, null, OTHER_MANIFEST_URL);
RelatedApplication manifestRelatedApps[] = new RelatedApplication[] {webApk};
mFakePackageManager.addWebApk(OTHER_MANIFEST_URL);
verifyInstalledApps(manifestRelatedApps, new RelatedApplication[] {});
TestInstalledAppProviderImplJni.addVerfication(OTHER_MANIFEST_URL, MANIFEST_URL);
verifyInstalledApps(manifestRelatedApps, new RelatedApplication[] {webApk});
}
/**
* Check that a website can query another WebAPK when not installed but verfied.
*/
@CalledByNativeJavaTest
public void testInstalledWebApkForOtherWebsiteNotInstalled() throws Exception {
RelatedApplication webApk =
createRelatedApplication(PLATFORM_WEBAPP, null, OTHER_MANIFEST_URL);
RelatedApplication manifestRelatedApps[] = new RelatedApplication[] {webApk};
TestInstalledAppProviderImplJni.addVerfication(MANIFEST_URL, OTHER_MANIFEST_URL);
verifyInstalledApps(manifestRelatedApps, new RelatedApplication[] {});
}
}
......@@ -2538,6 +2538,7 @@ jumbo_static_library("browser") {
"android/hung_renderer_infobar_delegate.h",
"android/image_fetcher/image_fetcher_bridge.cc",
"android/image_fetcher/image_fetcher_bridge.h",
"android/installedapp/installed_app_verifier.cc",
"android/instantapps/instant_apps_infobar_delegate.cc",
"android/instantapps/instant_apps_infobar_delegate.h",
"android/instantapps/instant_apps_settings.cc",
......
......@@ -241,6 +241,15 @@ bool DigitalAssetLinksHandler::CheckDigitalAssetLinkRelationshipForAndroidApp(
std::move(callback));
}
bool DigitalAssetLinksHandler::CheckDigitalAssetLinkRelationshipForWebApk(
const std::string& web_domain,
const std::string& manifest_url,
RelationshipCheckResultCallback callback) {
return CheckDigitalAssetLinkRelationship(
web_domain, "delegate_permission/common.query_webapk", base::nullopt,
{{"namespace", "web"}, {"site", manifest_url}}, std::move(callback));
}
bool DigitalAssetLinksHandler::CheckDigitalAssetLinkRelationship(
const std::string& web_domain,
const std::string& relationship,
......
......@@ -67,6 +67,14 @@ class DigitalAssetLinksHandler : public content::WebContentsObserver {
const std::string& package,
RelationshipCheckResultCallback callback);
// Checks if the asset links for |web_domain| allow pages controlled by
// |manifest_url| to query for WebAPKs generated by |web_domain|.
// TODO(rayankans): Link to the developer blog when published.
bool CheckDigitalAssetLinkRelationshipForWebApk(
const std::string& web_domain,
const std::string& manifest_url,
RelationshipCheckResultCallback callback);
private:
// Generic DAL verifier.
bool CheckDigitalAssetLinkRelationship(
......
......@@ -41,6 +41,12 @@ const char kStatementList[] = R"(
"64:2F:D4:BE:1C:4D:F8:36:2E:D3:50:C4:69:53:96:A1:3D:14:0A:23:AD:2F:BF:EB:6E:C6:E4:64:54:3B:34:C1"
]
}
}, {
"relation": ["delegate_permission/common.query_webapk"],
"target": {
"namespace": "web",
"site": "https://example2.com/manifest.json"
}
}]
)";
......@@ -272,4 +278,28 @@ TEST_F(DigitalAssetLinksHandlerTest, NetworkDisconnected) {
EXPECT_EQ(result_, RelationshipCheckResult::kNoConnection);
}
TEST_F(DigitalAssetLinksHandlerTest, WebApkPositiveResponse) {
DigitalAssetLinksHandler handler(GetSharedURLLoaderFactory());
handler.CheckDigitalAssetLinkRelationshipForWebApk(
kDomain, "https://example2.com/manifest.json",
base::BindOnce(&DigitalAssetLinksHandlerTest::OnRelationshipCheckComplete,
base::Unretained(this)));
AddResponse(kStatementList);
EXPECT_EQ(1, num_invocations_);
EXPECT_EQ(result_, RelationshipCheckResult::kSuccess);
}
TEST_F(DigitalAssetLinksHandlerTest, WebApkNegativeResponse) {
DigitalAssetLinksHandler handler(GetSharedURLLoaderFactory());
handler.CheckDigitalAssetLinkRelationshipForWebApk(
kDomain, "https://notverified.com/manifest.json",
base::BindOnce(&DigitalAssetLinksHandlerTest::OnRelationshipCheckComplete,
base::Unretained(this)));
AddResponse(kStatementList);
EXPECT_EQ(1, num_invocations_);
EXPECT_EQ(result_, RelationshipCheckResult::kFailure);
}
} // namespace digital_asset_links
peconn@chromium.org
rayankans@chromium.org
# COMPONENT: Platform>Apps>AppLauncher>Install
\ No newline at end of file
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <memory>
#include <string>
#include "base/android/callback_android.h"
#include "base/android/jni_android.h"
#include "base/android/jni_string.h"
#include "chrome/android/chrome_jni_headers/InstalledAppProviderImpl_jni.h"
#include "chrome/browser/android/digital_asset_links/digital_asset_links_handler.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_android.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/storage_partition.h"
namespace {
void DidGetResult(
std::unique_ptr<digital_asset_links::DigitalAssetLinksHandler> handler,
base::OnceCallback<void(bool)> callback,
digital_asset_links::RelationshipCheckResult result) {
std::move(callback).Run(
result == digital_asset_links::RelationshipCheckResult::kSuccess);
}
} // namespace
void JNI_InstalledAppProviderImpl_CheckDigitalAssetLinksRelationshipForWebApk(
JNIEnv* env,
const base::android::JavaParamRef<jobject>& jprofile,
const base::android::JavaParamRef<jstring>& jwebDomain,
const base::android::JavaParamRef<jstring>& jmanifestUrl,
const base::android::JavaParamRef<jobject>& jcallback) {
Profile* profile = ProfileAndroid::FromProfileAndroid(jprofile);
std::string web_domain = ConvertJavaStringToUTF8(env, jwebDomain);
std::string manifest_url = ConvertJavaStringToUTF8(env, jmanifestUrl);
auto callback =
base::BindOnce(&base::android::RunBooleanCallbackAndroid,
base::android::ScopedJavaGlobalRef<jobject>(jcallback));
auto handler =
std::make_unique<digital_asset_links::DigitalAssetLinksHandler>(
content::BrowserContext::GetDefaultStoragePartition(profile)
->GetURLLoaderFactoryForBrowserProcess());
auto* handler_ptr = handler.get();
// |handler| is owned by the callback, so it will be valid until the execution
// is over.
handler_ptr->CheckDigitalAssetLinkRelationshipForWebApk(
web_domain, manifest_url,
base::BindOnce(&DidGetResult, std::move(handler), std::move(callback)));
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment