diff --git a/src/controls/switch_node.cpp b/src/controls/switch_node.cpp index e43e70a4e..2bc00e4dc 100644 --- a/src/controls/switch_node.cpp +++ b/src/controls/switch_node.cpp @@ -39,13 +39,15 @@ bool CheckStringEquality(const std::string& v1, const std::string& v2, } } #if __cpp_lib_to_chars >= 201611L - auto [ptr, ec] = std::from_chars(str.data(), str.data() + str.size(), result); - return (ec == std::errc()); + const char* end = str.data() + str.size(); + auto [ptr, ec] = std::from_chars(str.data(), end, result); + return ec == std::errc() && ptr == end; #else try { - result = std::stoi(str); - return true; + std::size_t pos = 0; + result = std::stoi(str, &pos); + return pos == str.size(); } catch(...) { @@ -60,15 +62,26 @@ bool CheckStringEquality(const std::string& v1, const std::string& v2, return true; } // compare as real numbers next - auto ToReal = [](const std::string& str, auto& result) -> bool { + auto ToReal = [enums](const std::string& str, auto& result) -> bool { + if(enums) + { + auto it = enums->find(str); + if(it != enums->end()) + { + result = it->second; + return true; + } + } #if __cpp_lib_to_chars >= 201611L - auto [ptr, ec] = std::from_chars(str.data(), str.data() + str.size(), result); - return (ec == std::errc()); + const char* end = str.data() + str.size(); + auto [ptr, ec] = std::from_chars(str.data(), end, result); + return ec == std::errc() && ptr == end; #else try { - result = std::stod(str); - return true; + std::size_t pos = 0; + result = std::stod(str, &pos); + return pos == str.size(); } catch(...) { diff --git a/tests/gtest_switch.cpp b/tests/gtest_switch.cpp index 90ae19c5d..ac75b3c61 100644 --- a/tests/gtest_switch.cpp +++ b/tests/gtest_switch.cpp @@ -3,6 +3,7 @@ #include "behaviortree_cpp/behavior_tree.h" #include "behaviortree_cpp/bt_factory.h" +#include "behaviortree_cpp/controls/switch_node.h" #include "behaviortree_cpp/tree_node.h" #include @@ -245,3 +246,21 @@ TEST_F(SwitchTest, ActionFailure) ASSERT_EQ(NodeStatus::IDLE, action_42.status()); ASSERT_EQ(NodeStatus::IDLE, action_def.status()); } + +TEST(SwitchStringEquality, RejectsTrailingCharacters) +{ + using BT::details::CheckStringEquality; + + // legitimate matches must keep working + EXPECT_TRUE(CheckStringEquality("5", "5", nullptr)); + EXPECT_TRUE(CheckStringEquality("5", "5.0", nullptr)); + EXPECT_TRUE(CheckStringEquality("42", "42", nullptr)); + EXPECT_TRUE(CheckStringEquality("-7", "-7", nullptr)); + + // a selector with trailing bytes must not match a numeric case + EXPECT_FALSE(CheckStringEquality("5abc", "5", nullptr)); + EXPECT_FALSE(CheckStringEquality("42xxxx", "42", nullptr)); + EXPECT_FALSE(CheckStringEquality("1.0junk", "1.0", nullptr)); + EXPECT_FALSE(CheckStringEquality("5 ", "5", nullptr)); + EXPECT_FALSE(CheckStringEquality("none", "1", nullptr)); +}