From 23ca5d0e9c1db2fa8b0c1012eb199f8191400420 Mon Sep 17 00:00:00 2001 From: Amaury Chamayou Date: Tue, 2 Jun 2026 22:05:01 +0100 Subject: [PATCH 01/16] Potential fix for code scanning alert no. 974: File created without restricting permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> --- src/host/ledger.h | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/src/host/ledger.h b/src/host/ledger.h index 56b06394037a..5cca215d683a 100644 --- a/src/host/ledger.h +++ b/src/host/ledger.h @@ -17,9 +17,11 @@ #include #include #include +#include #include #include #include +#include #include #include #include @@ -120,13 +122,22 @@ namespace asynchost auto file_path = dir / file_name; - // Use exclusive-create mode ("x") to atomically fail if the file - // already exists, avoiding a separate fs::exists() stat call. + // Use O_EXCL to atomically fail if the file already exists, and create + // with restrictive permissions (0600) rather than relying on umask. { TimeBoundLogger log_if_slow( - fmt::format("Creating ledger file - fopen({})", file_path)); - // NOLINTNEXTLINE(cppcoreguidelines-owning-memory) - file = fopen(file_path.c_str(), "w+bx"); + fmt::format("Creating ledger file - open({})", file_path)); + int fd = open( + file_path.c_str(), O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR); + if (fd != -1) + { + // NOLINTNEXTLINE(cppcoreguidelines-owning-memory) + file = fdopen(fd, "w+b"); + if (file == nullptr) + { + close(fd); + } + } } if (file == nullptr) { From 3024f5b0a9195439ec64296b4c8b57bbb250bca3 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 2 Jun 2026 21:14:50 +0000 Subject: [PATCH 02/16] Harden remaining file creation sites --- src/ds/files.h | 86 +++++++++++++++++++++++++++++--- src/host/ledger.h | 15 +----- src/host/lfs_file_handler.h | 10 ++-- src/snapshots/snapshot_manager.h | 5 +- 4 files changed, 85 insertions(+), 31 deletions(-) diff --git a/src/ds/files.h b/src/ds/files.h index 00e967edbe48..28baba1c5596 100644 --- a/src/ds/files.h +++ b/src/ds/files.h @@ -2,7 +2,11 @@ // Licensed under the Apache 2.0 License. #pragma once +#include +#include #include +#include +#include #include #include #include @@ -10,6 +14,8 @@ #include #include #include +#include +#include #include #define FMT_HEADER_ONLY @@ -19,6 +25,39 @@ namespace files { namespace fs = std::filesystem; + static constexpr mode_t private_file_permissions = S_IRUSR | S_IWUSR; + + static int open_fd( + const fs::path& file, + int flags, + mode_t permissions = private_file_permissions) + { + return ::open(file.c_str(), flags, permissions); + } + + static FILE* open_file( + const fs::path& file, + int flags, + const char* mode, + mode_t permissions = private_file_permissions) + { + const auto fd = open_fd(file, flags, permissions); + if (fd == -1) + { + return nullptr; + } + + // NOLINTNEXTLINE(cppcoreguidelines-owning-memory) + auto* f = fdopen(fd, mode); + if (f == nullptr) + { + const auto saved_errno = errno; + close(fd); + errno = saved_errno; + } + + return f; + } /** * @brief Checks if a path exists @@ -114,6 +153,30 @@ namespace files return nlohmann::json::parse(v.begin(), v.end()); } + static void dump(const uint8_t* data, size_t size, const fs::path& file) + { + auto* f = open_file(file, O_WRONLY | O_CREAT | O_TRUNC, "wb"); + if (f == nullptr) + { + throw std::logic_error(fmt::format( + "Failed to open file {} for writing: {}", + file.string(), + std::strerror(errno))); // NOLINT(concurrency-mt-unsafe) + } + + const auto bytes_written = fwrite(data, sizeof(uint8_t), size, f); + const auto write_errno = errno; + const auto close_rc = fclose(f); + if (bytes_written != size || close_rc != 0) + { + errno = close_rc != 0 ? errno : write_errno; + throw std::logic_error(fmt::format( + "Failed to write to file {}: {}", + file.string(), + std::strerror(errno))); // NOLINT(concurrency-mt-unsafe) + } + } + /** * @brief Writes the content of a vector to a file * @@ -122,13 +185,12 @@ namespace files */ static void dump(const std::vector& data, const std::string& file) { - using namespace std; - ofstream f(file, ios::binary | ios::trunc); - f.write(reinterpret_cast(data.data()), data.size()); - if (!f) - { - throw logic_error("Failed to write to file: " + file); - } + dump(data.data(), data.size(), file); + } + + static void dump(const std::vector& data, const fs::path& file) + { + dump(data.data(), data.size(), file); } /** @@ -139,7 +201,15 @@ namespace files */ static void dump(const std::string& data, const std::string& file) { - dump(std::vector(data.begin(), data.end()), file); + dump( + reinterpret_cast(data.data()), + data.size(), + fs::path(file)); + } + + static void dump(const std::string& data, const fs::path& file) + { + dump(reinterpret_cast(data.data()), data.size(), file); } static void rename(const fs::path& src, const fs::path& dst) diff --git a/src/host/ledger.h b/src/host/ledger.h index 5cca215d683a..ded17c79826e 100644 --- a/src/host/ledger.h +++ b/src/host/ledger.h @@ -17,13 +17,10 @@ #include #include #include -#include #include #include #include -#include #include -#include #include #include @@ -127,17 +124,7 @@ namespace asynchost { TimeBoundLogger log_if_slow( fmt::format("Creating ledger file - open({})", file_path)); - int fd = open( - file_path.c_str(), O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR); - if (fd != -1) - { - // NOLINTNEXTLINE(cppcoreguidelines-owning-memory) - file = fdopen(fd, "w+b"); - if (file == nullptr) - { - close(fd); - } - } + file = files::open_file(file_path, O_RDWR | O_CREAT | O_EXCL, "w+b"); } if (file == nullptr) { diff --git a/src/host/lfs_file_handler.h b/src/host/lfs_file_handler.h index 24ff4105a711..1cb86cd06034 100644 --- a/src/host/lfs_file_handler.h +++ b/src/host/lfs_file_handler.h @@ -2,12 +2,12 @@ // Licensed under the Apache 2.0 License. #pragma once +#include "ds/files.h" #include "ds/messaging.h" #include "indexing/lfs_ringbuffer_types.h" #include "time_bound_logger.h" #include -#include namespace asynchost { @@ -50,16 +50,12 @@ namespace asynchost const auto target_path = root_dir / key; { TimeBoundLogger log_if_slow(fmt::format( - "Writing LFS file ({} bytes) - ofstream({})", + "Writing LFS file ({} bytes) - dump({})", encrypted.size(), target_path)); - std::ofstream f(target_path, std::ios::trunc | std::ios::binary); LOG_TRACE_FMT( "Writing {} byte file to {}", encrypted.size(), target_path); - f.write( - reinterpret_cast(encrypted.data()), - encrypted.size()); - f.close(); + files::dump(encrypted, target_path); } }); diff --git a/src/snapshots/snapshot_manager.h b/src/snapshots/snapshot_manager.h index 8f3cd44b86ab..ce2e88930e10 100644 --- a/src/snapshots/snapshot_manager.h +++ b/src/snapshots/snapshot_manager.h @@ -4,6 +4,7 @@ #include "ccf/ds/nonstd.h" #include "consensus/ledger_enclave_types.h" +#include "ds/files.h" #include "host/time_bound_logger.h" #include "snapshots/filenames.h" @@ -185,8 +186,8 @@ namespace snapshots { asynchost::TimeBoundLogger log_open_if_slow( fmt::format("Opening snapshot file - open({})", file_name)); - snapshot_fd = open( - full_snapshot_path.c_str(), O_CREAT | O_EXCL | O_WRONLY, 0664); + snapshot_fd = + files::open_fd(full_snapshot_path, O_CREAT | O_EXCL | O_WRONLY); } if (snapshot_fd == -1) { From c5e20f33ced0b2d5a678d55aeaa789e6783cb282 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 2 Jun 2026 21:15:53 +0000 Subject: [PATCH 03/16] Address validation feedback on secure file writes --- src/ds/files.h | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/ds/files.h b/src/ds/files.h index 28baba1c5596..0f8a404bba7c 100644 --- a/src/ds/files.h +++ b/src/ds/files.h @@ -51,9 +51,12 @@ namespace files auto* f = fdopen(fd, mode); if (f == nullptr) { - const auto saved_errno = errno; - close(fd); - errno = saved_errno; + auto saved_errno = errno; + if (close(fd) != 0 && saved_errno == 0) + { + saved_errno = errno; + } + errno = saved_errno != 0 ? saved_errno : EIO; } return f; @@ -166,10 +169,14 @@ namespace files const auto bytes_written = fwrite(data, sizeof(uint8_t), size, f); const auto write_errno = errno; + errno = 0; const auto close_rc = fclose(f); + const auto close_errno = errno; if (bytes_written != size || close_rc != 0) { - errno = close_rc != 0 ? errno : write_errno; + errno = bytes_written != size ? + (write_errno != 0 ? write_errno : EIO) : + (close_errno != 0 ? close_errno : EIO); throw std::logic_error(fmt::format( "Failed to write to file {}: {}", file.string(), From 974d919e02737359d42f110c427906f2e0b5b250 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 2 Jun 2026 21:16:52 +0000 Subject: [PATCH 04/16] Polish secure file write helper handling --- src/ds/files.h | 14 +++++++++++--- src/host/lfs_file_handler.h | 2 +- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/ds/files.h b/src/ds/files.h index 0f8a404bba7c..c3866e4af044 100644 --- a/src/ds/files.h +++ b/src/ds/files.h @@ -52,6 +52,8 @@ namespace files if (f == nullptr) { auto saved_errno = errno; + // Preserve the original fdopen() failure when it set errno, and only + // fall back to close()'s errno if fdopen() did not. if (close(fd) != 0 && saved_errno == 0) { saved_errno = errno; @@ -169,14 +171,20 @@ namespace files const auto bytes_written = fwrite(data, sizeof(uint8_t), size, f); const auto write_errno = errno; + // Preserve any write-side errno before fclose() can overwrite it. errno = 0; const auto close_rc = fclose(f); const auto close_errno = errno; if (bytes_written != size || close_rc != 0) { - errno = bytes_written != size ? - (write_errno != 0 ? write_errno : EIO) : - (close_errno != 0 ? close_errno : EIO); + if (bytes_written != size) + { + errno = write_errno != 0 ? write_errno : EIO; + } + else + { + errno = close_errno != 0 ? close_errno : EIO; + } throw std::logic_error(fmt::format( "Failed to write to file {}: {}", file.string(), diff --git a/src/host/lfs_file_handler.h b/src/host/lfs_file_handler.h index 1cb86cd06034..338869cd940f 100644 --- a/src/host/lfs_file_handler.h +++ b/src/host/lfs_file_handler.h @@ -50,7 +50,7 @@ namespace asynchost const auto target_path = root_dir / key; { TimeBoundLogger log_if_slow(fmt::format( - "Writing LFS file ({} bytes) - dump({})", + "Writing LFS file ({} bytes) - {}", encrypted.size(), target_path)); LOG_TRACE_FMT( From 0fa12194d25db4502de2308add0c9fdf74e34122 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 2 Jun 2026 21:35:16 +0000 Subject: [PATCH 05/16] Modernize files::dump overloads --- src/ds/files.h | 45 ++++++++++++++------------------------------- 1 file changed, 14 insertions(+), 31 deletions(-) diff --git a/src/ds/files.h b/src/ds/files.h index c3866e4af044..832910c2cfed 100644 --- a/src/ds/files.h +++ b/src/ds/files.h @@ -12,8 +12,10 @@ #include #include #include +#include #include #include +#include #include #include #include @@ -158,7 +160,7 @@ namespace files return nlohmann::json::parse(v.begin(), v.end()); } - static void dump(const uint8_t* data, size_t size, const fs::path& file) + static void dump(std::span data, const fs::path& file) { auto* f = open_file(file, O_WRONLY | O_CREAT | O_TRUNC, "wb"); if (f == nullptr) @@ -169,15 +171,16 @@ namespace files std::strerror(errno))); // NOLINT(concurrency-mt-unsafe) } - const auto bytes_written = fwrite(data, sizeof(uint8_t), size, f); + const auto bytes_written = + fwrite(data.data(), sizeof(uint8_t), data.size(), f); const auto write_errno = errno; // Preserve any write-side errno before fclose() can overwrite it. errno = 0; const auto close_rc = fclose(f); const auto close_errno = errno; - if (bytes_written != size || close_rc != 0) + if (bytes_written != data.size() || close_rc != 0) { - if (bytes_written != size) + if (bytes_written != data.size()) { errno = write_errno != 0 ? write_errno : EIO; } @@ -193,38 +196,18 @@ namespace files } /** - * @brief Writes the content of a vector to a file + * @brief Writes the content of a byte span to a file * - * @param data vector to write + * @param data bytes to write * @param file the path */ - static void dump(const std::vector& data, const std::string& file) - { - dump(data.data(), data.size(), file); - } - - static void dump(const std::vector& data, const fs::path& file) - { - dump(data.data(), data.size(), file); - } - - /** - * @brief Writes the content of a string to a file - * - * @param data string to write - * @param file the path - */ - static void dump(const std::string& data, const std::string& file) + static void dump(std::string_view data, const fs::path& file) { dump( - reinterpret_cast(data.data()), - data.size(), - fs::path(file)); - } - - static void dump(const std::string& data, const fs::path& file) - { - dump(reinterpret_cast(data.data()), data.size(), file); + std::span( + reinterpret_cast(data.data()), + data.size()), + file); } static void rename(const fs::path& src, const fs::path& dst) From 396266354dd5555203d02be18d0874737936b82c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 2 Jun 2026 21:35:53 +0000 Subject: [PATCH 06/16] Fix dump overload docstring --- src/ds/files.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ds/files.h b/src/ds/files.h index 832910c2cfed..d5af62923b49 100644 --- a/src/ds/files.h +++ b/src/ds/files.h @@ -196,9 +196,9 @@ namespace files } /** - * @brief Writes the content of a byte span to a file + * @brief Writes the content of a string to a file * - * @param data bytes to write + * @param data string to write * @param file the path */ static void dump(std::string_view data, const fs::path& file) From a0c3230ac5f95fcb5263b01ceb828ee663eac8c9 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 2 Jun 2026 21:36:57 +0000 Subject: [PATCH 07/16] Use std::as_bytes in dump helpers --- src/ds/files.h | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/src/ds/files.h b/src/ds/files.h index d5af62923b49..28fa82bf79b7 100644 --- a/src/ds/files.h +++ b/src/ds/files.h @@ -3,6 +3,7 @@ #pragma once #include +#include #include #include #include @@ -160,7 +161,8 @@ namespace files return nlohmann::json::parse(v.begin(), v.end()); } - static void dump(std::span data, const fs::path& file) + static void dump_bytes( + std::span data, const fs::path& file) { auto* f = open_file(file, O_WRONLY | O_CREAT | O_TRUNC, "wb"); if (f == nullptr) @@ -172,7 +174,7 @@ namespace files } const auto bytes_written = - fwrite(data.data(), sizeof(uint8_t), data.size(), f); + fwrite(data.data(), sizeof(std::byte), data.size(), f); const auto write_errno = errno; // Preserve any write-side errno before fclose() can overwrite it. errno = 0; @@ -196,18 +198,25 @@ namespace files } /** - * @brief Writes the content of a string to a file + * @brief Writes the content of a byte span to a file + * + * @param data bytes to write + * @param file the path + */ + static void dump(std::span data, const fs::path& file) + { + dump_bytes(std::as_bytes(data), file); + } + + /** + * @brief Writes the content of a string view to a file * - * @param data string to write + * @param data string view to write * @param file the path */ static void dump(std::string_view data, const fs::path& file) { - dump( - std::span( - reinterpret_cast(data.data()), - data.size()), - file); + dump_bytes(std::as_bytes(std::span(data)), file); } static void rename(const fs::path& src, const fs::path& dst) From 5a63f6a8563cf19261960ef3f001b5c16336bf21 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 2 Jun 2026 21:37:35 +0000 Subject: [PATCH 08/16] Align dump helper overload naming --- src/ds/files.h | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/ds/files.h b/src/ds/files.h index 28fa82bf79b7..1b1514718459 100644 --- a/src/ds/files.h +++ b/src/ds/files.h @@ -161,8 +161,7 @@ namespace files return nlohmann::json::parse(v.begin(), v.end()); } - static void dump_bytes( - std::span data, const fs::path& file) + static void dump(std::span data, const fs::path& file) { auto* f = open_file(file, O_WRONLY | O_CREAT | O_TRUNC, "wb"); if (f == nullptr) @@ -205,7 +204,7 @@ namespace files */ static void dump(std::span data, const fs::path& file) { - dump_bytes(std::as_bytes(data), file); + dump(std::as_bytes(data), file); } /** @@ -216,7 +215,7 @@ namespace files */ static void dump(std::string_view data, const fs::path& file) { - dump_bytes(std::as_bytes(std::span(data)), file); + dump(std::as_bytes(std::span(data)), file); } static void rename(const fs::path& src, const fs::path& dst) From 01e41ea1384432ee9c61c93c4435573049b586ad Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 2 Jun 2026 21:41:16 +0000 Subject: [PATCH 09/16] Format files helper header --- src/ds/files.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ds/files.h b/src/ds/files.h index 1b1514718459..5fffc113c573 100644 --- a/src/ds/files.h +++ b/src/ds/files.h @@ -6,8 +6,8 @@ #include #include #include -#include #include +#include #include #include #include From 87b80c03233957a01e412550fc8dda693d7ed7e1 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 2 Jun 2026 22:12:19 +0000 Subject: [PATCH 10/16] Suppress clang-tidy fclose warning --- src/ds/files.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ds/files.h b/src/ds/files.h index 5fffc113c573..a309a87ec70c 100644 --- a/src/ds/files.h +++ b/src/ds/files.h @@ -177,6 +177,7 @@ namespace files const auto write_errno = errno; // Preserve any write-side errno before fclose() can overwrite it. errno = 0; + // NOLINTNEXTLINE(cppcoreguidelines-owning-memory) const auto close_rc = fclose(f); const auto close_errno = errno; if (bytes_written != data.size() || close_rc != 0) From 6a3d31ff726fb9b404b546f5113f919496fa824a Mon Sep 17 00:00:00 2001 From: Amaury Chamayou Date: Wed, 3 Jun 2026 13:52:46 +0100 Subject: [PATCH 11/16] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- src/ds/files.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ds/files.h b/src/ds/files.h index a309a87ec70c..7a5d8235da38 100644 --- a/src/ds/files.h +++ b/src/ds/files.h @@ -50,6 +50,7 @@ namespace files return nullptr; } + errno = 0; // NOLINTNEXTLINE(cppcoreguidelines-owning-memory) auto* f = fdopen(fd, mode); if (f == nullptr) From 29eb7d31d4b7cde9415785f0fc5a1c1d85111848 Mon Sep 17 00:00:00 2001 From: Amaury Chamayou Date: Wed, 3 Jun 2026 13:52:57 +0100 Subject: [PATCH 12/16] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- src/ds/files.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ds/files.h b/src/ds/files.h index 7a5d8235da38..1ce1ce02c92b 100644 --- a/src/ds/files.h +++ b/src/ds/files.h @@ -173,10 +173,10 @@ namespace files std::strerror(errno))); // NOLINT(concurrency-mt-unsafe) } + errno = 0; const auto bytes_written = fwrite(data.data(), sizeof(std::byte), data.size(), f); const auto write_errno = errno; - // Preserve any write-side errno before fclose() can overwrite it. errno = 0; // NOLINTNEXTLINE(cppcoreguidelines-owning-memory) const auto close_rc = fclose(f); From 97b68fd4ea3dd1feb4522ed9b63658b86a88cdbc Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 3 Jun 2026 13:07:49 +0000 Subject: [PATCH 13/16] Add changelog entry for restrictive file permissions (0600) on host-created files --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 89fa5a612d47..5b74a38e0d35 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - `ccf.ledger` `MERKLE` verification level now also verifies COSE-only ledgers (previously a silent no-op) (#7904). +### Security + +- Host-created files (ledger chunks, snapshots, PID file, and node certificate/key files) are now created with restrictive permissions (`0600`) instead of relying on the process `umask`. Existing deployments will not see existing files affected; only newly created files will have these restricted permissions. + ## [7.0.4] [7.0.4]: https://github.com/microsoft/CCF/releases/tag/ccf-7.0.4 From 44e83bc99490a181edc85af9e23a1573464902b9 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 3 Jun 2026 13:19:15 +0000 Subject: [PATCH 14/16] Rename saved_errno to fdopen_errno for clarity in open_file() --- src/ds/files.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/ds/files.h b/src/ds/files.h index 1ce1ce02c92b..d19e1550f2f6 100644 --- a/src/ds/files.h +++ b/src/ds/files.h @@ -55,14 +55,14 @@ namespace files auto* f = fdopen(fd, mode); if (f == nullptr) { - auto saved_errno = errno; + auto fdopen_errno = errno; // Preserve the original fdopen() failure when it set errno, and only // fall back to close()'s errno if fdopen() did not. - if (close(fd) != 0 && saved_errno == 0) + if (close(fd) != 0 && fdopen_errno == 0) { - saved_errno = errno; + fdopen_errno = errno; } - errno = saved_errno != 0 ? saved_errno : EIO; + errno = fdopen_errno != 0 ? fdopen_errno : EIO; } return f; From cc863b7686b935fb00586c0db055c0cbc5fffa44 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 8 Jun 2026 10:59:34 +0000 Subject: [PATCH 15/16] Move security changelog entry to new [7.0.5] block and bump pyproject.toml version --- CHANGELOG.md | 12 ++++++++---- python/pyproject.toml | 2 +- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a5da10464aee..0110e966ea84 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,14 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). +## [7.0.5] + +[7.0.5]: https://github.com/microsoft/CCF/releases/tag/ccf-7.0.5 + +### Security + +- Host-created files (ledger chunks, snapshots, PID file, and node certificate/key files) are now created with restrictive permissions (`0600`) instead of relying on the process `umask`. Existing deployments will not see existing files affected; only newly created files will have these restricted permissions. + ## [7.0.4] [7.0.4]: https://github.com/microsoft/CCF/releases/tag/ccf-7.0.4 @@ -27,10 +35,6 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - `ccf.ledger` `MERKLE` verification level now also verifies COSE-only ledgers (previously a silent no-op) (#7904). - Nodes started in recovery or join mode from a snapshot more recent than the latest ledger file now correctly resume writing from the snapshot boundary (#7901). -### Security - -- Host-created files (ledger chunks, snapshots, PID file, and node certificate/key files) are now created with restrictive permissions (`0600`) instead of relying on the process `umask`. Existing deployments will not see existing files affected; only newly created files will have these restricted permissions. - ## [7.0.3] [7.0.3]: https://github.com/microsoft/CCF/releases/tag/ccf-7.0.3 diff --git a/python/pyproject.toml b/python/pyproject.toml index b8144b614556..e8809e787664 100644 --- a/python/pyproject.toml +++ b/python/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "ccf" -version = "7.0.4" +version = "7.0.5" authors = [ { name="CCF Team", email="CCF-Sec@microsoft.com" }, ] From a19c3502052ef7fe3146627445e8a3e3c0f84654 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 8 Jun 2026 11:01:51 +0000 Subject: [PATCH 16/16] Add PR number (#7916) to changelog security entry --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0110e966ea84..febfb4931044 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,7 +11,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ### Security -- Host-created files (ledger chunks, snapshots, PID file, and node certificate/key files) are now created with restrictive permissions (`0600`) instead of relying on the process `umask`. Existing deployments will not see existing files affected; only newly created files will have these restricted permissions. +- Host-created files (ledger chunks, snapshots, PID file, and node certificate/key files) are now created with restrictive permissions (`0600`) instead of relying on the process `umask`. Existing deployments will not see existing files affected; only newly created files will have these restricted permissions (#7916). ## [7.0.4]