/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ /** * The Security devtool supports the following arguments: * * Security CSP * Provides feedback about the current CSP * * * Security referrer * Provides information about the current referrer policy */ "use strict"; const { Cc, Ci, Cu, CC } = require("chrome"); const l10n = require("gcli/l10n"); const CSP = Cc["@mozilla.org/cspcontext;1"].getService(Ci.nsIContentSecurityPolicy); const GOOD_IMG_SRC = "chrome://browser/content/gcli_sec_good.svg"; const MOD_IMG_SRC = "chrome://browser/content/gcli_sec_moderate.svg"; const BAD_IMG_SRC = "chrome://browser/content/gcli_sec_bad.svg"; // special handling within policy const POLICY_REPORT_ONLY = "report-only" // special handling of directives const DIR_UPGRADE_INSECURE = "upgrade-insecure-requests"; const DIR_BLOCK_ALL_MIXED_CONTENT = "block-all-mixed-content"; // special handling of sources const SRC_UNSAFE_INLINE = "'unsafe-inline'"; const SRC_UNSAFE_EVAL = "'unsafe-eval'"; const WILDCARD_MSG = l10n.lookup("securityCSPRemWildCard"); const XSS_WARNING_MSG = l10n.lookup("securityCSPPotentialXSS"); const NO_CSP_ON_PAGE_MSG = l10n.lookup("securityCSPNoCSPOnPage"); const CONTENT_SECURITY_POLICY_MSG = l10n.lookup("securityCSPHeaderOnPage"); const CONTENT_SECURITY_POLICY_REPORT_ONLY_MSG = l10n.lookup("securityCSPROHeaderOnPage"); const NEXT_URI_HEADER = l10n.lookup("securityReferrerNextURI"); const CALCULATED_REFERRER_HEADER = l10n.lookup("securityReferrerCalculatedReferrer"); /* The official names from the W3C Referrer Policy Draft http://www.w3.org/TR/referrer-policy/ */ const REFERRER_POLICY_NAMES = [ "None When Downgrade (default)", "None", "Origin Only", "Origin When Cross-Origin", "Unsafe URL" ]; exports.items = [ { // --- General Security information name: "security", description: l10n.lookup("securityPrivacyDesc"), manual: l10n.lookup("securityManual") }, { // --- CSP specific Security information item: "command", runAt: "server", name: "security csp", description: l10n.lookup("securityCSPDesc"), manual: l10n.lookup("securityCSPManual"), returnType: "securityCSPInfo", exec: function(args, context) { var cspJSON = context.environment.document.nodePrincipal.cspJSON; var cspOBJ = JSON.parse(cspJSON); var outPolicies = []; var policies = cspOBJ["csp-policies"]; // loop over all the different policies for (var csp in policies) { var curPolicy = policies[csp]; // loop over all the directive-values within that policy var outDirectives = []; var outHeader = CONTENT_SECURITY_POLICY_MSG; for (var dir in curPolicy) { var curDir = curPolicy[dir]; // when iterating properties within the obj we might also // encounter the 'report-only' flag, which is not a csp directive. if (dir === POLICY_REPORT_ONLY) { outHeader = curPolicy[POLICY_REPORT_ONLY] === true ? CONTENT_SECURITY_POLICY_REPORT_ONLY_MSG : CONTENT_SECURITY_POLICY_MSG; continue; } // loop over all the directive-sources within that directive var outSrcs = []; // special case handling for the directives // upgrade-insecure-requests and block-all-mixed-content // which do not include any srcs if (dir === DIR_UPGRADE_INSECURE || dir === DIR_BLOCK_ALL_MIXED_CONTENT) { outSrcs.push({ icon: GOOD_IMG_SRC, src: "", // no src desc: "" // no description }); } for (var src in curDir) { var curSrc = curDir[src]; // the default icon and descritpion of the directive-src var outIcon = GOOD_IMG_SRC; var outDesc = ""; if (curSrc.indexOf("*") > -1) { outIcon = MOD_IMG_SRC; outDesc = WILDCARD_MSG; } if (curSrc == SRC_UNSAFE_INLINE || curSrc == SRC_UNSAFE_EVAL) { outIcon = BAD_IMG_SRC; outDesc = XSS_WARNING_MSG; } outSrcs.push({ icon: outIcon, src: curSrc, desc: outDesc }); } // append info about that directive to the directives array outDirectives.push({ dirValue: dir, dirSrc: outSrcs }); } // append info about the policy to the policies array outPolicies.push({ header: outHeader, directives: outDirectives }); } return outPolicies; } }, { item: "converter", from: "securityCSPInfo", to: "view", exec: function(cspInfo, context) { var url = context.environment.target.url; if (cspInfo.length == 0) { return context.createView({ html: "" + " " + " " + " " + " " + "
" + NO_CSP_ON_PAGE_MSG + " " + url + "
"}); } return context.createView({ html: "" + // iterate all policies " " + " " + " " + "
${csp.header} " + url + "

