mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-23 21:01:08 +00:00
Bug 1529338 - Implement CSP 'style-src-elem' and 'style-src-attr' directives. r=freddyb,emilio,dveditz
Differential Revision: https://phabricator.services.mozilla.com/D151926
This commit is contained in:
parent
059ff10a60
commit
4b8ffda4e2
@ -64,6 +64,8 @@ interface nsIContentSecurityPolicy : nsISerializable
|
||||
NAVIGATE_TO_DIRECTIVE = 21,
|
||||
SCRIPT_SRC_ELEM_DIRECTIVE = 22,
|
||||
SCRIPT_SRC_ATTR_DIRECTIVE = 23,
|
||||
STYLE_SRC_ELEM_DIRECTIVE = 24,
|
||||
STYLE_SRC_ATTR_DIRECTIVE = 25,
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -201,11 +201,17 @@ bool nsCSPContext::permitsInternal(
|
||||
permits = false;
|
||||
}
|
||||
|
||||
// See the comment in nsCSPContext::GetAllowsInline.
|
||||
// In CSP 3.0 the effective directive doesn't become the actually used
|
||||
// directive in case of a fallback from e.g. script-src-elem to
|
||||
// script-src or default-src.
|
||||
// TODO(bug 1779369): Fix this for all directive types.
|
||||
nsAutoString effectiveDirective(violatedDirective);
|
||||
if ((StaticPrefs::security_csp_script_src_attr_elem_enabled() &&
|
||||
(aDir == SCRIPT_SRC_ELEM_DIRECTIVE ||
|
||||
aDir == SCRIPT_SRC_ATTR_DIRECTIVE))) {
|
||||
(aDir == SCRIPT_SRC_ELEM_DIRECTIVE ||
|
||||
aDir == SCRIPT_SRC_ATTR_DIRECTIVE)) ||
|
||||
(StaticPrefs::security_csp_style_src_attr_elem_enabled() &&
|
||||
(aDir == STYLE_SRC_ELEM_DIRECTIVE ||
|
||||
aDir == STYLE_SRC_ATTR_DIRECTIVE))) {
|
||||
effectiveDirective.AssignASCII(CSP_CSPDirectiveToString(aDir));
|
||||
}
|
||||
|
||||
@ -584,9 +590,10 @@ nsCSPContext::GetAllowsInline(CSPDirective aDirective, const nsAString& aNonce,
|
||||
|
||||
if (aDirective != SCRIPT_SRC_ELEM_DIRECTIVE &&
|
||||
aDirective != SCRIPT_SRC_ATTR_DIRECTIVE &&
|
||||
aDirective != STYLE_SRC_DIRECTIVE) {
|
||||
aDirective != STYLE_SRC_ELEM_DIRECTIVE &&
|
||||
aDirective != STYLE_SRC_ATTR_DIRECTIVE) {
|
||||
MOZ_ASSERT(false,
|
||||
"can only allow inline for script-src-(attr/elem) or style");
|
||||
"can only allow inline for (script/style)-src-(attr/elem)");
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -634,6 +641,7 @@ nsCSPContext::GetAllowsInline(CSPDirective aDirective, const nsAString& aNonce,
|
||||
bool reportSample = false;
|
||||
mPolicies[i]->getDirectiveStringAndReportSampleForContentType(
|
||||
aDirective, violatedDirective, &reportSample);
|
||||
|
||||
// In CSP 3.0 the effective directive doesn't become the actually used
|
||||
// directive in case of a fallback from e.g. script-src-elem to
|
||||
// script-src or default-src.
|
||||
@ -641,7 +649,10 @@ nsCSPContext::GetAllowsInline(CSPDirective aDirective, const nsAString& aNonce,
|
||||
nsAutoString effectiveDirective(violatedDirective);
|
||||
if ((StaticPrefs::security_csp_script_src_attr_elem_enabled() &&
|
||||
(aDirective == SCRIPT_SRC_ELEM_DIRECTIVE ||
|
||||
aDirective == SCRIPT_SRC_ATTR_DIRECTIVE))) {
|
||||
aDirective == SCRIPT_SRC_ATTR_DIRECTIVE)) ||
|
||||
(StaticPrefs::security_csp_style_src_attr_elem_enabled() &&
|
||||
(aDirective == STYLE_SRC_ELEM_DIRECTIVE ||
|
||||
aDirective == STYLE_SRC_ATTR_DIRECTIVE))) {
|
||||
effectiveDirective.AssignASCII(CSP_CSPDirectiveToString(aDirective));
|
||||
}
|
||||
|
||||
@ -651,6 +662,7 @@ nsCSPContext::GetAllowsInline(CSPDirective aDirective, const nsAString& aNonce,
|
||||
aLineNumber, aColumnNumber);
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -1519,8 +1531,8 @@ class CSPReportSenderRunnable final : public Runnable {
|
||||
}
|
||||
|
||||
// 4) fire violation event
|
||||
// A frame-ancestors violation has occurred, but we should not dispatch the
|
||||
// violation event to a potentially cross-origin ancestor.
|
||||
// A frame-ancestors violation has occurred, but we should not dispatch
|
||||
// the violation event to a potentially cross-origin ancestor.
|
||||
if (!mViolatedDirective.EqualsLiteral("frame-ancestors")) {
|
||||
mCSPContext->FireViolationEvent(mTriggeringElement, mCSPEventListener,
|
||||
init);
|
||||
@ -1869,9 +1881,9 @@ CSPReportRedirectSink::AsyncOnChannelRedirect(
|
||||
nsresult rv = aOldChannel->Cancel(NS_ERROR_ABORT);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// notify an observer that we have blocked the report POST due to a redirect,
|
||||
// used in testing, do this async since we're in an async call now to begin
|
||||
// with
|
||||
// notify an observer that we have blocked the report POST due to a
|
||||
// redirect, used in testing, do this async since we're in an async call now
|
||||
// to begin with
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
rv = aOldChannel->GetURI(getter_AddRefs(uri));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
@ -50,6 +50,7 @@ nsCSPParser::nsCSPParser(policyTokens& aTokens, nsIURI* aSelfURI,
|
||||
mFrameSrc(nullptr),
|
||||
mWorkerSrc(nullptr),
|
||||
mScriptSrc(nullptr),
|
||||
mStyleSrc(nullptr),
|
||||
mParsingFrameAncestorsDir(false),
|
||||
mTokens(aTokens.Clone()),
|
||||
mSelfURI(aSelfURI),
|
||||
@ -859,9 +860,13 @@ nsCSPDirective* nsCSPParser::directiveName() {
|
||||
}
|
||||
|
||||
// script-src-attr and script-scr-elem might have been disabled.
|
||||
if ((directive == nsIContentSecurityPolicy::SCRIPT_SRC_ATTR_DIRECTIVE ||
|
||||
directive == nsIContentSecurityPolicy::SCRIPT_SRC_ELEM_DIRECTIVE) &&
|
||||
!StaticPrefs::security_csp_script_src_attr_elem_enabled()) {
|
||||
// Similarly style-src-{attr, elem}.
|
||||
if (((directive == nsIContentSecurityPolicy::SCRIPT_SRC_ATTR_DIRECTIVE ||
|
||||
directive == nsIContentSecurityPolicy::SCRIPT_SRC_ELEM_DIRECTIVE) &&
|
||||
!StaticPrefs::security_csp_script_src_attr_elem_enabled()) ||
|
||||
((directive == nsIContentSecurityPolicy::STYLE_SRC_ATTR_DIRECTIVE ||
|
||||
directive == nsIContentSecurityPolicy::STYLE_SRC_ELEM_DIRECTIVE) &&
|
||||
!StaticPrefs::security_csp_style_src_attr_elem_enabled())) {
|
||||
AutoTArray<nsString, 1> params = {mCurToken};
|
||||
logWarningErrorToConsole(nsIScriptError::warningFlag,
|
||||
"notSupportingDirective", params);
|
||||
@ -941,6 +946,13 @@ nsCSPDirective* nsCSPParser::directiveName() {
|
||||
return mScriptSrc;
|
||||
}
|
||||
|
||||
// If we have a style-src, cache it as a fallback for style-src-elem and
|
||||
// style-src-attr.
|
||||
if (directive == nsIContentSecurityPolicy::STYLE_SRC_DIRECTIVE) {
|
||||
mStyleSrc = new nsCSPStyleSrcDirective(directive);
|
||||
return mStyleSrc;
|
||||
}
|
||||
|
||||
return new nsCSPDirective(directive);
|
||||
}
|
||||
|
||||
@ -1155,6 +1167,20 @@ nsCSPPolicy* nsCSPParser::policy() {
|
||||
mScriptSrc->setRestrictScriptAttr();
|
||||
}
|
||||
|
||||
// If style-src is specified and style-src-elem is not specified, then
|
||||
// style-src serves as a fallback.
|
||||
if (mStyleSrc && !mPolicy->hasDirective(
|
||||
nsIContentSecurityPolicy::STYLE_SRC_ELEM_DIRECTIVE)) {
|
||||
mStyleSrc->setRestrictStyleElem();
|
||||
}
|
||||
|
||||
// If style-src is specified and style-attr-elem is not specified, then
|
||||
// style-src serves as a fallback.
|
||||
if (mStyleSrc && !mPolicy->hasDirective(
|
||||
nsIContentSecurityPolicy::STYLE_SRC_ATTR_DIRECTIVE)) {
|
||||
mStyleSrc->setRestrictStyleAttr();
|
||||
}
|
||||
|
||||
return mPolicy;
|
||||
}
|
||||
|
||||
|
@ -198,6 +198,7 @@ class nsCSPParser {
|
||||
nsCSPDirective* mFrameSrc;
|
||||
nsCSPDirective* mWorkerSrc;
|
||||
nsCSPScriptSrcDirective* mScriptSrc;
|
||||
nsCSPStyleSrcDirective* mStyleSrc;
|
||||
|
||||
// cache variable to let nsCSPHostSrc know that it's within
|
||||
// the frame-ancestors directive.
|
||||
|
@ -289,7 +289,7 @@ CSPDirective CSP_ContentTypeToDirective(nsContentPolicyType aType) {
|
||||
case nsIContentPolicy::TYPE_STYLESHEET:
|
||||
case nsIContentPolicy::TYPE_INTERNAL_STYLESHEET:
|
||||
case nsIContentPolicy::TYPE_INTERNAL_STYLESHEET_PRELOAD:
|
||||
return nsIContentSecurityPolicy::STYLE_SRC_DIRECTIVE;
|
||||
return nsIContentSecurityPolicy::STYLE_SRC_ELEM_DIRECTIVE;
|
||||
|
||||
case nsIContentPolicy::TYPE_FONT:
|
||||
case nsIContentPolicy::TYPE_INTERNAL_FONT_PRELOAD:
|
||||
@ -1293,10 +1293,7 @@ bool nsCSPChildSrcDirective::equals(CSPDirective aDirective) const {
|
||||
/* =============== nsCSPScriptSrcDirective ============= */
|
||||
|
||||
nsCSPScriptSrcDirective::nsCSPScriptSrcDirective(CSPDirective aDirective)
|
||||
: nsCSPDirective(aDirective),
|
||||
mRestrictWorkers(false),
|
||||
mRestrictScriptElem(false),
|
||||
mRestrictScriptAttr(false) {}
|
||||
: nsCSPDirective(aDirective) {}
|
||||
|
||||
nsCSPScriptSrcDirective::~nsCSPScriptSrcDirective() = default;
|
||||
|
||||
@ -1310,7 +1307,24 @@ bool nsCSPScriptSrcDirective::equals(CSPDirective aDirective) const {
|
||||
if (aDirective == nsIContentSecurityPolicy::SCRIPT_SRC_ATTR_DIRECTIVE) {
|
||||
return mRestrictScriptAttr;
|
||||
}
|
||||
return (mDirective == aDirective);
|
||||
return mDirective == aDirective;
|
||||
}
|
||||
|
||||
/* =============== nsCSPStyleSrcDirective ============= */
|
||||
|
||||
nsCSPStyleSrcDirective::nsCSPStyleSrcDirective(CSPDirective aDirective)
|
||||
: nsCSPDirective(aDirective) {}
|
||||
|
||||
nsCSPStyleSrcDirective::~nsCSPStyleSrcDirective() = default;
|
||||
|
||||
bool nsCSPStyleSrcDirective::equals(CSPDirective aDirective) const {
|
||||
if (aDirective == nsIContentSecurityPolicy::STYLE_SRC_ELEM_DIRECTIVE) {
|
||||
return mRestrictStyleElem;
|
||||
}
|
||||
if (aDirective == nsIContentSecurityPolicy::STYLE_SRC_ATTR_DIRECTIVE) {
|
||||
return mRestrictStyleAttr;
|
||||
}
|
||||
return mDirective == aDirective;
|
||||
}
|
||||
|
||||
/* =============== nsBlockAllMixedContentDirective ============= */
|
||||
|
@ -91,6 +91,8 @@ static const char* CSPStrDirectives[] = {
|
||||
"navigate-to", // NAVIGATE_TO_DIRECTIVE
|
||||
"script-src-elem", // SCRIPT_SRC_ELEM_DIRECTIVE
|
||||
"script-src-attr", // SCRIPT_SRC_ATTR_DIRECTIVE
|
||||
"style-src-elem", // STYLE_SRC_ELEM_DIRECTIVE
|
||||
"style-src-attr", // STYLE_SRC_ATTR_DIRECTIVE
|
||||
};
|
||||
|
||||
inline const char* CSP_CSPDirectiveToString(CSPDirective aDir) {
|
||||
@ -515,12 +517,33 @@ class nsCSPScriptSrcDirective : public nsCSPDirective {
|
||||
void setRestrictScriptElem() { mRestrictScriptElem = true; }
|
||||
void setRestrictScriptAttr() { mRestrictScriptAttr = true; }
|
||||
|
||||
virtual bool equals(CSPDirective aDirective) const override;
|
||||
bool equals(CSPDirective aDirective) const override;
|
||||
|
||||
private:
|
||||
bool mRestrictWorkers;
|
||||
bool mRestrictScriptElem;
|
||||
bool mRestrictScriptAttr;
|
||||
bool mRestrictWorkers = false;
|
||||
bool mRestrictScriptElem = false;
|
||||
bool mRestrictScriptAttr = false;
|
||||
};
|
||||
|
||||
/* =============== nsCSPStyleSrcDirective ============= */
|
||||
|
||||
/*
|
||||
* In CSP 3 style-src is use as a fallback for style-src-elem and
|
||||
* style-src-attr.
|
||||
*/
|
||||
class nsCSPStyleSrcDirective : public nsCSPDirective {
|
||||
public:
|
||||
explicit nsCSPStyleSrcDirective(CSPDirective aDirective);
|
||||
virtual ~nsCSPStyleSrcDirective();
|
||||
|
||||
void setRestrictStyleElem() { mRestrictStyleElem = true; }
|
||||
void setRestrictStyleAttr() { mRestrictStyleAttr = true; }
|
||||
|
||||
bool equals(CSPDirective aDirective) const override;
|
||||
|
||||
private:
|
||||
bool mRestrictStyleElem = false;
|
||||
bool mRestrictStyleAttr = false;
|
||||
};
|
||||
|
||||
/* =============== nsBlockAllMixedContentDirective === */
|
||||
|
@ -50,7 +50,7 @@ function checkResults(reportStr) {
|
||||
"http://mochi.test:8888/tests/dom/security/test/csp/test_report_for_import.html",
|
||||
"Incorrect referrer");
|
||||
is(cspReport["violated-directive"],
|
||||
"style-src",
|
||||
"style-src-elem",
|
||||
"Incorrect violated-directive");
|
||||
is(cspReport["original-policy"], POLICY, "Incorrect original-policy");
|
||||
is(cspReport["blocked-uri"],
|
||||
|
@ -306,9 +306,12 @@ bool nsStyleUtil::CSPAllowsInlineStyle(
|
||||
return true;
|
||||
}
|
||||
|
||||
nsIContentSecurityPolicy::CSPDirective directive =
|
||||
nsIContentSecurityPolicy::STYLE_SRC_ATTR_DIRECTIVE;
|
||||
// query the nonce
|
||||
nsAutoString nonce;
|
||||
if (aElement && aElement->NodeInfo()->NameAtom() == nsGkAtoms::style) {
|
||||
directive = nsIContentSecurityPolicy::STYLE_SRC_ELEM_DIRECTIVE;
|
||||
nsString* cspNonce =
|
||||
static_cast<nsString*>(aElement->GetProperty(nsGkAtoms::nonce));
|
||||
if (cspNonce) {
|
||||
@ -317,11 +320,11 @@ bool nsStyleUtil::CSPAllowsInlineStyle(
|
||||
}
|
||||
|
||||
bool allowInlineStyle = true;
|
||||
rv = csp->GetAllowsInline(
|
||||
nsIContentSecurityPolicy::STYLE_SRC_DIRECTIVE, nonce,
|
||||
false, // aParserCreated only applies to scripts
|
||||
aElement, nullptr, // nsICSPEventListener
|
||||
aStyleText, aLineNumber, aColumnNumber, &allowInlineStyle);
|
||||
rv = csp->GetAllowsInline(directive, nonce,
|
||||
false, // aParserCreated only applies to scripts
|
||||
aElement, nullptr, // nsICSPEventListener
|
||||
aStyleText, aLineNumber, aColumnNumber,
|
||||
&allowInlineStyle);
|
||||
NS_ENSURE_SUCCESS(rv, false);
|
||||
|
||||
return allowInlineStyle;
|
||||
|
@ -12337,6 +12337,12 @@
|
||||
value: @IS_NIGHTLY_BUILD@
|
||||
mirror: always
|
||||
|
||||
# The style-src-attr and style-src-elem directive
|
||||
- name: security.csp.style-src-attr-elem.enabled
|
||||
type: bool
|
||||
value: @IS_NIGHTLY_BUILD@
|
||||
mirror: always
|
||||
|
||||
# No way to enable on Android, Bug 1552602
|
||||
- name: security.webauth.u2f
|
||||
type: bool
|
||||
|
@ -1 +1 @@
|
||||
prefs: [security.csp.script-src-attr-elem.enabled:true]
|
||||
prefs: [security.csp.script-src-attr-elem.enabled:true, security.csp.style-src-attr-elem.enabled:true]
|
||||
|
@ -1,4 +0,0 @@
|
||||
[combine-header-and-meta-policies.sub.html]
|
||||
[Expecting logs: ["TEST COMPLETE", "violated-directive=img-src", "violated-directive=style-src-elem"\]]
|
||||
expected: FAIL
|
||||
|
@ -1,4 +0,0 @@
|
||||
[style-src-attr-allowed-src-blocked.html]
|
||||
[Should apply the style attribute]
|
||||
expected: FAIL
|
||||
|
@ -1,8 +0,0 @@
|
||||
[style-src-attr-blocked-src-allowed.html]
|
||||
expected: TIMEOUT
|
||||
[The attribute style should not be applied]
|
||||
expected: FAIL
|
||||
|
||||
[Should fire a security policy violation event]
|
||||
expected: NOTRUN
|
||||
|
@ -1,8 +0,0 @@
|
||||
[style-src-elem-allowed-attr-blocked.html]
|
||||
expected: TIMEOUT
|
||||
[The attribute style should not be applied and the inline style should be applied]
|
||||
expected: FAIL
|
||||
|
||||
[Should fire a security policy violation for the attribute]
|
||||
expected: NOTRUN
|
||||
|
@ -1,4 +0,0 @@
|
||||
[style-src-elem-allowed-src-blocked.html]
|
||||
[Inline style should be applied]
|
||||
expected: FAIL
|
||||
|
@ -1,8 +0,0 @@
|
||||
[style-src-elem-blocked-attr-allowed.html]
|
||||
expected: TIMEOUT
|
||||
[Should fire a security policy violation for the inline block]
|
||||
expected: NOTRUN
|
||||
|
||||
[The inline style should not be applied and the attribute style should be applied]
|
||||
expected: FAIL
|
||||
|
@ -1,8 +0,0 @@
|
||||
[style-src-elem-blocked-src-allowed.html]
|
||||
expected: TIMEOUT
|
||||
[Should fire a security policy violation event]
|
||||
expected: NOTRUN
|
||||
|
||||
[The inline style should not be applied]
|
||||
expected: FAIL
|
||||
|
@ -1,3 +0,0 @@
|
||||
[injected-inline-style-blocked.sub.html]
|
||||
[Expecting logs: ["violated-directive=style-src-elem","violated-directive=style-src-elem","PASS"\]]
|
||||
expected: FAIL
|
@ -1,7 +1,4 @@
|
||||
[inline-style-allowed-while-cloning-objects.sub.html]
|
||||
[Test that violation report event was fired]
|
||||
expected: FAIL
|
||||
|
||||
[inline-style-allowed-while-cloning-objects 18]
|
||||
expected: FAIL
|
||||
|
||||
|
@ -1,5 +0,0 @@
|
||||
implementation-status: backlog
|
||||
[inline-style-attribute-blocked.sub.html]
|
||||
[Expecting logs: ["violated-directive=style-src-attr","PASS"\]]
|
||||
expected: FAIL
|
||||
|
@ -1,6 +1,3 @@
|
||||
[style-blocked.html]
|
||||
[Violated directive is script-src-elem.]
|
||||
expected: FAIL
|
||||
|
||||
[document.styleSheets should contain an item for the blocked CSS.]
|
||||
expected: FAIL
|
||||
|
@ -1,4 +0,0 @@
|
||||
[style-src-hash-blocked.html]
|
||||
[Should fire a securitypolicyviolation event]
|
||||
expected: FAIL
|
||||
|
@ -1,4 +0,0 @@
|
||||
[style-src-imported-style-blocked.html]
|
||||
[Should fire a securitypolicyviolation event]
|
||||
expected: FAIL
|
||||
|
@ -1,4 +0,0 @@
|
||||
[style-src-injected-inline-style-blocked.html]
|
||||
[Should fire a securitypolicyviolation event]
|
||||
expected: FAIL
|
||||
|
@ -1,4 +0,0 @@
|
||||
[style-src-injected-stylesheet-blocked.sub.html]
|
||||
[Should fire a securitypolicyviolation event]
|
||||
expected: FAIL
|
||||
|
@ -1,5 +0,0 @@
|
||||
implementation-status: backlog
|
||||
[style-src-inline-style-attribute-blocked.html]
|
||||
[Should fire a securitypolicyviolation event]
|
||||
expected: FAIL
|
||||
|
@ -1,4 +0,0 @@
|
||||
[style-src-inline-style-blocked.html]
|
||||
[Should fire a securitypolicyviolation event]
|
||||
expected: FAIL
|
||||
|
@ -4,6 +4,3 @@ implementation-status: backlog
|
||||
[Test that paragraph remains unmodified and error events received.]
|
||||
expected: NOTRUN
|
||||
|
||||
[Should fire a securitypolicyviolation event]
|
||||
expected: FAIL
|
||||
|
||||
|
@ -1,4 +0,0 @@
|
||||
[style-src-inline-style-nonce-blocked.html]
|
||||
[Should fire a securitypolicyviolation event]
|
||||
expected: FAIL
|
||||
|
@ -1,4 +0,0 @@
|
||||
[style-src-none-blocked.html]
|
||||
[Should fire a securitypolicyviolation event]
|
||||
expected: FAIL
|
||||
|
@ -1,4 +0,0 @@
|
||||
[style-src-stylesheet-nonce-blocked.html]
|
||||
[Should fire a securitypolicyviolation event]
|
||||
expected: FAIL
|
||||
|
@ -1,4 +0,0 @@
|
||||
[stylehash-basic-blocked.sub.html]
|
||||
[Expecting alerts: ["PASS: The 'p' element's text is green, which means the style was correctly applied.", "violated-directive=style-src-elem"\]]
|
||||
expected: FAIL
|
||||
|
@ -1,4 +0,0 @@
|
||||
[stylenonce-allowed.sub.html]
|
||||
[Should fire securitypolicyviolation]
|
||||
expected: FAIL
|
||||
|
@ -1,4 +0,0 @@
|
||||
[stylenonce-blocked.sub.html]
|
||||
[Should fire securitypolicyviolation]
|
||||
expected: FAIL
|
||||
|
@ -1,5 +0,0 @@
|
||||
implementation-status: backlog
|
||||
[style_attribute_denied_wrong_hash.html]
|
||||
[Test that the inline style attribute is blocked]
|
||||
expected: FAIL
|
||||
|
Loading…
Reference in New Issue
Block a user