diff options
| author | Leitwolf11 | 2026-11-05 18:30:13 +0300 |
|---|---|---|
| committer | Leitwolf11 | 2026-11-05 18:30:13 +0300 |
| commit | 89b4769c35986444a0297ad0806a584c7181c85c (patch) | |
| tree | 2ec3dcb30828ef3749c503eb58e401b918beae7e /src/Progmasoft/Runner/Process.cpp | |
| parent | 268698be92962247a21a86dda4de2d4267adcf8f (diff) | |
| download | catch3-89b4769c35986444a0297ad0806a584c7181c85c.tar.gz | |
Withhold secret-looking variables from the processes the runner starts
Workers and the other processes the runner starts copied its whole
environment, tokens and passwords included. ChildProcess::Start now
leaves out every variable whose name contains, in any case, TOKEN,
SECRET, PASSWORD, PASSWD, CREDENTIAL, API_KEY, APIKEY, PRIVATE_KEY or
ACCESS_KEY (IsSecretVariable), unless ChildOptions::keptEnvironment
names it; names compare without case on Windows.
--pass-env=NAME, repeatable, and "pass-env" in catch3.json keep a
variable for the tests. The runner itself still starts with the whole
environment, so code that runs before the first worker sees it;
docs/progmasoft-runner.md says so.
A runner test starts the shell's environment listing with a secret and
a plain variable set, sees only the plain one, and sees the secret once
it is kept.
Diffstat (limited to 'src/Progmasoft/Runner/Process.cpp')
| -rw-r--r-- | src/Progmasoft/Runner/Process.cpp | 85 |
1 files changed, 83 insertions, 2 deletions
diff --git a/src/Progmasoft/Runner/Process.cpp b/src/Progmasoft/Runner/Process.cpp index c1d01302..b9463e94 100644 --- a/src/Progmasoft/Runner/Process.cpp +++ b/src/Progmasoft/Runner/Process.cpp @@ -1,9 +1,12 @@ // Copyright (c) 2026 Progmasoft. // SPDX-License-Identifier: MPL-2.0 WITH AdditionRef-Progmasoft-Exception-2.0 #include <algorithm> +#include <cctype> #include <cstdio> #include <cstdlib> #include <cstring> +#include <iterator> +#include <string_view> #include <thread> #include "Process.hpp" @@ -39,8 +42,64 @@ extern char **environ; namespace Progmasoft::Catch3::Runner { + bool + IsSecretVariable(std::string_view name) + { + std::string upper; + upper.reserve(name.size()); + for (const char character : name) + { + upper.push_back(character >= 'a' && character <= 'z' + ? static_cast<char>(character - 'a' + 'A') + : character); + } + constexpr std::string_view kSecretWords[] = { + "TOKEN", "SECRET", "PASSWORD", "PASSWD", "CREDENTIAL", + "API_KEY", "APIKEY", "PRIVATE_KEY", "ACCESS_KEY", + }; + return std::any_of(std::begin(kSecretWords), + std::end(kSecretWords), + [&](std::string_view word) { + return upper.find(word) != std::string::npos; + }); + } + namespace { + /// Whether a variable of this process stays out of a child: a secret + /// that the options do not keep. Windows spells names in any case. + bool + Withheld(std::string_view name, const std::vector<std::string> &kept) + { + if (!IsSecretVariable(name)) + { + return false; + } + return std::none_of( + kept.begin(), + kept.end(), + [&](const std::string &keptName) { + if (keptName.size() != name.size()) + { + return false; + } +#if defined(_WIN32) + return std::equal( + keptName.begin(), + keptName.end(), + name.begin(), + [](char left, char right) { + return std::toupper( + static_cast<unsigned char>(left)) + == std::toupper( + static_cast<unsigned char>(right)); + }); +#else + return keptName == name; +#endif + }); + } + #if defined(_WIN32) HANDLE AsHandle(std::intptr_t value) noexcept @@ -175,7 +234,8 @@ namespace Progmasoft::Catch3::Runner std::wstring EnvironmentBlock( - const std::vector<std::pair<std::string, std::string>> &additions) + const std::vector<std::pair<std::string, std::string>> &additions, + const std::vector<std::string> &kept) { std::vector<std::wstring> entries; wchar_t *current = GetEnvironmentStringsW(); @@ -184,6 +244,20 @@ namespace Progmasoft::Catch3::Runner for (const wchar_t *entry = current; *entry != L'\0'; entry += wcslen(entry) + 1) { + // A leading '=' marks the hidden per-drive variables. + const std::wstring_view text{ entry }; + const auto equals = text.find(L'=', 1); + std::string name; + for (const wchar_t character : text.substr(0, equals)) + { + name.push_back(character < 0x80 + ? static_cast<char>(character) + : '?'); + } + if (Withheld(name, kept)) + { + continue; + } entries.emplace_back(entry); } FreeEnvironmentStringsW(current); @@ -634,7 +708,8 @@ namespace Progmasoft::Catch3::Runner } AppendQuotedArgument(commandLine, Widen(options.arguments[index])); } - std::wstring environment = EnvironmentBlock(options.environment); + std::wstring environment + = EnvironmentBlock(options.environment, options.keptEnvironment); const std::wstring directory = options.workingDirectory.wstring(); PROCESS_INFORMATION information{}; @@ -957,6 +1032,12 @@ namespace Progmasoft::Catch3::Runner entry != nullptr && *entry != nullptr; ++entry) { + const std::string_view text{ *entry }; + if (Withheld(text.substr(0, text.find('=')), + options.keptEnvironment)) + { + continue; + } environmentStorage.emplace_back(*entry); } for (const auto &[name, value] : options.environment) |