Index: third_party/WebKit/Source/devtools/front_end/security/SecurityPanel.js |
diff --git a/third_party/WebKit/Source/devtools/front_end/security/SecurityPanel.js b/third_party/WebKit/Source/devtools/front_end/security/SecurityPanel.js |
index 2bc3e5f51ccdabe1cbaf2892e73c393dae294821..9e015868b06bbc841af5be6cdc46785b6ebe43a9 100644 |
--- a/third_party/WebKit/Source/devtools/front_end/security/SecurityPanel.js |
+++ b/third_party/WebKit/Source/devtools/front_end/security/SecurityPanel.js |
@@ -44,6 +44,7 @@ WebInspector.SecurityPanel.Origin; |
* @property {?NetworkAgent.SecurityDetails} securityDetails - Security details of the origin, if available. |
* @property {?Promise<!NetworkAgent.CertificateDetails>} certificateDetailsPromise - Certificate details of the origin. Only available if securityDetails are available. |
* @property {?WebInspector.SecurityOriginView} originView - Current SecurityOriginView corresponding to origin. |
+ * @property {?WebInspector.CSPParser} cspDetails - Content Security Policy details |
*/ |
WebInspector.SecurityPanel.OriginState; |
@@ -180,6 +181,17 @@ WebInspector.SecurityPanel.prototype = { |
originState.certificateDetailsPromise = request.target().networkManager.certificateDetailsPromise(securityDetails.certificateId); |
} |
+ if (Runtime.experiments.isEnabled("cspDetailsInSecurityPanel")) { |
+ var cspHeader = request.responseHeaders.filter(header => header.name.toLowerCase().includes("content-security-policy"))[0]; |
+ if (cspHeader) { |
+ originState.cspDetails = |
+ WebInspector.CSPParser.buildFromString( |
+ cspHeader.value, |
+ cspHeader.name.toLowerCase().includes("report-only") |
+ ); |
+ } |
+ } |
+ |
this._origins.set(origin, originState); |
this._sidebarTree.addOrigin(origin, securityState); |
@@ -821,6 +833,125 @@ WebInspector.SecurityOriginView = function(panel, origin, originState) |
noInfoSection.createChild("div", "origin-view-section-title").textContent = WebInspector.UIString("No Security Information"); |
noInfoSection.createChild("div").textContent = WebInspector.UIString("No security details are available for this origin."); |
} |
+ |
+ if (Runtime.experiments.isEnabled("cspDetailsInSecurityPanel") && originState.cspDetails) { |
+ let contentSecurityPolicySection = this.element.createChild("div", "origin-view-section"); |
+ contentSecurityPolicySection.createChild("div", "origin-view-section-title").textContent = WebInspector.UIString("Content Security Policy"); |
+ let toggleButton = createElement("button"); |
+ toggleButton.textContent = "Collapse All Policies"; |
+ toggleButton.dataset.showing = true; |
+ toggleButton.className = "collapse-policies"; |
+ contentSecurityPolicySection.appendChild(toggleButton); |
+ |
+ let reportUri = createElement("p"); |
+ reportUri.innerHTML = `<b>Report URI</b> <span>${originState.cspDetails.reportUri ? originState.cspDetails.reportUri : "Not Set"}</span>`; |
+ contentSecurityPolicySection.appendChild(reportUri); |
+ |
+ toggleButton.addEventListener("click", () => { |
+ let headings = contentSecurityPolicySection.querySelectorAll(".csp-rule-heading"); |
+ if (toggleButton.dataset.showing === "true") { |
+ Array.prototype.forEach.call(headings, item => { |
+ item.closeSection(); |
+ }); |
+ toggleButton.textContent = "Show All Policies"; |
+ toggleButton.dataset.showing = false; |
+ } else { |
+ Array.prototype.forEach.call(headings, item => { |
+ item.showSection(); |
+ }); |
+ toggleButton.textContent = "Collapse Policies"; |
+ toggleButton.dataset.showing = true; |
+ } |
+ }); |
+ |
+ /** |
+ * Create the sections for the CSP display. |
+ * |
+ * @param {!WebInspector.CSPParser.Policy} policy The CSP Policy to create a section for. |
+ */ |
+ function createPolicySection(policy) { |
+ |
+ // Since for some reason a blank policy may get made sometimes. |
+ if (!policy.name) { |
+ return; |
+ } |
+ |
+ /** |
+ * Create table row for a CSP Rule |
+ * @param {!WebInspector.CSPParser.Rule} rule The rule to create a table entry for. |
+ * @return {string} |
+ */ |
+ function createRuleRow(rule) { |
+ return ` |
+ <tr class="csp-rule csp-security-state-${rule.securityState}"> |
+ <th><span class="lock-icon lock-icon-${rule.securityState}"></span><p>${rule.value}</p></th> |
+ <td> |
+ <p><b>Security Level</b> <span>${rule.humanReadableSecurityState}</span></p> |
+ ${rule.reason ? `<p><b>Reason</b> <span>${rule.reason}</span></p>` : ""} |
+ <p><b>Report Only</b> <span>${rule.reportOnly ? "Yes" : "No"}</span></p> |
+ </td> |
+ </tr>`; |
+ } |
+ let heading = createElement("h4"); |
+ heading.dataset.rule = policy.name; |
+ heading.className = `csp-rule csp-rule-heading csp-security-state-${policy.securityState}`; |
+ heading.textContent = policy.name; |
+ heading.innerHTML += `<span class="icon"></span>`; |
+ contentSecurityPolicySection.appendChild(heading); |
+ |
+ let policyStateArea = createElement("p"); |
+ policyStateArea.innerHTML = `<b>Policy state</b> ${policy.humanReadableSecurityState}`; |
+ if (policy.securityStateReason) { |
+ policyStateArea.innerHTML += `<br><b>Reason</b> ${policy.securityStateReason}`; |
+ } |
+ contentSecurityPolicySection.appendChild(policyStateArea); |
+ |
+ let table = createElementWithClass("table", `csp-directive csp-rule-list-${policy.name}`); |
+ table.innerHTML += ` |
+ <tbody> |
+ ${policy.rules.sort((a, b) => a.value.localeCompare(b.value)).map(createRuleRow).join('')} |
+ </tbody> |
+ </table>`; |
+ contentSecurityPolicySection.appendChild(table); |
+ |
+ /** |
+ * @this {Element} |
+ */ |
+ heading.toggleSection = function() { |
+ contentSecurityPolicySection |
+ .querySelector(`.csp-rule-list-${policy.name}`) |
+ .classList |
+ .toggle("csp-directive_hidden"); |
+ this.classList.toggle("directive-is-hidden"); |
+ }; |
+ /** |
+ * @this {Element} |
+ */ |
+ heading.closeSection = function() { |
+ contentSecurityPolicySection |
+ .querySelector(`.csp-rule-list-${policy.name}`) |
+ .classList |
+ .add("csp-directive_hidden"); |
+ this.classList.add("directive-is-hidden"); |
+ }; |
+ /** |
+ * @this {Element} |
+ */ |
+ heading.showSection = function () { |
+ contentSecurityPolicySection |
+ .querySelector(`.csp-rule-list-${policy.name}`) |
+ .classList |
+ .remove("csp-directive_hidden"); |
+ this.classList.remove("directive-is-hidden"); |
+ }; |
+ heading.addEventListener("click", event => { |
+ if (event.target.toggleSection) |
+ event.target.toggleSection(); |
+ }); |
+ } |
+ |
+ originState.cspDetails.policies.forEach(createPolicySection); |
+ } |
} |
WebInspector.SecurityOriginView.prototype = { |