Improve cross-tool protocol's origin allow listing
Test: npm run build:all && npm run test:all Bug: b/260994827 Change-Id: Iab8db927a55f060784a375f00a831bd10020ed0c
This commit is contained in:
@@ -14,14 +14,10 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export interface Schema {
|
||||
MODE: "DEV"|"PROD";
|
||||
REMOTE_TOOL_URL: "http://localhost:8081"|"https://android-build.googleplex.com/builds/bug_tool";
|
||||
}
|
||||
export type Schema = Omit<GlobalConfig, "set">;
|
||||
|
||||
export class GlobalConfig implements Schema {
|
||||
readonly MODE = "PROD" as const;
|
||||
readonly REMOTE_TOOL_URL = "https://android-build.googleplex.com/builds/bug_tool" as const;
|
||||
class GlobalConfig {
|
||||
readonly MODE: "DEV"|"PROD" = "PROD" as const;
|
||||
|
||||
set(config: Schema) {
|
||||
Object.assign(this, config);
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
|
||||
import {Message, MessageBugReport, MessagePong, MessageTimestamp, MessageType} from "./messages";
|
||||
import {OriginAllowList} from "./origin_allow_list";
|
||||
import {
|
||||
CrossToolProtocolDependencyInversion,
|
||||
OnBugreportReceived,
|
||||
@@ -23,8 +24,15 @@ import {RealTimestamp} from "common/trace/timestamp";
|
||||
import {FunctionUtils} from "common/utils/function_utils";
|
||||
import {globalConfig} from "common/utils/global_config";
|
||||
|
||||
class CrossToolProtocol implements CrossToolProtocolDependencyInversion {
|
||||
private remoteToolWindow?: Window;
|
||||
class RemoteTool {
|
||||
constructor(
|
||||
public readonly window: Window,
|
||||
public readonly origin: string) {
|
||||
}
|
||||
}
|
||||
|
||||
export class CrossToolProtocol implements CrossToolProtocolDependencyInversion {
|
||||
private remoteTool?: RemoteTool;
|
||||
private onBugreportReceived: OnBugreportReceived = FunctionUtils.DO_NOTHING_ASYNC;
|
||||
private onTimestampReceived: OnTimestampReceived = FunctionUtils.DO_NOTHING_ASYNC;
|
||||
|
||||
@@ -43,34 +51,36 @@ class CrossToolProtocol implements CrossToolProtocolDependencyInversion {
|
||||
}
|
||||
|
||||
sendTimestamp(timestamp: RealTimestamp) {
|
||||
if (!this.remoteToolWindow) {
|
||||
if (!this.remoteTool) {
|
||||
return;
|
||||
}
|
||||
|
||||
const message = new MessageTimestamp(timestamp.getValueNs());
|
||||
this.remoteToolWindow.postMessage(message, globalConfig.REMOTE_TOOL_URL);
|
||||
this.remoteTool.window.postMessage(message, this.remoteTool.origin);
|
||||
console.log("Cross-tool protocol sent timestamp message:", message);
|
||||
}
|
||||
|
||||
private async onMessageReceived(event: MessageEvent) {
|
||||
if (event.origin !== globalConfig.REMOTE_TOOL_URL) {
|
||||
console.log("Cross-tool protocol ignoring message from unexpected origin.",
|
||||
if (!OriginAllowList.isAllowed(event.origin)) {
|
||||
console.log("Cross-tool protocol ignoring message from non-allowed origin.",
|
||||
"Origin:", event.origin, "Message:", event.data);
|
||||
return;
|
||||
}
|
||||
|
||||
this.remoteToolWindow = event.source as Window;
|
||||
|
||||
const message = event.data as Message;
|
||||
if (!message.type) {
|
||||
console.log("Cross-tool protocol received invalid message:", message);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.remoteTool) {
|
||||
this.remoteTool = new RemoteTool(event.source as Window, event.origin);
|
||||
}
|
||||
|
||||
switch(message.type) {
|
||||
case MessageType.PING:
|
||||
console.log("Cross-tool protocol received ping message:", message);
|
||||
(event.source as Window).postMessage(new MessagePong(), globalConfig.REMOTE_TOOL_URL);
|
||||
(event.source as Window).postMessage(new MessagePong(), event.origin);
|
||||
break;
|
||||
case MessageType.PONG:
|
||||
console.log("Cross-tool protocol received unexpected pong message:", message);
|
||||
@@ -104,5 +114,3 @@ class CrossToolProtocol implements CrossToolProtocolDependencyInversion {
|
||||
await this.onTimestampReceived(timestamp);
|
||||
}
|
||||
}
|
||||
|
||||
export {CrossToolProtocol};
|
||||
|
||||
54
tools/winscope-ng/src/cross_tool/origin_allow_list.spec.ts
Normal file
54
tools/winscope-ng/src/cross_tool/origin_allow_list.spec.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import {OriginAllowList} from "./origin_allow_list";
|
||||
|
||||
describe("OriginAllowList", () => {
|
||||
describe("dev mode", () => {
|
||||
const mode = "DEV" as const;
|
||||
|
||||
it("allows localhost", () => {
|
||||
expect(OriginAllowList.isAllowed("http://localhost:8081", mode)).toBeTrue();
|
||||
expect(OriginAllowList.isAllowed("https://localhost:8081", mode)).toBeTrue();
|
||||
});
|
||||
});
|
||||
|
||||
describe("prod mode", () => {
|
||||
const mode = "PROD" as const;
|
||||
|
||||
it("allows google.com", () => {
|
||||
expect(OriginAllowList.isAllowed("https://google.com", mode)).toBeTrue();
|
||||
expect(OriginAllowList.isAllowed("https://subdomain.google.com", mode)).toBeTrue();
|
||||
});
|
||||
|
||||
it("denies pseudo google.com", () => {
|
||||
expect(OriginAllowList.isAllowed("https://evilgoogle.com", mode)).toBeFalse();
|
||||
expect(OriginAllowList.isAllowed("https://evil.com/google.com", mode)).toBeFalse();
|
||||
});
|
||||
|
||||
it("allows googleplex.com", () => {
|
||||
expect(OriginAllowList.isAllowed("https://googleplex.com", mode)).toBeTrue();
|
||||
expect(OriginAllowList.isAllowed("https://subdomain.googleplex.com", mode))
|
||||
.toBeTrue();
|
||||
});
|
||||
|
||||
it("denies pseudo googleplex.com", () => {
|
||||
expect(OriginAllowList.isAllowed("https://evilgoogleplex.com", mode)).toBeFalse();
|
||||
expect(OriginAllowList.isAllowed("https://evil.com/subdomain.googleplex.com", mode))
|
||||
.toBeFalse();
|
||||
});
|
||||
});
|
||||
});
|
||||
52
tools/winscope-ng/src/cross_tool/origin_allow_list.ts
Normal file
52
tools/winscope-ng/src/cross_tool/origin_allow_list.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import {globalConfig} from "common/utils/global_config";
|
||||
|
||||
export class OriginAllowList {
|
||||
private static readonly ALLOW_LIST_PROD = [
|
||||
new RegExp("^https://([^\\/]*\\.)*googleplex\\.com$"),
|
||||
new RegExp("^https://([^\\/]*\\.)*google\\.com$"),
|
||||
];
|
||||
|
||||
private static readonly ALLOW_LIST_DEV = [
|
||||
...OriginAllowList.ALLOW_LIST_PROD,
|
||||
new RegExp("^(http|https)://localhost:8081$"), // remote tool mock
|
||||
];
|
||||
|
||||
static isAllowed(originUrl: string, mode = globalConfig.MODE): boolean {
|
||||
const list = OriginAllowList.getList(mode);
|
||||
|
||||
for (const regex of list) {
|
||||
if (regex.test(originUrl)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static getList(mode: typeof globalConfig.MODE): RegExp[] {
|
||||
switch(mode) {
|
||||
case "DEV":
|
||||
return OriginAllowList.ALLOW_LIST_DEV;
|
||||
case "PROD":
|
||||
return OriginAllowList.ALLOW_LIST_PROD;
|
||||
default:
|
||||
throw new Error(`Unhandled mode: ${globalConfig.MODE}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,7 +19,6 @@ import {globalConfig} from "common/utils/global_config";
|
||||
|
||||
globalConfig.set({
|
||||
MODE: "DEV",
|
||||
REMOTE_TOOL_URL: "http://localhost:8081"
|
||||
});
|
||||
|
||||
platformBrowserDynamic().bootstrapModule(AppModule)
|
||||
|
||||
Reference in New Issue
Block a user