Backed out 3 changesets (bug 1905239) for causing build bustages @ FrontendContext.cpp CLOSED TREE

Backed out changeset 029305d7a99b (bug 1905239)
Backed out changeset a282ae3a55f7 (bug 1905239)
Backed out changeset f27e56ebfc0b (bug 1905239)
This commit is contained in:
Alexandru Marc
2024-12-11 12:16:57 +02:00
parent 34faf9734d
commit 2e177dcfbb
15 changed files with 49 additions and 508 deletions

View File

@@ -73,8 +73,6 @@
#include "nsJSUtils.h" #include "nsJSUtils.h"
#include "nsILoadInfo.h" #include "nsILoadInfo.h"
#include "js/ColumnNumber.h" // JS::ColumnNumberOneOrigin #include "js/ColumnNumber.h" // JS::ColumnNumberOneOrigin
#include "js/GCVector.h"
#include "js/Value.h"
// This should be probably defined on some other place... but I couldn't find it // This should be probably defined on some other place... but I couldn't find it
#define WEBAPPS_PERM_NAME "webapps-manage" #define WEBAPPS_PERM_NAME "webapps-manage"
@@ -464,12 +462,7 @@ NS_IMPL_ISUPPORTS(nsScriptSecurityManager, nsIScriptSecurityManager)
///////////////// Security Checks ///////////////// ///////////////// Security Checks /////////////////
bool nsScriptSecurityManager::ContentSecurityPolicyPermitsJSAction( bool nsScriptSecurityManager::ContentSecurityPolicyPermitsJSAction(
JSContext* cx, JS::RuntimeCode aKind, JS::Handle<JSString*> aCodeString, JSContext* cx, JS::RuntimeCode aKind, JS::Handle<JSString*> aCode) {
JS::CompilationType aCompilationType,
JS::Handle<JS::StackGCVector<JSString*>> aParameterStrings,
JS::Handle<JSString*> aBodyString,
JS::Handle<JS::StackGCVector<JS::Value>> aParameterArgs,
JS::Handle<JS::Value> aBodyArg, bool* aOutCanCompileStrings) {
MOZ_ASSERT(cx == nsContentUtils::GetCurrentJSContext()); MOZ_ASSERT(cx == nsContentUtils::GetCurrentJSContext());
nsCOMPtr<nsIPrincipal> subjectPrincipal = nsContentUtils::SubjectPrincipal(); nsCOMPtr<nsIPrincipal> subjectPrincipal = nsContentUtils::SubjectPrincipal();
@@ -484,14 +477,13 @@ bool nsScriptSecurityManager::ContentSecurityPolicyPermitsJSAction(
if (contextForbidsEval) { if (contextForbidsEval) {
nsAutoJSString scriptSample; nsAutoJSString scriptSample;
if (aKind == JS::RuntimeCode::JS && if (aKind == JS::RuntimeCode::JS &&
NS_WARN_IF(!scriptSample.init(cx, aCodeString))) { NS_WARN_IF(!scriptSample.init(cx, aCode))) {
return false; return false;
} }
if (!nsContentSecurityUtils::IsEvalAllowed( if (!nsContentSecurityUtils::IsEvalAllowed(
cx, subjectPrincipal->IsSystemPrincipal(), scriptSample)) { cx, subjectPrincipal->IsSystemPrincipal(), scriptSample)) {
*aOutCanCompileStrings = false; return false;
return true;
} }
} }
@@ -511,7 +503,6 @@ bool nsScriptSecurityManager::ContentSecurityPolicyPermitsJSAction(
} }
// don't do anything unless there's a CSP // don't do anything unless there's a CSP
if (!csp) { if (!csp) {
*aOutCanCompileStrings = true;
return true; return true;
} }
} }
@@ -531,8 +522,7 @@ bool nsScriptSecurityManager::ContentSecurityPolicyPermitsJSAction(
nsresult rv = csp->GetAllowsEval(&reportViolation, &evalOK); nsresult rv = csp->GetAllowsEval(&reportViolation, &evalOK);
if (NS_FAILED(rv)) { if (NS_FAILED(rv)) {
NS_WARNING("CSP: failed to get allowsEval"); NS_WARNING("CSP: failed to get allowsEval");
*aOutCanCompileStrings = true; // fail open to not break sites. return true; // fail open to not break sites.
return true;
} }
} else { } else {
if (NS_FAILED(csp->GetAllowsWasmEval(&reportViolation, &evalOK))) { if (NS_FAILED(csp->GetAllowsWasmEval(&reportViolation, &evalOK))) {
@@ -555,7 +545,8 @@ bool nsScriptSecurityManager::ContentSecurityPolicyPermitsJSAction(
auto caller = JSCallingLocation::Get(cx); auto caller = JSCallingLocation::Get(cx);
nsAutoJSString scriptSample; nsAutoJSString scriptSample;
if (aKind == JS::RuntimeCode::JS && if (aKind == JS::RuntimeCode::JS &&
NS_WARN_IF(!scriptSample.init(cx, aCodeString))) { NS_WARN_IF(!scriptSample.init(cx, aCode))) {
JS_ClearPendingException(cx);
return false; return false;
} }
uint16_t violationType = uint16_t violationType =
@@ -568,8 +559,7 @@ bool nsScriptSecurityManager::ContentSecurityPolicyPermitsJSAction(
caller.mLine, caller.mColumn, u""_ns, u""_ns); caller.mLine, caller.mColumn, u""_ns, u""_ns);
} }
*aOutCanCompileStrings = evalOK; return evalOK;
return true;
} }
// static // static
@@ -1564,7 +1554,6 @@ void nsScriptSecurityManager::InitJSCallbacks(JSContext* aCx) {
static const JSSecurityCallbacks securityCallbacks = { static const JSSecurityCallbacks securityCallbacks = {
ContentSecurityPolicyPermitsJSAction, ContentSecurityPolicyPermitsJSAction,
nullptr, // codeForEvalGets
JSPrincipalsSubsume, JSPrincipalsSubsume,
}; };

View File

@@ -28,14 +28,17 @@ class SystemPrincipal;
namespace JS { namespace JS {
enum class RuntimeCode; enum class RuntimeCode;
enum class CompilationType;
} // namespace JS } // namespace JS
///////////////////////////// /////////////////////////////
// nsScriptSecurityManager // // nsScriptSecurityManager //
///////////////////////////// /////////////////////////////
#define NS_SCRIPTSECURITYMANAGER_CID \ #define NS_SCRIPTSECURITYMANAGER_CID \
{0x7ee2a4c0, 0x4b93, 0x17d3, {0xba, 0x18, 0x00, 0x60, 0xb0, 0xf1, 0x99, 0xa2}} { \
0x7ee2a4c0, 0x4b93, 0x17d3, { \
0xba, 0x18, 0x00, 0x60, 0xb0, 0xf1, 0x99, 0xa2 \
} \
}
class nsScriptSecurityManager final : public nsIScriptSecurityManager { class nsScriptSecurityManager final : public nsIScriptSecurityManager {
public: public:
@@ -88,13 +91,9 @@ class nsScriptSecurityManager final : public nsIScriptSecurityManager {
virtual ~nsScriptSecurityManager(); virtual ~nsScriptSecurityManager();
// Decides, based on CSP, whether or not eval() and stuff can be executed. // Decides, based on CSP, whether or not eval() and stuff can be executed.
static bool ContentSecurityPolicyPermitsJSAction( static bool ContentSecurityPolicyPermitsJSAction(JSContext* cx,
JSContext* aCx, JS::RuntimeCode aKind, JS::Handle<JSString*> aCodeString, JS::RuntimeCode kind,
JS::CompilationType aCompilationType, JS::Handle<JSString*> aCode);
JS::Handle<JS::StackGCVector<JSString*>> aParameterStrings,
JS::Handle<JSString*> aBodyString,
JS::Handle<JS::StackGCVector<JS::Value>> aParameterArgs,
JS::Handle<JS::Value> aBodyArg, bool* aOutCanCompileStrings);
static bool JSPrincipalsSubsume(JSPrincipals* first, JSPrincipals* second); static bool JSPrincipalsSubsume(JSPrincipals* first, JSPrincipals* second);

View File

@@ -26,10 +26,8 @@
#include "jsfriendapi.h" #include "jsfriendapi.h"
#include "js/friend/ErrorMessages.h" // js::GetErrorMessage, JSMSG_* #include "js/friend/ErrorMessages.h" // js::GetErrorMessage, JSMSG_*
#include "js/ContextOptions.h" #include "js/ContextOptions.h"
#include "js/GCVector.h"
#include "js/Initialization.h" #include "js/Initialization.h"
#include "js/LocaleSensitive.h" #include "js/LocaleSensitive.h"
#include "js/Value.h"
#include "js/WasmFeatures.h" #include "js/WasmFeatures.h"
#include "mozilla/ArrayUtils.h" #include "mozilla/ArrayUtils.h"
#include "mozilla/Atomics.h" #include "mozilla/Atomics.h"
@@ -501,13 +499,8 @@ class LogViolationDetailsRunnable final : public WorkerMainThreadRunnable {
~LogViolationDetailsRunnable() = default; ~LogViolationDetailsRunnable() = default;
}; };
bool ContentSecurityPolicyAllows( bool ContentSecurityPolicyAllows(JSContext* aCx, JS::RuntimeCode aKind,
JSContext* aCx, JS::RuntimeCode aKind, JS::Handle<JSString*> aCodeString, JS::Handle<JSString*> aCode) {
JS::CompilationType aCompilationType,
JS::Handle<JS::StackGCVector<JSString*>> aParameterStrings,
JS::Handle<JSString*> aBodyString,
JS::Handle<JS::StackGCVector<JS::Value>> aParameterArgs,
JS::Handle<JS::Value> aBodyArg, bool* aOutCanCompileStrings) {
WorkerPrivate* worker = GetWorkerPrivateFromContext(aCx); WorkerPrivate* worker = GetWorkerPrivateFromContext(aCx);
worker->AssertIsOnWorkerThread(); worker->AssertIsOnWorkerThread();
@@ -516,14 +509,14 @@ bool ContentSecurityPolicyAllows(
uint16_t violationType; uint16_t violationType;
nsAutoJSString scriptSample; nsAutoJSString scriptSample;
if (aKind == JS::RuntimeCode::JS) { if (aKind == JS::RuntimeCode::JS) {
if (NS_WARN_IF(!scriptSample.init(aCx, aCodeString))) { if (NS_WARN_IF(!scriptSample.init(aCx, aCode))) {
JS_ClearPendingException(aCx);
return false; return false;
} }
if (!nsContentSecurityUtils::IsEvalAllowed( if (!nsContentSecurityUtils::IsEvalAllowed(
aCx, worker->UsesSystemPrincipal(), scriptSample)) { aCx, worker->UsesSystemPrincipal(), scriptSample)) {
*aOutCanCompileStrings = false; return false;
return true;
} }
evalOK = worker->IsEvalAllowed(); evalOK = worker->IsEvalAllowed();
@@ -549,8 +542,7 @@ bool ContentSecurityPolicyAllows(
} }
} }
*aOutCanCompileStrings = evalOK; return evalOK;
return true;
} }
void CTypesActivityCallback(JSContext* aCx, JS::CTypesActivityType aType) { void CTypesActivityCallback(JSContext* aCx, JS::CTypesActivityType aType) {
@@ -1554,7 +1546,7 @@ class DumpCrashInfoRunnable final : public WorkerControlRunnable {
}; };
struct ActiveWorkerStats { struct ActiveWorkerStats {
template <uint32_t ActiveWorkerStats::* Category> template <uint32_t ActiveWorkerStats::*Category>
void Update(const nsTArray<WorkerPrivate*>& aWorkers) { void Update(const nsTArray<WorkerPrivate*>& aWorkers) {
for (const auto worker : aWorkers) { for (const auto worker : aWorkers) {
RefPtr<DumpCrashInfoRunnable> runnable = RefPtr<DumpCrashInfoRunnable> runnable =

View File

@@ -77,54 +77,23 @@ typedef bool (*JSSubsumesOp)(JSPrincipals* first, JSPrincipals* second);
namespace JS { namespace JS {
enum class RuntimeCode { JS, WASM }; enum class RuntimeCode { JS, WASM };
enum class CompilationType { DirectEval, IndirectEval, Function, Undefined };
} // namespace JS } // namespace JS
/* /*
* Used to check if a CSP instance wants to disable eval() and friends. * Used to check if a CSP instance wants to disable eval() and friends.
* See JSContext::isRuntimeCodeGenEnabled() in vm/JSContext.cpp. * See JSContext::isRuntimeCodeGenEnabled() in vm/JSContext.cpp.
* *
* codeString, compilationType, parameterStrings, bodyString, parameterArgs, * `code` is the JavaScript source code passed to eval/Function, but nullptr
* and bodyArg are defined in the "Dynamic Code Brand Checks" spec * for Wasm.
* (see https://tc39.es/proposal-dynamic-code-brand-checks).
* *
* An Undefined compilationType is used for cases that are not covered by that * Returning `false` from this callback will prevent the execution/compilation
* spec and unused parameters are null/empty. Currently, this includes Wasm * of the code.
* (only check if compilation is enabled) and ShadowRealmEval (only check
* codeString).
*
* `outCanCompileStrings` is set to false if this callback prevents the
* execution/compilation of the code and to true otherwise.
*
* Return false on failure, true on success. The |outCanCompileStrings|
* parameter should not be modified in case of failure.
*/ */
typedef bool (*JSCSPEvalChecker)( typedef bool (*JSCSPEvalChecker)(JSContext* cx, JS::RuntimeCode kind,
JSContext* cx, JS::RuntimeCode kind, JS::Handle<JSString*> codeString, JS::HandleString code);
JS::CompilationType compilationType,
JS::Handle<JS::StackGCVector<JSString*>> parameterStrings,
JS::Handle<JSString*> bodyString,
JS::Handle<JS::StackGCVector<JS::Value>> parameterArgs,
JS::Handle<JS::Value> bodyArg, bool* outCanCompileStrings);
/*
* Provide a string of code from an Object argument, to be used by eval.
* See JSContext::getCodeForEval() in vm/JSContext.cpp as well as
* https://tc39.es/proposal-dynamic-code-brand-checks/#sec-hostgetcodeforeval
*
* `code` is the JavaScript object passed by the user.
* `outCode` is the JavaScript string to be actually executed, with nullptr
* meaning NO-CODE.
*
* Return false on failure, true on success. The |outCode| parameter should not
* be modified in case of failure.
*/
typedef bool (*JSCodeForEvalOp)(JSContext* cx, JS::HandleObject code,
JS::MutableHandle<JSString*> outCode);
struct JSSecurityCallbacks { struct JSSecurityCallbacks {
JSCSPEvalChecker contentSecurityPolicyAllows; JSCSPEvalChecker contentSecurityPolicyAllows;
JSCodeForEvalOp codeForEvalGets;
JSSubsumesOp subsumes; JSSubsumesOp subsumes;
}; };

View File

@@ -239,41 +239,21 @@ static bool EvalKernel(JSContext* cx, HandleValue v, EvalType evalType,
env->is<GlobalLexicalEnvironmentObject>()); env->is<GlobalLexicalEnvironmentObject>());
AssertInnerizedEnvironmentChain(cx, *env); AssertInnerizedEnvironmentChain(cx, *env);
// "Dynamic Code Brand Checks" adds support for Object values. // Step 2.
// https://tc39.es/proposal-dynamic-code-brand-checks/#sec-performeval if (!v.isString()) {
// Steps 2-4.
RootedString str(cx);
if (v.isString()) {
str = v.toString();
} else if (v.isObject()) {
RootedObject obj(cx, &v.toObject());
if (!cx->getCodeForEval(obj, &str)) {
return false;
}
}
if (!str) {
vp.set(v); vp.set(v);
return true; return true;
} }
// Steps 6-8. // Steps 3-4.
JS::RootedVector<JSString*> parameterStrings(cx); RootedString str(cx, v.toString());
JS::RootedVector<Value> parameterArgs(cx); if (!cx->isRuntimeCodeGenEnabled(JS::RuntimeCode::JS, str)) {
bool canCompileStrings = false;
if (!cx->isRuntimeCodeGenEnabled(
JS::RuntimeCode::JS, str,
evalType == DIRECT_EVAL ? JS::CompilationType::DirectEval
: JS::CompilationType::IndirectEval,
parameterStrings, str, parameterArgs, v, &canCompileStrings)) {
return false;
}
if (!canCompileStrings) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
JSMSG_CSP_BLOCKED_EVAL); JSMSG_CSP_BLOCKED_EVAL);
return false; return false;
} }
// Step 9 ff. // Step 5 ff.
// Per ES5, indirect eval runs in the global scope. (eval is specified this // Per ES5, indirect eval runs in the global scope. (eval is specified this
// way so that the compiler can make assumptions about what bindings may or // way so that the compiler can make assumptions about what bindings may or

View File

@@ -196,16 +196,7 @@ static bool PerformShadowRealmEval(JSContext* cx, Handle<JSString*> sourceText,
MOZ_ASSERT(callerRealm != evalRealm); MOZ_ASSERT(callerRealm != evalRealm);
// Step 1. Perform ? HostEnsureCanCompileStrings(callerRealm, evalRealm). // Step 1. Perform ? HostEnsureCanCompileStrings(callerRealm, evalRealm).
JS::RootedVector<JSString*> parameterStrings(cx); if (!cx->isRuntimeCodeGenEnabled(JS::RuntimeCode::JS, sourceText)) {
JS::RootedVector<Value> parameterArgs(cx);
bool canCompileStrings = false;
if (!cx->isRuntimeCodeGenEnabled(JS::RuntimeCode::JS, sourceText,
JS::CompilationType::Undefined,
parameterStrings, nullptr, parameterArgs,
NullHandleValue, &canCompileStrings)) {
return false;
}
if (!canCompileStrings) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
JSMSG_CSP_BLOCKED_SHADOWREALM); JSMSG_CSP_BLOCKED_SHADOWREALM);
return false; return false;

View File

@@ -40,7 +40,6 @@ UNIFIED_SOURCES += [
"testDeflateStringToUTF8Buffer.cpp", "testDeflateStringToUTF8Buffer.cpp",
"testDeleteProperty.cpp", "testDeleteProperty.cpp",
"testDifferentNewTargetInvokeConstructor.cpp", "testDifferentNewTargetInvokeConstructor.cpp",
"testDynamicCodeBrandChecks.cpp",
"testEmptyWindowIsOmitted.cpp", "testEmptyWindowIsOmitted.cpp",
"testErrorCopying.cpp", "testErrorCopying.cpp",
"testErrorLineOfContext.cpp", "testErrorLineOfContext.cpp",

View File

@@ -1,269 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim: set ts=8 sts=2 et sw=2 tw=80:
*
* Tests that the column number of error reports is properly copied over from
* other reports when invoked from the C++ api.
*/
/* 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/. */
#include "jsapi-tests/tests.h"
BEGIN_TEST(testDynamicCodeBrandChecks_DefaultHostGetCodeForEval) {
JS::RootedValue v(cx);
// String arguments are evaluated.
EVAL("eval('5*8');", &v);
CHECK(v.isNumber() && v.toNumber() == 40);
// Other arguments are returned as is by eval.
EVAL("eval({myProp: 41});", &v);
CHECK(v.isObject());
JS::RootedObject obj(cx, &v.toObject());
JS::RootedValue myProp(cx);
CHECK(JS_GetProperty(cx, obj, "myProp", &myProp));
CHECK(myProp.isNumber() && myProp.toNumber() == 41);
EVAL("eval({trustedCode: '6*7'}).trustedCode;", &v);
CHECK(v.isString());
JSString* str = v.toString();
CHECK(JS_LinearStringEqualsLiteral(JS_ASSERT_STRING_IS_LINEAR(str), "6*7"));
EVAL("eval({trustedCode: 42}).trustedCode;", &v);
CHECK(v.isNumber() && v.toNumber() == 42);
return true;
}
END_TEST(testDynamicCodeBrandChecks_DefaultHostGetCodeForEval)
static bool ExtractTrustedCodeStringProperty(
JSContext* aCx, JS::Handle<JSObject*> aCode,
JS::MutableHandle<JSString*> outCode) {
JS::RootedValue value(aCx);
if (!JS_GetProperty(aCx, aCode, "trustedCode", &value)) {
return false;
}
if (value.isUndefined()) {
// If the property is undefined, return NO-CODE.
outCode.set(nullptr);
return true;
}
if (value.isString()) {
// If the property is a string, return it.
outCode.set(value.toString());
return true;
}
// Otherwise, emulate a failure.
JS_ReportErrorASCII(aCx, "Unsupported value for trustedCode property");
return false;
}
BEGIN_TEST(testDynamicCodeBrandChecks_CustomHostGetCodeForEval) {
JSSecurityCallbacks securityCallbacksWithEvalAcceptingObject = {
nullptr, // contentSecurityPolicyAllows
ExtractTrustedCodeStringProperty, // codeForEvalGets
nullptr // subsumes
};
JS_SetSecurityCallbacks(cx, &securityCallbacksWithEvalAcceptingObject);
JS::RootedValue v(cx);
// String arguments are evaluated.
EVAL("eval('5*8');", &v);
CHECK(v.isNumber() && v.toNumber() == 40);
// Other arguments are returned as is by eval...
EVAL("eval({myProp: 41});", &v);
CHECK(v.isObject());
JS::RootedObject obj(cx, &v.toObject());
JS::RootedValue myProp(cx);
CHECK(JS_GetProperty(cx, obj, "myProp", &myProp));
CHECK(myProp.isNumber() && myProp.toNumber() == 41);
// ... but Objects are first tentatively converted to String by the
// codeForEvalGets callback.
EVAL("eval({trustedCode: '6*7'});", &v);
CHECK(v.isNumber() && v.toNumber() == 6 * 7);
// And if that codeForEvalGets callback fails, then so does the eval call.
CHECK(!execDontReport("eval({trustedCode: 6*7});", __FILE__, __LINE__));
return true;
}
END_TEST(testDynamicCodeBrandChecks_CustomHostGetCodeForEval)
// This snippet defines a TrustedType that wraps some trustedCode string and
// stringifies to that string, as well as a helper to create a fake instance
// that can stringify to a different string.
const char* customTypesSnippet =
"function TrustedType(aTrustedCode) { this.trustedCode = aTrustedCode; };"
"TrustedType.prototype.toString = function() { return this.trustedCode; };"
"function CreateFakeTrustedType(aTrustedCode, aString) {"
" let fake = new TrustedType(aTrustedCode);"
" fake.toString = () => { return aString; };"
" return fake;"
"};";
BEGIN_TEST(testDynamicCodeBrandChecks_CustomHostEnsureCanCompileStrings) {
JSSecurityCallbacks securityCallbacksWithCustomHostEnsureCanCompileStrings = {
StringifiedObjectsMatchTrustedCodeProperties, // contentSecurityPolicyAllows
ExtractTrustedCodeStringProperty, // codeForEvalGets
nullptr // subsumes
};
JS_SetSecurityCallbacks(
cx, &securityCallbacksWithCustomHostEnsureCanCompileStrings);
JS::RootedValue v(cx);
EXEC(customTypesSnippet);
// String arguments are evaluated.
EVAL("eval('5*8');", &v);
CHECK(v.isNumber() && v.toNumber() == 40);
EVAL("(new Function('a', 'b', 'return a * b'))(6, 7);", &v);
CHECK(v.isNumber() && v.toNumber() == 42);
// The same works with TrustedType wrappers.
EVAL("eval(new TrustedType('5*8'));", &v);
CHECK(v.isNumber() && v.toNumber() == 40);
EVAL(
"(new Function(new TrustedType('a'), new TrustedType('b'), new "
"TrustedType('return a * b')))(6, 7);",
&v);
CHECK(v.isNumber() && v.toNumber() == 42);
// new Function fails if one of the stringified argument does not match the
// trustedCode property.
CHECK(!execDontReport(
"new Function(CreateFakeTrustedType('a', 'c'), 'b', 'return b');",
__FILE__, __LINE__));
CHECK(!execDontReport(
"new Function('a', CreateFakeTrustedType('b', 'c'), 'return a');",
__FILE__, __LINE__));
CHECK(
!execDontReport("new Function('a', 'b', CreateFakeTrustedType('return a "
"* b', 'return a + b'));",
__FILE__, __LINE__));
// new Function also fails if StringifiedObjectsMatchTrustedCodeProperties
// returns false.
CHECK(!execDontReport("new Function('a', 'b', new TrustedType(undefined));",
__FILE__, __LINE__));
// PerformEval relies on ExtractTrustedCodeProperty rather than toString() to
// obtain the code to execute, so StringifiedObjectsMatchTrustedCodeProperties
// will always allow the code execution for the specified security callbacks.
EVAL("eval(CreateFakeTrustedType('5*8', '6*7'));", &v);
CHECK(v.isNumber() && v.toNumber() == 40);
EVAL("eval(new TrustedType(undefined));", &v);
CHECK(v.isObject());
JS::RootedObject obj(cx, &v.toObject());
JS::RootedValue trustedCode(cx);
CHECK(JS_GetProperty(cx, obj, "trustedCode", &trustedCode));
CHECK(trustedCode.isUndefined());
return true;
}
// This is a HostEnsureCanCompileStrings() implementation similar to some checks
// described in the CSP spec: verify that aBodyString and aParameterStrings
// match the corresponding trustedCode property on aBodyArg and aParameterArgs
// objects. See https://w3c.github.io/webappsec-csp/#can-compile-strings
static bool StringifiedObjectsMatchTrustedCodeProperties(
JSContext* aCx, JS::RuntimeCode aKind, JS::Handle<JSString*> aCodeString,
JS::CompilationType aCompilationType,
JS::Handle<JS::StackGCVector<JSString*>> aParameterStrings,
JS::Handle<JSString*> aBodyString,
JS::Handle<JS::StackGCVector<JS::Value>> aParameterArgs,
JS::Handle<JS::Value> aBodyArg, bool* aOutCanCompileStrings) {
bool isTrusted = true;
auto comparePropertyAndString = [&aCx, &isTrusted](
JS::Handle<JS::Value> aValue,
JS::Handle<JSString*> aString) {
if (!aValue.isObject()) {
// Just trust non-Objects.
return true;
}
JS::RootedObject obj(aCx, &aValue.toObject());
JS::RootedString trustedCode(aCx);
if (!ExtractTrustedCodeStringProperty(aCx, obj, &trustedCode)) {
// Propagate the failure.
return false;
}
if (!trustedCode) {
// Emulate a failure if trustedCode is undefined.
JS_ReportErrorASCII(aCx,
"test failed, trustedCode property is undefined");
return false;
}
bool equals;
if (!EqualStrings(aCx, trustedCode, aString, &equals)) {
// Propagate the failure.
return false;
}
if (!equals) {
isTrusted = false;
}
return true;
};
if (!comparePropertyAndString(aBodyArg, aBodyString)) {
// Propagate the failure.
return false;
}
if (isTrusted) {
MOZ_ASSERT(aParameterArgs.length() == aParameterStrings.length());
for (size_t index = 0; index < aParameterArgs.length(); index++) {
if (!comparePropertyAndString(aParameterArgs[index],
aParameterStrings[index])) {
// Propagate the failure.
return false;
}
if (!isTrusted) {
break;
}
}
}
// Allow compilation if arguments are trusted.
*aOutCanCompileStrings = isTrusted;
return true;
}
END_TEST(testDynamicCodeBrandChecks_CustomHostEnsureCanCompileStrings)
BEGIN_TEST(testDynamicCodeBrandChecks_RejectObjectForEval) {
JSSecurityCallbacks securityCallbacksRejectObjectBody = {
DisallowObjectsAndFailOtherwise, // contentSecurityPolicyAllows
ExtractTrustedCodeStringProperty, // codeForEvalGets
nullptr // subsumes
};
JS_SetSecurityCallbacks(cx, &securityCallbacksRejectObjectBody);
JS::RootedValue v(cx);
EXEC(customTypesSnippet);
// With the specified security callbacks, eval() will always fail.
CHECK(!execDontReport("eval('5*8))", __FILE__, __LINE__));
CHECK(!execDontReport("eval(new TrustedType('5*8'))", __FILE__, __LINE__));
return true;
}
static bool DisallowObjectsAndFailOtherwise(
JSContext* aCx, JS::RuntimeCode aKind, JS::Handle<JSString*> aCodeString,
JS::CompilationType aCompilationType,
JS::Handle<JS::StackGCVector<JSString*>> aParameterStrings,
JS::Handle<JSString*> aBodyString,
JS::Handle<JS::StackGCVector<JS::Value>> aParameterArgs,
JS::Handle<JS::Value> aBodyArg, bool* aOutCanCompileStrings) {
if (aBodyArg.isObject()) {
// Disallow compilation for objects.
*aOutCanCompileStrings = false;
return true;
}
// Otherwise, emulate a failure.
JS_ReportErrorASCII(aCx, "aBodyArg is not an Object");
return false;
}
END_TEST(testDynamicCodeBrandChecks_RejectObjectForEval)

View File

@@ -258,7 +258,6 @@ struct StructuredCloneTestPrincipals final : public JSPrincipals {
JSSecurityCallbacks StructuredCloneTestPrincipals::securityCallbacks = { JSSecurityCallbacks StructuredCloneTestPrincipals::securityCallbacks = {
nullptr, // contentSecurityPolicyAllows nullptr, // contentSecurityPolicyAllows
nullptr, // codeForEvalGets
subsumes}; subsumes};
BEGIN_TEST(testStructuredClone_SavedFrame) { BEGIN_TEST(testStructuredClone_SavedFrame) {

View File

@@ -955,7 +955,6 @@ class ShellPrincipals final : public JSPrincipals {
JSSecurityCallbacks ShellPrincipals::securityCallbacks = { JSSecurityCallbacks ShellPrincipals::securityCallbacks = {
nullptr, // contentSecurityPolicyAllows nullptr, // contentSecurityPolicyAllows
nullptr, // codeForEvalGets
subsumes}; subsumes};
// The fully-trusted principal subsumes all other principals. // The fully-trusted principal subsumes all other principals.

View File

@@ -1233,35 +1233,15 @@ bool JSContext::isThrowingDebuggeeWouldRun() {
JSEXN_DEBUGGEEWOULDRUN; JSEXN_DEBUGGEEWOULDRUN;
} }
bool JSContext::isRuntimeCodeGenEnabled( bool JSContext::isRuntimeCodeGenEnabled(JS::RuntimeCode kind,
JS::RuntimeCode kind, JS::Handle<JSString*> codeString, HandleString code) {
JS::CompilationType compilationType,
JS::Handle<JS::StackGCVector<JSString*>> parameterStrings,
JS::Handle<JSString*> bodyString,
JS::Handle<JS::StackGCVector<JS::Value>> parameterArgs,
JS::Handle<JS::Value> bodyArg, bool* outCanCompileStrings) {
// Make sure that the CSP callback is installed and that it permits runtime // Make sure that the CSP callback is installed and that it permits runtime
// code generation. // code generation.
if (JSCSPEvalChecker allows = if (JSCSPEvalChecker allows =
runtime()->securityCallbacks->contentSecurityPolicyAllows) { runtime()->securityCallbacks->contentSecurityPolicyAllows) {
return allows(this, kind, codeString, compilationType, parameterStrings, return allows(this, kind, code);
bodyString, parameterArgs, bodyArg, outCanCompileStrings);
} }
// Default implementation from the "Dynamic Code Brand Checks" spec.
// https://tc39.es/proposal-dynamic-code-brand-checks/#sec-hostensurecancompilestrings
*outCanCompileStrings = true;
return true;
}
bool JSContext::getCodeForEval(HandleObject code,
JS::MutableHandle<JSString*> outCode) {
if (JSCodeForEvalOp gets = runtime()->securityCallbacks->codeForEvalGets) {
return gets(this, code, outCode);
}
// Default implementation from the "Dynamic Code Brand Checks" spec.
// https://tc39.es/proposal-dynamic-code-brand-checks/#sec-hostgetcodeforeval
outCode.set(nullptr);
return true; return true;
} }

View File

@@ -803,17 +803,7 @@ struct JS_PUBLIC_API JSContext : public JS::RootingContext,
// Checks if the page's Content-Security-Policy (CSP) allows // Checks if the page's Content-Security-Policy (CSP) allows
// runtime code generation "unsafe-eval", or "wasm-unsafe-eval" for Wasm. // runtime code generation "unsafe-eval", or "wasm-unsafe-eval" for Wasm.
bool isRuntimeCodeGenEnabled( bool isRuntimeCodeGenEnabled(JS::RuntimeCode kind, js::HandleString code);
JS::RuntimeCode kind, JS::Handle<JSString*> codeString,
JS::CompilationType compilationType,
JS::Handle<JS::StackGCVector<JSString*>> parameterStrings,
JS::Handle<JSString*> bodyString,
JS::Handle<JS::StackGCVector<JS::Value>> parameterArgs,
JS::Handle<JS::Value> bodyArg, bool* outCanCompileStrings);
// Get code to be used by eval for Object argument.
bool getCodeForEval(JS::HandleObject code,
JS::MutableHandle<JSString*> outCode);
size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const; size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const; size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const;

View File

@@ -1346,32 +1346,19 @@ static bool CreateDynamicFunction(JSContext* cx, const CallArgs& args,
return false; return false;
} }
JS::RootedVector<JSString*> parameterStrings(cx);
JS::RootedVector<Value> parameterArgs(cx);
if (args.length() > 1) { if (args.length() > 1) {
RootedString str(cx); RootedString str(cx);
// Steps 10, 14.d. // Steps 10, 14.d.
unsigned n = args.length() - 1; unsigned n = args.length() - 1;
if (!parameterStrings.reserve(n) || !parameterArgs.reserve(n)) {
return false;
}
for (unsigned i = 0; i < n; i++) { for (unsigned i = 0; i < n; i++) {
if (!parameterArgs.append(args[i])) {
return false;
}
// Steps 14.a-b, 14.d.i-ii. // Steps 14.a-b, 14.d.i-ii.
str = ToString<CanGC>(cx, args[i]); str = ToString<CanGC>(cx, args[i]);
if (!str) { if (!str) {
return false; return false;
} }
if (!parameterStrings.append(str)) {
return false;
}
// Steps 14.b, 14.d.iii. // Steps 14.b, 14.d.iii.
if (!sb.append(str)) { if (!sb.append(str)) {
return false; return false;
@@ -1399,13 +1386,10 @@ static bool CreateDynamicFunction(JSContext* cx, const CallArgs& args,
return false; return false;
} }
JS::RootedValue bodyArg(cx);
RootedString bodyString(cx);
if (args.length() > 0) { if (args.length() > 0) {
// Steps 13, 14.e, 15. // Steps 13, 14.e, 15.
bodyArg = args[args.length() - 1]; RootedString body(cx, ToString<CanGC>(cx, args[args.length() - 1]));
bodyString = ToString<CanGC>(cx, bodyArg); if (!body || !sb.append(body)) {
if (!bodyString || !sb.append(bodyString)) {
return false; return false;
} }
} }
@@ -1426,14 +1410,7 @@ static bool CreateDynamicFunction(JSContext* cx, const CallArgs& args,
} }
// Block this call if security callbacks forbid it. // Block this call if security callbacks forbid it.
bool canCompileStrings = false; if (!cx->isRuntimeCodeGenEnabled(JS::RuntimeCode::JS, functionText)) {
if (!cx->isRuntimeCodeGenEnabled(JS::RuntimeCode::JS, functionText,
JS::CompilationType::Function,
parameterStrings, bodyString, parameterArgs,
bodyArg, &canCompileStrings)) {
return false;
}
if (!canCompileStrings) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
JSMSG_CSP_BLOCKED_FUNCTION); JSMSG_CSP_BLOCKED_FUNCTION);
return false; return false;

View File

@@ -1619,16 +1619,7 @@ bool WasmModuleObject::construct(JSContext* cx, unsigned argc, Value* vp) {
return false; return false;
} }
JS::RootedVector<JSString*> parameterStrings(cx); if (!cx->isRuntimeCodeGenEnabled(JS::RuntimeCode::WASM, nullptr)) {
JS::RootedVector<Value> parameterArgs(cx);
bool canCompileStrings = false;
if (!cx->isRuntimeCodeGenEnabled(JS::RuntimeCode::WASM, nullptr,
JS::CompilationType::Undefined,
parameterStrings, nullptr, parameterArgs,
NullHandleValue, &canCompileStrings)) {
return false;
}
if (!canCompileStrings) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
JSMSG_CSP_BLOCKED_WASM, "WebAssembly.Module"); JSMSG_CSP_BLOCKED_WASM, "WebAssembly.Module");
return false; return false;
@@ -4446,16 +4437,7 @@ static bool WebAssembly_compile(JSContext* cx, unsigned argc, Value* vp) {
CallArgs callArgs = CallArgsFromVp(argc, vp); CallArgs callArgs = CallArgsFromVp(argc, vp);
JS::RootedVector<JSString*> parameterStrings(cx); if (!cx->isRuntimeCodeGenEnabled(JS::RuntimeCode::WASM, nullptr)) {
JS::RootedVector<Value> parameterArgs(cx);
bool canCompileStrings = false;
if (!cx->isRuntimeCodeGenEnabled(JS::RuntimeCode::WASM, nullptr,
JS::CompilationType::Undefined,
parameterStrings, nullptr, parameterArgs,
NullHandleValue, &canCompileStrings)) {
return RejectWithPendingException(cx, promise, callArgs);
}
if (!canCompileStrings) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
JSMSG_CSP_BLOCKED_WASM, "WebAssembly.compile"); JSMSG_CSP_BLOCKED_WASM, "WebAssembly.compile");
return RejectWithPendingException(cx, promise, callArgs); return RejectWithPendingException(cx, promise, callArgs);
@@ -4539,16 +4521,7 @@ static bool WebAssembly_instantiate(JSContext* cx, unsigned argc, Value* vp) {
return false; return false;
} }
} else { } else {
JS::RootedVector<JSString*> parameterStrings(cx); if (!cx->isRuntimeCodeGenEnabled(JS::RuntimeCode::WASM, nullptr)) {
JS::RootedVector<Value> parameterArgs(cx);
bool canCompileStrings = false;
if (!cx->isRuntimeCodeGenEnabled(JS::RuntimeCode::WASM, nullptr,
JS::CompilationType::Undefined,
parameterStrings, nullptr, parameterArgs,
NullHandleValue, &canCompileStrings)) {
return RejectWithPendingException(cx, promise, callArgs);
}
if (!canCompileStrings) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
JSMSG_CSP_BLOCKED_WASM, JSMSG_CSP_BLOCKED_WASM,
"WebAssembly.instantiate"); "WebAssembly.instantiate");
@@ -5141,16 +5114,7 @@ static bool WebAssembly_compileStreaming(JSContext* cx, unsigned argc,
CallArgs callArgs = CallArgsFromVp(argc, vp); CallArgs callArgs = CallArgsFromVp(argc, vp);
JS::RootedVector<JSString*> parameterStrings(cx); if (!cx->isRuntimeCodeGenEnabled(JS::RuntimeCode::WASM, nullptr)) {
JS::RootedVector<Value> parameterArgs(cx);
bool canCompileStrings = false;
if (!cx->isRuntimeCodeGenEnabled(JS::RuntimeCode::WASM, nullptr,
JS::CompilationType::Undefined,
parameterStrings, nullptr, parameterArgs,
NullHandleValue, &canCompileStrings)) {
return RejectWithPendingException(cx, resultPromise, callArgs);
}
if (!canCompileStrings) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
JSMSG_CSP_BLOCKED_WASM, JSMSG_CSP_BLOCKED_WASM,
"WebAssembly.compileStreaming"); "WebAssembly.compileStreaming");
@@ -5183,16 +5147,7 @@ static bool WebAssembly_instantiateStreaming(JSContext* cx, unsigned argc,
CallArgs callArgs = CallArgsFromVp(argc, vp); CallArgs callArgs = CallArgsFromVp(argc, vp);
JS::RootedVector<JSString*> parameterStrings(cx); if (!cx->isRuntimeCodeGenEnabled(JS::RuntimeCode::WASM, nullptr)) {
JS::RootedVector<Value> parameterArgs(cx);
bool canCompileStrings = false;
if (!cx->isRuntimeCodeGenEnabled(JS::RuntimeCode::WASM, nullptr,
JS::CompilationType::Undefined,
parameterStrings, nullptr, parameterArgs,
NullHandleValue, &canCompileStrings)) {
return RejectWithPendingException(cx, resultPromise, callArgs);
}
if (!canCompileStrings) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
JSMSG_CSP_BLOCKED_WASM, JSMSG_CSP_BLOCKED_WASM,
"WebAssembly.instantiateStreaming"); "WebAssembly.instantiateStreaming");

View File

@@ -206,16 +206,7 @@ JSObject* Module::createObject(JSContext* cx) const {
return nullptr; return nullptr;
} }
JS::RootedVector<JSString*> parameterStrings(cx); if (!cx->isRuntimeCodeGenEnabled(JS::RuntimeCode::WASM, nullptr)) {
JS::RootedVector<Value> parameterArgs(cx);
bool canCompileStrings = false;
if (!cx->isRuntimeCodeGenEnabled(JS::RuntimeCode::WASM, nullptr,
JS::CompilationType::Undefined,
parameterStrings, nullptr, parameterArgs,
NullHandleValue, &canCompileStrings)) {
return nullptr;
}
if (!canCompileStrings) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
JSMSG_CSP_BLOCKED_WASM, "WebAssembly.Module"); JSMSG_CSP_BLOCKED_WASM, "WebAssembly.Module");
return nullptr; return nullptr;