From 63a4998a183cb4358d8a51fb83d330bf945fe819 Mon Sep 17 00:00:00 2001 From: maxdcb <40819564+maxDcb@users.noreply.github.com> Date: Thu, 30 Apr 2026 09:58:36 +0200 Subject: [PATCH] Shell CI tests --- modules/Shell/tests/testsShell.cpp | 122 +++++++++++++++++++++++++---- 1 file changed, 107 insertions(+), 15 deletions(-) diff --git a/modules/Shell/tests/testsShell.cpp b/modules/Shell/tests/testsShell.cpp index 0d5a082..d9a0176 100644 --- a/modules/Shell/tests/testsShell.cpp +++ b/modules/Shell/tests/testsShell.cpp @@ -1,25 +1,117 @@ #include "../Shell.hpp" +#include "../../tests/TestHelpers.hpp" + #include +#include +#include + +namespace +{ + bool expectContains(const std::string& text, const std::string& needle, const std::string& message) + { + return test_helpers::expect(text.find(needle) != std::string::npos, message); + } + + C2Message initShell(Shell& shell, std::vector command, bool& ok) + { + C2Message message; + ok &= test_helpers::expect(shell.init(command, message) == 0, command.front() + " init should succeed"); + return message; + } + + C2Message runShellCommand(Shell& shell, std::vector command, bool& ok) + { + C2Message message = initShell(shell, command, ok); + C2Message ret; + ok &= test_helpers::expect(shell.process(message, ret) == 0, message.cmd() + " process should succeed"); + ok &= test_helpers::expect(ret.instruction() == "shell", message.cmd() + " return instruction should be set"); + return ret; + } + + std::vector stateSetCommand() + { +#ifdef _WIN32 + return {"shell", "set", "C2CORE_SHELL_STATE=alpha&", "echo", "c2core-state-set"}; +#else + return {"shell", "export", "C2CORE_SHELL_STATE=alpha;", "echo", "c2core-state-set"}; +#endif + } + + std::vector stateReadCommand() + { +#ifdef _WIN32 + return {"shell", "echo", "%C2CORE_SHELL_STATE%"}; +#else + return {"shell", "printf", "'%s\\n'", "\"$C2CORE_SHELL_STATE\""}; +#endif + } +} int main() { - Shell shell; - std::vector cmd = {"shell"}; - C2Message msg, ret; - shell.init(cmd, msg); - shell.process(msg, ret); + bool ok = true; - cmd = {"shell", "echo", "hello"}; - shell.init(cmd, msg); - msg.set_cmd("echo hello"); - shell.process(msg, ret); - bool ok = ret.returnvalue().find("hello") != std::string::npos; + { + Shell shell; + std::vector command = {"shell"}; + C2Message message; + ok &= test_helpers::expect(shell.init(command, message) == 0, "empty shell command init should succeed"); + ok &= test_helpers::expect(message.instruction() == "shell", "instruction should be set"); + ok &= test_helpers::expect(message.cmd().empty(), "empty shell command should not set a command payload"); - cmd = {"shell", "exit"}; - shell.init(cmd, msg); - msg.set_cmd("exit"); - shell.process(msg, ret); + command = {"shell", "echo", "hello"}; + message = C2Message{}; + ok &= test_helpers::expect(shell.init(command, message) == 0, "positional command init should succeed"); + ok &= test_helpers::expect(message.cmd() == "echo hello", "positional command should be packed without trailing spaces"); - std::cout << (ok ? "[+]" : "[-]") << " shell test" << std::endl; + command = {"shell", "cmd", "/c", "echo", "hello"}; + message = C2Message{}; + ok &= test_helpers::expect(shell.init(command, message) == 0, "multi-token command init should succeed"); + ok &= test_helpers::expect(message.cmd() == "cmd /c echo hello", "multi-token command should preserve token order"); + + C2Message ret; + std::string errorMsg = "unchanged"; + ok &= test_helpers::expect(shell.followUp(ret) == 0, "followUp should be a no-op"); + ok &= test_helpers::expect(shell.errorCodeToMsg(ret, errorMsg) == 0, "errorCodeToMsg should be a no-op"); + ok &= test_helpers::expect(errorMsg == "unchanged", "errorCodeToMsg should leave the supplied message unchanged"); + } + + { + Shell shell; + C2Message ret = runShellCommand(shell, {"shell"}, ok); + ok &= test_helpers::expect(ret.returnvalue() == "shell started", "empty command should start the persistent shell"); + + ret = runShellCommand(shell, stateSetCommand(), ok); + ok &= expectContains(ret.returnvalue(), "c2core-state-set", "first command should run in the persistent shell"); + + ret = runShellCommand(shell, stateReadCommand(), ok); + ok &= expectContains(ret.returnvalue(), "alpha", "state set by a previous command should persist"); + + ret = runShellCommand(shell, {"shell", "echo", "c2core-second-command"}, ok); + ok &= expectContains(ret.returnvalue(), "c2core-second-command", "a later command should still use the same shell"); + + ret = runShellCommand(shell, {"shell", "exit"}, ok); + ok &= test_helpers::expect(ret.returnvalue() == "shell terminated", "exit command should stop the shell"); + + ret = runShellCommand(shell, {"shell"}, ok); + ok &= test_helpers::expect(ret.returnvalue() == "shell started", "shell should be restartable after exit"); + + ret = runShellCommand(shell, {"shell", "exit"}, ok); + ok &= test_helpers::expect(ret.returnvalue() == "shell terminated", "second exit command should stop the restarted shell"); + } + +#ifdef _WIN32 + { + Shell shell; + bool branchOk = true; + C2Message message = initShell(shell, {"shell", "definitely_missing_c2_shell_program.exe"}, branchOk); + C2Message ret; + branchOk &= test_helpers::expect(shell.process(message, ret) == 0, "missing shell process should return through C2Message"); + branchOk &= test_helpers::expect(ret.errorCode() == 1, "missing shell program should set error code 1"); + ok &= branchOk; + } +#endif + + std::cout << (ok ? "[+]" : "[-]") << " shell tests" << std::endl; return ok ? 0 : 1; }