Skip to main content

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