From: Mike Stepanek (mstepane) Date: Mon, 13 Dec 2021 20:30:13 +0000 (+0000) Subject: Pull request #3209: Javascript de-aliasing X-Git-Tag: 3.1.19.0~7 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=3db9ac4ea09729778d4e2acada1d4977393fc19d;p=thirdparty%2Fsnort3.git Pull request #3209: Javascript de-aliasing Merge in SNORT/snort3 from ~DKYRYLOV/snort3:js_norm_dealias to master Squashed commit of the following: commit 5e04885d2ea2c5a56a9c4c501070ff5abfcde21d Author: dkyrylov Date: Wed Nov 17 18:39:08 2021 +0200 http_inspect: add JavaScript builtin de-aliasing --- diff --git a/src/utils/js_identifier_ctx.cc b/src/utils/js_identifier_ctx.cc index 5dff0b085..d830fb1f8 100644 --- a/src/utils/js_identifier_ctx.cc +++ b/src/utils/js_identifier_ctx.cc @@ -122,7 +122,26 @@ void JSIdentifierCtx::reset() scopes.emplace_back(JSProgramScopeType::GLOBAL); } -void JSIdentifierCtx::ProgramScope::add_alias(const char* alias, const std::string& value) +void JSIdentifierCtx::add_alias(const char* alias, const std::string&& value) +{ + assert(alias); + assert(!scopes.empty()); + scopes.back().add_alias(alias, std::move(value)); +} + +const char* JSIdentifierCtx::alias_lookup(const char* alias) const +{ + assert(alias); + + for (auto it = scopes.rbegin(); it != scopes.rend(); ++it) + { + if (const char* value = it->get_alias_value(alias)) + return value; + } + return nullptr; +} + +void JSIdentifierCtx::ProgramScope::add_alias(const char* alias, const std::string&& value) { assert(alias); aliases[alias] = value; @@ -143,25 +162,6 @@ const char* JSIdentifierCtx::ProgramScope::get_alias_value(const char* alias) co #ifdef CATCH_TEST_BUILD -void JSIdentifierCtx::add_alias(const char* alias, const std::string& value) -{ - assert(alias); - assert(!scopes.empty()); - scopes.back().add_alias(alias, value); -} - -const char* JSIdentifierCtx::alias_lookup(const char* alias) const -{ - assert(alias); - - for (auto it = scopes.rbegin(); it != scopes.rend(); ++it) - { - if (const char* value = it->get_alias_value(alias)) - return value; - } - return nullptr; -} - bool JSIdentifierCtx::scope_check(const std::list& compare) const { if (scopes.size() != compare.size()) diff --git a/src/utils/js_identifier_ctx.h b/src/utils/js_identifier_ctx.h index b692e466c..3517c09fd 100644 --- a/src/utils/js_identifier_ctx.h +++ b/src/utils/js_identifier_ctx.h @@ -39,6 +39,8 @@ public: virtual ~JSIdentifierCtxBase() = default; virtual const char* substitute(const char* identifier) = 0; + virtual void add_alias(const char* alias, const std::string&& value) = 0; + virtual const char* alias_lookup(const char* alias) const = 0; virtual bool built_in(const char* identifier) const = 0; virtual bool scope_push(JSProgramScopeType) = 0; @@ -56,6 +58,8 @@ public: const std::unordered_set& ident_built_in); virtual const char* substitute(const char* identifier) override; + virtual void add_alias(const char* alias, const std::string&& value) override; + virtual const char* alias_lookup(const char* alias) const override; virtual bool built_in(const char* identifier) const override; virtual bool scope_push(JSProgramScopeType) override; @@ -74,7 +78,7 @@ private: public: ProgramScope(JSProgramScopeType t) : t(t) {} - void add_alias(const char* alias, const std::string& value); + void add_alias(const char* alias, const std::string&& value); const char* get_alias_value(const char* alias) const; JSProgramScopeType type() const @@ -95,10 +99,6 @@ private: // advanced program scope access for testing #ifdef CATCH_TEST_BUILD public: - // alias tracking - void add_alias(const char* alias, const std::string& value); - const char* alias_lookup(const char* alias) const; - // compare scope list with the passed pattern bool scope_check(const std::list& compare) const; const std::list get_types() const; diff --git a/src/utils/js_tokenizer.h b/src/utils/js_tokenizer.h index 476dd3955..1bac36b15 100644 --- a/src/utils/js_tokenizer.h +++ b/src/utils/js_tokenizer.h @@ -117,6 +117,16 @@ private: ASI_GROUP_MAX }; + enum AliasState + { + ALIAS_NONE = 0, + ALIAS_DEFINITION, // var a + ALIAS_PREFIX, // var a +%possible PDU split% + // to handle ambiguity between a++, a+=, and a + b + ALIAS_EQUALS, // var a = + ALIAS_VALUE // var a = eval + }; + public: enum JSRet { @@ -162,6 +172,7 @@ private: JSRet do_operator_spacing(); JSRet do_semicolon_insertion(ASIGroup current); JSRet do_identifier_substitution(const char* lexeme, bool id_part); + JSRet push_identifier(const char* ident); bool unescape(const char* lexeme); void process_punctuator(JSToken tok = PUNCTUATOR); void process_closing_brace(); @@ -199,11 +210,28 @@ private: static const char* m2str(ScopeMetaType); static bool is_operator(JSToken); + void dealias_clear_mutated(bool id_part); + void dealias_increment(); + void dealias_identifier(bool id_part, bool assignment_start); + void dealias_reset(); + void dealias_prefix_reset(); + void dealias_equals(bool complex); + void dealias_append(); + void dealias_finalize(); + static const char* p_scope_codes[]; void* cur_buffer; void* tmp_buffer = nullptr; std::stringstream tmp; + + std::stringstream aliased; + std::string alias; + std::string last_dealiased; + AliasState alias_state = ALIAS_NONE; + bool prefix_increment = false; + bool dealias_stored = false; + uint8_t max_template_nesting; std::stack> brace_depth; JSToken token = UNDEFINED; diff --git a/src/utils/js_tokenizer.l b/src/utils/js_tokenizer.l index 369cd0c1c..35d6e5529 100644 --- a/src/utils/js_tokenizer.l +++ b/src/utils/js_tokenizer.l @@ -1047,38 +1047,38 @@ ALL_UNICODE [\0-\x7F]|[\xC2-\xDF][\x80-\xBF]|(\xE0[\xA0-\xBF]|[\xE1-\xEF][\x8 {BLOCK_COMMENT_SKIP} { } <> { RETURN(SCRIPT_CONTINUE) } - {LITERAL_DQ_STRING_START} { EXEC(do_semicolon_insertion(ASI_GROUP_7)) EXEC(do_spacing(LITERAL)) ECHO; BEGIN(dqstr); set_ident_norm(true); } -{LITERAL_DQ_STRING_END} { ECHO; BEGIN(divop); } + {LITERAL_DQ_STRING_START} { dealias_append(); EXEC(do_semicolon_insertion(ASI_GROUP_7)) EXEC(do_spacing(LITERAL)) ECHO; BEGIN(dqstr); set_ident_norm(true); } +{LITERAL_DQ_STRING_END} { dealias_append(); ECHO; BEGIN(divop); } {HTML_TAG_SCRIPT_CLOSE} { BEGIN(regst); RETURN(CLOSING_TAG) } \\{CR}{LF} { } \\{LF} { } \\{CR} { } {LINE_TERMINATORS} { BEGIN(regst); RETURN(BAD_TOKEN) } -{LITERAL_DQ_STRING_SKIP} { ECHO; } -{LITERAL_DQ_STRING_TEXT} { ECHO; } +{LITERAL_DQ_STRING_SKIP} { dealias_append(); ECHO; } +{LITERAL_DQ_STRING_TEXT} { dealias_append(); ECHO; } <> { RETURN(SCRIPT_CONTINUE) } - {LITERAL_SQ_STRING_START} { EXEC(do_semicolon_insertion(ASI_GROUP_7)) EXEC(do_spacing(LITERAL)) ECHO; BEGIN(sqstr); set_ident_norm(true); } -{LITERAL_SQ_STRING_END} { ECHO; BEGIN(divop); } + {LITERAL_SQ_STRING_START} { dealias_append(); EXEC(do_semicolon_insertion(ASI_GROUP_7)) EXEC(do_spacing(LITERAL)) ECHO; BEGIN(sqstr); set_ident_norm(true); } +{LITERAL_SQ_STRING_END} { dealias_append(); ECHO; BEGIN(divop); } {HTML_TAG_SCRIPT_CLOSE} { BEGIN(regst); RETURN(CLOSING_TAG) } \\{CR}{LF} { } \\{LF} { } \\{CR} { } {LINE_TERMINATORS} { BEGIN(regst); RETURN(BAD_TOKEN) } -{LITERAL_SQ_STRING_SKIP} { ECHO; } -{LITERAL_SQ_STRING_TEXT} { ECHO; } +{LITERAL_SQ_STRING_SKIP} { dealias_append(); ECHO; } +{LITERAL_SQ_STRING_TEXT} { dealias_append(); ECHO; } <> { RETURN(SCRIPT_CONTINUE) } - {LITERAL_TEMPLATE_START} { EXEC(do_semicolon_insertion(ASI_GROUP_7)) EXEC(do_spacing(LITERAL)) ECHO; BEGIN(tmpll); set_ident_norm(true); } -(\\\\)*{LITERAL_TEMPLATE_END} { ECHO; BEGIN(divop); } -(\\\\)*{LITERAL_TEMPLATE_SUBST_START} { EXEC(process_subst_open()) } + {LITERAL_TEMPLATE_START} { dealias_append(); EXEC(do_semicolon_insertion(ASI_GROUP_7)) EXEC(do_spacing(LITERAL)) ECHO; BEGIN(tmpll); set_ident_norm(true); } +(\\\\)*{LITERAL_TEMPLATE_END} { dealias_append(); ECHO; BEGIN(divop); } +(\\\\)*{LITERAL_TEMPLATE_SUBST_START} { EXEC(process_subst_open()) dealias_reset(); } {HTML_TAG_SCRIPT_CLOSE} { BEGIN(regst); RETURN(CLOSING_TAG) } (\\\\)*\\{LITERAL_TEMPLATE_SUBST_START} | /* escaped template substitution */ (\\\\)*\\{LITERAL_TEMPLATE_END} | /* escaped backtick */ -{LITERAL_TEMPLATE_OTHER} { ECHO; } +{LITERAL_TEMPLATE_OTHER} { dealias_append(); ECHO; } <> { RETURN(SCRIPT_CONTINUE) } -{LITERAL_REGEX_START} { EXEC(do_semicolon_insertion(ASI_GROUP_7)) EXEC(do_spacing(LITERAL)) yyout << '/'; states_correct(1); yyless(1); BEGIN(regex); set_ident_norm(true); } +{LITERAL_REGEX_START} { dealias_reset(); EXEC(do_semicolon_insertion(ASI_GROUP_7)) EXEC(do_spacing(LITERAL)) yyout << '/'; states_correct(1); yyless(1); BEGIN(regex); set_ident_norm(true); } {LITERAL_REGEX_END} { ECHO; BEGIN(divop); } {HTML_TAG_SCRIPT_CLOSE} { BEGIN(regst); RETURN(CLOSING_TAG) } {LITERAL_REGEX_SKIP} { ECHO; } @@ -1089,30 +1089,30 @@ ALL_UNICODE [\0-\x7F]|[\xC2-\xDF][\x80-\xBF]|(\xE0[\xA0-\xBF]|[\xE1-\xEF][\x8 <> { RETURN(SCRIPT_CONTINUE) } {DIV_OPERATOR} | -{DIV_ASSIGNMENT_OPERATOR} { previous_group = ASI_OTHER; ECHO; token = PUNCTUATOR; BEGIN(INITIAL); set_ident_norm(true); } - -{OPEN_BRACE} { EXEC(do_semicolon_insertion(ASI_GROUP_1)) if (meta_type() == ScopeMetaType::NOT_SET) { if (is_operator(token) || token == COLON || func_call()) set_meta_type(ScopeMetaType::OBJECT); else { set_meta_type(ScopeMetaType::BLOCK); EXEC(p_scope_push(meta_type())) } } EXEC(scope_push(BRACES)) if (!brace_depth.empty()) brace_depth.top()++; process_punctuator(); } -{CLOSE_BRACE} { EXEC(do_semicolon_insertion(ASI_GROUP_2)) if (meta_type() != ScopeMetaType::NOT_SET) EXEC(p_scope_pop(meta_type())) EXEC(scope_pop(BRACES)) process_closing_brace(); set_ident_norm(true); } -{OPEN_PARENTHESIS} { EXEC(do_semicolon_insertion(ASI_GROUP_3)) EXEC(scope_push(PARENTHESES)) if (token == IDENTIFIER || token == CLOSING_BRACKET || token == KEYWORD) set_func_call(true); process_punctuator(); } -{CLOSE_PARENTHESIS} { bool f_call = func_call(); bool id_norm = ident_norm(); if (meta_type() != ScopeMetaType::NOT_SET) EXEC(p_scope_pop(meta_type())) EXEC(scope_pop(PARENTHESES)) if (!f_call) set_ident_norm(id_norm); if (block_param()) { previous_group = ASI_OTHER; set_block_param(false); } else { EXEC(do_semicolon_insertion(ASI_GROUP_5)) } ECHO; token = PUNCTUATOR; BEGIN(divop); } -{OPEN_BRACKET} { EXEC(do_semicolon_insertion(ASI_GROUP_3)) EXEC(do_semicolon_insertion(ASI_GROUP_4)) EXEC(scope_push(BRACKETS)) process_punctuator(); } -{CLOSE_BRACKET} { EXEC(do_semicolon_insertion(ASI_GROUP_4)) EXEC(scope_pop(BRACKETS)) ECHO; token = CLOSING_BRACKET; BEGIN(divop); } - -{PUNCTUATOR_PREFIX} { EXEC(do_semicolon_insertion(ASI_GROUP_10)) process_punctuator(); set_ident_norm(true); } -{DOT_ACCESSOR} { previous_group = ASI_OTHER; ECHO; token = DOT; BEGIN(regst); } -{PUNCTUATOR_ARROW} { previous_group = ASI_OTHER; process_punctuator(); set_ident_norm(true); if (meta_type() == ScopeMetaType::NOT_SET) { set_meta_type(ScopeMetaType::FUNCTION); EXEC(p_scope_push(meta_type())) } } -{PUNCTUATOR_SEMICOLON} { previous_group = ASI_OTHER; process_punctuator(); set_ident_norm(true); if (meta_type() != ScopeMetaType::NOT_SET) { EXEC(p_scope_pop(meta_type())) set_meta_type(ScopeMetaType::NOT_SET); } } -{PUNCTUATOR_COLON} { previous_group = ASI_OTHER; process_punctuator(COLON); set_ident_norm(true); } -{OPERATOR_COMPARISON} { previous_group = ASI_OTHER; process_punctuator(OPERATOR_COMPARISON); set_ident_norm(true); } -{OPERATOR_COMPLEX_ASSIGNMENT} { previous_group = ASI_OTHER; process_punctuator(OPERATOR_COMPLEX_ASSIGNMENT); set_ident_norm(true); } -{OPERATOR_LOGICAL} { previous_group = ASI_OTHER; process_punctuator(OPERATOR_LOGICAL); set_ident_norm(true); } -{OPERATOR_SHIFT} { previous_group = ASI_OTHER; process_punctuator(OPERATOR_SHIFT); set_ident_norm(true); } -{PUNCTUATOR_COMMA} { previous_group = ASI_OTHER; process_punctuator(); set_ident_norm(true); } +{DIV_ASSIGNMENT_OPERATOR} { dealias_equals(true); previous_group = ASI_OTHER; ECHO; token = PUNCTUATOR; BEGIN(INITIAL); set_ident_norm(true); } + +{OPEN_BRACE} { dealias_reset(); EXEC(do_semicolon_insertion(ASI_GROUP_1)) if (meta_type() == ScopeMetaType::NOT_SET) { if (is_operator(token) || token == COLON || func_call()) set_meta_type(ScopeMetaType::OBJECT); else { set_meta_type(ScopeMetaType::BLOCK); EXEC(p_scope_push(meta_type())) } } EXEC(scope_push(BRACES)) if (!brace_depth.empty()) brace_depth.top()++; process_punctuator(); } +{CLOSE_BRACE} { dealias_clear_mutated(false); EXEC(do_semicolon_insertion(ASI_GROUP_2)) if (meta_type() != ScopeMetaType::NOT_SET) EXEC(p_scope_pop(meta_type())) EXEC(scope_pop(BRACES)) process_closing_brace(); set_ident_norm(true); } +{OPEN_PARENTHESIS} { dealias_clear_mutated(true); dealias_reset(); EXEC(do_semicolon_insertion(ASI_GROUP_3)) EXEC(scope_push(PARENTHESES)) if (token == IDENTIFIER || token == CLOSING_BRACKET || token == KEYWORD) set_func_call(true); process_punctuator(); } +{CLOSE_PARENTHESIS} { dealias_clear_mutated(false); dealias_reset(); bool f_call = func_call(); bool id_norm = ident_norm(); if (meta_type() != ScopeMetaType::NOT_SET) EXEC(p_scope_pop(meta_type())) EXEC(scope_pop(PARENTHESES)) if (!f_call) set_ident_norm(id_norm); if (block_param()) { previous_group = ASI_OTHER; set_block_param(false); } else { EXEC(do_semicolon_insertion(ASI_GROUP_5)) } ECHO; token = PUNCTUATOR; BEGIN(divop); } +{OPEN_BRACKET} { dealias_clear_mutated(true); dealias_append(); EXEC(do_semicolon_insertion(ASI_GROUP_3)) EXEC(do_semicolon_insertion(ASI_GROUP_4)) EXEC(scope_push(BRACKETS)) process_punctuator(); } +{CLOSE_BRACKET} { dealias_clear_mutated(false); dealias_append(); EXEC(do_semicolon_insertion(ASI_GROUP_4)) EXEC(scope_pop(BRACKETS)) ECHO; token = CLOSING_BRACKET; BEGIN(divop); } + +{PUNCTUATOR_PREFIX} { process_punctuator(); EXEC(do_semicolon_insertion(ASI_GROUP_10)) set_ident_norm(true); } +{DOT_ACCESSOR} { dealias_clear_mutated(true); previous_group = ASI_OTHER; dealias_append(); ECHO; token = DOT; BEGIN(regst); } +{PUNCTUATOR_ARROW} { dealias_clear_mutated(false); previous_group = ASI_OTHER; dealias_reset(); process_punctuator(); set_ident_norm(true); if (meta_type() == ScopeMetaType::NOT_SET) { set_meta_type(ScopeMetaType::FUNCTION); EXEC(p_scope_push(meta_type())) } } +{PUNCTUATOR_SEMICOLON} { dealias_clear_mutated(false); previous_group = ASI_OTHER; dealias_finalize(); process_punctuator(); set_ident_norm(true); if (meta_type() != ScopeMetaType::NOT_SET) { EXEC(p_scope_pop(meta_type())) set_meta_type(ScopeMetaType::NOT_SET); } } +{PUNCTUATOR_COLON} { dealias_clear_mutated(false); previous_group = ASI_OTHER; dealias_reset(); process_punctuator(COLON); set_ident_norm(true); } +{OPERATOR_COMPARISON} { dealias_clear_mutated(false); previous_group = ASI_OTHER; dealias_prefix_reset(); process_punctuator(OPERATOR_COMPARISON); set_ident_norm(true); } +{OPERATOR_COMPLEX_ASSIGNMENT} { dealias_clear_mutated(false); previous_group = ASI_OTHER; dealias_equals(true); process_punctuator(OPERATOR_COMPLEX_ASSIGNMENT); set_ident_norm(true); } +{OPERATOR_LOGICAL} { dealias_clear_mutated(false); previous_group = ASI_OTHER; dealias_prefix_reset(); process_punctuator(OPERATOR_LOGICAL); set_ident_norm(true); } +{OPERATOR_SHIFT} { dealias_clear_mutated(false); previous_group = ASI_OTHER; dealias_prefix_reset(); process_punctuator(OPERATOR_SHIFT); set_ident_norm(true); } +{PUNCTUATOR_COMMA} { dealias_clear_mutated(false); previous_group = ASI_OTHER; dealias_finalize(); process_punctuator(); set_ident_norm(true); } {USE_STRICT_DIRECTIVE} { previous_group = ASI_OTHER; EXEC(do_spacing(DIRECTIVE)) ECHO; BEGIN(INITIAL); yyout << ';'; set_ident_norm(true); } {USE_STRICT_DIRECTIVE_SC} { previous_group = ASI_OTHER; EXEC(do_spacing(DIRECTIVE)) ECHO; BEGIN(INITIAL); set_ident_norm(true); } -{KEYWORD_VAR_DECL} { EXEC(do_semicolon_insertion(ASI_GROUP_10)) if (token != DOT) set_ident_norm(true); EXEC(do_spacing(KEYWORD_VAR_DECL)) ECHO; BEGIN(regst); } +{KEYWORD_VAR_DECL} { EXEC(do_semicolon_insertion(ASI_GROUP_10)) if (token != DOT) set_ident_norm(true); alias_state = ALIAS_NONE; EXEC(do_spacing(KEYWORD_VAR_DECL)) ECHO; BEGIN(regst); } {KEYWORD_FUNCTION} { EXEC(do_semicolon_insertion(ASI_GROUP_10)) if (token != DOT) set_ident_norm(true); EXEC(do_spacing(KEYWORD_FUNCTION)) ECHO; BEGIN(regst); if (meta_type() == ScopeMetaType::NOT_SET) set_meta_type(ScopeMetaType::FUNCTION); } {KEYWORD_IF} | {KEYWORD_FOR} | @@ -1126,15 +1126,29 @@ ALL_UNICODE [\0-\x7F]|[\xC2-\xDF][\x80-\xBF]|(\xE0[\xA0-\xBF]|[\xE1-\xEF][\x8 {KEYWORD_ELSE} | {KEYWORD_FINALLY} { EXEC(do_semicolon_insertion(ASI_GROUP_10)) if (token != DOT) set_ident_norm(true); EXEC(do_spacing(KEYWORD_BLOCK)) ECHO; BEGIN(regst); if (meta_type() == ScopeMetaType::NOT_SET) { set_meta_type(ScopeMetaType::BLOCK); EXEC(p_scope_push(meta_type())) } } {KEYWORD_DO} { EXEC(do_semicolon_insertion(ASI_GROUP_10)) if (token != DOT) set_ident_norm(true); EXEC(do_spacing(KEYWORD_BLOCK)) ECHO; BEGIN(regst); if (meta_type() == ScopeMetaType::NOT_SET) { set_meta_type(ScopeMetaType::BLOCK); EXEC(p_scope_push(meta_type())) } set_do_loop(true); } -{KEYWORD_CLASS} { previous_group = ASI_OTHER; if (token != DOT) set_ident_norm(true); EXEC(do_spacing(KEYWORD_CLASS)) ECHO; BEGIN(regst); if (meta_type() == ScopeMetaType::NOT_SET) set_meta_type(ScopeMetaType::OBJECT); } -{KEYWORD_OTHER} { previous_group = ASI_OTHER; if (token != DOT) set_ident_norm(true); EXEC(do_spacing(KEYWORD)) ECHO; BEGIN(regst); } - -{OPERATOR_ASSIGNMENT} { previous_group = ASI_OTHER; process_punctuator(OPERATOR_ASSIGNMENT); set_ident_norm(true); } -{OPERATOR_PREFIX} { EXEC(do_semicolon_insertion(ASI_GROUP_6)) EXEC(do_operator_spacing()) ECHO; BEGIN(divop); set_ident_norm(true); } -{OPERATOR_INCR_DECR} { EXEC(do_semicolon_insertion(ASI_GROUP_8)) EXEC(do_operator_spacing()) ECHO; BEGIN(divop); set_ident_norm(true); } -{OPERATOR} { previous_group = ASI_OTHER; EXEC(do_operator_spacing()) ECHO; BEGIN(divop); set_ident_norm(true); } -{LITERAL} { EXEC(do_semicolon_insertion(ASI_GROUP_7)) EXEC(do_spacing(LITERAL)) ECHO; BEGIN(divop); set_ident_norm(true); } -{IDENTIFIER} { EXEC(do_semicolon_insertion(ASI_GROUP_7)) if (unescape(YYText())) { bool id_part = (token == DOT); EXEC(do_spacing(IDENTIFIER)) EXEC(do_identifier_substitution(YYText(), id_part)) } BEGIN(divop); } +{KEYWORD_CLASS} { previous_group = ASI_OTHER; dealias_reset(); if (token != DOT) set_ident_norm(true); EXEC(do_spacing(KEYWORD_CLASS)) ECHO; BEGIN(regst); if (meta_type() == ScopeMetaType::NOT_SET) set_meta_type(ScopeMetaType::OBJECT); } +{KEYWORD_OTHER} { previous_group = ASI_OTHER; dealias_reset(); if (token != DOT) set_ident_norm(true); EXEC(do_spacing(KEYWORD)) ECHO; BEGIN(regst); } + +{OPERATOR_ASSIGNMENT} { previous_group = ASI_OTHER; dealias_equals(false); process_punctuator(OPERATOR_ASSIGNMENT); set_ident_norm(true); } +{OPERATOR_PREFIX} { dealias_prefix_reset(); EXEC(do_semicolon_insertion(ASI_GROUP_6)) EXEC(do_operator_spacing()) ECHO; BEGIN(divop); set_ident_norm(true); } +{OPERATOR_INCR_DECR} { dealias_increment(); dealias_reset(); EXEC(do_semicolon_insertion(ASI_GROUP_8)) EXEC(do_operator_spacing()) ECHO; BEGIN(divop); set_ident_norm(true); } +{OPERATOR} { dealias_clear_mutated(false); previous_group = ASI_OTHER; dealias_prefix_reset(); EXEC(do_operator_spacing()) ECHO; BEGIN(divop); set_ident_norm(true);} +{LITERAL} { dealias_clear_mutated(false); dealias_append(); EXEC(do_semicolon_insertion(ASI_GROUP_7)) EXEC(do_spacing(LITERAL)) ECHO; BEGIN(divop); set_ident_norm(true); } +{IDENTIFIER} { + if (unescape(YYText())) { + bool id_part = (token == DOT); + bool assignment_start = token == KEYWORD_VAR_DECL || + token == PUNCTUATOR || + token == UNDEFINED; + EXEC(do_semicolon_insertion(ASI_GROUP_7)) + EXEC(do_spacing(IDENTIFIER)) + EXEC(do_identifier_substitution(YYText(), id_part)) + dealias_identifier(id_part, assignment_start); + } + else + EXEC(do_semicolon_insertion(ASI_GROUP_7)) + BEGIN(divop); + } .|{ALL_UNICODE} { previous_group = ASI_OTHER; ECHO; token = UNDEFINED; BEGIN(INITIAL); set_ident_norm(true); } <> { EEOF(eval_eof()) } @@ -1390,10 +1404,21 @@ JSTokenizer::JSRet JSTokenizer::do_identifier_substitution(const char* lexeme, b if (ident_ctx.built_in(lexeme) && !id_part) { set_ident_norm(false); - return do_identifier_substitution(lexeme, true); + yyout << lexeme; + return EOS; } - const char* ident = ident_ctx.substitute(lexeme); + const char *ident = nullptr; + if (!id_part) + ident = ident_ctx.alias_lookup(lexeme); + if (ident) + { + set_ident_norm(false); + last_dealiased = std::string(YYText()); + dealias_stored = true; + } + else + ident = ident_ctx.substitute(lexeme); if (!ident) { @@ -1407,6 +1432,7 @@ JSTokenizer::JSRet JSTokenizer::do_identifier_substitution(const char* lexeme, b "'%s' => '%s'\n", lexeme, ident); yyout << ident; + return EOS; } @@ -1418,9 +1444,11 @@ JSTokenizer::JSRet JSTokenizer::do_semicolon_insertion(ASIGroup current) newline_found = false; if (insert_semicolon[previous_group][current]) { + dealias_clear_mutated(false); + dealias_finalize(); yyout << ';'; - previous_group = ASI_OTHER; + previous_group = current; token = PUNCTUATOR; JSRet ret = EOS; @@ -1429,7 +1457,6 @@ JSTokenizer::JSRet JSTokenizer::do_semicolon_insertion(ASIGroup current) ret = p_scope_pop(meta_type()); set_meta_type(ScopeMetaType::NOT_SET); } - return ret; } } @@ -1648,6 +1675,10 @@ void JSTokenizer::states_adjust() case KEYWORD_FUNCTION: set_meta_type(ScopeMetaType::NOT_SET); break; case KEYWORD_BLOCK: p_scope_pop(meta_type()); set_meta_type(ScopeMetaType::NOT_SET); break; case KEYWORD_CLASS: set_meta_type(ScopeMetaType::NOT_SET); break; + case OPERATOR_ASSIGNMENT: alias_state = ALIAS_NONE; break; + case IDENTIFIER: + if (alias_state == ALIAS_DEFINITION) alias_state = ALIAS_NONE; + break; default: break; } @@ -1832,3 +1863,115 @@ bool JSTokenizer::is_operator(JSToken tok) } } +void JSTokenizer::dealias_clear_mutated(bool id_continue) +{ + if (!id_continue && prefix_increment && dealias_stored) + { + ident_ctx.add_alias(last_dealiased.c_str(), std::string(ident_ctx.substitute(last_dealiased.c_str()))); + } + dealias_stored = false; + prefix_increment = false; +} + +void JSTokenizer::dealias_increment() +{ + if (dealias_stored) + { + ident_ctx.add_alias(last_dealiased.c_str(), std::string(ident_ctx.substitute(last_dealiased.c_str()))); + } + prefix_increment = token != IDENTIFIER && token != CLOSING_BRACKET; + dealias_stored = false; +} + +void JSTokenizer::dealias_identifier(bool id_part, bool assignment_start) +{ + auto lexeme = YYText(); + switch(alias_state) + { + case ALIAS_NONE: + { + if (assignment_start) + { + alias = std::string(YYText()); + aliased.clear(); + aliased.str(""); + alias_state = ALIAS_DEFINITION; + } + break; + } + case ALIAS_PREFIX: + case ALIAS_DEFINITION: + { + dealias_reset(); + break; + } + case ALIAS_EQUALS: + alias_state = ALIAS_VALUE; + //fallthrough + case ALIAS_VALUE: + { + auto dealias = ident_ctx.alias_lookup(lexeme); + if ((ident_ctx.built_in(lexeme) && !id_part) || (!ident_norm() && id_part)) + aliased << YYText(); + else if (dealias) + aliased << dealias; + else + dealias_reset(); + break; + } + } +} + +void JSTokenizer::dealias_equals(bool complex_assignment) +{ + if (alias_state == ALIAS_DEFINITION || alias_state == ALIAS_PREFIX) + { + if (complex_assignment) + { + if (ident_ctx.alias_lookup(alias.c_str())) + ident_ctx.add_alias(alias.c_str(), std::string(ident_ctx.substitute(alias.c_str()))); + alias_state = ALIAS_NONE; + } + else + alias_state = ALIAS_EQUALS; + } +} + +void JSTokenizer::dealias_reset() +{ + if (alias_state != ALIAS_NONE) + { + if (alias_state == ALIAS_VALUE || alias_state == ALIAS_EQUALS) + if (ident_ctx.alias_lookup(alias.c_str())) + ident_ctx.add_alias(alias.c_str(), std::string(ident_ctx.substitute(alias.c_str()))); + alias_state = ALIAS_NONE; + } +} + +void JSTokenizer::dealias_prefix_reset() +{ + if (alias_state == ALIAS_DEFINITION) + alias_state = ALIAS_PREFIX; + else + dealias_reset(); +} + +void JSTokenizer::dealias_append() +{ + if (alias_state == ALIAS_VALUE) + aliased << YYText(); + else + dealias_reset(); +} + +void JSTokenizer::dealias_finalize() +{ + if (alias_state == ALIAS_VALUE) + { + ident_ctx.add_alias(alias.c_str(), aliased.str()); + //FIXIT-E: add check for the 'sensitive' assignments here. + alias_state = ALIAS_NONE; + } + else + dealias_reset(); +} \ No newline at end of file diff --git a/src/utils/test/CMakeLists.txt b/src/utils/test/CMakeLists.txt index cff4cd399..59efb5941 100644 --- a/src/utils/test/CMakeLists.txt +++ b/src/utils/test/CMakeLists.txt @@ -17,6 +17,17 @@ add_catch_test( js_normalizer_test ../js_normalizer.cc ../streambuf.cc ../util_cstring.cc + js_test_utils.cc +) + +add_catch_test( js_dealias_test + SOURCES + ${FLEX_js_tokenizer_OUTPUTS} + ../js_identifier_ctx.cc + ../js_normalizer.cc + ../streambuf.cc + ../util_cstring.cc + js_test_utils.cc ) add_catch_test( js_identifier_ctx_test diff --git a/src/utils/test/js_dealias_test.cc b/src/utils/test/js_dealias_test.cc new file mode 100644 index 000000000..574bad3cc --- /dev/null +++ b/src/utils/test/js_dealias_test.cc @@ -0,0 +1,810 @@ +//-------------------------------------------------------------------------- +// Copyright (C) 2021-2021 Cisco and/or its affiliates. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License Version 2 as published +// by the Free Software Foundation. You may not use, modify or distribute +// this program under any other version of the GNU General Public License. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +//-------------------------------------------------------------------------- +// js_dealias_test.cc author Oleksandr Serhiienko + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "catch/catch.hpp" + +#include "utils/test/js_test_utils.h" + +using namespace snort; + +// Unit tests + +#ifdef CATCH_TEST_BUILD + +TEST_CASE("De-aliasing - basic", "[JSNormalizer]") +{ + SECTION("function") + test_normalization( + "a = eval; a(\"2 + 2\");", + "var_0000=eval;eval(\"2 + 2\");" + ); + + SECTION("composite") + test_normalization( + "a = console.log.execute; a(\"2 + 2\");", + "var_0000=console.log.execute;console.log.execute(\"2 + 2\");" + ); + + SECTION("square bracket accessor") + test_normalization( + "a = console['log']; a(\"2 + 2\");", + "var_0000=console['log'];console['log'](\"2 + 2\");" + ); + + SECTION("function call") + test_normalization( + "a = eval('console.log(\\\'foo\\\')'); a;", + "var_0000=eval('console.log(\\\'foo\\\')');var_0000;" + ); + + SECTION("function call - composite") + test_normalization( + "a = console.log('123'); a;", + "var_0000=console.log('123');var_0000;" + ); + + SECTION("function call - square bracket accessor") + test_normalization( + "a = console['log']('foo'); a;", + "var_0000=console['log']('foo');var_0000;" + ); + + SECTION("function call - return value with dot accessor") + test_normalization( + "a = document.getElementById('id').field; a;", + "var_0000=document.getElementById('id').field;var_0000;" + ); + + SECTION("function call - return value with square bracket accessor") + test_normalization( + "a = document.getElementById('id')['field']; a;", + "var_0000=document.getElementById('id')['field'];var_0000;" + ); + + SECTION("with var keyword") + test_normalization( + "var a = eval; a('2 + 2');", + "var var_0000=eval;eval('2 + 2');" + ); + + SECTION("with let keyword") + test_normalization( + "let a = eval; a('2 + 2');", + "let var_0000=eval;eval('2 + 2');" + ); + + SECTION("with const keyword") + test_normalization( + "const a = eval; a('2 + 2');", + "const var_0000=eval;eval('2 + 2');" + ); + + SECTION("with *=") + test_normalization( + "a *= eval; a;", + "var_0000*=eval;var_0000;" + ); + + SECTION("with /=") + test_normalization( + "a /= eval; a;", + "var_0000/=eval;var_0000;" + ); + + SECTION("with %=") + test_normalization( + "a %= eval; a;", + "var_0000%=eval;var_0000;" + ); + + SECTION("with +=") + test_normalization( + "a += eval; a;", + "var_0000+=eval;var_0000;" + ); + + SECTION("with -=") + test_normalization( + "a -= eval; a;", + "var_0000-=eval;var_0000;" + ); + + SECTION("with <<=") + test_normalization( + "a <<= eval; a;", + "var_0000<<=eval;var_0000;" + ); + + SECTION("with >>=") + test_normalization( + "a >>= eval; a;", + "var_0000>>=eval;var_0000;" + ); + + SECTION("with >>>=") + test_normalization( + "a >>>= eval; a;", + "var_0000>>>=eval;var_0000;" + ); + + SECTION("with &=") + test_normalization( + "a &= eval; a;", + "var_0000&=eval;var_0000;" + ); + + SECTION("with ^=") + test_normalization( + "a ^= eval; a;", + "var_0000^=eval;var_0000;" + ); + + SECTION("with |=") + test_normalization( + "a |= eval; a;", + "var_0000|=eval;var_0000;" + ); + + SECTION("with prefix increment") + test_normalization( + "a = eval; a; ++a; a;", + "var_0000=eval;eval;++eval;var_0000;" + ); + + SECTION("with prefix decrement") + test_normalization( + "a = eval; a; --a; a;", + "var_0000=eval;eval;--eval;var_0000;" + ); + + SECTION("with postfix increment") + test_normalization( + "a = eval; a; a++; a;", + "var_0000=eval;eval;eval++;var_0000;" + ); + + SECTION("with postfix decrement") + test_normalization( + "a = eval; a; a--; a;", + "var_0000=eval;eval;eval--;var_0000;" + ); + + SECTION("with tilde") + test_normalization( + "a = eval; ~a; a;", + "var_0000=eval;~eval;eval;" + ); + + SECTION("with exclamation sign") + test_normalization( + "a = eval; !a; a;", + "var_0000=eval;!eval;eval;" + ); + + SECTION("with comparison operators") + test_normalization( + "a = eval;" + "a >= a;" + "a == a;" + "a != a;" + "a === a;" + "a !== a;" + "a < a;" + "a > a;" + "a <= a;", + "var_0000=eval;" + "eval>=eval;" + "eval==eval;" + "eval!=eval;" + "eval===eval;" + "eval!==eval;" + "evaleval;" + "eval<=eval;" + ); + + SECTION("with binary operators") + test_normalization( + "a = eval;" + "a & a;" + "a | a;" + "a ^ a;" + "a >> a;" + "a << a;", + "var_0000=eval;" + "eval&eval;" + "eval|eval;" + "eval^eval;" + "eval>>eval;" + "eval< {a; a = console.log; a;}; a;", + "var_0000=eval;var_0001=(eval)=>{eval;eval=console.log;console.log;};eval;" + // corner case + ); + + SECTION("default function argument") + test_normalization( + "a = eval; function f(a = 2) { a; } a;", + "var_0000=eval;function var_0001(eval=2){var_0000;}eval;" + ); + + SECTION("default arrow function argument") + test_normalization( + "a = eval; b = (a = 2) => { a; }; a;", + "var_0000=eval;var_0001=(eval=2)=>{var_0000;};var_0000;" + // corner case + ); + + SECTION("multiple nesting") + test_normalization( + "a = eval; function f() { a; a = console.log; a; " + "if (true) { a; a = document; a; } a; } a;", + "var_0000=eval;function var_0001(){eval;eval=console.log;console.log;" + "if(true){console.log;console.log=document;document;}console.log;}eval;" + ); + + SECTION("automatic semicolon insertion") + test_normalization( + "a = eval; if (true)\na\n=\nconsole\n.log\n\n a;", + "var_0000=eval;if(true)eval=console.log;eval;" + ); +} + +#endif // CATCH_TEST_BUILD + diff --git a/src/utils/test/js_normalizer_test.cc b/src/utils/test/js_normalizer_test.cc index 5e4e0661e..7efffebe0 100644 --- a/src/utils/test/js_normalizer_test.cc +++ b/src/utils/test/js_normalizer_test.cc @@ -21,57 +21,24 @@ #include "config.h" #endif -#include "catch/catch.hpp" - #include -#include + +#include "catch/catch.hpp" #include "utils/js_identifier_ctx.h" #include "utils/js_normalizer.h" +#include "utils/test/js_test_utils.h" -// Mock functions - -namespace snort -{ -[[noreturn]] void FatalError(const char*, ...) -{ exit(EXIT_FAILURE); } -void trace_vprintf(const char*, TraceLevel, const char*, const Packet*, const char*, va_list) {} -uint8_t TraceApi::get_constraints_generation() { return 0; } -void TraceApi::filter(const Packet&) {} -} - -THREAD_LOCAL const snort::Trace* http_trace = nullptr; - -class JSIdentifierCtxStub : public JSIdentifierCtxBase -{ -public: - JSIdentifierCtxStub() = default; - - const char* substitute(const char* identifier) override - { return identifier; } - bool built_in(const char*) const override - { return false; } - bool scope_push(JSProgramScopeType) override { return true; } - bool scope_pop(JSProgramScopeType) override { return true; } - void reset() override {} - size_t size() const override { return 0; } -}; +using namespace snort; -// Test cases +// Unit tests -using namespace snort; +#ifdef CATCH_TEST_BUILD #define DEPTH 65535 #define MAX_TEMPLATE_NESTING 4 #define MAX_BRACKET_DEPTH 256 #define MAX_SCOPE_DEPTH 256 - -static const std::unordered_set s_ident_built_in { "console", "eval", "document" }; - -// Unit tests - -#ifdef CATCH_TEST_BUILD - #define DST_SIZE 512 #define NORMALIZE(src) \ @@ -3709,16 +3676,6 @@ TEST_CASE("built-in identifiers split", "[JSNormalizer]") } } -static void test_scope(const char* context, std::list stack) -{ - std::string buf(context); - buf += ""; - JSIdentifierCtx ident_ctx(DEPTH, MAX_SCOPE_DEPTH, s_ident_built_in); - JSNormalizer normalizer(ident_ctx, DEPTH, MAX_TEMPLATE_NESTING, MAX_BRACKET_DEPTH); - normalizer.normalize(buf.c_str(), buf.size()); - CHECK(ident_ctx.get_types() == stack); -} - TEST_CASE("Scope tracking - basic","[JSNormalizer]") { SECTION("Global only") @@ -4011,24 +3968,6 @@ TEST_CASE("Scope tracking - closing","[JSNormalizer]") test_scope("function() { if (true)\nfor ( ; ; ) a = 2 }", {GLOBAL}); } -typedef std::tuple> PduCase; -static void test_normalization(std::list pdus) -{ - JSIdentifierCtx ident_ctx(DEPTH, MAX_SCOPE_DEPTH, s_ident_built_in); - JSNormalizer normalizer(ident_ctx, DEPTH, MAX_TEMPLATE_NESTING, MAX_BRACKET_DEPTH); - for(auto pdu:pdus) - { - const char* source; - const char* expected; - std::list stack; - std::tie(source,expected,stack) = pdu; - normalizer.normalize(source, strlen(source)); - std::string result_buf(normalizer.get_script(), normalizer.script_size()); - CHECK(ident_ctx.get_types() == stack); - CHECK(result_buf == expected); - } -} - TEST_CASE("Scope tracking - over multiple PDU","[JSNormalizer]") { // Every line represents a PDU. Each pdu has input buffer, expected script @@ -4186,17 +4125,6 @@ TEST_CASE("Scope tracking - over multiple PDU","[JSNormalizer]") }); } -static void test_normalization_bad(const char* source, const char* expected, - JSTokenizer::JSRet eret) -{ - JSIdentifierCtx ident_ctx(DEPTH, MAX_SCOPE_DEPTH, s_ident_built_in); - JSNormalizer normalizer(ident_ctx, DEPTH, MAX_TEMPLATE_NESTING, MAX_BRACKET_DEPTH); - auto ret = normalizer.normalize(source, strlen(source)); - std::string result_buf(normalizer.get_script(), normalizer.script_size()); - CHECK(eret == ret); - CHECK(result_buf == expected); -} - TEST_CASE("Scope tracking - error handling", "[JSNormalizer]") { SECTION("not identifier after var keyword") diff --git a/src/utils/test/js_test_utils.cc b/src/utils/test/js_test_utils.cc new file mode 100644 index 000000000..62914ffb8 --- /dev/null +++ b/src/utils/test/js_test_utils.cc @@ -0,0 +1,100 @@ +//-------------------------------------------------------------------------- +// Copyright (C) 2021-2021 Cisco and/or its affiliates. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License Version 2 as published +// by the Free Software Foundation. You may not use, modify or distribute +// this program under any other version of the GNU General Public License. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +//-------------------------------------------------------------------------- +// js_test_utils.cc author Danylo Kyrylov + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "utils/test/js_test_utils.h" + +#include "catch/catch.hpp" + +namespace snort +{ +[[noreturn]] void FatalError(const char*, ...) +{ exit(EXIT_FAILURE); } +void trace_vprintf(const char*, TraceLevel, const char*, const Packet*, const char*, va_list) {} +uint8_t TraceApi::get_constraints_generation() { return 0; } +void TraceApi::filter(const Packet&) {} +} + +THREAD_LOCAL const snort::Trace* http_trace = nullptr; + +using namespace snort; + +void test_scope(const char* context, std::list stack) +{ + std::string buf(context); + buf += ""; + JSIdentifierCtx ident_ctx(norm_depth, max_scope_depth, s_ident_built_in); + JSNormalizer normalizer(ident_ctx, norm_depth, max_template_nesting, max_bracket_depth); + normalizer.normalize(buf.c_str(), buf.size()); + CHECK(ident_ctx.get_types() == stack); +} + +void test_normalization(const char* source, const char* expected) +{ + JSIdentifierCtx ident_ctx(norm_depth, max_scope_depth, s_ident_built_in); + JSNormalizer normalizer(ident_ctx, norm_depth, max_template_nesting, max_bracket_depth); + normalizer.normalize(source, strlen(source)); + std::string result_buf(normalizer.get_script(), normalizer.script_size()); + CHECK(result_buf == expected); +} + +void test_normalization_bad(const char* source, const char* expected, JSTokenizer::JSRet eret) +{ + JSIdentifierCtx ident_ctx(norm_depth, max_scope_depth, s_ident_built_in); + JSNormalizer normalizer(ident_ctx, norm_depth, max_template_nesting, max_bracket_depth); + auto ret = normalizer.normalize(source, strlen(source)); + std::string result_buf(normalizer.get_script(), normalizer.script_size()); + CHECK(eret == ret); + CHECK(result_buf == expected); +} + +void test_normalization(const std::vector& pdus) +{ + JSIdentifierCtx ident_ctx(norm_depth, max_scope_depth, s_ident_built_in); + JSNormalizer normalizer(ident_ctx, norm_depth, max_template_nesting, max_bracket_depth); + + for (const auto& pdu : pdus) + { + const char* source = pdu.first; + const char* expected = pdu.second; + normalizer.normalize(source, strlen(source)); + std::string result_buf(normalizer.get_script(), normalizer.script_size()); + CHECK(result_buf == expected); + } +} + +void test_normalization(std::list pdus) +{ + JSIdentifierCtx ident_ctx(norm_depth, max_scope_depth, s_ident_built_in); + JSNormalizer normalizer(ident_ctx, norm_depth, max_template_nesting, max_bracket_depth); + for (auto pdu:pdus) + { + const char* source; + const char* expected; + std::list stack; + std::tie(source,expected,stack) = pdu; + normalizer.normalize(source, strlen(source)); + std::string result_buf(normalizer.get_script(), normalizer.script_size()); + CHECK(ident_ctx.get_types() == stack); + CHECK(result_buf == expected); + } +} diff --git a/src/utils/test/js_test_utils.h b/src/utils/test/js_test_utils.h new file mode 100644 index 000000000..dd4e2c87c --- /dev/null +++ b/src/utils/test/js_test_utils.h @@ -0,0 +1,73 @@ +//-------------------------------------------------------------------------- +// Copyright (C) 2021-2021 Cisco and/or its affiliates. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License Version 2 as published +// by the Free Software Foundation. You may not use, modify or distribute +// this program under any other version of the GNU General Public License. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +//-------------------------------------------------------------------------- +// js_test_utils.cc author Danylo Kyrylov + +#ifndef JS_TEST_UTILS_H +#define JS_TEST_UTILS_H + +#include +#include +#include +#include +#include + +#include "utils/js_identifier_ctx.h" +#include "utils/js_normalizer.h" + +constexpr int norm_depth=65535; +constexpr int max_template_nesting=4; +constexpr int max_bracket_depth=256; +constexpr int max_scope_depth=256; + +namespace snort +{ +[[noreturn]] void FatalError(const char*, ...); +void trace_vprintf(const char*, TraceLevel, const char*, const Packet*, const char*, va_list); +} + +class JSIdentifierCtxStub : public JSIdentifierCtxBase +{ +public: + JSIdentifierCtxStub() = default; + + const char* substitute(const char* identifier) override + { return identifier; } + virtual void add_alias(const char*, const std::string&&) override {} + virtual const char* alias_lookup(const char* alias) const override + { return alias; } + bool built_in(const char*) const override + { return false; } + bool scope_push(JSProgramScopeType) override { return true; } + bool scope_pop(JSProgramScopeType) override { return true; } + void reset() override {} + size_t size() const override { return 0; } +}; + +static const std::unordered_set s_ident_built_in { "console", "eval", "document" }; + +void test_scope(const char* context, std::list stack); +void test_normalization(const char* source, const char* expected); +void test_normalization_bad(const char* source, const char* expected, JSTokenizer::JSRet eret); +typedef std::pair PduCase; +// source, expected for a single PDU +void test_normalization(const std::vector& pdus); +typedef std::tuple> ScopedPduCase; +// source, expected, and current scope type stack for a single PDU +void test_normalization(std::list pdus); + +#endif // JS_TEST_UTILS_H