enhance: Add support for unsafe-eval and wasm-unsafe-eval (#32893)

Signed-off-by: Chocolate Pie <106949016+chocolate-pie@users.noreply.github.com>
This commit is contained in:
Chocolate Pie 2024-08-02 02:26:44 +09:00 committed by GitHub
parent 2cf207ddc8
commit 92866ab911
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
51 changed files with 755 additions and 73 deletions

View file

@ -474,6 +474,8 @@ pub struct Document {
fonts: MutNullableDom<FontFaceSet>,
/// <https://html.spec.whatwg.org/multipage/#visibility-state>
visibility_state: Cell<DocumentVisibilityState>,
/// <https://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml>
status_code: Option<u16>,
}
#[derive(JSTraceable, MallocSizeOf)]
@ -3001,6 +3003,10 @@ impl Document {
};
global_scope.report_an_error(error_info, HandleValue::null());
}
pub(crate) fn status_code(&self) -> Option<u16> {
self.status_code
}
}
fn is_character_value_key(key: &Key) -> bool {
@ -3156,6 +3162,7 @@ impl Document {
doc_loader: DocumentLoader,
referrer: Option<String>,
referrer_policy: Option<ReferrerPolicy>,
status_code: Option<u16>,
canceller: FetchCanceller,
) -> Document {
let url = url.unwrap_or_else(|| ServoUrl::parse("about:blank").unwrap());
@ -3306,6 +3313,7 @@ impl Document {
resize_observers: Default::default(),
fonts: Default::default(),
visibility_state: Cell::new(DocumentVisibilityState::Hidden),
status_code,
}
}
@ -3443,6 +3451,7 @@ impl Document {
docloader,
None,
None,
None,
Default::default(),
))
}
@ -3461,6 +3470,7 @@ impl Document {
doc_loader: DocumentLoader,
referrer: Option<String>,
referrer_policy: Option<ReferrerPolicy>,
status_code: Option<u16>,
canceller: FetchCanceller,
) -> DomRoot<Document> {
Self::new_with_proto(
@ -3477,6 +3487,7 @@ impl Document {
doc_loader,
referrer,
referrer_policy,
status_code,
canceller,
)
}
@ -3496,6 +3507,7 @@ impl Document {
doc_loader: DocumentLoader,
referrer: Option<String>,
referrer_policy: Option<ReferrerPolicy>,
status_code: Option<u16>,
canceller: FetchCanceller,
) -> DomRoot<Document> {
let document = reflect_dom_object_with_proto(
@ -3512,6 +3524,7 @@ impl Document {
doc_loader,
referrer,
referrer_policy,
status_code,
canceller,
)),
window,
@ -3634,6 +3647,7 @@ impl Document {
DocumentLoader::new(&self.loader()),
None,
None,
None,
Default::default(),
);
new_doc

View file

@ -155,6 +155,7 @@ impl DOMImplementationMethods for DOMImplementation {
loader,
None,
None,
None,
Default::default(),
);

View file

@ -78,6 +78,7 @@ impl DOMParserMethods for DOMParser {
loader,
None,
None,
None,
Default::default(),
);
ServoParser::parse_html_document(&document, Some(s), url);
@ -98,6 +99,7 @@ impl DOMParserMethods for DOMParser {
loader,
None,
None,
None,
Default::default(),
);
ServoParser::parse_xml_document(&document, Some(s), url);

View file

@ -18,7 +18,7 @@ use base::id::{
BlobId, BroadcastChannelRouterId, MessagePortId, MessagePortRouterId, PipelineId,
ServiceWorkerId, ServiceWorkerRegistrationId,
};
use content_security_policy::CspList;
use content_security_policy::{CheckResult, CspList, PolicyDisposition};
use crossbeam_channel::Sender;
use devtools_traits::{PageError, ScriptToDevtoolsControlMsg};
use dom_struct::dom_struct;
@ -28,14 +28,15 @@ use ipc_channel::router::ROUTER;
use js::glue::{IsWrapper, UnwrapObjectDynamic};
use js::jsapi::{
Compile1, CurrentGlobalOrNull, GetNonCCWObjectGlobal, HandleObject, Heap,
InstantiateGlobalStencil, InstantiateOptions, JSContext, JSObject, JSScript, SetScriptPrivate,
InstantiateGlobalStencil, InstantiateOptions, JSContext, JSObject, JSScript, RuntimeCode,
SetScriptPrivate,
};
use js::jsval::{JSVal, PrivateValue, UndefinedValue};
use js::panic::maybe_resume_unwind;
use js::rust::wrappers::{JS_ExecuteScript, JS_GetScriptPrivate};
use js::rust::{
get_object_class, transform_str_to_source_text, CompileOptionsWrapper, HandleValue,
MutableHandleValue, ParentRuntime, Runtime,
describe_scripted_caller, get_object_class, transform_str_to_source_text,
CompileOptionsWrapper, HandleValue, MutableHandleValue, ParentRuntime, Runtime,
};
use js::{JSCLASS_IS_DOMJSCLASS, JSCLASS_IS_GLOBAL};
use net_traits::blob_url_store::{get_blob_origin, BlobBuf};
@ -119,6 +120,7 @@ use crate::script_runtime::{
CommonScriptMsg, ContextForRequestInterrupt, JSContext as SafeJSContext, ScriptChan, ScriptPort,
};
use crate::script_thread::{MainThreadScriptChan, ScriptThread};
use crate::security_manager::CSPViolationReporter;
use crate::task::TaskCanceller;
use crate::task_source::dom_manipulation::DOMManipulationTaskSource;
use crate::task_source::file_reading::FileReadingTaskSource;
@ -2773,6 +2775,37 @@ impl GlobalScope {
}))
}
#[allow(unsafe_code)]
pub fn is_js_evaluation_allowed(&self, cx: SafeJSContext) -> bool {
let Some(csp_list) = self.get_csp_list() else {
return true;
};
let scripted_caller = unsafe { describe_scripted_caller(*cx) }.unwrap_or_default();
let is_js_evaluation_allowed = csp_list.is_js_evaluation_allowed() == CheckResult::Allowed;
if !is_js_evaluation_allowed {
// FIXME: Don't fire event if `script-src` and `default-src`
// were not passed.
for policy in csp_list.0 {
let task = CSPViolationReporter::new(
self,
None,
policy.disposition == PolicyDisposition::Report,
RuntimeCode::JS,
scripted_caller.filename.clone(),
scripted_caller.line,
scripted_caller.col,
);
self.dom_manipulation_task_source()
.queue(task, self)
.unwrap();
}
}
is_js_evaluation_allowed
}
pub fn create_image_bitmap(
&self,
image: ImageBitmapSource,
@ -3090,6 +3123,13 @@ impl GlobalScope {
None
}
pub fn status_code(&self) -> Option<u16> {
if let Some(window) = self.downcast::<Window>() {
return window.Document().status_code();
}
None
}
pub fn wgpu_id_hub(&self) -> Arc<Identities> {
self.gpu_id_hub.clone()
}

View file

@ -2,6 +2,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use content_security_policy::{CspList, PolicyDisposition, PolicySource};
use dom_struct::dom_struct;
use html5ever::{local_name, namespace_url, ns, LocalName, Prefix};
use js::rust::HandleObject;
@ -80,6 +81,48 @@ impl HTMLHeadElement {
}
}
}
/// <https://html.spec.whatwg.org/multipage/#attr-meta-http-equiv-content-security-policy>
pub fn set_content_security_policy(&self) {
let doc = document_from_node(self);
if doc.GetHead().as_deref() != Some(self) {
return;
}
let mut csp_list: Option<CspList> = None;
let node = self.upcast::<Node>();
let candinates = node
.traverse_preorder(ShadowIncluding::No)
.filter_map(DomRoot::downcast::<Element>)
.filter(|elem| elem.is::<HTMLMetaElement>())
.filter(|elem| {
elem.get_string_attribute(&local_name!("http-equiv"))
.to_ascii_lowercase() ==
"content-security-policy".to_owned()
})
.filter(|elem| {
elem.get_attribute(&ns!(), &local_name!("content"))
.is_some()
});
for meta in candinates {
if let Some(ref content) = meta.get_attribute(&ns!(), &local_name!("content")) {
let content = content.value();
let content_val = content.trim();
if !content_val.is_empty() {
let policies =
CspList::parse(content_val, PolicySource::Meta, PolicyDisposition::Enforce);
match csp_list {
Some(ref mut csp_list) => csp_list.append(policies),
None => csp_list = Some(policies),
}
}
}
}
doc.set_csp_list(csp_list);
}
}
impl VirtualMethods for HTMLHeadElement {

View file

@ -88,8 +88,10 @@ impl HTMLMetaElement {
// https://html.spec.whatwg.org/multipage/#attr-meta-http-equiv
} else if !self.HttpEquiv().is_empty() {
// TODO: Implement additional http-equiv candidates
if self.HttpEquiv().to_ascii_lowercase().as_str() == "refresh" {
self.declarative_refresh();
match self.HttpEquiv().to_ascii_lowercase().as_str() {
"refresh" => self.declarative_refresh(),
"content-security-policy" => self.apply_csp_list(),
_ => {},
}
}
}
@ -115,6 +117,15 @@ impl HTMLMetaElement {
}
}
/// <https://html.spec.whatwg.org/multipage/#attr-meta-http-equiv-content-security-policy>
fn apply_csp_list(&self) {
if let Some(parent) = self.upcast::<Node>().GetParentElement() {
if let Some(head) = parent.downcast::<HTMLHeadElement>() {
head.set_content_security_policy();
}
}
}
/// <https://html.spec.whatwg.org/multipage/#shared-declarative-refresh-steps>
fn declarative_refresh(&self) {
// 2

View file

@ -471,6 +471,7 @@ macro_rules! global_event_handlers(
event_handler!(progress, GetOnprogress, SetOnprogress);
event_handler!(ratechange, GetOnratechange, SetOnratechange);
event_handler!(reset, GetOnreset, SetOnreset);
event_handler!(securitypolicyviolation, GetOnsecuritypolicyviolation, SetOnsecuritypolicyviolation);
event_handler!(seeked, GetOnseeked, SetOnseeked);
event_handler!(seeking, GetOnseeking, SetOnseeking);
event_handler!(select, GetOnselect, SetOnselect);

View file

@ -525,6 +525,7 @@ pub(crate) mod rtcrtptransceiver;
pub mod rtcsessiondescription;
pub mod rtctrackevent;
pub mod screen;
pub mod securitypolicyviolationevent;
pub mod selection;
pub mod serviceworker;
pub mod serviceworkercontainer;

View file

@ -2296,6 +2296,7 @@ impl Node {
loader,
None,
None,
document.status_code(),
Default::default(),
);
DomRoot::upcast::<Node>(document)

View file

@ -720,8 +720,11 @@ impl From<RequestDestination> for NetTraitsRequestDestination {
RequestDestination::Document => NetTraitsRequestDestination::Document,
RequestDestination::Embed => NetTraitsRequestDestination::Embed,
RequestDestination::Font => NetTraitsRequestDestination::Font,
RequestDestination::Frame => NetTraitsRequestDestination::Frame,
RequestDestination::Iframe => NetTraitsRequestDestination::IFrame,
RequestDestination::Image => NetTraitsRequestDestination::Image,
RequestDestination::Manifest => NetTraitsRequestDestination::Manifest,
RequestDestination::Json => NetTraitsRequestDestination::Json,
RequestDestination::Object => NetTraitsRequestDestination::Object,
RequestDestination::Report => NetTraitsRequestDestination::Report,
RequestDestination::Script => NetTraitsRequestDestination::Script,
@ -743,8 +746,11 @@ impl From<NetTraitsRequestDestination> for RequestDestination {
NetTraitsRequestDestination::Document => RequestDestination::Document,
NetTraitsRequestDestination::Embed => RequestDestination::Embed,
NetTraitsRequestDestination::Font => RequestDestination::Font,
NetTraitsRequestDestination::Frame => RequestDestination::Frame,
NetTraitsRequestDestination::IFrame => RequestDestination::Iframe,
NetTraitsRequestDestination::Image => RequestDestination::Image,
NetTraitsRequestDestination::Manifest => RequestDestination::Manifest,
NetTraitsRequestDestination::Json => RequestDestination::Json,
NetTraitsRequestDestination::Object => RequestDestination::Object,
NetTraitsRequestDestination::Report => RequestDestination::Report,
NetTraitsRequestDestination::Script => RequestDestination::Script,
@ -759,6 +765,7 @@ impl From<NetTraitsRequestDestination> for RequestDestination {
NetTraitsRequestDestination::Video => RequestDestination::Video,
NetTraitsRequestDestination::Worker => RequestDestination::Worker,
NetTraitsRequestDestination::Xslt => RequestDestination::Xslt,
NetTraitsRequestDestination::WebIdentity => RequestDestination::_empty,
}
}
}

View file

@ -0,0 +1,180 @@
/* 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 https://mozilla.org/MPL/2.0/. */
use dom_struct::dom_struct;
use js::rust::HandleObject;
use servo_atoms::Atom;
use super::bindings::reflector::reflect_dom_object_with_proto;
use crate::dom::bindings::codegen::Bindings::EventBinding::Event_Binding::EventMethods;
use crate::dom::bindings::codegen::Bindings::SecurityPolicyViolationEventBinding::{
SecurityPolicyViolationEventDisposition, SecurityPolicyViolationEventInit,
SecurityPolicyViolationEventMethods,
};
use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::root::DomRoot;
use crate::dom::bindings::str::{DOMString, USVString};
use crate::dom::event::{Event, EventBubbles, EventCancelable};
use crate::dom::globalscope::GlobalScope;
// https://w3c.github.io/webappsec-csp/#securitypolicyviolationevent
#[dom_struct]
pub struct SecurityPolicyViolationEvent {
event: Event,
document_uri: USVString,
referrer: USVString,
blocked_uri: USVString,
effective_directive: DOMString,
violated_directive: DOMString,
original_policy: DOMString,
source_file: USVString,
sample: DOMString,
disposition: SecurityPolicyViolationEventDisposition,
status_code: u16,
line_number: u32,
column_number: u32,
}
impl SecurityPolicyViolationEvent {
fn new_inherited(init: &SecurityPolicyViolationEventInit) -> SecurityPolicyViolationEvent {
SecurityPolicyViolationEvent {
event: Event::new_inherited(),
document_uri: init.documentURI.clone(),
referrer: init.referrer.clone(),
blocked_uri: init.blockedURI.clone(),
effective_directive: init.effectiveDirective.clone(),
violated_directive: init.violatedDirective.clone(),
original_policy: init.originalPolicy.clone(),
source_file: init.sourceFile.clone(),
sample: init.sample.clone(),
disposition: init.disposition.clone(),
status_code: init.statusCode,
line_number: init.lineNumber,
column_number: init.columnNumber,
}
}
pub fn new_initialized(
global: &GlobalScope,
init: &SecurityPolicyViolationEventInit,
proto: Option<HandleObject>,
) -> DomRoot<SecurityPolicyViolationEvent> {
reflect_dom_object_with_proto(
Box::new(SecurityPolicyViolationEvent::new_inherited(init)),
global,
proto,
)
}
fn new_with_proto(
global: &GlobalScope,
proto: Option<HandleObject>,
type_: Atom,
bubbles: EventBubbles,
cancelable: EventCancelable,
init: &SecurityPolicyViolationEventInit,
) -> DomRoot<Self> {
let ev = SecurityPolicyViolationEvent::new_initialized(global, init, proto);
{
let event = ev.upcast::<Event>();
event.init_event(type_, bool::from(bubbles), bool::from(cancelable));
}
ev
}
pub fn new(
global: &GlobalScope,
type_: Atom,
bubbles: EventBubbles,
cancelable: EventCancelable,
init: &SecurityPolicyViolationEventInit,
) -> DomRoot<Self> {
Self::new_with_proto(global, None, type_, bubbles, cancelable, init)
}
#[allow(non_snake_case)]
pub fn Constructor(
global: &GlobalScope,
proto: Option<HandleObject>,
type_: DOMString,
init: &SecurityPolicyViolationEventInit,
) -> DomRoot<Self> {
SecurityPolicyViolationEvent::new_with_proto(
global,
proto,
Atom::from(type_),
EventBubbles::from(init.parent.bubbles),
EventCancelable::from(init.parent.cancelable),
init,
)
}
}
#[allow(non_snake_case)]
impl SecurityPolicyViolationEventMethods for SecurityPolicyViolationEvent {
/// <https://w3c.github.io/webappsec-csp/#dom-securitypolicyviolationevent-documenturi>
fn DocumentURI(&self) -> USVString {
self.document_uri.clone()
}
/// <https://w3c.github.io/webappsec-csp/#dom-securitypolicyviolationevent-referrer>
fn Referrer(&self) -> USVString {
self.referrer.clone()
}
/// <https://w3c.github.io/webappsec-csp/#dom-securitypolicyviolationevent-blockeduri>
fn BlockedURI(&self) -> USVString {
self.blocked_uri.clone()
}
/// <https://w3c.github.io/webappsec-csp/#dom-securitypolicyviolationevent-effectivedirective>
fn EffectiveDirective(&self) -> DOMString {
self.effective_directive.clone()
}
/// <https://w3c.github.io/webappsec-csp/#dom-securitypolicyviolationevent-violateddirective>
fn ViolatedDirective(&self) -> DOMString {
self.violated_directive.clone()
}
/// <https://w3c.github.io/webappsec-csp/#dom-securitypolicyviolationevent-originalpolicy>
fn OriginalPolicy(&self) -> DOMString {
self.original_policy.clone()
}
/// <https://w3c.github.io/webappsec-csp/#dom-securitypolicyviolationevent-sourcefile>
fn SourceFile(&self) -> USVString {
self.source_file.clone()
}
/// <https://w3c.github.io/webappsec-csp/#dom-securitypolicyviolationevent-sample>
fn Sample(&self) -> DOMString {
self.sample.clone()
}
/// <https://w3c.github.io/webappsec-csp/#dom-securitypolicyviolationevent-disposition>
fn Disposition(&self) -> SecurityPolicyViolationEventDisposition {
self.disposition.clone()
}
/// <https://w3c.github.io/webappsec-csp/#dom-securitypolicyviolationevent-statuscode>
fn StatusCode(&self) -> u16 {
self.status_code
}
/// <https://w3c.github.io/webappsec-csp/#dom-securitypolicyviolationevent-linenumber>
fn LineNumber(&self) -> u32 {
self.line_number
}
/// <https://w3c.github.io/webappsec-csp/#dom-securitypolicyviolationevent-columnnumber>
fn ColumnNumber(&self) -> u32 {
self.column_number
}
/// <https://dom.spec.whatwg.org/#dom-event-istrusted>
fn IsTrusted(&self) -> bool {
self.event.IsTrusted()
}
}

View file

@ -214,6 +214,7 @@ impl ServoParser {
loader,
None,
None,
None,
Default::default(),
);

View file

@ -77,6 +77,7 @@ interface mixin GlobalEventHandlers {
attribute EventHandler onreset;
attribute EventHandler onresize;
attribute EventHandler onscroll;
attribute EventHandler onsecuritypolicyviolation;
attribute EventHandler onseeked;
attribute EventHandler onseeking;
attribute EventHandler onselect;

View file

@ -47,7 +47,10 @@ enum RequestDestination {
"document",
"embed",
"font",
"frame",
"iframe",
"image",
"json",
"manifest",
"object",
"report",

View file

@ -0,0 +1,41 @@
/* 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 https://mozilla.org/MPL/2.0/. */
// https://w3c.github.io/webappsec-csp/#securitypolicyviolationevent
enum SecurityPolicyViolationEventDisposition {
"enforce", "report"
};
[Exposed=(Window,Worker)]
interface SecurityPolicyViolationEvent : Event {
constructor(DOMString type, optional SecurityPolicyViolationEventInit eventInitDict = {});
readonly attribute USVString documentURI;
readonly attribute USVString referrer;
readonly attribute USVString blockedURI;
readonly attribute DOMString effectiveDirective;
readonly attribute DOMString violatedDirective; // historical alias of effectiveDirective
readonly attribute DOMString originalPolicy;
readonly attribute USVString sourceFile;
readonly attribute DOMString sample;
readonly attribute SecurityPolicyViolationEventDisposition disposition;
readonly attribute unsigned short statusCode;
readonly attribute unsigned long lineNumber;
readonly attribute unsigned long columnNumber;
};
dictionary SecurityPolicyViolationEventInit : EventInit {
USVString documentURI = "";
USVString referrer = "";
USVString blockedURI = "";
DOMString violatedDirective = "";
DOMString effectiveDirective = "";
DOMString originalPolicy = "";
USVString sourceFile = "";
DOMString sample = "";
SecurityPolicyViolationEventDisposition disposition = "enforce";
unsigned short statusCode = 0;
unsigned long lineNumber = 0;
unsigned long columnNumber = 0;
};

View file

@ -55,6 +55,7 @@ impl XMLDocument {
doc_loader,
None,
None,
None,
Default::default(),
),
}

View file

@ -1527,6 +1527,7 @@ impl XMLHttpRequest {
docloader,
None,
None,
None,
Default::default(),
)
}