1 //--------------------------------------------------------------------------
2 // Copyright (C) 2021-2021 Cisco and/or its affiliates. All rights reserved.
3 //
4 // This program is free software; you can redistribute it and/or modify it
5 // under the terms of the GNU General Public License Version 2 as published
6 // by the Free Software Foundation. You may not use, modify or distribute
7 // this program under any other version of the GNU General Public License.
8 //
9 // This program is distributed in the hope that it will be useful, but
10 // WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 // General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License along
15 // with this program; if not, write to the Free Software Foundation, Inc.,
16 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 //--------------------------------------------------------------------------
18 // js_identifier_ctx.cc author Oleksandr Serhiienko <oserhiie@cisco.com>
19
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23
24 #include "js_identifier_ctx.h"
25
26 #include <cassert>
27
28 #if !defined(CATCH_TEST_BUILD) && !defined(BENCHMARK_TEST)
29 #include "service_inspectors/http_inspect/http_enum.h"
30 #include "service_inspectors/http_inspect/http_module.h"
31 #else
32 namespace HttpEnums
33 {
34 enum PEG_COUNT
35 {
36 PEG_JS_IDENTIFIER
37 };
38 }
39
40 class HttpModule
41 {
42 public:
increment_peg_counts(HttpEnums::PEG_COUNT)43 static void increment_peg_counts(HttpEnums::PEG_COUNT) {}
44 };
45 #endif // CATCH_TEST_BUILD
46
47 #define MAX_LAST_NAME 65535
48 #define HEX_DIGIT_MASK 15
49
50 static const char hex_digits[] =
51 {
52 '0', '1','2','3', '4', '5', '6', '7', '8','9', 'a', 'b', 'c', 'd', 'e', 'f'
53 };
54
format_name(int32_t num)55 static inline std::string format_name(int32_t num)
56 {
57 std::string name("var_");
58 name.reserve(8);
59 name.push_back(hex_digits[(num >> 12) & HEX_DIGIT_MASK]);
60 name.push_back(hex_digits[(num >> 8) & HEX_DIGIT_MASK]);
61 name.push_back(hex_digits[(num >> 4) & HEX_DIGIT_MASK]);
62 name.push_back(hex_digits[num & HEX_DIGIT_MASK]);
63
64 return name;
65 }
66
JSIdentifierCtx(int32_t depth,uint32_t max_scope_depth,const std::unordered_set<std::string> & ignored_ids)67 JSIdentifierCtx::JSIdentifierCtx(int32_t depth, uint32_t max_scope_depth,
68 const std::unordered_set<std::string>& ignored_ids)
69 : ignored_ids(ignored_ids), depth(depth), max_scope_depth(max_scope_depth)
70 {
71 scopes.emplace_back(JSProgramScopeType::GLOBAL);
72 }
73
substitute(const char * identifier)74 const char* JSIdentifierCtx::substitute(const char* identifier)
75 {
76 const auto it = ident_names.find(identifier);
77 if (it != ident_names.end())
78 return it->second.c_str();
79
80 if (ident_last_name >= depth || ident_last_name > MAX_LAST_NAME)
81 return nullptr;
82
83 ident_names[identifier] = format_name(ident_last_name++);
84 HttpModule::increment_peg_counts(HttpEnums::PEG_JS_IDENTIFIER);
85 return ident_names[identifier].c_str();
86 }
87
is_ignored(const char * identifier) const88 bool JSIdentifierCtx::is_ignored(const char* identifier) const
89 {
90 return ignored_ids.count(identifier);
91 }
92
scope_push(JSProgramScopeType t)93 bool JSIdentifierCtx::scope_push(JSProgramScopeType t)
94 {
95 assert(t != JSProgramScopeType::GLOBAL && t != JSProgramScopeType::PROG_SCOPE_TYPE_MAX);
96
97 if (scopes.size() >= max_scope_depth)
98 return false;
99
100 scopes.emplace_back(t);
101 return true;
102 }
103
scope_pop(JSProgramScopeType t)104 bool JSIdentifierCtx::scope_pop(JSProgramScopeType t)
105 {
106 assert(t != JSProgramScopeType::GLOBAL && t != JSProgramScopeType::PROG_SCOPE_TYPE_MAX);
107
108 if (scopes.back().type() != t)
109 return false;
110
111 assert(scopes.size() != 1);
112 scopes.pop_back();
113 return true;
114 }
115
reset()116 void JSIdentifierCtx::reset()
117 {
118 ident_last_name = 0;
119
120 ident_names.clear();
121 scopes.clear();
122 scopes.emplace_back(JSProgramScopeType::GLOBAL);
123 }
124
add_alias(const char * alias,const std::string && value)125 void JSIdentifierCtx::add_alias(const char* alias, const std::string&& value)
126 {
127 assert(alias);
128 assert(!scopes.empty());
129 scopes.back().add_alias(alias, std::move(value));
130 }
131
alias_lookup(const char * alias) const132 const char* JSIdentifierCtx::alias_lookup(const char* alias) const
133 {
134 assert(alias);
135
136 for (auto it = scopes.rbegin(); it != scopes.rend(); ++it)
137 {
138 if (const char* value = it->get_alias_value(alias))
139 return value;
140 }
141 return nullptr;
142 }
143
add_alias(const char * alias,const std::string && value)144 void JSIdentifierCtx::ProgramScope::add_alias(const char* alias, const std::string&& value)
145 {
146 assert(alias);
147 aliases[alias] = value;
148 }
149
get_alias_value(const char * alias) const150 const char* JSIdentifierCtx::ProgramScope::get_alias_value(const char* alias) const
151 {
152 assert(alias);
153
154 const auto it = aliases.find(alias);
155 if (it != aliases.end())
156 return it->second.c_str();
157 else
158 return nullptr;
159 }
160
161 // advanced program scope access for testing
162
163 #ifdef CATCH_TEST_BUILD
164
scope_check(const std::list<JSProgramScopeType> & compare) const165 bool JSIdentifierCtx::scope_check(const std::list<JSProgramScopeType>& compare) const
166 {
167 if (scopes.size() != compare.size())
168 return false;
169
170 auto cmp = compare.begin();
171 for (auto it = scopes.begin(); it != scopes.end(); ++it, ++cmp)
172 {
173 if (it->type() != *cmp)
174 return false;
175 }
176 return true;
177 }
178
get_types() const179 const std::list<JSProgramScopeType> JSIdentifierCtx::get_types() const
180 {
181 std::list<JSProgramScopeType> return_list;
182 for(const auto& scope:scopes)
183 {
184 return_list.push_back(scope.type());
185 }
186 return return_list;
187 }
188
scope_contains(size_t pos,const char * alias) const189 bool JSIdentifierCtx::scope_contains(size_t pos, const char* alias) const
190 {
191 size_t offset = 0;
192 for (auto it = scopes.begin(); it != scopes.end(); ++it, ++offset)
193 {
194 if (offset == pos)
195 return it->get_alias_value(alias);
196 }
197 assert(false);
198 return false;
199 }
200
201 #endif // CATCH_TEST_BUILD
202
203