Bug 1473218 - Implement report-sample support for CSP directives, r=ckerschb

This commit is contained in:
Andrea Marchesini 2018-07-06 08:01:49 +02:00
Родитель 25a1215bb8
Коммит 9042bfbc94
9 изменённых файлов: 61 добавлений и 22 удалений

Просмотреть файл

@ -563,10 +563,13 @@ nsCSPContext::GetAllowsInline(nsContentPolicyType aContentType,
*outAllowsInline = false;
}
nsAutoString violatedDirective;
mPolicies[i]->getDirectiveStringForContentType(aContentType, violatedDirective);
bool reportSample = false;
mPolicies[i]->getDirectiveStringAndReportSampleForContentType(aContentType,
violatedDirective,
&reportSample);
reportInlineViolation(aContentType,
aNonce,
content,
reportSample ? content : EmptyString(),
violatedDirective,
i,
aLineNumber,
@ -613,13 +616,16 @@ nsCSPContext::GetAllowsInline(nsContentPolicyType aContentType,
keyword, nonceOrHash, false)) \
{ \
nsAutoString violatedDirective; \
mPolicies[p]->getDirectiveStringForContentType( \
bool reportSample = false; \
mPolicies[p]->getDirectiveStringAndReportSampleForContentType( \
nsIContentPolicy::TYPE_ ## contentPolicyType, \
violatedDirective); \
violatedDirective, &reportSample); \
this->AsyncReportViolation(selfISupports, nullptr, violatedDirective, p, \
NS_LITERAL_STRING(observerTopic), \
aSourceFile, aScriptSample, aLineNum, \
aColumnNum); \
aSourceFile, \
reportSample \
? aScriptSample : EmptyString(), \
aLineNum, aColumnNum); \
} \
PR_END_MACRO; \
break
@ -1265,7 +1271,7 @@ class CSPReportSenderRunnable final : public Runnable
rv = blockedURI->SchemeIs("data", &isData);
if (NS_SUCCEEDED(rv) && isData) {
blockedDataStr.Truncate(40);
blockedDataStr.AppendASCII("...");
blockedDataStr.AppendASCII("");
}
}
} else if (blockedString) {

Просмотреть файл

@ -482,6 +482,10 @@ nsCSPParser::keywordSource()
return CSP_CreateHostSrcFromSelfURI(mSelfURI);
}
if (CSP_IsKeyword(mCurToken, CSP_REPORT_SAMPLE)) {
return new nsCSPKeywordSrc(CSP_UTF16KeywordToEnum(mCurToken));
}
if (CSP_IsKeyword(mCurToken, CSP_STRICT_DYNAMIC)) {
// make sure strict dynamic is enabled
if (!sStrictDynamicEnabled) {
@ -800,9 +804,10 @@ nsCSPParser::sourceList(nsTArray<nsCSPBaseSrc*>& outSrcs)
// Check if the directive contains a 'none'
if (isNone) {
// If the directive contains no other srcs, then we set the 'none'
if (outSrcs.Length() == 0) {
if (outSrcs.IsEmpty() ||
(outSrcs.Length() == 1 && outSrcs[0]->isReportSample())) {
nsCSPKeywordSrc *keyword = new nsCSPKeywordSrc(CSP_NONE);
outSrcs.AppendElement(keyword);
outSrcs.InsertElementAt(0, keyword);
}
// Otherwise, we ignore 'none' and report a warning
else {
@ -1150,9 +1155,10 @@ nsCSPParser::directive()
// If we can not parse any srcs; we let the source expression be the empty set ('none')
// see, http://www.w3.org/TR/CSP11/#source-list-parsing
if (srcs.Length() == 0) {
if (srcs.IsEmpty() ||
(srcs.Length() == 1 && srcs[0]->isReportSample())) {
nsCSPKeywordSrc *keyword = new nsCSPKeywordSrc(CSP_NONE);
srcs.AppendElement(keyword);
srcs.InsertElementAt(0, keyword);
}
// If policy contains 'strict-dynamic' invalidate all srcs within script-src.

Просмотреть файл

@ -1264,6 +1264,18 @@ nsCSPDirective::getDirName(nsAString& outStr) const
outStr.AppendASCII(CSP_CSPDirectiveToString(mDirective));
}
bool
nsCSPDirective::hasReportSampleKeyword() const
{
for (nsCSPBaseSrc* src : mSrcs) {
if (src->isReportSample()) {
return true;
}
}
return false;
}
/* =============== nsCSPChildSrcDirective ============= */
nsCSPChildSrcDirective::nsCSPChildSrcDirective(CSPDirective aDirective)
@ -1614,13 +1626,18 @@ nsCSPPolicy::hasDirective(CSPDirective aDir) const
* for the ::permits() function family.
*/
void
nsCSPPolicy::getDirectiveStringForContentType(nsContentPolicyType aContentType,
nsAString& outDirective) const
nsCSPPolicy::getDirectiveStringAndReportSampleForContentType(nsContentPolicyType aContentType,
nsAString& outDirective,
bool* aReportSample) const
{
MOZ_ASSERT(aReportSample);
*aReportSample = false;
nsCSPDirective* defaultDir = nullptr;
for (uint32_t i = 0; i < mDirectives.Length(); i++) {
if (mDirectives[i]->restrictsContentType(aContentType)) {
mDirectives[i]->getDirName(outDirective);
*aReportSample = mDirectives[i]->hasReportSampleKeyword();
return;
}
if (mDirectives[i]->isDefaultDirective()) {
@ -1631,6 +1648,7 @@ nsCSPPolicy::getDirectiveStringForContentType(nsContentPolicyType aContentType,
// the contentType must be restricted by the default directive
if (defaultDir) {
defaultDir->getDirName(outDirective);
*aReportSample = defaultDir->hasReportSampleKeyword();
return;
}
NS_ASSERTION(false, "Can not query directive string for contentType!");

Просмотреть файл

@ -126,6 +126,7 @@ inline CSPDirective CSP_StringToCSPDirective(const nsAString& aDir)
macro(CSP_NONE, "'none'") \
macro(CSP_NONCE, "'nonce-") \
macro(CSP_REQUIRE_SRI_FOR, "require-sri-for") \
macro(CSP_REPORT_SAMPLE, "'report-sample'") \
macro(CSP_STRICT_DYNAMIC, "'strict-dynamic'")
enum CSPKeyword {
@ -238,6 +239,9 @@ class nsCSPBaseSrc {
virtual void invalidate() const
{ mInvalidated = true; }
virtual bool isReportSample() const
{ return false; }
protected:
// invalidate srcs if 'script-dynamic' is present or also invalidate
// unsafe-inline' if nonce- or hash-source specified
@ -336,6 +340,8 @@ class nsCSPKeywordSrc : public nsCSPBaseSrc {
}
}
bool isReportSample() const override
{ return mKeyword == CSP_REPORT_SAMPLE; }
private:
CSPKeyword mKeyword;
};
@ -475,6 +481,8 @@ class nsCSPDirective {
virtual void getDirName(nsAString& outStr) const;
bool hasReportSampleKeyword() const;
protected:
CSPDirective mDirective;
nsTArray<nsCSPBaseSrc*> mSrcs;
@ -678,8 +686,9 @@ class nsCSPPolicy {
void getReportURIs(nsTArray<nsString> &outReportURIs) const;
void getDirectiveStringForContentType(nsContentPolicyType aContentType,
nsAString& outDirective) const;
void getDirectiveStringAndReportSampleForContentType(nsContentPolicyType aContentType,
nsAString& outDirective,
bool* aReportSample) const;
void getDirectiveAsString(CSPDirective aDir, nsAString& outDirective) const;

Просмотреть файл

@ -1 +1 @@
Content-Security-Policy: default-src 'self'; img-src 'none'
Content-Security-Policy: default-src 'self' 'report-sample'; img-src 'none' 'report-sample'

Просмотреть файл

@ -32,7 +32,7 @@ SpecialPowers.registerConsoleListener(function ConsoleMsgListener(aMsg) {
data_uri = "";
data_uri = aMsg.message.substr(data_start);
// this will either match the elipsis after the URI or the . at the end of the message
data_uri = data_uri.substr(0, data_uri.indexOf("."));
data_uri = data_uri.substr(0, data_uri.indexOf(""));
if (data_uri == "") {
return;
}

Просмотреть файл

@ -29,10 +29,10 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=548193
const testfile = "tests/dom/security/test/csp/file_report.html";
const reportURI = "http://mochi.test:8888/foo.sjs";
const policy = "default-src 'none'; report-uri " + reportURI;
const policy = "default-src 'none' 'report-sample'; report-uri " + reportURI;
const docUri = "http://mochi.test:8888/tests/dom/security/test/csp/file_testserver.sjs" +
"?file=tests/dom/security/test/csp/file_report.html" +
"&csp=default-src%20%27none%27%3B%20report-uri%20http%3A//mochi.test%3A8888/foo.sjs";
"&csp=default-src%20%27none%27%20%27report-sample%27%3B%20report-uri%20http%3A//mochi.test%3A8888/foo.sjs";
window.checkResults = function(reportObj) {
var cspReport = reportObj["csp-report"];
@ -52,7 +52,7 @@ window.checkResults = function(reportObj) {
is(cspReport["violated-directive"], "default-src", "Incorrect violated-directive");
is(cspReport["original-policy"], "default-src 'none'; report-uri http://mochi.test:8888/foo.sjs",
is(cspReport["original-policy"], "default-src 'none' 'report-sample'; report-uri http://mochi.test:8888/foo.sjs",
"Incorrect original-policy");
is(cspReport["source-file"], docUri, "Incorrect source-file");

Просмотреть файл

@ -70,7 +70,7 @@ function makeTest(id, expectedJSON, useReportOnlyPolicy, callback) {
// set up a new CSP instance for each test.
var csp = Cc["@mozilla.org/cspcontext;1"]
.createInstance(Ci.nsIContentSecurityPolicy);
var policy = "default-src 'none'; " +
var policy = "default-src 'none' 'report-sample'; " +
"report-uri " + REPORT_SERVER_URI +
":" + REPORT_SERVER_PORT +
"/test" + id;

Просмотреть файл

@ -1071,7 +1071,7 @@ add_task(async function test_contentscript_csp() {
let chaosMode = parseInt(env.get("MOZ_CHAOSMODE"), 16);
let checkCSPReports = !(chaosMode === 0 || chaosMode & 0x02);
gContentSecurityPolicy = `default-src 'none'; script-src 'nonce-deadbeef' 'unsafe-eval'; report-uri ${CSP_REPORT_PATH};`;
gContentSecurityPolicy = `default-src 'none' 'report-sample'; script-src 'nonce-deadbeef' 'unsafe-eval' 'report-sample'; report-uri ${CSP_REPORT_PATH};`;
let extension = ExtensionTestUtils.loadExtension(EXTENSION_DATA);
await extension.startup();