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)
55 .await
56 .unwrap_or_default();
57
58 gossan_core::try_push_finding(crate::finding_builder(target, Severity::Low,
59 format!("Lambda Function URL found: {}", name),
60 format!(
61 "https://{}.lambda-url.us-east-1.on.aws/ is resolving and returned HTTP {}. \
62 This indicates an active Lambda Function URL.",
63 name, status
64 ))
65 .evidence(Evidence::HttpResponse {
66 status,
67 headers: vec![("url".into(), url.clone().into())],
68 body_excerpt: Some(body.chars().take(300).collect::<String>().into()),
69 })
70 .tag("lambda").tag("cloud").tag("aws").tag("serverless"), &mut findings);
71 }
72 _ => {}
73 }
74
75 Ok(findings)
76 }
77}