1 
2 #include <cassert>
3 #include <stdlib.h>
4 #include <stdio.h>
5 #include <stdarg.h>
6 #include <time.h>
7 #include <string>
8 #include <algorithm>
9 #include <sys/types.h>
10 #include <sys/stat.h>
11 #include <errno.h>
12 #include <limits.h>
13 
14 // Careful: must include globals first for extern definitions
15 #include "thrift/globals.h"
16 
17 #include "thrift/parse/t_program.h"
18 #include "thrift/parse/t_scope.h"
19 #include "thrift/parse/t_const.h"
20 #include "thrift/parse/t_field.h"
21 
22 #include "thrift/version.h"
23 
24 #include "thrift/audit/t_audit.h"
25 
26 extern int g_warn;
27 extern std::string g_curpath;
28 extern bool g_return_failure;
29 
thrift_audit_warning(int level,const char * fmt,...)30 void thrift_audit_warning(int level, const char* fmt, ...) {
31    if (g_warn < level) {
32       return;
33    }
34    va_list args;
35    printf("[Thrift Audit Warning:%s] ", g_curpath.c_str());
36    va_start(args, fmt);
37    vprintf(fmt, args);
38    va_end(args);
39    printf("\n");
40 }
41 
thrift_audit_failure(const char * fmt,...)42 void thrift_audit_failure(const char* fmt, ...) {
43   va_list args;
44   fprintf(stderr, "[Thrift Audit Failure:%s] ", g_curpath.c_str());
45   va_start(args, fmt);
46   vfprintf(stderr, fmt, args);
47   va_end(args);
48   fprintf(stderr, "\n");
49   g_return_failure = true;
50 }
51 
compare_namespace(t_program * newProgram,t_program * oldProgram)52 void compare_namespace(t_program* newProgram, t_program* oldProgram)
53 {
54    const std::map<std::string, std::string>& newNamespaceMap = newProgram->get_all_namespaces();
55    const std::map<std::string, std::string>& oldNamespaceMap = oldProgram->get_all_namespaces();
56 
57    for(const auto & oldNamespaceMapIt : oldNamespaceMap)
58    {
59       auto newNamespaceMapIt = newNamespaceMap.find(oldNamespaceMapIt.first);
60       if(newNamespaceMapIt == newNamespaceMap.end())
61       {
62          thrift_audit_warning(1, "Language %s not found in new thrift file\n", (oldNamespaceMapIt.first).c_str());
63       }
64       else if((newNamespaceMapIt->second) != oldNamespaceMapIt.second)
65       {
66          thrift_audit_warning(1, "Namespace %s changed in new thrift file\n", (oldNamespaceMapIt.second).c_str());
67       }
68    }
69 }
70 
compare_enum_values(t_enum * newEnum,t_enum * oldEnum)71 void compare_enum_values(t_enum* newEnum,t_enum* oldEnum)
72 {
73    const std::vector<t_enum_value*>& oldEnumValues = oldEnum->get_constants();
74    for(auto oldEnumValue : oldEnumValues)
75    {
76       int enumValue = oldEnumValue->get_value();
77       t_enum_value* newEnumValue = newEnum->get_constant_by_value(enumValue);
78       if(newEnumValue != nullptr)
79       {
80          std::string enumName = oldEnumValue->get_name();
81          if(enumName != newEnumValue->get_name())
82          {
83             thrift_audit_warning(1, "Name of the value %d changed in enum %s\n", enumValue, oldEnum->get_name().c_str());
84          }
85       }
86       else
87       {
88          thrift_audit_failure("Enum value %d missing in %s\n", enumValue, oldEnum->get_name().c_str());
89       }
90 
91    }
92 }
93 
compare_enums(const std::vector<t_enum * > & newEnumList,const std::vector<t_enum * > & oldEnumList)94 void compare_enums(const std::vector<t_enum*>& newEnumList, const std::vector<t_enum*>& oldEnumList)
95 {
96    std::map<std::string,t_enum*> newEnumMap;
97    std::vector<t_enum*>::const_iterator newEnumIt;
98    for(newEnumIt = newEnumList.begin(); newEnumIt != newEnumList.end(); newEnumIt++)
99    {
100       newEnumMap[(*newEnumIt)->get_name()] = *newEnumIt;
101    }
102    std::vector<t_enum*>::const_iterator oldEnumIt;
103    for(oldEnumIt = oldEnumList.begin(); oldEnumIt != oldEnumList.end(); oldEnumIt++)
104    {
105       std::map<std::string,t_enum*>::iterator newEnumMapIt;
106       newEnumMapIt = newEnumMap.find((*oldEnumIt)->get_name());
107 
108       if(newEnumMapIt == newEnumMap.end())
109       {
110          thrift_audit_warning(1, "Enum %s not found in new thrift file\n",(*oldEnumIt)->get_name().c_str());
111       }
112       else
113       {
114          compare_enum_values(newEnumMapIt->second, *oldEnumIt);
115       }
116    }
117 }
118 
119 //This function returns 'true' if the two arguements are of same types.
120 //Returns false if they are of different type
compare_type(t_type * newType,t_type * oldType)121 bool compare_type(t_type* newType, t_type* oldType)
122 {
123    //Comparing names of two types will work when the newType and oldType are basic types or structs or enums.
124    //However, when they are containers, get_name() returns empty for which we have to compare the type of
125    //their elements as well.
126    if((newType->get_name()).empty() && (oldType->get_name()).empty())
127    {
128 
129       if(newType->is_list() && oldType->is_list())
130       {
131          t_type* newElementType = ((t_list*)newType)->get_elem_type();
132          t_type* oldElementType = ((t_list*)oldType)->get_elem_type();
133          return compare_type(newElementType, oldElementType);
134       }
135       else if(newType->is_map() && oldType->is_map())
136       {
137          t_type* newKeyType = ((t_map*)newType)->get_key_type();
138          t_type* oldKeyType = ((t_map*)oldType)->get_key_type();
139 
140          t_type* newValType = ((t_map*)newType)->get_val_type();
141          t_type* oldValType = ((t_map*)oldType)->get_val_type();
142 
143          return (compare_type(newKeyType, oldKeyType) && compare_type(newValType, oldValType));
144       }
145       else if(newType->is_set() && oldType->is_set())
146       {
147          t_type* newElementType = ((t_set*)newType)->get_elem_type();
148          t_type* oldElementType = ((t_set*)oldType)->get_elem_type();
149          return compare_type(newElementType, oldElementType);
150       }
151       else
152       {
153          return false;
154       }
155    }
156    else if(newType->get_name() == oldType->get_name())
157    {
158       return true;
159    }
160    else
161    {
162       return false;
163    }
164 }
165 
compare_pair(std::pair<t_const_value *,t_const_value * > newMapPair,std::pair<t_const_value *,t_const_value * > oldMapPair)166 bool compare_pair(std::pair<t_const_value*, t_const_value*> newMapPair, std::pair<t_const_value*, t_const_value*> oldMapPair)
167 {
168    return compare_defaults(newMapPair.first, oldMapPair.first) && compare_defaults(newMapPair.second, oldMapPair.second);
169 }
170 
171 // This function returns 'true' if the default values are same. Returns false if they are different.
compare_defaults(t_const_value * newStructDefault,t_const_value * oldStructDefault)172 bool compare_defaults(t_const_value* newStructDefault, t_const_value* oldStructDefault)
173 {
174    if(newStructDefault == nullptr && oldStructDefault == nullptr) return true;
175    else if(newStructDefault == nullptr && oldStructDefault != nullptr) return false;
176    else if (newStructDefault != nullptr && oldStructDefault == nullptr) return false;
177 
178    if(newStructDefault->get_type() != oldStructDefault->get_type())
179    {
180       return false;
181    }
182 
183    switch(newStructDefault->get_type())
184    {
185       case t_const_value::CV_INTEGER:
186          return (newStructDefault->get_integer() == oldStructDefault->get_integer());
187       case t_const_value::CV_DOUBLE:
188          return (newStructDefault->get_double() == oldStructDefault->get_double());
189       case t_const_value::CV_STRING:
190          return (newStructDefault->get_string() == oldStructDefault->get_string());
191       case t_const_value::CV_LIST:
192          {
193             const std::vector<t_const_value*>& oldDefaultList = oldStructDefault->get_list();
194             const std::vector<t_const_value*>& newDefaultList = newStructDefault->get_list();
195             bool defaultValuesCompare = (oldDefaultList.size() == newDefaultList.size());
196 
197             return defaultValuesCompare && std::equal(newDefaultList.begin(), newDefaultList.end(), oldDefaultList.begin(), compare_defaults);
198          }
199       case t_const_value::CV_MAP:
200          {
201             const std::map<t_const_value*, t_const_value*, t_const_value::value_compare> newMap = newStructDefault->get_map();
202             const std::map<t_const_value*, t_const_value*, t_const_value::value_compare> oldMap = oldStructDefault->get_map();
203 
204             bool defaultValuesCompare = (oldMap.size() == newMap.size());
205 
206             return defaultValuesCompare && std::equal(newMap.begin(), newMap.end(), oldMap.begin(), compare_pair);
207          }
208       case t_const_value::CV_IDENTIFIER:
209          return (newStructDefault->get_identifier() == oldStructDefault->get_identifier());
210       default:
211          return false;
212    }
213 
214 }
215 
compare_struct_field(t_field * newField,t_field * oldField,std::string oldStructName)216 void compare_struct_field(t_field* newField, t_field* oldField, std::string oldStructName)
217 {
218    t_type* newFieldType = newField->get_type();
219    t_type* oldFieldType = oldField->get_type();
220    if(!compare_type(newFieldType, oldFieldType))
221    {
222       thrift_audit_failure("Struct Field Type Changed for Id = %d in %s \n", newField->get_key(), oldStructName.c_str());
223    }
224 
225    // A Struct member can be optional if it is mentioned explicitly, or if it is assigned with default values.
226    bool newStructFieldOptional = (newField->get_req() != t_field::T_REQUIRED);
227    bool oldStructFieldOptional = (oldField->get_req() != t_field::T_REQUIRED);
228 
229    if(newStructFieldOptional != oldStructFieldOptional)
230    {
231       thrift_audit_failure("Struct Field Requiredness Changed for Id = %d in %s \n", newField->get_key(), oldStructName.c_str());
232    }
233    if(newStructFieldOptional || oldStructFieldOptional)
234    {
235       if(!compare_defaults(newField->get_value(), oldField->get_value()))
236       {
237          thrift_audit_warning(1, "Default value changed for Id = %d in %s \n", newField->get_key(), oldStructName.c_str());
238       }
239    }
240 
241    std::string fieldName = newField->get_name();
242    if(fieldName != oldField->get_name())
243    {
244       thrift_audit_warning(1, "Struct field name changed for Id = %d in %s\n", newField->get_key(), oldStructName.c_str());
245    }
246 
247 }
248 
compare_single_struct(t_struct * newStruct,t_struct * oldStruct,const std::string & oldStructName=std::string ())249 void compare_single_struct(t_struct* newStruct, t_struct* oldStruct, const std::string& oldStructName = std::string())
250 {
251    std::string structName = oldStructName.empty() ? oldStruct->get_name() : oldStructName;
252    const std::vector<t_field*>& oldStructMembersInIdOrder = oldStruct->get_sorted_members();
253    const std::vector<t_field*>& newStructMembersInIdOrder = newStruct->get_sorted_members();
254    auto oldStructMemberIt = oldStructMembersInIdOrder.begin();
255    auto newStructMemberIt = newStructMembersInIdOrder.begin();
256 
257    // Since we have the struct members in their ID order, comparing their IDs can be done by traversing the two member
258    // lists together.
259    while(!(oldStructMemberIt == oldStructMembersInIdOrder.end() && newStructMemberIt == newStructMembersInIdOrder.end()))
260    {
261       if(newStructMemberIt == newStructMembersInIdOrder.end() && oldStructMemberIt != oldStructMembersInIdOrder.end())
262       {
263          // A field ID has been removed from the end.
264          thrift_audit_failure("Struct Field removed for Id = %d in %s \n", (*oldStructMemberIt)->get_key(), structName.c_str());
265          oldStructMemberIt++;
266       }
267       else if(newStructMemberIt != newStructMembersInIdOrder.end() && oldStructMemberIt == oldStructMembersInIdOrder.end())
268       {
269          //New field ID has been added to the end.
270          if((*newStructMemberIt)->get_req() == t_field::T_REQUIRED)
271          {
272             thrift_audit_failure("Required Struct Field Added for Id = %d in %s \n", (*newStructMemberIt)->get_key(), structName.c_str());
273          }
274          newStructMemberIt++;
275       }
276       else if((*newStructMemberIt)->get_key() == (*oldStructMemberIt)->get_key())
277       {
278          //Field ID found in both structs. Compare field types, default values.
279          compare_struct_field(*newStructMemberIt, *oldStructMemberIt, structName);
280 
281          newStructMemberIt++;
282          oldStructMemberIt++;
283       }
284       else if((*newStructMemberIt)->get_key() < (*oldStructMemberIt)->get_key())
285       {
286          //New Field Id is inserted in between
287          //Adding fields to struct is fine, but adding them in the middle is suspicious. Error!!
288          thrift_audit_failure("Struct field is added in the middle with Id = %d in %s\n",  (*newStructMemberIt)->get_key(),  structName.c_str());
289          newStructMemberIt++;
290       }
291       else if((*newStructMemberIt)->get_key() > (*oldStructMemberIt)->get_key())
292       {
293          //A field is deleted in newStruct.
294          thrift_audit_failure("Struct Field removed for Id = %d in %s \n",  (*oldStructMemberIt)->get_key(), structName.c_str());
295          oldStructMemberIt++;
296       }
297 
298    }
299 }
300 
compare_structs(const std::vector<t_struct * > & newStructList,const std::vector<t_struct * > & oldStructList)301 void compare_structs(const std::vector<t_struct*>& newStructList, const std::vector<t_struct*>& oldStructList)
302 {
303    std::map<std::string,t_struct*> newStructMap;
304    std::vector<t_struct*>::const_iterator newStructListIt;
305    for(newStructListIt = newStructList.begin(); newStructListIt != newStructList.end(); newStructListIt++)
306    {
307       newStructMap[(*newStructListIt)->get_name()] = *newStructListIt;
308    }
309 
310    std::vector<t_struct*>::const_iterator oldStructListIt;
311    for(oldStructListIt = oldStructList.begin(); oldStructListIt != oldStructList.end(); oldStructListIt++)
312    {
313       std::map<std::string, t_struct*>::iterator newStructMapIt;
314       newStructMapIt = newStructMap.find((*oldStructListIt)->get_name());
315       if(newStructMapIt == newStructMap.end())
316       {
317          thrift_audit_failure("Struct %s not found in new thrift file\n", (*oldStructListIt)->get_name().c_str());
318       }
319       else
320       {
321          compare_single_struct(newStructMapIt->second, *oldStructListIt);
322       }
323    }
324 
325 }
326 
compare_single_function(t_function * newFunction,t_function * oldFunction)327 void compare_single_function(t_function* newFunction, t_function* oldFunction)
328 {
329    t_type* newFunctionReturnType = newFunction->get_returntype();
330 
331    if(newFunction->is_oneway() != oldFunction->is_oneway())
332    {
333       thrift_audit_failure("Oneway attribute changed for function %s\n",oldFunction->get_name().c_str());
334    }
335    if(!compare_type(newFunctionReturnType, oldFunction->get_returntype()))
336    {
337       thrift_audit_failure("Return type changed for function %s\n",oldFunction->get_name().c_str());
338    }
339 
340    //Compare function arguments.
341    compare_single_struct(newFunction->get_arglist(), oldFunction->get_arglist());
342    std::string exceptionName = oldFunction->get_name();
343    exceptionName += "_exception";
344    compare_single_struct(newFunction->get_xceptions(), oldFunction->get_xceptions(), exceptionName);
345 }
346 
compare_functions(const std::vector<t_function * > & newFunctionList,const std::vector<t_function * > & oldFunctionList)347 void compare_functions(const std::vector<t_function*>& newFunctionList, const std::vector<t_function*>& oldFunctionList)
348 {
349    std::map<std::string, t_function*> newFunctionMap;
350    std::map<std::string, t_function*>::iterator newFunctionMapIt;
351    for(auto newFunctionIt : newFunctionList)
352    {
353       newFunctionMap[newFunctionIt->get_name()] = newFunctionIt;
354    }
355 
356    for(auto oldFunctionIt : oldFunctionList)
357    {
358       newFunctionMapIt = newFunctionMap.find(oldFunctionIt->get_name());
359       if(newFunctionMapIt == newFunctionMap.end())
360       {
361          thrift_audit_failure("New Thrift File has missing function %s\n",oldFunctionIt->get_name().c_str());
362          continue;
363       }
364       else
365       {
366          //Function is found in both thrift files. Compare return type and argument list
367          compare_single_function(newFunctionMapIt->second, oldFunctionIt);
368       }
369    }
370 
371 }
372 
compare_services(const std::vector<t_service * > & newServices,const std::vector<t_service * > & oldServices)373 void compare_services(const std::vector<t_service*>& newServices, const std::vector<t_service*>& oldServices)
374 {
375    std::vector<t_service*>::const_iterator oldServiceIt;
376 
377    std::map<std::string, t_service*> newServiceMap;
378    for(auto newService : newServices)
379    {
380       newServiceMap[newService->get_name()] = newService;
381    }
382 
383 
384    for(oldServiceIt = oldServices.begin(); oldServiceIt != oldServices.end(); oldServiceIt++)
385    {
386       const std::string oldServiceName = (*oldServiceIt)->get_name();
387       auto newServiceMapIt = newServiceMap.find(oldServiceName);
388 
389       if(newServiceMapIt == newServiceMap.end())
390       {
391          thrift_audit_failure("New Thrift file is missing a service %s\n", oldServiceName.c_str());
392       }
393       else
394       {
395          t_service* oldServiceExtends = (*oldServiceIt)->get_extends();
396          t_service* newServiceExtends = (newServiceMapIt->second)->get_extends();
397 
398          if(oldServiceExtends == nullptr)
399          {
400             // It is fine to add extends. So if service in older thrift did not have any extends, we are fine.
401             // DO Nothing
402          }
403          else if(oldServiceExtends != nullptr && newServiceExtends == nullptr)
404          {
405             thrift_audit_failure("Change in Service inheritance for %s\n", oldServiceName.c_str());
406          }
407          else
408          {
409             std::string oldExtendsName = oldServiceExtends->get_name();
410             std::string newExtendsName = newServiceExtends->get_name();
411 
412             if( newExtendsName != oldExtendsName)
413             {
414                thrift_audit_failure("Change in Service inheritance for %s\n", oldServiceName.c_str());
415             }
416          }
417 
418          compare_functions((newServiceMapIt->second)->get_functions(), (*oldServiceIt)->get_functions());
419       }
420 
421    }
422 
423 }
424 
compare_consts(const std::vector<t_const * > & newConst,const std::vector<t_const * > & oldConst)425 void compare_consts(const std::vector<t_const*>& newConst, const std::vector<t_const*>& oldConst)
426 {
427    std::vector<t_const*>::const_iterator newConstIt;
428    std::vector<t_const*>::const_iterator oldConstIt;
429 
430    std::map<std::string, t_const*> newConstMap;
431 
432    for(newConstIt = newConst.begin(); newConstIt != newConst.end(); newConstIt++)
433    {
434       newConstMap[(*newConstIt)->get_name()] = *newConstIt;
435    }
436 
437    std::map<std::string, t_const*>::const_iterator newConstMapIt;
438    for(oldConstIt = oldConst.begin(); oldConstIt != oldConst.end(); oldConstIt++)
439    {
440       newConstMapIt = newConstMap.find((*oldConstIt)->get_name());
441       if(newConstMapIt == newConstMap.end())
442       {
443          thrift_audit_warning(1, "Constants Missing %s \n", ((*oldConstIt)->get_name()).c_str());
444       }
445       else if(!compare_type((newConstMapIt->second)->get_type(), (*oldConstIt)->get_type()))
446       {
447          thrift_audit_warning(1, "Constant %s is of different type \n", ((*oldConstIt)->get_name()).c_str());
448       }
449       else if(!compare_defaults((newConstMapIt->second)->get_value(), (*oldConstIt)->get_value()))
450       {
451          thrift_audit_warning(1, "Constant %s has different value\n", ((*oldConstIt)->get_name()).c_str());
452       }
453    }
454 }
455