@@ -435,6 +435,38 @@ def test_login_dry_run(self):
435435 self .subprocess_utils .check_output .assert_not_called ()
436436 self .assertFalse (os .path .exists (self .test_netrc_path ))
437437
438+ @mock .patch ('awscli.customizations.codeartifact.login.is_macos' , False )
439+ def test_login_adjusts_permissions_on_preexisting_file (self ):
440+ existing_content = (
441+ f'machine { self .hostname } login token password old-token\n '
442+ )
443+ with open (self .test_netrc_path , 'w' ) as f :
444+ f .write (existing_content )
445+ os .chmod (self .test_netrc_path , 0o644 )
446+ self .test_subject .login ()
447+ file_mode = os .stat (self .test_netrc_path ).st_mode
448+ self .assertEqual (stat .S_IMODE (file_mode ), 0o600 )
449+
450+ @mock .patch ('awscli.customizations.codeartifact.login.is_macos' , False )
451+ @mock .patch ('sys.stderr' )
452+ @mock .patch ('os.chmod' , side_effect = OSError (
453+ errno .EPERM , 'Operation not permitted'
454+ ))
455+ def test_login_writes_error_when_chmod_fails (
456+ self , mock_chmod , mock_stderr
457+ ):
458+ existing_content = (
459+ f'machine { self .hostname } login token password old-token\n '
460+ )
461+ with open (self .test_netrc_path , 'w' ) as f :
462+ f .write (existing_content )
463+ self .test_subject .login ()
464+ mock_stderr .write .assert_any_call (
465+ 'Unable to set file permissions for %s: '
466+ '[Errno 1] Operation not permitted%s'
467+ % (self .test_netrc_path , os .linesep )
468+ )
469+
438470
439471class TestNuGetLogin (unittest .TestCase ):
440472 _NUGET_INDEX_URL_FMT = NuGetLogin ._NUGET_INDEX_URL_FMT
@@ -1246,6 +1278,42 @@ def test_login_existing_pypi_rc_with_codeartifact_not_clobbered(self):
12461278 password = 'JgCXIr5xGG'
12471279 )
12481280
1281+ def test_login_sets_secure_permissions_on_new_file (self ):
1282+ self .assertFalse (os .path .exists (self .test_pypi_rc_path ))
1283+ self .test_subject .login ()
1284+ file_mode = os .stat (self .test_pypi_rc_path ).st_mode
1285+ self .assertEqual (stat .S_IMODE (file_mode ), 0o600 )
1286+
1287+ def test_login_adjusts_permissions_on_preexisting_file (self ):
1288+ existing_pypi_rc = '''\
1289+ [distutils]
1290+ index-servers=
1291+ pypi
1292+
1293+ [pypi]
1294+ repository: http://www.python.org/pypi/
1295+ username: test
1296+ password: JgCXIr5xGG
1297+ '''
1298+ with open (self .test_pypi_rc_path , 'w' ) as f :
1299+ f .write (existing_pypi_rc )
1300+ os .chmod (self .test_pypi_rc_path , 0o644 )
1301+ self .test_subject .login ()
1302+ file_mode = os .stat (self .test_pypi_rc_path ).st_mode
1303+ self .assertEqual (stat .S_IMODE (file_mode ), 0o600 )
1304+
1305+ @mock .patch ('sys.stderr' )
1306+ @mock .patch ('os.chmod' , side_effect = OSError (
1307+ errno .EPERM , 'Operation not permitted'
1308+ ))
1309+ def test_login_writes_error_when_chmod_fails (self , mock_chmod , mock_stderr ):
1310+ self .test_subject .login ()
1311+ mock_stderr .write .assert_any_call (
1312+ 'Unable to set file permissions for %s: '
1313+ '[Errno 1] Operation not permitted%s'
1314+ % (self .test_pypi_rc_path , os .linesep )
1315+ )
1316+
12491317 def test_login_existing_invalid_pypi_rc_error (self ):
12501318 # This is an invalid pypirc as the list of servers are expected under
12511319 # an 'index-servers' option instead of 'servers'.
0 commit comments