From 6264e70f8231c86e80e165cce0b818e0870d061d Mon Sep 17 00:00:00 2001 From: Teodora Sechkova Date: Tue, 14 Apr 2020 15:04:25 +0300 Subject: [PATCH 1/8] Add an optional mode argument to FilesystemBackend.put() Add an optional argument to StorageBackendInterface and FilesystemsBackend put() method for creating a file-object with custom mode (permissions). If custom permissions are not set, the implementation-specific default permissions apply. Signed-off-by: Teodora Sechkova --- securesystemslib/storage.py | 28 +++++++++++++++++++++++++--- securesystemslib/util.py | 0 2 files changed, 25 insertions(+), 3 deletions(-) mode change 100755 => 100644 securesystemslib/util.py diff --git a/securesystemslib/storage.py b/securesystemslib/storage.py index 96acd8c5..7c426d1f 100644 --- a/securesystemslib/storage.py +++ b/securesystemslib/storage.py @@ -65,7 +65,7 @@ def get(self, filepath: str) -> Iterator[BinaryIO]: @abc.abstractmethod - def put(self, fileobj: IO, filepath: str) -> None: + def put(self, fileobj: IO, filepath: str, mode=None): """ Store a file-like object in the storage backend. @@ -79,6 +79,13 @@ def put(self, fileobj: IO, filepath: str) -> None: filepath: The full path to the location where 'fileobj' will be stored. + mode: + Bit mask with custom file permissions for the file-like object. When + computing mode, the current OS umask value is first masked out. If None, + the default OS permissions apply. See os.open() 'mode' documentation. + On Windows systems only the file's read-only flag can be set. All other + bits are ignored. + securesystemslib.exceptions.StorageError, if the file can not be stored. @@ -208,14 +215,29 @@ def get(self, filepath:str) -> Iterator[BinaryIO]: file_object.close() - def put(self, fileobj: IO, filepath: str) -> None: + def put(self, fileobj: IO, filepath: str, mode=None) -> None: # If we are passed an open file, seek to the beginning such that we are # copying the entire contents if not fileobj.closed: fileobj.seek(0) + # If a file with the same name already exists, the new permissions + # may not be applied. try: - with open(filepath, 'wb') as destination_file: + os.remove(filepath) + except OSError: + pass + + try: + if mode is not None: + # The effective mode is modified by the process's umask. The mode + # of the created file is (mode & ~umask). + fd = os.open(filepath, os.O_WRONLY|os.O_CREAT, mode) + else: + # Use the default value (0o777) of the 'mode' argument of os.open() + fd = os.open(filepath, os.O_WRONLY|os.O_CREAT) + + with os.fdopen(fd, "wb") as destination_file: shutil.copyfileobj(fileobj, destination_file) # Force the destination file to be written to disk from Python's internal # and the operating system's buffers. os.fsync() should follow flush(). diff --git a/securesystemslib/util.py b/securesystemslib/util.py old mode 100755 new mode 100644 From eb401132eb697710244bee46b9c98f5938562d5e Mon Sep 17 00:00:00 2001 From: Teodora Sechkova Date: Tue, 14 Apr 2020 15:12:10 +0300 Subject: [PATCH 2/8] Create private keys with mode 0o600 Update generate_and_write_{rsa, ed25519, ecdsa}_keypair functions to create private keys with read and write permissions for the user only (0o600). Update util.persist_temp_file() with an optional permissions parameter. Signed-off-by: Teodora Sechkova --- securesystemslib/interface.py | 12 +++++++----- securesystemslib/util.py | 12 ++++++++++-- 2 files changed, 17 insertions(+), 7 deletions(-) mode change 100755 => 100644 securesystemslib/interface.py diff --git a/securesystemslib/interface.py b/securesystemslib/interface.py old mode 100755 new mode 100644 index 98616b62..5c986d9f --- a/securesystemslib/interface.py +++ b/securesystemslib/interface.py @@ -26,6 +26,7 @@ import logging import tempfile import json +import stat from securesystemslib import exceptions from securesystemslib import formats @@ -53,8 +54,9 @@ # security through 2031 and beyond. DEFAULT_RSA_KEY_BITS = 3072 - - +# Private keys are created with read and write permissions for the user only. +# Octal value 0o600. +PRIVATE_KEY_MODE = stat.S_IRUSR | stat.S_IWUSR def get_password(prompt='Password: ', confirm=False): @@ -239,7 +241,7 @@ def _generate_and_write_rsa_keypair(filepath=None, bits=DEFAULT_RSA_KEY_BITS, # Write PEM-encoded private key to file_object = tempfile.TemporaryFile() file_object.write(private.encode('utf-8')) - util.persist_temp_file(file_object, filepath) + util.persist_temp_file(file_object, filepath, mode=PRIVATE_KEY_MODE) return filepath @@ -508,7 +510,7 @@ def _generate_and_write_ed25519_keypair(filepath=None, password=None, # Write private key to file_object = tempfile.TemporaryFile() file_object.write(ed25519_key.encode('utf-8')) - util.persist_temp_file(file_object, filepath) + util.persist_temp_file(file_object, filepath, mode=PRIVATE_KEY_MODE) return filepath @@ -752,7 +754,7 @@ def _generate_and_write_ecdsa_keypair(filepath=None, password=None, # Write private key to file_object = tempfile.TemporaryFile() file_object.write(ecdsa_key.encode('utf-8')) - util.persist_temp_file(file_object, filepath) + util.persist_temp_file(file_object, filepath, mode=PRIVATE_KEY_MODE) return filepath diff --git a/securesystemslib/util.py b/securesystemslib/util.py index 44926b41..662fb72d 100644 --- a/securesystemslib/util.py +++ b/securesystemslib/util.py @@ -179,7 +179,8 @@ def persist_temp_file( temp_file: IO, persist_path: str, storage_backend: Optional[StorageBackendInterface] = None, - should_close: bool = True + should_close: bool = True, + mode = None ) -> None: """ @@ -203,6 +204,13 @@ def persist_temp_file( A boolean indicating whether the file should be closed after it has been persisted. Default is True, the file is closed. + mode: + Bit mask with custom file permissions for the newly created file. When + computing mode, the current OS umask value is first masked out. If None, + the default OS permissions apply. See os.open() 'mode' documentation. + On Windows systems only the file's read-only flag can be set. All other + bits are ignored. + securesystemslib.exceptions.StorageError: If file cannot be written. @@ -213,7 +221,7 @@ def persist_temp_file( if storage_backend is None: storage_backend = FilesystemBackend() - storage_backend.put(temp_file, persist_path) + storage_backend.put(temp_file, persist_path, mode=mode) if should_close: temp_file.close() From 9574d92eb729ca50fa72ba5e70186eec20770747 Mon Sep 17 00:00:00 2001 From: Teodora Sechkova Date: Tue, 14 Apr 2020 15:24:03 +0300 Subject: [PATCH 3/8] Update test_B7_persist_temp_file Add a test case for persisting a file with custom permissions Signed-off-by: Teodora Sechkova --- tests/test_util.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) mode change 100755 => 100644 tests/test_util.py diff --git a/tests/test_util.py b/tests/test_util.py old mode 100755 new mode 100644 index 01f57ab4..ee620b6a --- a/tests/test_util.py +++ b/tests/test_util.py @@ -24,6 +24,7 @@ import tempfile import unittest import timeit +import stat import securesystemslib.settings import securesystemslib.hash @@ -240,8 +241,21 @@ def test_B9_persist_temp_file(self): dest_path = os.path.join(dest_temp_dir, self.random_string()) tmpfile = tempfile.TemporaryFile() tmpfile.write(self.random_string().encode('utf-8')) - securesystemslib.util.persist_temp_file(tmpfile, dest_path) + + # Write a file with custom permissions + # stat.S_IRUSR: user has read permissions + # stat.S_IWUSR: user has write permissions + mode= stat.S_IRUSR | stat.S_IWUSR + securesystemslib.util.persist_temp_file(tmpfile, dest_path, mode=mode) self.assertTrue(dest_path) + + # Need to set also the stat.S_IFREG bit to match the st_mode output + # stat.S_IFREG - Regular file + expected_mode = stat.S_IFREG | mode + if os.name != 'posix': + # Windows only supports setting the read-only attribute. + expected_mode = stat.S_IFREG | 0o666 + self.assertEqual(os.stat(dest_path).st_mode, expected_mode) self.assertTrue(tmpfile.closed) # Test persisting a file without automatically closing the tmpfile From 4d026becc97f856d6107addfe9c0a8dc2f72723c Mon Sep 17 00:00:00 2001 From: Teodora Sechkova Date: Mon, 7 Dec 2020 15:18:06 +0200 Subject: [PATCH 4/8] Update test_generate_keypair_wrappers Add test case checking that private key is generated with read and write permissions for user only. Signed-off-by: Teodora Sechkova --- tests/test_interface.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/test_interface.py b/tests/test_interface.py index eab7f4ad..ff64975a 100755 --- a/tests/test_interface.py +++ b/tests/test_interface.py @@ -633,6 +633,7 @@ def test_generate_keypair_wrappers(self): """ key_pw = "pw" + expected_priv_mode = stat.S_IFREG|stat.S_IRUSR|stat.S_IWUSR for idx, (gen, gen_prompt, gen_plain, import_priv, schema) in enumerate([ ( generate_and_write_rsa_keypair, @@ -661,6 +662,10 @@ def test_generate_keypair_wrappers(self): priv = import_priv(fn_encrypted, key_pw) self.assertTrue(schema.matches(priv), assert_msg) + # Test that encrypted private key is generated with read and write + # permissions for user only + self.assertEqual(os.stat(fn_encrypted).st_mode, expected_priv_mode) + # Test generate_and_write_*_keypair errors if password is None or empty with self.assertRaises(FormatError, msg=assert_msg): fn_encrypted = gen(None) @@ -688,6 +693,9 @@ def test_generate_keypair_wrappers(self): priv = import_priv(fn_unencrypted) self.assertTrue(schema.matches(priv), assert_msg) + # Test that unencrypted private key is generated with read and write + # permissions for user only + self.assertEqual(os.stat(fn_unencrypted).st_mode, expected_priv_mode) def test_import_publickeys_from_file(self): From 0fffe11ad316fa32ec3af9612d06ea8d9ff0c215 Mon Sep 17 00:00:00 2001 From: Teodora Sechkova Date: Mon, 7 Dec 2020 16:46:16 +0200 Subject: [PATCH 5/8] Update _generate_and_write_*_keypair side effects Document as a side effect the overwriting of existing keys with the same filepath. Signed-off-by: Teodora Sechkova --- securesystemslib/interface.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/securesystemslib/interface.py b/securesystemslib/interface.py index 5c986d9f..114bfc69 100644 --- a/securesystemslib/interface.py +++ b/securesystemslib/interface.py @@ -206,6 +206,7 @@ def _generate_and_write_rsa_keypair(filepath=None, bits=DEFAULT_RSA_KEY_BITS, Side Effects: Prompts user for a password if 'prompt' is True. Writes key files to disk. + Overwrites filepath if already exists. Returns: The private key filepath. @@ -272,6 +273,7 @@ def generate_and_write_rsa_keypair(password, filepath=None, Side Effects: Writes key files to disk. + Overwrites filepath if already exists. Returns: The private key filepath. @@ -308,6 +310,7 @@ def generate_and_write_rsa_keypair_with_prompt(filepath=None, Side Effects: Prompts user for a password. Writes key files to disk. + Overwrites filepath if already exists. Returns: The private key filepath. @@ -340,6 +343,7 @@ def generate_and_write_unencrypted_rsa_keypair(filepath=None, Side Effects: Writes unencrypted key files to disk. + Overwrites filepath if already exists. Returns: The private key filepath. @@ -471,6 +475,7 @@ def _generate_and_write_ed25519_keypair(filepath=None, password=None, Side Effects: Prompts user for a password if 'prompt' is True. Writes key files to disk. + Overwrites filepath if already exists. Returns: The private key filepath. @@ -538,6 +543,7 @@ def generate_and_write_ed25519_keypair(password, filepath=None): Side Effects: Writes key files to disk. + Overwrites filepath if already exists. Returns: The private key filepath. @@ -570,6 +576,7 @@ def generate_and_write_ed25519_keypair_with_prompt(filepath=None): Side Effects: Prompts user for a password. Writes key files to disk. + Overwrites filepath if already exists. Returns: The private key filepath. @@ -597,6 +604,7 @@ def generate_and_write_unencrypted_ed25519_keypair(filepath=None): Side Effects: Writes unencrypted key files to disk. + Overwrites filepath if already exists. Returns: The private key filepath. @@ -715,6 +723,7 @@ def _generate_and_write_ecdsa_keypair(filepath=None, password=None, Side Effects: Prompts user for a password if 'prompt' is True. Writes key files to disk. + Overwrites filepath if already exists. Returns: The private key filepath. @@ -782,6 +791,7 @@ def generate_and_write_ecdsa_keypair(password, filepath=None): Side Effects: Writes key files to disk. + Overwrites filepath if already exists. Returns: The private key filepath. @@ -814,6 +824,7 @@ def generate_and_write_ecdsa_keypair_with_prompt(filepath=None): Side Effects: Prompts user for a password. Writes key files to disk. + Overwrites filepath if already exists. Returns: The private key filepath. @@ -841,6 +852,7 @@ def generate_and_write_unencrypted_ecdsa_keypair(filepath=None): Side Effects: Writes unencrypted key files to disk. + Overwrites filepath if already exists. Returns: The private key filepath. From c58d65d1aedb451a6093bf8c20dfd8be0e4f02a2 Mon Sep 17 00:00:00 2001 From: Joshua Lock Date: Wed, 21 Sep 2022 18:14:43 +0100 Subject: [PATCH 6/8] Preserve filesystem abstraction when restricting file permissions Rather than leak filesystem details to the user by exposing the file mode we add an abstract "restrict" boolean parameter. How the created object on the storage is restricted is an implementation detail of the StorageBackend. Suggested-by: Jussi Kukkonen Signed-off-by: Joshua Lock --- securesystemslib/interface.py | 11 +++-------- securesystemslib/storage.py | 30 +++++++++++++++++------------- securesystemslib/util.py | 14 ++++++-------- tests/test_util.py | 9 +++------ 4 files changed, 29 insertions(+), 35 deletions(-) diff --git a/securesystemslib/interface.py b/securesystemslib/interface.py index 114bfc69..1bc9f425 100644 --- a/securesystemslib/interface.py +++ b/securesystemslib/interface.py @@ -26,7 +26,6 @@ import logging import tempfile import json -import stat from securesystemslib import exceptions from securesystemslib import formats @@ -54,10 +53,6 @@ # security through 2031 and beyond. DEFAULT_RSA_KEY_BITS = 3072 -# Private keys are created with read and write permissions for the user only. -# Octal value 0o600. -PRIVATE_KEY_MODE = stat.S_IRUSR | stat.S_IWUSR - def get_password(prompt='Password: ', confirm=False): """Prompts user to enter a password. @@ -242,7 +237,7 @@ def _generate_and_write_rsa_keypair(filepath=None, bits=DEFAULT_RSA_KEY_BITS, # Write PEM-encoded private key to file_object = tempfile.TemporaryFile() file_object.write(private.encode('utf-8')) - util.persist_temp_file(file_object, filepath, mode=PRIVATE_KEY_MODE) + util.persist_temp_file(file_object, filepath, restrict=True) return filepath @@ -515,7 +510,7 @@ def _generate_and_write_ed25519_keypair(filepath=None, password=None, # Write private key to file_object = tempfile.TemporaryFile() file_object.write(ed25519_key.encode('utf-8')) - util.persist_temp_file(file_object, filepath, mode=PRIVATE_KEY_MODE) + util.persist_temp_file(file_object, filepath, restrict=True) return filepath @@ -763,7 +758,7 @@ def _generate_and_write_ecdsa_keypair(filepath=None, password=None, # Write private key to file_object = tempfile.TemporaryFile() file_object.write(ecdsa_key.encode('utf-8')) - util.persist_temp_file(file_object, filepath, mode=PRIVATE_KEY_MODE) + util.persist_temp_file(file_object, filepath, restrict=True) return filepath diff --git a/securesystemslib/storage.py b/securesystemslib/storage.py index 7c426d1f..acd84c8a 100644 --- a/securesystemslib/storage.py +++ b/securesystemslib/storage.py @@ -20,9 +20,10 @@ import logging import os import shutil +import stat from contextlib import contextmanager from securesystemslib import exceptions -from typing import BinaryIO, IO, Iterator, List +from typing import BinaryIO, IO, Iterator, List, Optional logger = logging.getLogger(__name__) @@ -65,7 +66,8 @@ def get(self, filepath: str) -> Iterator[BinaryIO]: @abc.abstractmethod - def put(self, fileobj: IO, filepath: str, mode=None): + def put(self, fileobj: IO, filepath: str, restrict: Optional[bool] = False + ) -> None: """ Store a file-like object in the storage backend. @@ -79,12 +81,12 @@ def put(self, fileobj: IO, filepath: str, mode=None): filepath: The full path to the location where 'fileobj' will be stored. - mode: - Bit mask with custom file permissions for the file-like object. When - computing mode, the current OS umask value is first masked out. If None, - the default OS permissions apply. See os.open() 'mode' documentation. - On Windows systems only the file's read-only flag can be set. All other - bits are ignored. + restrict: + Whether the file should be created with restricted permissions. + What counts as restricted is backend-specific. For a filesystem on a + UNIX-like operating system, that may mean read/write permissions only + for the user (octal mode 0o600). For a cloud storage system, that + likely means Cloud provider specific ACL restrictions. securesystemslib.exceptions.StorageError, if the file can not be stored. @@ -215,7 +217,8 @@ def get(self, filepath:str) -> Iterator[BinaryIO]: file_object.close() - def put(self, fileobj: IO, filepath: str, mode=None) -> None: + def put(self, fileobj: IO, filepath: str, restrict: Optional[bool] = False + ) -> None: # If we are passed an open file, seek to the beginning such that we are # copying the entire contents if not fileobj.closed: @@ -229,10 +232,11 @@ def put(self, fileobj: IO, filepath: str, mode=None) -> None: pass try: - if mode is not None: - # The effective mode is modified by the process's umask. The mode - # of the created file is (mode & ~umask). - fd = os.open(filepath, os.O_WRONLY|os.O_CREAT, mode) + if restrict: + # On UNIX-based systems restricted files are created with read and + # write permissions for the user only (octal value 0o600). + fd = os.open(filepath, os.O_WRONLY|os.O_CREAT, + stat.S_IRUSR|stat.S_IWUSR) else: # Use the default value (0o777) of the 'mode' argument of os.open() fd = os.open(filepath, os.O_WRONLY|os.O_CREAT) diff --git a/securesystemslib/util.py b/securesystemslib/util.py index 662fb72d..d716358a 100644 --- a/securesystemslib/util.py +++ b/securesystemslib/util.py @@ -180,7 +180,7 @@ def persist_temp_file( persist_path: str, storage_backend: Optional[StorageBackendInterface] = None, should_close: bool = True, - mode = None + restrict: bool = False ) -> None: """ @@ -204,12 +204,10 @@ def persist_temp_file( A boolean indicating whether the file should be closed after it has been persisted. Default is True, the file is closed. - mode: - Bit mask with custom file permissions for the newly created file. When - computing mode, the current OS umask value is first masked out. If None, - the default OS permissions apply. See os.open() 'mode' documentation. - On Windows systems only the file's read-only flag can be set. All other - bits are ignored. + restrict: + A boolean indicating whether the file should have restricted privileges. + What evactly counts as restricted privileges is an implementation detail + of the backing StorageBackendInterface implementation. securesystemslib.exceptions.StorageError: If file cannot be written. @@ -221,7 +219,7 @@ def persist_temp_file( if storage_backend is None: storage_backend = FilesystemBackend() - storage_backend.put(temp_file, persist_path, mode=mode) + storage_backend.put(temp_file, persist_path, restrict=restrict) if should_close: temp_file.close() diff --git a/tests/test_util.py b/tests/test_util.py index ee620b6a..df3e9427 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -242,16 +242,13 @@ def test_B9_persist_temp_file(self): tmpfile = tempfile.TemporaryFile() tmpfile.write(self.random_string().encode('utf-8')) - # Write a file with custom permissions - # stat.S_IRUSR: user has read permissions - # stat.S_IWUSR: user has write permissions - mode= stat.S_IRUSR | stat.S_IWUSR - securesystemslib.util.persist_temp_file(tmpfile, dest_path, mode=mode) + # Write a file with restricted permissions + securesystemslib.util.persist_temp_file(tmpfile, dest_path, restrict=True) self.assertTrue(dest_path) # Need to set also the stat.S_IFREG bit to match the st_mode output # stat.S_IFREG - Regular file - expected_mode = stat.S_IFREG | mode + expected_mode = stat.S_IFREG | stat.S_IRUSR | stat.S_IWUSR if os.name != 'posix': # Windows only supports setting the read-only attribute. expected_mode = stat.S_IFREG | 0o666 From 6250c790c9b9c317e661b345b345bede42c77cb9 Mon Sep 17 00:00:00 2001 From: Joshua Lock Date: Thu, 22 Sep 2022 10:10:36 +0100 Subject: [PATCH 7/8] Tighten docstring language on overwriting files Signed-off-by: Joshua Lock --- securesystemslib/interface.py | 24 ++++++++++++------------ tests/test_util.py | 2 +- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/securesystemslib/interface.py b/securesystemslib/interface.py index 1bc9f425..73fe6453 100644 --- a/securesystemslib/interface.py +++ b/securesystemslib/interface.py @@ -201,7 +201,7 @@ def _generate_and_write_rsa_keypair(filepath=None, bits=DEFAULT_RSA_KEY_BITS, Side Effects: Prompts user for a password if 'prompt' is True. Writes key files to disk. - Overwrites filepath if already exists. + Overwrites files if they already exist. Returns: The private key filepath. @@ -268,7 +268,7 @@ def generate_and_write_rsa_keypair(password, filepath=None, Side Effects: Writes key files to disk. - Overwrites filepath if already exists. + Overwrites files if they already exist. Returns: The private key filepath. @@ -305,7 +305,7 @@ def generate_and_write_rsa_keypair_with_prompt(filepath=None, Side Effects: Prompts user for a password. Writes key files to disk. - Overwrites filepath if already exists. + Overwrites files if they already exist. Returns: The private key filepath. @@ -338,7 +338,7 @@ def generate_and_write_unencrypted_rsa_keypair(filepath=None, Side Effects: Writes unencrypted key files to disk. - Overwrites filepath if already exists. + Overwrites files if they already exist. Returns: The private key filepath. @@ -470,7 +470,7 @@ def _generate_and_write_ed25519_keypair(filepath=None, password=None, Side Effects: Prompts user for a password if 'prompt' is True. Writes key files to disk. - Overwrites filepath if already exists. + Overwrites files if they already exist. Returns: The private key filepath. @@ -538,7 +538,7 @@ def generate_and_write_ed25519_keypair(password, filepath=None): Side Effects: Writes key files to disk. - Overwrites filepath if already exists. + Overwrites files if they already exist. Returns: The private key filepath. @@ -571,7 +571,7 @@ def generate_and_write_ed25519_keypair_with_prompt(filepath=None): Side Effects: Prompts user for a password. Writes key files to disk. - Overwrites filepath if already exists. + Overwrites files if they already exist. Returns: The private key filepath. @@ -599,7 +599,7 @@ def generate_and_write_unencrypted_ed25519_keypair(filepath=None): Side Effects: Writes unencrypted key files to disk. - Overwrites filepath if already exists. + Overwrites files if they already exist. Returns: The private key filepath. @@ -718,7 +718,7 @@ def _generate_and_write_ecdsa_keypair(filepath=None, password=None, Side Effects: Prompts user for a password if 'prompt' is True. Writes key files to disk. - Overwrites filepath if already exists. + Overwrites files if they already exist. Returns: The private key filepath. @@ -786,7 +786,7 @@ def generate_and_write_ecdsa_keypair(password, filepath=None): Side Effects: Writes key files to disk. - Overwrites filepath if already exists. + Overwrites files if they already exist. Returns: The private key filepath. @@ -819,7 +819,7 @@ def generate_and_write_ecdsa_keypair_with_prompt(filepath=None): Side Effects: Prompts user for a password. Writes key files to disk. - Overwrites filepath if already exists. + Overwrites files if they already exist. Returns: The private key filepath. @@ -847,7 +847,7 @@ def generate_and_write_unencrypted_ecdsa_keypair(filepath=None): Side Effects: Writes unencrypted key files to disk. - Overwrites filepath if already exists. + Overwrites files if they already exist. Returns: The private key filepath. diff --git a/tests/test_util.py b/tests/test_util.py index df3e9427..00588900 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -248,7 +248,7 @@ def test_B9_persist_temp_file(self): # Need to set also the stat.S_IFREG bit to match the st_mode output # stat.S_IFREG - Regular file - expected_mode = stat.S_IFREG | stat.S_IRUSR | stat.S_IWUSR + expected_mode = stat.S_IFREG | stat.S_IRUSR | stat.S_IWUSR if os.name != 'posix': # Windows only supports setting the read-only attribute. expected_mode = stat.S_IFREG | 0o666 From 94329375b2d37b6f306a01f5decd3b0991977a45 Mon Sep 17 00:00:00 2001 From: Joshua Lock Date: Tue, 11 Oct 2022 11:00:59 +0100 Subject: [PATCH 8/8] Apply suggestions from code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Lukas PĆ¼hringer --- securesystemslib/storage.py | 6 +++++- tests/test_util.py | 4 ++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/securesystemslib/storage.py b/securesystemslib/storage.py index acd84c8a..d488402e 100644 --- a/securesystemslib/storage.py +++ b/securesystemslib/storage.py @@ -238,7 +238,11 @@ def put(self, fileobj: IO, filepath: str, restrict: Optional[bool] = False fd = os.open(filepath, os.O_WRONLY|os.O_CREAT, stat.S_IRUSR|stat.S_IWUSR) else: - # Use the default value (0o777) of the 'mode' argument of os.open() + # Non-restricted files use the default 'mode' argument of os.open() + # granting read, write, and execute for all users (octal mode 0o777). + # NOTE: mode may be modified by the user's file mode creation mask + # (umask) or on Windows limited to the smaller set of OS supported + # permisssions. fd = os.open(filepath, os.O_WRONLY|os.O_CREAT) with os.fdopen(fd, "wb") as destination_file: diff --git a/tests/test_util.py b/tests/test_util.py index 00588900..ee3908f3 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -249,9 +249,9 @@ def test_B9_persist_temp_file(self): # Need to set also the stat.S_IFREG bit to match the st_mode output # stat.S_IFREG - Regular file expected_mode = stat.S_IFREG | stat.S_IRUSR | stat.S_IWUSR - if os.name != 'posix': + if os.name == 'nt': # Windows only supports setting the read-only attribute. - expected_mode = stat.S_IFREG | 0o666 + expected_mode = stat.S_IFREG | stat.S_IWUSR | stat.S_IRUSR | stat.S_IWGRP | stat.S_IRGRP | stat.S_IWOTH | stat.S_IROTH self.assertEqual(os.stat(dest_path).st_mode, expected_mode) self.assertTrue(tmpfile.closed)