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