Coverage for graphqler / fuzzer / engine / detectors / ssrf_injection / ssrf_injection_detector.py: 57%
28 statements
« prev ^ index » next coverage.py v7.13.4, created at 2026-03-18 23:20 -0400
« prev ^ index » next coverage.py v7.13.4, created at 2026-03-18 23:20 -0400
1from typing import Type
3import requests
5from .ssrf_injection_materialilzer import SSRFInjectionMaterializer
6from ..detector import Detector
8# Response patterns that may indicate internal service data leakage via SSRF
9SSRF_RESPONSE_PATTERNS = [
10 "connection refused",
11 "no route to host",
12 "failed to connect",
13 "could not connect",
14 "open(): permission denied",
15 "operation not permitted",
16 "network is unreachable",
17 # AWS/GCP/Azure metadata service indicators
18 "ami-id",
19 "instance-id",
20 "169.254.169.254",
21 "metadata.google.internal",
22]
25class SSRFInjectionDetector(Detector):
26 @property
27 def DETECTION_NAME(self) -> str:
28 return "SSRF Injection"
30 @property
31 def detect_only_once_for_api(self) -> bool:
32 return False
34 @property
35 def detect_only_once_for_node(self) -> bool:
36 return True
38 @property
39 def materializer(self) -> Type[SSRFInjectionMaterializer]:
40 return SSRFInjectionMaterializer
42 def _is_vulnerable(self, graphql_response: dict, request_response: requests.Response) -> bool:
43 response_text_lower = request_response.text.lower()
44 return any(pattern in response_text_lower for pattern in SSRF_RESPONSE_PATTERNS)
46 def _is_potentially_vulnerable(self, graphql_response: dict, request_response: requests.Response) -> bool:
47 if graphql_response is None:
48 return False
49 # A 200 with data on SSRF-like inputs suggests the server may be making outbound requests
50 ssrf_input_keywords = ['"http://', '"https://', '"localhost', '"127.0.0.1', '"0.0.0.0']
51 if request_response.status_code == 200 and graphql_response.get('data') and any(kw in self.payload for kw in ssrf_input_keywords):
52 return True
53 return False