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

1from typing import Type 

2 

3import requests 

4 

5from .ssrf_injection_materialilzer import SSRFInjectionMaterializer 

6from ..detector import Detector 

7 

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] 

23 

24 

25class SSRFInjectionDetector(Detector): 

26 @property 

27 def DETECTION_NAME(self) -> str: 

28 return "SSRF Injection" 

29 

30 @property 

31 def detect_only_once_for_api(self) -> bool: 

32 return False 

33 

34 @property 

35 def detect_only_once_for_node(self) -> bool: 

36 return True 

37 

38 @property 

39 def materializer(self) -> Type[SSRFInjectionMaterializer]: 

40 return SSRFInjectionMaterializer 

41 

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) 

45 

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