gossan_cloud/
cloudfront.rs1use async_trait::async_trait;
4use gossan_core::Target;
5use secfinding::{Evidence, Finding, Severity};
6
7use crate::provider::CloudProvider;
8
9pub struct CloudFrontProvider;
10
11#[async_trait]
12impl CloudProvider for CloudFrontProvider {
13 fn name(&self) -> &'static str {
14 "cloudfront"
15 }
16
17 fn endpoint(&self, name: &str) -> String {
18 format!("https://{}.cloudfront.net/", name)
19 }
20
21 async fn probe(
22 &self,
23 client: &reqwest::Client,
24 name: &str,
25 target: &Target,
26 ) -> anyhow::Result<Vec<Finding>> {
27 let dist: String = name
30 .chars()
31 .filter(|c| c.is_ascii_alphanumeric())
32 .collect::<String>()
33 .to_lowercase();
34
35 if dist.len() > 63 {
38 return Ok(vec![]);
39 }
40
41 let url = self.endpoint(name);
42 let mut findings = Vec::new();
43
44 let resp = match client.get(&url).send().await {
45 Ok(r) => r,
46 Err(_) => return Ok(vec![]),
47 };
48
49 let status = resp.status().as_u16();
50
51 match status {
54 200 | 401 | 403 => {
55 let body = gossan_core::net::bounded_text(resp, 4 * 1024 * 1024)
56 .await
57 .unwrap_or_default();
58
59 if body.contains("<Error><Code>NoSuchDistribution</Code>") {
63 } else {
65 gossan_core::try_push_finding(
66 crate::finding_builder(
67 target,
68 Severity::Low,
69 format!("CloudFront Distribution found: {}", name),
70 format!(
71 "https://{}.cloudfront.net/ is resolving and returned HTTP {}. \
72 This indicates an active CloudFront distribution.",
73 name, status
74 ),
75 )
76 .evidence(Evidence::HttpResponse {
77 status,
78 headers: vec![("url".into(), url.clone().into())],
79 body_excerpt: Some(body.chars().take(300).collect::<String>().into()),
80 })
81 .tag("cloudfront")
82 .tag("cloud")
83 .tag("cdn"),
84 &mut findings,
85 );
86 }
87 }
88 _ => {}
89 }
90
91 Ok(findings)
92 }
93}