" + " " + // >> iterate all directives " " + " " + " " + " " + "
${dir.dirValue} " + " " + // >> >> iterate all srs " " + " " + " " + " " + " " + "
${src.src} ${src.desc}
" + "
" + "
", data: { cspinfo: cspInfo, } }); } }, { // --- Referrer Policy specific Security information item: "command", runAt: "server", name: "security referrer", description: l10n.lookup("securityReferrerPolicyDesc"), manual: l10n.lookup("securityReferrerPolicyManual"), returnType: "securityReferrerPolicyInfo", exec: function(args, context) { var doc = context.environment.document; var referrerPolicy = doc.referrerPolicy; var pageURI = doc.documentURIObject; var sameDomainReferrer = ""; var otherDomainReferrer = ""; var downgradeReferrer = ""; var otherDowngradeReferrer = ""; var origin = pageURI.prePath; switch (referrerPolicy) { case Ci.nsIHttpChannel.REFERRER_POLICY_NO_REFERRER: // sends no referrer sameDomainReferrer = otherDomainReferrer = downgradeReferrer = otherDowngradeReferrer = "(no referrer)"; break; case Ci.nsIHttpChannel.REFERRER_POLICY_ORIGIN: // only sends the origin of the referring URL sameDomainReferrer = otherDomainReferrer = downgradeReferrer = otherDowngradeReferrer = origin; break; case Ci.nsIHttpChannel.REFERRER_POLICY_ORIGIN_WHEN_XORIGIN: // same as default, but reduced to ORIGIN when cross-origin. sameDomainReferrer = pageURI.spec; otherDomainReferrer = downgradeReferrer = otherDowngradeReferrer = origin; break; case Ci.nsIHttpChannel.REFERRER_POLICY_UNSAFE_URL: // always sends the referrer, even on downgrade. sameDomainReferrer = otherDomainReferrer = downgradeReferrer = otherDowngradeReferrer = pageURI.spec; break; case Ci.nsIHttpChannel.REFERRER_POLICY_NO_REFERRER_WHEN_DOWNGRADE: // default state, doesn't send referrer from https->http sameDomainReferrer = otherDomainReferrer = pageURI.spec; downgradeReferrer = otherDowngradeReferrer = "(no referrer)"; break; default: // this is a new referrer policy which we do not know about sameDomainReferrer = otherDomainReferrer = downgradeReferrer = otherDowngradeReferrer = "(unknown Referrer Policy)"; break; } var sameDomainUri = origin + "/*"; var referrerUrls = [ // add the referrer uri 'referrer' we would send when visiting 'uri' { uri: pageURI.scheme+'://example.com/', referrer: otherDomainReferrer, description: l10n.lookup('securityReferrerPolicyOtherDomain')}, { uri: sameDomainUri, referrer: sameDomainReferrer, description: l10n.lookup('securityReferrerPolicySameDomain')} ]; if (pageURI.schemeIs('https')) { // add the referrer we would send on downgrading http->https if (sameDomainReferrer != downgradeReferrer) { referrerUrls.push({ uri: "http://"+pageURI.hostPort+"/*", referrer: downgradeReferrer, description: l10n.lookup('securityReferrerPolicySameDomainDowngrade') }); } if (otherDomainReferrer != otherDowngradeReferrer) { referrerUrls.push({ uri: "http://example.com/", referrer: otherDowngradeReferrer, description: l10n.lookup('securityReferrerPolicyOtherDomainDowngrade') }); } } return { header: l10n.lookupFormat("securityReferrerPolicyReportHeader", [pageURI.spec]), policyName: REFERRER_POLICY_NAMES[referrerPolicy], urls: referrerUrls } } }, { item: "converter", from: "securityReferrerPolicyInfo", to: "view", exec: function(referrerPolicyInfo, context) { return context.createView({ html: "
" + " ${rpi.header}
" + " ${rpi.policyName}
" + " " + " " + " " + " " + " " + // iterate all policies " " + " " + " " + " " + "
" + NEXT_URI_HEADER + " " + CALCULATED_REFERRER_HEADER + "
${nextURI.description} (e.g., ${nextURI.uri}) ${nextURI.referrer}
" + "
", data: { rpi: referrerPolicyInfo, } }); } } ];