brooks_lib/verifying/cors/
verify.rs1use regex::Regex;
21
22use crate::{
23 cdni::{
24 cors::spec::{
25 AccessControlAllowOrigin, AccessControlAllowOriginPattern, CrossoriginPolicy,
26 TypedCrossoriginPolicy,
27 },
28 gmd::spec::TypedGenericMetadata,
29 },
30 verifying::verify::{
31 AnalysisResult, CdniVerificationKey, MetadataAnalysisContext, MetadataVerificationError,
32 Verifier,
33 },
34};
35use std::{
36 error::Error,
37 fmt::{Debug, Display},
38};
39
40#[derive(Debug, Clone, Default)]
41pub struct CrossoriginPolicyVerificationKey {}
42
43#[derive(Debug)]
44pub enum AccessControlAllowOriginVerificationError {
45 BadPattern(Box<dyn Error>),
46}
47
48impl Display for AccessControlAllowOriginVerificationError {
49 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
50 match self {
51 AccessControlAllowOriginVerificationError::BadPattern(error) => {
52 write!(f, "Bad pattern in allow-origin: {error}")
53 }
54 }
55 }
56}
57
58impl Error for AccessControlAllowOriginVerificationError {}
59
60#[derive(Debug)]
61pub enum CrossoriginPolicyVerificationError {
62 AccessControlAllowOriginVerificationError(AccessControlAllowOriginVerificationError),
63}
64
65impl Display for CrossoriginPolicyVerificationError {
66 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
67 match self {
68 CrossoriginPolicyVerificationError::AccessControlAllowOriginVerificationError(
69 access_control_allow_origin_verification_error,
70 ) => write!(f, "{access_control_allow_origin_verification_error}"),
71 }
72 }
73}
74
75impl Error for CrossoriginPolicyVerificationError {}
76
77fn verify_allow_origin(
78 allow_origin: &AccessControlAllowOrigin<()>,
79) -> Result<AccessControlAllowOrigin<CrossoriginPolicyVerificationKey>, MetadataVerificationError> {
80 let mut analyzed_allow_origin: Vec<AccessControlAllowOriginPattern> = vec![];
81 for pattern in &allow_origin.allow_origin {
82 let r = Regex::new(&pattern.pattern).map_err(|e| {
83 MetadataVerificationError::CrossoriginPolicyVerificationError(
84 CrossoriginPolicyVerificationError::AccessControlAllowOriginVerificationError(
85 AccessControlAllowOriginVerificationError::BadPattern(e.into()),
86 )
87 .into(),
88 )
89 })?;
90 analyzed_allow_origin.push(AccessControlAllowOriginPattern {
91 pattern: pattern.pattern.clone(),
92 aug: Some(r),
93 });
94 }
95
96 Ok(AccessControlAllowOrigin {
97 allow_origin: analyzed_allow_origin,
98 wildcard_return: allow_origin.wildcard_return,
99 aug: CrossoriginPolicyVerificationKey::default(),
100 })
101}
102
103#[derive(Debug)]
104pub struct CrossoriginPolicyMetadataAnalyzer {}
105
106fn analyze_crossoriginpolicy(
107 typed_cop: &TypedCrossoriginPolicy<()>,
108 input: MetadataAnalysisContext,
109) -> AnalysisResult<MetadataAnalysisContext, MetadataVerificationError> {
110 let verified_allow_origin = verify_allow_origin(&typed_cop.value.allow_origin)?;
111
112 let res = TypedCrossoriginPolicy::<CrossoriginPolicyVerificationKey> {
113 tpe: TypedCrossoriginPolicy::<()>::typed_cdni_metadata_name(),
114 value: CrossoriginPolicy::<CrossoriginPolicyVerificationKey> {
115 allow_origin: verified_allow_origin,
116 preflight_only: typed_cop.value.preflight_only,
117 aug: CrossoriginPolicyVerificationKey::default(),
118 },
119 };
120
121 Ok((input, CdniVerificationKey::CrossoriginPolicy(Box::new(res))))
122}
123
124impl Verifier<MetadataAnalysisContext, MetadataVerificationError>
125 for CrossoriginPolicyMetadataAnalyzer
126{
127 fn analyze(
128 &self,
129 v: &TypedGenericMetadata<()>,
130 input: MetadataAnalysisContext,
131 ) -> AnalysisResult<MetadataAnalysisContext, MetadataVerificationError> {
132 let typed_cop = serde_json::from_value::<TypedCrossoriginPolicy<()>>(
133 serde_json::to_value(v)
134 .map_err(|e| MetadataVerificationError::JsonError(e.to_string()))?,
135 )
136 .map_err(|e| MetadataVerificationError::JsonError(e.to_string()))?;
137
138 analyze_crossoriginpolicy(&typed_cop, input)
140 }
141
142 fn type_name(&self, candidate: &str) -> bool {
143 candidate == TypedCrossoriginPolicy::<()>::typed_cdni_metadata_name()
144 }
145}
146
147#[cfg(test)]
148mod test {
149 use std::{assert_matches, path::Path};
150
151 use crate::{
152 cdni::cors::spec::{CrossoriginPolicy, TypedCrossoriginPolicy},
153 environment::scope::Scopes,
154 mel::types::Type,
155 tests::read_test_file,
156 verifying::{
157 cors::verify::analyze_crossoriginpolicy,
158 verify::{CdniVerificationKey, MetadataAnalysisContext},
159 },
160 };
161
162 #[test]
163 fn test_verify_draft_figure1() {
164 let json = read_test_file(Path::new("./src/cdni/tests/cors/figure1.json"));
165 let cop_metadata = serde_json::from_str::<TypedCrossoriginPolicy<()>>(&json)
166 .expect("Could not parse JSON test file");
167 assert_eq!(
168 cop_metadata.tpe,
169 TypedCrossoriginPolicy::<()>::typed_cdni_metadata_name()
170 );
171
172 let (_, result) = analyze_crossoriginpolicy(
173 &cop_metadata,
174 MetadataAnalysisContext {
175 scopes: Scopes::<Type>::default(),
176 },
177 )
178 .expect("Could not verify valid MI.CrossoriginPolicy metadata");
179
180 let result = match result {
181 CdniVerificationKey::CrossoriginPolicy(r) => r,
182 _ => todo!(),
183 };
184
185 assert_matches!(
186 *result,
187 TypedCrossoriginPolicy {
188 tpe: _,
189 value: CrossoriginPolicy {
190 allow_origin: ao,
191 preflight_only: None,
192 aug: _,
193 }
194 } if ao.allow_origin.len() == 1
195 );
196 }
197
198 #[test]
199 fn test_verify_bad_pattern() {
200 let json = read_test_file(Path::new("./src/cdni/tests/cors/bad_pattern.json"));
201 let cop_metadata = serde_json::from_str::<TypedCrossoriginPolicy<()>>(&json)
202 .expect("Could not parse JSON test file");
203 assert_eq!(
204 cop_metadata.tpe,
205 TypedCrossoriginPolicy::<()>::typed_cdni_metadata_name()
206 );
207
208 let result = analyze_crossoriginpolicy(
209 &cop_metadata,
210 MetadataAnalysisContext {
211 scopes: Scopes::<Type>::default(),
212 },
213 );
214
215 assert_matches!(result, Err(_));
216 }
217}