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
« prev ^ index » next coverage.py v7.13.4, created at 2026-03-18 23:20 -0400
1"""Integration tests for the api-security-api.
3Verifies that GraphQLer's API-level security detectors correctly flag the
4intentionally misconfigured endpoints in tests/test-apis/api-security-api/.
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"""
13import os
14import shutil
15import unittest
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)
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
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
39 parsed = __main__.parse_config(cls.CONFIG_PATH)
40 __main__.set_config(parsed)
42 wait_for_server(cls.URL, timeout=30)
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)
52 # ── Compilation ──────────────────────────────────────────────────────────
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)
60 # ── Fuzzing ──────────────────────────────────────────────────────────────
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)
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)
76 # ── Security detection ────────────────────────────────────────────────────
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)
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 )
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 )
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 )