// Tests for oatpp-authkit/util/SessionCookie.hpp (authkit#16 M-9). #include "oatpp-authkit/util/SessionCookie.hpp" #include #include #include namespace { int g_failures = 0; #define REQUIRE(expr) do { \ if (!(expr)) { \ std::fprintf(stderr, "FAIL %s:%d %s\n", __FILE__, __LINE__, #expr); \ ++g_failures; \ } \ } while (0) using namespace oatpp_authkit; bool has(const std::string& hay, const std::string& needle) { return hay.find(needle) != std::string::npos; } void test_defaults_are_hardened() { std::string c = buildSetSessionCookie("tok123"); REQUIRE(has(c, "session=tok123")); REQUIRE(has(c, "Path=/")); REQUIRE(has(c, "HttpOnly")); REQUIRE(has(c, "Secure")); REQUIRE(has(c, "SameSite=Strict")); REQUIRE(!has(c, "Max-Age")); // session cookie by default } void test_options_respected() { SessionCookieOptions o; o.name = "__Host-session"; o.secure = false; // dev opt-out o.sameSite = "Lax"; o.maxAgeSeconds = 3600; std::string c = buildSetSessionCookie("t", o); REQUIRE(has(c, "__Host-session=t")); REQUIRE(!has(c, "Secure")); REQUIRE(has(c, "SameSite=Lax")); REQUIRE(has(c, "Max-Age=3600")); } void test_clear_cookie_expires_now() { std::string c = buildClearSessionCookie(); REQUIRE(has(c, "Max-Age=0")); REQUIRE(has(c, "session=")); } void test_injection_guard() { bool threw = false; try { buildSetSessionCookie("tok\r\nSet-Cookie: evil=1"); } catch (const std::invalid_argument&) { threw = true; } REQUIRE(threw); bool threw2 = false; try { buildSetSessionCookie("tok; Domain=evil.com"); } // ';' injection catch (const std::invalid_argument&) { threw2 = true; } REQUIRE(threw2); } } // namespace int main() { test_defaults_are_hardened(); test_options_respected(); test_clear_cookie_expires_now(); test_injection_guard(); std::printf("%s (%d failures)\n", g_failures ? "FAIL" : "OK", g_failures); return g_failures ? 1 : 0; }