Issue #2402 - CSP Violation events should have the correct sample for inline contexts. https://bugzilla.mozilla.org/show_bug.cgi?id=1473587 Add preference to increase max length of CSP report source sample. https://bugzilla.mozilla.org/show_bug.cgi?id=1415352 Return valid columnNumber value in CSP violation events. https://bugzilla.mozilla.org/show_bug.cgi?id=1418246

This commit is contained in:
Brian Smith
2024-01-08 07:46:11 -06:00
committed by roytam1
parent e74612e23e
commit 5b068f3726
38 changed files with 277 additions and 106 deletions
+18 -5
View File
@@ -535,7 +535,8 @@ NS_IMPL_ISUPPORTS(nsScriptSecurityManager,
///////////////// Security Checks /////////////////
bool
nsScriptSecurityManager::ContentSecurityPolicyPermitsJSAction(JSContext *cx)
nsScriptSecurityManager::ContentSecurityPolicyPermitsJSAction(JSContext *cx,
JS::HandleValue aValue)
{
MOZ_ASSERT(cx == nsContentUtils::GetCurrentJSContext());
nsCOMPtr<nsIPrincipal> subjectPrincipal = nsContentUtils::SubjectPrincipal();
@@ -558,12 +559,23 @@ nsScriptSecurityManager::ContentSecurityPolicyPermitsJSAction(JSContext *cx)
}
if (reportViolation) {
nsAutoString fileName;
unsigned lineNum = 0;
NS_NAMED_LITERAL_STRING(scriptSample, "call to eval() or related function blocked by CSP");
JS::Rooted<JSString*> jsString(cx, JS::ToString(cx, aValue));
if (NS_WARN_IF(!jsString)) {
JS_ClearPendingException(cx);
return false;
}
nsAutoJSString scriptSample;
if (NS_WARN_IF(!scriptSample.init(cx, jsString))) {
JS_ClearPendingException(cx);
return false;
}
JS::AutoFilename scriptFilename;
if (JS::DescribeScriptedCaller(cx, &scriptFilename, &lineNum)) {
nsAutoString fileName;
unsigned lineNum = 0;
unsigned columnNum = 0;
if (JS::DescribeScriptedCaller(cx, &scriptFilename, &lineNum, &columnNum)) {
if (const char *file = scriptFilename.get()) {
CopyUTF8toUTF16(nsDependentCString(file), fileName);
}
@@ -574,6 +586,7 @@ nsScriptSecurityManager::ContentSecurityPolicyPermitsJSAction(JSContext *cx)
fileName,
scriptSample,
lineNum,
columnNum,
EmptyString(),
EmptyString());
}
+1 -1
View File
@@ -91,7 +91,7 @@ private:
// Decides, based on CSP, whether or not eval() and stuff can be executed.
static bool
ContentSecurityPolicyPermitsJSAction(JSContext *cx);
ContentSecurityPolicyPermitsJSAction(JSContext *cx, JS::HandleValue aValue);
static bool
JSPrincipalsSubsume(JSPrincipals *first, JSPrincipals *second);
+1
View File
@@ -11910,6 +11910,7 @@ nsIDocument::InlineScriptAllowedByCSP()
true, // aParserCreated
EmptyString(), // FIXME get script sample (bug 1314567)
0, // aLineNumber
0, // aColumnNumber
&allowsInlineScript);
NS_ENSURE_SUCCESS(rv, true);
}
+13
View File
@@ -101,6 +101,19 @@ public:
* was set
*/
virtual uint32_t GetLineNumber() = 0;
// This doesn't entirely belong here since they only make sense for
// some types of linking elements, but it's a better place than
// anywhere else.
virtual void SetColumnNumber(uint32_t aColumnNumber) = 0;
/**
* Get the column number, as previously set by SetColumnNumber.
*
* @return the column number of this element; or 1 if no column number
* was set
*/
virtual uint32_t GetColumnNumber() = 0;
};
NS_DEFINE_STATIC_IID_ACCESSOR(nsIStyleSheetLinkingElement,
+16 -1
View File
@@ -40,6 +40,7 @@ nsStyleLinkElement::nsStyleLinkElement()
: mDontLoadStyle(false)
, mUpdatesEnabled(true)
, mLineNumber(1)
, mColumnNumber(1)
{
}
@@ -127,6 +128,18 @@ nsStyleLinkElement::GetLineNumber()
return mLineNumber;
}
/* virtual */ void
nsStyleLinkElement::SetColumnNumber(uint32_t aColumnNumber)
{
mColumnNumber = aColumnNumber;
}
/* virtual */ uint32_t
nsStyleLinkElement::GetColumnNumber()
{
return mColumnNumber;
}
/* static */ bool
nsStyleLinkElement::IsImportEnabled()
{
@@ -412,8 +425,10 @@ nsStyleLinkElement::DoUpdateStyleSheet(nsIDocument* aOldDocument,
if (!nsStyleUtil::CSPAllowsInlineStyle(thisContent,
thisContent->NodePrincipal(),
doc->GetDocumentURI(),
mLineNumber, text, &rv))
mLineNumber, mColumnNumber, text,
&rv)) {
return rv;
}
// Parse the style sheet.
rv = doc->CSSLoader()->
+3
View File
@@ -54,6 +54,8 @@ public:
virtual void OverrideBaseURI(nsIURI* aNewBaseURI) override;
virtual void SetLineNumber(uint32_t aLineNumber) override;
virtual uint32_t GetLineNumber() override;
void SetColumnNumber(uint32_t aColumnNumber) override;
uint32_t GetColumnNumber() override;
enum RelValue {
ePREFETCH = 0x00000001,
@@ -140,6 +142,7 @@ protected:
bool mDontLoadStyle;
bool mUpdatesEnabled;
uint32_t mLineNumber;
uint32_t mColumnNumber;
};
#endif /* nsStyleLinkElement_h___ */
+1 -1
View File
@@ -191,7 +191,7 @@ nsStyledElement::ParseStyleAttribute(const nsAString& aValue,
if (!isNativeAnon &&
!nsStyleUtil::CSPAllowsInlineStyle(nullptr, NodePrincipal(),
doc->GetDocumentURI(), 0, aValue,
doc->GetDocumentURI(), 0, 0, aValue,
nullptr))
return;
+11 -17
View File
@@ -786,28 +786,22 @@ EventListenerManager::SetEventHandler(nsIAtom* aName,
rv = doc->NodePrincipal()->GetCsp(getter_AddRefs(csp));
NS_ENSURE_SUCCESS(rv, rv);
if (csp) {
// let's generate a script sample and pass it as aContent,
// it will not match the hash, but allows us to pass
// the script sample in aContent.
nsAutoString scriptSample, attr, tagName(NS_LITERAL_STRING("UNKNOWN"));
aName->ToString(attr);
nsCOMPtr<nsIDOMNode> domNode(do_QueryInterface(mTarget));
if (domNode) {
domNode->GetNodeName(tagName);
}
// build a "script sample" based on what we know about this element
scriptSample.Assign(attr);
scriptSample.AppendLiteral(" attribute on ");
scriptSample.Append(tagName);
scriptSample.AppendLiteral(" element");
unsigned lineNum = 0;
unsigned columnNum = 0;
JSContext* cx = nsContentUtils::GetCurrentJSContext();
if (cx && !JS::DescribeScriptedCaller(cx, nullptr, &lineNum, &columnNum)) {
JS_ClearPendingException(cx);
}
if (csp) {
bool allowsInlineScript = true;
rv = csp->GetAllowsInline(nsIContentPolicy::TYPE_SCRIPT,
EmptyString(), // aNonce
true, // aParserCreated (true because attribute event handler)
scriptSample,
0, // aLineNumber
aBody,
lineNum, // aLineNumber
columnNum, // aColumnNumber
&allowsInlineScript);
NS_ENSURE_SUCCESS(rv, rv);
@@ -140,6 +140,8 @@ interface nsIContentSecurityPolicy : nsISerializable
* (and compare to the hashes listed in the policy)
* @param aLineNumber The line number of the inline resource
* (used for reporting)
* @param aColumnNumber The column number of the inline resource
* (used for reporting)
* @return
* Whether or not the effects of the inline style should be allowed
* (block the rules if false).
@@ -148,7 +150,8 @@ interface nsIContentSecurityPolicy : nsISerializable
in AString aNonce,
in boolean aParserCreated,
in AString aContent,
in unsigned long aLineNumber);
in unsigned long aLineNumber,
in unsigned long aColumnNumber);
/**
* whether this policy allows eval and eval-like functions
@@ -188,6 +191,8 @@ interface nsIContentSecurityPolicy : nsISerializable
* sample of the violating content (to aid debugging)
* @param lineNum
* source line number of the violation (if available)
* @param columnNum
* source column number of the violation (if available)
* @param aNonce
* (optional) If this is a nonce violation, include the nonce so we can
* recheck to determine which policies were violated and send the
@@ -202,6 +207,7 @@ interface nsIContentSecurityPolicy : nsISerializable
in AString sourceFile,
in AString scriptSample,
in int32_t lineNum,
in int32_t columnNum,
[optional] in AString nonce,
[optional] in AString content);
+1
View File
@@ -185,6 +185,7 @@ nsresult nsJSThunk::EvaluateScript(nsIChannel *aChannel,
true, // aParserCreated
EmptyString(), // aContent
0, // aLineNumber
0, // aColumnNumber
&allowsInlineScript);
//return early if inline scripts are not allowed
+3 -1
View File
@@ -1474,6 +1474,7 @@ CSPAllowsInlineScript(nsIScriptElement *aElement, nsIDocument *aDocument)
rv = csp->GetAllowsInline(nsIContentPolicy::TYPE_SCRIPT,
nonce, parserCreated, scriptText,
aElement->GetScriptLineNumber(),
aElement->GetScriptColumnNumber(),
&allowInlineScript);
return allowInlineScript;
}
@@ -2695,10 +2696,11 @@ ScriptLoader::VerifySRI(ScriptLoadRequest* aRequest,
nsAutoCString violationURISpec;
mDocument->GetDocumentURI()->GetAsciiSpec(violationURISpec);
uint32_t lineNo = aRequest->Element() ? aRequest->Element()->GetScriptLineNumber() : 0;
uint32_t columnNo = aRequest->Element() ? aRequest->Element()->GetScriptColumnNumber() : 0;
csp->LogViolationDetails(
nsIContentSecurityPolicy::VIOLATION_TYPE_REQUIRE_SRI_FOR_SCRIPT,
NS_ConvertUTF8toUTF16(violationURISpec),
EmptyString(), lineNo, EmptyString(), EmptyString());
EmptyString(), lineNo, columnNo, EmptyString(), EmptyString());
rv = NS_ERROR_SRI_CORRUPT;
}
}
+16
View File
@@ -30,6 +30,7 @@ public:
explicit nsIScriptElement(mozilla::dom::FromParser aFromParser)
: mLineNumber(1),
mColumnNumber(1),
mAlreadyStarted(false),
mMalformed(false),
mDoneAddingChildren(aFromParser == mozilla::dom::NOT_FROM_PARSER ||
@@ -138,6 +139,16 @@ public:
return mLineNumber;
}
void SetScriptColumnNumber(uint32_t aColumnNumber)
{
mColumnNumber = aColumnNumber;
}
uint32_t GetScriptColumnNumber()
{
return mColumnNumber;
}
void SetIsMalformed()
{
mMalformed = true;
@@ -281,6 +292,11 @@ protected:
*/
uint32_t mLineNumber;
/**
* The start column number of the script.
*/
uint32_t mColumnNumber;
/**
* The "already started" flag per HTML5.
*/
+1 -1
View File
@@ -46,7 +46,7 @@ CheckInternal(nsIContentSecurityPolicy* aCSP,
if (reportViolation) {
aCSP->LogViolationDetails(nsIContentSecurityPolicy::VIOLATION_TYPE_EVAL,
aFileNameString, aExpression, aLineNum,
EmptyString(), EmptyString());
aColumnNum, EmptyString(), EmptyString());
}
return NS_OK;
+68 -24
View File
@@ -40,6 +40,7 @@
#include "nsScriptSecurityManager.h"
#include "nsStringStream.h"
#include "mozilla/Logging.h"
#include "mozilla/Preferences.h"
#include "mozilla/dom/CSPReportBinding.h"
#include "mozilla/dom/CSPDictionariesBinding.h"
#include "mozilla/net/ReferrerPolicy.h"
@@ -271,7 +272,8 @@ nsCSPContext::permitsInternal(CSPDirective aDir,
EmptyString(), /* no observer subject */
EmptyString(), /* no source file */
EmptyString(), /* no script sample */
0); /* no line number */
0, /* no line number */
0); /* no column number */
}
}
}
@@ -298,6 +300,14 @@ nsCSPContext::nsCSPContext()
, mLoadingPrincipal(nullptr)
, mQueueUpMessages(true)
{
static bool sInitialized = false;
if (!sInitialized) {
Preferences::AddIntVarCache(&sScriptSampleMaxLength,
"security.csp.reporting.script-sample.max-length",
40);
sInitialized = true;
}
CSPCONTEXTLOG(("nsCSPContext::nsCSPContext"));
}
@@ -470,7 +480,8 @@ nsCSPContext::reportInlineViolation(nsContentPolicyType aContentType,
const nsAString& aContent,
const nsAString& aViolatedDirective,
uint32_t aViolatedPolicyIndex, // TODO, use report only flag for that
uint32_t aLineNumber)
uint32_t aLineNumber,
uint32_t aColumnNumber)
{
nsString observerSubject;
// if the nonce is non empty, then we report the nonce error, otherwise
@@ -500,9 +511,9 @@ nsCSPContext::reportInlineViolation(nsContentPolicyType aContentType,
}
nsAutoString codeSample(aContent);
// cap the length of the script sample at 40 chars
if (codeSample.Length() > 40) {
codeSample.Truncate(40);
// cap the length of the script sample
if (codeSample.Length() > ScriptSampleMaxLength()) {
codeSample.Truncate(ScriptSampleMaxLength());
codeSample.AppendLiteral("...");
}
AsyncReportViolation(selfISupports, // aBlockedContentSource
@@ -512,7 +523,8 @@ nsCSPContext::reportInlineViolation(nsContentPolicyType aContentType,
observerSubject, // aObserverSubject
NS_ConvertUTF8toUTF16(sourceFile), // aSourceFile
codeSample, // aScriptSample
aLineNumber); // aLineNum
aLineNumber, // aLineNum
aColumnNumber); // aColumnNum
}
NS_IMETHODIMP
@@ -521,6 +533,7 @@ nsCSPContext::GetAllowsInline(nsContentPolicyType aContentType,
bool aParserCreated,
const nsAString& aContent,
uint32_t aLineNumber,
uint32_t aColumnNumber,
bool* outAllowsInline)
{
*outAllowsInline = true;
@@ -565,7 +578,8 @@ nsCSPContext::GetAllowsInline(nsContentPolicyType aContentType,
aContent,
violatedDirective,
i,
aLineNumber);
aLineNumber,
aColumnNumber);
}
}
return NS_OK;
@@ -584,7 +598,8 @@ nsCSPContext::GetAllowsInline(nsContentPolicyType aContentType,
* which is why we must check allows() again here.
*
* Note: This macro uses some parameters from its caller's context:
* p, mPolicies, this, aSourceFile, aScriptSample, aLineNum, selfISupports
* p, mPolicies, this, aSourceFile, aScriptSample, aLineNum, aColumnNum,
* selfISupports
*
* @param violationType: the VIOLATION_TYPE_* constant (partial symbol)
* such as INLINE_SCRIPT
@@ -611,8 +626,8 @@ nsCSPContext::GetAllowsInline(nsContentPolicyType aContentType,
nsIContentPolicy::TYPE_ ## contentPolicyType, \
violatedDirective); \
this->AsyncReportViolation(selfISupports, nullptr, violatedDirective, p, \
NS_LITERAL_STRING(observerTopic), \
aSourceFile, aScriptSample, aLineNum); \
NS_LITERAL_STRING(observerTopic), aSourceFile,\
aScriptSample, aLineNum, aColumnNum); \
} \
PR_END_MACRO; \
break
@@ -644,6 +659,7 @@ nsCSPContext::LogViolationDetails(uint16_t aViolationType,
const nsAString& aSourceFile,
const nsAString& aScriptSample,
int32_t aLineNum,
int32_t aColumnNum,
const nsAString& aNonce,
const nsAString& aContent)
{
@@ -844,6 +860,7 @@ nsCSPContext::GatherSecurityPolicyViolationEventData(
nsAString& aSourceFile,
nsAString& aScriptSample,
uint32_t aLineNum,
uint32_t aColumnNum,
mozilla::dom::SecurityPolicyViolationEventInit& aViolationEventInit)
{
NS_ENSURE_ARG_MAX(aViolatedPolicyIndex, mPolicies.Length() - 1);
@@ -907,8 +924,19 @@ nsCSPContext::GatherSecurityPolicyViolationEventData(
aViolationEventInit.mSourceFile = aSourceFile;
}
// sample
// sample, max 40 chars.
aViolationEventInit.mSample = aScriptSample;
uint32_t length = aViolationEventInit.mSample.Length();
if (length > ScriptSampleMaxLength()) {
uint32_t desiredLength = ScriptSampleMaxLength();
// Don't cut off right before a low surrogate. Just include it.
if (NS_IS_LOW_SURROGATE(aViolationEventInit.mSample[desiredLength])) {
desiredLength++;
}
aViolationEventInit.mSample.Replace(ScriptSampleMaxLength(),
length - desiredLength,
nsContentUtils::GetLocalizedEllipsis());
}
// disposition
aViolationEventInit.mDisposition = mPolicies[aViolatedPolicyIndex]->getReportOnlyFlag()
@@ -936,8 +964,7 @@ nsCSPContext::GatherSecurityPolicyViolationEventData(
aViolationEventInit.mLineNumber = aLineNum;
// column-number
// TODO: Set correct column number.
aViolationEventInit.mColumnNumber = 0;
aViolationEventInit.mColumnNumber = aColumnNum;
aViolationEventInit.mBubbles = true;
aViolationEventInit.mComposed = true;
@@ -1012,7 +1039,8 @@ nsCSPContext::SendReports(
reportURICstring.get()));
logToConsole(u"triedToSendReport", params, ArrayLength(params),
aViolationEventInit.mSourceFile, aViolationEventInit.mSample,
aViolationEventInit.mLineNumber, 0, nsIScriptError::errorFlag);
aViolationEventInit.mLineNumber, aViolationEventInit.mColumnNumber,
nsIScriptError::errorFlag);
continue; // don't return yet, there may be more URIs
}
@@ -1054,7 +1082,8 @@ nsCSPContext::SendReports(
const char16_t* params[] = { reportURIs[r].get() };
logToConsole(u"reportURInotHttpsOrHttp2", params, ArrayLength(params),
aViolationEventInit.mSourceFile, aViolationEventInit.mSample,
aViolationEventInit.mLineNumber, 0, nsIScriptError::errorFlag);
aViolationEventInit.mLineNumber, aViolationEventInit.mColumnNumber,
nsIScriptError::errorFlag);
continue;
}
@@ -1119,7 +1148,8 @@ nsCSPContext::SendReports(
CSPCONTEXTLOG(("AsyncOpen failed for report URI %s", params[0]));
logToConsole(u"triedToSendReport", params, ArrayLength(params),
aViolationEventInit.mSourceFile, aViolationEventInit.mSample,
aViolationEventInit.mLineNumber, 0, nsIScriptError::errorFlag);
aViolationEventInit.mLineNumber, aViolationEventInit.mColumnNumber,
nsIScriptError::errorFlag);
} else {
CSPCONTEXTLOG(("Sent violation report to URI %s", reportURICstring.get()));
}
@@ -1162,6 +1192,7 @@ class CSPReportSenderRunnable final : public Runnable
const nsAString& aSourceFile,
const nsAString& aScriptSample,
uint32_t aLineNum,
uint32_t aColumnNum,
nsCSPContext* aCSPContext)
: mBlockedContentSource(aBlockedContentSource)
, mOriginalURI(aOriginalURI)
@@ -1171,6 +1202,7 @@ class CSPReportSenderRunnable final : public Runnable
, mSourceFile(aSourceFile)
, mScriptSample(aScriptSample)
, mLineNum(aLineNum)
, mColumnNum(aColumnNum)
, mCSPContext(aCSPContext)
{
NS_ASSERTION(!aViolatedDirective.IsEmpty(), "Can not send reports without a violated directive");
@@ -1208,7 +1240,7 @@ class CSPReportSenderRunnable final : public Runnable
blockedURI, blockedDataStr, mOriginalURI,
mViolatedDirective, mViolatedPolicyIndex,
mSourceFile, mScriptSample, mLineNum,
init);
mColumnNum, init);
NS_ENSURE_SUCCESS(rv, rv);
// 1) notify observers
@@ -1223,15 +1255,22 @@ class CSPReportSenderRunnable final : public Runnable
mCSPContext->SendReports(init, mViolatedPolicyIndex);
// 3) log to console (one per policy violation)
// if mBlockedContentSource is not a URI, it could be a string
nsCOMPtr<nsISupportsCString> blockedString = do_QueryInterface(mBlockedContentSource);
if (blockedURI) {
blockedURI->GetSpec(blockedDataStr);
bool isData = false;
rv = blockedURI->SchemeIs("data", &isData);
if (NS_SUCCEEDED(rv) && isData) {
blockedDataStr.Truncate(40);
blockedDataStr.AppendASCII("...");
if (blockedDataStr.Length() > nsCSPContext::ScriptSampleMaxLength()) {
bool isData = false;
rv = blockedURI->SchemeIs("data", &isData);
if (NS_SUCCEEDED(rv) && isData &&
blockedDataStr.Length() > nsCSPContext::ScriptSampleMaxLength()) {
blockedDataStr.Truncate(nsCSPContext::ScriptSampleMaxLength());
blockedDataStr.Append(NS_ConvertUTF16toUTF8(nsContentUtils::GetLocalizedEllipsis()));
}
}
} else if (blockedString) {
blockedString->GetData(blockedDataStr);
}
if (blockedDataStr.Length() > 0) {
@@ -1241,7 +1280,7 @@ class CSPReportSenderRunnable final : public Runnable
mCSPContext->logToConsole(mReportOnlyFlag ? u"CSPROViolationWithURI" :
u"CSPViolationWithURI",
params, ArrayLength(params), mSourceFile, mScriptSample,
mLineNum, 0, nsIScriptError::errorFlag);
mLineNum, mColumnNum, nsIScriptError::errorFlag);
}
// 4) fire violation event
@@ -1260,6 +1299,7 @@ class CSPReportSenderRunnable final : public Runnable
nsString mSourceFile;
nsString mScriptSample;
uint32_t mLineNum;
uint32_t mColumnNum;
RefPtr<nsCSPContext> mCSPContext;
};
@@ -1287,6 +1327,8 @@ class CSPReportSenderRunnable final : public Runnable
* a sample of the violating inline script
* @param aLineNum
* source line number of the violation (if available)
* @param aColumnNum
* source column number of the violation (if available)
*/
nsresult
nsCSPContext::AsyncReportViolation(nsISupports* aBlockedContentSource,
@@ -1296,7 +1338,8 @@ nsCSPContext::AsyncReportViolation(nsISupports* aBlockedContentSource,
const nsAString& aObserverSubject,
const nsAString& aSourceFile,
const nsAString& aScriptSample,
uint32_t aLineNum)
uint32_t aLineNum,
uint32_t aColumnNum)
{
NS_ENSURE_ARG_MAX(aViolatedPolicyIndex, mPolicies.Length() - 1);
@@ -1309,6 +1352,7 @@ nsCSPContext::AsyncReportViolation(nsISupports* aBlockedContentSource,
aSourceFile,
aScriptSample,
aLineNum,
aColumnNum,
this));
return NS_OK;
}
+12 -3
View File
@@ -85,6 +85,7 @@ class nsCSPContext : public nsIContentSecurityPolicy
nsAString& aSourceFile,
nsAString& aScriptSample,
uint32_t aLineNum,
uint32_t aColumnNum,
mozilla::dom::SecurityPolicyViolationEventInit& aViolationEventInit);
nsresult SendReports(
@@ -101,7 +102,8 @@ class nsCSPContext : public nsIContentSecurityPolicy
const nsAString& aObserverSubject,
const nsAString& aSourceFile,
const nsAString& aScriptSample,
uint32_t aLineNum);
uint32_t aLineNum,
uint32_t aColumnNum);
// Hands off! Don't call this method unless you know what you
// are doing. It's only supposed to be called from within
@@ -110,10 +112,14 @@ class nsCSPContext : public nsIContentSecurityPolicy
mLoadingPrincipal = nullptr;
}
nsWeakPtr GetLoadingContext(){
nsWeakPtr GetLoadingContext() {
return mLoadingContext;
}
static uint32_t ScriptSampleMaxLength() {
return std::max(sScriptSampleMaxLength, 0);
}
private:
bool permitsInternal(CSPDirective aDir,
nsIURI* aContentLocation,
@@ -132,7 +138,10 @@ class nsCSPContext : public nsIContentSecurityPolicy
const nsAString& aContent,
const nsAString& aViolatedDirective,
uint32_t aViolatedPolicyIndex,
uint32_t aLineNumber);
uint32_t aLineNumber,
uint32_t aColumnNumber);
static int32_t sScriptSampleMaxLength;
nsString mReferrer;
uint64_t mInnerWindowID; // used for web console logging
+1 -1
View File
@@ -405,7 +405,7 @@ nsSMILCSSValueType::ValueFromString(nsCSSPropertyID aPropID,
if (doc && !nsStyleUtil::CSPAllowsInlineStyle(nullptr,
doc->NodePrincipal(),
doc->GetDocumentURI(),
0, aString, nullptr)) {
0, 0, aString, nullptr)) {
return;
}
+1
View File
@@ -16,6 +16,7 @@ dictionary CSPReportProperties {
DOMString source-file;
DOMString script-sample;
long line-number;
long column-number;
};
dictionary CSPReport {
+27 -8
View File
@@ -547,14 +547,21 @@ class LogViolationDetailsRunnable final : public WorkerMainThreadRunnable
{
nsString mFileName;
uint32_t mLineNum;
uint32_t mColumnNum;
nsString mScriptSample;
public:
LogViolationDetailsRunnable(WorkerPrivate* aWorker,
const nsString& aFileName,
uint32_t aLineNum)
uint32_t aLineNum,
uint32_t aColumnNum,
const nsAString& aScriptSample)
: WorkerMainThreadRunnable(aWorker,
NS_LITERAL_CSTRING("RuntimeService :: LogViolationDetails"))
, mFileName(aFileName), mLineNum(aLineNum)
, mFileName(aFileName)
, mLineNum(aLineNum)
, mColumnNum(aColumnNum)
, mScriptSample(aScriptSample)
{
MOZ_ASSERT(aWorker);
}
@@ -566,24 +573,38 @@ private:
};
bool
ContentSecurityPolicyAllows(JSContext* aCx)
ContentSecurityPolicyAllows(JSContext* aCx, JS::HandleValue aValue)
{
WorkerPrivate* worker = GetWorkerPrivateFromContext(aCx);
worker->AssertIsOnWorkerThread();
if (worker->GetReportCSPViolations()) {
JS::Rooted<JSString*> jsString(aCx, JS::ToString(aCx, aValue));
if (NS_WARN_IF(!jsString)) {
JS_ClearPendingException(aCx);
return false;
}
nsAutoJSString scriptSample;
if (NS_WARN_IF(!scriptSample.init(aCx, jsString))) {
JS_ClearPendingException(aCx);
return false;
}
nsString fileName;
uint32_t lineNum = 0;
uint32_t columnNum = 0;
JS::AutoFilename file;
if (JS::DescribeScriptedCaller(aCx, &file, &lineNum) && file.get()) {
if (JS::DescribeScriptedCaller(aCx, &file, &lineNum, &columnNum) && file.get()) {
fileName = NS_ConvertUTF8toUTF16(file.get());
} else {
MOZ_ASSERT(!JS_IsExceptionPending(aCx));
}
RefPtr<LogViolationDetailsRunnable> runnable =
new LogViolationDetailsRunnable(worker, fileName, lineNum);
new LogViolationDetailsRunnable(worker, fileName, lineNum, columnNum,
scriptSample);
ErrorResult rv;
runnable->Dispatch(Killing, rv);
@@ -2698,11 +2719,9 @@ LogViolationDetailsRunnable::MainThreadRun()
nsIContentSecurityPolicy* csp = mWorkerPrivate->GetCSP();
if (csp) {
NS_NAMED_LITERAL_STRING(scriptSample,
"Call to eval() or related function blocked by CSP.");
if (mWorkerPrivate->GetReportCSPViolations()) {
csp->LogViolationDetails(nsIContentSecurityPolicy::VIOLATION_TYPE_EVAL,
mFileName, scriptSample, mLineNum,
mFileName, mScriptSample, mLineNum, mColumnNum,
EmptyString(), EmptyString());
}
}
+1 -1
View File
@@ -1167,7 +1167,7 @@ private:
if (wcsp) {
wcsp->LogViolationDetails(
nsIContentSecurityPolicy::VIOLATION_TYPE_REQUIRE_SRI_FOR_SCRIPT,
aLoadInfo.mURL, EmptyString(), 0, EmptyString(), EmptyString());
aLoadInfo.mURL, EmptyString(), 0, 0, EmptyString(), EmptyString());
}
return NS_ERROR_SRI_CORRUPT;
}
+6 -4
View File
@@ -248,10 +248,11 @@ NS_IMETHODIMP
nsXBLContentSink::HandleStartElement(const char16_t *aName,
const char16_t **aAtts,
uint32_t aAttsCount,
uint32_t aLineNumber)
uint32_t aLineNumber,
uint32_t aColumnNumber)
{
nsresult rv = nsXMLContentSink::HandleStartElement(aName, aAtts, aAttsCount,
aLineNumber);
aLineNumber, aColumnNumber);
if (NS_FAILED(rv))
return rv;
@@ -850,7 +851,8 @@ nsXBLContentSink::ConstructParameter(const char16_t **aAtts)
nsresult
nsXBLContentSink::CreateElement(const char16_t** aAtts, uint32_t aAttsCount,
mozilla::dom::NodeInfo* aNodeInfo, uint32_t aLineNumber,
mozilla::dom::NodeInfo* aNodeInfo,
uint32_t aLineNumber, uint32_t aColumnNumber,
nsIContent** aResult, bool* aAppendContent,
FromParser aFromParser)
{
@@ -858,7 +860,7 @@ nsXBLContentSink::CreateElement(const char16_t** aAtts, uint32_t aAttsCount,
if (!aNodeInfo->NamespaceEquals(kNameSpaceID_XUL)) {
#endif
return nsXMLContentSink::CreateElement(aAtts, aAttsCount, aNodeInfo,
aLineNumber, aResult,
aLineNumber, aColumnNumber, aResult,
aAppendContent, aFromParser);
#ifdef MOZ_XUL
}
+4 -2
View File
@@ -69,7 +69,8 @@ public:
NS_IMETHOD HandleStartElement(const char16_t *aName,
const char16_t **aAtts,
uint32_t aAttsCount,
uint32_t aLineNumber) override;
uint32_t aLineNumber,
uint32_t aColumnNumber) override;
NS_IMETHOD HandleEndElement(const char16_t *aName) override;
@@ -89,7 +90,8 @@ protected:
bool NotifyForDocElement() override { return false; }
nsresult CreateElement(const char16_t** aAtts, uint32_t aAttsCount,
mozilla::dom::NodeInfo* aNodeInfo, uint32_t aLineNumber,
mozilla::dom::NodeInfo* aNodeInfo,
uint32_t aLineNumber, uint32_t aColumnNumber,
nsIContent** aResult, bool* aAppendContent,
mozilla::dom::FromParser aFromParser) override;
+9 -4
View File
@@ -446,7 +446,8 @@ nsXMLContentSink::SetParser(nsParserBase* aParser)
nsresult
nsXMLContentSink::CreateElement(const char16_t** aAtts, uint32_t aAttsCount,
mozilla::dom::NodeInfo* aNodeInfo, uint32_t aLineNumber,
mozilla::dom::NodeInfo* aNodeInfo,
uint32_t aLineNumber, uint32_t aColumnNumber,
nsIContent** aResult, bool* aAppendContent,
FromParser aFromParser)
{
@@ -466,6 +467,7 @@ nsXMLContentSink::CreateElement(const char16_t** aAtts, uint32_t aAttsCount,
) {
nsCOMPtr<nsIScriptElement> sele = do_QueryInterface(content);
sele->SetScriptLineNumber(aLineNumber);
sele->SetScriptColumnNumber(aColumnNumber);
sele->SetCreatorParser(GetParser());
}
@@ -500,6 +502,7 @@ nsXMLContentSink::CreateElement(const char16_t** aAtts, uint32_t aAttsCount,
}
if (!aNodeInfo->Equals(nsGkAtoms::link, kNameSpaceID_XHTML)) {
ssle->SetLineNumber(aFromParser ? aLineNumber : 0);
ssle->SetColumnNumber(aFromParser ? aColumnNumber : 0);
}
}
}
@@ -918,10 +921,11 @@ NS_IMETHODIMP
nsXMLContentSink::HandleStartElement(const char16_t *aName,
const char16_t **aAtts,
uint32_t aAttsCount,
uint32_t aLineNumber)
uint32_t aLineNumber,
uint32_t aColumnNumber)
{
return HandleStartElement(aName, aAtts, aAttsCount, aLineNumber,
true);
aColumnNumber, true);
}
nsresult
@@ -929,6 +933,7 @@ nsXMLContentSink::HandleStartElement(const char16_t *aName,
const char16_t **aAtts,
uint32_t aAttsCount,
uint32_t aLineNumber,
uint32_t aColumnNumber,
bool aInterruptable)
{
NS_PRECONDITION(aAttsCount % 2 == 0, "incorrect aAttsCount");
@@ -963,7 +968,7 @@ nsXMLContentSink::HandleStartElement(const char16_t *aName,
nsIDOMNode::ELEMENT_NODE);
result = CreateElement(aAtts, aAttsCount, nodeInfo, aLineNumber,
getter_AddRefs(content), &appendContent,
aColumnNumber, getter_AddRefs(content), &appendContent,
FROM_PARSER_NETWORK);
NS_ENSURE_SUCCESS(result, result);
+3 -2
View File
@@ -114,7 +114,8 @@ protected:
nsIContent *aContent);
virtual bool NotifyForDocElement() { return true; }
virtual nsresult CreateElement(const char16_t** aAtts, uint32_t aAttsCount,
mozilla::dom::NodeInfo* aNodeInfo, uint32_t aLineNumber,
mozilla::dom::NodeInfo* aNodeInfo,
uint32_t aLineNumber, uint32_t aColumnNumber,
nsIContent** aResult, bool* aAppendContent,
mozilla::dom::FromParser aFromParser);
@@ -161,7 +162,7 @@ protected:
nsresult HandleStartElement(const char16_t *aName, const char16_t **aAtts,
uint32_t aAttsCount, uint32_t aLineNumber,
bool aInterruptable);
uint32_t aColumnNumber, bool aInterruptable);
nsresult HandleEndElement(const char16_t *aName, bool aInterruptable);
nsresult HandleCharacterData(const char16_t *aData, uint32_t aLength,
bool aInterruptable);
+5 -2
View File
@@ -83,7 +83,8 @@ protected:
nsIAtom* aTagName,
nsIContent* aContent) override;
virtual nsresult CreateElement(const char16_t** aAtts, uint32_t aAttsCount,
mozilla::dom::NodeInfo* aNodeInfo, uint32_t aLineNumber,
mozilla::dom::NodeInfo* aNodeInfo,
uint32_t aLineNumber, uint32_t aColumnNumber,
nsIContent** aResult, bool* aAppendContent,
mozilla::dom::FromParser aFromParser) override;
virtual nsresult CloseElement(nsIContent* aContent) override;
@@ -199,7 +200,8 @@ nsXMLFragmentContentSink::SetDocElement(int32_t aNameSpaceID,
nsresult
nsXMLFragmentContentSink::CreateElement(const char16_t** aAtts, uint32_t aAttsCount,
mozilla::dom::NodeInfo* aNodeInfo, uint32_t aLineNumber,
mozilla::dom::NodeInfo* aNodeInfo,
uint32_t aLineNumber, uint32_t aColumnNumber,
nsIContent** aResult, bool* aAppendContent,
FromParser /*aFromParser*/)
{
@@ -207,6 +209,7 @@ nsXMLFragmentContentSink::CreateElement(const char16_t** aAtts, uint32_t aAttsCo
// fancy CloseElement stuff.
nsresult rv = nsXMLContentSink::CreateElement(aAtts, aAttsCount,
aNodeInfo, aLineNumber,
aColumnNumber,
aResult, aAppendContent,
NOT_FROM_PARSER);
@@ -122,7 +122,8 @@ NS_IMETHODIMP
txStylesheetSink::HandleStartElement(const char16_t *aName,
const char16_t **aAtts,
uint32_t aAttsCount,
uint32_t aLineNumber)
uint32_t aLineNumber,
uint32_t aColumnNumber)
{
NS_PRECONDITION(aAttsCount % 2 == 0, "incorrect aAttsCount");
+4 -3
View File
@@ -437,7 +437,8 @@ NS_IMETHODIMP
XULContentSinkImpl::HandleStartElement(const char16_t *aName,
const char16_t **aAtts,
uint32_t aAttsCount,
uint32_t aLineNumber)
uint32_t aLineNumber,
uint32_t aColumnNumber)
{
// XXX Hopefully the parser will flag this before we get here. If
// we're in the epilog, there should be no new elements
@@ -694,7 +695,7 @@ XULContentSinkImpl::ReportError(const char16_t* aErrorText,
parsererror.Append((char16_t)0xFFFF);
parsererror.AppendLiteral("parsererror");
rv = HandleStartElement(parsererror.get(), noAtts, 0, 0);
rv = HandleStartElement(parsererror.get(), noAtts, 0, 0, 0);
NS_ENSURE_SUCCESS(rv,rv);
rv = HandleCharacterData(aErrorText, NS_strlen(aErrorText));
@@ -704,7 +705,7 @@ XULContentSinkImpl::ReportError(const char16_t* aErrorText,
sourcetext.Append((char16_t)0xFFFF);
sourcetext.AppendLiteral("sourcetext");
rv = HandleStartElement(sourcetext.get(), noAtts, 0, 0);
rv = HandleStartElement(sourcetext.get(), noAtts, 0, 0, 0);
NS_ENSURE_SUCCESS(rv,rv);
rv = HandleCharacterData(aSourceText, NS_strlen(aSourceText));
+2 -2
View File
@@ -64,10 +64,10 @@ typedef bool
/*
* Used to check if a CSP instance wants to disable eval() and friends.
* See js_CheckCSPPermitsJSAction() in jsobj.
* See GlobalObject::isRuntimeCodeGenEnabled() in vm/GlobalObject.cpp.
*/
typedef bool
(* JSCSPEvalChecker)(JSContext* cx);
(* JSCSPEvalChecker)(JSContext* cx, JS::HandleValue aValue);
struct JSSecurityCallbacks {
JSCSPEvalChecker contentSecurityPolicyAllows;
+3 -2
View File
@@ -227,7 +227,7 @@ EvalKernel(JSContext* cx, HandleValue v, EvalType evalType, AbstractFramePtr cal
AssertInnerizedEnvironmentChain(cx, *env);
Rooted<GlobalObject*> envGlobal(cx, &env->global());
if (!GlobalObject::isRuntimeCodeGenEnabled(cx, envGlobal)) {
if (!GlobalObject::isRuntimeCodeGenEnabled(cx, v, envGlobal)) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CSP_BLOCKED_EVAL);
return false;
}
@@ -330,7 +330,8 @@ js::DirectEvalStringFromIon(JSContext* cx,
AssertInnerizedEnvironmentChain(cx, *env);
Rooted<GlobalObject*> envGlobal(cx, &env->global());
if (!GlobalObject::isRuntimeCodeGenEnabled(cx, envGlobal)) {
RootedValue v(cx, StringValue(str));
if (!GlobalObject::isRuntimeCodeGenEnabled(cx, v, envGlobal)) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CSP_BLOCKED_EVAL);
return false;
}
+8 -7
View File
@@ -1625,13 +1625,6 @@ static bool
FunctionConstructor(JSContext* cx, const CallArgs& args, GeneratorKind generatorKind,
FunctionAsyncKind asyncKind)
{
// Block this call if security callbacks forbid it.
Rooted<GlobalObject*> global(cx, &args.callee().global());
if (!GlobalObject::isRuntimeCodeGenEnabled(cx, global)) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CSP_BLOCKED_FUNCTION);
return false;
}
bool isStarGenerator = generatorKind == StarGenerator;
bool isAsync = asyncKind == AsyncFunction;
MOZ_ASSERT(generatorKind != LegacyGenerator);
@@ -1733,6 +1726,14 @@ FunctionConstructor(JSContext* cx, const CallArgs& args, GeneratorKind generator
if (!functionText)
return false;
// Block this call if security callbacks forbid it.
Rooted<GlobalObject*> global(cx, &args.callee().global());
RootedValue v(cx, StringValue(functionText));
if (!GlobalObject::isRuntimeCodeGenEnabled(cx, v, global)) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CSP_BLOCKED_FUNCTION);
return false;
}
/*
* NB: (new Function) is not lexically closed by its caller, it's just an
* anonymous function in the top-level scope that its constructor inhabits.
+10 -4
View File
@@ -543,17 +543,23 @@ GlobalObject::initSelfHostingBuiltins(JSContext* cx, Handle<GlobalObject*> globa
}
/* static */ bool
GlobalObject::isRuntimeCodeGenEnabled(JSContext* cx, Handle<GlobalObject*> global)
GlobalObject::isRuntimeCodeGenEnabled(JSContext* cx, HandleValue code,
Handle<GlobalObject*> global)
{
HeapSlot& v = global->getSlotRef(RUNTIME_CODEGEN_ENABLED);
if (v.isUndefined()) {
/*
* If there are callbacks, make sure that the CSP callback is installed
* and that it permits runtime code generation, then cache the result.
* and that it permits runtime code generation.
*/
JSCSPEvalChecker allows = cx->runtime()->securityCallbacks->contentSecurityPolicyAllows;
Value boolValue = BooleanValue(!allows || allows(cx));
v.set(global, HeapSlot::Slot, RUNTIME_CODEGEN_ENABLED, boolValue);
if (allows)
return allows(cx, code);
// Let's cache the result only if the contentSecurityPolicyAllows callback is not set. In
// this way, contentSecurityPolicyAllows callback is executed each time, with the current
// HandleValue code.
v.set(global, HeapSlot::Slot, RUNTIME_CODEGEN_ENABLED, JS::TrueValue());
}
return !v.isFalse();
}
+2 -1
View File
@@ -803,7 +803,8 @@ class GlobalObject : public NativeObject
template<typename T>
inline Value createArrayFromBuffer() const;
static bool isRuntimeCodeGenEnabled(JSContext* cx, Handle<GlobalObject*> global);
static bool isRuntimeCodeGenEnabled(JSContext* cx, HandleValue code,
Handle<GlobalObject*> global);
// Warn about use of the deprecated watch/unwatch functions in the global
// in which |obj| was created, if no prior warning was given.
+1 -1
View File
@@ -955,7 +955,7 @@ SheetLoadData::OnStreamComplete(nsIUnicharStreamLoader* aLoader,
csp->LogViolationDetails(
nsIContentSecurityPolicy::VIOLATION_TYPE_REQUIRE_SRI_FOR_STYLE,
NS_ConvertUTF8toUTF16(spec), EmptyString(),
0, EmptyString(), EmptyString());
0, 0, EmptyString(), EmptyString());
return NS_OK;
}
} else {
+2 -1
View File
@@ -739,6 +739,7 @@ nsStyleUtil::CSPAllowsInlineStyle(nsIContent* aContent,
nsIPrincipal* aPrincipal,
nsIURI* aSourceURI,
uint32_t aLineNumber,
uint32_t aColumnNumber,
const nsSubstring& aStyleText,
nsresult* aRv)
{
@@ -776,7 +777,7 @@ nsStyleUtil::CSPAllowsInlineStyle(nsIContent* aContent,
rv = csp->GetAllowsInline(nsIContentPolicy::TYPE_STYLESHEET,
nonce,
false, // aParserCreated only applies to scripts
aStyleText, aLineNumber,
aStyleText, aLineNumber, aColumnNumber,
&allowInlineStyle);
NS_ENSURE_SUCCESS(rv, false);
+4
View File
@@ -164,6 +164,9 @@ public:
* @param aLineNumber
* Line number of inline style element in the containing document (for
* reporting violations)
* @param aColumnNumber
* Column number of inline style element in the containing document (for
* reporting violations)
* @param aStyleText
* Contents of the inline style element (for reporting violations)
* @param aRv
@@ -175,6 +178,7 @@ public:
nsIPrincipal* aPrincipal,
nsIURI* aSourceURI,
uint32_t aLineNumber,
uint32_t aColumnNumber,
const nsSubstring& aStyleText,
nsresult* aRv);
+2 -1
View File
@@ -394,7 +394,8 @@ nsExpatDriver::HandleStartElement(const char16_t *aValue,
nsresult rv = mSink->
HandleStartElement(aValue, aAtts, attrArrayLength,
XML_GetCurrentLineNumber(mExpatParser));
XML_GetCurrentLineNumber(mExpatParser),
XML_GetCurrentColumnNumber(mExpatParser));
MaybeStopParser(rv);
}
}
+3 -1
View File
@@ -28,11 +28,13 @@ interface nsIExpatSink : nsISupports
* present in aAtts.
* @param aAttsCount the number of elements in aAtts.
* @param aLineNumber the line number of the start tag in the data stream.
* @param aColumnNumber the column number of the start tag in the data stream.
*/
void HandleStartElement(in wstring aName,
[array, size_is(aAttsCount)] in wstring aAtts,
in unsigned long aAttsCount,
in unsigned long aLineNumber);
in unsigned long aLineNumber,
in unsigned long aColumnNumber);
/**
* Called to handle the closing tag of an element.
+2 -1
View File
@@ -82,7 +82,8 @@ NS_IMETHODIMP
nsSAXXMLReader::HandleStartElement(const char16_t *aName,
const char16_t **aAtts,
uint32_t aAttsCount,
uint32_t aLineNumber)
uint32_t aLineNumber,
uint32_t aColumnNumber)
{
if (!mContentHandler)
return NS_OK;
+3 -2
View File
@@ -388,8 +388,9 @@ RDFContentSinkImpl::QueryInterface(REFNSIID iid, void** result)
NS_IMETHODIMP
RDFContentSinkImpl::HandleStartElement(const char16_t *aName,
const char16_t **aAtts,
uint32_t aAttsCount,
uint32_t aLineNumber)
uint32_t aAttsCount,
uint32_t aLineNumber,
uint32_t aColumnNumber)
{
FlushText();