@@ -252,6 +252,80 @@ def side_effect(*args, **_kwargs):
252252 sandbox .Sandbox (enable_networking = False )
253253 self .assertIn ("failed to create sandbox via subprocess" , str (ctx .exception ))
254254
255+ def test_invalid_networking_mode (self ): # pylint: disable=unused-argument
256+ with self .assertRaises (ValueError ) as ctx :
257+ sandbox .Sandbox (network = "invalid-net" )
258+ self .assertIn (
259+ "Invalid network mode 'invalid-net'. Valid options are 'none',"
260+ " 'sandbox', 'host', or None." ,
261+ str (ctx .exception ),
262+ )
263+
264+ @mock .patch ("os.geteuid" , return_value = 0 )
265+ @mock .patch ("subprocess.run" )
266+ def test_network_modes (self , mock_run , mock_geteuid ): # pylint: disable=unused-argument
267+ mock_run .return_value = mock .Mock (returncode = 0 )
268+ for net in ["none" , "sandbox" , "host" ]:
269+ mock_run .reset_mock ()
270+ sb = sandbox .Sandbox (network = net )
271+ args = mock_run .call_args_list [0 ][0 ][0 ]
272+ if net != "none" :
273+ self .assertIn (f"--network={ net } " , args )
274+ else :
275+ self .assertIn ("--network=none" , args )
276+ sb .close ()
277+
278+ @mock .patch ("os.geteuid" , return_value = 0 )
279+ @mock .patch ("subprocess.run" )
280+ def test_network_none_overrides_enable_networking_true (
281+ self , mock_run , mock_geteuid
282+ ): # pylint: disable=unused-argument
283+ mock_run .return_value = mock .Mock (returncode = 0 )
284+ sb = sandbox .Sandbox (enable_networking = True , network = "none" )
285+ args = mock_run .call_args_list [0 ][0 ][0 ]
286+ self .assertIn ("--network=none" , args )
287+ config_path = os .path .join (sb .bundle_dir , "config.json" )
288+ with open (config_path , "r" ) as f :
289+ spec = json .load (f )
290+ namespaces = spec .get ("linux" , {}).get ("namespaces" , [])
291+ namespace_types = {ns .get ("type" ) for ns in namespaces }
292+ self .assertNotIn ("network" , namespace_types )
293+ sb .close ()
294+
295+ @mock .patch ("os.geteuid" , return_value = 1000 )
296+ def test_network_sandbox_nonroot_raises_error (
297+ self , mock_geteuid
298+ ): # pylint: disable=unused-argument
299+ with self .assertRaises (sandbox .Error ) as ctx :
300+ sandbox .Sandbox (network = "sandbox" )
301+ self .assertIn (
302+ "enabling networking requires running as root" , str (ctx .exception )
303+ )
304+
305+ def test_network_mode_none_real_sandbox (self ):
306+ """Verifies starting a real sandbox with network='none' without mocks."""
307+ with sandbox .Sandbox (network = "none" ) as sb :
308+ stdout , _ = sb .exec ("echo" , "hello network none" )
309+ self .assertEqual (stdout .strip (), "hello network none" )
310+ config_path = os .path .join (sb .bundle_dir , "config.json" )
311+ with open (config_path , "r" ) as f :
312+ spec = json .load (f )
313+ namespaces = spec .get ("linux" , {}).get ("namespaces" , [])
314+ namespace_types = {ns .get ("type" ) for ns in namespaces }
315+ self .assertNotIn ("network" , namespace_types )
316+
317+ def test_network_mode_host_real_sandbox (self ):
318+ """Verifies starting a real sandbox with network='host' without mocks."""
319+ with sandbox .Sandbox (network = "host" ) as sb :
320+ stdout , _ = sb .exec ("echo" , "hello network host" )
321+ self .assertEqual (stdout .strip (), "hello network host" )
322+ config_path = os .path .join (sb .bundle_dir , "config.json" )
323+ with open (config_path , "r" ) as f :
324+ spec = json .load (f )
325+ namespaces = spec .get ("linux" , {}).get ("namespaces" , [])
326+ namespace_types = {ns .get ("type" ) for ns in namespaces }
327+ self .assertNotIn ("network" , namespace_types )
328+
255329 def test_find_runsc_not_found (self ):
256330 old_runsc_path = os .environ .get ("RUNSC_PATH" )
257331 if "RUNSC_PATH" in os .environ :
0 commit comments