SecurityPanel.js 41 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. export default class SecurityPanel extends UI.PanelWithSidebar{constructor(){super('security');this._mainView=new Security.SecurityMainView(this);const title=createElementWithClass('span','title');title.textContent=Common.UIString('Overview');this._sidebarMainViewElement=new Security.SecurityPanelSidebarTreeElement(title,this._setVisibleView.bind(this,this._mainView),'security-main-view-sidebar-tree-item','lock-icon');this._sidebarMainViewElement.tooltip=title.textContent;this._sidebarTree=new Security.SecurityPanelSidebarTree(this._sidebarMainViewElement,this.showOrigin.bind(this));this.panelSidebarElement().appendChild(this._sidebarTree.element);this._lastResponseReceivedForLoaderId=new Map();this._origins=new Map();this._filterRequestCounts=new Map();SDK.targetManager.observeModels(Security.SecurityModel,this);}
  2. static _instance(){return(self.runtime.sharedInstance(Security.SecurityPanel));}
  3. static createCertificateViewerButtonForOrigin(text,origin){const certificateButton=UI.createTextButton(text,async e=>{e.consume();const names=await SDK.multitargetNetworkManager.getCertificate(origin);if(names.length>0){Host.InspectorFrontendHost.showCertificateViewer(names);}},'origin-button');UI.ARIAUtils.markAsMenuButton(certificateButton);return certificateButton;}
  4. static createCertificateViewerButtonForCert(text,names){const certificateButton=UI.createTextButton(text,e=>{e.consume();Host.InspectorFrontendHost.showCertificateViewer(names);},'security-certificate-button');UI.ARIAUtils.markAsMenuButton(certificateButton);return certificateButton;}
  5. static createHighlightedUrl(url,securityState){const schemeSeparator='://';const index=url.indexOf(schemeSeparator);if(index===-1){const text=createElement('span','');text.textContent=url;return text;}
  6. const highlightedUrl=createElement('span');const scheme=url.substr(0,index);const content=url.substr(index+schemeSeparator.length);highlightedUrl.createChild('span','url-scheme-'+securityState).textContent=scheme;highlightedUrl.createChild('span','url-scheme-separator').textContent=schemeSeparator;highlightedUrl.createChild('span').textContent=content;return highlightedUrl;}
  7. _updateSecurityState(newSecurityState,explanations,summary){this._sidebarMainViewElement.setSecurityState(newSecurityState);this._mainView.updateSecurityState(newSecurityState,explanations,summary);}
  8. _onSecurityStateChanged(event){const data=(event.data);const securityState=(data.securityState);const explanations=(data.explanations);const summary=(data.summary);this._updateSecurityState(securityState,explanations,summary);}
  9. _updateVisibleSecurityState(visibleSecurityState){this._sidebarMainViewElement.setSecurityState(visibleSecurityState.securityState);this._mainView.updateVisibleSecurityState(visibleSecurityState);}
  10. _onVisibleSecurityStateChanged(event){const data=(event.data);this._updateVisibleSecurityState(data);}
  11. selectAndSwitchToMainView(){this._sidebarMainViewElement.select(true);}
  12. showOrigin(origin){const originState=this._origins.get(origin);if(!originState.originView){originState.originView=new Security.SecurityOriginView(this,origin,originState);}
  13. this._setVisibleView(originState.originView);}
  14. wasShown(){super.wasShown();if(!this._visibleView){this.selectAndSwitchToMainView();}}
  15. focus(){this._sidebarTree.focus();}
  16. _setVisibleView(view){if(this._visibleView===view){return;}
  17. if(this._visibleView){this._visibleView.detach();}
  18. this._visibleView=view;if(view){this.splitWidget().setMainWidget(view);}}
  19. _onResponseReceived(event){const request=(event.data);if(request.resourceType()===Common.resourceTypes.Document){this._lastResponseReceivedForLoaderId.set(request.loaderId,request);}}
  20. _processRequest(request){const origin=Common.ParsedURL.extractOrigin(request.url());if(!origin){return;}
  21. let securityState=(request.securityState());if(request.mixedContentType===Protocol.Security.MixedContentType.Blockable||request.mixedContentType===Protocol.Security.MixedContentType.OptionallyBlockable){securityState=Protocol.Security.SecurityState.Insecure;}
  22. if(this._origins.has(origin)){const originState=this._origins.get(origin);const oldSecurityState=originState.securityState;originState.securityState=this._securityStateMin(oldSecurityState,securityState);if(oldSecurityState!==originState.securityState){const securityDetails=(request.securityDetails());if(securityDetails){originState.securityDetails=securityDetails;}
  23. this._sidebarTree.updateOrigin(origin,securityState);if(originState.originView){originState.originView.setSecurityState(securityState);}}}else{const originState={};originState.securityState=securityState;const securityDetails=request.securityDetails();if(securityDetails){originState.securityDetails=securityDetails;}
  24. originState.loadedFromCache=request.cached();this._origins.set(origin,originState);this._sidebarTree.addOrigin(origin,securityState);}}
  25. _onRequestFinished(event){const request=(event.data);this._updateFilterRequestCounts(request);this._processRequest(request);}
  26. _updateFilterRequestCounts(request){if(request.mixedContentType===Protocol.Security.MixedContentType.None){return;}
  27. let filterKey=Network.NetworkLogView.MixedContentFilterValues.All;if(request.wasBlocked()){filterKey=Network.NetworkLogView.MixedContentFilterValues.Blocked;}else if(request.mixedContentType===Protocol.Security.MixedContentType.Blockable){filterKey=Network.NetworkLogView.MixedContentFilterValues.BlockOverridden;}else if(request.mixedContentType===Protocol.Security.MixedContentType.OptionallyBlockable){filterKey=Network.NetworkLogView.MixedContentFilterValues.Displayed;}
  28. if(!this._filterRequestCounts.has(filterKey)){this._filterRequestCounts.set(filterKey,1);}else{this._filterRequestCounts.set(filterKey,this._filterRequestCounts.get(filterKey)+1);}
  29. this._mainView.refreshExplanations();}
  30. filterRequestCount(filterKey){return this._filterRequestCounts.get(filterKey)||0;}
  31. _securityStateMin(stateA,stateB){return Security.SecurityModel.SecurityStateComparator(stateA,stateB)<0?stateA:stateB;}
  32. modelAdded(securityModel){if(this._securityModel){return;}
  33. this._securityModel=securityModel;const resourceTreeModel=securityModel.resourceTreeModel();const networkManager=securityModel.networkManager();this._eventListeners=[resourceTreeModel.addEventListener(SDK.ResourceTreeModel.Events.MainFrameNavigated,this._onMainFrameNavigated,this),resourceTreeModel.addEventListener(SDK.ResourceTreeModel.Events.InterstitialShown,this._onInterstitialShown,this),resourceTreeModel.addEventListener(SDK.ResourceTreeModel.Events.InterstitialHidden,this._onInterstitialHidden,this),networkManager.addEventListener(SDK.NetworkManager.Events.ResponseReceived,this._onResponseReceived,this),networkManager.addEventListener(SDK.NetworkManager.Events.RequestFinished,this._onRequestFinished,this),];if(Root.Runtime.experiments.isEnabled('handleVisibleSecurityStateChanged')){this._eventListeners.push(securityModel.addEventListener(Security.SecurityModel.Events.VisibleSecurityStateChanged,this._onVisibleSecurityStateChanged,this));}else{this._eventListeners.push(securityModel.addEventListener(Security.SecurityModel.Events.SecurityStateChanged,this._onSecurityStateChanged,this));}
  34. if(resourceTreeModel.isInterstitialShowing()){this._onInterstitialShown();}}
  35. modelRemoved(securityModel){if(this._securityModel!==securityModel){return;}
  36. delete this._securityModel;Common.EventTarget.removeEventListeners(this._eventListeners);}
  37. _onMainFrameNavigated(event){const frame=(event.data);const request=this._lastResponseReceivedForLoaderId.get(frame.loaderId);this.selectAndSwitchToMainView();this._sidebarTree.clearOrigins();this._origins.clear();this._lastResponseReceivedForLoaderId.clear();this._filterRequestCounts.clear();this._mainView.refreshExplanations();const origin=Common.ParsedURL.extractOrigin(request?request.url():frame.url);this._sidebarTree.setMainOrigin(origin);if(request){this._processRequest(request);}}
  38. _onInterstitialShown(){this.selectAndSwitchToMainView();this._sidebarTree.toggleOriginsList(true);}
  39. _onInterstitialHidden(){this._sidebarTree.toggleOriginsList(false);}}
  40. export class SecurityPanelSidebarTree extends UI.TreeOutlineInShadow{constructor(mainViewElement,showOriginInPanel){super();this.registerRequiredCSS('security/sidebar.css');this.registerRequiredCSS('security/lockIcon.css');this.appendChild(mainViewElement);this._showOriginInPanel=showOriginInPanel;this._mainOrigin=null;this._originGroups=new Map();this._originGroupTitles=new Map([[Security.SecurityPanelSidebarTree.OriginGroup.MainOrigin,ls`Main origin`],[Security.SecurityPanelSidebarTree.OriginGroup.NonSecure,ls`Non-secure origins`],[Security.SecurityPanelSidebarTree.OriginGroup.Secure,ls`Secure origins`],[Security.SecurityPanelSidebarTree.OriginGroup.Unknown,ls`Unknown / canceled`],]);for(const key in Security.SecurityPanelSidebarTree.OriginGroup){const group=Security.SecurityPanelSidebarTree.OriginGroup[key];const element=this._createOriginGroupElement(this._originGroupTitles.get(group));this._originGroups.set(group,element);this.appendChild(element);}
  41. this._clearOriginGroups();const mainViewReloadMessage=new UI.TreeElement(Common.UIString('Reload to view details'));mainViewReloadMessage.selectable=false;mainViewReloadMessage.listItemElement.classList.add('security-main-view-reload-message');this._originGroups.get(Security.SecurityPanelSidebarTree.OriginGroup.MainOrigin).appendChild(mainViewReloadMessage);this._elementsByOrigin=new Map();}
  42. _createOriginGroupElement(originGroupTitle){const originGroup=new UI.TreeElement(originGroupTitle,true);originGroup.selectable=false;originGroup.setCollapsible(false);originGroup.expand();originGroup.listItemElement.classList.add('security-sidebar-origins');return originGroup;}
  43. toggleOriginsList(hidden){for(const element of this._originGroups.values()){element.hidden=hidden;}}
  44. addOrigin(origin,securityState){const originElement=new Security.SecurityPanelSidebarTreeElement(Security.SecurityPanel.createHighlightedUrl(origin,securityState),this._showOriginInPanel.bind(this,origin),'security-sidebar-tree-item','security-property');originElement.tooltip=origin;this._elementsByOrigin.set(origin,originElement);this.updateOrigin(origin,securityState);}
  45. setMainOrigin(origin){this._mainOrigin=origin;}
  46. updateOrigin(origin,securityState){const originElement=(this._elementsByOrigin.get(origin));originElement.setSecurityState(securityState);let newParent;if(origin===this._mainOrigin){newParent=this._originGroups.get(Security.SecurityPanelSidebarTree.OriginGroup.MainOrigin);if(securityState===Protocol.Security.SecurityState.Secure){newParent.title=ls`Main origin (secure)`;}else{newParent.title=ls`Main origin (non-secure)`;}}else{switch(securityState){case Protocol.Security.SecurityState.Secure:newParent=this._originGroups.get(Security.SecurityPanelSidebarTree.OriginGroup.Secure);break;case Protocol.Security.SecurityState.Unknown:newParent=this._originGroups.get(Security.SecurityPanelSidebarTree.OriginGroup.Unknown);break;default:newParent=this._originGroups.get(Security.SecurityPanelSidebarTree.OriginGroup.NonSecure);break;}}
  47. const oldParent=originElement.parent;if(oldParent!==newParent){if(oldParent){oldParent.removeChild(originElement);if(oldParent.childCount()===0){oldParent.hidden=true;}}
  48. newParent.appendChild(originElement);newParent.hidden=false;}}
  49. _clearOriginGroups(){for(const originGroup of this._originGroups.values()){originGroup.removeChildren();originGroup.hidden=true;}
  50. const mainOrigin=this._originGroups.get(Security.SecurityPanelSidebarTree.OriginGroup.MainOrigin);mainOrigin.title=this._originGroupTitles.get(Security.SecurityPanelSidebarTree.OriginGroup.MainOrigin);mainOrigin.hidden=false;}
  51. clearOrigins(){this._clearOriginGroups();this._elementsByOrigin.clear();}}
  52. export const OriginGroup={MainOrigin:Symbol('MainOrigin'),NonSecure:Symbol('NonSecure'),Secure:Symbol('Secure'),Unknown:Symbol('Unknown')};export class SecurityPanelSidebarTreeElement extends UI.TreeElement{constructor(textElement,selectCallback,className,cssPrefix){super('',false);this._selectCallback=selectCallback;this._cssPrefix=cssPrefix;this.listItemElement.classList.add(className);this._iconElement=this.listItemElement.createChild('div','icon');this._iconElement.classList.add(this._cssPrefix);this.listItemElement.appendChild(textElement);this.setSecurityState(Protocol.Security.SecurityState.Unknown);}
  53. static SecurityStateComparator(a,b){return Security.SecurityModel.SecurityStateComparator(a.securityState(),b.securityState());}
  54. setSecurityState(newSecurityState){if(this._securityState){this._iconElement.classList.remove(this._cssPrefix+'-'+this._securityState);}
  55. this._securityState=newSecurityState;this._iconElement.classList.add(this._cssPrefix+'-'+newSecurityState);}
  56. securityState(){return this._securityState;}
  57. onselect(){this._selectCallback();return true;}}
  58. export class SecurityMainView extends UI.VBox{constructor(panel){super(true);this.registerRequiredCSS('security/mainView.css');this.registerRequiredCSS('security/lockIcon.css');this.setMinimumSize(200,100);this.contentElement.classList.add('security-main-view');this._panel=panel;this._summarySection=this.contentElement.createChild('div','security-summary');this._securityExplanationsMain=this.contentElement.createChild('div','security-explanation-list security-explanations-main');this._securityExplanationsExtra=this.contentElement.createChild('div','security-explanation-list security-explanations-extra');const summaryDiv=this._summarySection.createChild('div','security-summary-section-title');summaryDiv.textContent=ls`Security overview`;UI.ARIAUtils.markAsHeading(summaryDiv,1);const lockSpectrum=this._summarySection.createChild('div','lock-spectrum');this._lockSpectrum=new Map([[Protocol.Security.SecurityState.Secure,lockSpectrum.createChild('div','lock-icon lock-icon-secure')],[Protocol.Security.SecurityState.Neutral,lockSpectrum.createChild('div','lock-icon lock-icon-neutral')],[Protocol.Security.SecurityState.Insecure,lockSpectrum.createChild('div','lock-icon lock-icon-insecure')],]);this._lockSpectrum.get(Protocol.Security.SecurityState.Secure).title=Common.UIString('Secure');this._lockSpectrum.get(Protocol.Security.SecurityState.Neutral).title=Common.UIString('Info');this._lockSpectrum.get(Protocol.Security.SecurityState.Insecure).title=Common.UIString('Not secure');this._summarySection.createChild('div','triangle-pointer-container').createChild('div','triangle-pointer-wrapper').createChild('div','triangle-pointer');this._summaryText=this._summarySection.createChild('div','security-summary-text');UI.ARIAUtils.markAsHeading(this._summaryText,2);}
  59. _addExplanation(parent,explanation){const explanationSection=parent.createChild('div','security-explanation');explanationSection.classList.add('security-explanation-'+explanation.securityState);explanationSection.createChild('div','security-property').classList.add('security-property-'+explanation.securityState);const text=explanationSection.createChild('div','security-explanation-text');const explanationHeader=text.createChild('div','security-explanation-title');if(explanation.title){explanationHeader.createChild('span').textContent=explanation.title+' - ';explanationHeader.createChild('span','security-explanation-title-'+explanation.securityState).textContent=explanation.summary;}else{explanationHeader.textContent=explanation.summary;}
  60. text.createChild('div').textContent=explanation.description;if(explanation.certificate.length){text.appendChild(Security.SecurityPanel.createCertificateViewerButtonForCert(Common.UIString('View certificate'),explanation.certificate));}
  61. if(explanation.recommendations&&explanation.recommendations.length){const recommendationList=text.createChild('ul','security-explanation-recommendations');for(const recommendation of explanation.recommendations){recommendationList.createChild('li').textContent=recommendation;}}
  62. return text;}
  63. updateSecurityState(newSecurityState,explanations,summary){this._summarySection.classList.remove('security-summary-'+this._securityState);this._securityState=newSecurityState;this._summarySection.classList.add('security-summary-'+this._securityState);const summaryExplanationStrings={'unknown':ls`The security of this page is unknown.`,'insecure':ls`This page is not secure.`,'neutral':ls`This page is not secure.`,'secure':ls`This page is secure (valid HTTPS).`,'insecure-broken':ls`This page is not secure (broken HTTPS).`};if(this._securityState===Protocol.Security.SecurityState.Insecure){this._lockSpectrum.get(Protocol.Security.SecurityState.Insecure).classList.add('lock-icon-insecure');this._lockSpectrum.get(Protocol.Security.SecurityState.Insecure).classList.remove('lock-icon-insecure-broken');this._lockSpectrum.get(Protocol.Security.SecurityState.Insecure).title=Common.UIString('Not secure');}else if(this._securityState===Protocol.Security.SecurityState.InsecureBroken){this._lockSpectrum.get(Protocol.Security.SecurityState.Insecure).classList.add('lock-icon-insecure-broken');this._lockSpectrum.get(Protocol.Security.SecurityState.Insecure).classList.remove('lock-icon-insecure');this._lockSpectrum.get(Protocol.Security.SecurityState.Insecure).title=Common.UIString('Not secure (broken)');}
  64. this._summaryText.textContent=summary||summaryExplanationStrings[this._securityState];this._explanations=explanations;this.refreshExplanations();}
  65. updateVisibleSecurityState(visibleSecurityState){this._summarySection.classList.remove('security-summary-'+this._securityState);this._securityState=visibleSecurityState.securityState;this._summarySection.classList.add('security-summary-'+this._securityState);if(this._securityState===Protocol.Security.SecurityState.Insecure){this._lockSpectrum.get(Protocol.Security.SecurityState.Insecure).classList.add('lock-icon-insecure');this._lockSpectrum.get(Protocol.Security.SecurityState.Insecure).classList.remove('lock-icon-insecure-broken');this._lockSpectrum.get(Protocol.Security.SecurityState.Insecure).title=ls`Not secure`;}else if(this._securityState===Protocol.Security.SecurityState.InsecureBroken){this._lockSpectrum.get(Protocol.Security.SecurityState.Insecure).classList.add('lock-icon-insecure-broken');this._lockSpectrum.get(Protocol.Security.SecurityState.Insecure).classList.remove('lock-icon-insecure');this._lockSpectrum.get(Protocol.Security.SecurityState.Insecure).title=ls`Not secure (broken)`;}
  66. const{summary,explanations}=this._getSecuritySummaryAndExplanations(visibleSecurityState);this._summaryText.textContent=summary||Security.SummaryMessages[this._securityState];this._explanations=this._orderExplanations(explanations);this.refreshExplanations();}
  67. _getSecuritySummaryAndExplanations(visibleSecurityState){const{securityState,securityStateIssueIds}=visibleSecurityState;let summary;const explanations=[];summary=this._explainSafetyTipSecurity(visibleSecurityState,summary,explanations);if(securityStateIssueIds.includes('malicious-content')){summary=ls`This page is dangerous (flagged by Google Safe Browsing).`;explanations.unshift(new Security.SecurityStyleExplanation(Protocol.Security.SecurityState.Insecure,undefined,ls`Flagged by Google Safe Browsing`,ls`To check this page's status, visit g.co/safebrowsingstatus.`));}else if(securityStateIssueIds.includes('is-error-page')&&(visibleSecurityState.certificateSecurityState===null||visibleSecurityState.certificateSecurityState.certificateNetworkError===null)){summary=ls`This is an error page.`;return{summary,explanations};}else if(securityState===Protocol.Security.SecurityState.InsecureBroken&&securityStateIssueIds.includes('scheme-is-not-cryptographic')){summary=summary||ls`This page is insecure (unencrypted HTTP).`;if(securityStateIssueIds.includes('insecure-input-events')){explanations.push(new Security.SecurityStyleExplanation(Protocol.Security.SecurityState.Insecure,undefined,ls`Form field edited on a non-secure page`,ls`Data was entered in a field on a non-secure page. A warning has been added to the URL bar.`));}}
  68. if(securityStateIssueIds.includes('scheme-is-not-cryptographic')){if(securityState===Protocol.Security.SecurityState.Neutral&&!securityStateIssueIds.includes('insecure-origin')){summary=ls`This page has a non-HTTPS secure origin.`;}
  69. return{summary,explanations};}
  70. this._explainCertificateSecurity(visibleSecurityState,explanations);this._explainConnectionSecurity(visibleSecurityState,explanations);this._explainContentSecurity(visibleSecurityState,explanations);return{summary,explanations};}
  71. _explainSafetyTipSecurity(visibleSecurityState,summary,explanations){const{securityStateIssueIds,safetyTipInfo}=visibleSecurityState;const currentExplanations=[];if(securityStateIssueIds.includes('bad_reputation')){currentExplanations.push({summary:ls`This page is suspicious`,description:ls`Chrome has determined that this site could be fake or fraudulent.\n\nIf you believe this is shown in error please visit https://bugs.chromium.org/p/chromium/issues/entry?template=Safety+Tips+Appeals.`});}else if(securityStateIssueIds.includes('lookalike')&&safetyTipInfo.safeUrl){currentExplanations.push({summary:ls`Possible spoofing URL`,description:ls`This site's hostname looks similar to ${
  72. new URL(safetyTipInfo.safeUrl)
  73. .hostname}. Attackers sometimes mimic sites by making small, hard-to-see changes to the domain name.\n\nIf you believe this is shown in error please visit https://bugs.chromium.org/p/chromium/issues/entry?template=Safety+Tips+Appeals.`});}
  74. if(currentExplanations.length>0){summary=summary||ls`This page is suspicious (flagged by Chrome).`;explanations.push(new Security.SecurityStyleExplanation(Protocol.Security.SecurityState.Insecure,undefined,currentExplanations[0].summary,currentExplanations[0].description));}
  75. return summary;}
  76. _explainCertificateSecurity(visibleSecurityState,explanations){const{certificateSecurityState,securityStateIssueIds}=visibleSecurityState;const title=ls`Certificate`;if(certificateSecurityState&&certificateSecurityState.certificateHasSha1Signature){const explanationSummary=ls`insecure (SHA-1)`;const description=ls`The certificate chain for this site contains a certificate signed using SHA-1.`;if(certificateSecurityState.certificateHasWeakSignature){explanations.push(new Security.SecurityStyleExplanation(Protocol.Security.SecurityState.Insecure,title,explanationSummary,description,certificateSecurityState.certificate,Protocol.Security.MixedContentType.None));}else{explanations.push(new Security.SecurityStyleExplanation(Protocol.Security.SecurityState.Neutral,title,explanationSummary,description,certificateSecurityState.certificate,Protocol.Security.MixedContentType.None));}}
  77. if(certificateSecurityState&&securityStateIssueIds.includes('cert-missing-subject-alt-name')){explanations.push(new Security.SecurityStyleExplanation(Protocol.Security.SecurityState.Insecure,title,ls`Subject Alternative Name missing`,ls`The certificate for this site does not contain a Subject Alternative Name extension containing a domain name or IP address.`,certificateSecurityState.certificate,Protocol.Security.MixedContentType.None));}
  78. if(certificateSecurityState&&certificateSecurityState.certificateNetworkError!==null){explanations.push(new Security.SecurityStyleExplanation(Protocol.Security.SecurityState.Insecure,title,ls`missing`,ls`This site is missing a valid, trusted certificate (${certificateSecurityState.certificateNetworkError}).`,certificateSecurityState.certificate,Protocol.Security.MixedContentType.None));}else if(certificateSecurityState&&!certificateSecurityState.certificateHasSha1Signature){explanations.push(new Security.SecurityStyleExplanation(Protocol.Security.SecurityState.Secure,title,ls`valid and trusted`,ls`The connection to this site is using a valid, trusted server certificate issued by ${
  79. certificateSecurityState.issuer}.`,certificateSecurityState.certificate,Protocol.Security.MixedContentType.None));}
  80. if(securityStateIssueIds.includes('pkp-bypassed')){explanations.push(new Security.SecurityStyleExplanation(Protocol.Security.SecurityState.Info,title,ls`Public-Key-Pinning bypassed`,ls`Public-Key-Pinning was bypassed by a local root certificate.`));}
  81. if(certificateSecurityState&&certificateSecurityState.isCertificateExpiringSoon()){explanations.push(new Security.SecurityStyleExplanation(Protocol.Security.SecurityState.Info,undefined,ls`Certificate expires soon`,ls`The certificate for this site expires in less than 48 hours and needs to be renewed.`));}}
  82. _explainConnectionSecurity(visibleSecurityState,explanations){const certificateSecurityState=visibleSecurityState.certificateSecurityState;if(!certificateSecurityState){return;}
  83. const title=ls`Connection`;if(certificateSecurityState.modernSSL){explanations.push(new Security.SecurityStyleExplanation(Protocol.Security.SecurityState.Secure,title,ls`secure connection settings`,ls`The connection to this site is encrypted and authenticated using ${certificateSecurityState.protocol}, ${
  84. certificateSecurityState.getKeyExchangeName()}, and ${certificateSecurityState.getCipherFullName()}.`));return;}
  85. const recommendations=[];if(certificateSecurityState.obsoleteSslProtocol){recommendations.push(ls`${certificateSecurityState.protocol} is obsolete. Enable TLS 1.2 or later.`);}
  86. if(certificateSecurityState.obsoleteSslKeyExchange){recommendations.push(ls`RSA key exchange is obsolete. Enable an ECDHE-based cipher suite.`);}
  87. if(certificateSecurityState.obsoleteSslCipher){recommendations.push(ls`${certificateSecurityState.cipher} is obsolete. Enable an AES-GCM-based cipher suite.`);}
  88. if(certificateSecurityState.obsoleteSslSignature){recommendations.push(ls`The server signature uses SHA-1, which is obsolete. Enable a SHA-2 signature algorithm instead. (Note this is different from the signature in the certificate.)`);}
  89. explanations.push(new Security.SecurityStyleExplanation(Protocol.Security.SecurityState.Info,title,ls`obsolete connection settings`,ls`The connection to this site is encrypted and authenticated using ${certificateSecurityState.protocol}, ${
  90. certificateSecurityState.getKeyExchangeName()}, and ${certificateSecurityState.getCipherFullName()}.`,undefined,undefined,recommendations));}
  91. _explainContentSecurity(visibleSecurityState,explanations){let addSecureExplanation=true;const title=ls`Resources`;const securityStateIssueIds=visibleSecurityState.securityStateIssueIds;if(securityStateIssueIds.includes('ran-mixed-content')){addSecureExplanation=false;explanations.push(new Security.SecurityStyleExplanation(Protocol.Security.SecurityState.Insecure,title,ls`active mixed content`,ls`You have recently allowed non-secure content (such as scripts or iframes) to run on this site.`,[],Protocol.Security.MixedContentType.Blockable));}
  92. if(securityStateIssueIds.includes('displayed-mixed-content')){addSecureExplanation=false;explanations.push(new Security.SecurityStyleExplanation(Protocol.Security.SecurityState.Neutral,title,ls`mixed content`,ls`This page includes HTTP resources.`,[],Protocol.Security.MixedContentType.OptionallyBlockable));}
  93. if(securityStateIssueIds.includes('contained-mixed-form')){addSecureExplanation=false;explanations.push(new Security.SecurityStyleExplanation(Protocol.Security.SecurityState.Neutral,title,ls`non-secure form`,ls`This page includes a form with a non-secure "action" attribute.`));}
  94. if(visibleSecurityState.certificateSecurityState===null||visibleSecurityState.certificateSecurityState.certificateNetworkError===null){if(securityStateIssueIds.includes('ran-content-with-cert-error')){addSecureExplanation=false;explanations.push(new Security.SecurityStyleExplanation(Protocol.Security.SecurityState.Insecure,title,ls`active content with certificate errors`,ls`You have recently allowed content loaded with certificate errors (such as scripts or iframes) to run on this site.`));}
  95. if(securityStateIssueIds.includes('displayed-content-with-cert-errors')){addSecureExplanation=false;explanations.push(new Security.SecurityStyleExplanation(Protocol.Security.SecurityState.Neutral,title,ls`content with certificate errors`,ls`This page includes resources that were loaded with certificate errors.`));}}
  96. if(addSecureExplanation){if(!securityStateIssueIds.includes('scheme-is-not-cryptographic')){explanations.push(new Security.SecurityStyleExplanation(Protocol.Security.SecurityState.Secure,title,ls`all served securely`,ls`All resources on this page are served securely.`));}}}
  97. _orderExplanations(explanations){if(explanations.length===0){return explanations;}
  98. const securityStateOrder=[Protocol.Security.SecurityState.Insecure,Protocol.Security.SecurityState.Neutral,Protocol.Security.SecurityState.Secure,Protocol.Security.SecurityState.Info];const orderedExplanations=[];securityStateOrder.forEach(securityState=>orderedExplanations.push(...explanations.filter(explanation=>explanation.securityState===securityState)));return orderedExplanations;}
  99. refreshExplanations(){this._securityExplanationsMain.removeChildren();this._securityExplanationsExtra.removeChildren();for(const explanation of this._explanations){if(explanation.securityState===Protocol.Security.SecurityState.Info){this._addExplanation(this._securityExplanationsExtra,explanation);}else{switch(explanation.mixedContentType){case Protocol.Security.MixedContentType.Blockable:this._addMixedContentExplanation(this._securityExplanationsMain,explanation,Network.NetworkLogView.MixedContentFilterValues.BlockOverridden);break;case Protocol.Security.MixedContentType.OptionallyBlockable:this._addMixedContentExplanation(this._securityExplanationsMain,explanation,Network.NetworkLogView.MixedContentFilterValues.Displayed);break;default:this._addExplanation(this._securityExplanationsMain,explanation);break;}}}
  100. if(this._panel.filterRequestCount(Network.NetworkLogView.MixedContentFilterValues.Blocked)>0){const explanation=({securityState:Protocol.Security.SecurityState.Info,summary:Common.UIString('Blocked mixed content'),description:Common.UIString('Your page requested non-secure resources that were blocked.'),mixedContentType:Protocol.Security.MixedContentType.Blockable,certificate:[]});this._addMixedContentExplanation(this._securityExplanationsMain,explanation,Network.NetworkLogView.MixedContentFilterValues.Blocked);}}
  101. _addMixedContentExplanation(parent,explanation,filterKey){const element=this._addExplanation(parent,explanation);const filterRequestCount=this._panel.filterRequestCount(filterKey);if(!filterRequestCount){const refreshPrompt=element.createChild('div','security-mixed-content');refreshPrompt.textContent=Common.UIString('Reload the page to record requests for HTTP resources.');return;}
  102. const requestsAnchor=element.createChild('div','security-mixed-content devtools-link');UI.ARIAUtils.markAsLink(requestsAnchor);requestsAnchor.tabIndex=0;if(filterRequestCount===1){requestsAnchor.textContent=Common.UIString('View %d request in Network Panel',filterRequestCount);}else{requestsAnchor.textContent=Common.UIString('View %d requests in Network Panel',filterRequestCount);}
  103. requestsAnchor.addEventListener('click',this.showNetworkFilter.bind(this,filterKey));requestsAnchor.addEventListener('keydown',event=>{if(isEnterKey(event)){this.showNetworkFilter(filterKey,event);}});}
  104. showNetworkFilter(filterKey,e){e.consume();Network.NetworkPanel.revealAndFilter([{filterType:Network.NetworkLogView.FilterType.MixedContent,filterValue:filterKey}]);}}
  105. export class SecurityOriginView extends UI.VBox{constructor(panel,origin,originState){super();this._panel=panel;this.setMinimumSize(200,100);this.element.classList.add('security-origin-view');this.registerRequiredCSS('security/originView.css');this.registerRequiredCSS('security/lockIcon.css');const titleSection=this.element.createChild('div','title-section');const titleDiv=titleSection.createChild('div','title-section-header');titleDiv.textContent=ls`Origin`;UI.ARIAUtils.markAsHeading(titleDiv,1);const originDisplay=titleSection.createChild('div','origin-display');this._originLockIcon=originDisplay.createChild('span','security-property');this._originLockIcon.classList.add('security-property-'+originState.securityState);originDisplay.appendChild(Security.SecurityPanel.createHighlightedUrl(origin,originState.securityState));const originNetworkDiv=titleSection.createChild('div','view-network-button');const originNetworkLink=originNetworkDiv.createChild('span','devtools-link origin-button');originNetworkLink.textContent=ls`View requests in Network Panel`;originNetworkLink.addEventListener('click',e=>{e.consume();const parsedURL=new Common.ParsedURL(origin);Network.NetworkPanel.revealAndFilter([{filterType:Network.NetworkLogView.FilterType.Domain,filterValue:parsedURL.host},{filterType:Network.NetworkLogView.FilterType.Scheme,filterValue:parsedURL.scheme}]);});UI.ARIAUtils.markAsLink(originNetworkLink);if(originState.securityDetails){const connectionSection=this.element.createChild('div','origin-view-section');const connectionDiv=connectionSection.createChild('div','origin-view-section-title');connectionDiv.textContent=ls`Connection`;UI.ARIAUtils.markAsHeading(connectionDiv,2);let table=new Security.SecurityDetailsTable();connectionSection.appendChild(table.element());table.addRow(Common.UIString('Protocol'),originState.securityDetails.protocol);if(originState.securityDetails.keyExchange){table.addRow(Common.UIString('Key exchange'),originState.securityDetails.keyExchange);}
  106. if(originState.securityDetails.keyExchangeGroup){table.addRow(Common.UIString('Key exchange group'),originState.securityDetails.keyExchangeGroup);}
  107. table.addRow(Common.UIString('Cipher'),originState.securityDetails.cipher+
  108. (originState.securityDetails.mac?' with '+originState.securityDetails.mac:''));const certificateSection=this.element.createChild('div','origin-view-section');const certificateDiv=certificateSection.createChild('div','origin-view-section-title');certificateDiv.textContent=ls`Certificate`;UI.ARIAUtils.markAsHeading(certificateDiv,2);const sctListLength=originState.securityDetails.signedCertificateTimestampList.length;const ctCompliance=originState.securityDetails.certificateTransparencyCompliance;let sctSection;if(sctListLength||ctCompliance!==Protocol.Network.CertificateTransparencyCompliance.Unknown){sctSection=this.element.createChild('div','origin-view-section');const sctDiv=sctSection.createChild('div','origin-view-section-title');sctDiv.textContent=ls`Certificate Transparency`;UI.ARIAUtils.markAsHeading(sctDiv,2);}
  109. const sanDiv=this._createSanDiv(originState.securityDetails.sanList);const validFromString=new Date(1000*originState.securityDetails.validFrom).toUTCString();const validUntilString=new Date(1000*originState.securityDetails.validTo).toUTCString();table=new Security.SecurityDetailsTable();certificateSection.appendChild(table.element());table.addRow(Common.UIString('Subject'),originState.securityDetails.subjectName);table.addRow(Common.UIString('SAN'),sanDiv);table.addRow(Common.UIString('Valid from'),validFromString);table.addRow(Common.UIString('Valid until'),validUntilString);table.addRow(Common.UIString('Issuer'),originState.securityDetails.issuer);table.addRow('',Security.SecurityPanel.createCertificateViewerButtonForOrigin(Common.UIString('Open full certificate details'),origin));if(!sctSection){return;}
  110. const sctSummaryTable=new Security.SecurityDetailsTable();sctSummaryTable.element().classList.add('sct-summary');sctSection.appendChild(sctSummaryTable.element());for(let i=0;i<sctListLength;i++){const sct=originState.securityDetails.signedCertificateTimestampList[i];sctSummaryTable.addRow(Common.UIString('SCT'),sct.logDescription+' ('+sct.origin+', '+sct.status+')');}
  111. const sctTableWrapper=sctSection.createChild('div','sct-details');sctTableWrapper.classList.add('hidden');for(let i=0;i<sctListLength;i++){const sctTable=new Security.SecurityDetailsTable();sctTableWrapper.appendChild(sctTable.element());const sct=originState.securityDetails.signedCertificateTimestampList[i];sctTable.addRow(Common.UIString('Log name'),sct.logDescription);sctTable.addRow(Common.UIString('Log ID'),sct.logId.replace(/(.{2})/g,'$1 '));sctTable.addRow(Common.UIString('Validation status'),sct.status);sctTable.addRow(Common.UIString('Source'),sct.origin);sctTable.addRow(Common.UIString('Issued at'),new Date(sct.timestamp).toUTCString());sctTable.addRow(Common.UIString('Hash algorithm'),sct.hashAlgorithm);sctTable.addRow(Common.UIString('Signature algorithm'),sct.signatureAlgorithm);sctTable.addRow(Common.UIString('Signature data'),sct.signatureData.replace(/(.{2})/g,'$1 '));}
  112. if(sctListLength){function toggleSctDetailsDisplay(){let buttonText;const isDetailsShown=!sctTableWrapper.classList.contains('hidden');if(isDetailsShown){buttonText=ls`Show full details`;}else{buttonText=ls`Hide full details`;}
  113. toggleSctsDetailsLink.textContent=buttonText;UI.ARIAUtils.setAccessibleName(toggleSctsDetailsLink,buttonText);UI.ARIAUtils.setExpanded(toggleSctsDetailsLink,!isDetailsShown);sctSummaryTable.element().classList.toggle('hidden');sctTableWrapper.classList.toggle('hidden');}
  114. const toggleSctsDetailsLink=UI.createTextButton(ls`Show full details`,toggleSctDetailsDisplay,'details-toggle');sctSection.appendChild(toggleSctsDetailsLink);}
  115. switch(ctCompliance){case Protocol.Network.CertificateTransparencyCompliance.Compliant:sctSection.createChild('div','origin-view-section-notes').textContent=Common.UIString('This request complies with Chrome\'s Certificate Transparency policy.');break;case Protocol.Network.CertificateTransparencyCompliance.NotCompliant:sctSection.createChild('div','origin-view-section-notes').textContent=Common.UIString('This request does not comply with Chrome\'s Certificate Transparency policy.');break;case Protocol.Network.CertificateTransparencyCompliance.Unknown:break;}
  116. const noteSection=this.element.createChild('div','origin-view-section origin-view-notes');if(originState.loadedFromCache){noteSection.createChild('div').textContent=Common.UIString('This response was loaded from cache. Some security details might be missing.');}
  117. noteSection.createChild('div').textContent=Common.UIString('The security details above are from the first inspected response.');}else if(originState.securityState===Protocol.Security.SecurityState.Secure){const secureSection=this.element.createChild('div','origin-view-section');const secureDiv=secureSection.createChild('div','origin-view-section-title');secureDiv.textContent=ls`Secure`;UI.ARIAUtils.markAsHeading(secureDiv,2);secureSection.createChild('div').textContent=ls`This origin is a non-HTTPS secure origin.`;}else if(originState.securityState!==Protocol.Security.SecurityState.Unknown){const notSecureSection=this.element.createChild('div','origin-view-section');const notSecureDiv=notSecureSection.createChild('div','origin-view-section-title');notSecureDiv.textContent=ls`Not secure`;UI.ARIAUtils.markAsHeading(notSecureDiv,2);notSecureSection.createChild('div').textContent=Common.UIString('Your connection to this origin is not secure.');}else{const noInfoSection=this.element.createChild('div','origin-view-section');const noInfoDiv=noInfoSection.createChild('div','origin-view-section-title');noInfoDiv.textContent=ls`No security information`;UI.ARIAUtils.markAsHeading(noInfoDiv,2);noInfoSection.createChild('div').textContent=Common.UIString('No security details are available for this origin.');}}
  118. _createSanDiv(sanList){const sanDiv=createElement('div');if(sanList.length===0){sanDiv.textContent=Common.UIString('(n/a)');sanDiv.classList.add('empty-san');}else{const truncatedNumToShow=2;const listIsTruncated=sanList.length>truncatedNumToShow+1;for(let i=0;i<sanList.length;i++){const span=sanDiv.createChild('span','san-entry');span.textContent=sanList[i];if(listIsTruncated&&i>=truncatedNumToShow){span.classList.add('truncated-entry');}}
  119. if(listIsTruncated){function toggleSANTruncation(){const isTruncated=sanDiv.classList.contains('truncated-san');let buttonText;if(isTruncated){sanDiv.classList.remove('truncated-san');buttonText=ls`Show less`;}else{sanDiv.classList.add('truncated-san');buttonText=ls`Show more (${sanList.length} total)`;}
  120. truncatedSANToggle.textContent=buttonText;UI.ARIAUtils.setAccessibleName(truncatedSANToggle,buttonText);UI.ARIAUtils.setExpanded(truncatedSANToggle,isTruncated);}
  121. const truncatedSANToggle=UI.createTextButton(ls`Show more (${sanList.length} total)`,toggleSANTruncation);sanDiv.appendChild(truncatedSANToggle);toggleSANTruncation();}}
  122. return sanDiv;}
  123. setSecurityState(newSecurityState){for(const className of Array.prototype.slice.call(this._originLockIcon.classList)){if(className.startsWith('security-property-')){this._originLockIcon.classList.remove(className);}}
  124. this._originLockIcon.classList.add('security-property-'+newSecurityState);}}
  125. export class SecurityDetailsTable{constructor(){this._element=createElement('table');this._element.classList.add('details-table');}
  126. element(){return this._element;}
  127. addRow(key,value){const row=this._element.createChild('div','details-table-row');row.createChild('div').textContent=key;const valueDiv=row.createChild('div');if(typeof value==='string'){valueDiv.textContent=value;}else{valueDiv.appendChild(value);}}}
  128. self.Security=self.Security||{};Security=Security||{};Security.SecurityPanel=SecurityPanel;Security.SecurityPanel.Origin;Security.SecurityPanel.OriginState;Security.SecurityPanelSidebarTree=SecurityPanelSidebarTree;Security.SecurityPanelSidebarTree.OriginGroup=OriginGroup;Security.SecurityPanelSidebarTreeElement=SecurityPanelSidebarTreeElement;Security.SecurityMainView=SecurityMainView;Security.SecurityOriginView=SecurityOriginView;Security.SecurityDetailsTable=SecurityDetailsTable;