Skip to main content

actix_web_csp/core/
source.rs

1use crate::constants::{
2    NONCE_PREFIX, NONE_SOURCE, REPORT_SAMPLE_SOURCE, SELF_SOURCE, STRICT_DYNAMIC_SOURCE,
3    SUFFIX_QUOTE, UNSAFE_EVAL_SOURCE, UNSAFE_HASHES_SOURCE, UNSAFE_INLINE_SOURCE,
4    WASM_UNSAFE_EVAL_SOURCE,
5};
6use crate::security::hash::HashAlgorithm;
7use crate::utils::BufferWriter;
8use bytes::BytesMut;
9use std::{
10    borrow::Cow,
11    fmt,
12    hash::{Hash, Hasher},
13};
14
15#[derive(Debug, Clone, PartialEq, Eq)]
16pub enum Source {
17    None,
18    Self_,
19    UnsafeInline,
20    UnsafeEval,
21    StrictDynamic,
22    ReportSample,
23    WasmUnsafeEval,
24    UnsafeHashes,
25    Host(Cow<'static, str>),
26    Scheme(Cow<'static, str>),
27    Nonce(Cow<'static, str>),
28    Hash {
29        algorithm: HashAlgorithm,
30        value: Cow<'static, str>,
31    },
32}
33
34impl Source {
35    #[inline(always)]
36    pub const fn is_none(&self) -> bool {
37        matches!(self, Source::None)
38    }
39
40    #[inline(always)]
41    pub const fn is_self(&self) -> bool {
42        matches!(self, Source::Self_)
43    }
44
45    #[inline(always)]
46    pub const fn is_unsafe_inline(&self) -> bool {
47        matches!(self, Source::UnsafeInline)
48    }
49
50    #[inline(always)]
51    pub const fn is_unsafe_eval(&self) -> bool {
52        matches!(self, Source::UnsafeEval)
53    }
54
55    #[inline]
56    pub const fn as_static_str(&self) -> Option<&'static str> {
57        match self {
58            Source::None => Some(NONE_SOURCE),
59            Source::Self_ => Some(SELF_SOURCE),
60            Source::UnsafeInline => Some(UNSAFE_INLINE_SOURCE),
61            Source::UnsafeEval => Some(UNSAFE_EVAL_SOURCE),
62            Source::StrictDynamic => Some(STRICT_DYNAMIC_SOURCE),
63            Source::ReportSample => Some(REPORT_SAMPLE_SOURCE),
64            Source::WasmUnsafeEval => Some(WASM_UNSAFE_EVAL_SOURCE),
65            Source::UnsafeHashes => Some(UNSAFE_HASHES_SOURCE),
66            _ => None,
67        }
68    }
69
70    #[inline]
71    pub fn estimated_size(&self) -> usize {
72        match self {
73            Source::None => NONE_SOURCE.len(),
74            Source::Self_ => SELF_SOURCE.len(),
75            Source::UnsafeInline => UNSAFE_INLINE_SOURCE.len(),
76            Source::UnsafeEval => UNSAFE_EVAL_SOURCE.len(),
77            Source::StrictDynamic => STRICT_DYNAMIC_SOURCE.len(),
78            Source::ReportSample => REPORT_SAMPLE_SOURCE.len(),
79            Source::WasmUnsafeEval => WASM_UNSAFE_EVAL_SOURCE.len(),
80            Source::UnsafeHashes => UNSAFE_HASHES_SOURCE.len(),
81            Source::Host(host) => host.len(),
82            Source::Scheme(scheme) => scheme.len() + 1,
83            Source::Nonce(nonce) => NONCE_PREFIX.len() + nonce.len() + SUFFIX_QUOTE.len(),
84            Source::Hash { algorithm, value } => {
85                algorithm.prefix().len() + value.len() + SUFFIX_QUOTE.len()
86            }
87        }
88    }
89
90    #[inline]
91    pub fn contains_nonce(&self) -> bool {
92        matches!(self, Source::Nonce(_))
93    }
94
95    #[inline]
96    pub fn contains_hash(&self) -> bool {
97        matches!(self, Source::Hash { .. })
98    }
99
100    #[inline]
101    pub fn scheme(&self) -> Option<&str> {
102        match self {
103            Source::Scheme(scheme) => Some(scheme),
104            _ => None,
105        }
106    }
107
108    #[inline]
109    pub fn host(&self) -> Option<&str> {
110        match self {
111            Source::Host(host) => Some(host),
112            _ => None,
113        }
114    }
115
116    #[inline]
117    pub fn nonce(&self) -> Option<&str> {
118        match self {
119            Source::Nonce(nonce) => Some(nonce),
120            _ => None,
121        }
122    }
123
124    #[inline]
125    pub fn hash_value(&self) -> Option<(&str, HashAlgorithm)> {
126        match self {
127            Source::Hash { algorithm, value } => Some((value, *algorithm)),
128            _ => None,
129        }
130    }
131}
132
133impl Hash for Source {
134    fn hash<H: Hasher>(&self, state: &mut H) {
135        core::mem::discriminant(self).hash(state);
136        match self {
137            Source::None
138            | Source::Self_
139            | Source::UnsafeInline
140            | Source::UnsafeEval
141            | Source::StrictDynamic
142            | Source::ReportSample
143            | Source::WasmUnsafeEval
144            | Source::UnsafeHashes => {}
145            Source::Host(host) => host.hash(state),
146            Source::Scheme(scheme) => scheme.hash(state),
147            Source::Nonce(nonce) => nonce.hash(state),
148            Source::Hash { algorithm, value } => {
149                algorithm.hash(state);
150                value.hash(state);
151            }
152        }
153    }
154}
155
156impl fmt::Display for Source {
157    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
158        match self {
159            Source::None => f.write_str(NONE_SOURCE),
160            Source::Self_ => f.write_str(SELF_SOURCE),
161            Source::UnsafeInline => f.write_str(UNSAFE_INLINE_SOURCE),
162            Source::UnsafeEval => f.write_str(UNSAFE_EVAL_SOURCE),
163            Source::StrictDynamic => f.write_str(STRICT_DYNAMIC_SOURCE),
164            Source::ReportSample => f.write_str(REPORT_SAMPLE_SOURCE),
165            Source::WasmUnsafeEval => f.write_str(WASM_UNSAFE_EVAL_SOURCE),
166            Source::UnsafeHashes => f.write_str(UNSAFE_HASHES_SOURCE),
167            Source::Host(host) => f.write_str(host),
168            Source::Scheme(scheme) => write!(f, "{}:", scheme),
169            Source::Nonce(nonce) => write!(f, "{}{}{}", NONCE_PREFIX, nonce, SUFFIX_QUOTE),
170            Source::Hash { algorithm, value } => {
171                write!(f, "{}{}{}", algorithm.prefix(), value, SUFFIX_QUOTE)
172            }
173        }
174    }
175}
176
177impl BufferWriter for Source {
178    fn write_to_buffer(&self, buffer: &mut BytesMut) {
179        match self {
180            Source::None => buffer.extend_from_slice(NONE_SOURCE.as_bytes()),
181            Source::Self_ => buffer.extend_from_slice(SELF_SOURCE.as_bytes()),
182            Source::UnsafeInline => buffer.extend_from_slice(UNSAFE_INLINE_SOURCE.as_bytes()),
183            Source::UnsafeEval => buffer.extend_from_slice(UNSAFE_EVAL_SOURCE.as_bytes()),
184            Source::StrictDynamic => buffer.extend_from_slice(STRICT_DYNAMIC_SOURCE.as_bytes()),
185            Source::ReportSample => buffer.extend_from_slice(REPORT_SAMPLE_SOURCE.as_bytes()),
186            Source::WasmUnsafeEval => buffer.extend_from_slice(WASM_UNSAFE_EVAL_SOURCE.as_bytes()),
187            Source::UnsafeHashes => buffer.extend_from_slice(UNSAFE_HASHES_SOURCE.as_bytes()),
188            Source::Host(host) => {
189                if let Some(interned) = crate::utils::intern_string(host) {
190                    buffer.extend_from_slice(interned.as_bytes());
191                } else {
192                    buffer.extend_from_slice(host.as_bytes());
193                }
194            }
195            Source::Scheme(scheme) => {
196                buffer.extend_from_slice(scheme.as_bytes());
197                buffer.extend_from_slice(b":");
198            }
199            Source::Nonce(nonce) => {
200                buffer.reserve(NONCE_PREFIX.len() + nonce.len() + SUFFIX_QUOTE.len());
201                buffer.extend_from_slice(NONCE_PREFIX.as_bytes());
202                buffer.extend_from_slice(nonce.as_bytes());
203                buffer.extend_from_slice(SUFFIX_QUOTE.as_bytes());
204            }
205            Source::Hash { algorithm, value } => {
206                let prefix = algorithm.prefix();
207                buffer.reserve(prefix.len() + value.len() + SUFFIX_QUOTE.len());
208                buffer.extend_from_slice(prefix.as_bytes());
209                buffer.extend_from_slice(value.as_bytes());
210                buffer.extend_from_slice(SUFFIX_QUOTE.as_bytes());
211            }
212        }
213    }
214}