From 89423c072f93f6c1a0a38bc9c830c0355c6256ec Mon Sep 17 00:00:00 2001 From: Pavan Kumar Reddy Balireddy Date: Wed, 16 Sep 2026 09:46:13 +0530 Subject: [PATCH] fix(jst): harden session handling and request parsing - revalidate active sessions and prevent stale proxy persistence - handle failed session creation and startup safely - expire DUKSID on logout and preserve secure-cookie behavior - parse standalone DUKSID cookie fields only - retain equals signs in GET, POST, and file metadata values - add prefix-runtime regression tests and synchronize parser fixtures --- jsts/jst_prefix.js | 109 ++-- tests/parser/jst_parser_backslash.jst.parsed | 181 ++++++- .../parser/jst_parser_comment_tag.jst.parsed | 181 ++++++- .../jst_parser_include_code_after.jst.parsed | 181 ++++++- .../jst_parser_include_code_before.jst.parsed | 181 ++++++- .../jst_parser_include_malformed_1.jst.parsed | 181 ++++++- .../jst_parser_include_nested.jst.parsed | 181 ++++++- ...include_not_if_in_block_comment.jst.parsed | 181 ++++++- ...arser_include_not_if_in_content.jst.parsed | 181 ++++++- ..._include_not_if_in_line_comment.jst.parsed | 181 ++++++- .../parser/jst_parser_include_once.jst.parsed | 181 ++++++- .../jst_parser_include_runtime.jst.parsed | 181 ++++++- .../jst_parser_include_unknown.jst.parsed | 181 ++++++- tests/parser/jst_parser_line_feeds.jst.parsed | 181 ++++++- .../jst_parser_single_quotes.jst.parsed | 181 ++++++- .../jst_parser_skip_whitespace.jst.parsed | 181 ++++++- ...t_parser_template_block_content.jst.parsed | 181 ++++++- ...st_parser_template_block_string.jst.parsed | 181 ++++++- tests/parser/jst_prefix.js | 164 +++++- tests/parser_test.cpp | 492 +++++++++++++++++- 20 files changed, 3464 insertions(+), 378 deletions(-) diff --git a/jsts/jst_prefix.js b/jsts/jst_prefix.js index 8f3a778..61ed913 100644 --- a/jsts/jst_prefix.js +++ b/jsts/jst_prefix.js @@ -89,69 +89,104 @@ var $_SERVER = new Proxy({}, { var $_SESSION = {}; var $_jst_session = null; var $_val_input = {}; +function _jst_session_cookie() +{ + var $cookie = "Set-Cookie: DUKSID=" + ccsp_session.getId() + "; httponly"; + if(ccsp_session.isSecure()) + $cookie += "; secure"; + return $cookie; +} +function _jst_expire_session_cookie() +{ + var $cookie = "Set-Cookie: DUKSID=; Max-Age=0; httponly"; + if(ccsp_session.isSecure()) + $cookie += "; secure"; + return $cookie; +} function session_start() { if($_jst_session) - return; - if($_val_input == 1) + { + if(ccsp_session.start()) + return true; + + $_jst_session = null; + $_SESSION = {}; + return false; + } + if($_val_input == 1) { $_val_input = 0; - return; + return false; + } + if(!ccsp_session.start()) + { + /* A stale cookie must not create a proxy backed by an inactive session. */ + $_jst_session = null; + $_SESSION = {}; + return false; + } + if(ccsp_session.getStatus()) + { + header(_jst_session_cookie()); } - ccsp_session.start(); - var host = getenv('HTTPS'); - if (host == false) - var $cookie = "Set-Cookie: DUKSID=" + ccsp_session.getId() + "; httponly"; - else - var $cookie = "Set-Cookie: DUKSID=" + ccsp_session.getId() + "; secure" + "; httponly"; - header($cookie); $_jst_session = ccsp_session.getData(); + if($_jst_session === null || typeof($_jst_session) !== 'object') + $_jst_session = {}; $_SESSION = new Proxy($_jst_session, { get: function(obj, prop) { return obj[prop]; }, set: function(obj, prop, val){ obj[prop] = val; - ccsp_session.setData(obj); + if(ccsp_session.getStatus()) + ccsp_session.setData(obj); return true; }, deleteProperty(obj, prop) { if(prop in obj) { delete obj[prop]; - ccsp_session.setData(obj); + if(ccsp_session.getStatus()) + ccsp_session.setData(obj); } return true; } }); + return true; } function session_create(){ - ccsp_session.create(); - var host = getenv('HTTPS'); - if (host == false) - var $cookie = "Set-Cookie: DUKSID=" + ccsp_session.getId() + "; httponly"; - else - var $cookie = "Set-Cookie: DUKSID=" + ccsp_session.getId() + "; secure" + "; httponly"; - header($cookie); + if(!ccsp_session.create()) + { + $_jst_session = null; + $_SESSION = {}; + return false; + } + header(_jst_session_cookie()); $_jst_session = ccsp_session.getData(); + if($_jst_session === null || typeof($_jst_session) !== 'object') + $_jst_session = {}; $_SESSION = new Proxy($_jst_session, { get: function(obj, prop) { return obj[prop]; }, set: function(obj, prop, val){ obj[prop] = val; - ccsp_session.setData(obj); + if(ccsp_session.getStatus()) + ccsp_session.setData(obj); return true; }, deleteProperty(obj, prop) { if(prop in obj) { delete obj[prop]; - ccsp_session.setData(obj); + if(ccsp_session.getStatus()) + ccsp_session.setData(obj); } return true; } }); + return true; } function session_id() { @@ -163,6 +198,7 @@ function session_status() } function session_destroy() { + header(_jst_expire_session_cookie()); delete $_jst_session; $_jst_session = null; delete $_SESSION; @@ -170,7 +206,14 @@ function session_destroy() return ccsp_session.destroy(); } function session_unset() -{//FIXME +{ + if(!$_jst_session || !ccsp_session.getStatus()) + return false; + + for(var $key in $_jst_session) + delete $_jst_session[$key]; + + return ccsp_session.setData($_jst_session); } function session_print() { @@ -186,11 +229,11 @@ if(postData) var postValues = postData.split('&'); for(var i = 0; i < postValues.length; ++i) { - var postValue = postValues[i].split('='); - if(postValue.length == 2) + var eqIdx = postValues[i].indexOf('='); + if(eqIdx != -1) { - var value = postValue[1].replace(/[+]/g," "); - $_POST[postValue[0]] = decodeURIComponent(value); + var value = postValues[i].substring(eqIdx + 1).replace(/[+]/g," "); + $_POST[postValues[i].substring(0, eqIdx)] = decodeURIComponent(value); } else { @@ -212,17 +255,17 @@ if(filesData) var fileId = null; for(var j = 0; j < fileData.length; ++j) { - var fileValue = fileData[j].split('='); - if(fileValue.length == 2) + var eqIdx = fileData[j].indexOf('='); + if(eqIdx != -1) { if(!fileId) { - fileId = decodeURIComponent(fileValue[1]); + fileId = decodeURIComponent(fileData[j].substring(eqIdx + 1)); $_FILES[fileId]={}; } else { - $_FILES[fileId][decodeURIComponent(fileValue[0])]=decodeURIComponent(fileValue[1]); + $_FILES[fileId][decodeURIComponent(fileData[j].substring(0, eqIdx))]=decodeURIComponent(fileData[j].substring(eqIdx + 1)); } } else @@ -241,10 +284,10 @@ $_GET= (function () var ar = qs.split('&'); for(var i=0; i(prefix_file)), + std::istreambuf_iterator()); + prefix += "\n} catch (e) { throw e; }\n"; + + ASSERT_EQ(duk_peval_lstring(ctx, prefix.c_str(), prefix.length()), DUK_EXEC_SUCCESS) + << duk_safe_to_string(ctx, -1); + duk_pop(ctx); +} + +static bool evaluateJavaScriptBoolean(duk_context* ctx, const char* source) +{ + if (duk_peval_string(ctx, source) != DUK_EXEC_SUCCESS) + { + duk_pop(ctx); + return false; + } + + const bool result = duk_get_boolean(ctx, -1); + duk_pop(ctx); + return result; +} + +static std::string evaluateJavaScriptString(duk_context* ctx, const char* source) +{ + if (duk_peval_string(ctx, source) != DUK_EXEC_SUCCESS) + { + duk_pop(ctx); + return ""; + } + + const char* result = duk_get_string(ctx, -1); + std::string value = result ? result : ""; + duk_pop(ctx); + return value; +} + int recurseDirectory(const string& path, vector& files, const string& match) { DIR *dir; @@ -346,11 +444,66 @@ TEST(general, session_create_destroy_cycle_and_id_format) duk_destroy_heap(ctx); } +TEST(general, session_is_secure_detects_request_scheme) +{ + EnvVarGuard https_guard("HTTPS"); + EnvVarGuard request_scheme_guard("REQUEST_SCHEME"); + EnvVarGuard ssl_protocol_guard("SSL_PROTOCOL"); + https_guard.set(nullptr); + request_scheme_guard.set(nullptr); + ssl_protocol_guard.set(nullptr); + + duk_context* ctx = duk_create_heap_default(); + ASSERT_NE(ctx, nullptr); + + duk_push_c_function(ctx, ccsp_session_module_open, 0); + ASSERT_EQ(duk_pcall(ctx, 0), DUK_EXEC_SUCCESS); + duk_put_global_string(ctx, "ccsp_session"); + + duk_get_global_string(ctx, "ccsp_session"); + duk_get_prop_string(ctx, -1, "isSecure"); + ASSERT_EQ(duk_pcall(ctx, 0), DUK_EXEC_SUCCESS); + EXPECT_FALSE(duk_get_boolean(ctx, -1)); + duk_pop_2(ctx); + + https_guard.set("on"); + duk_get_global_string(ctx, "ccsp_session"); + duk_get_prop_string(ctx, -1, "isSecure"); + ASSERT_EQ(duk_pcall(ctx, 0), DUK_EXEC_SUCCESS); + EXPECT_TRUE(duk_get_boolean(ctx, -1)); + duk_pop_2(ctx); + + https_guard.set("off"); + request_scheme_guard.set("https"); + duk_get_global_string(ctx, "ccsp_session"); + duk_get_prop_string(ctx, -1, "isSecure"); + ASSERT_EQ(duk_pcall(ctx, 0), DUK_EXEC_SUCCESS); + EXPECT_TRUE(duk_get_boolean(ctx, -1)); + duk_pop_2(ctx); + + request_scheme_guard.set("http"); + ssl_protocol_guard.set("TLSv1.3"); + duk_get_global_string(ctx, "ccsp_session"); + duk_get_prop_string(ctx, -1, "isSecure"); + ASSERT_EQ(duk_pcall(ctx, 0), DUK_EXEC_SUCCESS); + EXPECT_TRUE(duk_get_boolean(ctx, -1)); + duk_pop_2(ctx); + + ssl_protocol_guard.set(nullptr); + duk_get_global_string(ctx, "ccsp_session"); + duk_get_prop_string(ctx, -1, "isSecure"); + ASSERT_EQ(duk_pcall(ctx, 0), DUK_EXEC_SUCCESS); + EXPECT_FALSE(duk_get_boolean(ctx, -1)); + duk_pop_2(ctx); + + duk_destroy_heap(ctx); +} + TEST(general, session_start_accepts_existing_valid_cookie_id) { EnvVarGuard cookie_guard("HTTP_COOKIE"); const std::string session_id = makeValidSessionId('A'); - const std::string cookie = "DUKSID=" + session_id; + const std::string cookie = "theme=dark; DUKSID=" + session_id + "; lang=en"; const std::string session_file = "/tmp/" + session_id; FILE* file = fopen(session_file.c_str(), "w"); @@ -388,6 +541,35 @@ TEST(general, session_start_accepts_existing_valid_cookie_id) duk_destroy_heap(ctx); } +TEST(general, session_start_rejects_embedded_duksid_cookie_name) +{ + EnvVarGuard cookie_guard("HTTP_COOKIE"); + const std::string session_id = makeValidSessionId('G'); + const std::string cookie = "OTHERDUKSID=" + session_id; + const std::string session_file = "/tmp/" + session_id; + + FILE* file = fopen(session_file.c_str(), "w"); + ASSERT_NE(file, nullptr); + fclose(file); + cookie_guard.set(cookie.c_str()); + + duk_context* ctx = duk_create_heap_default(); + ASSERT_NE(ctx, nullptr); + + duk_push_c_function(ctx, ccsp_session_module_open, 0); + ASSERT_EQ(duk_pcall(ctx, 0), DUK_EXEC_SUCCESS); + duk_put_global_string(ctx, "ccsp_session"); + + duk_get_global_string(ctx, "ccsp_session"); + duk_get_prop_string(ctx, -1, "start"); + ASSERT_EQ(duk_pcall(ctx, 0), DUK_EXEC_SUCCESS); + EXPECT_FALSE(duk_get_boolean(ctx, -1)); + duk_pop_2(ctx); + + duk_destroy_heap(ctx); + unlink(session_file.c_str()); +} + TEST(general, session_start_rejects_invalid_cookie_ids) { const std::vector cookies = { @@ -425,6 +607,149 @@ TEST(general, session_start_rejects_invalid_cookie_ids) } } +TEST(general, session_start_rejects_missing_cookie) +{ + EnvVarGuard cookie_guard("HTTP_COOKIE"); + cookie_guard.set(nullptr); + + duk_context* ctx = duk_create_heap_default(); + ASSERT_NE(ctx, nullptr); + + duk_push_c_function(ctx, ccsp_session_module_open, 0); + ASSERT_EQ(duk_pcall(ctx, 0), DUK_EXEC_SUCCESS); + duk_put_global_string(ctx, "ccsp_session"); + + duk_get_global_string(ctx, "ccsp_session"); + duk_get_prop_string(ctx, -1, "start"); + ASSERT_EQ(duk_pcall(ctx, 0), DUK_EXEC_SUCCESS); + EXPECT_FALSE(duk_get_boolean(ctx, -1)); + duk_pop_2(ctx); + + duk_get_global_string(ctx, "ccsp_session"); + duk_get_prop_string(ctx, -1, "getStatus"); + ASSERT_EQ(duk_pcall(ctx, 0), DUK_EXEC_SUCCESS); + EXPECT_FALSE(duk_get_boolean(ctx, -1)); + duk_pop_2(ctx); + + duk_destroy_heap(ctx); +} + +TEST(general, session_start_rejects_cookie_from_other_scheme) +{ + EnvVarGuard cookie_guard("HTTP_COOKIE"); + EnvVarGuard https_guard("HTTPS"); + const std::string session_id = makeValidSessionId('D'); + const std::string cookie = "DUKSID=" + session_id; + const std::string session_file = "/tmp/" + session_id; + + FILE* file = fopen(session_file.c_str(), "w"); + ASSERT_NE(file, nullptr); + fclose(file); + cookie_guard.set(cookie.c_str()); + https_guard.set("on"); + + duk_context* ctx = duk_create_heap_default(); + ASSERT_NE(ctx, nullptr); + + duk_push_c_function(ctx, ccsp_session_module_open, 0); + ASSERT_EQ(duk_pcall(ctx, 0), DUK_EXEC_SUCCESS); + duk_put_global_string(ctx, "ccsp_session"); + + duk_get_global_string(ctx, "ccsp_session"); + duk_get_prop_string(ctx, -1, "start"); + ASSERT_EQ(duk_pcall(ctx, 0), DUK_EXEC_SUCCESS); + EXPECT_FALSE(duk_get_boolean(ctx, -1)); + duk_pop_2(ctx); + + EXPECT_EQ(access(session_file.c_str(), F_OK), 0); + unlink(session_file.c_str()); + duk_destroy_heap(ctx); +} + +TEST(general, session_get_data_without_session_returns_empty_object) +{ + EnvVarGuard cookie_guard("HTTP_COOKIE"); + cookie_guard.set(nullptr); + + duk_context* ctx = duk_create_heap_default(); + ASSERT_NE(ctx, nullptr); + + duk_push_c_function(ctx, ccsp_session_module_open, 0); + ASSERT_EQ(duk_pcall(ctx, 0), DUK_EXEC_SUCCESS); + duk_put_global_string(ctx, "ccsp_session"); + + duk_get_global_string(ctx, "ccsp_session"); + duk_get_prop_string(ctx, -1, "getData"); + ASSERT_EQ(duk_pcall(ctx, 0), DUK_EXEC_SUCCESS); + EXPECT_TRUE(duk_is_object(ctx, -1)); + duk_pop_2(ctx); + + duk_destroy_heap(ctx); +} + +TEST(general, session_destroy_without_session_returns_false) +{ + EnvVarGuard cookie_guard("HTTP_COOKIE"); + cookie_guard.set(nullptr); + + duk_context* ctx = duk_create_heap_default(); + ASSERT_NE(ctx, nullptr); + + duk_push_c_function(ctx, ccsp_session_module_open, 0); + ASSERT_EQ(duk_pcall(ctx, 0), DUK_EXEC_SUCCESS); + duk_put_global_string(ctx, "ccsp_session"); + + duk_get_global_string(ctx, "ccsp_session"); + duk_get_prop_string(ctx, -1, "destroy"); + ASSERT_EQ(duk_pcall(ctx, 0), DUK_EXEC_SUCCESS); + EXPECT_FALSE(duk_get_boolean(ctx, -1)); + duk_pop_2(ctx); + + duk_destroy_heap(ctx); +} + +TEST(general, session_start_rejects_expired_session) +{ + EnvVarGuard cookie_guard("HTTP_COOKIE"); + const std::string session_id = makeValidSessionId('C'); + const std::string cookie = "DUKSID=" + session_id; + const std::string session_file = "/tmp/" + session_id; + + FILE* file = fopen(session_file.c_str(), "w"); + ASSERT_NE(file, nullptr); + fclose(file); + cookie_guard.set(cookie.c_str()); + + duk_context* ctx = duk_create_heap_default(); + ASSERT_NE(ctx, nullptr); + + duk_push_c_function(ctx, ccsp_session_module_open, 0); + ASSERT_EQ(duk_pcall(ctx, 0), DUK_EXEC_SUCCESS); + duk_put_global_string(ctx, "ccsp_session"); + + duk_get_global_string(ctx, "ccsp_session"); + duk_get_prop_string(ctx, -1, "start"); + ASSERT_EQ(duk_pcall(ctx, 0), DUK_EXEC_SUCCESS); + EXPECT_TRUE(duk_get_boolean(ctx, -1)); + duk_pop_2(ctx); + + ASSERT_EQ(unlink(session_file.c_str()), 0); + + duk_get_global_string(ctx, "ccsp_session"); + duk_get_prop_string(ctx, -1, "start"); + ASSERT_EQ(duk_pcall(ctx, 0), DUK_EXEC_SUCCESS); + EXPECT_FALSE(duk_get_boolean(ctx, -1)); + duk_pop_2(ctx); + + duk_get_global_string(ctx, "ccsp_session"); + duk_get_prop_string(ctx, -1, "getStatus"); + ASSERT_EQ(duk_pcall(ctx, 0), DUK_EXEC_SUCCESS); + EXPECT_FALSE(duk_get_boolean(ctx, -1)); + duk_pop_2(ctx); + + duk_destroy_heap(ctx); +} + TEST(general, session_start_rejects_missing_session_file) { EnvVarGuard cookie_guard("HTTP_COOKIE"); @@ -457,6 +782,169 @@ TEST(general, session_start_rejects_missing_session_file) duk_destroy_heap(ctx); } +TEST(general, session_prefix_start_failure_emits_no_header_and_keeps_empty_session) +{ + EnvVarGuard cookie_guard("HTTP_COOKIE"); + cookie_guard.set(nullptr); + + duk_context* ctx = duk_create_heap_default(); + ASSERT_NE(ctx, nullptr); + installSessionPrefixDependencies(ctx); + evaluateSessionPrefix(ctx); + + EXPECT_FALSE(evaluateJavaScriptBoolean(ctx, "session_start()")); + EXPECT_TRUE(evaluateJavaScriptBoolean(ctx, "_jst_header_buffer === ''")); + EXPECT_TRUE(evaluateJavaScriptBoolean(ctx, + "$_jst_session === null && Object.getPrototypeOf($_SESSION) === Object.prototype")); + EXPECT_TRUE(evaluateJavaScriptBoolean(ctx, + "$_SESSION.safe = 'value'; delete $_SESSION.safe; !session_status()")); + + duk_destroy_heap(ctx); +} + +TEST(general, session_prefix_parses_values_containing_equals_signs) +{ + EnvVarGuard query_string_guard("QUERY_STRING"); + query_string_guard.set("mac_ssid==2"); + + duk_context* ctx = duk_create_heap_default(); + ASSERT_NE(ctx, nullptr); + installSessionPrefixDependencies(ctx, "token=abc%3D%3D", + "id=file1&name=config%3Dbackup&type=application%2Foctet-stream"); + evaluateSessionPrefix(ctx); + + EXPECT_TRUE(evaluateJavaScriptBoolean(ctx, + "$_GET.mac_ssid === '=2' && $_POST.token === 'abc==' && " + "$_FILES.file1.name === 'config=backup'")); + + duk_destroy_heap(ctx); +} + +TEST(general, session_prefix_unset_clears_persisted_data_and_rejects_inactive_session) +{ + EnvVarGuard cookie_guard("HTTP_COOKIE"); + cookie_guard.set(nullptr); + + duk_context* ctx = duk_create_heap_default(); + ASSERT_NE(ctx, nullptr); + installSessionPrefixDependencies(ctx); + evaluateSessionPrefix(ctx); + + EXPECT_FALSE(evaluateJavaScriptBoolean(ctx, "session_unset()")); + ASSERT_TRUE(evaluateJavaScriptBoolean(ctx, "session_create()")); + ASSERT_TRUE(evaluateJavaScriptBoolean(ctx, + "$_SESSION.name = 'alice'; $_SESSION.count = 2; $_SESSION.enabled = true; true")); + const std::string session_id = evaluateJavaScriptString(ctx, "session_id()"); + ASSERT_FALSE(session_id.empty()); + const std::string session_file = "/tmp/" + session_id; + + ASSERT_TRUE(evaluateJavaScriptBoolean(ctx, "session_unset()")); + EXPECT_TRUE(evaluateJavaScriptBoolean(ctx, + "Object.keys($_SESSION).length === 0 && session_status()")); + + std::ifstream persisted_data(session_file); + ASSERT_TRUE(persisted_data.is_open()); + EXPECT_TRUE(std::string((std::istreambuf_iterator(persisted_data)), + std::istreambuf_iterator()).empty()); + + ASSERT_TRUE(evaluateJavaScriptBoolean(ctx, "session_destroy()")); + EXPECT_TRUE(evaluateJavaScriptBoolean(ctx, + "_jst_header_buffer.indexOf('Set-Cookie: DUKSID=; Max-Age=0; httponly') !== -1 && " + "_jst_header_buffer.indexOf('; secure') === -1")); + EXPECT_FALSE(evaluateJavaScriptBoolean(ctx, "session_unset()")); + duk_destroy_heap(ctx); +} + +TEST(general, session_prefix_cookie_secure_attribute_follows_request_scheme) +{ + EnvVarGuard https_guard("HTTPS"); + EnvVarGuard request_scheme_guard("REQUEST_SCHEME"); + EnvVarGuard ssl_protocol_guard("SSL_PROTOCOL"); + https_guard.set(nullptr); + request_scheme_guard.set(nullptr); + ssl_protocol_guard.set(nullptr); + + duk_context* ctx = duk_create_heap_default(); + ASSERT_NE(ctx, nullptr); + installSessionPrefixDependencies(ctx); + evaluateSessionPrefix(ctx); + + ASSERT_TRUE(evaluateJavaScriptBoolean(ctx, "session_create()")); + EXPECT_TRUE(evaluateJavaScriptBoolean(ctx, + "_jst_header_buffer.indexOf('; httponly') !== -1 && _jst_header_buffer.indexOf('; secure') === -1")); + ASSERT_TRUE(evaluateJavaScriptBoolean(ctx, "session_destroy()")); + duk_destroy_heap(ctx); + + request_scheme_guard.set("https"); + ctx = duk_create_heap_default(); + ASSERT_NE(ctx, nullptr); + installSessionPrefixDependencies(ctx); + evaluateSessionPrefix(ctx); + + ASSERT_TRUE(evaluateJavaScriptBoolean(ctx, "session_create()")); + EXPECT_TRUE(evaluateJavaScriptBoolean(ctx, + "_jst_header_buffer.indexOf('; httponly') !== -1 && _jst_header_buffer.indexOf('; secure') !== -1")); + EXPECT_TRUE(evaluateJavaScriptBoolean(ctx, "session_id().charAt(8) === '1'")); + ASSERT_TRUE(evaluateJavaScriptBoolean(ctx, "session_destroy()")); + EXPECT_TRUE(evaluateJavaScriptBoolean(ctx, + "_jst_header_buffer.indexOf('Set-Cookie: DUKSID=; Max-Age=0; httponly; secure') !== -1")); + duk_destroy_heap(ctx); +} + +TEST(general, session_prefix_rejects_https_cookie_on_http_request) +{ + EnvVarGuard cookie_guard("HTTP_COOKIE"); + EnvVarGuard https_guard("HTTPS"); + const std::string session_id = std::string("jst_sess1") + std::string(31, 'E'); + const std::string session_file = "/tmp/" + session_id; + + FILE* file = fopen(session_file.c_str(), "w"); + ASSERT_NE(file, nullptr); + fclose(file); + cookie_guard.set(("DUKSID=" + session_id).c_str()); + https_guard.set(nullptr); + + duk_context* ctx = duk_create_heap_default(); + ASSERT_NE(ctx, nullptr); + installSessionPrefixDependencies(ctx); + evaluateSessionPrefix(ctx); + + EXPECT_FALSE(evaluateJavaScriptBoolean(ctx, "session_start()")); + EXPECT_TRUE(evaluateJavaScriptBoolean(ctx, + "_jst_header_buffer === '' && !session_status() && $_jst_session === null")); + EXPECT_EQ(access(session_file.c_str(), F_OK), 0); + + unlink(session_file.c_str()); + duk_destroy_heap(ctx); +} + +TEST(general, session_prefix_stale_proxy_does_not_recreate_deleted_session_file) +{ + EnvVarGuard cookie_guard("HTTP_COOKIE"); + const std::string session_id = makeValidSessionId('F'); + const std::string session_file = "/tmp/" + session_id; + + FILE* file = fopen(session_file.c_str(), "w"); + ASSERT_NE(file, nullptr); + fputs("persisted|s|value;", file); + fclose(file); + cookie_guard.set(("DUKSID=" + session_id).c_str()); + + duk_context* ctx = duk_create_heap_default(); + ASSERT_NE(ctx, nullptr); + installSessionPrefixDependencies(ctx); + evaluateSessionPrefix(ctx); + + ASSERT_TRUE(evaluateJavaScriptBoolean(ctx, "session_start()")); + ASSERT_EQ(unlink(session_file.c_str()), 0); + EXPECT_FALSE(evaluateJavaScriptBoolean(ctx, "session_start()")); + EXPECT_TRUE(evaluateJavaScriptBoolean(ctx, + "$_SESSION.added = 'new'; delete $_SESSION.persisted; !session_status()")); + EXPECT_NE(access(session_file.c_str(), F_OK), 0); + + duk_destroy_heap(ctx); +} + int main(int argc, char* argv[]) { ::testing::InitGoogleTest(&argc, argv);