Bug 1905239 - Add new parameters to HostEnsureCanCompileStrings hook. r=tschuster

Currently, we do this via isRuntimeCodeGenEnabled whose single argument
is equivalent to codeString in "Dynamic Code Brand Checks" spec [1]. We
extend this hook to accept new parameters from that spec and adjust
PerformEval and CreateDynamicFunction accordingly. We don't change the
behavior for PerformShadowRealmEval [2] and WASM, i.e. we keep dummy
parameters.

[1] https://tc39.es/proposal-dynamic-code-brand-checks
[2] https://github.com/tc39/proposal-shadowrealm/issues/414

Differential Revision: https://phabricator.services.mozilla.com/D229588
This commit is contained in:
Frédéric Wang
2024-12-11 20:00:32 +00:00
parent 410b2420a9
commit c94fbf2e3e
12 changed files with 367 additions and 37 deletions

View File

@@ -73,6 +73,8 @@
#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"
@@ -462,7 +464,12 @@ NS_IMPL_ISUPPORTS(nsScriptSecurityManager, nsIScriptSecurityManager)
///////////////// Security Checks ///////////////// ///////////////// Security Checks /////////////////
bool nsScriptSecurityManager::ContentSecurityPolicyPermitsJSAction( bool nsScriptSecurityManager::ContentSecurityPolicyPermitsJSAction(
JSContext* cx, JS::RuntimeCode aKind, JS::Handle<JSString*> aCode) { JSContext* cx, 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) {
MOZ_ASSERT(cx == nsContentUtils::GetCurrentJSContext()); MOZ_ASSERT(cx == nsContentUtils::GetCurrentJSContext());
nsCOMPtr<nsIPrincipal> subjectPrincipal = nsContentUtils::SubjectPrincipal(); nsCOMPtr<nsIPrincipal> subjectPrincipal = nsContentUtils::SubjectPrincipal();
@@ -477,13 +484,14 @@ 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, aCode))) { NS_WARN_IF(!scriptSample.init(cx, aCodeString))) {
return false; return false;
} }
if (!nsContentSecurityUtils::IsEvalAllowed( if (!nsContentSecurityUtils::IsEvalAllowed(
cx, subjectPrincipal->IsSystemPrincipal(), scriptSample)) { cx, subjectPrincipal->IsSystemPrincipal(), scriptSample)) {
return false; *aOutCanCompileStrings = false;
return true;
} }
} }
@@ -503,6 +511,7 @@ 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;
} }
} }
@@ -522,7 +531,8 @@ 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");
return true; // fail open to not break sites. *aOutCanCompileStrings = 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))) {
@@ -545,8 +555,7 @@ 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, aCode))) { NS_WARN_IF(!scriptSample.init(cx, aCodeString))) {
JS_ClearPendingException(cx);
return false; return false;
} }
uint16_t violationType = uint16_t violationType =
@@ -559,7 +568,8 @@ bool nsScriptSecurityManager::ContentSecurityPolicyPermitsJSAction(
caller.mLine, caller.mColumn, u""_ns, u""_ns); caller.mLine, caller.mColumn, u""_ns, u""_ns);
} }
return evalOK; *aOutCanCompileStrings = evalOK;
return true;
} }
// static // static

View File

