1use async_trait::async_trait;
4use gossan_core::Target;
5use secfinding::{Evidence, Finding, Severity};
6
7use crate::provider::CloudProvider;
8
9pub struct LambdaProvider;
10
11#[async_trait]
12impl CloudProvider for LambdaProvider {
13 fn name(&self) -> &'static str {
14 "lambda"
15 }
16
17 fn endpoint(&self, name: &str) -> String {
18 format!("https://{}.lambda-url.us-east-1.on.aws/", name)
20 }
21
22 async fn probe(
23 &self,
24 client: &reqwest::Client,
25 name: &str,
26 target: &Target,
27 ) -> anyhow::Result<Vec<Finding>> {
28 let lambda_id: String = name
30 .chars()
31 .filter(|c| c.is_ascii_alphanumeric())
32 .collect::<String>()
33 .to_lowercase();
34
35 if lambda_id.len() != 32 && name.len() != 32 {
36 return Ok(vec![]);
37 }
38
39 let url = self.endpoint(name);
40 let mut findings = Vec::new();
41
42 let resp = match client.get(&url).send().await {
43 Ok(r) => r,
44 Err(_) => return Ok(vec![]),
45 };
46
47 let status = resp.status().as_u16();
48
49 match status {
53 200 | 401 | 403 | 404 | 500 | 502 => {
54 let body = gossan_core::net::bounded_text(resp, 4 * 1024 * 1024).await.unwrap_or_default();
55
56 gossan_core::try_push_finding(crate::finding_builder(target, Severity::Low,
57 format!("Lambda Function URL found: {}", name),
58 format!(
59 "https://{}.lambda-url.us-east-1.on.aws/ is resolving and returned HTTP {}. \
60 This indicates an active Lambda Function URL.",
61 name, status
62 ))
63 .evidence(Evidence::HttpResponse {
64 status,
65 headers: vec![("url".into(), url.clone().into())],
66 body_excerpt: Some(body.chars().take(300).collect::<String>().into()),
67 })
68 .tag("lambda").tag("cloud").tag("aws").tag("serverless"), &mut findings);
69 }
70 _ => {}
71 }
72
73 Ok(findings)
74 }
75}