mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-24 05:11:16 +00:00
bug 515443 CSP no-eval support. r=mrbkap,brendan
This commit is contained in:
parent
d5657f6cbb
commit
1090529f8c
@ -431,6 +431,10 @@ private:
|
||||
jsval id, JSAccessMode mode,
|
||||
jsval *vp);
|
||||
|
||||
// Decides, based on CSP, whether or not eval() and stuff can be executed.
|
||||
static JSBool
|
||||
ContentSecurityPolicyPermitsJSAction(JSContext *cx);
|
||||
|
||||
// Returns null if a principal cannot be found; generally callers
|
||||
// should error out at that point.
|
||||
static nsIPrincipal*
|
||||
|
@ -93,6 +93,7 @@
|
||||
#include "nsCDefaultURIFixup.h"
|
||||
#include "nsIChromeRegistry.h"
|
||||
#include "nsPrintfCString.h"
|
||||
#include "nsIContentSecurityPolicy.h"
|
||||
|
||||
static NS_DEFINE_CID(kZipReaderCID, NS_ZIPREADER_CID);
|
||||
|
||||
@ -513,6 +514,50 @@ NS_IMPL_ISUPPORTS5(nsScriptSecurityManager,
|
||||
///////////////////////////////////////////////////
|
||||
|
||||
///////////////// Security Checks /////////////////
|
||||
JSBool
|
||||
nsScriptSecurityManager::ContentSecurityPolicyPermitsJSAction(JSContext *cx)
|
||||
{
|
||||
// Get the security manager
|
||||
nsScriptSecurityManager *ssm =
|
||||
nsScriptSecurityManager::GetScriptSecurityManager();
|
||||
|
||||
NS_ASSERTION(ssm, "Failed to get security manager service");
|
||||
if (!ssm)
|
||||
return JS_FALSE;
|
||||
|
||||
nsresult rv;
|
||||
nsIPrincipal* subjectPrincipal = ssm->GetSubjectPrincipal(cx, &rv);
|
||||
|
||||
NS_ASSERTION(NS_SUCCEEDED(rv), "CSP: Failed to get nsIPrincipal from js context");
|
||||
if (NS_FAILED(rv))
|
||||
return JS_FALSE; // Not just absence of principal, but failure.
|
||||
|
||||
if (!subjectPrincipal)
|
||||
return JS_FALSE;
|
||||
|
||||
nsCOMPtr<nsIContentSecurityPolicy> csp;
|
||||
rv = subjectPrincipal->GetCsp(getter_AddRefs(csp));
|
||||
NS_ASSERTION(NS_SUCCEEDED(rv), "CSP: Failed to get CSP from principal.");
|
||||
|
||||
// don't do anything unless there's a CSP
|
||||
if (!csp)
|
||||
return JS_TRUE;
|
||||
|
||||
PRBool evalOK = PR_TRUE;
|
||||
// this call will send violation reports as warranted (and return true if
|
||||
// reportOnly is set).
|
||||
rv = csp->GetAllowsEval(&evalOK);
|
||||
|
||||
if (NS_FAILED(rv))
|
||||
{
|
||||
NS_WARNING("CSP: failed to get allowsEval");
|
||||
return JS_TRUE; // fail open to not break sites.
|
||||
}
|
||||
|
||||
return evalOK;
|
||||
}
|
||||
|
||||
|
||||
JSBool
|
||||
nsScriptSecurityManager::CheckObjectAccess(JSContext *cx, JSObject *obj,
|
||||
jsval id, JSAccessMode mode,
|
||||
@ -3361,9 +3406,10 @@ nsresult nsScriptSecurityManager::Init()
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
static JSSecurityCallbacks securityCallbacks = {
|
||||
CheckObjectAccess,
|
||||
NULL,
|
||||
NULL
|
||||
CheckObjectAccess,
|
||||
NULL,
|
||||
NULL,
|
||||
ContentSecurityPolicyPermitsJSAction
|
||||
};
|
||||
|
||||
#ifdef DEBUG
|
||||
|
@ -351,6 +351,10 @@ _TEST_FILES = test_bug5141.html \
|
||||
test_CSP_inlinescript.html \
|
||||
file_CSP_inlinescript_main.html \
|
||||
file_CSP_inlinescript_main.html^headers^ \
|
||||
test_CSP_evalscript.html \
|
||||
file_CSP_evalscript.sjs \
|
||||
file_CSP_evalscript_main.html \
|
||||
file_CSP_evalscript_main.js \
|
||||
test_bug540854.html \
|
||||
bug540854.sjs \
|
||||
$(NULL)
|
||||
|
@ -50,6 +50,7 @@
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "nsDOMError.h"
|
||||
#include "nsGlobalWindow.h"
|
||||
#include "nsIContentSecurityPolicy.h"
|
||||
|
||||
static const char kSetIntervalStr[] = "setInterval";
|
||||
static const char kSetTimeoutStr[] = "setTimeout";
|
||||
@ -203,7 +204,7 @@ nsJSScriptTimeoutHandler::Init(nsGlobalWindow *aWindow, PRBool *aIsInterval,
|
||||
JSAutoRequest ar(cx);
|
||||
|
||||
if (argc < 1) {
|
||||
::JS_ReportError(cx, "Function %s requires at least 1 parameter",
|
||||
::JS_ReportError(cx, "Function %s requires at least 2 parameter",
|
||||
*aIsInterval ? kSetIntervalStr : kSetTimeoutStr);
|
||||
return NS_ERROR_DOM_TYPE_ERR;
|
||||
}
|
||||
@ -243,6 +244,32 @@ nsJSScriptTimeoutHandler::Init(nsGlobalWindow *aWindow, PRBool *aIsInterval,
|
||||
}
|
||||
|
||||
if (expr) {
|
||||
// if CSP is enabled, and setTimeout/setInterval was called with a string
|
||||
// or object, disable the registration and log an error
|
||||
nsCOMPtr<nsIDocument> doc = do_QueryInterface(aWindow->GetExtantDocument());
|
||||
|
||||
if (doc) {
|
||||
nsCOMPtr<nsIContentSecurityPolicy> csp;
|
||||
nsresult rv = doc->NodePrincipal()->GetCsp(getter_AddRefs(csp));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (csp) {
|
||||
PRBool allowsEval;
|
||||
// this call will send violation reports as warranted (and return true if
|
||||
// reportOnly is set).
|
||||
rv = csp->GetAllowsEval(&allowsEval);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (!allowsEval) {
|
||||
::JS_ReportError(cx, "call to %s blocked by CSP",
|
||||
*aIsInterval ? kSetIntervalStr : kSetTimeoutStr);
|
||||
|
||||
// Note: Our only caller knows to turn NS_ERROR_DOM_TYPE_ERR into NS_OK.
|
||||
return NS_ERROR_DOM_TYPE_ERR;
|
||||
}
|
||||
}
|
||||
} // if there's no document, we don't have to do anything.
|
||||
|
||||
rv = NS_HOLD_JS_OBJECTS(this, nsJSScriptTimeoutHandler);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
|
@ -326,3 +326,4 @@ MSG_DEF(JSMSG_CANT_DEFINE_ARRAY_INDEX,243, 0, JSEXN_TYPEERR, "can't define array
|
||||
MSG_DEF(JSMSG_TYPED_ARRAY_BAD_INDEX, 244, 0, JSEXN_ERR, "invalid or out-of-range index")
|
||||
MSG_DEF(JSMSG_TYPED_ARRAY_NEGATIVE_ARG, 245, 1, JSEXN_ERR, "argument {0} must be >= 0")
|
||||
MSG_DEF(JSMSG_TYPED_ARRAY_BAD_ARGS, 246, 0, JSEXN_ERR, "invalid arguments")
|
||||
MSG_DEF(JSMSG_CSP_BLOCKED_FUNCTION, 247, 0, JSEXN_ERR, "call to Function() blocked by CSP")
|
||||
|
@ -2015,9 +2015,10 @@ JS_DropPrincipals(JSContext *cx, JSPrincipals *principals);
|
||||
|
||||
|
||||
struct JSSecurityCallbacks {
|
||||
JSCheckAccessOp checkObjectAccess;
|
||||
JSPrincipalsTranscoder principalsTranscoder;
|
||||
JSObjectPrincipalsFinder findObjectPrincipals;
|
||||
JSCheckAccessOp checkObjectAccess;
|
||||
JSPrincipalsTranscoder principalsTranscoder;
|
||||
JSObjectPrincipalsFinder findObjectPrincipals;
|
||||
JSCSPEvalChecker contentSecurityPolicyAllows;
|
||||
};
|
||||
|
||||
extern JS_PUBLIC_API(JSSecurityCallbacks *)
|
||||
|
@ -2218,6 +2218,17 @@ Function(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
/*
|
||||
* CSP check: whether new Function() is allowed at all.
|
||||
* Report errors via CSP is done in the script security manager.
|
||||
* js_CheckContentSecurityPolicy is defined in jsobj.cpp
|
||||
*/
|
||||
if (!js_CheckContentSecurityPolicy(cx)) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
|
||||
JSMSG_CSP_BLOCKED_FUNCTION);
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
n = argc ? argc - 1 : 0;
|
||||
if (n > 0) {
|
||||
enum { OK, BAD, BAD_FORMAL } state;
|
||||
|
@ -1118,6 +1118,26 @@ Object_p_valueOf(JSContext* cx, JSObject* obj, JSString *hint)
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Check if CSP allows new Function() or eval() to run in the current
|
||||
* principals.
|
||||
*/
|
||||
JSBool
|
||||
js_CheckContentSecurityPolicy(JSContext *cx)
|
||||
{
|
||||
JSSecurityCallbacks *callbacks;
|
||||
callbacks = JS_GetSecurityCallbacks(cx);
|
||||
|
||||
// if there are callbacks, make sure that the CSP callback is installed and
|
||||
// that it permits eval().
|
||||
if (callbacks) {
|
||||
return callbacks->contentSecurityPolicyAllows &&
|
||||
callbacks->contentSecurityPolicyAllows(cx);
|
||||
}
|
||||
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check whether principals subsumes scopeobj's principals, and return true
|
||||
* if so (or if scopeobj has no principals, for backward compatibility with
|
||||
@ -1394,6 +1414,13 @@ obj_eval(JSContext *cx, uintN argc, jsval *vp)
|
||||
if (!result)
|
||||
return JS_FALSE;
|
||||
|
||||
// CSP check: is eval() allowed at all?
|
||||
// report errors via CSP is done in the script security mgr.
|
||||
if (!js_CheckContentSecurityPolicy(cx)) {
|
||||
JS_ReportError(cx, "call to eval() blocked by CSP");
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
JSObject *callee = JSVAL_TO_OBJECT(vp[0]);
|
||||
JSPrincipals *principals = js_EvalFramePrincipals(cx, callee, caller);
|
||||
uintN line;
|
||||
|
@ -1100,6 +1100,10 @@ extern JSBool
|
||||
js_CheckPrincipalsAccess(JSContext *cx, JSObject *scopeobj,
|
||||
JSPrincipals *principals, JSAtom *caller);
|
||||
|
||||
/* For CSP -- checks if eval() and friends are allowed to run. */
|
||||
extern JSBool
|
||||
js_CheckContentSecurityPolicy(JSContext *cx);
|
||||
|
||||
/* Infallible -- returns its argument if there is no wrapped object. */
|
||||
extern JSObject *
|
||||
js_GetWrappedObject(JSContext *cx, JSObject *obj);
|
||||
|
@ -584,6 +584,13 @@ typedef JSBool
|
||||
typedef JSPrincipals *
|
||||
(* JSObjectPrincipalsFinder)(JSContext *cx, JSObject *obj);
|
||||
|
||||
/*
|
||||
* Used to check if a CSP instance wants to disable eval() and friends.
|
||||
* See js_CheckCSPPermitsJSAction() in jsobj.
|
||||
*/
|
||||
typedef JSBool
|
||||
(* JSCSPEvalChecker)(JSContext *cx);
|
||||
|
||||
JS_END_EXTERN_C
|
||||
|
||||
#endif /* jspubtd_h___ */
|
||||
|
Loading…
Reference in New Issue
Block a user