Safe shell
This commit is contained in:
+2
-1
@@ -51,7 +51,8 @@ int main(int argc, char** argv) {
|
||||
master_registery.register_tool(make_file_read_tool());
|
||||
master_registery.register_tool(make_grep_tool());
|
||||
#if !defined(_WIN32)
|
||||
master_registery.register_tool(make_shell_tool());
|
||||
master_registery.register_tool(make_shell_tool(true)); // run_shell (safe)
|
||||
master_registery.register_tool(make_shell_tool(false)); // run_dangerous_shell
|
||||
#endif
|
||||
// Add more base tools here:
|
||||
|
||||
|
||||
+10
-10
@@ -1,42 +1,42 @@
|
||||
#!/bin/bash
|
||||
# test_tool_shell.sh - Fully validates the run_shell tool
|
||||
|
||||
# Mock response: ask for run_shell
|
||||
tool_call run_shell '{"command":"echo Hello from shell","timeout_seconds":5}' >> mock_responses.txt
|
||||
# Mock response: ask for run_dangerous_shell
|
||||
tool_call run_dangerous_shell '{"command":"echo Hello from shell","timeout_seconds":5}' >> mock_responses.txt
|
||||
be_done 'Shell done' >> mock_responses.txt
|
||||
|
||||
OUTPUT=$("${DRHARNESS}" -F chat.json -n -m "Run shell command" -t run_shell -d < "mock_responses.txt" 2>/dev/null)
|
||||
OUTPUT=$("${DRHARNESS}" -F chat.json -n -m "Run shell command" -t run_dangerous_shell -d < "mock_responses.txt" 2>/dev/null)
|
||||
|
||||
if [ "${OUTPUT}" != "Shell done" ]; then
|
||||
echo "FAIL: run_shell tool - expected 'Shell done', got '${OUTPUT}'"
|
||||
echo "FAIL: run_dangerous_shell tool - expected 'Shell done', got '${OUTPUT}'"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
RESULT=$(dr_result chat.json 2)
|
||||
if ! echo "$RESULT" | grep -q '^Hello from shell$' ; then
|
||||
echo "FAIL: run_shell tool - expected 'Hello from shell' in output"
|
||||
echo "FAIL: run_dangerous_shell tool - expected 'Hello from shell' in output"
|
||||
echo "Got: $RESULT"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Test error: invalid command (exit code != 0)
|
||||
rm -f chat.json
|
||||
tool_call run_shell '{"command":"exit 42"}' >> mock_responses2.txt
|
||||
tool_call run_dangerous_shell '{"command":"exit 42"}' >> mock_responses2.txt
|
||||
be_done 'Error handled' >> mock_responses2.txt
|
||||
|
||||
"${DRHARNESS}" -F chat.json -n -m "Run invalid shell" -t run_shell -d < "mock_responses2.txt" >/dev/null 2>&1
|
||||
"${DRHARNESS}" -F chat.json -n -m "Run invalid shell" -t run_dangerous_shell -d < "mock_responses2.txt" >/dev/null 2>&1
|
||||
TOOL_RESULT=$(dr_result chat.json 2)
|
||||
|
||||
if ! echo "${TOOL_RESULT}" | grep -q "\[exit 42\]"; then
|
||||
echo "FAIL: run_shell tool - expected exit code in output"
|
||||
echo "FAIL: run_dangerous_shell tool - expected exit code in output"
|
||||
echo "Got: ${TOOL_RESULT}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
tool_call run_shell '{"command":"cat /dev/urandom > /dev/null","timeout_seconds":1}' >> mock_responses3.txt
|
||||
tool_call run_dangerous_shell '{"command":"cat /dev/urandom > /dev/null","timeout_seconds":1}' >> mock_responses3.txt
|
||||
be_done 'done' >> mock_responses3.txt
|
||||
|
||||
# If we hit the timeout of the timeout utility then we fail
|
||||
timeout 5s "${DRHARNESS}" -F chat3.json -n -m "This is a test" -t run_shell -d < "mock_responses3.txt"
|
||||
timeout 5s "${DRHARNESS}" -F chat3.json -n -m "This is a test" -t run_dangerous_shell -d < "mock_responses3.txt"
|
||||
|
||||
echo "PASS: tool_shell"
|
||||
|
||||
+15
-3
@@ -2,6 +2,7 @@
|
||||
#include "json_types.hpp"
|
||||
#include <array>
|
||||
#include <cstdio>
|
||||
#include <iostream>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <sys/wait.h>
|
||||
@@ -10,9 +11,11 @@
|
||||
#include <signal.h>
|
||||
#include <cstring>
|
||||
|
||||
Tool make_shell_tool() {
|
||||
Tool make_shell_tool(bool safe) {
|
||||
std::string tool_name = safe ? "run_shell" : "run_dangerous_shell";
|
||||
|
||||
return Tool{
|
||||
.name = "run_shell",
|
||||
.name = tool_name,
|
||||
.description = "Run a shell command and return its stdout and stderr. "
|
||||
"Use for inspecting files, running programs, or querying the system.",
|
||||
.parameters_schema = {
|
||||
@@ -32,9 +35,18 @@ Tool make_shell_tool() {
|
||||
.convert_params = [](const nlohmann::json& args) -> std::any {
|
||||
return ShellToolParams(args.get<ShellToolParams>());
|
||||
},
|
||||
.execute = [](const std::any& args) -> std::string {
|
||||
.execute = [safe](const std::any& args) -> std::string {
|
||||
auto params = std::any_cast<ShellToolParams>(args);
|
||||
|
||||
if (safe) {
|
||||
std::cerr << "Approve command: " << params.command << " ? [y/N]: ";
|
||||
char response = 0;
|
||||
std::cin.get(response);
|
||||
if (response != 'y' && response != 'Y') {
|
||||
return "Execution cancelled by user.";
|
||||
}
|
||||
}
|
||||
|
||||
// 1. Create a pipe for stdout
|
||||
int pipefd[2];
|
||||
if (pipe(pipefd) < 0) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#pragma once
|
||||
#include "tool.hpp"
|
||||
|
||||
Tool make_shell_tool();
|
||||
Tool make_shell_tool(bool safe);
|
||||
|
||||
Reference in New Issue
Block a user