From b4fbe7351dd10ebc6c28c413a45df3ddd1dca060 Mon Sep 17 00:00:00 2001 From: Pavan Kumar Reddy Balireddy Date: Fri, 4 Sep 2026 14:29:56 +0530 Subject: [PATCH 01/14] RDKB-66802 : Fix invalid session handling --- jsts/jst_prefix.js | 52 +++++++++---- source/jst_session.c | 81 ++++++++++++++++++--- tests/parser/jst_prefix.js | 24 +++++- tests/parser_test.cpp | 145 ++++++++++++++++++++++++++++++++++++- 4 files changed, 269 insertions(+), 33 deletions(-) diff --git a/jsts/jst_prefix.js b/jsts/jst_prefix.js index 8f3a778..54758b7 100644 --- a/jsts/jst_prefix.js +++ b/jsts/jst_prefix.js @@ -89,6 +89,13 @@ 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 session_start() { if($_jst_session) @@ -98,42 +105,48 @@ function session_start() $_val_input = 0; return; } - 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); + if(!ccsp_session.start()) + { + /* A stale cookie must not create a proxy backed by an inactive session. */ + $_jst_session = {}; + $_SESSION = {}; + return false; + } + if(ccsp_session.getStatus()) + { + 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_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); + 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]; @@ -170,7 +183,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() { diff --git a/source/jst_session.c b/source/jst_session.c index 1935b6d..44d3b60 100644 --- a/source/jst_session.c +++ b/source/jst_session.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -46,6 +47,9 @@ #define SESSION_FILE_MAX_PATH 100 #define SESSION_TMP_DIR "/tmp" #define SESSION_NUMBER_PRECISION 12 +#define SESSION_SCHEME_OFFSET SESSION_PREFIX_LEN +#define SESSION_SCHEME_HTTP '0' +#define SESSION_SCHEME_HTTPS '1' #define BYTE_TO_PRINTABLE_HEX_CODE(B) ( PRINTABLE_HEX_CODES[ (uint32_t)(B) % (uint32_t)(sizeof(PRINTABLE_HEX_CODES)-1) ] ) /* @@ -71,6 +75,32 @@ static char* session_identifier = NULL; +static int request_is_https(void) +{ + const char* val; + + val = getenv("HTTPS"); + if(val && val[0] && strcasecmp(val, "off") != 0) + return 1; + + val = getenv("REQUEST_SCHEME"); + if(val && strcasecmp(val, "https") == 0) + return 1; + + val = getenv("SSL_PROTOCOL"); + if(val && val[0]) + return 1; + + return 0; +} + +/*tags the first character after the prefix so an id captured on one scheme + cannot be replayed on the other*/ +static char request_session_scheme(void) +{ + return request_is_https() ? SESSION_SCHEME_HTTPS : SESSION_SCHEME_HTTP; +} + static int is_valid_session_identifier(const char* session_id) { size_t idx; @@ -139,6 +169,9 @@ static duk_ret_t session_start(duk_context *ctx) if(utime(path, NULL) != 0) { CosaPhpExtLog("failed to update last accesstime on file %s: %s", path, strerror(errno)); + /*the backing file is gone, drop the stale id so getStatus/getId stay in sync*/ + free(session_identifier); + session_identifier = NULL; RETURN_FALSE; } RETURN_TRUE; @@ -177,19 +210,26 @@ static duk_ret_t session_start(duk_context *ctx) if(is_valid_session_identifier(parsed_sesid)) { - char filename[SESSION_FILE_MAX_PATH]; - if(get_session_file_path(parsed_sesid, filename, sizeof(filename))) + if(parsed_sesid[SESSION_SCHEME_OFFSET] != request_session_scheme()) { - CosaPhpExtLog("%s: Checking for Session file %s\n", __PRETTY_FUNCTION__, filename); - if(access(filename, F_OK) == 0) - { - CosaPhpExtLog("%s: Session file %s exists\n", __PRETTY_FUNCTION__, filename); - memcpy(session_identifier, parsed_sesid, SESSION_ID_LENGTH); - session_identifier[SESSION_ID_LENGTH] = '\0'; - } - else + CosaPhpExtLog("%s: SessionID scheme mismatch, rejecting\n", __PRETTY_FUNCTION__); + } + else + { + char filename[SESSION_FILE_MAX_PATH]; + if(get_session_file_path(parsed_sesid, filename, sizeof(filename))) { - CosaPhpExtLog("%s: Failed to read Session file %s\n", __PRETTY_FUNCTION__, filename); + CosaPhpExtLog("%s: Checking for Session file %s\n", __PRETTY_FUNCTION__, filename); + if(access(filename, F_OK) == 0) + { + CosaPhpExtLog("%s: Session file %s exists\n", __PRETTY_FUNCTION__, filename); + memcpy(session_identifier, parsed_sesid, SESSION_ID_LENGTH); + session_identifier[SESSION_ID_LENGTH] = '\0'; + } + else + { + CosaPhpExtLog("%s: Failed to read Session file %s\n", __PRETTY_FUNCTION__, filename); + } } } } @@ -240,6 +280,8 @@ static duk_ret_t session_create(duk_context *ctx) session_id[i] = BYTE_TO_PRINTABLE_HEX_CODE(bytes[i]); } + session_id[0] = request_session_scheme(); + if(session_identifier) { char filename[SESSION_FILE_MAX_PATH]; @@ -284,7 +326,9 @@ static duk_ret_t session_get_data(duk_context *ctx) if(session_identifier == NULL) { - RETURN_FALSE; + /*return an empty object so callers can safely read properties without a session*/ + duk_push_object(ctx); + return 1; } valid = 0; /*valid becomes 1 only if we process a valid data file completely*/ @@ -495,6 +539,18 @@ static duk_ret_t session_get_status(duk_context *ctx) } } +static duk_ret_t session_is_secure(duk_context *ctx) +{ + if(request_is_https()) + { + RETURN_TRUE; + } + else + { + RETURN_FALSE; + } +} + static duk_ret_t session_destroy(duk_context *ctx) { char filename[SESSION_FILE_MAX_PATH]; @@ -530,6 +586,7 @@ static const duk_function_list_entry ccsp_session_funcs[] = { { "getData", session_get_data, 0 }, { "setData", session_set_data, 1 }, { "getStatus", session_get_status, 0 }, + { "isSecure", session_is_secure, 0 }, { "destroy", session_destroy, 0 }, { NULL, NULL, 0 } }; diff --git a/tests/parser/jst_prefix.js b/tests/parser/jst_prefix.js index 41386b4..dc51345 100644 --- a/tests/parser/jst_prefix.js +++ b/tests/parser/jst_prefix.js @@ -84,7 +84,13 @@ function session_start() { if($_jst_session) return; - ccsp_session.start(); + if(!ccsp_session.start()) + { + /* A stale cookie must not create a proxy backed by an inactive session. */ + $_jst_session = {}; + $_SESSION = {}; + return false; + } header("Set-Cookie: DUKSID=" + ccsp_session.getId() + ";"); $_jst_session = ccsp_session.getData(); $_SESSION = new Proxy($_jst_session, { @@ -93,18 +99,21 @@ function session_start() }, 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() { @@ -123,7 +132,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() { diff --git a/tests/parser_test.cpp b/tests/parser_test.cpp index d7eb028..d824b34 100644 --- a/tests/parser_test.cpp +++ b/tests/parser_test.cpp @@ -136,7 +136,7 @@ class StdinRedirectGuard static std::string makeValidSessionId(char fill) { - return std::string("jst_sess") + std::string(32, fill); + return std::string("jst_sess0") + std::string(31, fill); } static string getFieldValue(const string& input, const string& key) @@ -425,6 +425,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"); From a6fe4396de1a2f63517a418c653e5fee481f9108 Mon Sep 17 00:00:00 2001 From: Pavan Kumar Reddy B <57708013+pavankumar464@users.noreply.github.com> Date: Sun, 6 Sep 2026 22:09:35 +0530 Subject: [PATCH 02/14] Apply batched suggestions from code review Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- jsts/jst_prefix.js | 2 +- source/jst_session.c | 7 +++++-- tests/parser/jst_prefix.js | 4 ++-- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/jsts/jst_prefix.js b/jsts/jst_prefix.js index 54758b7..0a5d3f7 100644 --- a/jsts/jst_prefix.js +++ b/jsts/jst_prefix.js @@ -108,7 +108,7 @@ function session_start() if(!ccsp_session.start()) { /* A stale cookie must not create a proxy backed by an inactive session. */ - $_jst_session = {}; + $_jst_session = null; $_SESSION = {}; return false; } diff --git a/source/jst_session.c b/source/jst_session.c index 44d3b60..70f09c8 100644 --- a/source/jst_session.c +++ b/source/jst_session.c @@ -80,8 +80,11 @@ static int request_is_https(void) const char* val; val = getenv("HTTPS"); - if(val && val[0] && strcasecmp(val, "off") != 0) - return 1; + if(val && val[0]) + { + if(strcasecmp(val, "on") == 0 || strcmp(val, "1") == 0 || strcasecmp(val, "true") == 0) + return 1; + } val = getenv("REQUEST_SCHEME"); if(val && strcasecmp(val, "https") == 0) diff --git a/tests/parser/jst_prefix.js b/tests/parser/jst_prefix.js index dc51345..6a2317b 100644 --- a/tests/parser/jst_prefix.js +++ b/tests/parser/jst_prefix.js @@ -83,11 +83,11 @@ var $_jst_session = null; function session_start() { if($_jst_session) - return; + return true; if(!ccsp_session.start()) { /* A stale cookie must not create a proxy backed by an inactive session. */ - $_jst_session = {}; + $_jst_session = null; $_SESSION = {}; return false; } From 0f83099858a5bff07bda724438a9f5a3c222cdcd Mon Sep 17 00:00:00 2001 From: Pavan Kumar Reddy Balireddy Date: Sun, 6 Sep 2026 22:35:46 +0530 Subject: [PATCH 03/14] Fix session_start and session_create return handling --- jsts/jst_prefix.js | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/jsts/jst_prefix.js b/jsts/jst_prefix.js index 0a5d3f7..3f02761 100644 --- a/jsts/jst_prefix.js +++ b/jsts/jst_prefix.js @@ -99,11 +99,11 @@ function _jst_session_cookie() function session_start() { if($_jst_session) - return; + return true; if($_val_input == 1) { $_val_input = 0; - return; + return false; } if(!ccsp_session.start()) { @@ -142,7 +142,12 @@ function session_start() return true; } function session_create(){ - ccsp_session.create(); + 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') @@ -165,6 +170,7 @@ function session_create(){ return true; } }); + return true; } function session_id() { From f4efadc2854e08debbc15989f8c05cd52e711b9d Mon Sep 17 00:00:00 2001 From: Pavan Kumar Reddy Balireddy Date: Sun, 6 Sep 2026 22:53:24 +0530 Subject: [PATCH 04/14] Prevent stale session proxy persistence --- jsts/jst_prefix.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/jsts/jst_prefix.js b/jsts/jst_prefix.js index 3f02761..fa861ab 100644 --- a/jsts/jst_prefix.js +++ b/jsts/jst_prefix.js @@ -158,14 +158,16 @@ function session_create(){ }, 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; } From 126d31b87f2e089fac256a57c26c136d63851769 Mon Sep 17 00:00:00 2001 From: Pavan Kumar Reddy Balireddy Date: Sun, 6 Sep 2026 23:10:06 +0530 Subject: [PATCH 05/14] Adding isSecure request scheme detection tests --- tests/parser_test.cpp | 55 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/tests/parser_test.cpp b/tests/parser_test.cpp index d824b34..9be0f82 100644 --- a/tests/parser_test.cpp +++ b/tests/parser_test.cpp @@ -346,6 +346,61 @@ 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"); From 3a20adf39f0791fa0554799d3000d0dc8ebe0236 Mon Sep 17 00:00:00 2001 From: Pavan Kumar Reddy Balireddy Date: Mon, 7 Sep 2026 08:05:28 +0530 Subject: [PATCH 06/14] test(session): cover wrapper session lifecycle and secure cookie handling --- tests/CMakeLists.txt | 2 + tests/parser_test.cpp | 218 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 220 insertions(+) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index baa60e7..103cd5a 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -77,6 +77,8 @@ add_executable( ../source/jst_session.c ../source/jst_internal.c ../source/duktape/duktape.c) +target_compile_definitions(parser_test PRIVATE + JST_PREFIX_PATH="${CMAKE_SOURCE_DIR}/jsts/jst_prefix.js") target_link_libraries(parser_test libgtest libgmock -pthread) install(DIRECTORY parser DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) diff --git a/tests/parser_test.cpp b/tests/parser_test.cpp index 9be0f82..3675ebb 100644 --- a/tests/parser_test.cpp +++ b/tests/parser_test.cpp @@ -154,6 +154,84 @@ static string getFieldValue(const string& input, const string& key) return input.substr(start, end - start); } +static duk_ret_t test_getenv(duk_context* ctx) +{ + const char* name = duk_require_string(ctx, 0); + const char* value = getenv(name); + + if (value) + duk_push_string(ctx, value); + else + duk_push_false(ctx); + + return 1; +} + +static duk_ret_t test_no_post_data(duk_context* ctx) +{ + duk_push_false(ctx); + return 1; +} + +static void installSessionPrefixDependencies(duk_context* ctx) +{ + 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_push_object(ctx); + duk_push_c_function(ctx, test_getenv, 1); + duk_put_prop_string(ctx, -2, "getenv"); + duk_put_global_string(ctx, "ccsp"); + + duk_push_object(ctx); + duk_push_c_function(ctx, test_no_post_data, 0); + duk_put_prop_string(ctx, -2, "getPost"); + duk_push_c_function(ctx, test_no_post_data, 0); + duk_put_prop_string(ctx, -2, "getFiles"); + duk_put_global_string(ctx, "ccsp_post"); +} + +static void evaluateSessionPrefix(duk_context* ctx) +{ + std::ifstream prefix_file(JST_PREFIX_PATH); + ASSERT_TRUE(prefix_file.is_open()); + std::string prefix((std::istreambuf_iterator(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; @@ -655,6 +733,146 @@ 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_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_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()")); + 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); From 0ac891307ef1d7784f33b564e4db1d5d21a4e1a7 Mon Sep 17 00:00:00 2001 From: Pavan Kumar Reddy Balireddy Date: Mon, 14 Sep 2026 22:17:27 +0530 Subject: [PATCH 07/14] fix(session): expire logout cookie and validate DUKSID boundaries --- jsts/jst_prefix.js | 8 +++++++ source/jst_session.c | 52 +++++++++++++++++++++++++++++++++---------- tests/parser_test.cpp | 36 +++++++++++++++++++++++++++++- 3 files changed, 83 insertions(+), 13 deletions(-) diff --git a/jsts/jst_prefix.js b/jsts/jst_prefix.js index fa861ab..22d4104 100644 --- a/jsts/jst_prefix.js +++ b/jsts/jst_prefix.js @@ -96,6 +96,13 @@ function _jst_session_cookie() $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) @@ -184,6 +191,7 @@ function session_status() } function session_destroy() { + header(_jst_expire_session_cookie()); delete $_jst_session; $_jst_session = null; delete $_SESSION; diff --git a/source/jst_session.c b/source/jst_session.c index 70f09c8..919873b 100644 --- a/source/jst_session.c +++ b/source/jst_session.c @@ -152,11 +152,47 @@ static int get_session_file_path(const char* session_id, char* path, size_t path return 1; } +static int get_session_id_cookie(const char* cookie, const char** value, size_t* value_len) +{ + const char* field = cookie; + const char* matched_value = NULL; + size_t matched_value_len = 0; + + while(field) + { + const char* field_end = strchr(field, ';'); + const char* field_start = field; + const char* value_end = field_end ? field_end : field + strlen(field); + + while(field_start < value_end && (*field_start == ' ' || *field_start == '\t')) + field_start++; + + while(value_end > field_start && + (value_end[-1] == ' ' || value_end[-1] == '\t')) + value_end--; + + if((size_t)(value_end - field_start) >= 7 && + strncmp(field_start, "DUKSID=", 7) == 0) + { + matched_value = field_start + 7; + matched_value_len = (size_t)(value_end - matched_value); + } + + field = field_end ? field_end + 1 : NULL; + } + + if(!matched_value) + return 0; + + *value = matched_value; + *value_len = matched_value_len; + return 1; +} + static duk_ret_t session_start(duk_context *ctx) { CosaPhpExtLog("%s: entered\n", __PRETTY_FUNCTION__); const char* cookie; - const char* sesid_end; size_t sesid_len; char parsed_sesid[SESSION_ID_LENGTH + 1]; /* if session already created then do nothing */ @@ -194,18 +230,10 @@ static duk_ret_t session_start(duk_context *ctx) CosaPhpExtLog("%s: cookie %s\n", __PRETTY_FUNCTION__, cookie); /*load session id from cookie*/ const char* sesid = NULL; - const char* tmp = cookie; - while (tmp = strstr(tmp, "DUKSID=")) - { - sesid= tmp; - tmp++; - } - CosaPhpExtLog("%s: sesid %s\n", __PRETTY_FUNCTION__, sesid); - if(sesid) + if(get_session_id_cookie(cookie, &sesid, &sesid_len)) { - sesid += 7; - sesid_end = strchr(sesid, ';'); - sesid_len = sesid_end ? (size_t)(sesid_end - sesid) : strlen(sesid); + CosaPhpExtLog("%s: sesid %.*s\n", __PRETTY_FUNCTION__, + (int)sesid_len, sesid); if(sesid_len == SESSION_ID_LENGTH) { memcpy(parsed_sesid, sesid, SESSION_ID_LENGTH); diff --git a/tests/parser_test.cpp b/tests/parser_test.cpp index 3675ebb..2570799 100644 --- a/tests/parser_test.cpp +++ b/tests/parser_test.cpp @@ -483,7 +483,7 @@ 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"); @@ -521,6 +521,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 = { @@ -781,6 +810,9 @@ TEST(general, session_prefix_unset_clears_persisted_data_and_rejects_inactive_se 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); } @@ -816,6 +848,8 @@ TEST(general, session_prefix_cookie_secure_attribute_follows_request_scheme) "_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); } From 43186bddfdecd7e7d875c06a8cb6fe7a15e3ce67 Mon Sep 17 00:00:00 2001 From: Pavan Kumar Reddy Balireddy Date: Tue, 15 Sep 2026 14:40:38 +0530 Subject: [PATCH 08/14] revalidate stale sessions and fix parser test path --- jsts/jst_prefix.js | 9 ++++++++- tests/CMakeLists.txt | 2 +- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/jsts/jst_prefix.js b/jsts/jst_prefix.js index 22d4104..e210f23 100644 --- a/jsts/jst_prefix.js +++ b/jsts/jst_prefix.js @@ -106,7 +106,14 @@ function _jst_expire_session_cookie() function session_start() { if($_jst_session) - return true; + { + if(ccsp_session.start()) + return true; + + $_jst_session = null; + $_SESSION = {}; + return false; + } if($_val_input == 1) { $_val_input = 0; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 103cd5a..f982969 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -78,7 +78,7 @@ add_executable( ../source/jst_internal.c ../source/duktape/duktape.c) target_compile_definitions(parser_test PRIVATE - JST_PREFIX_PATH="${CMAKE_SOURCE_DIR}/jsts/jst_prefix.js") + JST_PREFIX_PATH=\"${CMAKE_SOURCE_DIR}/jsts/jst_prefix.js\") target_link_libraries(parser_test libgtest libgmock -pthread) install(DIRECTORY parser DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) From 8e2cc6ca63546f0f608baeb9519d611fa34605ba Mon Sep 17 00:00:00 2001 From: Pavan Kumar Reddy Balireddy Date: Wed, 16 Sep 2026 09:39:14 +0530 Subject: [PATCH 09/14] test(session): assert no cookie on failed session start --- tests/parser_test.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/parser_test.cpp b/tests/parser_test.cpp index 2570799..32db44d 100644 --- a/tests/parser_test.cpp +++ b/tests/parser_test.cpp @@ -773,7 +773,8 @@ TEST(general, session_prefix_start_failure_emits_no_header_and_keeps_empty_sessi evaluateSessionPrefix(ctx); EXPECT_FALSE(evaluateJavaScriptBoolean(ctx, "session_start()")); - EXPECT_TRUE(evaluateJavaScriptBoolean(ctx, "_jst_header_buffer === ''")); + EXPECT_TRUE(evaluateJavaScriptBoolean(ctx, + "_jst_header_buffer.indexOf('Set-Cookie:') === -1")); EXPECT_TRUE(evaluateJavaScriptBoolean(ctx, "$_jst_session === null && Object.getPrototypeOf($_SESSION) === Object.prototype")); EXPECT_TRUE(evaluateJavaScriptBoolean(ctx, @@ -873,7 +874,8 @@ TEST(general, session_prefix_rejects_https_cookie_on_http_request) EXPECT_FALSE(evaluateJavaScriptBoolean(ctx, "session_start()")); EXPECT_TRUE(evaluateJavaScriptBoolean(ctx, - "_jst_header_buffer === '' && !session_status() && $_jst_session === null")); + "_jst_header_buffer.indexOf('Set-Cookie:') === -1 && " + "!session_status() && $_jst_session === null")); EXPECT_EQ(access(session_file.c_str(), F_OK), 0); unlink(session_file.c_str()); From 6b3d368aca5f4e68a8e7a1c6c18f3ebdc6544a30 Mon Sep 17 00:00:00 2001 From: Pavan Kumar Reddy Balireddy Date: Wed, 16 Sep 2026 10:04:54 +0530 Subject: [PATCH 10/14] fix(session): prevent stale proxies writing replacement sessions Bind each session proxy to the native ID active at creation so a retained proxy cannot persist data into a replacement session. --- jsts/jst_prefix.js | 66 +++++++++++++++++-------------------------- tests/parser_test.cpp | 21 ++++++++++++++ 2 files changed, 47 insertions(+), 40 deletions(-) diff --git a/jsts/jst_prefix.js b/jsts/jst_prefix.js index e210f23..75ee27e 100644 --- a/jsts/jst_prefix.js +++ b/jsts/jst_prefix.js @@ -103,6 +103,30 @@ function _jst_expire_session_cookie() $cookie += "; secure"; return $cookie; } +function _jst_session_proxy($session) +{ + var $session_id = ccsp_session.getId(); + return new Proxy($session, { + get: function(obj, prop) { + return obj[prop]; + }, + set: function(obj, prop, val){ + obj[prop] = val; + if(ccsp_session.getStatus() && ccsp_session.getId() === $session_id) + ccsp_session.setData(obj); + return true; + }, + deleteProperty(obj, prop) { + if(prop in obj) + { + delete obj[prop]; + if(ccsp_session.getStatus() && ccsp_session.getId() === $session_id) + ccsp_session.setData(obj); + } + return true; + } + }); +} function session_start() { if($_jst_session) @@ -133,26 +157,7 @@ function session_start() $_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; - if(ccsp_session.getStatus()) - ccsp_session.setData(obj); - return true; - }, - deleteProperty(obj, prop) { - if(prop in obj) - { - delete obj[prop]; - if(ccsp_session.getStatus()) - ccsp_session.setData(obj); - } - return true; - } - }); + $_SESSION = _jst_session_proxy($_jst_session); return true; } function session_create(){ @@ -166,26 +171,7 @@ function session_create(){ $_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; - if(ccsp_session.getStatus()) - ccsp_session.setData(obj); - return true; - }, - deleteProperty(obj, prop) { - if(prop in obj) - { - delete obj[prop]; - if(ccsp_session.getStatus()) - ccsp_session.setData(obj); - } - return true; - } - }); + $_SESSION = _jst_session_proxy($_jst_session); return true; } function session_id() diff --git a/tests/parser_test.cpp b/tests/parser_test.cpp index 32db44d..c9c017b 100644 --- a/tests/parser_test.cpp +++ b/tests/parser_test.cpp @@ -818,6 +818,27 @@ TEST(general, session_prefix_unset_clears_persisted_data_and_rejects_inactive_se duk_destroy_heap(ctx); } +TEST(general, session_prefix_old_proxy_does_not_write_replacement_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); + ASSERT_TRUE(evaluateJavaScriptBoolean(ctx, + "ccsp_session.isSecure = function() { return false; }; true")); + + ASSERT_TRUE(evaluateJavaScriptBoolean(ctx, + "session_create(); var oldSession = $_SESSION; session_create(); " + "oldSession.stale = 'value'; $_SESSION.current = 'value'; " + "ccsp_session.getData().stale === undefined && " + "ccsp_session.getData().current === 'value'")); + ASSERT_TRUE(evaluateJavaScriptBoolean(ctx, "session_destroy()")); + duk_destroy_heap(ctx); +} + TEST(general, session_prefix_cookie_secure_attribute_follows_request_scheme) { EnvVarGuard https_guard("HTTPS"); From 5dcfe4063df6101a9a5136ca4ecfda4c0296c82a Mon Sep 17 00:00:00 2001 From: Pavan Kumar Reddy Balireddy Date: Wed, 16 Sep 2026 11:40:05 +0530 Subject: [PATCH 11/14] test(parser): regenerate prefix golden fixtures Synchronize the copied prefix and generated parser snapshots with the current JST runtime prefix. --- jsts/jst_prefix.js | 2 +- tests/parser/jst_parser_backslash.jst.parsed | 167 ++++++++++++++++-- .../parser/jst_parser_comment_tag.jst.parsed | 167 ++++++++++++++++-- .../jst_parser_include_code_after.jst.parsed | 167 ++++++++++++++++-- .../jst_parser_include_code_before.jst.parsed | 167 ++++++++++++++++-- .../jst_parser_include_malformed_1.jst.parsed | 167 ++++++++++++++++-- .../jst_parser_include_nested.jst.parsed | 167 ++++++++++++++++-- ...include_not_if_in_block_comment.jst.parsed | 167 ++++++++++++++++-- ...arser_include_not_if_in_content.jst.parsed | 167 ++++++++++++++++-- ..._include_not_if_in_line_comment.jst.parsed | 167 ++++++++++++++++-- .../parser/jst_parser_include_once.jst.parsed | 167 ++++++++++++++++-- .../jst_parser_include_runtime.jst.parsed | 167 ++++++++++++++++-- .../jst_parser_include_unknown.jst.parsed | 167 ++++++++++++++++-- tests/parser/jst_parser_line_feeds.jst.parsed | 167 ++++++++++++++++-- .../jst_parser_single_quotes.jst.parsed | 167 ++++++++++++++++-- .../jst_parser_skip_whitespace.jst.parsed | 167 ++++++++++++++++-- ...t_parser_template_block_content.jst.parsed | 167 ++++++++++++++++-- ...st_parser_template_block_string.jst.parsed | 167 ++++++++++++++++-- tests/parser/jst_prefix.js | 140 ++++++++++++--- 19 files changed, 2668 insertions(+), 313 deletions(-) diff --git a/jsts/jst_prefix.js b/jsts/jst_prefix.js index 75ee27e..c8c9942 100644 --- a/jsts/jst_prefix.js +++ b/jsts/jst_prefix.js @@ -138,7 +138,7 @@ function session_start() $_SESSION = {}; return false; } - if($_val_input == 1) + if($_val_input == 1) { $_val_input = 0; return false; diff --git a/tests/parser/jst_parser_backslash.jst.parsed b/tests/parser/jst_parser_backslash.jst.parsed index b695eb3..a7f644d 100644 --- a/tests/parser/jst_parser_backslash.jst.parsed +++ b/tests/parser/jst_parser_backslash.jst.parsed @@ -1,19 +1,45 @@ +/* + If not stated otherwise in this file or this component's Licenses.txt file the + following copyright and licenses apply: + + Copyright 2018 RDK Management + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ try { /* HEADERS: accumulate headers into a buffer to send to stdout in _jst_finish*/ -_jst_header_buffer = "Content-type: text/html"; +_jst_header_content_type_set = false; +_jst_header_buffer = ""; function header(str) { - if(str.toLowerCase().indexOf('location:') == 0) + lstr = str.toLowerCase(); + if(lstr.indexOf('location:') == 0) { _jst_header_buffer = "HTTP/1.0 302 Ok\r\n"; _jst_header_buffer += "Status: 302 Moved\r\n"; - _jst_header_buffer += str; + _jst_header_buffer += str + "\r\n"; } else { - _jst_header_buffer += "\n" + str; + if(lstr.indexOf('content-type:') == 0) + { + _jst_header_content_type_set = true; + if(lstr.indexOf('application/json') != -1) + _jst_header_buffer += "Content-Type: text/html\r\n"; + } + _jst_header_buffer += str + "\r\n"; } } @@ -22,6 +48,7 @@ function header(str) _jst_echo_buffer = ""; function echo(str) { + str = (typeof(str)!="undefined") ? str : ""; _jst_echo_buffer += str; } @@ -29,9 +56,9 @@ function echo(str) and it will send the headers and content to stdout */ function _jst_finish() { - print(_jst_header_buffer); - print("\r\n\r\n\n"); - print(_jst_echo_buffer); + if(!_jst_header_content_type_set) + print("Content-type: text/html\r"); + print(_jst_header_buffer + "\r\n" + _jst_echo_buffer); } /* EXIT: there is no way to simply quit in the middle of a script, so @@ -61,32 +88,92 @@ var $_SERVER = new Proxy({}, { /* SESSION: session data set by web app, saved to disk, and referenced by session id stored in cookie */ var $_SESSION = {}; var $_jst_session = null; -function session_start() +var $_val_input = {}; +function _jst_session_cookie() { - if($_jst_session) - return; - ccsp_session.start(); - header("Set-Cookie: DUKSID=" + ccsp_session.getId() + ";"); - $_jst_session = ccsp_session.getData(); - $_SESSION = new Proxy($_jst_session, { + 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 _jst_session_proxy($session) +{ + var $session_id = ccsp_session.getId(); + return new Proxy($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.getId() === $session_id) + 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.getId() === $session_id) + ccsp_session.setData(obj); } return true; } }); } +function session_start() +{ + if($_jst_session) + { + if(ccsp_session.start()) + return true; + + $_jst_session = null; + $_SESSION = {}; + return false; + } + if($_val_input == 1) + { + $_val_input = 0; + 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()); + } + $_jst_session = ccsp_session.getData(); + if($_jst_session === null || typeof($_jst_session) !== 'object') + $_jst_session = {}; + $_SESSION = _jst_session_proxy($_jst_session); + return true; +} +function session_create(){ + 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 = _jst_session_proxy($_jst_session); + return true; +} function session_id() { return ccsp_session.getId(); @@ -97,6 +184,7 @@ function session_status() } function session_destroy() { + header(_jst_expire_session_cookie()); delete $_jst_session; $_jst_session = null; delete $_SESSION; @@ -104,7 +192,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() { @@ -127,7 +222,41 @@ if(postData) $_POST[postValue[0]] = decodeURIComponent(value); } else + { print("unexpected post data"); + $_val_input = 1; + } + } +} + +/* FILES: multipart/form-data files via stdin */ +$_FILES={}; +var filesData = ccsp_post.getFiles(); +if(filesData) +{ + var fileList = filesData.split(';'); + for(var i = 0; i < fileList.length; ++i) + { + var fileData = fileList[i].split('&'); + var fileId = null; + for(var j = 0; j < fileData.length; ++j) + { + var fileValue = fileData[j].split('='); + if(fileValue.length == 2) + { + if(!fileId) + { + fileId = decodeURIComponent(fileValue[1]); + $_FILES[fileId]={}; + } + else + { + $_FILES[fileId][decodeURIComponent(fileValue[0])]=decodeURIComponent(fileValue[1]); + } + } + else + print("unexpected file data"); + } } } @@ -157,6 +286,10 @@ function include($filepath) /* begin application code */ + + + + echo('a \\ b\n\ '); /* end application code */ diff --git a/tests/parser/jst_parser_comment_tag.jst.parsed b/tests/parser/jst_parser_comment_tag.jst.parsed index a721fbf..12f619b 100644 --- a/tests/parser/jst_parser_comment_tag.jst.parsed +++ b/tests/parser/jst_parser_comment_tag.jst.parsed @@ -1,19 +1,45 @@ +/* + If not stated otherwise in this file or this component's Licenses.txt file the + following copyright and licenses apply: + + Copyright 2018 RDK Management + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ try { /* HEADERS: accumulate headers into a buffer to send to stdout in _jst_finish*/ -_jst_header_buffer = "Content-type: text/html"; +_jst_header_content_type_set = false; +_jst_header_buffer = ""; function header(str) { - if(str.toLowerCase().indexOf('location:') == 0) + lstr = str.toLowerCase(); + if(lstr.indexOf('location:') == 0) { _jst_header_buffer = "HTTP/1.0 302 Ok\r\n"; _jst_header_buffer += "Status: 302 Moved\r\n"; - _jst_header_buffer += str; + _jst_header_buffer += str + "\r\n"; } else { - _jst_header_buffer += "\n" + str; + if(lstr.indexOf('content-type:') == 0) + { + _jst_header_content_type_set = true; + if(lstr.indexOf('application/json') != -1) + _jst_header_buffer += "Content-Type: text/html\r\n"; + } + _jst_header_buffer += str + "\r\n"; } } @@ -22,6 +48,7 @@ function header(str) _jst_echo_buffer = ""; function echo(str) { + str = (typeof(str)!="undefined") ? str : ""; _jst_echo_buffer += str; } @@ -29,9 +56,9 @@ function echo(str) and it will send the headers and content to stdout */ function _jst_finish() { - print(_jst_header_buffer); - print("\r\n\r\n\n"); - print(_jst_echo_buffer); + if(!_jst_header_content_type_set) + print("Content-type: text/html\r"); + print(_jst_header_buffer + "\r\n" + _jst_echo_buffer); } /* EXIT: there is no way to simply quit in the middle of a script, so @@ -61,32 +88,92 @@ var $_SERVER = new Proxy({}, { /* SESSION: session data set by web app, saved to disk, and referenced by session id stored in cookie */ var $_SESSION = {}; var $_jst_session = null; -function session_start() +var $_val_input = {}; +function _jst_session_cookie() { - if($_jst_session) - return; - ccsp_session.start(); - header("Set-Cookie: DUKSID=" + ccsp_session.getId() + ";"); - $_jst_session = ccsp_session.getData(); - $_SESSION = new Proxy($_jst_session, { + 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 _jst_session_proxy($session) +{ + var $session_id = ccsp_session.getId(); + return new Proxy($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.getId() === $session_id) + 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.getId() === $session_id) + ccsp_session.setData(obj); } return true; } }); } +function session_start() +{ + if($_jst_session) + { + if(ccsp_session.start()) + return true; + + $_jst_session = null; + $_SESSION = {}; + return false; + } + if($_val_input == 1) + { + $_val_input = 0; + 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()); + } + $_jst_session = ccsp_session.getData(); + if($_jst_session === null || typeof($_jst_session) !== 'object') + $_jst_session = {}; + $_SESSION = _jst_session_proxy($_jst_session); + return true; +} +function session_create(){ + 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 = _jst_session_proxy($_jst_session); + return true; +} function session_id() { return ccsp_session.getId(); @@ -97,6 +184,7 @@ function session_status() } function session_destroy() { + header(_jst_expire_session_cookie()); delete $_jst_session; $_jst_session = null; delete $_SESSION; @@ -104,7 +192,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() { @@ -127,7 +222,41 @@ if(postData) $_POST[postValue[0]] = decodeURIComponent(value); } else + { print("unexpected post data"); + $_val_input = 1; + } + } +} + +/* FILES: multipart/form-data files via stdin */ +$_FILES={}; +var filesData = ccsp_post.getFiles(); +if(filesData) +{ + var fileList = filesData.split(';'); + for(var i = 0; i < fileList.length; ++i) + { + var fileData = fileList[i].split('&'); + var fileId = null; + for(var j = 0; j < fileData.length; ++j) + { + var fileValue = fileData[j].split('='); + if(fileValue.length == 2) + { + if(!fileId) + { + fileId = decodeURIComponent(fileValue[1]); + $_FILES[fileId]={}; + } + else + { + $_FILES[fileId][decodeURIComponent(fileValue[0])]=decodeURIComponent(fileValue[1]); + } + } + else + print("unexpected file data"); + } } } @@ -157,6 +286,10 @@ function include($filepath) /* begin application code */ + + + + echo('/*\n\ '); echo("This cannot be commented out"); diff --git a/tests/parser/jst_parser_include_code_after.jst.parsed b/tests/parser/jst_parser_include_code_after.jst.parsed index cf2e250..fbd36da 100644 --- a/tests/parser/jst_parser_include_code_after.jst.parsed +++ b/tests/parser/jst_parser_include_code_after.jst.parsed @@ -1,19 +1,45 @@ +/* + If not stated otherwise in this file or this component's Licenses.txt file the + following copyright and licenses apply: + + Copyright 2018 RDK Management + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ try { /* HEADERS: accumulate headers into a buffer to send to stdout in _jst_finish*/ -_jst_header_buffer = "Content-type: text/html"; +_jst_header_content_type_set = false; +_jst_header_buffer = ""; function header(str) { - if(str.toLowerCase().indexOf('location:') == 0) + lstr = str.toLowerCase(); + if(lstr.indexOf('location:') == 0) { _jst_header_buffer = "HTTP/1.0 302 Ok\r\n"; _jst_header_buffer += "Status: 302 Moved\r\n"; - _jst_header_buffer += str; + _jst_header_buffer += str + "\r\n"; } else { - _jst_header_buffer += "\n" + str; + if(lstr.indexOf('content-type:') == 0) + { + _jst_header_content_type_set = true; + if(lstr.indexOf('application/json') != -1) + _jst_header_buffer += "Content-Type: text/html\r\n"; + } + _jst_header_buffer += str + "\r\n"; } } @@ -22,6 +48,7 @@ function header(str) _jst_echo_buffer = ""; function echo(str) { + str = (typeof(str)!="undefined") ? str : ""; _jst_echo_buffer += str; } @@ -29,9 +56,9 @@ function echo(str) and it will send the headers and content to stdout */ function _jst_finish() { - print(_jst_header_buffer); - print("\r\n\r\n\n"); - print(_jst_echo_buffer); + if(!_jst_header_content_type_set) + print("Content-type: text/html\r"); + print(_jst_header_buffer + "\r\n" + _jst_echo_buffer); } /* EXIT: there is no way to simply quit in the middle of a script, so @@ -61,32 +88,92 @@ var $_SERVER = new Proxy({}, { /* SESSION: session data set by web app, saved to disk, and referenced by session id stored in cookie */ var $_SESSION = {}; var $_jst_session = null; -function session_start() +var $_val_input = {}; +function _jst_session_cookie() { - if($_jst_session) - return; - ccsp_session.start(); - header("Set-Cookie: DUKSID=" + ccsp_session.getId() + ";"); - $_jst_session = ccsp_session.getData(); - $_SESSION = new Proxy($_jst_session, { + 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 _jst_session_proxy($session) +{ + var $session_id = ccsp_session.getId(); + return new Proxy($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.getId() === $session_id) + 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.getId() === $session_id) + ccsp_session.setData(obj); } return true; } }); } +function session_start() +{ + if($_jst_session) + { + if(ccsp_session.start()) + return true; + + $_jst_session = null; + $_SESSION = {}; + return false; + } + if($_val_input == 1) + { + $_val_input = 0; + 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()); + } + $_jst_session = ccsp_session.getData(); + if($_jst_session === null || typeof($_jst_session) !== 'object') + $_jst_session = {}; + $_SESSION = _jst_session_proxy($_jst_session); + return true; +} +function session_create(){ + 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 = _jst_session_proxy($_jst_session); + return true; +} function session_id() { return ccsp_session.getId(); @@ -97,6 +184,7 @@ function session_status() } function session_destroy() { + header(_jst_expire_session_cookie()); delete $_jst_session; $_jst_session = null; delete $_SESSION; @@ -104,7 +192,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() { @@ -127,7 +222,41 @@ if(postData) $_POST[postValue[0]] = decodeURIComponent(value); } else + { print("unexpected post data"); + $_val_input = 1; + } + } +} + +/* FILES: multipart/form-data files via stdin */ +$_FILES={}; +var filesData = ccsp_post.getFiles(); +if(filesData) +{ + var fileList = filesData.split(';'); + for(var i = 0; i < fileList.length; ++i) + { + var fileData = fileList[i].split('&'); + var fileId = null; + for(var j = 0; j < fileData.length; ++j) + { + var fileValue = fileData[j].split('='); + if(fileValue.length == 2) + { + if(!fileId) + { + fileId = decodeURIComponent(fileValue[1]); + $_FILES[fileId]={}; + } + else + { + $_FILES[fileId][decodeURIComponent(fileValue[0])]=decodeURIComponent(fileValue[1]); + } + } + else + print("unexpected file data"); + } } } @@ -159,6 +288,10 @@ function include($filepath) + + + + echo("should appear only once"); THIS SHOULD APPEAR AFTER THE INCLUDE diff --git a/tests/parser/jst_parser_include_code_before.jst.parsed b/tests/parser/jst_parser_include_code_before.jst.parsed index fbf5b75..9d4ace1 100644 --- a/tests/parser/jst_parser_include_code_before.jst.parsed +++ b/tests/parser/jst_parser_include_code_before.jst.parsed @@ -1,19 +1,45 @@ +/* + If not stated otherwise in this file or this component's Licenses.txt file the + following copyright and licenses apply: + + Copyright 2018 RDK Management + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ try { /* HEADERS: accumulate headers into a buffer to send to stdout in _jst_finish*/ -_jst_header_buffer = "Content-type: text/html"; +_jst_header_content_type_set = false; +_jst_header_buffer = ""; function header(str) { - if(str.toLowerCase().indexOf('location:') == 0) + lstr = str.toLowerCase(); + if(lstr.indexOf('location:') == 0) { _jst_header_buffer = "HTTP/1.0 302 Ok\r\n"; _jst_header_buffer += "Status: 302 Moved\r\n"; - _jst_header_buffer += str; + _jst_header_buffer += str + "\r\n"; } else { - _jst_header_buffer += "\n" + str; + if(lstr.indexOf('content-type:') == 0) + { + _jst_header_content_type_set = true; + if(lstr.indexOf('application/json') != -1) + _jst_header_buffer += "Content-Type: text/html\r\n"; + } + _jst_header_buffer += str + "\r\n"; } } @@ -22,6 +48,7 @@ function header(str) _jst_echo_buffer = ""; function echo(str) { + str = (typeof(str)!="undefined") ? str : ""; _jst_echo_buffer += str; } @@ -29,9 +56,9 @@ function echo(str) and it will send the headers and content to stdout */ function _jst_finish() { - print(_jst_header_buffer); - print("\r\n\r\n\n"); - print(_jst_echo_buffer); + if(!_jst_header_content_type_set) + print("Content-type: text/html\r"); + print(_jst_header_buffer + "\r\n" + _jst_echo_buffer); } /* EXIT: there is no way to simply quit in the middle of a script, so @@ -61,32 +88,92 @@ var $_SERVER = new Proxy({}, { /* SESSION: session data set by web app, saved to disk, and referenced by session id stored in cookie */ var $_SESSION = {}; var $_jst_session = null; -function session_start() +var $_val_input = {}; +function _jst_session_cookie() { - if($_jst_session) - return; - ccsp_session.start(); - header("Set-Cookie: DUKSID=" + ccsp_session.getId() + ";"); - $_jst_session = ccsp_session.getData(); - $_SESSION = new Proxy($_jst_session, { + 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 _jst_session_proxy($session) +{ + var $session_id = ccsp_session.getId(); + return new Proxy($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.getId() === $session_id) + 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.getId() === $session_id) + ccsp_session.setData(obj); } return true; } }); } +function session_start() +{ + if($_jst_session) + { + if(ccsp_session.start()) + return true; + + $_jst_session = null; + $_SESSION = {}; + return false; + } + if($_val_input == 1) + { + $_val_input = 0; + 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()); + } + $_jst_session = ccsp_session.getData(); + if($_jst_session === null || typeof($_jst_session) !== 'object') + $_jst_session = {}; + $_SESSION = _jst_session_proxy($_jst_session); + return true; +} +function session_create(){ + 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 = _jst_session_proxy($_jst_session); + return true; +} function session_id() { return ccsp_session.getId(); @@ -97,6 +184,7 @@ function session_status() } function session_destroy() { + header(_jst_expire_session_cookie()); delete $_jst_session; $_jst_session = null; delete $_SESSION; @@ -104,7 +192,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() { @@ -127,7 +222,41 @@ if(postData) $_POST[postValue[0]] = decodeURIComponent(value); } else + { print("unexpected post data"); + $_val_input = 1; + } + } +} + +/* FILES: multipart/form-data files via stdin */ +$_FILES={}; +var filesData = ccsp_post.getFiles(); +if(filesData) +{ + var fileList = filesData.split(';'); + for(var i = 0; i < fileList.length; ++i) + { + var fileData = fileList[i].split('&'); + var fileId = null; + for(var j = 0; j < fileData.length; ++j) + { + var fileValue = fileData[j].split('='); + if(fileValue.length == 2) + { + if(!fileId) + { + fileId = decodeURIComponent(fileValue[1]); + $_FILES[fileId]={}; + } + else + { + $_FILES[fileId][decodeURIComponent(fileValue[0])]=decodeURIComponent(fileValue[1]); + } + } + else + print("unexpected file data"); + } } } @@ -158,6 +287,10 @@ function include($filepath) /* begin application code */ + + + + THIS SHOULD APPEAR BEFORE THE INCLUDE echo("should appear only once"); diff --git a/tests/parser/jst_parser_include_malformed_1.jst.parsed b/tests/parser/jst_parser_include_malformed_1.jst.parsed index 36ea207..594bd41 100644 --- a/tests/parser/jst_parser_include_malformed_1.jst.parsed +++ b/tests/parser/jst_parser_include_malformed_1.jst.parsed @@ -1,19 +1,45 @@ +/* + If not stated otherwise in this file or this component's Licenses.txt file the + following copyright and licenses apply: + + Copyright 2018 RDK Management + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ try { /* HEADERS: accumulate headers into a buffer to send to stdout in _jst_finish*/ -_jst_header_buffer = "Content-type: text/html"; +_jst_header_content_type_set = false; +_jst_header_buffer = ""; function header(str) { - if(str.toLowerCase().indexOf('location:') == 0) + lstr = str.toLowerCase(); + if(lstr.indexOf('location:') == 0) { _jst_header_buffer = "HTTP/1.0 302 Ok\r\n"; _jst_header_buffer += "Status: 302 Moved\r\n"; - _jst_header_buffer += str; + _jst_header_buffer += str + "\r\n"; } else { - _jst_header_buffer += "\n" + str; + if(lstr.indexOf('content-type:') == 0) + { + _jst_header_content_type_set = true; + if(lstr.indexOf('application/json') != -1) + _jst_header_buffer += "Content-Type: text/html\r\n"; + } + _jst_header_buffer += str + "\r\n"; } } @@ -22,6 +48,7 @@ function header(str) _jst_echo_buffer = ""; function echo(str) { + str = (typeof(str)!="undefined") ? str : ""; _jst_echo_buffer += str; } @@ -29,9 +56,9 @@ function echo(str) and it will send the headers and content to stdout */ function _jst_finish() { - print(_jst_header_buffer); - print("\r\n\r\n\n"); - print(_jst_echo_buffer); + if(!_jst_header_content_type_set) + print("Content-type: text/html\r"); + print(_jst_header_buffer + "\r\n" + _jst_echo_buffer); } /* EXIT: there is no way to simply quit in the middle of a script, so @@ -61,32 +88,92 @@ var $_SERVER = new Proxy({}, { /* SESSION: session data set by web app, saved to disk, and referenced by session id stored in cookie */ var $_SESSION = {}; var $_jst_session = null; -function session_start() +var $_val_input = {}; +function _jst_session_cookie() { - if($_jst_session) - return; - ccsp_session.start(); - header("Set-Cookie: DUKSID=" + ccsp_session.getId() + ";"); - $_jst_session = ccsp_session.getData(); - $_SESSION = new Proxy($_jst_session, { + 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 _jst_session_proxy($session) +{ + var $session_id = ccsp_session.getId(); + return new Proxy($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.getId() === $session_id) + 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.getId() === $session_id) + ccsp_session.setData(obj); } return true; } }); } +function session_start() +{ + if($_jst_session) + { + if(ccsp_session.start()) + return true; + + $_jst_session = null; + $_SESSION = {}; + return false; + } + if($_val_input == 1) + { + $_val_input = 0; + 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()); + } + $_jst_session = ccsp_session.getData(); + if($_jst_session === null || typeof($_jst_session) !== 'object') + $_jst_session = {}; + $_SESSION = _jst_session_proxy($_jst_session); + return true; +} +function session_create(){ + 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 = _jst_session_proxy($_jst_session); + return true; +} function session_id() { return ccsp_session.getId(); @@ -97,6 +184,7 @@ function session_status() } function session_destroy() { + header(_jst_expire_session_cookie()); delete $_jst_session; $_jst_session = null; delete $_SESSION; @@ -104,7 +192,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() { @@ -127,7 +222,41 @@ if(postData) $_POST[postValue[0]] = decodeURIComponent(value); } else + { print("unexpected post data"); + $_val_input = 1; + } + } +} + +/* FILES: multipart/form-data files via stdin */ +$_FILES={}; +var filesData = ccsp_post.getFiles(); +if(filesData) +{ + var fileList = filesData.split(';'); + for(var i = 0; i < fileList.length; ++i) + { + var fileData = fileList[i].split('&'); + var fileId = null; + for(var j = 0; j < fileData.length; ++j) + { + var fileValue = fileData[j].split('='); + if(fileValue.length == 2) + { + if(!fileId) + { + fileId = decodeURIComponent(fileValue[1]); + $_FILES[fileId]={}; + } + else + { + $_FILES[fileId][decodeURIComponent(fileValue[0])]=decodeURIComponent(fileValue[1]); + } + } + else + print("unexpected file data"); + } } } @@ -158,6 +287,10 @@ function include($filepath) /* begin application code */ + + + + include( x "include/once.jst"); /* end application code */ diff --git a/tests/parser/jst_parser_include_nested.jst.parsed b/tests/parser/jst_parser_include_nested.jst.parsed index 1f2b01e..8aad386 100644 --- a/tests/parser/jst_parser_include_nested.jst.parsed +++ b/tests/parser/jst_parser_include_nested.jst.parsed @@ -1,19 +1,45 @@ +/* + If not stated otherwise in this file or this component's Licenses.txt file the + following copyright and licenses apply: + + Copyright 2018 RDK Management + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ try { /* HEADERS: accumulate headers into a buffer to send to stdout in _jst_finish*/ -_jst_header_buffer = "Content-type: text/html"; +_jst_header_content_type_set = false; +_jst_header_buffer = ""; function header(str) { - if(str.toLowerCase().indexOf('location:') == 0) + lstr = str.toLowerCase(); + if(lstr.indexOf('location:') == 0) { _jst_header_buffer = "HTTP/1.0 302 Ok\r\n"; _jst_header_buffer += "Status: 302 Moved\r\n"; - _jst_header_buffer += str; + _jst_header_buffer += str + "\r\n"; } else { - _jst_header_buffer += "\n" + str; + if(lstr.indexOf('content-type:') == 0) + { + _jst_header_content_type_set = true; + if(lstr.indexOf('application/json') != -1) + _jst_header_buffer += "Content-Type: text/html\r\n"; + } + _jst_header_buffer += str + "\r\n"; } } @@ -22,6 +48,7 @@ function header(str) _jst_echo_buffer = ""; function echo(str) { + str = (typeof(str)!="undefined") ? str : ""; _jst_echo_buffer += str; } @@ -29,9 +56,9 @@ function echo(str) and it will send the headers and content to stdout */ function _jst_finish() { - print(_jst_header_buffer); - print("\r\n\r\n\n"); - print(_jst_echo_buffer); + if(!_jst_header_content_type_set) + print("Content-type: text/html\r"); + print(_jst_header_buffer + "\r\n" + _jst_echo_buffer); } /* EXIT: there is no way to simply quit in the middle of a script, so @@ -61,32 +88,92 @@ var $_SERVER = new Proxy({}, { /* SESSION: session data set by web app, saved to disk, and referenced by session id stored in cookie */ var $_SESSION = {}; var $_jst_session = null; -function session_start() +var $_val_input = {}; +function _jst_session_cookie() { - if($_jst_session) - return; - ccsp_session.start(); - header("Set-Cookie: DUKSID=" + ccsp_session.getId() + ";"); - $_jst_session = ccsp_session.getData(); - $_SESSION = new Proxy($_jst_session, { + 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 _jst_session_proxy($session) +{ + var $session_id = ccsp_session.getId(); + return new Proxy($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.getId() === $session_id) + 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.getId() === $session_id) + ccsp_session.setData(obj); } return true; } }); } +function session_start() +{ + if($_jst_session) + { + if(ccsp_session.start()) + return true; + + $_jst_session = null; + $_SESSION = {}; + return false; + } + if($_val_input == 1) + { + $_val_input = 0; + 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()); + } + $_jst_session = ccsp_session.getData(); + if($_jst_session === null || typeof($_jst_session) !== 'object') + $_jst_session = {}; + $_SESSION = _jst_session_proxy($_jst_session); + return true; +} +function session_create(){ + 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 = _jst_session_proxy($_jst_session); + return true; +} function session_id() { return ccsp_session.getId(); @@ -97,6 +184,7 @@ function session_status() } function session_destroy() { + header(_jst_expire_session_cookie()); delete $_jst_session; $_jst_session = null; delete $_SESSION; @@ -104,7 +192,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() { @@ -127,7 +222,41 @@ if(postData) $_POST[postValue[0]] = decodeURIComponent(value); } else + { print("unexpected post data"); + $_val_input = 1; + } + } +} + +/* FILES: multipart/form-data files via stdin */ +$_FILES={}; +var filesData = ccsp_post.getFiles(); +if(filesData) +{ + var fileList = filesData.split(';'); + for(var i = 0; i < fileList.length; ++i) + { + var fileData = fileList[i].split('&'); + var fileId = null; + for(var j = 0; j < fileData.length; ++j) + { + var fileValue = fileData[j].split('='); + if(fileValue.length == 2) + { + if(!fileId) + { + fileId = decodeURIComponent(fileValue[1]); + $_FILES[fileId]={}; + } + else + { + $_FILES[fileId][decodeURIComponent(fileValue[0])]=decodeURIComponent(fileValue[1]); + } + } + else + print("unexpected file data"); + } } } @@ -158,6 +287,10 @@ function include($filepath) /* begin application code */ + + + + diff --git a/tests/parser/jst_parser_include_not_if_in_block_comment.jst.parsed b/tests/parser/jst_parser_include_not_if_in_block_comment.jst.parsed index b7b8b2f..75b0a2c 100644 --- a/tests/parser/jst_parser_include_not_if_in_block_comment.jst.parsed +++ b/tests/parser/jst_parser_include_not_if_in_block_comment.jst.parsed @@ -1,19 +1,45 @@ +/* + If not stated otherwise in this file or this component's Licenses.txt file the + following copyright and licenses apply: + + Copyright 2018 RDK Management + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ try { /* HEADERS: accumulate headers into a buffer to send to stdout in _jst_finish*/ -_jst_header_buffer = "Content-type: text/html"; +_jst_header_content_type_set = false; +_jst_header_buffer = ""; function header(str) { - if(str.toLowerCase().indexOf('location:') == 0) + lstr = str.toLowerCase(); + if(lstr.indexOf('location:') == 0) { _jst_header_buffer = "HTTP/1.0 302 Ok\r\n"; _jst_header_buffer += "Status: 302 Moved\r\n"; - _jst_header_buffer += str; + _jst_header_buffer += str + "\r\n"; } else { - _jst_header_buffer += "\n" + str; + if(lstr.indexOf('content-type:') == 0) + { + _jst_header_content_type_set = true; + if(lstr.indexOf('application/json') != -1) + _jst_header_buffer += "Content-Type: text/html\r\n"; + } + _jst_header_buffer += str + "\r\n"; } } @@ -22,6 +48,7 @@ function header(str) _jst_echo_buffer = ""; function echo(str) { + str = (typeof(str)!="undefined") ? str : ""; _jst_echo_buffer += str; } @@ -29,9 +56,9 @@ function echo(str) and it will send the headers and content to stdout */ function _jst_finish() { - print(_jst_header_buffer); - print("\r\n\r\n\n"); - print(_jst_echo_buffer); + if(!_jst_header_content_type_set) + print("Content-type: text/html\r"); + print(_jst_header_buffer + "\r\n" + _jst_echo_buffer); } /* EXIT: there is no way to simply quit in the middle of a script, so @@ -61,32 +88,92 @@ var $_SERVER = new Proxy({}, { /* SESSION: session data set by web app, saved to disk, and referenced by session id stored in cookie */ var $_SESSION = {}; var $_jst_session = null; -function session_start() +var $_val_input = {}; +function _jst_session_cookie() { - if($_jst_session) - return; - ccsp_session.start(); - header("Set-Cookie: DUKSID=" + ccsp_session.getId() + ";"); - $_jst_session = ccsp_session.getData(); - $_SESSION = new Proxy($_jst_session, { + 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 _jst_session_proxy($session) +{ + var $session_id = ccsp_session.getId(); + return new Proxy($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.getId() === $session_id) + 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.getId() === $session_id) + ccsp_session.setData(obj); } return true; } }); } +function session_start() +{ + if($_jst_session) + { + if(ccsp_session.start()) + return true; + + $_jst_session = null; + $_SESSION = {}; + return false; + } + if($_val_input == 1) + { + $_val_input = 0; + 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()); + } + $_jst_session = ccsp_session.getData(); + if($_jst_session === null || typeof($_jst_session) !== 'object') + $_jst_session = {}; + $_SESSION = _jst_session_proxy($_jst_session); + return true; +} +function session_create(){ + 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 = _jst_session_proxy($_jst_session); + return true; +} function session_id() { return ccsp_session.getId(); @@ -97,6 +184,7 @@ function session_status() } function session_destroy() { + header(_jst_expire_session_cookie()); delete $_jst_session; $_jst_session = null; delete $_SESSION; @@ -104,7 +192,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() { @@ -127,7 +222,41 @@ if(postData) $_POST[postValue[0]] = decodeURIComponent(value); } else + { print("unexpected post data"); + $_val_input = 1; + } + } +} + +/* FILES: multipart/form-data files via stdin */ +$_FILES={}; +var filesData = ccsp_post.getFiles(); +if(filesData) +{ + var fileList = filesData.split(';'); + for(var i = 0; i < fileList.length; ++i) + { + var fileData = fileList[i].split('&'); + var fileId = null; + for(var j = 0; j < fileData.length; ++j) + { + var fileValue = fileData[j].split('='); + if(fileValue.length == 2) + { + if(!fileId) + { + fileId = decodeURIComponent(fileValue[1]); + $_FILES[fileId]={}; + } + else + { + $_FILES[fileId][decodeURIComponent(fileValue[0])]=decodeURIComponent(fileValue[1]); + } + } + else + print("unexpected file data"); + } } } @@ -158,6 +287,10 @@ function include($filepath) /* begin application code */ + + + + /*include("include/once.jst");*/ /* include("include/once.jst"); diff --git a/tests/parser/jst_parser_include_not_if_in_content.jst.parsed b/tests/parser/jst_parser_include_not_if_in_content.jst.parsed index 6623d64..ab52650 100644 --- a/tests/parser/jst_parser_include_not_if_in_content.jst.parsed +++ b/tests/parser/jst_parser_include_not_if_in_content.jst.parsed @@ -1,19 +1,45 @@ +/* + If not stated otherwise in this file or this component's Licenses.txt file the + following copyright and licenses apply: + + Copyright 2018 RDK Management + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ try { /* HEADERS: accumulate headers into a buffer to send to stdout in _jst_finish*/ -_jst_header_buffer = "Content-type: text/html"; +_jst_header_content_type_set = false; +_jst_header_buffer = ""; function header(str) { - if(str.toLowerCase().indexOf('location:') == 0) + lstr = str.toLowerCase(); + if(lstr.indexOf('location:') == 0) { _jst_header_buffer = "HTTP/1.0 302 Ok\r\n"; _jst_header_buffer += "Status: 302 Moved\r\n"; - _jst_header_buffer += str; + _jst_header_buffer += str + "\r\n"; } else { - _jst_header_buffer += "\n" + str; + if(lstr.indexOf('content-type:') == 0) + { + _jst_header_content_type_set = true; + if(lstr.indexOf('application/json') != -1) + _jst_header_buffer += "Content-Type: text/html\r\n"; + } + _jst_header_buffer += str + "\r\n"; } } @@ -22,6 +48,7 @@ function header(str) _jst_echo_buffer = ""; function echo(str) { + str = (typeof(str)!="undefined") ? str : ""; _jst_echo_buffer += str; } @@ -29,9 +56,9 @@ function echo(str) and it will send the headers and content to stdout */ function _jst_finish() { - print(_jst_header_buffer); - print("\r\n\r\n\n"); - print(_jst_echo_buffer); + if(!_jst_header_content_type_set) + print("Content-type: text/html\r"); + print(_jst_header_buffer + "\r\n" + _jst_echo_buffer); } /* EXIT: there is no way to simply quit in the middle of a script, so @@ -61,32 +88,92 @@ var $_SERVER = new Proxy({}, { /* SESSION: session data set by web app, saved to disk, and referenced by session id stored in cookie */ var $_SESSION = {}; var $_jst_session = null; -function session_start() +var $_val_input = {}; +function _jst_session_cookie() { - if($_jst_session) - return; - ccsp_session.start(); - header("Set-Cookie: DUKSID=" + ccsp_session.getId() + ";"); - $_jst_session = ccsp_session.getData(); - $_SESSION = new Proxy($_jst_session, { + 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 _jst_session_proxy($session) +{ + var $session_id = ccsp_session.getId(); + return new Proxy($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.getId() === $session_id) + 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.getId() === $session_id) + ccsp_session.setData(obj); } return true; } }); } +function session_start() +{ + if($_jst_session) + { + if(ccsp_session.start()) + return true; + + $_jst_session = null; + $_SESSION = {}; + return false; + } + if($_val_input == 1) + { + $_val_input = 0; + 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()); + } + $_jst_session = ccsp_session.getData(); + if($_jst_session === null || typeof($_jst_session) !== 'object') + $_jst_session = {}; + $_SESSION = _jst_session_proxy($_jst_session); + return true; +} +function session_create(){ + 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 = _jst_session_proxy($_jst_session); + return true; +} function session_id() { return ccsp_session.getId(); @@ -97,6 +184,7 @@ function session_status() } function session_destroy() { + header(_jst_expire_session_cookie()); delete $_jst_session; $_jst_session = null; delete $_SESSION; @@ -104,7 +192,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() { @@ -127,7 +222,41 @@ if(postData) $_POST[postValue[0]] = decodeURIComponent(value); } else + { print("unexpected post data"); + $_val_input = 1; + } + } +} + +/* FILES: multipart/form-data files via stdin */ +$_FILES={}; +var filesData = ccsp_post.getFiles(); +if(filesData) +{ + var fileList = filesData.split(';'); + for(var i = 0; i < fileList.length; ++i) + { + var fileData = fileList[i].split('&'); + var fileId = null; + for(var j = 0; j < fileData.length; ++j) + { + var fileValue = fileData[j].split('='); + if(fileValue.length == 2) + { + if(!fileId) + { + fileId = decodeURIComponent(fileValue[1]); + $_FILES[fileId]={}; + } + else + { + $_FILES[fileId][decodeURIComponent(fileValue[0])]=decodeURIComponent(fileValue[1]); + } + } + else + print("unexpected file data"); + } } } @@ -157,6 +286,10 @@ function include($filepath) /* begin application code */ + + + + echo('//FIXME: if i remove this comment, then the following line doesn\'t output\n\ include("include/once.jst");\n\ \n\ diff --git a/tests/parser/jst_parser_include_not_if_in_line_comment.jst.parsed b/tests/parser/jst_parser_include_not_if_in_line_comment.jst.parsed index 0611a2c..25975ef 100644 --- a/tests/parser/jst_parser_include_not_if_in_line_comment.jst.parsed +++ b/tests/parser/jst_parser_include_not_if_in_line_comment.jst.parsed @@ -1,19 +1,45 @@ +/* + If not stated otherwise in this file or this component's Licenses.txt file the + following copyright and licenses apply: + + Copyright 2018 RDK Management + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ try { /* HEADERS: accumulate headers into a buffer to send to stdout in _jst_finish*/ -_jst_header_buffer = "Content-type: text/html"; +_jst_header_content_type_set = false; +_jst_header_buffer = ""; function header(str) { - if(str.toLowerCase().indexOf('location:') == 0) + lstr = str.toLowerCase(); + if(lstr.indexOf('location:') == 0) { _jst_header_buffer = "HTTP/1.0 302 Ok\r\n"; _jst_header_buffer += "Status: 302 Moved\r\n"; - _jst_header_buffer += str; + _jst_header_buffer += str + "\r\n"; } else { - _jst_header_buffer += "\n" + str; + if(lstr.indexOf('content-type:') == 0) + { + _jst_header_content_type_set = true; + if(lstr.indexOf('application/json') != -1) + _jst_header_buffer += "Content-Type: text/html\r\n"; + } + _jst_header_buffer += str + "\r\n"; } } @@ -22,6 +48,7 @@ function header(str) _jst_echo_buffer = ""; function echo(str) { + str = (typeof(str)!="undefined") ? str : ""; _jst_echo_buffer += str; } @@ -29,9 +56,9 @@ function echo(str) and it will send the headers and content to stdout */ function _jst_finish() { - print(_jst_header_buffer); - print("\r\n\r\n\n"); - print(_jst_echo_buffer); + if(!_jst_header_content_type_set) + print("Content-type: text/html\r"); + print(_jst_header_buffer + "\r\n" + _jst_echo_buffer); } /* EXIT: there is no way to simply quit in the middle of a script, so @@ -61,32 +88,92 @@ var $_SERVER = new Proxy({}, { /* SESSION: session data set by web app, saved to disk, and referenced by session id stored in cookie */ var $_SESSION = {}; var $_jst_session = null; -function session_start() +var $_val_input = {}; +function _jst_session_cookie() { - if($_jst_session) - return; - ccsp_session.start(); - header("Set-Cookie: DUKSID=" + ccsp_session.getId() + ";"); - $_jst_session = ccsp_session.getData(); - $_SESSION = new Proxy($_jst_session, { + 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 _jst_session_proxy($session) +{ + var $session_id = ccsp_session.getId(); + return new Proxy($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.getId() === $session_id) + 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.getId() === $session_id) + ccsp_session.setData(obj); } return true; } }); } +function session_start() +{ + if($_jst_session) + { + if(ccsp_session.start()) + return true; + + $_jst_session = null; + $_SESSION = {}; + return false; + } + if($_val_input == 1) + { + $_val_input = 0; + 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()); + } + $_jst_session = ccsp_session.getData(); + if($_jst_session === null || typeof($_jst_session) !== 'object') + $_jst_session = {}; + $_SESSION = _jst_session_proxy($_jst_session); + return true; +} +function session_create(){ + 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 = _jst_session_proxy($_jst_session); + return true; +} function session_id() { return ccsp_session.getId(); @@ -97,6 +184,7 @@ function session_status() } function session_destroy() { + header(_jst_expire_session_cookie()); delete $_jst_session; $_jst_session = null; delete $_SESSION; @@ -104,7 +192,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() { @@ -127,7 +222,41 @@ if(postData) $_POST[postValue[0]] = decodeURIComponent(value); } else + { print("unexpected post data"); + $_val_input = 1; + } + } +} + +/* FILES: multipart/form-data files via stdin */ +$_FILES={}; +var filesData = ccsp_post.getFiles(); +if(filesData) +{ + var fileList = filesData.split(';'); + for(var i = 0; i < fileList.length; ++i) + { + var fileData = fileList[i].split('&'); + var fileId = null; + for(var j = 0; j < fileData.length; ++j) + { + var fileValue = fileData[j].split('='); + if(fileValue.length == 2) + { + if(!fileId) + { + fileId = decodeURIComponent(fileValue[1]); + $_FILES[fileId]={}; + } + else + { + $_FILES[fileId][decodeURIComponent(fileValue[0])]=decodeURIComponent(fileValue[1]); + } + } + else + print("unexpected file data"); + } } } @@ -158,6 +287,10 @@ function include($filepath) /* begin application code */ + + + + //include("includes/once.jst"); //blah include("include/once.jst"); //include("include/once.jst"); blah diff --git a/tests/parser/jst_parser_include_once.jst.parsed b/tests/parser/jst_parser_include_once.jst.parsed index e3167dc..231f274 100644 --- a/tests/parser/jst_parser_include_once.jst.parsed +++ b/tests/parser/jst_parser_include_once.jst.parsed @@ -1,19 +1,45 @@ +/* + If not stated otherwise in this file or this component's Licenses.txt file the + following copyright and licenses apply: + + Copyright 2018 RDK Management + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ try { /* HEADERS: accumulate headers into a buffer to send to stdout in _jst_finish*/ -_jst_header_buffer = "Content-type: text/html"; +_jst_header_content_type_set = false; +_jst_header_buffer = ""; function header(str) { - if(str.toLowerCase().indexOf('location:') == 0) + lstr = str.toLowerCase(); + if(lstr.indexOf('location:') == 0) { _jst_header_buffer = "HTTP/1.0 302 Ok\r\n"; _jst_header_buffer += "Status: 302 Moved\r\n"; - _jst_header_buffer += str; + _jst_header_buffer += str + "\r\n"; } else { - _jst_header_buffer += "\n" + str; + if(lstr.indexOf('content-type:') == 0) + { + _jst_header_content_type_set = true; + if(lstr.indexOf('application/json') != -1) + _jst_header_buffer += "Content-Type: text/html\r\n"; + } + _jst_header_buffer += str + "\r\n"; } } @@ -22,6 +48,7 @@ function header(str) _jst_echo_buffer = ""; function echo(str) { + str = (typeof(str)!="undefined") ? str : ""; _jst_echo_buffer += str; } @@ -29,9 +56,9 @@ function echo(str) and it will send the headers and content to stdout */ function _jst_finish() { - print(_jst_header_buffer); - print("\r\n\r\n\n"); - print(_jst_echo_buffer); + if(!_jst_header_content_type_set) + print("Content-type: text/html\r"); + print(_jst_header_buffer + "\r\n" + _jst_echo_buffer); } /* EXIT: there is no way to simply quit in the middle of a script, so @@ -61,32 +88,92 @@ var $_SERVER = new Proxy({}, { /* SESSION: session data set by web app, saved to disk, and referenced by session id stored in cookie */ var $_SESSION = {}; var $_jst_session = null; -function session_start() +var $_val_input = {}; +function _jst_session_cookie() { - if($_jst_session) - return; - ccsp_session.start(); - header("Set-Cookie: DUKSID=" + ccsp_session.getId() + ";"); - $_jst_session = ccsp_session.getData(); - $_SESSION = new Proxy($_jst_session, { + 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 _jst_session_proxy($session) +{ + var $session_id = ccsp_session.getId(); + return new Proxy($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.getId() === $session_id) + 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.getId() === $session_id) + ccsp_session.setData(obj); } return true; } }); } +function session_start() +{ + if($_jst_session) + { + if(ccsp_session.start()) + return true; + + $_jst_session = null; + $_SESSION = {}; + return false; + } + if($_val_input == 1) + { + $_val_input = 0; + 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()); + } + $_jst_session = ccsp_session.getData(); + if($_jst_session === null || typeof($_jst_session) !== 'object') + $_jst_session = {}; + $_SESSION = _jst_session_proxy($_jst_session); + return true; +} +function session_create(){ + 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 = _jst_session_proxy($_jst_session); + return true; +} function session_id() { return ccsp_session.getId(); @@ -97,6 +184,7 @@ function session_status() } function session_destroy() { + header(_jst_expire_session_cookie()); delete $_jst_session; $_jst_session = null; delete $_SESSION; @@ -104,7 +192,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() { @@ -127,7 +222,41 @@ if(postData) $_POST[postValue[0]] = decodeURIComponent(value); } else + { print("unexpected post data"); + $_val_input = 1; + } + } +} + +/* FILES: multipart/form-data files via stdin */ +$_FILES={}; +var filesData = ccsp_post.getFiles(); +if(filesData) +{ + var fileList = filesData.split(';'); + for(var i = 0; i < fileList.length; ++i) + { + var fileData = fileList[i].split('&'); + var fileId = null; + for(var j = 0; j < fileData.length; ++j) + { + var fileValue = fileData[j].split('='); + if(fileValue.length == 2) + { + if(!fileId) + { + fileId = decodeURIComponent(fileValue[1]); + $_FILES[fileId]={}; + } + else + { + $_FILES[fileId][decodeURIComponent(fileValue[0])]=decodeURIComponent(fileValue[1]); + } + } + else + print("unexpected file data"); + } } } @@ -159,6 +288,10 @@ function include($filepath) + + + + echo("should appear only once"); diff --git a/tests/parser/jst_parser_include_runtime.jst.parsed b/tests/parser/jst_parser_include_runtime.jst.parsed index 5901de6..d85d3f6 100644 --- a/tests/parser/jst_parser_include_runtime.jst.parsed +++ b/tests/parser/jst_parser_include_runtime.jst.parsed @@ -1,19 +1,45 @@ +/* + If not stated otherwise in this file or this component's Licenses.txt file the + following copyright and licenses apply: + + Copyright 2018 RDK Management + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ try { /* HEADERS: accumulate headers into a buffer to send to stdout in _jst_finish*/ -_jst_header_buffer = "Content-type: text/html"; +_jst_header_content_type_set = false; +_jst_header_buffer = ""; function header(str) { - if(str.toLowerCase().indexOf('location:') == 0) + lstr = str.toLowerCase(); + if(lstr.indexOf('location:') == 0) { _jst_header_buffer = "HTTP/1.0 302 Ok\r\n"; _jst_header_buffer += "Status: 302 Moved\r\n"; - _jst_header_buffer += str; + _jst_header_buffer += str + "\r\n"; } else { - _jst_header_buffer += "\n" + str; + if(lstr.indexOf('content-type:') == 0) + { + _jst_header_content_type_set = true; + if(lstr.indexOf('application/json') != -1) + _jst_header_buffer += "Content-Type: text/html\r\n"; + } + _jst_header_buffer += str + "\r\n"; } } @@ -22,6 +48,7 @@ function header(str) _jst_echo_buffer = ""; function echo(str) { + str = (typeof(str)!="undefined") ? str : ""; _jst_echo_buffer += str; } @@ -29,9 +56,9 @@ function echo(str) and it will send the headers and content to stdout */ function _jst_finish() { - print(_jst_header_buffer); - print("\r\n\r\n\n"); - print(_jst_echo_buffer); + if(!_jst_header_content_type_set) + print("Content-type: text/html\r"); + print(_jst_header_buffer + "\r\n" + _jst_echo_buffer); } /* EXIT: there is no way to simply quit in the middle of a script, so @@ -61,32 +88,92 @@ var $_SERVER = new Proxy({}, { /* SESSION: session data set by web app, saved to disk, and referenced by session id stored in cookie */ var $_SESSION = {}; var $_jst_session = null; -function session_start() +var $_val_input = {}; +function _jst_session_cookie() { - if($_jst_session) - return; - ccsp_session.start(); - header("Set-Cookie: DUKSID=" + ccsp_session.getId() + ";"); - $_jst_session = ccsp_session.getData(); - $_SESSION = new Proxy($_jst_session, { + 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 _jst_session_proxy($session) +{ + var $session_id = ccsp_session.getId(); + return new Proxy($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.getId() === $session_id) + 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.getId() === $session_id) + ccsp_session.setData(obj); } return true; } }); } +function session_start() +{ + if($_jst_session) + { + if(ccsp_session.start()) + return true; + + $_jst_session = null; + $_SESSION = {}; + return false; + } + if($_val_input == 1) + { + $_val_input = 0; + 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()); + } + $_jst_session = ccsp_session.getData(); + if($_jst_session === null || typeof($_jst_session) !== 'object') + $_jst_session = {}; + $_SESSION = _jst_session_proxy($_jst_session); + return true; +} +function session_create(){ + 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 = _jst_session_proxy($_jst_session); + return true; +} function session_id() { return ccsp_session.getId(); @@ -97,6 +184,7 @@ function session_status() } function session_destroy() { + header(_jst_expire_session_cookie()); delete $_jst_session; $_jst_session = null; delete $_SESSION; @@ -104,7 +192,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() { @@ -127,7 +222,41 @@ if(postData) $_POST[postValue[0]] = decodeURIComponent(value); } else + { print("unexpected post data"); + $_val_input = 1; + } + } +} + +/* FILES: multipart/form-data files via stdin */ +$_FILES={}; +var filesData = ccsp_post.getFiles(); +if(filesData) +{ + var fileList = filesData.split(';'); + for(var i = 0; i < fileList.length; ++i) + { + var fileData = fileList[i].split('&'); + var fileId = null; + for(var j = 0; j < fileData.length; ++j) + { + var fileValue = fileData[j].split('='); + if(fileValue.length == 2) + { + if(!fileId) + { + fileId = decodeURIComponent(fileValue[1]); + $_FILES[fileId]={}; + } + else + { + $_FILES[fileId][decodeURIComponent(fileValue[0])]=decodeURIComponent(fileValue[1]); + } + } + else + print("unexpected file data"); + } } } @@ -159,6 +288,10 @@ function include($filepath) + + + + //jst parser should skip all these and copy into output verbatim var path1 = "include/once.jst" diff --git a/tests/parser/jst_parser_include_unknown.jst.parsed b/tests/parser/jst_parser_include_unknown.jst.parsed index bdaffe7..04163f5 100644 --- a/tests/parser/jst_parser_include_unknown.jst.parsed +++ b/tests/parser/jst_parser_include_unknown.jst.parsed @@ -1,19 +1,45 @@ +/* + If not stated otherwise in this file or this component's Licenses.txt file the + following copyright and licenses apply: + + Copyright 2018 RDK Management + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ try { /* HEADERS: accumulate headers into a buffer to send to stdout in _jst_finish*/ -_jst_header_buffer = "Content-type: text/html"; +_jst_header_content_type_set = false; +_jst_header_buffer = ""; function header(str) { - if(str.toLowerCase().indexOf('location:') == 0) + lstr = str.toLowerCase(); + if(lstr.indexOf('location:') == 0) { _jst_header_buffer = "HTTP/1.0 302 Ok\r\n"; _jst_header_buffer += "Status: 302 Moved\r\n"; - _jst_header_buffer += str; + _jst_header_buffer += str + "\r\n"; } else { - _jst_header_buffer += "\n" + str; + if(lstr.indexOf('content-type:') == 0) + { + _jst_header_content_type_set = true; + if(lstr.indexOf('application/json') != -1) + _jst_header_buffer += "Content-Type: text/html\r\n"; + } + _jst_header_buffer += str + "\r\n"; } } @@ -22,6 +48,7 @@ function header(str) _jst_echo_buffer = ""; function echo(str) { + str = (typeof(str)!="undefined") ? str : ""; _jst_echo_buffer += str; } @@ -29,9 +56,9 @@ function echo(str) and it will send the headers and content to stdout */ function _jst_finish() { - print(_jst_header_buffer); - print("\r\n\r\n\n"); - print(_jst_echo_buffer); + if(!_jst_header_content_type_set) + print("Content-type: text/html\r"); + print(_jst_header_buffer + "\r\n" + _jst_echo_buffer); } /* EXIT: there is no way to simply quit in the middle of a script, so @@ -61,32 +88,92 @@ var $_SERVER = new Proxy({}, { /* SESSION: session data set by web app, saved to disk, and referenced by session id stored in cookie */ var $_SESSION = {}; var $_jst_session = null; -function session_start() +var $_val_input = {}; +function _jst_session_cookie() { - if($_jst_session) - return; - ccsp_session.start(); - header("Set-Cookie: DUKSID=" + ccsp_session.getId() + ";"); - $_jst_session = ccsp_session.getData(); - $_SESSION = new Proxy($_jst_session, { + 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 _jst_session_proxy($session) +{ + var $session_id = ccsp_session.getId(); + return new Proxy($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.getId() === $session_id) + 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.getId() === $session_id) + ccsp_session.setData(obj); } return true; } }); } +function session_start() +{ + if($_jst_session) + { + if(ccsp_session.start()) + return true; + + $_jst_session = null; + $_SESSION = {}; + return false; + } + if($_val_input == 1) + { + $_val_input = 0; + 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()); + } + $_jst_session = ccsp_session.getData(); + if($_jst_session === null || typeof($_jst_session) !== 'object') + $_jst_session = {}; + $_SESSION = _jst_session_proxy($_jst_session); + return true; +} +function session_create(){ + 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 = _jst_session_proxy($_jst_session); + return true; +} function session_id() { return ccsp_session.getId(); @@ -97,6 +184,7 @@ function session_status() } function session_destroy() { + header(_jst_expire_session_cookie()); delete $_jst_session; $_jst_session = null; delete $_SESSION; @@ -104,7 +192,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() { @@ -127,7 +222,41 @@ if(postData) $_POST[postValue[0]] = decodeURIComponent(value); } else + { print("unexpected post data"); + $_val_input = 1; + } + } +} + +/* FILES: multipart/form-data files via stdin */ +$_FILES={}; +var filesData = ccsp_post.getFiles(); +if(filesData) +{ + var fileList = filesData.split(';'); + for(var i = 0; i < fileList.length; ++i) + { + var fileData = fileList[i].split('&'); + var fileId = null; + for(var j = 0; j < fileData.length; ++j) + { + var fileValue = fileData[j].split('='); + if(fileValue.length == 2) + { + if(!fileId) + { + fileId = decodeURIComponent(fileValue[1]); + $_FILES[fileId]={}; + } + else + { + $_FILES[fileId][decodeURIComponent(fileValue[0])]=decodeURIComponent(fileValue[1]); + } + } + else + print("unexpected file data"); + } } } @@ -159,6 +288,10 @@ function include($filepath) + + + + //jst parser should skip all these and copy into output verbatim include("path1'); diff --git a/tests/parser/jst_parser_line_feeds.jst.parsed b/tests/parser/jst_parser_line_feeds.jst.parsed index 5865d89..7b5de0d 100644 --- a/tests/parser/jst_parser_line_feeds.jst.parsed +++ b/tests/parser/jst_parser_line_feeds.jst.parsed @@ -1,19 +1,45 @@ +/* + If not stated otherwise in this file or this component's Licenses.txt file the + following copyright and licenses apply: + + Copyright 2018 RDK Management + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ try { /* HEADERS: accumulate headers into a buffer to send to stdout in _jst_finish*/ -_jst_header_buffer = "Content-type: text/html"; +_jst_header_content_type_set = false; +_jst_header_buffer = ""; function header(str) { - if(str.toLowerCase().indexOf('location:') == 0) + lstr = str.toLowerCase(); + if(lstr.indexOf('location:') == 0) { _jst_header_buffer = "HTTP/1.0 302 Ok\r\n"; _jst_header_buffer += "Status: 302 Moved\r\n"; - _jst_header_buffer += str; + _jst_header_buffer += str + "\r\n"; } else { - _jst_header_buffer += "\n" + str; + if(lstr.indexOf('content-type:') == 0) + { + _jst_header_content_type_set = true; + if(lstr.indexOf('application/json') != -1) + _jst_header_buffer += "Content-Type: text/html\r\n"; + } + _jst_header_buffer += str + "\r\n"; } } @@ -22,6 +48,7 @@ function header(str) _jst_echo_buffer = ""; function echo(str) { + str = (typeof(str)!="undefined") ? str : ""; _jst_echo_buffer += str; } @@ -29,9 +56,9 @@ function echo(str) and it will send the headers and content to stdout */ function _jst_finish() { - print(_jst_header_buffer); - print("\r\n\r\n\n"); - print(_jst_echo_buffer); + if(!_jst_header_content_type_set) + print("Content-type: text/html\r"); + print(_jst_header_buffer + "\r\n" + _jst_echo_buffer); } /* EXIT: there is no way to simply quit in the middle of a script, so @@ -61,32 +88,92 @@ var $_SERVER = new Proxy({}, { /* SESSION: session data set by web app, saved to disk, and referenced by session id stored in cookie */ var $_SESSION = {}; var $_jst_session = null; -function session_start() +var $_val_input = {}; +function _jst_session_cookie() { - if($_jst_session) - return; - ccsp_session.start(); - header("Set-Cookie: DUKSID=" + ccsp_session.getId() + ";"); - $_jst_session = ccsp_session.getData(); - $_SESSION = new Proxy($_jst_session, { + 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 _jst_session_proxy($session) +{ + var $session_id = ccsp_session.getId(); + return new Proxy($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.getId() === $session_id) + 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.getId() === $session_id) + ccsp_session.setData(obj); } return true; } }); } +function session_start() +{ + if($_jst_session) + { + if(ccsp_session.start()) + return true; + + $_jst_session = null; + $_SESSION = {}; + return false; + } + if($_val_input == 1) + { + $_val_input = 0; + 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()); + } + $_jst_session = ccsp_session.getData(); + if($_jst_session === null || typeof($_jst_session) !== 'object') + $_jst_session = {}; + $_SESSION = _jst_session_proxy($_jst_session); + return true; +} +function session_create(){ + 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 = _jst_session_proxy($_jst_session); + return true; +} function session_id() { return ccsp_session.getId(); @@ -97,6 +184,7 @@ function session_status() } function session_destroy() { + header(_jst_expire_session_cookie()); delete $_jst_session; $_jst_session = null; delete $_SESSION; @@ -104,7 +192,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() { @@ -127,7 +222,41 @@ if(postData) $_POST[postValue[0]] = decodeURIComponent(value); } else + { print("unexpected post data"); + $_val_input = 1; + } + } +} + +/* FILES: multipart/form-data files via stdin */ +$_FILES={}; +var filesData = ccsp_post.getFiles(); +if(filesData) +{ + var fileList = filesData.split(';'); + for(var i = 0; i < fileList.length; ++i) + { + var fileData = fileList[i].split('&'); + var fileId = null; + for(var j = 0; j < fileData.length; ++j) + { + var fileValue = fileData[j].split('='); + if(fileValue.length == 2) + { + if(!fileId) + { + fileId = decodeURIComponent(fileValue[1]); + $_FILES[fileId]={}; + } + else + { + $_FILES[fileId][decodeURIComponent(fileValue[0])]=decodeURIComponent(fileValue[1]); + } + } + else + print("unexpected file data"); + } } } @@ -157,6 +286,10 @@ function include($filepath) /* begin application code */ + + + + echo('a\n\ \n\ b\n\ diff --git a/tests/parser/jst_parser_single_quotes.jst.parsed b/tests/parser/jst_parser_single_quotes.jst.parsed index c4013f8..45d56c0 100644 --- a/tests/parser/jst_parser_single_quotes.jst.parsed +++ b/tests/parser/jst_parser_single_quotes.jst.parsed @@ -1,19 +1,45 @@ +/* + If not stated otherwise in this file or this component's Licenses.txt file the + following copyright and licenses apply: + + Copyright 2018 RDK Management + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ try { /* HEADERS: accumulate headers into a buffer to send to stdout in _jst_finish*/ -_jst_header_buffer = "Content-type: text/html"; +_jst_header_content_type_set = false; +_jst_header_buffer = ""; function header(str) { - if(str.toLowerCase().indexOf('location:') == 0) + lstr = str.toLowerCase(); + if(lstr.indexOf('location:') == 0) { _jst_header_buffer = "HTTP/1.0 302 Ok\r\n"; _jst_header_buffer += "Status: 302 Moved\r\n"; - _jst_header_buffer += str; + _jst_header_buffer += str + "\r\n"; } else { - _jst_header_buffer += "\n" + str; + if(lstr.indexOf('content-type:') == 0) + { + _jst_header_content_type_set = true; + if(lstr.indexOf('application/json') != -1) + _jst_header_buffer += "Content-Type: text/html\r\n"; + } + _jst_header_buffer += str + "\r\n"; } } @@ -22,6 +48,7 @@ function header(str) _jst_echo_buffer = ""; function echo(str) { + str = (typeof(str)!="undefined") ? str : ""; _jst_echo_buffer += str; } @@ -29,9 +56,9 @@ function echo(str) and it will send the headers and content to stdout */ function _jst_finish() { - print(_jst_header_buffer); - print("\r\n\r\n\n"); - print(_jst_echo_buffer); + if(!_jst_header_content_type_set) + print("Content-type: text/html\r"); + print(_jst_header_buffer + "\r\n" + _jst_echo_buffer); } /* EXIT: there is no way to simply quit in the middle of a script, so @@ -61,32 +88,92 @@ var $_SERVER = new Proxy({}, { /* SESSION: session data set by web app, saved to disk, and referenced by session id stored in cookie */ var $_SESSION = {}; var $_jst_session = null; -function session_start() +var $_val_input = {}; +function _jst_session_cookie() { - if($_jst_session) - return; - ccsp_session.start(); - header("Set-Cookie: DUKSID=" + ccsp_session.getId() + ";"); - $_jst_session = ccsp_session.getData(); - $_SESSION = new Proxy($_jst_session, { + 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 _jst_session_proxy($session) +{ + var $session_id = ccsp_session.getId(); + return new Proxy($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.getId() === $session_id) + 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.getId() === $session_id) + ccsp_session.setData(obj); } return true; } }); } +function session_start() +{ + if($_jst_session) + { + if(ccsp_session.start()) + return true; + + $_jst_session = null; + $_SESSION = {}; + return false; + } + if($_val_input == 1) + { + $_val_input = 0; + 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()); + } + $_jst_session = ccsp_session.getData(); + if($_jst_session === null || typeof($_jst_session) !== 'object') + $_jst_session = {}; + $_SESSION = _jst_session_proxy($_jst_session); + return true; +} +function session_create(){ + 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 = _jst_session_proxy($_jst_session); + return true; +} function session_id() { return ccsp_session.getId(); @@ -97,6 +184,7 @@ function session_status() } function session_destroy() { + header(_jst_expire_session_cookie()); delete $_jst_session; $_jst_session = null; delete $_SESSION; @@ -104,7 +192,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() { @@ -127,7 +222,41 @@ if(postData) $_POST[postValue[0]] = decodeURIComponent(value); } else + { print("unexpected post data"); + $_val_input = 1; + } + } +} + +/* FILES: multipart/form-data files via stdin */ +$_FILES={}; +var filesData = ccsp_post.getFiles(); +if(filesData) +{ + var fileList = filesData.split(';'); + for(var i = 0; i < fileList.length; ++i) + { + var fileData = fileList[i].split('&'); + var fileId = null; + for(var j = 0; j < fileData.length; ++j) + { + var fileValue = fileData[j].split('='); + if(fileValue.length == 2) + { + if(!fileId) + { + fileId = decodeURIComponent(fileValue[1]); + $_FILES[fileId]={}; + } + else + { + $_FILES[fileId][decodeURIComponent(fileValue[0])]=decodeURIComponent(fileValue[1]); + } + } + else + print("unexpected file data"); + } } } @@ -157,6 +286,10 @@ function include($filepath) /* begin application code */ + + + + echo('\'a b c\'\n\ '); /* end application code */ diff --git a/tests/parser/jst_parser_skip_whitespace.jst.parsed b/tests/parser/jst_parser_skip_whitespace.jst.parsed index 8269b12..4d38071 100644 --- a/tests/parser/jst_parser_skip_whitespace.jst.parsed +++ b/tests/parser/jst_parser_skip_whitespace.jst.parsed @@ -1,19 +1,45 @@ +/* + If not stated otherwise in this file or this component's Licenses.txt file the + following copyright and licenses apply: + + Copyright 2018 RDK Management + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ try { /* HEADERS: accumulate headers into a buffer to send to stdout in _jst_finish*/ -_jst_header_buffer = "Content-type: text/html"; +_jst_header_content_type_set = false; +_jst_header_buffer = ""; function header(str) { - if(str.toLowerCase().indexOf('location:') == 0) + lstr = str.toLowerCase(); + if(lstr.indexOf('location:') == 0) { _jst_header_buffer = "HTTP/1.0 302 Ok\r\n"; _jst_header_buffer += "Status: 302 Moved\r\n"; - _jst_header_buffer += str; + _jst_header_buffer += str + "\r\n"; } else { - _jst_header_buffer += "\n" + str; + if(lstr.indexOf('content-type:') == 0) + { + _jst_header_content_type_set = true; + if(lstr.indexOf('application/json') != -1) + _jst_header_buffer += "Content-Type: text/html\r\n"; + } + _jst_header_buffer += str + "\r\n"; } } @@ -22,6 +48,7 @@ function header(str) _jst_echo_buffer = ""; function echo(str) { + str = (typeof(str)!="undefined") ? str : ""; _jst_echo_buffer += str; } @@ -29,9 +56,9 @@ function echo(str) and it will send the headers and content to stdout */ function _jst_finish() { - print(_jst_header_buffer); - print("\r\n\r\n\n"); - print(_jst_echo_buffer); + if(!_jst_header_content_type_set) + print("Content-type: text/html\r"); + print(_jst_header_buffer + "\r\n" + _jst_echo_buffer); } /* EXIT: there is no way to simply quit in the middle of a script, so @@ -61,32 +88,92 @@ var $_SERVER = new Proxy({}, { /* SESSION: session data set by web app, saved to disk, and referenced by session id stored in cookie */ var $_SESSION = {}; var $_jst_session = null; -function session_start() +var $_val_input = {}; +function _jst_session_cookie() { - if($_jst_session) - return; - ccsp_session.start(); - header("Set-Cookie: DUKSID=" + ccsp_session.getId() + ";"); - $_jst_session = ccsp_session.getData(); - $_SESSION = new Proxy($_jst_session, { + 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 _jst_session_proxy($session) +{ + var $session_id = ccsp_session.getId(); + return new Proxy($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.getId() === $session_id) + 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.getId() === $session_id) + ccsp_session.setData(obj); } return true; } }); } +function session_start() +{ + if($_jst_session) + { + if(ccsp_session.start()) + return true; + + $_jst_session = null; + $_SESSION = {}; + return false; + } + if($_val_input == 1) + { + $_val_input = 0; + 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()); + } + $_jst_session = ccsp_session.getData(); + if($_jst_session === null || typeof($_jst_session) !== 'object') + $_jst_session = {}; + $_SESSION = _jst_session_proxy($_jst_session); + return true; +} +function session_create(){ + 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 = _jst_session_proxy($_jst_session); + return true; +} function session_id() { return ccsp_session.getId(); @@ -97,6 +184,7 @@ function session_status() } function session_destroy() { + header(_jst_expire_session_cookie()); delete $_jst_session; $_jst_session = null; delete $_SESSION; @@ -104,7 +192,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() { @@ -127,7 +222,41 @@ if(postData) $_POST[postValue[0]] = decodeURIComponent(value); } else + { print("unexpected post data"); + $_val_input = 1; + } + } +} + +/* FILES: multipart/form-data files via stdin */ +$_FILES={}; +var filesData = ccsp_post.getFiles(); +if(filesData) +{ + var fileList = filesData.split(';'); + for(var i = 0; i < fileList.length; ++i) + { + var fileData = fileList[i].split('&'); + var fileId = null; + for(var j = 0; j < fileData.length; ++j) + { + var fileValue = fileData[j].split('='); + if(fileValue.length == 2) + { + if(!fileId) + { + fileId = decodeURIComponent(fileValue[1]); + $_FILES[fileId]={}; + } + else + { + $_FILES[fileId][decodeURIComponent(fileValue[0])]=decodeURIComponent(fileValue[1]); + } + } + else + print("unexpected file data"); + } } } @@ -157,6 +286,10 @@ function include($filepath) /* begin application code */ + + + + echo('begin content\n\ ');echo('\n\ end content\n\ diff --git a/tests/parser/jst_parser_template_block_content.jst.parsed b/tests/parser/jst_parser_template_block_content.jst.parsed index 292d504..0d58a29 100644 --- a/tests/parser/jst_parser_template_block_content.jst.parsed +++ b/tests/parser/jst_parser_template_block_content.jst.parsed @@ -1,19 +1,45 @@ +/* + If not stated otherwise in this file or this component's Licenses.txt file the + following copyright and licenses apply: + + Copyright 2018 RDK Management + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ try { /* HEADERS: accumulate headers into a buffer to send to stdout in _jst_finish*/ -_jst_header_buffer = "Content-type: text/html"; +_jst_header_content_type_set = false; +_jst_header_buffer = ""; function header(str) { - if(str.toLowerCase().indexOf('location:') == 0) + lstr = str.toLowerCase(); + if(lstr.indexOf('location:') == 0) { _jst_header_buffer = "HTTP/1.0 302 Ok\r\n"; _jst_header_buffer += "Status: 302 Moved\r\n"; - _jst_header_buffer += str; + _jst_header_buffer += str + "\r\n"; } else { - _jst_header_buffer += "\n" + str; + if(lstr.indexOf('content-type:') == 0) + { + _jst_header_content_type_set = true; + if(lstr.indexOf('application/json') != -1) + _jst_header_buffer += "Content-Type: text/html\r\n"; + } + _jst_header_buffer += str + "\r\n"; } } @@ -22,6 +48,7 @@ function header(str) _jst_echo_buffer = ""; function echo(str) { + str = (typeof(str)!="undefined") ? str : ""; _jst_echo_buffer += str; } @@ -29,9 +56,9 @@ function echo(str) and it will send the headers and content to stdout */ function _jst_finish() { - print(_jst_header_buffer); - print("\r\n\r\n\n"); - print(_jst_echo_buffer); + if(!_jst_header_content_type_set) + print("Content-type: text/html\r"); + print(_jst_header_buffer + "\r\n" + _jst_echo_buffer); } /* EXIT: there is no way to simply quit in the middle of a script, so @@ -61,32 +88,92 @@ var $_SERVER = new Proxy({}, { /* SESSION: session data set by web app, saved to disk, and referenced by session id stored in cookie */ var $_SESSION = {}; var $_jst_session = null; -function session_start() +var $_val_input = {}; +function _jst_session_cookie() { - if($_jst_session) - return; - ccsp_session.start(); - header("Set-Cookie: DUKSID=" + ccsp_session.getId() + ";"); - $_jst_session = ccsp_session.getData(); - $_SESSION = new Proxy($_jst_session, { + 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 _jst_session_proxy($session) +{ + var $session_id = ccsp_session.getId(); + return new Proxy($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.getId() === $session_id) + 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.getId() === $session_id) + ccsp_session.setData(obj); } return true; } }); } +function session_start() +{ + if($_jst_session) + { + if(ccsp_session.start()) + return true; + + $_jst_session = null; + $_SESSION = {}; + return false; + } + if($_val_input == 1) + { + $_val_input = 0; + 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()); + } + $_jst_session = ccsp_session.getData(); + if($_jst_session === null || typeof($_jst_session) !== 'object') + $_jst_session = {}; + $_SESSION = _jst_session_proxy($_jst_session); + return true; +} +function session_create(){ + 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 = _jst_session_proxy($_jst_session); + return true; +} function session_id() { return ccsp_session.getId(); @@ -97,6 +184,7 @@ function session_status() } function session_destroy() { + header(_jst_expire_session_cookie()); delete $_jst_session; $_jst_session = null; delete $_SESSION; @@ -104,7 +192,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() { @@ -127,7 +222,41 @@ if(postData) $_POST[postValue[0]] = decodeURIComponent(value); } else + { print("unexpected post data"); + $_val_input = 1; + } + } +} + +/* FILES: multipart/form-data files via stdin */ +$_FILES={}; +var filesData = ccsp_post.getFiles(); +if(filesData) +{ + var fileList = filesData.split(';'); + for(var i = 0; i < fileList.length; ++i) + { + var fileData = fileList[i].split('&'); + var fileId = null; + for(var j = 0; j < fileData.length; ++j) + { + var fileValue = fileData[j].split('='); + if(fileValue.length == 2) + { + if(!fileId) + { + fileId = decodeURIComponent(fileValue[1]); + $_FILES[fileId]={}; + } + else + { + $_FILES[fileId][decodeURIComponent(fileValue[0])]=decodeURIComponent(fileValue[1]); + } + } + else + print("unexpected file data"); + } } } @@ -157,6 +286,10 @@ function include($filepath) /* begin application code */ + + + + echo("Hello World"); /* end application code */ exit(0); diff --git a/tests/parser/jst_parser_template_block_string.jst.parsed b/tests/parser/jst_parser_template_block_string.jst.parsed index 9238bf5..429a92a 100644 --- a/tests/parser/jst_parser_template_block_string.jst.parsed +++ b/tests/parser/jst_parser_template_block_string.jst.parsed @@ -1,19 +1,45 @@ +/* + If not stated otherwise in this file or this component's Licenses.txt file the + following copyright and licenses apply: + + Copyright 2018 RDK Management + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ try { /* HEADERS: accumulate headers into a buffer to send to stdout in _jst_finish*/ -_jst_header_buffer = "Content-type: text/html"; +_jst_header_content_type_set = false; +_jst_header_buffer = ""; function header(str) { - if(str.toLowerCase().indexOf('location:') == 0) + lstr = str.toLowerCase(); + if(lstr.indexOf('location:') == 0) { _jst_header_buffer = "HTTP/1.0 302 Ok\r\n"; _jst_header_buffer += "Status: 302 Moved\r\n"; - _jst_header_buffer += str; + _jst_header_buffer += str + "\r\n"; } else { - _jst_header_buffer += "\n" + str; + if(lstr.indexOf('content-type:') == 0) + { + _jst_header_content_type_set = true; + if(lstr.indexOf('application/json') != -1) + _jst_header_buffer += "Content-Type: text/html\r\n"; + } + _jst_header_buffer += str + "\r\n"; } } @@ -22,6 +48,7 @@ function header(str) _jst_echo_buffer = ""; function echo(str) { + str = (typeof(str)!="undefined") ? str : ""; _jst_echo_buffer += str; } @@ -29,9 +56,9 @@ function echo(str) and it will send the headers and content to stdout */ function _jst_finish() { - print(_jst_header_buffer); - print("\r\n\r\n\n"); - print(_jst_echo_buffer); + if(!_jst_header_content_type_set) + print("Content-type: text/html\r"); + print(_jst_header_buffer + "\r\n" + _jst_echo_buffer); } /* EXIT: there is no way to simply quit in the middle of a script, so @@ -61,32 +88,92 @@ var $_SERVER = new Proxy({}, { /* SESSION: session data set by web app, saved to disk, and referenced by session id stored in cookie */ var $_SESSION = {}; var $_jst_session = null; -function session_start() +var $_val_input = {}; +function _jst_session_cookie() { - if($_jst_session) - return; - ccsp_session.start(); - header("Set-Cookie: DUKSID=" + ccsp_session.getId() + ";"); - $_jst_session = ccsp_session.getData(); - $_SESSION = new Proxy($_jst_session, { + 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 _jst_session_proxy($session) +{ + var $session_id = ccsp_session.getId(); + return new Proxy($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.getId() === $session_id) + 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.getId() === $session_id) + ccsp_session.setData(obj); } return true; } }); } +function session_start() +{ + if($_jst_session) + { + if(ccsp_session.start()) + return true; + + $_jst_session = null; + $_SESSION = {}; + return false; + } + if($_val_input == 1) + { + $_val_input = 0; + 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()); + } + $_jst_session = ccsp_session.getData(); + if($_jst_session === null || typeof($_jst_session) !== 'object') + $_jst_session = {}; + $_SESSION = _jst_session_proxy($_jst_session); + return true; +} +function session_create(){ + 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 = _jst_session_proxy($_jst_session); + return true; +} function session_id() { return ccsp_session.getId(); @@ -97,6 +184,7 @@ function session_status() } function session_destroy() { + header(_jst_expire_session_cookie()); delete $_jst_session; $_jst_session = null; delete $_SESSION; @@ -104,7 +192,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() { @@ -127,7 +222,41 @@ if(postData) $_POST[postValue[0]] = decodeURIComponent(value); } else + { print("unexpected post data"); + $_val_input = 1; + } + } +} + +/* FILES: multipart/form-data files via stdin */ +$_FILES={}; +var filesData = ccsp_post.getFiles(); +if(filesData) +{ + var fileList = filesData.split(';'); + for(var i = 0; i < fileList.length; ++i) + { + var fileData = fileList[i].split('&'); + var fileId = null; + for(var j = 0; j < fileData.length; ++j) + { + var fileValue = fileData[j].split('='); + if(fileValue.length == 2) + { + if(!fileId) + { + fileId = decodeURIComponent(fileValue[1]); + $_FILES[fileId]={}; + } + else + { + $_FILES[fileId][decodeURIComponent(fileValue[0])]=decodeURIComponent(fileValue[1]); + } + } + else + print("unexpected file data"); + } } } @@ -158,6 +287,10 @@ function include($filepath) /* begin application code */ + + + + var world="World"; echo('\n\ Hello ');echo(world);echo('!\n\ diff --git a/tests/parser/jst_prefix.js b/tests/parser/jst_prefix.js index 6a2317b..c8c9942 100644 --- a/tests/parser/jst_prefix.js +++ b/tests/parser/jst_prefix.js @@ -16,23 +16,30 @@ See the License for the specific language governing permissions and limitations under the License. */ - try { /* HEADERS: accumulate headers into a buffer to send to stdout in _jst_finish*/ -_jst_header_buffer = "Content-type: text/html"; +_jst_header_content_type_set = false; +_jst_header_buffer = ""; function header(str) { - if(str.toLowerCase().indexOf('location:') == 0) + lstr = str.toLowerCase(); + if(lstr.indexOf('location:') == 0) { _jst_header_buffer = "HTTP/1.0 302 Ok\r\n"; _jst_header_buffer += "Status: 302 Moved\r\n"; - _jst_header_buffer += str; + _jst_header_buffer += str + "\r\n"; } else { - _jst_header_buffer += "\n" + str; + if(lstr.indexOf('content-type:') == 0) + { + _jst_header_content_type_set = true; + if(lstr.indexOf('application/json') != -1) + _jst_header_buffer += "Content-Type: text/html\r\n"; + } + _jst_header_buffer += str + "\r\n"; } } @@ -41,6 +48,7 @@ function header(str) _jst_echo_buffer = ""; function echo(str) { + str = (typeof(str)!="undefined") ? str : ""; _jst_echo_buffer += str; } @@ -48,9 +56,9 @@ function echo(str) and it will send the headers and content to stdout */ function _jst_finish() { - print(_jst_header_buffer); - print("\r\n\r\n\n"); - print(_jst_echo_buffer); + if(!_jst_header_content_type_set) + print("Content-type: text/html\r"); + print(_jst_header_buffer + "\r\n" + _jst_echo_buffer); } /* EXIT: there is no way to simply quit in the middle of a script, so @@ -80,26 +88,31 @@ var $_SERVER = new Proxy({}, { /* SESSION: session data set by web app, saved to disk, and referenced by session id stored in cookie */ var $_SESSION = {}; var $_jst_session = null; -function session_start() +var $_val_input = {}; +function _jst_session_cookie() { - if($_jst_session) - return true; - if(!ccsp_session.start()) - { - /* A stale cookie must not create a proxy backed by an inactive session. */ - $_jst_session = null; - $_SESSION = {}; - return false; - } - header("Set-Cookie: DUKSID=" + ccsp_session.getId() + ";"); - $_jst_session = ccsp_session.getData(); - $_SESSION = new Proxy($_jst_session, { + 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 _jst_session_proxy($session) +{ + var $session_id = ccsp_session.getId(); + return new Proxy($session, { get: function(obj, prop) { return obj[prop]; }, set: function(obj, prop, val){ obj[prop] = val; - if(ccsp_session.getStatus()) + if(ccsp_session.getStatus() && ccsp_session.getId() === $session_id) ccsp_session.setData(obj); return true; }, @@ -107,12 +120,58 @@ function session_start() if(prop in obj) { delete obj[prop]; - if(ccsp_session.getStatus()) + if(ccsp_session.getStatus() && ccsp_session.getId() === $session_id) ccsp_session.setData(obj); } return true; } }); +} +function session_start() +{ + if($_jst_session) + { + if(ccsp_session.start()) + return true; + + $_jst_session = null; + $_SESSION = {}; + return false; + } + if($_val_input == 1) + { + $_val_input = 0; + 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()); + } + $_jst_session = ccsp_session.getData(); + if($_jst_session === null || typeof($_jst_session) !== 'object') + $_jst_session = {}; + $_SESSION = _jst_session_proxy($_jst_session); + return true; +} +function session_create(){ + 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 = _jst_session_proxy($_jst_session); return true; } function session_id() @@ -125,6 +184,7 @@ function session_status() } function session_destroy() { + header(_jst_expire_session_cookie()); delete $_jst_session; $_jst_session = null; delete $_SESSION; @@ -162,7 +222,41 @@ if(postData) $_POST[postValue[0]] = decodeURIComponent(value); } else + { print("unexpected post data"); + $_val_input = 1; + } + } +} + +/* FILES: multipart/form-data files via stdin */ +$_FILES={}; +var filesData = ccsp_post.getFiles(); +if(filesData) +{ + var fileList = filesData.split(';'); + for(var i = 0; i < fileList.length; ++i) + { + var fileData = fileList[i].split('&'); + var fileId = null; + for(var j = 0; j < fileData.length; ++j) + { + var fileValue = fileData[j].split('='); + if(fileValue.length == 2) + { + if(!fileId) + { + fileId = decodeURIComponent(fileValue[1]); + $_FILES[fileId]={}; + } + else + { + $_FILES[fileId][decodeURIComponent(fileValue[0])]=decodeURIComponent(fileValue[1]); + } + } + else + print("unexpected file data"); + } } } From 6cb039b969d9ff9a31a9a7b7eff87a2fe1e12e13 Mon Sep 17 00:00:00 2001 From: Pavan Kumar Reddy Balireddy Date: Wed, 16 Sep 2026 13:34:44 +0530 Subject: [PATCH 12/14] fix(session): persist empty sessions before revalidation --- source/jst_session.c | 23 +++++++++++++++++++++++ tests/parser_test.cpp | 9 ++++++--- 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/source/jst_session.c b/source/jst_session.c index 919873b..6f44432 100644 --- a/source/jst_session.c +++ b/source/jst_session.c @@ -335,6 +335,29 @@ static duk_ret_t session_create(duk_context *ctx) snprintf(session_identifier, SESSION_ID_LENGTH+1, "%s%s", SESSION_PREFIX, session_id); free(session_id); + { + char filename[SESSION_FILE_MAX_PATH]; + FILE* file; + + if(!get_session_file_path(session_identifier, filename, sizeof(filename))) + { + free(session_identifier); + session_identifier = NULL; + RETURN_FALSE; + } + + file = fopen(filename, "w"); + if(!file) + { + CosaPhpExtLog("Failed to create session file %s: %s\n", filename, strerror(errno)); + free(session_identifier); + session_identifier = NULL; + RETURN_FALSE; + } + + fclose(file); + } + RETURN_TRUE; return 1; } diff --git a/tests/parser_test.cpp b/tests/parser_test.cpp index c9c017b..7804d85 100644 --- a/tests/parser_test.cpp +++ b/tests/parser_test.cpp @@ -402,11 +402,14 @@ TEST(general, session_create_destroy_cycle_and_id_format) snprintf(first_session_file, sizeof(first_session_file), "/tmp/%s", first_id); duk_pop_2(ctx); - FILE* stale = fopen(first_session_file, "w"); - ASSERT_NE(stale, nullptr); - fclose(stale); ASSERT_EQ(access(first_session_file, F_OK), 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_TRUE(duk_get_boolean(ctx, -1)); + duk_pop_2(ctx); + duk_get_global_string(ctx, "ccsp_session"); duk_get_prop_string(ctx, -1, "create"); ASSERT_EQ(duk_pcall(ctx, 0), DUK_EXEC_SUCCESS); From bd50b741b016601bfdc0a89efd18270ccdcf043b Mon Sep 17 00:00:00 2001 From: Pavan Kumar Reddy Balireddy Date: Wed, 16 Sep 2026 13:56:12 +0530 Subject: [PATCH 13/14] fix(session): restrict backing file permissions Create session files with owner-only access and make HTTPS cookie rejection tests independent of inherited CGI environment variables. --- source/jst_session.c | 9 +++++---- tests/parser_test.cpp | 7 +++++++ 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/source/jst_session.c b/source/jst_session.c index 6f44432..d2c94ff 100644 --- a/source/jst_session.c +++ b/source/jst_session.c @@ -28,6 +28,7 @@ #include #include #include +#include #include "jst_internal.h" #include #include @@ -337,7 +338,7 @@ static duk_ret_t session_create(duk_context *ctx) { char filename[SESSION_FILE_MAX_PATH]; - FILE* file; + int fd; if(!get_session_file_path(session_identifier, filename, sizeof(filename))) { @@ -346,8 +347,8 @@ static duk_ret_t session_create(duk_context *ctx) RETURN_FALSE; } - file = fopen(filename, "w"); - if(!file) + fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR); + if(fd < 0) { CosaPhpExtLog("Failed to create session file %s: %s\n", filename, strerror(errno)); free(session_identifier); @@ -355,7 +356,7 @@ static duk_ret_t session_create(duk_context *ctx) RETURN_FALSE; } - fclose(file); + close(fd); } RETURN_TRUE; diff --git a/tests/parser_test.cpp b/tests/parser_test.cpp index 7804d85..17f83e6 100644 --- a/tests/parser_test.cpp +++ b/tests/parser_test.cpp @@ -403,6 +403,9 @@ TEST(general, session_create_destroy_cycle_and_id_format) duk_pop_2(ctx); ASSERT_EQ(access(first_session_file, F_OK), 0); + struct stat first_session_stat; + ASSERT_EQ(stat(first_session_file, &first_session_stat), 0); + EXPECT_EQ(first_session_stat.st_mode & 0777, S_IRUSR | S_IWUSR); duk_get_global_string(ctx, "ccsp_session"); duk_get_prop_string(ctx, -1, "start"); @@ -882,6 +885,8 @@ TEST(general, session_prefix_rejects_https_cookie_on_http_request) { EnvVarGuard cookie_guard("HTTP_COOKIE"); EnvVarGuard https_guard("HTTPS"); + EnvVarGuard request_scheme_guard("REQUEST_SCHEME"); + EnvVarGuard ssl_protocol_guard("SSL_PROTOCOL"); const std::string session_id = std::string("jst_sess1") + std::string(31, 'E'); const std::string session_file = "/tmp/" + session_id; @@ -890,6 +895,8 @@ TEST(general, session_prefix_rejects_https_cookie_on_http_request) fclose(file); cookie_guard.set(("DUKSID=" + session_id).c_str()); 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); From 78652d22d7fbce26053c4efd4277198b642fcf53 Mon Sep 17 00:00:00 2001 From: Pavan Kumar Reddy Balireddy Date: Wed, 16 Sep 2026 16:30:20 +0530 Subject: [PATCH 14/14] session handling --- jsts/jst_prefix.js | 19 +++++- source/jst_session.c | 64 +++++++++---------- tests/parser/jst_parser_backslash.jst.parsed | 23 ++++++- .../parser/jst_parser_comment_tag.jst.parsed | 23 ++++++- .../jst_parser_include_code_after.jst.parsed | 23 ++++++- .../jst_parser_include_code_before.jst.parsed | 23 ++++++- .../jst_parser_include_malformed_1.jst.parsed | 23 ++++++- .../jst_parser_include_nested.jst.parsed | 23 ++++++- ...include_not_if_in_block_comment.jst.parsed | 23 ++++++- ...arser_include_not_if_in_content.jst.parsed | 23 ++++++- ..._include_not_if_in_line_comment.jst.parsed | 23 ++++++- .../parser/jst_parser_include_once.jst.parsed | 23 ++++++- .../jst_parser_include_runtime.jst.parsed | 23 ++++++- .../jst_parser_include_unknown.jst.parsed | 23 ++++++- tests/parser/jst_parser_line_feeds.jst.parsed | 23 ++++++- .../jst_parser_single_quotes.jst.parsed | 23 ++++++- .../jst_parser_skip_whitespace.jst.parsed | 23 ++++++- ...t_parser_template_block_content.jst.parsed | 23 ++++++- ...st_parser_template_block_string.jst.parsed | 23 ++++++- tests/parser/jst_prefix.js | 19 +++++- tests/parser_test.cpp | 35 +++++++++- 21 files changed, 438 insertions(+), 90 deletions(-) diff --git a/jsts/jst_prefix.js b/jsts/jst_prefix.js index c8c9942..7934fe6 100644 --- a/jsts/jst_prefix.js +++ b/jsts/jst_prefix.js @@ -103,6 +103,18 @@ function _jst_expire_session_cookie() $cookie += "; secure"; return $cookie; } +function _jst_session_is_current($session_id) +{ + if(!ccsp_session.getStatus() || ccsp_session.getId() !== $session_id) + return false; + + if(ccsp_session.start() && ccsp_session.getId() === $session_id) + return true; + + $_jst_session = null; + $_SESSION = {}; + return false; +} function _jst_session_proxy($session) { var $session_id = ccsp_session.getId(); @@ -112,7 +124,7 @@ function _jst_session_proxy($session) }, set: function(obj, prop, val){ obj[prop] = val; - if(ccsp_session.getStatus() && ccsp_session.getId() === $session_id) + if(_jst_session_is_current($session_id)) ccsp_session.setData(obj); return true; }, @@ -120,7 +132,7 @@ function _jst_session_proxy($session) if(prop in obj) { delete obj[prop]; - if(ccsp_session.getStatus() && ccsp_session.getId() === $session_id) + if(_jst_session_is_current($session_id)) ccsp_session.setData(obj); } return true; @@ -193,7 +205,8 @@ function session_destroy() } function session_unset() { - if(!$_jst_session || !ccsp_session.getStatus()) + var $session_id = ccsp_session.getId(); + if(!$_jst_session || !_jst_session_is_current($session_id)) return false; for(var $key in $_jst_session) diff --git a/source/jst_session.c b/source/jst_session.c index d2c94ff..ac414ca 100644 --- a/source/jst_session.c +++ b/source/jst_session.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -289,8 +290,11 @@ static duk_ret_t session_create(duk_context *ctx) /*create a new one*/ static const char PRINTABLE_HEX_CODES[] = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; int i = 0, n = 0; + int fd; uint8_t bytes[SESSION_ID_BYTES_LENGTH]; char* session_id = NULL; + char* new_session_identifier = NULL; + char filename[SESSION_FILE_MAX_PATH]; session_id = (char*)malloc(SESSION_ID_BYTES_LENGTH+1); if(!session_id) @@ -314,50 +318,46 @@ static duk_ret_t session_create(duk_context *ctx) session_id[0] = request_session_scheme(); - if(session_identifier) - { - char filename[SESSION_FILE_MAX_PATH]; - if(get_session_file_path(session_identifier, filename, sizeof(filename))) - unlink(filename); - free(session_identifier); - session_identifier = NULL; - } - - session_identifier = (char*)malloc(SESSION_ID_LENGTH+1); - if(!session_identifier) + new_session_identifier = (char*)malloc(SESSION_ID_LENGTH+1); + if(!new_session_identifier) { - CosaPhpExtLog("Failed to allocate session_identifier!\n"); + CosaPhpExtLog("Failed to allocate new_session_identifier!\n"); free(session_id); RETURN_FALSE; } - memset(session_identifier, 0, SESSION_ID_LENGTH+1); session_id[SESSION_ID_BYTES_LENGTH] = '\0'; - snprintf(session_identifier, SESSION_ID_LENGTH+1, "%s%s", SESSION_PREFIX, session_id); + snprintf(new_session_identifier, SESSION_ID_LENGTH+1, "%s%s", SESSION_PREFIX, session_id); free(session_id); + if(!get_session_file_path(new_session_identifier, filename, sizeof(filename))) { - char filename[SESSION_FILE_MAX_PATH]; - int fd; - - if(!get_session_file_path(session_identifier, filename, sizeof(filename))) - { - free(session_identifier); - session_identifier = NULL; - RETURN_FALSE; - } + free(new_session_identifier); + RETURN_FALSE; + } - fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR); - if(fd < 0) - { - CosaPhpExtLog("Failed to create session file %s: %s\n", filename, strerror(errno)); - free(session_identifier); - session_identifier = NULL; - RETURN_FALSE; - } + fd = open(filename, O_WRONLY | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR); + if(fd < 0) + { + CosaPhpExtLog("Failed to create session file %s: %s\n", filename, strerror(errno)); + free(new_session_identifier); + RETURN_FALSE; + } + if(close(fd) != 0) + { + CosaPhpExtLog("Failed to close session file %s: %s\n", filename, strerror(errno)); + unlink(filename); + free(new_session_identifier); + RETURN_FALSE; + } - close(fd); + if(session_identifier) + { + if(get_session_file_path(session_identifier, filename, sizeof(filename))) + unlink(filename); + free(session_identifier); } + session_identifier = new_session_identifier; RETURN_TRUE; return 1; diff --git a/tests/parser/jst_parser_backslash.jst.parsed b/tests/parser/jst_parser_backslash.jst.parsed index a7f644d..0828fe4 100644 --- a/tests/parser/jst_parser_backslash.jst.parsed +++ b/tests/parser/jst_parser_backslash.jst.parsed @@ -103,6 +103,18 @@ function _jst_expire_session_cookie() $cookie += "; secure"; return $cookie; } +function _jst_session_is_current($session_id) +{ + if(!ccsp_session.getStatus() || ccsp_session.getId() !== $session_id) + return false; + + if(ccsp_session.start() && ccsp_session.getId() === $session_id) + return true; + + $_jst_session = null; + $_SESSION = {}; + return false; +} function _jst_session_proxy($session) { var $session_id = ccsp_session.getId(); @@ -112,7 +124,7 @@ function _jst_session_proxy($session) }, set: function(obj, prop, val){ obj[prop] = val; - if(ccsp_session.getStatus() && ccsp_session.getId() === $session_id) + if(_jst_session_is_current($session_id)) ccsp_session.setData(obj); return true; }, @@ -120,7 +132,7 @@ function _jst_session_proxy($session) if(prop in obj) { delete obj[prop]; - if(ccsp_session.getStatus() && ccsp_session.getId() === $session_id) + if(_jst_session_is_current($session_id)) ccsp_session.setData(obj); } return true; @@ -193,7 +205,8 @@ function session_destroy() } function session_unset() { - if(!$_jst_session || !ccsp_session.getStatus()) + var $session_id = ccsp_session.getId(); + if(!$_jst_session || !_jst_session_is_current($session_id)) return false; for(var $key in $_jst_session) @@ -290,6 +303,10 @@ function include($filepath) + + + + echo('a \\ b\n\ '); /* end application code */ diff --git a/tests/parser/jst_parser_comment_tag.jst.parsed b/tests/parser/jst_parser_comment_tag.jst.parsed index 12f619b..e0557e3 100644 --- a/tests/parser/jst_parser_comment_tag.jst.parsed +++ b/tests/parser/jst_parser_comment_tag.jst.parsed @@ -103,6 +103,18 @@ function _jst_expire_session_cookie() $cookie += "; secure"; return $cookie; } +function _jst_session_is_current($session_id) +{ + if(!ccsp_session.getStatus() || ccsp_session.getId() !== $session_id) + return false; + + if(ccsp_session.start() && ccsp_session.getId() === $session_id) + return true; + + $_jst_session = null; + $_SESSION = {}; + return false; +} function _jst_session_proxy($session) { var $session_id = ccsp_session.getId(); @@ -112,7 +124,7 @@ function _jst_session_proxy($session) }, set: function(obj, prop, val){ obj[prop] = val; - if(ccsp_session.getStatus() && ccsp_session.getId() === $session_id) + if(_jst_session_is_current($session_id)) ccsp_session.setData(obj); return true; }, @@ -120,7 +132,7 @@ function _jst_session_proxy($session) if(prop in obj) { delete obj[prop]; - if(ccsp_session.getStatus() && ccsp_session.getId() === $session_id) + if(_jst_session_is_current($session_id)) ccsp_session.setData(obj); } return true; @@ -193,7 +205,8 @@ function session_destroy() } function session_unset() { - if(!$_jst_session || !ccsp_session.getStatus()) + var $session_id = ccsp_session.getId(); + if(!$_jst_session || !_jst_session_is_current($session_id)) return false; for(var $key in $_jst_session) @@ -290,6 +303,10 @@ function include($filepath) + + + + echo('/*\n\ '); echo("This cannot be commented out"); diff --git a/tests/parser/jst_parser_include_code_after.jst.parsed b/tests/parser/jst_parser_include_code_after.jst.parsed index fbd36da..27f8148 100644 --- a/tests/parser/jst_parser_include_code_after.jst.parsed +++ b/tests/parser/jst_parser_include_code_after.jst.parsed @@ -103,6 +103,18 @@ function _jst_expire_session_cookie() $cookie += "; secure"; return $cookie; } +function _jst_session_is_current($session_id) +{ + if(!ccsp_session.getStatus() || ccsp_session.getId() !== $session_id) + return false; + + if(ccsp_session.start() && ccsp_session.getId() === $session_id) + return true; + + $_jst_session = null; + $_SESSION = {}; + return false; +} function _jst_session_proxy($session) { var $session_id = ccsp_session.getId(); @@ -112,7 +124,7 @@ function _jst_session_proxy($session) }, set: function(obj, prop, val){ obj[prop] = val; - if(ccsp_session.getStatus() && ccsp_session.getId() === $session_id) + if(_jst_session_is_current($session_id)) ccsp_session.setData(obj); return true; }, @@ -120,7 +132,7 @@ function _jst_session_proxy($session) if(prop in obj) { delete obj[prop]; - if(ccsp_session.getStatus() && ccsp_session.getId() === $session_id) + if(_jst_session_is_current($session_id)) ccsp_session.setData(obj); } return true; @@ -193,7 +205,8 @@ function session_destroy() } function session_unset() { - if(!$_jst_session || !ccsp_session.getStatus()) + var $session_id = ccsp_session.getId(); + if(!$_jst_session || !_jst_session_is_current($session_id)) return false; for(var $key in $_jst_session) @@ -292,6 +305,10 @@ function include($filepath) + + + + echo("should appear only once"); THIS SHOULD APPEAR AFTER THE INCLUDE diff --git a/tests/parser/jst_parser_include_code_before.jst.parsed b/tests/parser/jst_parser_include_code_before.jst.parsed index 9d4ace1..da99d95 100644 --- a/tests/parser/jst_parser_include_code_before.jst.parsed +++ b/tests/parser/jst_parser_include_code_before.jst.parsed @@ -103,6 +103,18 @@ function _jst_expire_session_cookie() $cookie += "; secure"; return $cookie; } +function _jst_session_is_current($session_id) +{ + if(!ccsp_session.getStatus() || ccsp_session.getId() !== $session_id) + return false; + + if(ccsp_session.start() && ccsp_session.getId() === $session_id) + return true; + + $_jst_session = null; + $_SESSION = {}; + return false; +} function _jst_session_proxy($session) { var $session_id = ccsp_session.getId(); @@ -112,7 +124,7 @@ function _jst_session_proxy($session) }, set: function(obj, prop, val){ obj[prop] = val; - if(ccsp_session.getStatus() && ccsp_session.getId() === $session_id) + if(_jst_session_is_current($session_id)) ccsp_session.setData(obj); return true; }, @@ -120,7 +132,7 @@ function _jst_session_proxy($session) if(prop in obj) { delete obj[prop]; - if(ccsp_session.getStatus() && ccsp_session.getId() === $session_id) + if(_jst_session_is_current($session_id)) ccsp_session.setData(obj); } return true; @@ -193,7 +205,8 @@ function session_destroy() } function session_unset() { - if(!$_jst_session || !ccsp_session.getStatus()) + var $session_id = ccsp_session.getId(); + if(!$_jst_session || !_jst_session_is_current($session_id)) return false; for(var $key in $_jst_session) @@ -291,6 +304,10 @@ function include($filepath) + + + + THIS SHOULD APPEAR BEFORE THE INCLUDE echo("should appear only once"); diff --git a/tests/parser/jst_parser_include_malformed_1.jst.parsed b/tests/parser/jst_parser_include_malformed_1.jst.parsed index 594bd41..3f4083b 100644 --- a/tests/parser/jst_parser_include_malformed_1.jst.parsed +++ b/tests/parser/jst_parser_include_malformed_1.jst.parsed @@ -103,6 +103,18 @@ function _jst_expire_session_cookie() $cookie += "; secure"; return $cookie; } +function _jst_session_is_current($session_id) +{ + if(!ccsp_session.getStatus() || ccsp_session.getId() !== $session_id) + return false; + + if(ccsp_session.start() && ccsp_session.getId() === $session_id) + return true; + + $_jst_session = null; + $_SESSION = {}; + return false; +} function _jst_session_proxy($session) { var $session_id = ccsp_session.getId(); @@ -112,7 +124,7 @@ function _jst_session_proxy($session) }, set: function(obj, prop, val){ obj[prop] = val; - if(ccsp_session.getStatus() && ccsp_session.getId() === $session_id) + if(_jst_session_is_current($session_id)) ccsp_session.setData(obj); return true; }, @@ -120,7 +132,7 @@ function _jst_session_proxy($session) if(prop in obj) { delete obj[prop]; - if(ccsp_session.getStatus() && ccsp_session.getId() === $session_id) + if(_jst_session_is_current($session_id)) ccsp_session.setData(obj); } return true; @@ -193,7 +205,8 @@ function session_destroy() } function session_unset() { - if(!$_jst_session || !ccsp_session.getStatus()) + var $session_id = ccsp_session.getId(); + if(!$_jst_session || !_jst_session_is_current($session_id)) return false; for(var $key in $_jst_session) @@ -291,6 +304,10 @@ function include($filepath) + + + + include( x "include/once.jst"); /* end application code */ diff --git a/tests/parser/jst_parser_include_nested.jst.parsed b/tests/parser/jst_parser_include_nested.jst.parsed index 8aad386..31cb1b9 100644 --- a/tests/parser/jst_parser_include_nested.jst.parsed +++ b/tests/parser/jst_parser_include_nested.jst.parsed @@ -103,6 +103,18 @@ function _jst_expire_session_cookie() $cookie += "; secure"; return $cookie; } +function _jst_session_is_current($session_id) +{ + if(!ccsp_session.getStatus() || ccsp_session.getId() !== $session_id) + return false; + + if(ccsp_session.start() && ccsp_session.getId() === $session_id) + return true; + + $_jst_session = null; + $_SESSION = {}; + return false; +} function _jst_session_proxy($session) { var $session_id = ccsp_session.getId(); @@ -112,7 +124,7 @@ function _jst_session_proxy($session) }, set: function(obj, prop, val){ obj[prop] = val; - if(ccsp_session.getStatus() && ccsp_session.getId() === $session_id) + if(_jst_session_is_current($session_id)) ccsp_session.setData(obj); return true; }, @@ -120,7 +132,7 @@ function _jst_session_proxy($session) if(prop in obj) { delete obj[prop]; - if(ccsp_session.getStatus() && ccsp_session.getId() === $session_id) + if(_jst_session_is_current($session_id)) ccsp_session.setData(obj); } return true; @@ -193,7 +205,8 @@ function session_destroy() } function session_unset() { - if(!$_jst_session || !ccsp_session.getStatus()) + var $session_id = ccsp_session.getId(); + if(!$_jst_session || !_jst_session_is_current($session_id)) return false; for(var $key in $_jst_session) @@ -291,6 +304,10 @@ function include($filepath) + + + + diff --git a/tests/parser/jst_parser_include_not_if_in_block_comment.jst.parsed b/tests/parser/jst_parser_include_not_if_in_block_comment.jst.parsed index 75b0a2c..47e7068 100644 --- a/tests/parser/jst_parser_include_not_if_in_block_comment.jst.parsed +++ b/tests/parser/jst_parser_include_not_if_in_block_comment.jst.parsed @@ -103,6 +103,18 @@ function _jst_expire_session_cookie() $cookie += "; secure"; return $cookie; } +function _jst_session_is_current($session_id) +{ + if(!ccsp_session.getStatus() || ccsp_session.getId() !== $session_id) + return false; + + if(ccsp_session.start() && ccsp_session.getId() === $session_id) + return true; + + $_jst_session = null; + $_SESSION = {}; + return false; +} function _jst_session_proxy($session) { var $session_id = ccsp_session.getId(); @@ -112,7 +124,7 @@ function _jst_session_proxy($session) }, set: function(obj, prop, val){ obj[prop] = val; - if(ccsp_session.getStatus() && ccsp_session.getId() === $session_id) + if(_jst_session_is_current($session_id)) ccsp_session.setData(obj); return true; }, @@ -120,7 +132,7 @@ function _jst_session_proxy($session) if(prop in obj) { delete obj[prop]; - if(ccsp_session.getStatus() && ccsp_session.getId() === $session_id) + if(_jst_session_is_current($session_id)) ccsp_session.setData(obj); } return true; @@ -193,7 +205,8 @@ function session_destroy() } function session_unset() { - if(!$_jst_session || !ccsp_session.getStatus()) + var $session_id = ccsp_session.getId(); + if(!$_jst_session || !_jst_session_is_current($session_id)) return false; for(var $key in $_jst_session) @@ -291,6 +304,10 @@ function include($filepath) + + + + /*include("include/once.jst");*/ /* include("include/once.jst"); diff --git a/tests/parser/jst_parser_include_not_if_in_content.jst.parsed b/tests/parser/jst_parser_include_not_if_in_content.jst.parsed index ab52650..30e9a4a 100644 --- a/tests/parser/jst_parser_include_not_if_in_content.jst.parsed +++ b/tests/parser/jst_parser_include_not_if_in_content.jst.parsed @@ -103,6 +103,18 @@ function _jst_expire_session_cookie() $cookie += "; secure"; return $cookie; } +function _jst_session_is_current($session_id) +{ + if(!ccsp_session.getStatus() || ccsp_session.getId() !== $session_id) + return false; + + if(ccsp_session.start() && ccsp_session.getId() === $session_id) + return true; + + $_jst_session = null; + $_SESSION = {}; + return false; +} function _jst_session_proxy($session) { var $session_id = ccsp_session.getId(); @@ -112,7 +124,7 @@ function _jst_session_proxy($session) }, set: function(obj, prop, val){ obj[prop] = val; - if(ccsp_session.getStatus() && ccsp_session.getId() === $session_id) + if(_jst_session_is_current($session_id)) ccsp_session.setData(obj); return true; }, @@ -120,7 +132,7 @@ function _jst_session_proxy($session) if(prop in obj) { delete obj[prop]; - if(ccsp_session.getStatus() && ccsp_session.getId() === $session_id) + if(_jst_session_is_current($session_id)) ccsp_session.setData(obj); } return true; @@ -193,7 +205,8 @@ function session_destroy() } function session_unset() { - if(!$_jst_session || !ccsp_session.getStatus()) + var $session_id = ccsp_session.getId(); + if(!$_jst_session || !_jst_session_is_current($session_id)) return false; for(var $key in $_jst_session) @@ -290,6 +303,10 @@ function include($filepath) + + + + echo('//FIXME: if i remove this comment, then the following line doesn\'t output\n\ include("include/once.jst");\n\ \n\ diff --git a/tests/parser/jst_parser_include_not_if_in_line_comment.jst.parsed b/tests/parser/jst_parser_include_not_if_in_line_comment.jst.parsed index 25975ef..e67c6a9 100644 --- a/tests/parser/jst_parser_include_not_if_in_line_comment.jst.parsed +++ b/tests/parser/jst_parser_include_not_if_in_line_comment.jst.parsed @@ -103,6 +103,18 @@ function _jst_expire_session_cookie() $cookie += "; secure"; return $cookie; } +function _jst_session_is_current($session_id) +{ + if(!ccsp_session.getStatus() || ccsp_session.getId() !== $session_id) + return false; + + if(ccsp_session.start() && ccsp_session.getId() === $session_id) + return true; + + $_jst_session = null; + $_SESSION = {}; + return false; +} function _jst_session_proxy($session) { var $session_id = ccsp_session.getId(); @@ -112,7 +124,7 @@ function _jst_session_proxy($session) }, set: function(obj, prop, val){ obj[prop] = val; - if(ccsp_session.getStatus() && ccsp_session.getId() === $session_id) + if(_jst_session_is_current($session_id)) ccsp_session.setData(obj); return true; }, @@ -120,7 +132,7 @@ function _jst_session_proxy($session) if(prop in obj) { delete obj[prop]; - if(ccsp_session.getStatus() && ccsp_session.getId() === $session_id) + if(_jst_session_is_current($session_id)) ccsp_session.setData(obj); } return true; @@ -193,7 +205,8 @@ function session_destroy() } function session_unset() { - if(!$_jst_session || !ccsp_session.getStatus()) + var $session_id = ccsp_session.getId(); + if(!$_jst_session || !_jst_session_is_current($session_id)) return false; for(var $key in $_jst_session) @@ -291,6 +304,10 @@ function include($filepath) + + + + //include("includes/once.jst"); //blah include("include/once.jst"); //include("include/once.jst"); blah diff --git a/tests/parser/jst_parser_include_once.jst.parsed b/tests/parser/jst_parser_include_once.jst.parsed index 231f274..1fc095a 100644 --- a/tests/parser/jst_parser_include_once.jst.parsed +++ b/tests/parser/jst_parser_include_once.jst.parsed @@ -103,6 +103,18 @@ function _jst_expire_session_cookie() $cookie += "; secure"; return $cookie; } +function _jst_session_is_current($session_id) +{ + if(!ccsp_session.getStatus() || ccsp_session.getId() !== $session_id) + return false; + + if(ccsp_session.start() && ccsp_session.getId() === $session_id) + return true; + + $_jst_session = null; + $_SESSION = {}; + return false; +} function _jst_session_proxy($session) { var $session_id = ccsp_session.getId(); @@ -112,7 +124,7 @@ function _jst_session_proxy($session) }, set: function(obj, prop, val){ obj[prop] = val; - if(ccsp_session.getStatus() && ccsp_session.getId() === $session_id) + if(_jst_session_is_current($session_id)) ccsp_session.setData(obj); return true; }, @@ -120,7 +132,7 @@ function _jst_session_proxy($session) if(prop in obj) { delete obj[prop]; - if(ccsp_session.getStatus() && ccsp_session.getId() === $session_id) + if(_jst_session_is_current($session_id)) ccsp_session.setData(obj); } return true; @@ -193,7 +205,8 @@ function session_destroy() } function session_unset() { - if(!$_jst_session || !ccsp_session.getStatus()) + var $session_id = ccsp_session.getId(); + if(!$_jst_session || !_jst_session_is_current($session_id)) return false; for(var $key in $_jst_session) @@ -292,6 +305,10 @@ function include($filepath) + + + + echo("should appear only once"); diff --git a/tests/parser/jst_parser_include_runtime.jst.parsed b/tests/parser/jst_parser_include_runtime.jst.parsed index d85d3f6..8fe54b7 100644 --- a/tests/parser/jst_parser_include_runtime.jst.parsed +++ b/tests/parser/jst_parser_include_runtime.jst.parsed @@ -103,6 +103,18 @@ function _jst_expire_session_cookie() $cookie += "; secure"; return $cookie; } +function _jst_session_is_current($session_id) +{ + if(!ccsp_session.getStatus() || ccsp_session.getId() !== $session_id) + return false; + + if(ccsp_session.start() && ccsp_session.getId() === $session_id) + return true; + + $_jst_session = null; + $_SESSION = {}; + return false; +} function _jst_session_proxy($session) { var $session_id = ccsp_session.getId(); @@ -112,7 +124,7 @@ function _jst_session_proxy($session) }, set: function(obj, prop, val){ obj[prop] = val; - if(ccsp_session.getStatus() && ccsp_session.getId() === $session_id) + if(_jst_session_is_current($session_id)) ccsp_session.setData(obj); return true; }, @@ -120,7 +132,7 @@ function _jst_session_proxy($session) if(prop in obj) { delete obj[prop]; - if(ccsp_session.getStatus() && ccsp_session.getId() === $session_id) + if(_jst_session_is_current($session_id)) ccsp_session.setData(obj); } return true; @@ -193,7 +205,8 @@ function session_destroy() } function session_unset() { - if(!$_jst_session || !ccsp_session.getStatus()) + var $session_id = ccsp_session.getId(); + if(!$_jst_session || !_jst_session_is_current($session_id)) return false; for(var $key in $_jst_session) @@ -292,6 +305,10 @@ function include($filepath) + + + + //jst parser should skip all these and copy into output verbatim var path1 = "include/once.jst" diff --git a/tests/parser/jst_parser_include_unknown.jst.parsed b/tests/parser/jst_parser_include_unknown.jst.parsed index 04163f5..bbef194 100644 --- a/tests/parser/jst_parser_include_unknown.jst.parsed +++ b/tests/parser/jst_parser_include_unknown.jst.parsed @@ -103,6 +103,18 @@ function _jst_expire_session_cookie() $cookie += "; secure"; return $cookie; } +function _jst_session_is_current($session_id) +{ + if(!ccsp_session.getStatus() || ccsp_session.getId() !== $session_id) + return false; + + if(ccsp_session.start() && ccsp_session.getId() === $session_id) + return true; + + $_jst_session = null; + $_SESSION = {}; + return false; +} function _jst_session_proxy($session) { var $session_id = ccsp_session.getId(); @@ -112,7 +124,7 @@ function _jst_session_proxy($session) }, set: function(obj, prop, val){ obj[prop] = val; - if(ccsp_session.getStatus() && ccsp_session.getId() === $session_id) + if(_jst_session_is_current($session_id)) ccsp_session.setData(obj); return true; }, @@ -120,7 +132,7 @@ function _jst_session_proxy($session) if(prop in obj) { delete obj[prop]; - if(ccsp_session.getStatus() && ccsp_session.getId() === $session_id) + if(_jst_session_is_current($session_id)) ccsp_session.setData(obj); } return true; @@ -193,7 +205,8 @@ function session_destroy() } function session_unset() { - if(!$_jst_session || !ccsp_session.getStatus()) + var $session_id = ccsp_session.getId(); + if(!$_jst_session || !_jst_session_is_current($session_id)) return false; for(var $key in $_jst_session) @@ -292,6 +305,10 @@ function include($filepath) + + + + //jst parser should skip all these and copy into output verbatim include("path1'); diff --git a/tests/parser/jst_parser_line_feeds.jst.parsed b/tests/parser/jst_parser_line_feeds.jst.parsed index 7b5de0d..5e47d3b 100644 --- a/tests/parser/jst_parser_line_feeds.jst.parsed +++ b/tests/parser/jst_parser_line_feeds.jst.parsed @@ -103,6 +103,18 @@ function _jst_expire_session_cookie() $cookie += "; secure"; return $cookie; } +function _jst_session_is_current($session_id) +{ + if(!ccsp_session.getStatus() || ccsp_session.getId() !== $session_id) + return false; + + if(ccsp_session.start() && ccsp_session.getId() === $session_id) + return true; + + $_jst_session = null; + $_SESSION = {}; + return false; +} function _jst_session_proxy($session) { var $session_id = ccsp_session.getId(); @@ -112,7 +124,7 @@ function _jst_session_proxy($session) }, set: function(obj, prop, val){ obj[prop] = val; - if(ccsp_session.getStatus() && ccsp_session.getId() === $session_id) + if(_jst_session_is_current($session_id)) ccsp_session.setData(obj); return true; }, @@ -120,7 +132,7 @@ function _jst_session_proxy($session) if(prop in obj) { delete obj[prop]; - if(ccsp_session.getStatus() && ccsp_session.getId() === $session_id) + if(_jst_session_is_current($session_id)) ccsp_session.setData(obj); } return true; @@ -193,7 +205,8 @@ function session_destroy() } function session_unset() { - if(!$_jst_session || !ccsp_session.getStatus()) + var $session_id = ccsp_session.getId(); + if(!$_jst_session || !_jst_session_is_current($session_id)) return false; for(var $key in $_jst_session) @@ -290,6 +303,10 @@ function include($filepath) + + + + echo('a\n\ \n\ b\n\ diff --git a/tests/parser/jst_parser_single_quotes.jst.parsed b/tests/parser/jst_parser_single_quotes.jst.parsed index 45d56c0..dbe0f15 100644 --- a/tests/parser/jst_parser_single_quotes.jst.parsed +++ b/tests/parser/jst_parser_single_quotes.jst.parsed @@ -103,6 +103,18 @@ function _jst_expire_session_cookie() $cookie += "; secure"; return $cookie; } +function _jst_session_is_current($session_id) +{ + if(!ccsp_session.getStatus() || ccsp_session.getId() !== $session_id) + return false; + + if(ccsp_session.start() && ccsp_session.getId() === $session_id) + return true; + + $_jst_session = null; + $_SESSION = {}; + return false; +} function _jst_session_proxy($session) { var $session_id = ccsp_session.getId(); @@ -112,7 +124,7 @@ function _jst_session_proxy($session) }, set: function(obj, prop, val){ obj[prop] = val; - if(ccsp_session.getStatus() && ccsp_session.getId() === $session_id) + if(_jst_session_is_current($session_id)) ccsp_session.setData(obj); return true; }, @@ -120,7 +132,7 @@ function _jst_session_proxy($session) if(prop in obj) { delete obj[prop]; - if(ccsp_session.getStatus() && ccsp_session.getId() === $session_id) + if(_jst_session_is_current($session_id)) ccsp_session.setData(obj); } return true; @@ -193,7 +205,8 @@ function session_destroy() } function session_unset() { - if(!$_jst_session || !ccsp_session.getStatus()) + var $session_id = ccsp_session.getId(); + if(!$_jst_session || !_jst_session_is_current($session_id)) return false; for(var $key in $_jst_session) @@ -290,6 +303,10 @@ function include($filepath) + + + + echo('\'a b c\'\n\ '); /* end application code */ diff --git a/tests/parser/jst_parser_skip_whitespace.jst.parsed b/tests/parser/jst_parser_skip_whitespace.jst.parsed index 4d38071..b9cb53a 100644 --- a/tests/parser/jst_parser_skip_whitespace.jst.parsed +++ b/tests/parser/jst_parser_skip_whitespace.jst.parsed @@ -103,6 +103,18 @@ function _jst_expire_session_cookie() $cookie += "; secure"; return $cookie; } +function _jst_session_is_current($session_id) +{ + if(!ccsp_session.getStatus() || ccsp_session.getId() !== $session_id) + return false; + + if(ccsp_session.start() && ccsp_session.getId() === $session_id) + return true; + + $_jst_session = null; + $_SESSION = {}; + return false; +} function _jst_session_proxy($session) { var $session_id = ccsp_session.getId(); @@ -112,7 +124,7 @@ function _jst_session_proxy($session) }, set: function(obj, prop, val){ obj[prop] = val; - if(ccsp_session.getStatus() && ccsp_session.getId() === $session_id) + if(_jst_session_is_current($session_id)) ccsp_session.setData(obj); return true; }, @@ -120,7 +132,7 @@ function _jst_session_proxy($session) if(prop in obj) { delete obj[prop]; - if(ccsp_session.getStatus() && ccsp_session.getId() === $session_id) + if(_jst_session_is_current($session_id)) ccsp_session.setData(obj); } return true; @@ -193,7 +205,8 @@ function session_destroy() } function session_unset() { - if(!$_jst_session || !ccsp_session.getStatus()) + var $session_id = ccsp_session.getId(); + if(!$_jst_session || !_jst_session_is_current($session_id)) return false; for(var $key in $_jst_session) @@ -290,6 +303,10 @@ function include($filepath) + + + + echo('begin content\n\ ');echo('\n\ end content\n\ diff --git a/tests/parser/jst_parser_template_block_content.jst.parsed b/tests/parser/jst_parser_template_block_content.jst.parsed index 0d58a29..38b006e 100644 --- a/tests/parser/jst_parser_template_block_content.jst.parsed +++ b/tests/parser/jst_parser_template_block_content.jst.parsed @@ -103,6 +103,18 @@ function _jst_expire_session_cookie() $cookie += "; secure"; return $cookie; } +function _jst_session_is_current($session_id) +{ + if(!ccsp_session.getStatus() || ccsp_session.getId() !== $session_id) + return false; + + if(ccsp_session.start() && ccsp_session.getId() === $session_id) + return true; + + $_jst_session = null; + $_SESSION = {}; + return false; +} function _jst_session_proxy($session) { var $session_id = ccsp_session.getId(); @@ -112,7 +124,7 @@ function _jst_session_proxy($session) }, set: function(obj, prop, val){ obj[prop] = val; - if(ccsp_session.getStatus() && ccsp_session.getId() === $session_id) + if(_jst_session_is_current($session_id)) ccsp_session.setData(obj); return true; }, @@ -120,7 +132,7 @@ function _jst_session_proxy($session) if(prop in obj) { delete obj[prop]; - if(ccsp_session.getStatus() && ccsp_session.getId() === $session_id) + if(_jst_session_is_current($session_id)) ccsp_session.setData(obj); } return true; @@ -193,7 +205,8 @@ function session_destroy() } function session_unset() { - if(!$_jst_session || !ccsp_session.getStatus()) + var $session_id = ccsp_session.getId(); + if(!$_jst_session || !_jst_session_is_current($session_id)) return false; for(var $key in $_jst_session) @@ -290,6 +303,10 @@ function include($filepath) + + + + echo("Hello World"); /* end application code */ exit(0); diff --git a/tests/parser/jst_parser_template_block_string.jst.parsed b/tests/parser/jst_parser_template_block_string.jst.parsed index 429a92a..56b1f2a 100644 --- a/tests/parser/jst_parser_template_block_string.jst.parsed +++ b/tests/parser/jst_parser_template_block_string.jst.parsed @@ -103,6 +103,18 @@ function _jst_expire_session_cookie() $cookie += "; secure"; return $cookie; } +function _jst_session_is_current($session_id) +{ + if(!ccsp_session.getStatus() || ccsp_session.getId() !== $session_id) + return false; + + if(ccsp_session.start() && ccsp_session.getId() === $session_id) + return true; + + $_jst_session = null; + $_SESSION = {}; + return false; +} function _jst_session_proxy($session) { var $session_id = ccsp_session.getId(); @@ -112,7 +124,7 @@ function _jst_session_proxy($session) }, set: function(obj, prop, val){ obj[prop] = val; - if(ccsp_session.getStatus() && ccsp_session.getId() === $session_id) + if(_jst_session_is_current($session_id)) ccsp_session.setData(obj); return true; }, @@ -120,7 +132,7 @@ function _jst_session_proxy($session) if(prop in obj) { delete obj[prop]; - if(ccsp_session.getStatus() && ccsp_session.getId() === $session_id) + if(_jst_session_is_current($session_id)) ccsp_session.setData(obj); } return true; @@ -193,7 +205,8 @@ function session_destroy() } function session_unset() { - if(!$_jst_session || !ccsp_session.getStatus()) + var $session_id = ccsp_session.getId(); + if(!$_jst_session || !_jst_session_is_current($session_id)) return false; for(var $key in $_jst_session) @@ -291,6 +304,10 @@ function include($filepath) + + + + var world="World"; echo('\n\ Hello ');echo(world);echo('!\n\ diff --git a/tests/parser/jst_prefix.js b/tests/parser/jst_prefix.js index c8c9942..7934fe6 100644 --- a/tests/parser/jst_prefix.js +++ b/tests/parser/jst_prefix.js @@ -103,6 +103,18 @@ function _jst_expire_session_cookie() $cookie += "; secure"; return $cookie; } +function _jst_session_is_current($session_id) +{ + if(!ccsp_session.getStatus() || ccsp_session.getId() !== $session_id) + return false; + + if(ccsp_session.start() && ccsp_session.getId() === $session_id) + return true; + + $_jst_session = null; + $_SESSION = {}; + return false; +} function _jst_session_proxy($session) { var $session_id = ccsp_session.getId(); @@ -112,7 +124,7 @@ function _jst_session_proxy($session) }, set: function(obj, prop, val){ obj[prop] = val; - if(ccsp_session.getStatus() && ccsp_session.getId() === $session_id) + if(_jst_session_is_current($session_id)) ccsp_session.setData(obj); return true; }, @@ -120,7 +132,7 @@ function _jst_session_proxy($session) if(prop in obj) { delete obj[prop]; - if(ccsp_session.getStatus() && ccsp_session.getId() === $session_id) + if(_jst_session_is_current($session_id)) ccsp_session.setData(obj); } return true; @@ -193,7 +205,8 @@ function session_destroy() } function session_unset() { - if(!$_jst_session || !ccsp_session.getStatus()) + var $session_id = ccsp_session.getId(); + if(!$_jst_session || !_jst_session_is_current($session_id)) return false; for(var $key in $_jst_session) diff --git a/tests/parser_test.cpp b/tests/parser_test.cpp index 17f83e6..92b21ce 100644 --- a/tests/parser_test.cpp +++ b/tests/parser_test.cpp @@ -421,6 +421,16 @@ TEST(general, session_create_destroy_cycle_and_id_format) EXPECT_NE(access(first_session_file, F_OK), 0); + duk_get_global_string(ctx, "ccsp_session"); + duk_get_prop_string(ctx, -1, "getId"); + ASSERT_EQ(duk_pcall(ctx, 0), DUK_EXEC_SUCCESS); + const char* second_id = duk_get_string(ctx, -1); + ASSERT_NE(second_id, nullptr); + char second_session_file[128] = {0}; + snprintf(second_session_file, sizeof(second_session_file), "/tmp/%s", second_id); + duk_pop_2(ctx); + EXPECT_EQ(access(second_session_file, F_OK), 0); + duk_get_global_string(ctx, "ccsp_session"); duk_get_prop_string(ctx, -1, "destroy"); ASSERT_EQ(duk_pcall(ctx, 0), DUK_EXEC_SUCCESS); @@ -845,6 +855,30 @@ TEST(general, session_prefix_old_proxy_does_not_write_replacement_session) duk_destroy_heap(ctx); } +TEST(general, session_prefix_unset_does_not_recreate_deleted_session_file) +{ + EnvVarGuard cookie_guard("HTTP_COOKIE"); + cookie_guard.set(nullptr); + + duk_context* ctx = duk_create_heap_default(); + ASSERT_NE(ctx, nullptr); + installSessionPrefixDependencies(ctx); + evaluateSessionPrefix(ctx); + + ASSERT_TRUE(evaluateJavaScriptBoolean(ctx, + "session_create(); $_SESSION.persisted = 'value'; true")); + const std::string session_id = evaluateJavaScriptString(ctx, "session_id()"); + ASSERT_FALSE(session_id.empty()); + const std::string session_file = "/tmp/" + session_id; + ASSERT_EQ(unlink(session_file.c_str()), 0); + + EXPECT_FALSE(evaluateJavaScriptBoolean(ctx, "session_unset()")); + EXPECT_FALSE(evaluateJavaScriptBoolean(ctx, "session_status()")); + EXPECT_NE(access(session_file.c_str(), F_OK), 0); + + duk_destroy_heap(ctx); +} + TEST(general, session_prefix_cookie_secure_attribute_follows_request_scheme) { EnvVarGuard https_guard("HTTPS"); @@ -932,7 +966,6 @@ TEST(general, session_prefix_stale_proxy_does_not_recreate_deleted_session_file) 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);