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}