UNPKG

chrome-devtools-frontend

Version:
243 lines (199 loc) • 13 kB
// Copyright 2022 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. import * as Platform from '../../core/platform/platform.js'; import * as SDK from '../../core/sdk/sdk.js'; import * as Protocol from '../../generated/protocol.js'; import {createTarget, updateHostConfig} from '../../testing/EnvironmentHelpers.js'; import {describeWithMockConnection} from '../../testing/MockConnection.js'; import {getMainFrame, navigate} from '../../testing/ResourceTreeHelpers.js'; import * as Security from './security.js'; const {urlString} = Platform.DevToolsPath; describeWithMockConnection('SecurityAndPrivacyPanel', () => { describe('viewMemory', () => { it('initially shows control view if privacy UI is enabled', () => { updateHostConfig({devToolsPrivacyUI: {enabled: true}}); const securityPanel = Security.SecurityPanel.SecurityPanel.instance({forceNew: true}); assert.instanceOf(securityPanel.visibleView, Security.CookieControlsView.CookieControlsView); }); it('initially shows security main view if privacy UI is not enabled', () => { updateHostConfig({devToolsPrivacyUI: {enabled: false}}); const securityPanel = Security.SecurityPanel.SecurityPanel.instance({forceNew: true}); assert.instanceOf(securityPanel.visibleView, Security.SecurityPanel.SecurityMainView); }); it('remembers last selected view when new panel is made', () => { updateHostConfig({devToolsPrivacyUI: {enabled: true}}); let securityPanel = Security.SecurityPanel.SecurityPanel.instance({forceNew: true}); // Should initially be the controls view assert.instanceOf(securityPanel.visibleView, Security.CookieControlsView.CookieControlsView); // Select and switch to the security main view securityPanel.sidebar.securityOverviewElement.select(/* omitFocus=*/ false, /* selectedByUser=*/ true); assert.instanceOf(securityPanel.visibleView, Security.SecurityPanel.SecurityMainView); // Create a new security panel. The last selected view memory should make the main view visible securityPanel = Security.SecurityPanel.SecurityPanel.instance({forceNew: true}); assert.instanceOf(securityPanel.visibleView, Security.SecurityPanel.SecurityMainView); }); }); describe('updateOrigin', () => { it('correctly updates the URL scheme highlighting', () => { const origin = urlString`https://foo.bar`; const securityPanel = Security.SecurityPanel.SecurityPanel.instance({forceNew: true}); securityPanel.sidebar.addOrigin(origin, Protocol.Security.SecurityState.Unknown); assert.notExists( securityPanel.sidebar.sidebarTree.contentElement.querySelector('.highlighted-url > .url-scheme-secure')); assert.exists( securityPanel.sidebar.sidebarTree.contentElement.querySelector('.highlighted-url > .url-scheme-unknown')); securityPanel.sidebar.updateOrigin(origin, Protocol.Security.SecurityState.Secure); assert.exists( securityPanel.sidebar.sidebarTree.contentElement.querySelector('.highlighted-url > .url-scheme-secure')); assert.notExists( securityPanel.sidebar.sidebarTree.contentElement.querySelector('.highlighted-url > .url-scheme-unknown')); }); }); }); describeWithMockConnection('SecurityPanel', () => { let target: SDK.Target.Target; let prerenderTarget: SDK.Target.Target; beforeEach(() => { const tabTarget = createTarget({type: SDK.Target.Type.TAB}); prerenderTarget = createTarget({parentTarget: tabTarget, subtype: 'prerender'}); target = createTarget({parentTarget: tabTarget}); }); it('updates when security state changes', async () => { const securityPanel = Security.SecurityPanel.SecurityPanel.instance({forceNew: true}); const securityModel = target.model(Security.SecurityModel.SecurityModel); assert.exists(securityModel); const visibleSecurityState = { securityState: Protocol.Security.SecurityState.Insecure, securityStateIssueIds: [], certificateSecurityState: null, } as unknown as Security.SecurityModel.PageVisibleSecurityState; securityModel.dispatchEventToListeners( Security.SecurityModel.Events.VisibleSecurityStateChanged, visibleSecurityState); assert.isTrue(securityPanel.mainView.contentElement.querySelector('.security-summary') ?.classList.contains('security-summary-insecure')); visibleSecurityState.securityState = Protocol.Security.SecurityState.Secure; securityModel.dispatchEventToListeners( Security.SecurityModel.Events.VisibleSecurityStateChanged, visibleSecurityState); assert.isFalse(securityPanel.mainView.contentElement.querySelector('.security-summary') ?.classList.contains('security-summary-insecure')); assert.isTrue(securityPanel.mainView.contentElement.querySelector('.security-summary') ?.classList.contains('security-summary-secure')); }); it('can switch to a different SecurityModel', async () => { const mainSecurityModel = target.model(Security.SecurityModel.SecurityModel); assert.exists(mainSecurityModel); const securityPanel = Security.SecurityPanel.SecurityPanel.instance({forceNew: true}); // Add the main target to the security panel. securityPanel.modelAdded(mainSecurityModel); const visibleSecurityState = { securityState: Protocol.Security.SecurityState.Insecure, securityStateIssueIds: [], certificateSecurityState: null, } as unknown as Security.SecurityModel.PageVisibleSecurityState; mainSecurityModel.dispatchEventToListeners( Security.SecurityModel.Events.VisibleSecurityStateChanged, visibleSecurityState); assert.isTrue(securityPanel.mainView.contentElement.querySelector('.security-summary') ?.classList.contains('security-summary-insecure')); // Switch to the prerender target. const prerenderSecurityModel = prerenderTarget.model(Security.SecurityModel.SecurityModel); assert.exists(prerenderSecurityModel); securityPanel.modelAdded(prerenderSecurityModel); securityPanel.modelRemoved(mainSecurityModel); // Check that the security panel does not listen to events from the previous target. visibleSecurityState.securityState = Protocol.Security.SecurityState.Secure; mainSecurityModel.dispatchEventToListeners( Security.SecurityModel.Events.VisibleSecurityStateChanged, visibleSecurityState); assert.isTrue(securityPanel.mainView.contentElement.querySelector('.security-summary') ?.classList.contains('security-summary-insecure')); // Check that the security panel listens to events from the current target. prerenderSecurityModel.dispatchEventToListeners( Security.SecurityModel.Events.VisibleSecurityStateChanged, visibleSecurityState); assert.isTrue(securityPanel.mainView.contentElement.querySelector('.security-summary') ?.classList.contains('security-summary-secure')); // Check that the SecurityPanel listens to any PrimaryPageChanged event const sidebarTreeClearSpy = sinon.spy(securityPanel.sidebar, 'clearOrigins'); navigate(getMainFrame(target)); sinon.assert.calledOnce(sidebarTreeClearSpy); }); it('shows \'reload page\' message when no data is available', async () => { const securityModel = target.model(Security.SecurityModel.SecurityModel); assert.exists(securityModel); const securityPanel = Security.SecurityPanel.SecurityPanel.instance({forceNew: true}); // Check that reload message is visible initially. const reloadMessage = securityPanel.sidebar.sidebarTree.shadowRoot.querySelector('.security-main-view-reload-message'); assert.instanceOf(reloadMessage, HTMLLIElement); assert.isFalse(reloadMessage.classList.contains('hidden')); // Check that reload message is hidden when there is data to display. const networkManager = securityModel.networkManager(); const request = { wasBlocked: () => false, url: () => 'https://www.example.com', securityState: () => Protocol.Security.SecurityState.Secure, securityDetails: () => null, cached: () => false, } as SDK.NetworkRequest.NetworkRequest; networkManager.dispatchEventToListeners(SDK.NetworkManager.Events.RequestFinished, request); assert.isTrue(reloadMessage.classList.contains('hidden')); // Check that reload message is hidden after clearing data. navigate(getMainFrame(target)); assert.isFalse(reloadMessage.classList.contains('hidden')); }); it('shows origins with blockable and optionally blockable resources in the sidebar', async () => { const securityPanel = Security.SecurityPanel.SecurityPanel.instance({forceNew: true}); const sidebarTreeClearSpy = sinon.spy(securityPanel.sidebar, 'addOrigin'); const pageVisibleSecurityState = new Security.SecurityModel.PageVisibleSecurityState( Protocol.Security.SecurityState.Neutral, null, null, ['displayed-mixed-content', 'ran-mixed-content']); const securityModel = target.model(Security.SecurityModel.SecurityModel); assert.exists(securityModel); securityModel.dispatchEventToListeners( Security.SecurityModel.Events.VisibleSecurityStateChanged, pageVisibleSecurityState); const passive = SDK.NetworkRequest.NetworkRequest.create( '0' as Protocol.Network.RequestId, urlString`http://foo.test`, urlString`https://foo.test`, '0' as Protocol.Page.FrameId, '0' as Protocol.Network.LoaderId, null); passive.mixedContentType = Protocol.Security.MixedContentType.OptionallyBlockable; const networkManager = securityModel.networkManager(); networkManager.dispatchEventToListeners(SDK.NetworkManager.Events.RequestFinished, passive); assert.isTrue( sidebarTreeClearSpy.calledOnceWith(urlString`http://foo.test`, Protocol.Security.SecurityState.Insecure)); sidebarTreeClearSpy.resetHistory(); const active = SDK.NetworkRequest.NetworkRequest.create( '0' as Protocol.Network.RequestId, urlString`http://bar.test`, urlString`https://bar.test`, '0' as Protocol.Page.FrameId, '0' as Protocol.Network.LoaderId, null); active.mixedContentType = Protocol.Security.MixedContentType.Blockable; networkManager.dispatchEventToListeners(SDK.NetworkManager.Events.RequestFinished, active); assert.isTrue( sidebarTreeClearSpy.calledOnceWith(urlString`http://bar.test`, Protocol.Security.SecurityState.Insecure)); }); it('hides and shows the sidebar origin list when an interstitial is shown or hidden', async () => { const securityPanel = Security.SecurityPanel.SecurityPanel.instance({forceNew: true}); const toggleSidebarSpy = sinon.spy(securityPanel.sidebar, 'toggleOriginsList'); const resourceTreeModel = target.model(SDK.ResourceTreeModel.ResourceTreeModel); assert.exists(resourceTreeModel); const networkManager = target.model(SDK.NetworkManager.NetworkManager); assert.exists(networkManager); const request1 = SDK.NetworkRequest.NetworkRequest.create( '0' as Protocol.Network.RequestId, urlString`https://foo.test/`, urlString`https://foo.test`, '0' as Protocol.Page.FrameId, '0' as Protocol.Network.LoaderId, null); request1.setSecurityState(Protocol.Security.SecurityState.Secure); networkManager.dispatchEventToListeners(SDK.NetworkManager.Events.RequestFinished, request1); const request2 = SDK.NetworkRequest.NetworkRequest.create( '0' as Protocol.Network.RequestId, urlString`https://bar.test/foo.jpg`, urlString`https://bar.test`, '0' as Protocol.Page.FrameId, '0' as Protocol.Network.LoaderId, null); request2.setSecurityState(Protocol.Security.SecurityState.Secure); networkManager.dispatchEventToListeners(SDK.NetworkManager.Events.RequestFinished, request2); resourceTreeModel.dispatchEventToListeners(SDK.ResourceTreeModel.Events.InterstitialShown); // Simulate a request finishing after the interstitial is shown, to make sure that doesn't show up in the sidebar. const request3 = SDK.NetworkRequest.NetworkRequest.create( '0' as Protocol.Network.RequestId, urlString`https://bar.test/foo.jpg`, urlString`https://bar.test`, '0' as Protocol.Page.FrameId, '0' as Protocol.Network.LoaderId, null); request3.setSecurityState(Protocol.Security.SecurityState.Unknown); networkManager.dispatchEventToListeners(SDK.NetworkManager.Events.RequestFinished, request3); assert.isTrue(toggleSidebarSpy.calledOnceWith(true)); toggleSidebarSpy.resetHistory(); // Test that the sidebar is shown again when the interstitial is hidden. https://crbug.com/559150 resourceTreeModel.dispatchEventToListeners(SDK.ResourceTreeModel.Events.InterstitialHidden); assert.isTrue(toggleSidebarSpy.calledOnceWith(false)); }); });