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