script/dom/
trustedscript.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4use std::fmt;
5
6use dom_struct::dom_struct;
7use js::jsapi::CompilationType;
8use js::rust::HandleValue;
9
10use crate::dom::bindings::codegen::Bindings::TrustedScriptBinding::TrustedScriptMethods;
11use crate::dom::bindings::codegen::UnionTypes::TrustedScriptOrString;
12use crate::dom::bindings::error::Fallible;
13use crate::dom::bindings::reflector::{Reflector, reflect_dom_object};
14use crate::dom::bindings::root::DomRoot;
15use crate::dom::bindings::str::DOMString;
16use crate::dom::csp::CspReporting;
17use crate::dom::globalscope::GlobalScope;
18use crate::dom::trustedtypepolicy::TrustedType;
19use crate::dom::trustedtypepolicyfactory::{DEFAULT_SCRIPT_SINK_GROUP, TrustedTypePolicyFactory};
20use crate::script_runtime::{CanGc, JSContext};
21
22#[dom_struct]
23pub struct TrustedScript {
24    reflector_: Reflector,
25
26    data: DOMString,
27}
28
29impl TrustedScript {
30    fn new_inherited(data: DOMString) -> Self {
31        Self {
32            reflector_: Reflector::new(),
33            data,
34        }
35    }
36
37    #[cfg_attr(crown, allow(crown::unrooted_must_root))]
38    pub(crate) fn new(data: DOMString, global: &GlobalScope, can_gc: CanGc) -> DomRoot<Self> {
39        reflect_dom_object(Box::new(Self::new_inherited(data)), global, can_gc)
40    }
41
42    pub(crate) fn get_trusted_script_compliant_string(
43        global: &GlobalScope,
44        value: TrustedScriptOrString,
45        sink: &str,
46        can_gc: CanGc,
47    ) -> Fallible<DOMString> {
48        match value {
49            TrustedScriptOrString::String(value) => {
50                TrustedTypePolicyFactory::get_trusted_type_compliant_string(
51                    TrustedType::TrustedScript,
52                    global,
53                    value,
54                    sink,
55                    DEFAULT_SCRIPT_SINK_GROUP,
56                    can_gc,
57                )
58            },
59
60            TrustedScriptOrString::TrustedScript(trusted_script) => Ok(trusted_script.data.clone()),
61        }
62    }
63
64    pub(crate) fn data(&self) -> DOMString {
65        self.data.clone()
66    }
67
68    /// <https://www.w3.org/TR/CSP/#can-compile-strings>
69    #[allow(clippy::too_many_arguments)]
70    pub(crate) fn can_compile_string_with_trusted_type(
71        cx: JSContext,
72        global: &GlobalScope,
73        code_string: DOMString,
74        compilation_type: CompilationType,
75        parameter_strings: Vec<DOMString>,
76        body_string: DOMString,
77        parameter_args: Vec<TrustedScriptOrString>,
78        body_arg: HandleValue,
79        can_gc: CanGc,
80    ) -> bool {
81        // Step 2.1. Let compilationSink be "Function" if compilationType is "FUNCTION",
82        // and "eval" otherwise.
83        let compilation_sink = if compilation_type == CompilationType::Function {
84            "Function"
85        } else {
86            "eval"
87        };
88        // Step 2.2. Let isTrusted be true if bodyArg implements TrustedScript,
89        // and false otherwise.
90        let mut is_trusted = match TrustedTypePolicyFactory::is_trusted_script(cx, body_arg) {
91            // Step 2.3. If isTrusted is true then:
92            Ok(trusted_script) => {
93                // Step 2.3.1. If bodyString is not equal to bodyArg’s data, set isTrusted to false.
94                body_string == trusted_script.data
95            },
96            _ => false,
97        };
98        // Step 2.4. If isTrusted is true, then:
99        if is_trusted {
100            // Step 2.4.1. Assert: parameterArgs’ [list/size=] is equal to [parameterStrings]' size.
101            assert!(parameter_args.len() == parameter_strings.len());
102            // Step 2.4.2. For each index of the range 0 to |parameterArgs]' [list/size=]:
103            for index in 0..parameter_args.len() {
104                // Step 2.4.2.1. Let arg be parameterArgs[index].
105                match &parameter_args[index] {
106                    // Step 2.4.2.2. If arg implements TrustedScript, then:
107                    TrustedScriptOrString::TrustedScript(trusted_script) => {
108                        // Step 2.4.2.2.1. if parameterStrings[index] is not equal to arg’s data,
109                        // set isTrusted to false.
110                        if parameter_strings[index] != trusted_script.data() {
111                            is_trusted = false;
112                        }
113                    },
114                    // Step 2.4.2.3. Otherwise, set isTrusted to false.
115                    TrustedScriptOrString::String(_) => {
116                        is_trusted = false;
117                    },
118                }
119            }
120        }
121        // Step 2.5. Let sourceToValidate be a new TrustedScript object created in realm
122        // whose data is set to codeString if isTrusted is true, and codeString otherwise.
123        let source_string = if is_trusted {
124            // We don't need to call the compliant string algorithm, as it would immediately
125            // unroll the type as allowed by copying the data. This allows us to skip creating
126            // the DOM object.
127            code_string
128        } else {
129            // Step 2.6. Let sourceString be the result of executing the
130            // Get Trusted Type compliant string algorithm, with TrustedScript, realm,
131            // sourceToValidate, compilationSink, and 'script'.
132            match TrustedScript::get_trusted_script_compliant_string(
133                global,
134                TrustedScriptOrString::String(code_string.clone()),
135                compilation_sink,
136                can_gc,
137            ) {
138                // Step 2.7. If the algorithm throws an error, throw an EvalError.
139                Err(_) => {
140                    return false;
141                },
142                Ok(source_string) => {
143                    // Step 2.8. If sourceString is not equal to codeString, throw an EvalError.
144                    if source_string != code_string {
145                        return false;
146                    }
147                    source_string
148                },
149            }
150        };
151        global
152            .get_csp_list()
153            .is_js_evaluation_allowed(global, &source_string)
154    }
155}
156
157impl fmt::Display for TrustedScript {
158    #[inline]
159    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
160        f.write_str(&self.data)
161    }
162}
163
164impl TrustedScriptMethods<crate::DomTypeHolder> for TrustedScript {
165    /// <https://www.w3.org/TR/trusted-types/#trustedscript-stringification-behavior>
166    fn Stringifier(&self) -> DOMString {
167        self.data.clone()
168    }
169
170    /// <https://www.w3.org/TR/trusted-types/#dom-trustedscript-tojson>
171    fn ToJSON(&self) -> DOMString {
172        self.data.clone()
173    }
174}