else:
raise NetrcParseError("bad follower token %r" % tt,
file, lexer.lineno)
- self._security_check(fp, default_netrc, self.hosts[entryname][0])
-
- def _security_check(self, fp, default_netrc, login):
- if _can_security_check() and default_netrc and login != "anonymous":
- prop = os.fstat(fp.fileno())
- current_user_id = os.getuid()
- if prop.st_uid != current_user_id:
- fowner = _getpwuid(prop.st_uid)
- user = _getpwuid(current_user_id)
- raise NetrcParseError(
- f"~/.netrc file owner ({fowner}) does not match"
- f" current user ({user})")
- if (prop.st_mode & (stat.S_IRWXG | stat.S_IRWXO)):
- raise NetrcParseError(
- "~/.netrc access too permissive: access"
- " permissions must restrict access to only"
- " the owner")
+
+ if _can_security_check() and default_netrc:
+ for entry in self.hosts.values():
+ if entry[0] != "anonymous":
+ # Raises on security issue; once passed once can exit.
+ self._security_check(fp)
+ return
+
+ def _security_check(self, fp):
+ prop = os.fstat(fp.fileno())
+ current_user_id = os.getuid()
+ if prop.st_uid != current_user_id:
+ fowner = _getpwuid(prop.st_uid)
+ user = _getpwuid(current_user_id)
+ raise NetrcParseError(
+ f"~/.netrc file owner ({fowner}) does not match"
+ f" current user ({user})")
+ if (prop.st_mode & (stat.S_IRWXG | stat.S_IRWXO)):
+ raise NetrcParseError(
+ "~/.netrc access too permissive: access"
+ " permissions must restrict access to only"
+ " the owner")
def authenticators(self, host):
"""Return a (user, account, password) tuple for given host."""
import netrc, os, unittest, sys, textwrap
+from pathlib import Path
from test import support
from test.support import os_helper
+from unittest.mock import patch
+
temp_filename = os_helper.TESTFN
self.assertEqual(nrc.hosts['foo.domain.com'],
('anonymous', '', 'pass'))
+ @unittest.skipUnless(os.name == 'posix', 'POSIX only test')
+ @unittest.skipUnless(hasattr(os, 'getuid'), "os.getuid is required")
+ @os_helper.skip_unless_working_chmod
+ def test_security_only_once(self):
+ # Make sure security check is only run once per parse when multiple
+ # entries are found.
+ with patch.object(netrc.netrc, "_security_check") as mock:
+ with os_helper.temp_dir() as tmp_dir:
+ netrc_path = Path(tmp_dir) / '.netrc'
+ netrc_path.write_text("""\
+ machine foo.domain.com login bar password pass
+ machine bar.domain.com login foo password pass
+ """)
+ netrc_path.chmod(0o600)
+ with os_helper.EnvironmentVarGuard() as environ:
+ environ.set('HOME', tmp_dir)
+ netrc.netrc()
+
+ mock.assert_called_once()
+
if __name__ == "__main__":
unittest.main()