@@ -28,6 +28,7 @@ class SystemPrincipal;
namespace JS { namespace JS {
enum class RuntimeCode; enum class RuntimeCode;
enum class CompilationType;
} // namespace JS } // namespace JS
///////////////////////////// /////////////////////////////
@@ -91,9 +92,13 @@ 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(JSContext* cx, static bool ContentSecurityPolicyPermitsJSAction(
JS::RuntimeCode kind, 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);
static bool JSPrincipalsSubsume(JSPrincipals* first, JSPrincipals* second); static bool JSPrincipalsSubsume(JSPrincipals* first, JSPrincipals* second);

View File

@@ -26,8 +26,10 @@
#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"
@@ -499,8 +501,13 @@ class LogViolationDetailsRunnable final : public WorkerMainThreadRunnable {
~LogViolationDetailsRunnable() = default; ~LogViolationDetailsRunnable() = default;
}; };
bool ContentSecurityPolicyAllows(JSContext* aCx, JS::RuntimeCode aKind, bool ContentSecurityPolicyAllows(
JS::Handle<JSString*> aCode) { 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) {
WorkerPrivate* worker = GetWorkerPrivateFromContext(aCx); WorkerPrivate* worker = GetWorkerPrivateFromContext(aCx);
worker->AssertIsOnWorkerThread(); worker->AssertIsOnWorkerThread();
@@ -509,14 +516,14 @@ bool ContentSecurityPolicyAllows(JSContext* aCx, JS::RuntimeCode aKind,
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, aCode))) { if (NS_WARN_IF(!scriptSample.init(aCx, aCodeString))) {
JS_ClearPendingException(aCx);
return false; return false;
} }
if (!nsContentSecurityUtils::IsEvalAllowed( if (!nsContentSecurityUtils::IsEvalAllowed(
aCx, worker->UsesSystemPrincipal(), scriptSample)) { aCx, worker->UsesSystemPrincipal(), scriptSample)) {
return false; *aOutCanCompileStrings = false;
return true;
} }
evalOK = worker->IsEvalAllowed(); evalOK = worker->IsEvalAllowed();
@@ -542,7 +549,8 @@ bool ContentSecurityPolicyAllows(JSContext* aCx, JS::RuntimeCode aKind,
} }
} }
return evalOK; *aOutCanCompileStrings = evalOK;
return true;
} }
void CTypesActivityCallback(JSContext* aCx, JS::CTypesActivityType aType) { void CTypesActivityCallback(JSContext* aCx, JS::CTypesActivityType aType) {

View File

@@ -77,20 +77,35 @@ 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.
* *
* `code` is the JavaScript source code passed to eval/Function, but nullptr * codeString, compilationType, parameterStrings, bodyString, parameterArgs,
* for Wasm. * and bodyArg are defined in the "Dynamic Code Brand Checks" spec
* (see https://tc39.es/proposal-dynamic-code-brand-checks).
* *
* Returning `false` from this callback will prevent the execution/compilation * An Undefined compilationType is used for cases that are not covered by that
* of the code. * spec and unused parameters are null/empty. Currently, this includes Wasm
* (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)(JSContext* cx, JS::RuntimeCode kind, typedef bool (*JSCSPEvalChecker)(
JS::HandleString code); JSContext* cx, 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);
/* /*
* Provide a string of code from an Object argument, to be used by eval. * Provide a string of code from an Object argument, to be used by eval.

View File

@@ -257,7 +257,17 @@ static bool EvalKernel(JSContext* cx, HandleValue v, EvalType evalType,
} }
// Steps 6-8. // Steps 6-8.
if (!cx->isRuntimeCodeGenEnabled(JS::RuntimeCode::JS, str)) { JS::RootedVector<JSString*> parameterStrings(cx);
JS::RootedVector<Value> parameterArgs(cx);
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;

View File

@@ -196,7 +196,16 @@ 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).
if (!cx->isRuntimeCodeGenEnabled(JS::RuntimeCode::JS, sourceText)) { JS::RootedVector<JSString*> parameterStrings(cx);
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

@@ -92,3 +92,184 @@ BEGIN_TEST(testDynamicCodeBrandChecks_CustomHostGetCodeForEval) {
return true; return true;
} }
END_TEST(testDynamicCodeBrandChecks_CustomHostGetCodeForEval) 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__));
cx->clearPendingException();
CHECK(!execDontReport(
"new Function('a', CreateFakeTrustedType('b', 'c'), 'return a');",
__FILE__, __LINE__));
cx->clearPendingException();
CHECK(
!execDontReport("new Function('a', 'b', CreateFakeTrustedType('return a "
"* b', 'return a + b'));",
__FILE__, __LINE__));
cx->clearPendingException();
// new Function also fails if StringifiedObjectsMatchTrustedCodeProperties
// returns false.
CHECK(!execDontReport("new Function('a', 'b', new TrustedType(undefined));",
__FILE__, __LINE__));
cx->clearPendingException();
// 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__));
cx->clearPendingException();
CHECK(!execDontReport("eval(new TrustedType('5*8'))", __FILE__, __LINE__));
cx->clearPendingException();
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

@@ -1233,15 +1233,24 @@ bool JSContext::isThrowingDebuggeeWouldRun() {
JSEXN_DEBUGGEEWOULDRUN; JSEXN_DEBUGGEEWOULDRUN;
} }
bool JSContext::isRuntimeCodeGenEnabled(JS::RuntimeCode kind, bool JSContext::isRuntimeCodeGenEnabled(
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) {
// 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, code); return allows(this, kind, codeString, compilationType, parameterStrings,
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; return true;
} }

View File

@@ -803,7 +803,13 @@ 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(JS::RuntimeCode kind, js::HandleString code); bool isRuntimeCodeGenEnabled(
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. // Get code to be used by eval for Object argument.
bool getCodeForEval(JS::HandleObject code, bool getCodeForEval(JS::HandleObject code,

View File

@@ -1346,19 +1346,32 @@ 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;
@@ -1386,10 +1399,13 @@ 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.
RootedString body(cx, ToString<CanGC>(cx, args[args.length() - 1])); bodyArg = args[args.length() - 1];
if (!body || !sb.append(body)) { bodyString = ToString<CanGC>(cx, bodyArg);
if (!bodyString || !sb.append(bodyString)) {
return false; return false;
} }
} }
@@ -1410,7 +1426,14 @@ static bool CreateDynamicFunction(JSContext* cx, const CallArgs& args,
} }
// Block this call if security callbacks forbid it. // Block this call if security callbacks forbid it.
if (!cx->isRuntimeCodeGenEnabled(JS::RuntimeCode::JS, functionText)) { bool canCompileStrings = false;
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,7 +1619,16 @@ bool WasmModuleObject::construct(JSContext* cx, unsigned argc, Value* vp) {
return false; return false;
} }
if (!cx->isRuntimeCodeGenEnabled(JS::RuntimeCode::WASM, nullptr)) { JS::RootedVector<JSString*> parameterStrings(cx);
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;
@@ -4437,7 +4446,16 @@ static bool WebAssembly_compile(JSContext* cx, unsigned argc, Value* vp) {
CallArgs callArgs = CallArgsFromVp(argc, vp); CallArgs callArgs = CallArgsFromVp(argc, vp);
if (!cx->isRuntimeCodeGenEnabled(JS::RuntimeCode::WASM, nullptr)) { JS::RootedVector<JSString*> parameterStrings(cx);
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);
@@ -4521,7 +4539,16 @@ static bool WebAssembly_instantiate(JSContext* cx, unsigned argc, Value* vp) {
return false; return false;
} }
} else { } else {
if (!cx->isRuntimeCodeGenEnabled(JS::RuntimeCode::WASM, nullptr)) { JS::RootedVector<JSString*> parameterStrings(cx);
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");
@@ -5114,7 +5141,16 @@ static bool WebAssembly_compileStreaming(JSContext* cx, unsigned argc,
CallArgs callArgs = CallArgsFromVp(argc, vp); CallArgs callArgs = CallArgsFromVp(argc, vp);
if (!cx->isRuntimeCodeGenEnabled(JS::RuntimeCode::WASM, nullptr)) { JS::RootedVector<JSString*> parameterStrings(cx);
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");
@@ -5147,7 +5183,16 @@ static bool WebAssembly_instantiateStreaming(JSContext* cx, unsigned argc,
CallArgs callArgs = CallArgsFromVp(argc, vp); CallArgs callArgs = CallArgsFromVp(argc, vp);
if (!cx->isRuntimeCodeGenEnabled(JS::RuntimeCode::WASM, nullptr)) { JS::RootedVector<JSString*> parameterStrings(cx);
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,7 +206,16 @@ JSObject* Module::createObject(JSContext* cx) const {
return nullptr; return nullptr;
} }
if (!cx->isRuntimeCodeGenEnabled(JS::RuntimeCode::WASM, nullptr)) { JS::RootedVector<JSString*> parameterStrings(cx);
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;