Skip to content

Commit

Permalink
Bug 1848315 - CSP: Introduce new and more detailed error messages. r=…
Browse files Browse the repository at this point in the history
…freddyb,devtools-reviewers

Differential Revision: https://phabricator.services.mozilla.com/D186143
  • Loading branch information
Tom Schuster committed Mar 1, 2024
1 parent e8b302c commit 97cfc68
Show file tree
Hide file tree
Showing 7 changed files with 343 additions and 51 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,18 @@ const TEST_URI =
const TEST_VIOLATIONS =
"https://example.com/browser/devtools/client/webconsole/" +
"test/browser/test-csp-many-errors.html";
const CSP_VIOLATION_MSG =
"Content-Security-Policy: The page\u2019s settings blocked the loading of a resource " +
"at inline (\u201cstyle-src\u201d).";
const CSP_TOO_MANY_REPORTS_MSG =
"Content-Security-Policy: Prevented too many CSP reports from being sent within a short period of time.";

const bundle = Services.strings.createBundle(
"chrome://global/locale/security/csp.properties"
);
const CSP_VIOLATION_MSG = bundle.formatStringFromName(
"CSPInlineStyleViolation",
["style-src 'none'", "style-src-attr"]
);
const CSP_TOO_MANY_REPORTS_MSG = bundle.formatStringFromName(
"tooManyReports",
[]
);

