From cb34d7aabeeee7dafbfedd9faf4c31458cadcfad Mon Sep 17 00:00:00 2001 From: Pavan Kumar Reddy B <57708013+pavankumar464@users.noreply.github.com> Date: Thu, 17 Sep 2026 08:54:55 +0530 Subject: [PATCH 1/3] RDKB-66802 : harden session lifecycle and cookie scope (#44) Reason for change: Fix invalid session handling Test Procedure: Test for UI Sessions Risks: Low Priority: P1 --------- Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- jsts/jst_prefix.js | 132 +++-- source/jst_session.c | 186 +++++-- tests/CMakeLists.txt | 2 + tests/parser/jst_parser_backslash.jst.parsed | 184 +++++- .../parser/jst_parser_comment_tag.jst.parsed | 184 +++++- .../jst_parser_include_code_after.jst.parsed | 184 +++++- .../jst_parser_include_code_before.jst.parsed | 184 +++++- .../jst_parser_include_malformed_1.jst.parsed | 184 +++++- .../jst_parser_include_nested.jst.parsed | 184 +++++- ...include_not_if_in_block_comment.jst.parsed | 184 +++++- ...arser_include_not_if_in_content.jst.parsed | 184 +++++- ..._include_not_if_in_line_comment.jst.parsed | 184 +++++- .../parser/jst_parser_include_once.jst.parsed | 184 +++++- .../jst_parser_include_runtime.jst.parsed | 184 +++++- .../jst_parser_include_unknown.jst.parsed | 184 +++++- tests/parser/jst_parser_line_feeds.jst.parsed | 184 +++++- .../jst_parser_single_quotes.jst.parsed | 184 +++++- .../jst_parser_skip_whitespace.jst.parsed | 184 +++++- ...t_parser_template_block_content.jst.parsed | 184 +++++- ...st_parser_template_block_string.jst.parsed | 184 +++++- tests/parser/jst_prefix.js | 159 +++++- tests/parser_test.cpp | 526 +++++++++++++++++- 22 files changed, 3739 insertions(+), 394 deletions(-) diff --git a/jsts/jst_prefix.js b/jsts/jst_prefix.js index 8f3a778..7934fe6 100644 --- a/jsts/jst_prefix.js +++ b/jsts/jst_prefix.js @@ -89,69 +89,102 @@ var $_SERVER = new Proxy({}, { var $_SESSION = {}; var $_jst_session = null; var $_val_input = {}; -function session_start() +function _jst_session_cookie() { - if($_jst_session) - return; - if($_val_input == 1) - { - $_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); - $_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_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(); + return new Proxy($session, { get: function(obj, prop) { return obj[prop]; }, set: function(obj, prop, val){ obj[prop] = val; - ccsp_session.setData(obj); + if(_jst_session_is_current($session_id)) + ccsp_session.setData(obj); return true; }, deleteProperty(obj, prop) { if(prop in obj) { delete obj[prop]; - ccsp_session.setData(obj); + if(_jst_session_is_current($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(){ - ccsp_session.create(); - var host = getenv('HTTPS'); - if (host == false) - var $cookie = "Set-Cookie: DUKSID=" + ccsp_session.getId() + "; httponly"; - else - var $cookie = "Set-Cookie: DUKSID=" + ccsp_session.getId() + "; secure" + "; httponly"; - header($cookie); + if(!ccsp_session.create()) + { + $_jst_session = null; + $_SESSION = {}; + return false; + } + header(_jst_session_cookie()); $_jst_session = ccsp_session.getData(); - $_SESSION = new Proxy($_jst_session, { - get: function(obj, prop) { - return obj[prop]; - }, - set: function(obj, prop, val){ - obj[prop] = val; - ccsp_session.setData(obj); - return true; - }, - deleteProperty(obj, prop) { - if(prop in obj) - { - delete obj[prop]; - ccsp_session.setData(obj); - } - return true; - } - }); + if($_jst_session === null || typeof($_jst_session) !== 'object') + $_jst_session = {}; + $_SESSION = _jst_session_proxy($_jst_session); + return true; } function session_id() { @@ -163,6 +196,7 @@ function session_status() } function session_destroy() { + header(_jst_expire_session_cookie()); delete $_jst_session; $_jst_session = null; delete $_SESSION; @@ -170,7 +204,15 @@ function session_destroy() return ccsp_session.destroy(); } function session_unset() -{//FIXME +{ + var $session_id = ccsp_session.getId(); + if(!$_jst_session || !_jst_session_is_current($session_id)) + 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..ac414ca 100644 --- a/source/jst_session.c +++ b/source/jst_session.c @@ -21,12 +21,15 @@ #include #include #include +#include #include #include #include +#include #include #include #include +#include #include "jst_internal.h" #include #include @@ -46,6 +49,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 +77,35 @@ static char* session_identifier = NULL; +static int request_is_https(void) +{ + const char* val; + + val = getenv("HTTPS"); + 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) + 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; @@ -119,11 +154,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 */ @@ -139,6 +210,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; @@ -158,18 +232,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=")) + if(get_session_id_cookie(cookie, &sesid, &sesid_len)) { - sesid= tmp; - tmp++; - } - CosaPhpExtLog("%s: sesid %s\n", __PRETTY_FUNCTION__, sesid); - if(sesid) - { - 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); @@ -177,19 +243,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); + } } } } @@ -217,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) @@ -240,28 +316,49 @@ static duk_ret_t session_create(duk_context *ctx) session_id[i] = BYTE_TO_PRINTABLE_HEX_CODE(bytes[i]); } - 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_id[0] = request_session_scheme(); - 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))) + { + free(new_session_identifier); + 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; + } + + 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; } @@ -284,7 +381,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 +594,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 +641,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/CMakeLists.txt b/tests/CMakeLists.txt index baa60e7..f982969 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/jst_parser_backslash.jst.parsed b/tests/parser/jst_parser_backslash.jst.parsed index b695eb3..0828fe4 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,104 @@ 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_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(); + return new Proxy($session, { get: function(obj, prop) { return obj[prop]; }, set: function(obj, prop, val){ obj[prop] = val; - ccsp_session.setData(obj); + if(_jst_session_is_current($session_id)) + ccsp_session.setData(obj); return true; }, deleteProperty(obj, prop) { if(prop in obj) { delete obj[prop]; - ccsp_session.setData(obj); + if(_jst_session_is_current($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 +196,7 @@ function session_status() } function session_destroy() { + header(_jst_expire_session_cookie()); delete $_jst_session; $_jst_session = null; delete $_SESSION; @@ -104,7 +204,15 @@ function session_destroy() return ccsp_session.destroy(); } function session_unset() -{//FIXME +{ + var $session_id = ccsp_session.getId(); + if(!$_jst_session || !_jst_session_is_current($session_id)) + return false; + + for(var $key in $_jst_session) + delete $_jst_session[$key]; + + return ccsp_session.setData($_jst_session); } function session_print() { @@ -127,7 +235,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 +299,14 @@ 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..e0557e3 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,104 @@ 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_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(); + return new Proxy($session, { get: function(obj, prop) { return obj[prop]; }, set: function(obj, prop, val){ obj[prop] = val; - ccsp_session.setData(obj); + if(_jst_session_is_current($session_id)) + ccsp_session.setData(obj); return true; }, deleteProperty(obj, prop) { if(prop in obj) { delete obj[prop]; - ccsp_session.setData(obj); + if(_jst_session_is_current($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 +196,7 @@ function session_status() } function session_destroy() { + header(_jst_expire_session_cookie()); delete $_jst_session; $_jst_session = null; delete $_SESSION; @@ -104,7 +204,15 @@ function session_destroy() return ccsp_session.destroy(); } function session_unset() -{//FIXME +{ + var $session_id = ccsp_session.getId(); + if(!$_jst_session || !_jst_session_is_current($session_id)) + return false; + + for(var $key in $_jst_session) + delete $_jst_session[$key]; + + return ccsp_session.setData($_jst_session); } function session_print() { @@ -127,7 +235,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 +299,14 @@ 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..27f8148 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,104 @@ 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_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(); + return new Proxy($session, { get: function(obj, prop) { return obj[prop]; }, set: function(obj, prop, val){ obj[prop] = val; - ccsp_session.setData(obj); + if(_jst_session_is_current($session_id)) + ccsp_session.setData(obj); return true; }, deleteProperty(obj, prop) { if(prop in obj) { delete obj[prop]; - ccsp_session.setData(obj); + if(_jst_session_is_current($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 +196,7 @@ function session_status() } function session_destroy() { + header(_jst_expire_session_cookie()); delete $_jst_session; $_jst_session = null; delete $_SESSION; @@ -104,7 +204,15 @@ function session_destroy() return ccsp_session.destroy(); } function session_unset() -{//FIXME +{ + var $session_id = ccsp_session.getId(); + if(!$_jst_session || !_jst_session_is_current($session_id)) + return false; + + for(var $key in $_jst_session) + delete $_jst_session[$key]; + + return ccsp_session.setData($_jst_session); } function session_print() { @@ -127,7 +235,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 +301,14 @@ 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..da99d95 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,104 @@ 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_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(); + return new Proxy($session, { get: function(obj, prop) { return obj[prop]; }, set: function(obj, prop, val){ obj[prop] = val; - ccsp_session.setData(obj); + if(_jst_session_is_current($session_id)) + ccsp_session.setData(obj); return true; }, deleteProperty(obj, prop) { if(prop in obj) { delete obj[prop]; - ccsp_session.setData(obj); + if(_jst_session_is_current($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 +196,7 @@ function session_status() } function session_destroy() { + header(_jst_expire_session_cookie()); delete $_jst_session; $_jst_session = null; delete $_SESSION; @@ -104,7 +204,15 @@ function session_destroy() return ccsp_session.destroy(); } function session_unset() -{//FIXME +{ + var $session_id = ccsp_session.getId(); + if(!$_jst_session || !_jst_session_is_current($session_id)) + return false; + + for(var $key in $_jst_session) + delete $_jst_session[$key]; + + return ccsp_session.setData($_jst_session); } function session_print() { @@ -127,7 +235,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 +300,14 @@ 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..3f4083b 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,104 @@ 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_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(); + return new Proxy($session, { get: function(obj, prop) { return obj[prop]; }, set: function(obj, prop, val){ obj[prop] = val; - ccsp_session.setData(obj); + if(_jst_session_is_current($session_id)) + ccsp_session.setData(obj); return true; }, deleteProperty(obj, prop) { if(prop in obj) { delete obj[prop]; - ccsp_session.setData(obj); + if(_jst_session_is_current($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 +196,7 @@ function session_status() } function session_destroy() { + header(_jst_expire_session_cookie()); delete $_jst_session; $_jst_session = null; delete $_SESSION; @@ -104,7 +204,15 @@ function session_destroy() return ccsp_session.destroy(); } function session_unset() -{//FIXME +{ + var $session_id = ccsp_session.getId(); + if(!$_jst_session || !_jst_session_is_current($session_id)) + return false; + + for(var $key in $_jst_session) + delete $_jst_session[$key]; + + return ccsp_session.setData($_jst_session); } function session_print() { @@ -127,7 +235,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 +300,14 @@ 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..31cb1b9 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,104 @@ 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_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(); + return new Proxy($session, { get: function(obj, prop) { return obj[prop]; }, set: function(obj, prop, val){ obj[prop] = val; - ccsp_session.setData(obj); + if(_jst_session_is_current($session_id)) + ccsp_session.setData(obj); return true; }, deleteProperty(obj, prop) { if(prop in obj) { delete obj[prop]; - ccsp_session.setData(obj); + if(_jst_session_is_current($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 +196,7 @@ function session_status() } function session_destroy() { + header(_jst_expire_session_cookie()); delete $_jst_session; $_jst_session = null; delete $_SESSION; @@ -104,7 +204,15 @@ function session_destroy() return ccsp_session.destroy(); } function session_unset() -{//FIXME +{ + var $session_id = ccsp_session.getId(); + if(!$_jst_session || !_jst_session_is_current($session_id)) + return false; + + for(var $key in $_jst_session) + delete $_jst_session[$key]; + + return ccsp_session.setData($_jst_session); } function session_print() { @@ -127,7 +235,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 +300,14 @@ 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..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 @@ -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,104 @@ 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_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(); + return new Proxy($session, { get: function(obj, prop) { return obj[prop]; }, set: function(obj, prop, val){ obj[prop] = val; - ccsp_session.setData(obj); + if(_jst_session_is_current($session_id)) + ccsp_session.setData(obj); return true; }, deleteProperty(obj, prop) { if(prop in obj) { delete obj[prop]; - ccsp_session.setData(obj); + if(_jst_session_is_current($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 +196,7 @@ function session_status() } function session_destroy() { + header(_jst_expire_session_cookie()); delete $_jst_session; $_jst_session = null; delete $_SESSION; @@ -104,7 +204,15 @@ function session_destroy() return ccsp_session.destroy(); } function session_unset() -{//FIXME +{ + var $session_id = ccsp_session.getId(); + if(!$_jst_session || !_jst_session_is_current($session_id)) + return false; + + for(var $key in $_jst_session) + delete $_jst_session[$key]; + + return ccsp_session.setData($_jst_session); } function session_print() { @@ -127,7 +235,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 +300,14 @@ 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..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 @@ -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,104 @@ 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_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(); + return new Proxy($session, { get: function(obj, prop) { return obj[prop]; }, set: function(obj, prop, val){ obj[prop] = val; - ccsp_session.setData(obj); + if(_jst_session_is_current($session_id)) + ccsp_session.setData(obj); return true; }, deleteProperty(obj, prop) { if(prop in obj) { delete obj[prop]; - ccsp_session.setData(obj); + if(_jst_session_is_current($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 +196,7 @@ function session_status() } function session_destroy() { + header(_jst_expire_session_cookie()); delete $_jst_session; $_jst_session = null; delete $_SESSION; @@ -104,7 +204,15 @@ function session_destroy() return ccsp_session.destroy(); } function session_unset() -{//FIXME +{ + var $session_id = ccsp_session.getId(); + if(!$_jst_session || !_jst_session_is_current($session_id)) + return false; + + for(var $key in $_jst_session) + delete $_jst_session[$key]; + + return ccsp_session.setData($_jst_session); } function session_print() { @@ -127,7 +235,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 +299,14 @@ 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..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 @@ -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,104 @@ 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_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(); + return new Proxy($session, { get: function(obj, prop) { return obj[prop]; }, set: function(obj, prop, val){ obj[prop] = val; - ccsp_session.setData(obj); + if(_jst_session_is_current($session_id)) + ccsp_session.setData(obj); return true; }, deleteProperty(obj, prop) { if(prop in obj) { delete obj[prop]; - ccsp_session.setData(obj); + if(_jst_session_is_current($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 +196,7 @@ function session_status() } function session_destroy() { + header(_jst_expire_session_cookie()); delete $_jst_session; $_jst_session = null; delete $_SESSION; @@ -104,7 +204,15 @@ function session_destroy() return ccsp_session.destroy(); } function session_unset() -{//FIXME +{ + var $session_id = ccsp_session.getId(); + if(!$_jst_session || !_jst_session_is_current($session_id)) + return false; + + for(var $key in $_jst_session) + delete $_jst_session[$key]; + + return ccsp_session.setData($_jst_session); } function session_print() { @@ -127,7 +235,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 +300,14 @@ 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..1fc095a 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,104 @@ 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_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(); + return new Proxy($session, { get: function(obj, prop) { return obj[prop]; }, set: function(obj, prop, val){ obj[prop] = val; - ccsp_session.setData(obj); + if(_jst_session_is_current($session_id)) + ccsp_session.setData(obj); return true; }, deleteProperty(obj, prop) { if(prop in obj) { delete obj[prop]; - ccsp_session.setData(obj); + if(_jst_session_is_current($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 +196,7 @@ function session_status() } function session_destroy() { + header(_jst_expire_session_cookie()); delete $_jst_session; $_jst_session = null; delete $_SESSION; @@ -104,7 +204,15 @@ function session_destroy() return ccsp_session.destroy(); } function session_unset() -{//FIXME +{ + var $session_id = ccsp_session.getId(); + if(!$_jst_session || !_jst_session_is_current($session_id)) + return false; + + for(var $key in $_jst_session) + delete $_jst_session[$key]; + + return ccsp_session.setData($_jst_session); } function session_print() { @@ -127,7 +235,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 +301,14 @@ 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..8fe54b7 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,104 @@ 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_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(); + return new Proxy($session, { get: function(obj, prop) { return obj[prop]; }, set: function(obj, prop, val){ obj[prop] = val; - ccsp_session.setData(obj); + if(_jst_session_is_current($session_id)) + ccsp_session.setData(obj); return true; }, deleteProperty(obj, prop) { if(prop in obj) { delete obj[prop]; - ccsp_session.setData(obj); + if(_jst_session_is_current($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 +196,7 @@ function session_status() } function session_destroy() { + header(_jst_expire_session_cookie()); delete $_jst_session; $_jst_session = null; delete $_SESSION; @@ -104,7 +204,15 @@ function session_destroy() return ccsp_session.destroy(); } function session_unset() -{//FIXME +{ + var $session_id = ccsp_session.getId(); + if(!$_jst_session || !_jst_session_is_current($session_id)) + return false; + + for(var $key in $_jst_session) + delete $_jst_session[$key]; + + return ccsp_session.setData($_jst_session); } function session_print() { @@ -127,7 +235,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 +301,14 @@ 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..bbef194 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,104 @@ 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_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(); + return new Proxy($session, { get: function(obj, prop) { return obj[prop]; }, set: function(obj, prop, val){ obj[prop] = val; - ccsp_session.setData(obj); + if(_jst_session_is_current($session_id)) + ccsp_session.setData(obj); return true; }, deleteProperty(obj, prop) { if(prop in obj) { delete obj[prop]; - ccsp_session.setData(obj); + if(_jst_session_is_current($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 +196,7 @@ function session_status() } function session_destroy() { + header(_jst_expire_session_cookie()); delete $_jst_session; $_jst_session = null; delete $_SESSION; @@ -104,7 +204,15 @@ function session_destroy() return ccsp_session.destroy(); } function session_unset() -{//FIXME +{ + var $session_id = ccsp_session.getId(); + if(!$_jst_session || !_jst_session_is_current($session_id)) + return false; + + for(var $key in $_jst_session) + delete $_jst_session[$key]; + + return ccsp_session.setData($_jst_session); } function session_print() { @@ -127,7 +235,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 +301,14 @@ 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..5e47d3b 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,104 @@ 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_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(); + return new Proxy($session, { get: function(obj, prop) { return obj[prop]; }, set: function(obj, prop, val){ obj[prop] = val; - ccsp_session.setData(obj); + if(_jst_session_is_current($session_id)) + ccsp_session.setData(obj); return true; }, deleteProperty(obj, prop) { if(prop in obj) { delete obj[prop]; - ccsp_session.setData(obj); + if(_jst_session_is_current($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 +196,7 @@ function session_status() } function session_destroy() { + header(_jst_expire_session_cookie()); delete $_jst_session; $_jst_session = null; delete $_SESSION; @@ -104,7 +204,15 @@ function session_destroy() return ccsp_session.destroy(); } function session_unset() -{//FIXME +{ + var $session_id = ccsp_session.getId(); + if(!$_jst_session || !_jst_session_is_current($session_id)) + return false; + + for(var $key in $_jst_session) + delete $_jst_session[$key]; + + return ccsp_session.setData($_jst_session); } function session_print() { @@ -127,7 +235,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 +299,14 @@ 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..dbe0f15 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,104 @@ 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_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(); + return new Proxy($session, { get: function(obj, prop) { return obj[prop]; }, set: function(obj, prop, val){ obj[prop] = val; - ccsp_session.setData(obj); + if(_jst_session_is_current($session_id)) + ccsp_session.setData(obj); return true; }, deleteProperty(obj, prop) { if(prop in obj) { delete obj[prop]; - ccsp_session.setData(obj); + if(_jst_session_is_current($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 +196,7 @@ function session_status() } function session_destroy() { + header(_jst_expire_session_cookie()); delete $_jst_session; $_jst_session = null; delete $_SESSION; @@ -104,7 +204,15 @@ function session_destroy() return ccsp_session.destroy(); } function session_unset() -{//FIXME +{ + var $session_id = ccsp_session.getId(); + if(!$_jst_session || !_jst_session_is_current($session_id)) + return false; + + for(var $key in $_jst_session) + delete $_jst_session[$key]; + + return ccsp_session.setData($_jst_session); } function session_print() { @@ -127,7 +235,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 +299,14 @@ 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..b9cb53a 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,104 @@ 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_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(); + return new Proxy($session, { get: function(obj, prop) { return obj[prop]; }, set: function(obj, prop, val){ obj[prop] = val; - ccsp_session.setData(obj); + if(_jst_session_is_current($session_id)) + ccsp_session.setData(obj); return true; }, deleteProperty(obj, prop) { if(prop in obj) { delete obj[prop]; - ccsp_session.setData(obj); + if(_jst_session_is_current($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 +196,7 @@ function session_status() } function session_destroy() { + header(_jst_expire_session_cookie()); delete $_jst_session; $_jst_session = null; delete $_SESSION; @@ -104,7 +204,15 @@ function session_destroy() return ccsp_session.destroy(); } function session_unset() -{//FIXME +{ + var $session_id = ccsp_session.getId(); + if(!$_jst_session || !_jst_session_is_current($session_id)) + return false; + + for(var $key in $_jst_session) + delete $_jst_session[$key]; + + return ccsp_session.setData($_jst_session); } function session_print() { @@ -127,7 +235,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 +299,14 @@ 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..38b006e 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,104 @@ 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_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(); + return new Proxy($session, { get: function(obj, prop) { return obj[prop]; }, set: function(obj, prop, val){ obj[prop] = val; - ccsp_session.setData(obj); + if(_jst_session_is_current($session_id)) + ccsp_session.setData(obj); return true; }, deleteProperty(obj, prop) { if(prop in obj) { delete obj[prop]; - ccsp_session.setData(obj); + if(_jst_session_is_current($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 +196,7 @@ function session_status() } function session_destroy() { + header(_jst_expire_session_cookie()); delete $_jst_session; $_jst_session = null; delete $_SESSION; @@ -104,7 +204,15 @@ function session_destroy() return ccsp_session.destroy(); } function session_unset() -{//FIXME +{ + var $session_id = ccsp_session.getId(); + if(!$_jst_session || !_jst_session_is_current($session_id)) + return false; + + for(var $key in $_jst_session) + delete $_jst_session[$key]; + + return ccsp_session.setData($_jst_session); } function session_print() { @@ -127,7 +235,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 +299,14 @@ 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..56b1f2a 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,104 @@ 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_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(); + return new Proxy($session, { get: function(obj, prop) { return obj[prop]; }, set: function(obj, prop, val){ obj[prop] = val; - ccsp_session.setData(obj); + if(_jst_session_is_current($session_id)) + ccsp_session.setData(obj); return true; }, deleteProperty(obj, prop) { if(prop in obj) { delete obj[prop]; - ccsp_session.setData(obj); + if(_jst_session_is_current($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 +196,7 @@ function session_status() } function session_destroy() { + header(_jst_expire_session_cookie()); delete $_jst_session; $_jst_session = null; delete $_SESSION; @@ -104,7 +204,15 @@ function session_destroy() return ccsp_session.destroy(); } function session_unset() -{//FIXME +{ + var $session_id = ccsp_session.getId(); + if(!$_jst_session || !_jst_session_is_current($session_id)) + return false; + + for(var $key in $_jst_session) + delete $_jst_session[$key]; + + return ccsp_session.setData($_jst_session); } function session_print() { @@ -127,7 +235,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 +300,14 @@ 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 41386b4..7934fe6 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,32 +88,104 @@ 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_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(); + return new Proxy($session, { get: function(obj, prop) { return obj[prop]; }, set: function(obj, prop, val){ obj[prop] = val; - ccsp_session.setData(obj); + if(_jst_session_is_current($session_id)) + ccsp_session.setData(obj); return true; }, deleteProperty(obj, prop) { if(prop in obj) { delete obj[prop]; - ccsp_session.setData(obj); + if(_jst_session_is_current($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(); @@ -116,6 +196,7 @@ function session_status() } function session_destroy() { + header(_jst_expire_session_cookie()); delete $_jst_session; $_jst_session = null; delete $_SESSION; @@ -123,7 +204,15 @@ function session_destroy() return ccsp_session.destroy(); } function session_unset() -{//FIXME +{ + var $session_id = ccsp_session.getId(); + if(!$_jst_session || !_jst_session_is_current($session_id)) + return false; + + for(var $key in $_jst_session) + delete $_jst_session[$key]; + + return ccsp_session.setData($_jst_session); } function session_print() { @@ -146,7 +235,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"); + } } } diff --git a/tests/parser_test.cpp b/tests/parser_test.cpp index d7eb028..92b21ce 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) @@ -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; @@ -324,10 +402,16 @@ 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); + 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"); + 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"); @@ -337,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); @@ -346,11 +440,66 @@ TEST(general, session_create_destroy_cycle_and_id_format) duk_destroy_heap(ctx); } +TEST(general, session_is_secure_detects_request_scheme) +{ + EnvVarGuard https_guard("HTTPS"); + EnvVarGuard request_scheme_guard("REQUEST_SCHEME"); + EnvVarGuard ssl_protocol_guard("SSL_PROTOCOL"); + https_guard.set(nullptr); + request_scheme_guard.set(nullptr); + ssl_protocol_guard.set(nullptr); + + duk_context* ctx = duk_create_heap_default(); + ASSERT_NE(ctx, nullptr); + + duk_push_c_function(ctx, ccsp_session_module_open, 0); + ASSERT_EQ(duk_pcall(ctx, 0), DUK_EXEC_SUCCESS); + duk_put_global_string(ctx, "ccsp_session"); + + duk_get_global_string(ctx, "ccsp_session"); + duk_get_prop_string(ctx, -1, "isSecure"); + ASSERT_EQ(duk_pcall(ctx, 0), DUK_EXEC_SUCCESS); + EXPECT_FALSE(duk_get_boolean(ctx, -1)); + duk_pop_2(ctx); + + https_guard.set("on"); + duk_get_global_string(ctx, "ccsp_session"); + duk_get_prop_string(ctx, -1, "isSecure"); + ASSERT_EQ(duk_pcall(ctx, 0), DUK_EXEC_SUCCESS); + EXPECT_TRUE(duk_get_boolean(ctx, -1)); + duk_pop_2(ctx); + + https_guard.set("off"); + request_scheme_guard.set("https"); + duk_get_global_string(ctx, "ccsp_session"); + duk_get_prop_string(ctx, -1, "isSecure"); + ASSERT_EQ(duk_pcall(ctx, 0), DUK_EXEC_SUCCESS); + EXPECT_TRUE(duk_get_boolean(ctx, -1)); + duk_pop_2(ctx); + + request_scheme_guard.set("http"); + ssl_protocol_guard.set("TLSv1.3"); + duk_get_global_string(ctx, "ccsp_session"); + duk_get_prop_string(ctx, -1, "isSecure"); + ASSERT_EQ(duk_pcall(ctx, 0), DUK_EXEC_SUCCESS); + EXPECT_TRUE(duk_get_boolean(ctx, -1)); + duk_pop_2(ctx); + + ssl_protocol_guard.set(nullptr); + duk_get_global_string(ctx, "ccsp_session"); + duk_get_prop_string(ctx, -1, "isSecure"); + ASSERT_EQ(duk_pcall(ctx, 0), DUK_EXEC_SUCCESS); + EXPECT_FALSE(duk_get_boolean(ctx, -1)); + duk_pop_2(ctx); + + duk_destroy_heap(ctx); +} + TEST(general, session_start_accepts_existing_valid_cookie_id) { EnvVarGuard cookie_guard("HTTP_COOKIE"); const std::string session_id = makeValidSessionId('A'); - const std::string cookie = "DUKSID=" + session_id; + const std::string cookie = "theme=dark; DUKSID=" + session_id + "; lang=en"; const std::string session_file = "/tmp/" + session_id; FILE* file = fopen(session_file.c_str(), "w"); @@ -388,6 +537,35 @@ TEST(general, session_start_accepts_existing_valid_cookie_id) duk_destroy_heap(ctx); } +TEST(general, session_start_rejects_embedded_duksid_cookie_name) +{ + EnvVarGuard cookie_guard("HTTP_COOKIE"); + const std::string session_id = makeValidSessionId('G'); + const std::string cookie = "OTHERDUKSID=" + session_id; + const std::string session_file = "/tmp/" + session_id; + + FILE* file = fopen(session_file.c_str(), "w"); + ASSERT_NE(file, nullptr); + fclose(file); + cookie_guard.set(cookie.c_str()); + + duk_context* ctx = duk_create_heap_default(); + ASSERT_NE(ctx, nullptr); + + duk_push_c_function(ctx, ccsp_session_module_open, 0); + ASSERT_EQ(duk_pcall(ctx, 0), DUK_EXEC_SUCCESS); + duk_put_global_string(ctx, "ccsp_session"); + + duk_get_global_string(ctx, "ccsp_session"); + duk_get_prop_string(ctx, -1, "start"); + ASSERT_EQ(duk_pcall(ctx, 0), DUK_EXEC_SUCCESS); + EXPECT_FALSE(duk_get_boolean(ctx, -1)); + duk_pop_2(ctx); + + duk_destroy_heap(ctx); + unlink(session_file.c_str()); +} + TEST(general, session_start_rejects_invalid_cookie_ids) { const std::vector cookies = { @@ -425,6 +603,149 @@ TEST(general, session_start_rejects_invalid_cookie_ids) } } +TEST(general, session_start_rejects_missing_cookie) +{ + EnvVarGuard cookie_guard("HTTP_COOKIE"); + cookie_guard.set(nullptr); + + duk_context* ctx = duk_create_heap_default(); + ASSERT_NE(ctx, nullptr); + + duk_push_c_function(ctx, ccsp_session_module_open, 0); + ASSERT_EQ(duk_pcall(ctx, 0), DUK_EXEC_SUCCESS); + duk_put_global_string(ctx, "ccsp_session"); + + duk_get_global_string(ctx, "ccsp_session"); + duk_get_prop_string(ctx, -1, "start"); + ASSERT_EQ(duk_pcall(ctx, 0), DUK_EXEC_SUCCESS); + EXPECT_FALSE(duk_get_boolean(ctx, -1)); + duk_pop_2(ctx); + + duk_get_global_string(ctx, "ccsp_session"); + duk_get_prop_string(ctx, -1, "getStatus"); + ASSERT_EQ(duk_pcall(ctx, 0), DUK_EXEC_SUCCESS); + EXPECT_FALSE(duk_get_boolean(ctx, -1)); + duk_pop_2(ctx); + + duk_destroy_heap(ctx); +} + +TEST(general, session_start_rejects_cookie_from_other_scheme) +{ + EnvVarGuard cookie_guard("HTTP_COOKIE"); + EnvVarGuard https_guard("HTTPS"); + const std::string session_id = makeValidSessionId('D'); + const std::string cookie = "DUKSID=" + session_id; + const std::string session_file = "/tmp/" + session_id; + + FILE* file = fopen(session_file.c_str(), "w"); + ASSERT_NE(file, nullptr); + fclose(file); + cookie_guard.set(cookie.c_str()); + https_guard.set("on"); + + duk_context* ctx = duk_create_heap_default(); + ASSERT_NE(ctx, nullptr); + + duk_push_c_function(ctx, ccsp_session_module_open, 0); + ASSERT_EQ(duk_pcall(ctx, 0), DUK_EXEC_SUCCESS); + duk_put_global_string(ctx, "ccsp_session"); + + duk_get_global_string(ctx, "ccsp_session"); + duk_get_prop_string(ctx, -1, "start"); + ASSERT_EQ(duk_pcall(ctx, 0), DUK_EXEC_SUCCESS); + EXPECT_FALSE(duk_get_boolean(ctx, -1)); + duk_pop_2(ctx); + + EXPECT_EQ(access(session_file.c_str(), F_OK), 0); + unlink(session_file.c_str()); + duk_destroy_heap(ctx); +} + +TEST(general, session_get_data_without_session_returns_empty_object) +{ + EnvVarGuard cookie_guard("HTTP_COOKIE"); + cookie_guard.set(nullptr); + + duk_context* ctx = duk_create_heap_default(); + ASSERT_NE(ctx, nullptr); + + duk_push_c_function(ctx, ccsp_session_module_open, 0); + ASSERT_EQ(duk_pcall(ctx, 0), DUK_EXEC_SUCCESS); + duk_put_global_string(ctx, "ccsp_session"); + + duk_get_global_string(ctx, "ccsp_session"); + duk_get_prop_string(ctx, -1, "getData"); + ASSERT_EQ(duk_pcall(ctx, 0), DUK_EXEC_SUCCESS); + EXPECT_TRUE(duk_is_object(ctx, -1)); + duk_pop_2(ctx); + + duk_destroy_heap(ctx); +} + +TEST(general, session_destroy_without_session_returns_false) +{ + EnvVarGuard cookie_guard("HTTP_COOKIE"); + cookie_guard.set(nullptr); + + duk_context* ctx = duk_create_heap_default(); + ASSERT_NE(ctx, nullptr); + + duk_push_c_function(ctx, ccsp_session_module_open, 0); + ASSERT_EQ(duk_pcall(ctx, 0), DUK_EXEC_SUCCESS); + duk_put_global_string(ctx, "ccsp_session"); + + duk_get_global_string(ctx, "ccsp_session"); + duk_get_prop_string(ctx, -1, "destroy"); + ASSERT_EQ(duk_pcall(ctx, 0), DUK_EXEC_SUCCESS); + EXPECT_FALSE(duk_get_boolean(ctx, -1)); + duk_pop_2(ctx); + + duk_destroy_heap(ctx); +} + +TEST(general, session_start_rejects_expired_session) +{ + EnvVarGuard cookie_guard("HTTP_COOKIE"); + const std::string session_id = makeValidSessionId('C'); + const std::string cookie = "DUKSID=" + session_id; + const std::string session_file = "/tmp/" + session_id; + + FILE* file = fopen(session_file.c_str(), "w"); + ASSERT_NE(file, nullptr); + fclose(file); + cookie_guard.set(cookie.c_str()); + + duk_context* ctx = duk_create_heap_default(); + ASSERT_NE(ctx, nullptr); + + duk_push_c_function(ctx, ccsp_session_module_open, 0); + ASSERT_EQ(duk_pcall(ctx, 0), DUK_EXEC_SUCCESS); + duk_put_global_string(ctx, "ccsp_session"); + + duk_get_global_string(ctx, "ccsp_session"); + duk_get_prop_string(ctx, -1, "start"); + ASSERT_EQ(duk_pcall(ctx, 0), DUK_EXEC_SUCCESS); + EXPECT_TRUE(duk_get_boolean(ctx, -1)); + duk_pop_2(ctx); + + ASSERT_EQ(unlink(session_file.c_str()), 0); + + duk_get_global_string(ctx, "ccsp_session"); + duk_get_prop_string(ctx, -1, "start"); + ASSERT_EQ(duk_pcall(ctx, 0), DUK_EXEC_SUCCESS); + EXPECT_FALSE(duk_get_boolean(ctx, -1)); + duk_pop_2(ctx); + + duk_get_global_string(ctx, "ccsp_session"); + duk_get_prop_string(ctx, -1, "getStatus"); + ASSERT_EQ(duk_pcall(ctx, 0), DUK_EXEC_SUCCESS); + EXPECT_FALSE(duk_get_boolean(ctx, -1)); + duk_pop_2(ctx); + + duk_destroy_heap(ctx); +} + TEST(general, session_start_rejects_missing_session_file) { EnvVarGuard cookie_guard("HTTP_COOKIE"); @@ -457,6 +778,201 @@ 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.indexOf('Set-Cookie:') === -1")); + 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_TRUE(evaluateJavaScriptBoolean(ctx, + "_jst_header_buffer.indexOf('Set-Cookie: DUKSID=; Max-Age=0; httponly') !== -1 && " + "_jst_header_buffer.indexOf('; secure') === -1")); + EXPECT_FALSE(evaluateJavaScriptBoolean(ctx, "session_unset()")); + duk_destroy_heap(ctx); +} + +TEST(general, session_prefix_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_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"); + EnvVarGuard request_scheme_guard("REQUEST_SCHEME"); + EnvVarGuard ssl_protocol_guard("SSL_PROTOCOL"); + https_guard.set(nullptr); + request_scheme_guard.set(nullptr); + ssl_protocol_guard.set(nullptr); + + duk_context* ctx = duk_create_heap_default(); + ASSERT_NE(ctx, nullptr); + installSessionPrefixDependencies(ctx); + evaluateSessionPrefix(ctx); + + ASSERT_TRUE(evaluateJavaScriptBoolean(ctx, "session_create()")); + EXPECT_TRUE(evaluateJavaScriptBoolean(ctx, + "_jst_header_buffer.indexOf('; httponly') !== -1 && _jst_header_buffer.indexOf('; secure') === -1")); + ASSERT_TRUE(evaluateJavaScriptBoolean(ctx, "session_destroy()")); + duk_destroy_heap(ctx); + + request_scheme_guard.set("https"); + ctx = duk_create_heap_default(); + ASSERT_NE(ctx, nullptr); + installSessionPrefixDependencies(ctx); + evaluateSessionPrefix(ctx); + + ASSERT_TRUE(evaluateJavaScriptBoolean(ctx, "session_create()")); + EXPECT_TRUE(evaluateJavaScriptBoolean(ctx, + "_jst_header_buffer.indexOf('; httponly') !== -1 && _jst_header_buffer.indexOf('; secure') !== -1")); + EXPECT_TRUE(evaluateJavaScriptBoolean(ctx, "session_id().charAt(8) === '1'")); + ASSERT_TRUE(evaluateJavaScriptBoolean(ctx, "session_destroy()")); + EXPECT_TRUE(evaluateJavaScriptBoolean(ctx, + "_jst_header_buffer.indexOf('Set-Cookie: DUKSID=; Max-Age=0; httponly; secure') !== -1")); + duk_destroy_heap(ctx); +} + +TEST(general, session_prefix_rejects_https_cookie_on_http_request) +{ + EnvVarGuard cookie_guard("HTTP_COOKIE"); + EnvVarGuard https_guard("HTTPS"); + 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; + + 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); + 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); + + EXPECT_FALSE(evaluateJavaScriptBoolean(ctx, "session_start()")); + EXPECT_TRUE(evaluateJavaScriptBoolean(ctx, + "_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()); + 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_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 a98f25218ec45057d61b89f1595faaabe68c419d Mon Sep 17 00:00:00 2001 From: Pavan Kumar Reddy B <57708013+pavankumar464@users.noreply.github.com> Date: Thu, 17 Sep 2026 14:02:25 +0530 Subject: [PATCH 2/3] RDKB-66901 : Updated parsers in GET, POST, and file metadata (#45) Reason for change: Fix GET, POST, and file metadata parsing Test Procedure: Test for UI actions Risks: Low Priority: P1 --- jsts/jst_prefix.js | 22 +++---- tests/parser/jst_parser_backslash.jst.parsed | 24 ++++---- .../parser/jst_parser_comment_tag.jst.parsed | 24 ++++---- .../jst_parser_include_code_after.jst.parsed | 24 ++++---- .../jst_parser_include_code_before.jst.parsed | 24 ++++---- .../jst_parser_include_malformed_1.jst.parsed | 24 ++++---- .../jst_parser_include_nested.jst.parsed | 24 ++++---- ...include_not_if_in_block_comment.jst.parsed | 24 ++++---- ...arser_include_not_if_in_content.jst.parsed | 24 ++++---- ..._include_not_if_in_line_comment.jst.parsed | 24 ++++---- .../parser/jst_parser_include_once.jst.parsed | 24 ++++---- .../jst_parser_include_runtime.jst.parsed | 24 ++++---- .../jst_parser_include_unknown.jst.parsed | 24 ++++---- tests/parser/jst_parser_line_feeds.jst.parsed | 24 ++++---- .../jst_parser_single_quotes.jst.parsed | 24 ++++---- .../jst_parser_skip_whitespace.jst.parsed | 24 ++++---- ...t_parser_template_block_content.jst.parsed | 24 ++++---- ...st_parser_template_block_string.jst.parsed | 24 ++++---- tests/parser/jst_prefix.js | 22 +++---- tests/parser_test.cpp | 59 +++++++++++++++++++ 20 files changed, 285 insertions(+), 226 deletions(-) diff --git a/jsts/jst_prefix.js b/jsts/jst_prefix.js index 7934fe6..412fc0d 100644 --- a/jsts/jst_prefix.js +++ b/jsts/jst_prefix.js @@ -228,11 +228,11 @@ if(postData) var postValues = postData.split('&'); for(var i = 0; i < postValues.length; ++i) { - var postValue = postValues[i].split('='); - if(postValue.length == 2) + var eqIdx = postValues[i].indexOf('='); + if(eqIdx != -1) { - var value = postValue[1].replace(/[+]/g," "); - $_POST[postValue[0]] = decodeURIComponent(value); + var value = postValues[i].substring(eqIdx + 1).replace(/[+]/g," "); + $_POST[postValues[i].substring(0, eqIdx)] = decodeURIComponent(value); } else { @@ -254,17 +254,17 @@ if(filesData) var fileId = null; for(var j = 0; j < fileData.length; ++j) { - var fileValue = fileData[j].split('='); - if(fileValue.length == 2) + var eqIdx = fileData[j].indexOf('='); + if(eqIdx != -1) { if(!fileId) { - fileId = decodeURIComponent(fileValue[1]); + fileId = decodeURIComponent(fileData[j].substring(eqIdx + 1)); $_FILES[fileId]={}; } else { - $_FILES[fileId][decodeURIComponent(fileValue[0])]=decodeURIComponent(fileValue[1]); + $_FILES[fileId][decodeURIComponent(fileData[j].substring(0, eqIdx))]=decodeURIComponent(fileData[j].substring(eqIdx + 1)); } } else @@ -283,10 +283,10 @@ $_GET= (function () var ar = qs.split('&'); for(var i=0; i Date: Mon, 21 Sep 2026 06:43:05 +0000 Subject: [PATCH 3/3] Add changelog for release 2.10.1 --- CHANGELOG.md | 61 +++++----------------------------------------------- 1 file changed, 5 insertions(+), 56 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8f52ae4..597c431 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,75 +4,24 @@ All notable changes to this project will be documented in this file. Dates are d Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog). -#### [2.10.0](https://github.com/rdkcentral/javascript-templates/compare/2.9.0...2.10.0) - -- Merge tag '2.9.0' into develop [`3556799`](https://github.com/rdkcentral/javascript-templates/commit/3556799ffd5f73068b6808fecfcb85bebb3a3e04) - -#### [2.9.0](https://github.com/rdkcentral/javascript-templates/compare/2.8.0...2.9.0) - -> 21 August 2026 +#### 2.10.1 +- RDKB-66901 : Updated parsers in GET, POST, and file metadata [`#45`](https://github.com/rdkcentral/javascript-templates/pull/45) +- RDKB-66802 : harden session lifecycle and cookie scope [`#44`](https://github.com/rdkcentral/javascript-templates/pull/44) - RDKB-65595 : [Risk-Critical] JST (Generic) Security Fuzzing Report [`#34`](https://github.com/rdkcentral/javascript-templates/pull/34) - RDKB-66532 RDKB-66534 : Validate session ID format before handling [`#36`](https://github.com/rdkcentral/javascript-templates/pull/36) - RDKB-65597 : [Risk-High] JST (Generic) Security Fuzzing Report [`#35`](https://github.com/rdkcentral/javascript-templates/pull/35) - RDKB-66116 : use freedesktop dbus-1.14 and gate native-build/CodeQL by source paths [`#31`](https://github.com/rdkcentral/javascript-templates/pull/31) -- Add changelog for release 2.9.0 [`664c13e`](https://github.com/rdkcentral/javascript-templates/commit/664c13e2f971516443bca4f3c0aa9c3c01daddf6) - -#### [2.8.0](https://github.com/rdkcentral/javascript-templates/compare/2.3.0...2.8.0) - -> 22 July 2026 - -- Merge tag '2.3.0' into develop [`f0478c8`](https://github.com/rdkcentral/javascript-templates/commit/f0478c8b644c4feb6f322abc556f527d562ebd58) - -#### [2.3.0](https://github.com/rdkcentral/javascript-templates/compare/2.2.0...2.3.0) - -> 22 July 2026 - - RDKB-66032 : Add PR Format Check workflow [`#29`](https://github.com/rdkcentral/javascript-templates/pull/29) - RDKB-64641 sets post_data = NULL to keep getPost() unset for file-only multipart bodies [`#27`](https://github.com/rdkcentral/javascript-templates/pull/27) - RDKB-64256 fix OOB access in log_syntax_error for malformed include parsing [`#26`](https://github.com/rdkcentral/javascript-templates/pull/26) - RDKB-65677 eliminate session_create leaks and strengthen regression coverage [`#24`](https://github.com/rdkcentral/javascript-templates/pull/24) -- Add changelog for release 2.3.0 [`6d3c2ca`](https://github.com/rdkcentral/javascript-templates/commit/6d3c2ca36a06cc878c9b125b4053dd66d4be3f25) -- Merge tag '2.2.0' into develop [`d73972d`](https://github.com/rdkcentral/javascript-templates/commit/d73972dcf3281b29682f4561a32904d0eb9611dc) - -#### [2.2.0](https://github.com/rdkcentral/javascript-templates/compare/2.1.0...2.2.0) - -> 15 June 2026 - - RDKB-65466 : sso validation token [`#19`](https://github.com/rdkcentral/javascript-templates/pull/19) -- Add changelog for release 2.2.0 [`7323c07`](https://github.com/rdkcentral/javascript-templates/commit/7323c0772b5f6c7f573093bbeca0f4b65bb1e1ef) -- Merge tag '2.1.0' into develop [`6246ada`](https://github.com/rdkcentral/javascript-templates/commit/6246adaaa950bded6b962e748c7f4058285f0a6f) - -#### [2.1.0](https://github.com/rdkcentral/javascript-templates/compare/2.0.0...2.1.0) - -> 7 May 2026 - - RDKB-63696 CMXB7-6329 CPU & Load average spike and jst crash during stability test [`#15`](https://github.com/rdkcentral/javascript-templates/pull/15) -- Add changelog for release 2.1.0 [`32e56da`](https://github.com/rdkcentral/javascript-templates/commit/32e56da5db64fa93f399f27867a83cccdcabf1ac) -- Merge tag '2.0.0' into develop [`591bc95`](https://github.com/rdkcentral/javascript-templates/commit/591bc9532ad335d4ed3a3e7b962854f8c63263e8) - -### [2.0.0](https://github.com/rdkcentral/javascript-templates/compare/1.0.1...2.0.0) - -> 4 March 2026 - - RDKB-63154 RDKB-63013 Native Build Integration [`#10`](https://github.com/rdkcentral/javascript-templates/pull/10) - Deploy fossid_integration_stateless_diffscan_target_repo action [`#9`](https://github.com/rdkcentral/javascript-templates/pull/9) - Deploy cla action [`#6`](https://github.com/rdkcentral/javascript-templates/pull/6) -- Add changelog for release 2.0.0 [`116a000`](https://github.com/rdkcentral/javascript-templates/commit/116a000a495ed2513bb60b33fb17f6547cf87ab7) -- Merge tag '1.0.1' into develop [`b57e33b`](https://github.com/rdkcentral/javascript-templates/commit/b57e33b30cde9886736a441e3d1be158759a0f5b) - -#### [1.0.1](https://github.com/rdkcentral/javascript-templates/compare/1.0.0...1.0.1) - -> 25 September 2025 - - RDKB-61157: Something has screwed up error in PAM [`#3`](https://github.com/rdkcentral/javascript-templates/pull/3) -- Add changelog for release [`b058882`](https://github.com/rdkcentral/javascript-templates/commit/b058882f7d110e523432045dc1a8269758b54f93) -- Merge tag '1.0.0' into develop [`a26bae7`](https://github.com/rdkcentral/javascript-templates/commit/a26bae70b3b18068706c5ef590e8a19f29be90c8) - -#### 1.0.0 - -> 7 August 2025 - - Import of source (stable2) [`9977c8d`](https://github.com/rdkcentral/javascript-templates/commit/9977c8d13ee9d72a94fb592ba189b5b87aabc92e) -- Deploy cla action [`c23bce2`](https://github.com/rdkcentral/javascript-templates/commit/c23bce21ea9e67479a8e55534016ea333d733196) -- Deploy fossid_integration_stateless_diffscan_target_repo action [`bd39e65`](https://github.com/rdkcentral/javascript-templates/commit/bd39e65b785a83725041924be73fda403985ff9b) +- Add changelog for release 2.9.0 [`664c13e`](https://github.com/rdkcentral/javascript-templates/commit/664c13e2f971516443bca4f3c0aa9c3c01daddf6) +- Add changelog for release [`b058882`](https://github.com/rdkcentral/javascript-templates/commit/b058882f7d110e523432045dc1a8269758b54f93)