// Tests for oatpp-authkit/util/TokenExtract.hpp — exact-name cookie parsing // (authkit#16 M-1) and isValidIp. #include "oatpp-authkit/util/TokenExtract.hpp" #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; void test_cookie_exact_name_match() { // Basic. REQUIRE(cookieValue("session=abc", "session") == "abc"); REQUIRE(cookieValue("session=abc; other=1", "session") == "abc"); REQUIRE(cookieValue("other=1; session=abc", "session") == "abc"); REQUIRE(cookieValue("other=1; session=abc; more=2", "session") == "abc"); // OWS trimming around the pair and value. REQUIRE(cookieValue("a=1; session=abc ; b=2", "session") == "abc"); // The substring trap: a prefixed/suffixed cookie name must NOT match. REQUIRE(cookieValue("xsession=evil", "session") == ""); REQUIRE(cookieValue("notsession=evil", "session") == ""); REQUIRE(cookieValue("my_session=evil", "session") == ""); // Attacker plants a sibling cookie before the real one: exact match still // returns the genuine session value, not the shadow. REQUIRE(cookieValue("xsession=evil; session=real", "session") == "real"); REQUIRE(cookieValue("session=real; xsession=evil", "session") == "real"); // Missing / empty. REQUIRE(cookieValue("", "session") == ""); REQUIRE(cookieValue("foo=bar", "session") == ""); REQUIRE(cookieValue("session=", "session") == ""); // __Host- prefixed name is matched only as an exact name. REQUIRE(cookieValue("__Host-session=tok", "__Host-session") == "tok"); REQUIRE(cookieValue("__Host-session=tok", "session") == ""); } void test_session_cookie_token() { // Bare `session` name resolves. REQUIRE(sessionCookieToken("session=abc") == "abc"); REQUIRE(sessionCookieToken("other=1; session=abc; more=2") == "abc"); // Regression: the `__Host-session` hardened name must also resolve — the // exact-name parse (M-1) otherwise silently 401s consumers that renamed // their session cookie to `__Host-session`. REQUIRE(sessionCookieToken("__Host-session=tok") == "tok"); REQUIRE(sessionCookieToken("other=1; __Host-session=tok") == "tok"); REQUIRE(sessionCookieToken("__Host-session=tok; x=1") == "tok"); // The bare name is preferred when both are somehow present. REQUIRE(sessionCookieToken("session=real; __Host-session=other") == "real"); // Substring traps still don't match either recognised name. REQUIRE(sessionCookieToken("x__Host-session=evil") == ""); REQUIRE(sessionCookieToken("xsession=evil") == ""); REQUIRE(sessionCookieToken("") == ""); REQUIRE(sessionCookieToken("foo=bar") == ""); } void test_is_valid_ip() { REQUIRE(isValidIp("192.168.1.1")); REQUIRE(isValidIp("::1")); REQUIRE(isValidIp("2001:db8::1")); REQUIRE(!isValidIp("192.168.1.256")); REQUIRE(!isValidIp("1.1.1.1; rm -rf")); REQUIRE(!isValidIp("")); REQUIRE(!isValidIp(std::string(46, 'a'))); // over length cap } } // namespace int main() { test_cookie_exact_name_match(); test_session_cookie_token(); test_is_valid_ip(); std::printf("%s (%d failures)\n", g_failures ? "FAIL" : "OK", g_failures); return g_failures ? 1 : 0; }