Bug 1570738 - Record Telemetry if eval() is used in the Parent Process r=ckerschb

Differential Revision: https://phabricator.services.mozilla.com/D40332

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Tom Ritter 2019-08-06 19:56:23 +00:00
Родитель 4a8f79932f
Коммит 20c32a0175
7 изменённых файлов: 98 добавлений и 47 удалений

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

@ -500,9 +500,10 @@ pref("browser.tabs.remote.enforceRemoteTypeRestrictions", true);
#endif #endif
#ifdef NIGHTLY_BUILD #ifdef NIGHTLY_BUILD
// allow_eval_with_system_principal is enabled on Firefox Desktop only at this // allow_eval_* is enabled on Firefox Desktop only at this
// point in time // point in time
pref("security.allow_eval_with_system_principal", false); pref("security.allow_eval_with_system_principal", false);
pref("security.allow_eval_in_parent_process", false);
pref("browser.tabs.remote.useHTTPResponseProcessSelection", true); pref("browser.tabs.remote.useHTTPResponseProcessSelection", true);
#else #else
// Disabled outside of nightly due to bug 1554217 // Disabled outside of nightly due to bug 1554217

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

@ -424,11 +424,12 @@ bool nsScriptSecurityManager::ContentSecurityPolicyPermitsJSAction(
nsresult rv = csp->GetAllowsEval(&reportViolation, &evalOK); nsresult rv = csp->GetAllowsEval(&reportViolation, &evalOK);
// A little convoluted. We want the scriptSample for a) reporting a violation // A little convoluted. We want the scriptSample for a) reporting a violation
// or b) passing it to AssertEvalNotUsingSystemPrincipal. So do the work to // or b) passing it to AssertEvalNotUsingSystemPrincipal or c) we're in the
// get it if either of those cases is true. // parent process. So do the work to get it if either of those cases is true.
nsAutoJSString scriptSample; nsAutoJSString scriptSample;
nsCOMPtr<nsIPrincipal> subjectPrincipal = nsContentUtils::SubjectPrincipal(); nsCOMPtr<nsIPrincipal> subjectPrincipal = nsContentUtils::SubjectPrincipal();
if (reportViolation || subjectPrincipal->IsSystemPrincipal()) { if (reportViolation || subjectPrincipal->IsSystemPrincipal() ||
XRE_IsE10sParentProcess()) {
JS::Rooted<JSString*> jsString(cx, JS::ToString(cx, aValue)); JS::Rooted<JSString*> jsString(cx, JS::ToString(cx, aValue));
if (NS_WARN_IF(!jsString)) { if (NS_WARN_IF(!jsString)) {
JS_ClearPendingException(cx); JS_ClearPendingException(cx);
@ -442,8 +443,8 @@ bool nsScriptSecurityManager::ContentSecurityPolicyPermitsJSAction(
} }
#if !defined(ANDROID) && (defined(NIGHTLY_BUILD) || defined(DEBUG)) #if !defined(ANDROID) && (defined(NIGHTLY_BUILD) || defined(DEBUG))
nsContentSecurityManager::AssertEvalNotUsingSystemPrincipal( nsContentSecurityManager::AssertEvalNotRestricted(cx, subjectPrincipal,
cx, subjectPrincipal, scriptSample); scriptSample);
#endif #endif
if (NS_FAILED(rv)) { if (NS_FAILED(rv)) {

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

@ -33,8 +33,8 @@ nsresult CheckInternal(nsIContentSecurityPolicy* aCSP,
#if !defined(ANDROID) && (defined(NIGHTLY_BUILD) || defined(DEBUG)) #if !defined(ANDROID) && (defined(NIGHTLY_BUILD) || defined(DEBUG))
JSContext* cx = nsContentUtils::GetCurrentJSContext(); JSContext* cx = nsContentUtils::GetCurrentJSContext();
nsContentSecurityManager::AssertEvalNotUsingSystemPrincipal( nsContentSecurityManager::AssertEvalNotRestricted(cx, aSubjectPrincipal,
cx, aSubjectPrincipal, aExpression); aExpression);
#endif #endif
// The value is set at any "return", but better to have a default value here. // The value is set at any "return", but better to have a default value here.

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

@ -71,6 +71,8 @@ static NS_NAMED_LITERAL_STRING(sAllowedEval1, "this");
static NS_NAMED_LITERAL_STRING(sAllowedEval2, static NS_NAMED_LITERAL_STRING(sAllowedEval2,
"function anonymous(\n) {\nreturn this\n}"); "function anonymous(\n) {\nreturn this\n}");
static Atomic<bool, mozilla::Relaxed> sTelemetryEventEnabled(false);
/* static */ /* static */
bool nsContentSecurityManager::AllowTopLevelNavigationToDataURI( bool nsContentSecurityManager::AllowTopLevelNavigationToDataURI(
nsIChannel* aChannel) { nsIChannel* aChannel) {
@ -384,20 +386,32 @@ FilenameType nsContentSecurityManager::FilenameToEvalType(
} }
/* static */ /* static */
void nsContentSecurityManager::AssertEvalNotUsingSystemPrincipal( void nsContentSecurityManager::AssertEvalNotRestricted(
JSContext* cx, nsIPrincipal* aSubjectPrincipal, const nsAString& aScript) { JSContext* cx, nsIPrincipal* aSubjectPrincipal, const nsAString& aScript) {
if (!aSubjectPrincipal->IsSystemPrincipal()) { bool systemPrincipal = aSubjectPrincipal->IsSystemPrincipal();
if (systemPrincipal &&
StaticPrefs::security_allow_eval_with_system_principal()) {
MOZ_LOG(
sCSMLog, LogLevel::Debug,
("Allowing eval() %s because allowing pref is "
"enabled",
(systemPrincipal ? "with System Principal" : "in parent process")));
return; return;
} }
// Use static pref for performance reasons. if (XRE_IsE10sParentProcess() &&
if (StaticPrefs::security_allow_eval_with_system_principal()) { StaticPrefs::security_allow_eval_in_parent_process()) {
MOZ_LOG(sCSMLog, LogLevel::Debug, MOZ_LOG(sCSMLog, LogLevel::Debug,
("Allowing eval() with SystemPrincipal because allowing pref is " ("Allowing eval() in parent process because allowing pref is "
"enabled")); "enabled"));
return; return;
} }
if (!systemPrincipal && !XRE_IsE10sParentProcess()) {
// Usage of eval we are unconcerned with.
return;
}
// This preferences is a file used for autoconfiguration of Firefox // This preferences is a file used for autoconfiguration of Firefox
// by administrators. It has also been (ab)used by the userChromeJS // by administrators. It has also been (ab)used by the userChromeJS
// project to run legacy-style 'extensions', some of which use eval, // project to run legacy-style 'extensions', some of which use eval,
@ -405,43 +419,63 @@ void nsContentSecurityManager::AssertEvalNotUsingSystemPrincipal(
nsAutoString configPref; nsAutoString configPref;
Preferences::GetString("general.config.filename", configPref); Preferences::GetString("general.config.filename", configPref);
if (!configPref.IsEmpty()) { if (!configPref.IsEmpty()) {
MOZ_LOG(sCSMLog, LogLevel::Debug, MOZ_LOG(
("Allowing eval() with SystemPrincipal because of " sCSMLog, LogLevel::Debug,
"general.config.filename")); ("Allowing eval() %s because of "
"general.config.filename",
(systemPrincipal ? "with System Principal" : "in parent process")));
return; return;
} }
// We permit these two common idioms to get access to the global JS object // We permit these two common idioms to get access to the global JS object
if (!aScript.IsEmpty() && if (!aScript.IsEmpty() &&
(aScript == sAllowedEval1 || aScript == sAllowedEval2)) { (aScript == sAllowedEval1 || aScript == sAllowedEval2)) {
MOZ_LOG(sCSMLog, LogLevel::Debug, MOZ_LOG(
("Allowing eval() with SystemPrincipal because a key string is " sCSMLog, LogLevel::Debug,
"provided")); ("Allowing eval() %s because a key string is "
"provided",
(systemPrincipal ? "with System Principal" : "in parent process")));
return; return;
} }
nsAutoCString fileName; // Check the allowlist for the provided filename. getFilename is a helper
JS::AutoFilename scriptFilename; // function
if (JS::DescribeScriptedCaller(cx, &scriptFilename)) { auto getFilename = [](JSContext* cx) -> const nsCString {
nsDependentCSubstring fileName_(scriptFilename.get(), JS::AutoFilename scriptFilename;
strlen(scriptFilename.get())); if (JS::DescribeScriptedCaller(cx, &scriptFilename)) {
ToLowerCase(fileName_); nsDependentCSubstring fileName_(scriptFilename.get(),
// Extract file name alone if scriptFilename contains line number strlen(scriptFilename.get()));
// separated by multiple space delimiters in few cases. ToLowerCase(fileName_);
int32_t fileNameIndex = fileName_.FindChar(' '); // Extract file name alone if scriptFilename contains line number
if (fileNameIndex != -1) { // separated by multiple space delimiters in few cases.
fileName_.SetLength(fileNameIndex); int32_t fileNameIndex = fileName_.FindChar(' ');
} if (fileNameIndex != -1) {
fileName_.SetLength(fileNameIndex);
for (const nsLiteralCString& allowlistEntry : evalAllowlist) {
if (fileName_.Equals(allowlistEntry)) {
return;
} }
}
fileName = fileName_; nsAutoCString fileName(fileName_);
return std::move(fileName);
}
return NS_LITERAL_CSTRING("unknown-file");
};
nsCString fileName = getFilename(cx);
for (const nsLiteralCString& allowlistEntry : evalAllowlist) {
if (fileName.Equals(allowlistEntry)) {
MOZ_LOG(
sCSMLog, LogLevel::Debug,
("Allowing eval() %s because the containing "
"file is in the allowlist",
(systemPrincipal ? "with System Principal" : "in parent process")));
return;
}
} }
// Send Telemetry
Telemetry::EventID eventType =
systemPrincipal ? Telemetry::EventID::Security_Evalusage_Systemcontext
: Telemetry::EventID::Security_Evalusage_Parentprocess;
FilenameType fileNameType = FilenameType fileNameType =
FilenameToEvalType(NS_ConvertUTF8toUTF16(fileName)); FilenameToEvalType(NS_ConvertUTF8toUTF16(fileName));
mozilla::Maybe<nsTArray<EventExtraEntry>> extra; mozilla::Maybe<nsTArray<EventExtraEntry>> extra;
@ -452,18 +486,25 @@ void nsContentSecurityManager::AssertEvalNotUsingSystemPrincipal(
} else { } else {
extra = Nothing(); extra = Nothing();
} }
Telemetry::RecordEvent(Telemetry::EventID::Security_Evalusage_Systemcontext, if (!sTelemetryEventEnabled.exchange(true)) {
mozilla::Some(fileNameType.first()), extra); sTelemetryEventEnabled = true;
Telemetry::SetEventRecordingEnabled(
NS_LITERAL_CSTRING("security.evalUsage"), true);
}
Telemetry::RecordEvent(eventType, mozilla::Some(fileNameType.first()), extra);
// Crash or Log
#ifdef DEBUG #ifdef DEBUG
MOZ_CRASH_UNSAFE_PRINTF( MOZ_CRASH_UNSAFE_PRINTF(
"Blocking eval() with SystemPrincipal from file %s and script provided " "Blocking eval() %s from file %s and script provided "
"%s", "%s",
(systemPrincipal ? "with System Principal" : "in parent process"),
fileName.get(), NS_ConvertUTF16toUTF8(aScript).get()); fileName.get(), NS_ConvertUTF16toUTF8(aScript).get());
#else #else
MOZ_LOG(sCSMLog, LogLevel::Debug, MOZ_LOG(sCSMLog, LogLevel::Warning,
("Blocking eval() with SystemPrincipal from file %s and script " ("Blocking eval() %s from file %s and script "
"provided %s", "provided %s",
(systemPrincipal ? "with System Principal" : "in parent process"),
fileName.get(), NS_ConvertUTF16toUTF8(aScript).get())); fileName.get(), NS_ConvertUTF16toUTF8(aScript).get()));
#endif #endif

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

@ -41,9 +41,9 @@ class nsContentSecurityManager : public nsIContentSecurityManager,
static bool AllowInsecureRedirectToDataURI(nsIChannel* aNewChannel); static bool AllowInsecureRedirectToDataURI(nsIChannel* aNewChannel);
static FilenameType FilenameToEvalType(const nsString& fileName); static FilenameType FilenameToEvalType(const nsString& fileName);
static void AssertEvalNotUsingSystemPrincipal(JSContext* cx, static void AssertEvalNotRestricted(JSContext* cx,
nsIPrincipal* aSubjectPrincipal, nsIPrincipal* aSubjectPrincipal,
const nsAString& aScript); const nsAString& aScript);
private: private:
static nsresult CheckChannel(nsIChannel* aChannel); static nsresult CheckChannel(nsIChannel* aChannel);

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

@ -5971,6 +5971,13 @@
value: true value: true
mirror: always mirror: always
# Disabled by default so it doesn't affect Thunderbird/SeaMonkey, but
# enabled in firefox.js
- name: security.allow_eval_in_parent_process
type: bool
value: true
mirror: always
# Whether strict file origin policy is in effect. # Whether strict file origin policy is in effect.
- name: security.fileuri.strict_origin_policy - name: security.fileuri.strict_origin_policy
type: RelaxedAtomicBool type: RelaxedAtomicBool

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

@ -1593,11 +1593,12 @@ intl.ui.browserLanguage:
security: security:
evalUsage: evalUsage:
objects: ["systemContext"] objects: ["systemContext", "parentProcess"]
bug_numbers: bug_numbers:
- 1567623 - 1567623
description: > description: >
eval() (or an eval()-like method) was called while running in the System Principal Context. eval() (or an eval()-like method) was called while running in the System Principal
context or the Parent Process.
Expected values are: Expected values are:
chromeuri - chrome:// file chromeuri - chrome:// file
resourceuri - resource:// file resourceuri - resource:// file