termivar_scanner/payload_strategies/
mod.rs1use std::sync::Arc;
23
24use crate::payload_strategy::{PayloadStrategyError, PayloadStrategyRegistry};
25
26pub mod api_authorization_context_pair;
27pub mod cors_origin_pair;
28pub mod encoding;
29pub mod external_url_query_pair;
30pub mod http_header_control_pair;
31#[cfg(feature = "normalization-resilience")]
32pub(crate) mod normalization_resilience_query_pair;
33pub mod reflection_marker_query_pair;
34pub mod sql_quote_balance_query_pair;
35pub mod ssti_arithmetic_expression_pair;
36#[cfg(feature = "template-evaluation-review")]
37pub mod template_jinja_uppercase_expression_pair;
38pub mod xss_attribute_boundary_query_pair;
39pub mod xss_javascript_lexical_boundary_query_pair;
40pub mod xss_structural_query_pair;
41
42pub use api_authorization_context_pair::{
43 ApiAuthorizationContextPairStrategy, API_AUTHORIZATION_CONTEXT_PAIR_HEADER_NAME,
44 API_AUTHORIZATION_CONTEXT_PAIR_ID, API_AUTHORIZATION_CONTEXT_PAIR_REVISION,
45};
46pub use cors_origin_pair::{
47 CorsOriginPairStrategy, CORS_ORIGIN_PAIR_HEADER_NAME, CORS_ORIGIN_PAIR_ID,
48 CORS_ORIGIN_PAIR_REVISION,
49};
50pub use encoding::{encode_into_artifact, hex_encode, percent_encode, PayloadEncoding};
51pub use external_url_query_pair::{
52 ExternalUrlQueryPairStrategy, EXTERNAL_URL_QUERY_PAIR_ID, EXTERNAL_URL_QUERY_PAIR_REVISION,
53};
54pub use http_header_control_pair::{
55 HttpHeaderControlPairStrategy, HTTP_HEADER_CONTROL_PAIR_HEADER_NAME,
56 HTTP_HEADER_CONTROL_PAIR_ID, HTTP_HEADER_CONTROL_PAIR_REVISION,
57};
58#[cfg(feature = "normalization-resilience")]
59pub(crate) use normalization_resilience_query_pair::NormalizationResilienceQueryPairStrategy;
60pub use reflection_marker_query_pair::{
61 ReflectionMarkerQueryPairStrategy, REFLECTION_MARKER_QUERY_PAIR_ID,
62 REFLECTION_MARKER_QUERY_PAIR_REVISION,
63};
64pub use sql_quote_balance_query_pair::{
65 SqlQuoteBalanceQueryPairStrategy, SQL_QUOTE_BALANCE_QUERY_PAIR_ID,
66 SQL_QUOTE_BALANCE_QUERY_PAIR_REVISION,
67};
68pub use ssti_arithmetic_expression_pair::{
69 SstiArithmeticExpressionPairStrategy, SSTI_ARITHMETIC_EXPRESSION_PAIR_ID,
70 SSTI_ARITHMETIC_EXPRESSION_PAIR_REVISION,
71};
72#[cfg(feature = "template-evaluation-review")]
73pub use template_jinja_uppercase_expression_pair::{
74 TemplateJinjaUppercaseExpressionPairStrategy, TEMPLATE_JINJA_UPPERCASE_EXPRESSION_PAIR_ID,
75 TEMPLATE_JINJA_UPPERCASE_EXPRESSION_PAIR_REVISION,
76};
77pub use xss_attribute_boundary_query_pair::{
78 XssAttributeBoundaryQueryPairStrategy, XSS_ATTRIBUTE_BOUNDARY_QUERY_PAIR_ID,
79 XSS_ATTRIBUTE_BOUNDARY_QUERY_PAIR_REVISION,
80};
81pub(crate) use xss_javascript_lexical_boundary_query_pair::XssJavascriptLexicalProbeTokens;
82pub use xss_javascript_lexical_boundary_query_pair::{
83 XssJavascriptLexicalBoundaryQueryPairStrategy, XSS_JAVASCRIPT_LEXICAL_BOUNDARY_QUERY_PAIR_ID,
84 XSS_JAVASCRIPT_LEXICAL_BOUNDARY_QUERY_PAIR_REVISION,
85};
86pub use xss_structural_query_pair::{
87 XssStructuralQueryPairStrategy, XSS_STRUCTURAL_QUERY_PAIR_ID,
88 XSS_STRUCTURAL_QUERY_PAIR_REVISION,
89};
90
91pub fn standard_payload_strategies() -> Result<PayloadStrategyRegistry, PayloadStrategyError> {
97 let mut registry = PayloadStrategyRegistry::new();
98 registry.register(Arc::new(HttpHeaderControlPairStrategy::new()))?;
99 #[cfg(feature = "normalization-resilience")]
100 registry.register(Arc::new(NormalizationResilienceQueryPairStrategy::new()))?;
101 registry.register(Arc::new(ApiAuthorizationContextPairStrategy::new()))?;
102 registry.register(Arc::new(CorsOriginPairStrategy::new()))?;
103 registry.register(Arc::new(ExternalUrlQueryPairStrategy::new()))?;
104 registry.register(Arc::new(ReflectionMarkerQueryPairStrategy::new()))?;
105 registry.register(Arc::new(SqlQuoteBalanceQueryPairStrategy::new()))?;
106 registry.register(Arc::new(SstiArithmeticExpressionPairStrategy::new()))?;
107 #[cfg(feature = "template-evaluation-review")]
108 registry.register(Arc::new(TemplateJinjaUppercaseExpressionPairStrategy::new()))?;
109 registry.register(Arc::new(XssAttributeBoundaryQueryPairStrategy::new()))?;
110 registry.register(Arc::new(
111 XssJavascriptLexicalBoundaryQueryPairStrategy::new(),
112 ))?;
113 registry.register(Arc::new(XssStructuralQueryPairStrategy::new()))?;
114 Ok(registry)
115}
116
117#[cfg(test)]
118mod tests {
119 use super::*;
120 use crate::payload_strategy::{
121 PayloadSeed, PayloadStrategyLimits, PayloadStrategyRef, PayloadVariantRole,
122 };
123
124 #[test]
125 fn standard_registry_registers_every_built_in_strategy() {
126 let registry = standard_payload_strategies().unwrap();
127 let header_pair = PayloadStrategyRef::new(
128 HTTP_HEADER_CONTROL_PAIR_ID,
129 HTTP_HEADER_CONTROL_PAIR_REVISION,
130 )
131 .unwrap();
132 let authorization_pair = PayloadStrategyRef::new(
133 API_AUTHORIZATION_CONTEXT_PAIR_ID,
134 API_AUTHORIZATION_CONTEXT_PAIR_REVISION,
135 )
136 .unwrap();
137 let cors_origin_pair =
138 PayloadStrategyRef::new(CORS_ORIGIN_PAIR_ID, CORS_ORIGIN_PAIR_REVISION).unwrap();
139 let external_url_query_pair =
140 PayloadStrategyRef::new(EXTERNAL_URL_QUERY_PAIR_ID, EXTERNAL_URL_QUERY_PAIR_REVISION)
141 .unwrap();
142 let reflection_marker_pair = PayloadStrategyRef::new(
143 REFLECTION_MARKER_QUERY_PAIR_ID,
144 REFLECTION_MARKER_QUERY_PAIR_REVISION,
145 )
146 .unwrap();
147
148 let sql_quote_pair = PayloadStrategyRef::new(
149 SQL_QUOTE_BALANCE_QUERY_PAIR_ID,
150 SQL_QUOTE_BALANCE_QUERY_PAIR_REVISION,
151 )
152 .unwrap();
153
154 let ssti_arithmetic_pair = PayloadStrategyRef::new(
155 SSTI_ARITHMETIC_EXPRESSION_PAIR_ID,
156 SSTI_ARITHMETIC_EXPRESSION_PAIR_REVISION,
157 )
158 .unwrap();
159
160 let xss_structural_pair = PayloadStrategyRef::new(
161 XSS_STRUCTURAL_QUERY_PAIR_ID,
162 XSS_STRUCTURAL_QUERY_PAIR_REVISION,
163 )
164 .unwrap();
165
166 let xss_attribute_boundary_pair = PayloadStrategyRef::new(
167 XSS_ATTRIBUTE_BOUNDARY_QUERY_PAIR_ID,
168 XSS_ATTRIBUTE_BOUNDARY_QUERY_PAIR_REVISION,
169 )
170 .unwrap();
171
172 let xss_javascript_lexical_boundary_pair = PayloadStrategyRef::new(
173 XSS_JAVASCRIPT_LEXICAL_BOUNDARY_QUERY_PAIR_ID,
174 XSS_JAVASCRIPT_LEXICAL_BOUNDARY_QUERY_PAIR_REVISION,
175 )
176 .unwrap();
177
178 let expected_len = 10
179 + cfg!(feature = "normalization-resilience") as usize
180 + cfg!(feature = "template-evaluation-review") as usize;
181 assert_eq!(registry.len(), expected_len);
182 #[cfg(feature = "normalization-resilience")]
183 {
184 use normalization_resilience_query_pair::{
185 NORMALIZATION_RESILIENCE_QUERY_PAIR_ID,
186 NORMALIZATION_RESILIENCE_QUERY_PAIR_REVISION,
187 };
188
189 let normalization = PayloadStrategyRef::new(
190 NORMALIZATION_RESILIENCE_QUERY_PAIR_ID,
191 NORMALIZATION_RESILIENCE_QUERY_PAIR_REVISION,
192 )
193 .unwrap();
194 assert!(registry.contains(&normalization));
195 }
196 #[cfg(feature = "template-evaluation-review")]
197 {
198 let template = PayloadStrategyRef::new(
199 TEMPLATE_JINJA_UPPERCASE_EXPRESSION_PAIR_ID,
200 TEMPLATE_JINJA_UPPERCASE_EXPRESSION_PAIR_REVISION,
201 )
202 .unwrap();
203 assert!(registry.contains(&template));
204 }
205 assert!(registry.contains(&header_pair));
206 assert!(registry.contains(&authorization_pair));
207 assert!(registry.contains(&cors_origin_pair));
208 assert!(registry.contains(&external_url_query_pair));
209 assert!(registry.contains(&reflection_marker_pair));
210 assert!(registry.contains(&sql_quote_pair));
211 assert!(registry.contains(&ssti_arithmetic_pair));
212 assert!(registry.contains(&xss_attribute_boundary_pair));
213 assert!(registry.contains(&xss_javascript_lexical_boundary_pair));
214 assert!(registry.contains(&xss_structural_pair));
215 }
216
217 #[test]
218 fn standard_registry_can_derive_a_pair_for_its_strategy() {
219 let registry = standard_payload_strategies().unwrap();
220 let reference = PayloadStrategyRef::new(
221 HTTP_HEADER_CONTROL_PAIR_ID,
222 HTTP_HEADER_CONTROL_PAIR_REVISION,
223 )
224 .unwrap();
225 let limits = PayloadStrategyLimits::default();
226 let seed = PayloadSeed::new(b"application/json".to_vec(), limits).unwrap();
227
228 let control = registry
229 .derive_one(&reference, PayloadVariantRole::Control, &seed, limits)
230 .unwrap();
231 let candidate = registry
232 .derive_one(&reference, PayloadVariantRole::Candidate, &seed, limits)
233 .unwrap();
234
235 assert_eq!(control.as_bytes(), b"*/*");
236 assert_eq!(candidate.as_bytes(), b"*/*, application/json");
237 assert_ne!(control.receipt().sha256(), candidate.receipt().sha256());
238 }
239}