break;
case JSTokenizer::SCRIPT_ENDED:
case JSTokenizer::CLOSING_TAG:
- *infractions += INF_JS_CLOSING_TAG;
- events->create_event(EVENT_JS_CLOSING_TAG);
- ssn->js_built_in_event = true;
+ assert(false); // should not be present in external
break;
case JSTokenizer::BAD_TOKEN:
case JSTokenizer::WRONG_CLOSING_SYMBOL:
*infractions += INF_MIXED_ENCODINGS;
events->create_event(EVENT_MIXED_ENCODINGS);
}
+ if (js_ctx.is_closing_tag_seen())
+ {
+ *infractions += INF_JS_CLOSING_TAG;
+ events->create_event(EVENT_JS_CLOSING_TAG);
+ }
if (ssn->js_built_in_event)
break;
const std::list<JSProgramScopeType> JSIdentifierCtx::get_types() const
{
std::list<JSProgramScopeType> return_list;
- for(const auto& scope:scopes)
+ for (const auto& scope : scopes)
{
return_list.push_back(scope.type());
}
bool is_opening_tag_seen() const
{ return tokenizer.is_opening_tag_seen(); }
+ bool is_closing_tag_seen() const
+ { return tokenizer.is_closing_tag_seen(); }
+
#if defined(CATCH_TEST_BUILD) || defined(BENCHMARK_TEST)
const char* get_tmp_buf() const
{ return tmp_buf; }
bool is_unescape_nesting_seen() const;
bool is_mixed_encoding_seen() const;
bool is_opening_tag_seen() const;
+ bool is_closing_tag_seen() const;
+
protected:
[[noreturn]] void LexerError(const char* msg) override
{ snort::FatalError("%s", msg); }
void lit_int_code_point(int base);
void char_code_no_match();
void explicit_otag();
+ void ctag_in_regex();
static const char* p_scope_codes[];
bool unescape_nest_seen = false;
bool mixed_encoding_seen = false;
bool opening_tag_seen = false;
+ bool closing_tag_seen = false;
uint8_t max_template_nesting;
VStack<uint16_t> brace_depth;
{LITERAL_DQ_STRING_START} { EXEC(literal_dq_string_start()) }
<dqstr,unesc_dqstr>{LITERAL_DQ_STRING_END} { dealias_append(); ECHO; BEGIN(divop); }
<dqstr,unesc_dqstr>{HTML_TAG_SCRIPT_OPEN} { if (!ext_script) { opening_tag_seen = true; } ECHO; }
-<dqstr,unesc_dqstr>{HTML_TAG_SCRIPT_CLOSE} { if (ext_script) { ECHO; } else { BEGIN(regst); RETURN(CLOSING_TAG) } }
+<dqstr,unesc_dqstr>{HTML_TAG_SCRIPT_CLOSE} { if (!ext_script) { BEGIN(regst); RETURN(CLOSING_TAG) } else { ECHO; } }
<dqstr,unesc_dqstr>\\{CR}{LF} { /* skip */ }
<dqstr,unesc_dqstr>\\{LF} { /* skip */ }
<dqstr,unesc_dqstr>\\{CR} { /* skip */ }
{LITERAL_SQ_STRING_START} { EXEC(literal_sq_string_start()) }
<sqstr,unesc_sqstr>{LITERAL_SQ_STRING_END} { dealias_append(); ECHO; BEGIN(divop); }
<sqstr,unesc_sqstr>{HTML_TAG_SCRIPT_OPEN} { if (!ext_script) { opening_tag_seen = true; } ECHO; }
-<sqstr,unesc_sqstr>{HTML_TAG_SCRIPT_CLOSE} { if (ext_script) { ECHO; } else { BEGIN(regst); RETURN(CLOSING_TAG) } }
+<sqstr,unesc_sqstr>{HTML_TAG_SCRIPT_CLOSE} { if (!ext_script) { BEGIN(regst); RETURN(CLOSING_TAG) } else { ECHO; } }
<sqstr,unesc_sqstr>\\{CR}{LF} { /* skip */ }
<sqstr,unesc_sqstr>\\{LF} { /* skip */ }
<sqstr,unesc_sqstr>\\{CR} { /* skip */ }
<tmpll,unesc_tmpll>(\\\\)*{LITERAL_TEMPLATE_END} { dealias_append(); ECHO; BEGIN(divop); }
<tmpll,unesc_tmpll>(\\\\)*{LITERAL_TEMPLATE_SUBST_START} { EXEC(process_subst_open()) dealias_reset(); }
<tmpll,unesc_tmpll>{HTML_TAG_SCRIPT_OPEN} { if (!ext_script) { opening_tag_seen = true; } ECHO; }
-<tmpll,unesc_tmpll>{HTML_TAG_SCRIPT_CLOSE} { BEGIN(regst); RETURN(CLOSING_TAG) }
+<tmpll,unesc_tmpll>{HTML_TAG_SCRIPT_CLOSE} { if (!ext_script) { BEGIN(regst); RETURN(CLOSING_TAG) } else { ECHO; } }
<tmpll,unesc_tmpll><<EOF>> { RETURN(SCRIPT_CONTINUE) }
<tmpll>{UNICODE_ESCAPE_SEQUENCE} |
<tmpll>{HEX_ESCAPE_SEQUENCE} { escaped_unicode_utf_8(); }
<regst>{LITERAL_REGEX_START} { EXEC(literal_regex_start()) }
<regex>{LITERAL_REGEX_END} { EXEC(literal_regex_end()) }
<regex>{HTML_TAG_SCRIPT_OPEN} { if (!ext_script) { opening_tag_seen = true; } ECHO; }
-<regex>{HTML_TAG_SCRIPT_CLOSE} { BEGIN(regst); RETURN(CLOSING_TAG) }
+<regex>{HTML_TAG_SCRIPT_CLOSE} { if (!ext_script) { BEGIN(regst); RETURN(CLOSING_TAG) } else { ctag_in_regex(); } }
<regex>\\{CR} { BEGIN(regst); RETURN(BAD_TOKEN) }
<regex>\\{LF} { BEGIN(regst); RETURN(BAD_TOKEN) }
<regex>{CR} { BEGIN(regst); RETURN(BAD_TOKEN) }
return opening_tag_seen;
}
+bool JSTokenizer::is_closing_tag_seen() const
+{
+ return closing_tag_seen;
+}
+
void JSTokenizer::set_block_param(bool f)
{
scope_cur().block_param = f;
}
JSTokenizer::JSRet JSTokenizer::html_closing_script_tag()
-{ return global_scope() ? SCRIPT_ENDED : ENDED_IN_INNER_SCOPE; }
+{
+ if (!ext_script)
+ return global_scope() ? SCRIPT_ENDED : ENDED_IN_INNER_SCOPE;
+ else
+ {
+ closing_tag_seen = true;
+ states_correct(1);
+ operator_comparison();
+ return EOS;
+ }
+}
JSTokenizer::JSRet JSTokenizer::literal_dq_string_start()
{
operator_comparison();
}
+void JSTokenizer::ctag_in_regex()
+{
+ // out of '</script>', consume only the leading '<' and renormalize the rest
+ states_correct(1);
+ ECHO;
+}
+
JSTokenizer::JSRet JSTokenizer::process(size_t& bytes_in, bool external_script)
{
yy_flush_buffer(YY_CURRENT_BUFFER);
static const char unexpected_tag_expected26[] =
"var regex=/ <script> /;";
+static const char unexpected_tag_buf27[] =
+ "var template = ` </script> `;";
+
+static const char unexpected_tag_expected27[] =
+ "var template=` ";
+
+static const char unexpected_tag_expected27_ext[] =
+ "var template=` </script> `;";
+
+static const char unexpected_tag_buf28[] =
+ "var regex = / </script> /;/";
+
+static const char unexpected_tag_expected28[] =
+ "var regex=/ ";
+
+static const char unexpected_tag_expected28_ext[] =
+ "var regex=/ </s cript>/;/";
+
+static const char unexpected_tag_buf29[] =
+ "var a = 5 </script>/";
+
+static const char unexpected_tag_expected29[] =
+ "var a=5";
+
+static const char unexpected_tag_expected29_ext[] =
+ "var a=5</script>/";
+
TEST_CASE("nested script tags", "[JSNormalizer]")
{
SECTION("explicit open tag - simple")
VALIDATE(unexpected_tag_buf26, unexpected_tag_expected26);
CHECK_OTAG(true);
}
+ SECTION("closing tag within template literal")
+ {
+ NORMALIZE(unexpected_tag_buf27);
+ VALIDATE_FAIL(unexpected_tag_buf27, unexpected_tag_expected27, JSTokenizer::CLOSING_TAG, 26);
+ }
+ SECTION("closing tag within regex literal")
+ {
+ NORMALIZE(unexpected_tag_buf28);
+ VALIDATE_FAIL(unexpected_tag_buf28, unexpected_tag_expected28, JSTokenizer::CLOSING_TAG, 23);
+ }
+ SECTION("closing tag from regex literal expression")
+ {
+ NORMALIZE(unexpected_tag_buf29);
+ VALIDATE_FAIL(unexpected_tag_buf29, unexpected_tag_expected29, JSTokenizer::SCRIPT_ENDED, 19);
+ }
}
TEST_CASE("opening tag sequence", "[JSNormalizer]")
VALIDATE(unexpected_tag_buf26, unexpected_tag_expected26);
CHECK_OTAG(false);
}
+ SECTION("closing tag within template literal")
+ {
+ NORMALIZE_EXT(unexpected_tag_buf27);
+ VALIDATE(unexpected_tag_buf27, unexpected_tag_expected27_ext);
+ }
+ SECTION("closing tag within regex literal")
+ {
+ NORMALIZE_EXT(unexpected_tag_buf28);
+ VALIDATE(unexpected_tag_buf28, unexpected_tag_expected28_ext);
+ }
+ SECTION("closing tag from regex literal expression")
+ {
+ NORMALIZE_EXT(unexpected_tag_buf29);
+ CHECK(norm.is_closing_tag_seen());
+ VALIDATE(unexpected_tag_buf29, unexpected_tag_expected29_ext);
+ }
}
TEST_CASE("split between tokens", "[JSNormalizer]")