Coverage for tests / integration / test_api_security_api.py: 0%

58 statements  

« prev     ^ index     » next       coverage.py v7.13.4, created at 2026-03-18 23:20 -0400

1"""Integration tests for the api-security-api. 

2 

3Verifies that GraphQLer's API-level security detectors correctly flag the 

4intentionally misconfigured endpoints in tests/test-apis/api-security-api/. 

5 

6Detectors exercised: 

7 - Introspection Enabled (Apollo Server started with introspection: true) 

8 - Field Suggestions Enabled (Apollo Server returns "Did you mean…" hints) 

9 - Query Deny Bypass (middleware blocks `adminUsers` by name but not 

10 by alias: `s: adminUsers`) 

11""" 

12 

13import os 

14import shutil 

15import unittest 

16 

17from graphqler import __main__, config 

18from tests.integration.utils.run_api import run_node_project, wait_for_server 

19from tests.integration.utils.stats import ( 

20 get_vulnerabilities_from_stats, 

21 is_detection_flagged, 

22) 

23 

24 

25class TestAPISecurityAPI(unittest.TestCase): 

26 PORT = 4004 

27 URL = f"http://localhost:{PORT}/graphql" 

28 PATH = "ci-test-api-security-api/" 

29 API_PATH = "tests/test-apis/api-security-api" 

30 CONFIG_PATH = "tests/test-apis/test_configs/api_security_api_config.toml" 

31 process = None 

32 process_pid = None 

33 

34 @classmethod 

35 def setUpClass(cls): 

36 cls.process = run_node_project(cls.API_PATH, [], str(cls.PORT)) 

37 cls.process_pid = cls.process.pid 

38 

39 parsed = __main__.parse_config(cls.CONFIG_PATH) 

40 __main__.set_config(parsed) 

41 

42 wait_for_server(cls.URL, timeout=30) 

43 

44 @classmethod 

45 def tearDownClass(cls): 

46 if cls.process and cls.process.pid == cls.process_pid: 

47 cls.process.kill() 

48 cls.process.wait() 

49 if os.path.exists(cls.PATH): 

50 shutil.rmtree(cls.PATH) 

51 

52 # ── Compilation ────────────────────────────────────────────────────────── 

53 

54 def test_compile_generates_introspection_file(self): 

55 __main__.run_compile_mode(self.PATH, self.URL) 

56 introspection_path = os.path.join(self.PATH, config.INTROSPECTION_RESULT_FILE_NAME) 

57 self.assertTrue(os.path.exists(introspection_path)) 

58 self.assertGreater(os.path.getsize(introspection_path), 0) 

59 

60 # ── Fuzzing ────────────────────────────────────────────────────────────── 

61 

62 def test_fuzz_generates_stats_file(self): 

63 __main__.run_compile_mode(self.PATH, self.URL) 

64 __main__.run_fuzz_mode(self.PATH, self.URL) 

65 stats_path = os.path.join(self.PATH, config.STATS_FILE_NAME) 

66 self.assertTrue(os.path.exists(stats_path)) 

67 self.assertGreater(os.path.getsize(stats_path), 0) 

68 

69 def test_fuzz_generates_json_stats_file(self): 

70 __main__.run_compile_mode(self.PATH, self.URL) 

71 __main__.run_fuzz_mode(self.PATH, self.URL) 

72 json_path = os.path.join(self.PATH, "stats.json") 

73 self.assertTrue(os.path.exists(json_path)) 

74 self.assertGreater(os.path.getsize(json_path), 0) 

75 

76 # ── Security detection ──────────────────────────────────────────────────── 

77 

78 def _run_and_get_vulns(self): 

79 __main__.run_compile_mode(self.PATH, self.URL) 

80 __main__.run_fuzz_mode(self.PATH, self.URL) 

81 return get_vulnerabilities_from_stats(self.PATH) 

82 

83 def test_introspection_enabled_detected(self): 

84 vulns = self._run_and_get_vulns() 

85 self.assertTrue( 

86 is_detection_flagged(vulns, "Introspection Enabled"), 

87 f"Expected introspection-enabled to be flagged. Got: {vulns}", 

88 ) 

89 

90 def test_field_suggestions_enabled_detected(self): 

91 vulns = self._run_and_get_vulns() 

92 self.assertTrue( 

93 is_detection_flagged(vulns, "Field Suggestions Enabled"), 

94 f"Expected field-suggestions-enabled to be flagged. Got: {vulns}", 

95 ) 

96 

97 def test_query_deny_bypass_detected(self): 

98 vulns = self._run_and_get_vulns() 

99 self.assertTrue( 

100 is_detection_flagged(vulns, "Query deny bypass"), 

101 f"Expected query-deny-bypass to be flagged. Got: {vulns}", 

102 )