@@ -35,6 +35,12 @@ def _policy_payload() -> dict:
3535 "secret_scanning_push_protection" : {"required" : True , "mode" : "api" },
3636 "secret_scanning_non_provider_patterns" : {"required" : True , "mode" : "api" },
3737 "secret_scanning_validity_checks" : {"required" : True , "mode" : "api" },
38+ "org_code_security_configuration" : {
39+ "required" : True ,
40+ "mode" : "api" ,
41+ "configuration_id" : 240284 ,
42+ "required_repository_status" : "enforced" ,
43+ },
3844 "dependabot_config" : {"required" : True , "mode" : "repo_file" , "path" : ".github/dependabot.yml" },
3945 "codeql_workflow" : {"required" : True , "mode" : "repo_file" , "path" : ".github/workflows/codeql.yml" },
4046 "codeql_config" : {"required" : True , "mode" : "repo_file" , "path" : ".github/codeql/codeql-config.yml" },
@@ -48,6 +54,7 @@ def _impl(path: str) -> tuple[int, dict]:
4854 return (
4955 0 ,
5056 {
57+ "id" : 123 ,
5158 "default_branch" : "main" ,
5259 "security_and_analysis" : {
5360 "secret_scanning" : {"status" : "enabled" },
@@ -71,6 +78,61 @@ def _impl(path: str) -> tuple[int, dict]:
7178 return 0 , {}
7279 if path == "repos/example/repo/dependabot/alerts?per_page=1" :
7380 return 0 , []
81+ if path == "orgs/example/code-security/configurations/240284" :
82+ return 0 , {
83+ "id" : 240284 ,
84+ "secret_scanning" : "enabled" ,
85+ "secret_scanning_push_protection" : "enabled" ,
86+ "secret_scanning_non_provider_patterns" : "enabled" ,
87+ "secret_scanning_validity_checks" : "enabled" ,
88+ }
89+ if path == "orgs/example/code-security/configurations/240284/repositories" :
90+ return 0 , [{"status" : "enforced" , "repository" : {"id" : 123 }}]
91+ raise AssertionError (path )
92+
93+ return _impl
94+
95+
96+ def _fake_gh_json_without_org_enforcement (secret_non_provider : str , validity_checks : str ):
97+ def _impl (path : str ) -> tuple [int , dict ]:
98+ if path == "repos/example/repo" :
99+ return (
100+ 0 ,
101+ {
102+ "id" : 123 ,
103+ "default_branch" : "main" ,
104+ "security_and_analysis" : {
105+ "secret_scanning" : {"status" : "enabled" },
106+ "secret_scanning_push_protection" : {"status" : "enabled" },
107+ "secret_scanning_non_provider_patterns" : {"status" : secret_non_provider },
108+ "secret_scanning_validity_checks" : {"status" : validity_checks },
109+ },
110+ },
111+ )
112+ if path == "repos/example/repo/actions/permissions" :
113+ return 0 , {"enabled" : True , "allowed_actions" : "all" , "sha_pinning_required" : True }
114+ if path == "repos/example/repo/branches/main/protection" :
115+ return 0 , {"required_status_checks" : {"contexts" : ["Quick Feedback" ]}}
116+ if path == "repos/example/repo/environments" :
117+ return 0 , {"environments" : [{"name" : "owner-approved-sensitive" }]}
118+ if path == "repos/example/repo/private-vulnerability-reporting" :
119+ return 0 , {"enabled" : True }
120+ if path == "repos/example/repo/vulnerability-alerts" :
121+ return 0 , {}
122+ if path == "repos/example/repo/code-scanning/default-setup" :
123+ return 0 , {}
124+ if path == "repos/example/repo/dependabot/alerts?per_page=1" :
125+ return 0 , []
126+ if path == "orgs/example/code-security/configurations/240284" :
127+ return 0 , {
128+ "id" : 240284 ,
129+ "secret_scanning" : "enabled" ,
130+ "secret_scanning_push_protection" : "enabled" ,
131+ "secret_scanning_non_provider_patterns" : "enabled" ,
132+ "secret_scanning_validity_checks" : "enabled" ,
133+ }
134+ if path == "orgs/example/code-security/configurations/240284/repositories" :
135+ return 0 , []
74136 raise AssertionError (path )
75137
76138 return _impl
@@ -83,7 +145,7 @@ def test_github_control_plane_requires_secret_scanning_subfeatures(tmp_path: Pat
83145 policy_path .write_text (json .dumps (_policy_payload (), ensure_ascii = False , indent = 2 ), encoding = "utf-8" )
84146
85147 monkeypatch .setattr (module , "ROOT" , tmp_path )
86- monkeypatch .setattr (module , "_gh_json" , _fake_gh_json ("disabled" , "disabled" ))
148+ monkeypatch .setattr (module , "_gh_json" , _fake_gh_json_without_org_enforcement ("disabled" , "disabled" ))
87149 monkeypatch .setattr (module , "_repo_path_exists" , lambda _path : True )
88150 monkeypatch .setattr (sys , "argv" , ["check_github_control_plane.py" , "--policy" , str (policy_path ), "--output" , str (output_path )])
89151
@@ -110,3 +172,21 @@ def test_github_control_plane_accepts_enabled_secret_scanning_subfeatures(tmp_pa
110172 assert rc == 0
111173 assert report ["errors" ] == []
112174 assert report ["security_and_analysis" ]["secret_scanning_non_provider_patterns" ]["status" ] == "enabled"
175+
176+
177+ def test_github_control_plane_accepts_enforced_org_config_fallback (tmp_path : Path , monkeypatch ) -> None :
178+ module = _load_module ()
179+ policy_path = tmp_path / "policy.json"
180+ output_path = tmp_path / "report.json"
181+ policy_path .write_text (json .dumps (_policy_payload (), ensure_ascii = False , indent = 2 ), encoding = "utf-8" )
182+
183+ monkeypatch .setattr (module , "ROOT" , tmp_path )
184+ monkeypatch .setattr (module , "_gh_json" , _fake_gh_json ("disabled" , "disabled" ))
185+ monkeypatch .setattr (module , "_repo_path_exists" , lambda _path : True )
186+ monkeypatch .setattr (sys , "argv" , ["check_github_control_plane.py" , "--policy" , str (policy_path ), "--output" , str (output_path )])
187+
188+ rc = module .main ()
189+ report = json .loads (output_path .read_text (encoding = "utf-8" ))
190+ assert rc == 0
191+ assert report ["errors" ] == []
192+ assert report ["org_code_security_configuration" ]["id" ] == 240284
0 commit comments