add_task(async function () {
// Reduce the limit to reduce the log spam.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@
"use strict";

add_task(async function () {
const bundle = Services.strings.createBundle(
"chrome://global/locale/security/csp.properties"
);

const TEST_URI =
"data:text/html;charset=utf8,<!DOCTYPE html>Web Console CSP violation test";
const hud = await openNewTabAndConsole(TEST_URI);
Expand All @@ -15,10 +19,14 @@ add_task(async function () {
const TEST_VIOLATION =
"https://example.com/browser/devtools/client/webconsole/" +
"test/browser/test-csp-violation.html";
const CSP_VIOLATION_MSG =
"Content-Security-Policy: The page\u2019s settings " +
"blocked the loading of a resource at " +
"http://some.example.com/test.png (\u201cimg-src\u201d).";
const CSP_VIOLATION_MSG = bundle.formatStringFromName(
"CSPGenericViolation",
[
"img-src https://example.com",
"http://some.example.com/test.png",
"img-src",
]
);
const onRepeatedMessage = waitForRepeatedMessageByType(
hud,
CSP_VIOLATION_MSG,
Expand All @@ -35,9 +43,10 @@ add_task(async function () {
const TEST_VIOLATION =
"https://example.com/browser/devtools/client/webconsole/" +
"test/browser/test-csp-violation-inline.html";
const CSP_VIOLATION =
`Content-Security-Policy: The page’s settings blocked` +
` the loading of a resource at inline (“style-src”).`;
const CSP_VIOLATION = bundle.formatStringFromName(
"CSPInlineStyleViolation",
["style-src 'self'", "style-src-elem"]
);
const VIOLATION_LOCATION_HTML = "test-csp-violation-inline.html:18:1";
const VIOLATION_LOCATION_JS = "test-csp-violation-inline.html:14:25";
await navigateTo(TEST_VIOLATION);
Expand Down Expand Up @@ -71,7 +80,11 @@ add_task(async function () {
const TEST_VIOLATION =
"https://example.com/browser/devtools/client/webconsole/" +
"test/browser/test-csp-violation-base-uri.html";
const CSP_VIOLATION = `Content-Security-Policy: The page’s settings blocked the loading of a resource at https://evil.com/ (“base-uri”).`;
const CSP_VIOLATION = bundle.formatStringFromName("CSPGenericViolation", [
"base-uri 'self'",
"https://evil.com/",
"base-uri",
]);
const VIOLATION_LOCATION = "test-csp-violation-base-uri.html:15:25";
await navigateTo(TEST_VIOLATION);
let msg = await waitFor(() => findErrorMessage(hud, CSP_VIOLATION));
Expand All @@ -97,7 +110,11 @@ add_task(async function () {
const TEST_VIOLATION =
"https://example.com/browser/devtools/client/webconsole/" +
"test/browser/test-csp-violation-form-action.html";
const CSP_VIOLATION = `Content-Security-Policy: The page’s settings blocked the loading of a resource at https://evil.com/evil.com (“form-action”).`;
const CSP_VIOLATION = bundle.formatStringFromName("CSPGenericViolation", [
"form-action 'self'",
"https://evil.com/evil.com",
"form-action",
]);
const VIOLATION_LOCATION = "test-csp-violation-form-action.html:14:40";

await navigateTo(TEST_VIOLATION);
Expand All @@ -116,9 +133,11 @@ add_task(async function () {
const TEST_VIOLATION =
"https://example.com/browser/devtools/client/webconsole/" +
"test/browser/test-csp-violation-frame-ancestor-parent.html";
const CSP_VIOLATION =
`Content-Security-Policy: The page’s settings blocked` +
` the loading of a resource at ${TEST_VIOLATION} (“frame-ancestors”).`;
const CSP_VIOLATION = bundle.formatStringFromName("CSPGenericViolation", [
"frame-ancestors 'none'",
TEST_VIOLATION,
"frame-ancestors",
]);
await navigateTo(TEST_VIOLATION);
const msg = await waitFor(() => findErrorMessage(hud, CSP_VIOLATION));
ok(msg, "Frame-Ancestors violation by html was printed");
Expand All @@ -129,8 +148,11 @@ add_task(async function () {
const TEST_VIOLATION =
"https://example.com/browser/devtools/client/webconsole/" +
"test/browser/test-csp-violation-event-handler.html";
const CSP_VIOLATION = `Content-Security-Policy: The page’s settings blocked the loading of a resource at inline (“script-src”).
Source: document.body.textContent = 'JavaScript …`;
const CSP_VIOLATION =
bundle.formatStringFromName("CSPEventHandlerScriptViolation", [
"script-src 'self'",
"script-src-attr",
]) + `\nSource: document.body.textContent = 'JavaScript …`;
// Future-Todo: Include line and column number.
const VIOLATION_LOCATION = "test-csp-violation-event-handler.html";
await navigateTo(TEST_VIOLATION);
Expand Down
91 changes: 85 additions & 6 deletions dom/locales/en-US/chrome/security/csp.properties
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,93 @@

# CSP Warnings:

# LOCALIZATION NOTE (CSPViolationWithURI):
# %1$S is the directive that has been violated.
# LOCALIZATION NOTE (CSPInlineStyleViolation):
# %1$S is the entire directive that has been violated. (e.g. "default-src 'none'")
# %2$S is the type of directive used by the resource (e.g. style-src-elem)
CSPInlineStyleViolation = The page’s settings blocked an inline style (%2$S) from being applied because it violates the following directive: “%1$S”
# LOCALIZATION NOTE (CSPROInlineStyleViolation):
# %1$S is the entire directive that has been violated. (e.g. "default-src 'none'")
# %2$S is the type of directive used by the resource (e.g. style-src-elem)
CSPROInlineStyleViolation = (Report-Only policy) The page’s settings would block an inline style (%2$S) from being applied because it violates the following directive: “%1$S”
# LOCALIZATION NOTE (CSPInlineScriptViolation):
# %1$S is the entire directive that has been violated. (e.g. "default-src 'none'")
# %2$S is the type of directive used by the resource (e.g. script-src-elem)
CSPInlineScriptViolation = The page’s settings blocked an inline script (%2$S) from being executed because it violates the following directive: “%1$S”
# LOCALIZATION NOTE (CSPROInlineScriptViolation):
# %1$S is the entire directive that has been violated. (e.g. "default-src 'none'")
# %2$S is the type of directive used by the resource (e.g. script-src-elem)
CSPROInlineScriptViolation = (Report-Only policy) The page’s settings would block an inline script (%2$S) from being executed because it violates the following directive: “%1$S”
# LOCALIZATION NOTE (CSPEventHandlerScriptViolation):
# %1$S is the entire directive that has been violated. (e.g. "default-src 'none'")
# %2$S is the type of directive used by the resource (e.g. script-src-attr)
CSPEventHandlerScriptViolation = The page’s settings blocked an event handler (%2$S) from being executed because it violates the following directive: “%1$S”
# LOCALIZATION NOTE (CSPROEventHandlerScriptViolation):
# %1$S is the entire directive that has been violated. (e.g. "default-src 'none'")
# %2$S is the type of directive used by the resource (e.g. script-src-attr)
CSPROEventHandlerScriptViolation = (Report-Only policy) The page’s settings would block an event handler (%2$S) from being executed because it violates the following directive: “%1$S”
# LOCALIZATION NOTE (CSPEvalScriptViolation):
# Don't translate 'unsafe-eval'.
# %1$S is the entire directive that has been violated. (e.g. "default-src 'none'")
# %2$S is the type of directive used by the resource (e.g. script-src)
CSPEvalScriptViolation = The page’s settings blocked a JavaScript eval (%2$S) from being executed because it violates the following directive: “%1$S” (Missing 'unsafe-eval')
# LOCALIZATION NOTE (CSPROEvalScriptViolation):
# Don't translate 'unsafe-eval'.
# %1$S is the entire directive that has been violated. (e.g. "default-src 'none'")
# %2$S is the type of directive used by the resource (e.g. script-src)
CSPROEvalScriptViolation = (Report-Only policy) The page’s settings would block a JavaScript eval (%2$S) from being executed because it violates the following directive: “%1$S” (Missing 'unsafe-eval')
# LOCALIZATION NOTE (CSPWasmEvalScriptViolation):
# WebAssembly is a feature name.
# Don't translate 'wasm-unsafe-eval' or 'unsafe-eval'.
# %1$S is the entire directive that has been violated. (e.g. "default-src 'none'")
# %2$S is the type of directive used by the resource (e.g. script-src)
CSPWasmEvalScriptViolation = The page’s settings blocked WebAssembly (%2$S) from being executed because it violates the following directive: “%1$S” (Missing 'wasm-unsafe-eval' or 'unsafe-eval')
# LOCALIZATION NOTE (CSPROWasmEvalScriptViolation):
# WebAssembly is a feature name.
# Don't translate 'wasm-unsafe-eval' or 'unsafe-eval'.
# %1$S is the entire directive that has been violated. (e.g. "default-src 'none'")
# %2$S is the type of directive used by the resource (e.g. script-src)
CSPROWasmEvalScriptViolation = (Report-Only policy) The page’s settings would block WebAssembly (%2$S) from being executed because it violates the following directive: “%1$S” (Missing 'wasm-unsafe-eval' or 'unsafe-eval')
# LOCALIZATION NOTE (CSPStyleViolation):
# %1$S is the entire directive that has been violated. (e.g. "default-src 'none'")
# %2$S is the URI of the resource which violated the directive.
CSPViolationWithURI = The page’s settings blocked the loading of a resource at %2$S (“%1$S”).
# LOCALIZATION NOTE (CSPROViolationWithURI):
# %1$S is the directive that has been violated.
# %3$S is the type of directive used by the resource (e.g. style-src)
CSPStyleViolation = The page’s settings blocked a style (%3$S) at %2$S from being applied because it violates the following directive: “%1$S”
# LOCALIZATION NOTE (CSPROStyleViolation):
# %1$S is the entire directive that has been violated. (e.g. "default-src 'none'")
# %2$S is the URI of the resource which violated the directive.
CSPROViolationWithURI = The page’s settings observed the loading of a resource at %2$S (“%1$S”). A CSP report is being sent.
# %3$S is the type of directive used by the resource (e.g. style-src)
CSPROStyleViolation = (Report-Only policy) The page’s settings would block a style (%3$S) at %2$S from being applied because it violates the following directive: “%1$S”
# LOCALIZATION NOTE (CSPScriptViolation):
# %1$S is the entire directive that has been violated. (e.g. "default-src 'none'")
# %2$S is the URI of the resource which violated the directive.
# %3$S is the type of directive used by the resource (e.g. script-src-elem)
CSPScriptViolation = The page’s settings blocked a script (%3$S) at %2$S from being executed because it violates the following directive: “%1$S”
# LOCALIZATION NOTE (CSPROScriptViolation):
# %1$S is the entire directive that has been violated. (e.g. "default-src 'none'")
# %2$S is the URI of the resource which violated the directive.
# %3$S is the type of directive used by the resource (e.g. script-src-elem)
CSPROScriptViolation = (Report-Only policy) The page’s settings would block a script (%3$S) at %2$S from being executed because it violates the following directive: “%1$S”
# LOCALIZATION NOTE (CSPWorkerViolation):
# %1$S is the entire directive that has been violated. (e.g. "default-src 'none'")
# %2$S is the URI of the resource which violated the directive.
# %3$S is the type of directive used by the resource (e.g. worker-src)
CSPWorkerViolation = The page’s settings blocked a worker script (%3$S) at %2$S from being executed because it violates the following directive: “%1$S”
# LOCALIZATION NOTE (CSPROWorkerViolation):
# %1$S is the entire directive that has been violated. (e.g. "default-src 'none'")
# %2$S is the URI of the resource which violated the directive.
# %3$S is the type of directive used by the resource (e.g. worker-src)
CSPROWorkerViolation = (Report-Only policy) The page’s settings would block a worker script (%3$S) at %2$S from being executed because it violates the following directive: “%1$S”
# LOCALIZATION NOTE (CSPGenericViolation):
# %1$S is the entire directive that has been violated. (e.g. "default-src 'none'")
# %2$S is the URI of the resource which violated the directive.
# %3$S is the type of directive used by the resource (e.g. image-src)
CSPGenericViolation = The page’s settings blocked the loading of a resource (%3$S) at %2$S because it violates the following directive: “%1$S”
# LOCALIZATION NOTE (CSPROGenericViolation):
# %1$S is the entire directive that has been violated. (e.g. "default-src 'none'")
# %2$S is the URI of the resource which violated the directive.
# %3$S is the type of directive used by the resource (e.g. image-src)
CSPROGenericViolation = (Report-Only policy) The page’s settings would block the loading of a resource (%3$S) at %2$S because it violates the following directive: “%1$S”

# LOCALIZATION NOTE (triedToSendReport):
# %1$S is the URI we attempted to send a report to.
triedToSendReport = Tried to send report to invalid URI: “%1$S”
Expand Down
125 changes: 99 additions & 26 deletions dom/security/nsCSPContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1468,32 +1468,7 @@ class CSPReportSenderRunnable final : public Runnable {
mCSPContext->SendReports(init, mViolatedPolicyIndex);

// 3) log to console (one per policy violation)

if (mBlockedURI) {
mBlockedURI->GetSpec(blockedContentSource);
if (blockedContentSource.Length() >
nsCSPContext::ScriptSampleMaxLength()) {
bool isData = mBlockedURI->SchemeIs("data");
if (NS_SUCCEEDED(rv) && isData &&
blockedContentSource.Length() >
nsCSPContext::ScriptSampleMaxLength()) {
blockedContentSource.Truncate(nsCSPContext::ScriptSampleMaxLength());
blockedContentSource.Append(
NS_ConvertUTF16toUTF8(nsContentUtils::GetLocalizedEllipsis()));
}
}
}

if (blockedContentSource.Length() > 0) {
nsString blockedContentSource16 =
NS_ConvertUTF8toUTF16(blockedContentSource);
AutoTArray<nsString, 2> params = {mViolatedDirective,
blockedContentSource16};
mCSPContext->logToConsole(
mReportOnlyFlag ? "CSPROViolationWithURI" : "CSPViolationWithURI",
params, mSourceFile, mScriptSample, mLineNum, mColumnNum,
nsIScriptError::errorFlag);
}
ReportToConsole();

// 4) fire violation event
// A frame-ancestors violation has occurred, but we should not dispatch
Expand All @@ -1507,6 +1482,104 @@ class CSPReportSenderRunnable final : public Runnable {
}

private:
void ReportToConsole() const {
NS_ConvertUTF8toUTF16 effectiveDirective(
CSP_CSPDirectiveToString(mEffectiveDirective));

switch (mBlockedContentSource) {
case nsCSPContext::BlockedContentSource::eInline: {
const char* errorName = nullptr;
if (mEffectiveDirective == CSPDirective::STYLE_SRC_ATTR_DIRECTIVE ||
mEffectiveDirective == CSPDirective::STYLE_SRC_ELEM_DIRECTIVE) {
errorName = mReportOnlyFlag ? "CSPROInlineStyleViolation"
: "CSPInlineStyleViolation";
} else if (mEffectiveDirective ==
CSPDirective::SCRIPT_SRC_ATTR_DIRECTIVE) {
errorName = mReportOnlyFlag ? "CSPROEventHandlerScriptViolation"
: "CSPEventHandlerScriptViolation";
} else {
MOZ_ASSERT(mEffectiveDirective ==
CSPDirective::SCRIPT_SRC_ELEM_DIRECTIVE);
errorName = mReportOnlyFlag ? "CSPROInlineScriptViolation"
: "CSPInlineScriptViolation";
}

AutoTArray<nsString, 2> params = {mViolatedDirectiveString,
effectiveDirective};
mCSPContext->logToConsole(errorName, params, mSourceFile, mScriptSample,
mLineNum, mColumnNum,
nsIScriptError::errorFlag);
break;
}

case nsCSPContext::BlockedContentSource::eEval: {
AutoTArray<nsString, 2> params = {mViolatedDirectiveString,
effectiveDirective};
mCSPContext->logToConsole(mReportOnlyFlag ? "CSPROEvalScriptViolation"
: "CSPEvalScriptViolation",
params, mSourceFile, mScriptSample, mLineNum,
mColumnNum, nsIScriptError::errorFlag);
break;
}

case nsCSPContext::BlockedContentSource::eWasmEval: {
AutoTArray<nsString, 2> params = {mViolatedDirectiveString,
effectiveDirective};
mCSPContext->logToConsole(mReportOnlyFlag
? "CSPROWasmEvalScriptViolation"
: "CSPWasmEvalScriptViolation",
params, mSourceFile, mScriptSample, mLineNum,
mColumnNum, nsIScriptError::errorFlag);
break;
}

case nsCSPContext::BlockedContentSource::eSelf:
case nsCSPContext::BlockedContentSource::eUnknown: {
nsAutoString source(u"<unknown>"_ns);
if (mBlockedURI) {
nsAutoCString uri;
mBlockedURI->GetSpec(uri);

if (mBlockedURI->SchemeIs("data") &&
uri.Length() > nsCSPContext::ScriptSampleMaxLength()) {
uri.Truncate(nsCSPContext::ScriptSampleMaxLength());
uri.Append(
NS_ConvertUTF16toUTF8(nsContentUtils::GetLocalizedEllipsis()));
}

if (!uri.IsEmpty()) {
CopyUTF8toUTF16(uri, source);
}
}

const char* errorName = nullptr;
switch (mEffectiveDirective) {
case CSPDirective::STYLE_SRC_ELEM_DIRECTIVE:
errorName =
mReportOnlyFlag ? "CSPROStyleViolation" : "CSPStyleViolation";
break;
case CSPDirective::SCRIPT_SRC_ELEM_DIRECTIVE:
errorName =
mReportOnlyFlag ? "CSPROScriptViolation" : "CSPScriptViolation";
break;
case CSPDirective::WORKER_SRC_DIRECTIVE:
errorName =
mReportOnlyFlag ? "CSPROWorkerViolation" : "CSPWorkerViolation";
break;
default:
errorName = mReportOnlyFlag ? "CSPROGenericViolation"
: "CSPGenericViolation";
}

AutoTArray<nsString, 3> params = {mViolatedDirectiveString, source,
effectiveDirective};
mCSPContext->logToConsole(errorName, params, mSourceFile, mScriptSample,
mLineNum, mColumnNum,
nsIScriptError::errorFlag);
}
}
}

RefPtr<Element> mTriggeringElement;
nsCOMPtr<nsICSPEventListener> mCSPEventListener;
nsCOMPtr<nsIURI> mBlockedURI;
Expand Down
Loading

0 comments on commit 97cfc68

Please sign in to comment.