From 6531232cd2a0249b7d2c40e9691c5d04d083d6ec Mon Sep 17 00:00:00 2001
From: Fawwaz Anugrah Wiradhika Dharmasatya <anugrahdwfawwaz@gmail.com>
Date: Thu, 6 Jun 2024 13:28:36 +0700
Subject: [PATCH] feat: handle object stored in variable (not transtive yet)

---
 src/lib/ACLAnalyzer.py               | 63 ++++++++++++++++----------
 src/lib/ASTGenerator.py              |  2 -
 src/lib/RouteSanitizationAnalyzer.py | 68 +++++++++++++++++++++-------
 tests/tc1/class_views.py             |  6 +--
 tests/tc1/views.py                   |  5 +-
 todo.txt                             |  9 ++--
 6 files changed, 100 insertions(+), 53 deletions(-)

diff --git a/src/lib/ACLAnalyzer.py b/src/lib/ACLAnalyzer.py
index 80270b7..a33f53a 100644
--- a/src/lib/ACLAnalyzer.py
+++ b/src/lib/ACLAnalyzer.py
@@ -36,11 +36,8 @@ class ACLAnalyzer():
   
 
   def analyze_class(self,route:ElementContext)->list[str]:
-    #TODO CFG
     method_lists = list(filter(lambda x:x['type']=='class_method' and x['parent']==route.get_base_element_name(),route.cfg.source_code_method_list))
     # Remove yang ada di no check list
-    # class method list
-    class_methods = route.cfg.ast.children[-1].children
     for ctx in route.context:
       if ctx.startswith("Exempt::"):
         exempted = ctx.split("::")[1]
@@ -50,11 +47,6 @@ class ACLAnalyzer():
     # Cek setiap fungsi
     class_acls = []
     for f in method_lists:
-      # Cari namanya
-      # start_node = None
-      # for cm in class_methods:
-      #   if(cm.children[1].text.decode()==f['name']):
-      #     start_node = cm
       ctx = ElementContext(f"{f['parent']}.{f['name']}",'function',route.location,[f"Parent::{f['parent']}"])
       # if start_node:
       # ctx.set_cfg(CFGGenerator().generate(ctx))
@@ -69,7 +61,8 @@ class ACLAnalyzer():
     # Cek parent nya untuk menentukaan apakah ada dekorator
     # Inisialisasi principal_list
     # if not is_route:
-#print("ACL",route)
+    # print("ACL",route)
+    var_list = []
     principal_list = [principal for principal in self.acl_info.principal_list]
 
     route.cfg.reset()
@@ -99,14 +92,19 @@ class ACLAnalyzer():
       if node.type in ['call','assignment']:
         # Cek call method
         # Format: [attribute,argument_list] | [identifier,argument_list]
-#print("masuk pak eko")
+        # print("masuk pak eko")
         child_list = flatten_node(node.children)
         fun_name = ""
         for i in range(len(child_list)):
           if child_list[i] in [':',',',]:
             #TODO handle buat variabel
             continue
-          elif child_list[i] in ['=',')']:
+          elif child_list[i] in [')']:
+              fun_name = ""
+          elif child_list[i] == '=':
+              # Simpan nama variabel di left hand side
+              #TODO simpan line
+              var_list.append({'name':fun_name,'type':child_list[i+1]})
               fun_name = ""
           elif child_list[i]=='==':
             # Cek left side
@@ -347,15 +345,25 @@ class ACLAnalyzer():
           elif components[i] in ['(',')']:
             # Cek apakah fungsinya dipanggil
             if fun_name:
+              parts = fun_name.split(".")
+              # Coba konversi dulu
+              # print(var_list)
+              if len(parts)>1:
+                #TODO hanya handle yg paling awal aja
+                # print(parts)
+                for var in var_list:
+                  if parts[0]==var['name']:
+                    parts[0] = var['type']
+                fun_name = ".".join(parts)    
               for rute in route.cfg.source_code_method_list:
                 function_name = ""
-                if fun_name.split(".")[0]!='self' and rute['type']=='module_function' and rute['name']==fun_name:
+                if parts[0]!='self' and rute['type']=='module_function' and rute['name']==fun_name:
                   function_name = fun_name
-                elif rute['type']=='class_method' and rute['name']==fun_name.split(".")[-1]:
-                  if fun_name.split(".")[0]!='self':
+                elif rute['type']=='class_method' and rute['name']==parts[-1]:
+                  if parts[0]!='self':
                     function_name = fun_name
                   else:
-                    function_name = fun_name.split(".")[-1]
+                    function_name = parts[-1]
                 else:
                   function_name = fun_name
                 # Cek
@@ -406,7 +414,6 @@ class ACLAnalyzer():
     return principal_list
 
   def analyze_module(self,route:ElementContext)->list[list[str,list[str]]]:
-    #TODO CFG
     # Cari info semua fungsi di module
     method_lists = route.cfg.source_code_method_list
     # Remove yang ada di no check list
@@ -463,6 +470,7 @@ class ACLAnalyzer():
 #print("acl",acl_list)
       # Kasus kalau acl nya kelas atau fungsi
       elif acl_class.type in ['class','function']:
+        # print("kim no")
         # Cek apakah ada parent context
         parent_class = ""
         for k in acl_class.context:
@@ -474,13 +482,14 @@ class ACLAnalyzer():
           # Kali aja ada di module ini
           for function in route.cfg.source_code_method_list:
             if(acl_class.type=='class' and function['type']=='class_method' and function['parent']==acl_class.get_base_element_name()):
-              if ctx:
+              full_name = name
+              if len(name.split("."))==1 and ctx:
                 j = 0
                 elmt = ctx[j]
                 while elmt in ['(',')',',']:
                   j+=1
                   elmt = ctx[j]
-              full_name = f"{name}.{elmt}"
+                full_name = f"{name}.{elmt}"
               if(full_name==f"{function['parent']}.{function['name']}"):
                 # Cek apakah dia manggil fungsi yang diimport di acl
 #print(301
@@ -518,11 +527,11 @@ class ACLAnalyzer():
         else:
           # Beda file, cek di import ada gak
           # Kali aja ada di module ini
-          # if name=='is_admin' and route.name=="Views.class_get_logs":
-          #   print("Parent",parent_class)
-          #   print("NAMA",name)
-          #   print("ACL",acl_class)
-          #   print("ROUTE",route)
+          # if name=='RoleCheck.is_admin':
+            # print("Parent",parent_class)
+            # print("NAMA",name)
+            # print("ACL",acl_class)
+            # print("ROUTE",route)
             # print(route.cfg.source_code)
           dependency_lists = route.cfg.source_code_dependency_list
           route_position =  route.cfg.get_line_index(route.name)
@@ -548,7 +557,7 @@ class ACLAnalyzer():
                   if m["rename"]:
                     # Pake rename
                     comparator = m["rename"]
-                  if ctx:
+                  if len(name.split("."))==1 and ctx:
                     j = 0
                     elmt = ctx[j]
                     j+=1
@@ -556,20 +565,24 @@ class ACLAnalyzer():
                       elmt = ctx[j]
                       j+=1
                     name = f"{name}.{elmt}"
+                  # print("nemu",name)
                   for method in acl_class.cfg.source_code_method_list:
                     if method['type']!='class_method' or (method['type']=='class_method' and method['parent']!=m['original']):
                       continue
+                    # print(f'{comparator}.{method["name"]}')
                     if(name==f'{comparator}.{method["name"]}'):
                       # print("GOMEN", self.acl_info.acl_context)
                       # Cek apakah dia manggil fungsi yang diimport di acl
 #print(501)
                       this_acl = None
                       for acl in self.analyze_class(acl_class):
+                        # print("ACL",acl)
                         if acl[0]==name:
                           this_acl = acl
                           break
                       if this_acl:
                         acl_list = list(set(acl_list) & set(acl[1]))
+                        # print("ACL LUST",acl_list)
             elif(acl_class.type=='function'):
               # Cukup cek apakah kelas atau fungsinya sama
                 # print("pake nanya")
@@ -605,7 +618,7 @@ class ACLAnalyzer():
                       # print("prev",acl_list)
                       acl_list = list(set(acl_list) & set(self.analyze_function(acl_class,False)))
                       # print("after",acl_list)
-#print("aclku",acl_list)
+    # print("aclku",acl_list)
                       # acl_list = self.acl_info.acl_context[acl_class.get_base_element_name()]
     return acl_list
   
diff --git a/src/lib/ASTGenerator.py b/src/lib/ASTGenerator.py
index 616f686..229e782 100644
--- a/src/lib/ASTGenerator.py
+++ b/src/lib/ASTGenerator.py
@@ -107,8 +107,6 @@ class ASTGenerator():
       class_name = ""
       if node[0].parent.parent.type=='block':
         class_name = node[0].parent.parent.parent.children[1].text.decode()
-      #TODO handle decorated class method
-      # print(node)
       if(node[0].text.decode() == ctx.name.split("(")[0] or (class_name and f"{class_name}.{node[0].text.decode()}"==ctx.name.split("(")[0])):
         # Fungsinya ketemu
         ast = node[0].parent
diff --git a/src/lib/RouteSanitizationAnalyzer.py b/src/lib/RouteSanitizationAnalyzer.py
index b11421b..0256a60 100644
--- a/src/lib/RouteSanitizationAnalyzer.py
+++ b/src/lib/RouteSanitizationAnalyzer.py
@@ -59,6 +59,7 @@ class RouteSanitizationAnalyzer():
     return unsanitized_methods
 
   def analyze_function(self,route:ElementContext)->bool:
+    var_list = []
     # print("rota",route)
     ##print(self.project_info.acl_class)
     # Cek parent nya untuk menentukaan apakah ada dekorator
@@ -95,12 +96,20 @@ class RouteSanitizationAnalyzer():
         # Format: [attribute,argument_list] | [identifier,argument_list]
         child_list = flatten_node(node.children)
         fun_name = ""
-        for child in child_list:
-          if child in [':',',']:
+        # if b'rolecheck = RoleCheck()' in node.text:
+          # print("as",node)
+          # print(child_list)
+        for i in range(len(child_list)):
+          if child_list[i] in [':',',']:
             continue
-          elif child in ['=',')']:
+          elif child_list[i] in [')']:
               fun_name = ""
-          elif child in ['(']:
+          elif child_list[i] == '=':
+              # Simpan nama variabel di left hand side
+              #TODO simpan line
+              var_list.append({'name':fun_name,'type':child_list[i+1]})
+              fun_name = ""
+          elif child_list[i] in ['(']:
             # Cek apakah fungsinya ada
             if fun_name:
               # cek buat fungsi di kelas yang sama
@@ -122,7 +131,7 @@ class RouteSanitizationAnalyzer():
                   return True
               fun_name = ""
           else:
-            fun_name += child
+            fun_name += child_list[i]
       elif node.type=='while_statement':
         for elmt in node.children:
           # Skip kalau dah ':'
@@ -260,7 +269,10 @@ class RouteSanitizationAnalyzer():
           else:
             components += flatten_node(child.children)
         # Cek tiap componentnya
-        #print("comp",components)
+        # if b'if (rolecheck.is_admin(current_user)):' in node.text:
+        #   print("asu",node)
+        #   print(child_list)
+        #   print("comp",components)
         fun_name = ""
 #print(components)
         for i in range(len(components)):
@@ -271,18 +283,27 @@ class RouteSanitizationAnalyzer():
             # Cek apakah fungsinya dipanggil
             #print("fun",fun_name)
             if fun_name:
+              parts = fun_name.split(".")
+              # Coba konversi dulu
+              # print(var_list)
+              if len(parts)>1:
+                #TODO hanya handle yg paling awal aja
+                # print(parts)
+                for var in var_list:
+                  if parts[0]==var['name']:
+                    parts[0] = var['type']              
 #print(route.cfg.source_code_method_list)
               for rute in route.cfg.source_code_method_list:
                 function_name = ""
-                if fun_name.split(".")[0]!='self' and rute['type']=='module_function' and rute['name']==fun_name:
-                  function_name = fun_name
-                elif rute['type']=='class_method' and rute['name']==fun_name.split(".")[-1]:
-                  if fun_name.split(".")[0]!='self':
-                    function_name = fun_name
+                if parts[0]!='self' and rute['type']=='module_function' and rute['name']==".".join(parts):
+                  function_name = ".".join(parts)
+                elif rute['type']=='class_method' and rute['name']==parts[-1]:
+                  if parts[0]!='self':
+                    function_name = ".".join(parts)
                   else:
-                    function_name = fun_name.split(".")[-1]
+                    function_name = parts[-1]
                 else: 
-                  function_name = fun_name
+                  function_name = ".".join(parts)
                 #print("func",function_name)
                 # Cek
 #print(function_name)
@@ -361,13 +382,17 @@ class RouteSanitizationAnalyzer():
 #print(acl_class.type)
             if(acl_class.type=='class' and function['type']=='class_method' and function['parent']==acl_class.get_base_element_name()):
               ##print("furu",function['name'],acl_class.get_base_element_name())
-              if ctx:
+              #TODO hanya handle 1 accessing misal a.b, a.b.c gak bisa
+              full_name = name
+              if len(name.split("."))==1 and ctx:
                 j = 0
                 elmt = ctx[j]
                 while elmt in ['(',')',',']:
                   j+=1
+                  if j >= len(ctx):
+                    break
                   elmt = ctx[j]
-              full_name = f"{name}.{elmt}"
+                full_name = f"{name}.{elmt}"
               if(full_name==f"{function['parent']}.{function['name']}"):
                 # Cek apakah dia manggil fungsi yang diimport di acl
 #print(301)
@@ -406,21 +431,30 @@ class RouteSanitizationAnalyzer():
                   if m["rename"]:
                     # Pake rename
                     comparator = m["rename"]
-                  if ctx:
+                  #TODO hanya handle 1 accessing misal a.b, a.b.c gak bisa
+                  # print(name)
+                  # print("asnp",name.split("."))
+                  if len(name.split("."))==1 and ctx:
+                    # print("as",ctx)
                     j = 0
                     elmt = ctx[j]
                     while elmt in ['(',')',',']:
                       j+=1
+                      if j >= len(ctx):
+                        break
                       elmt = ctx[j]
                     name = f"{name}.{elmt}"
                   for method in acl_class.cfg.source_code_method_list:
-#print("method",method)
+                    # print("method",method)
 #print("namae",name)
                     if method['type']!='class_method' or (method['type']=='class_method' and method['parent']!=m['original']):
                       continue
+                    # print(f'{comparator}.{method["name"]}')
+                    # print(name)
                     if(name==f'{comparator}.{method["name"]}'):
                       # Cek apakah dia manggil fungsi yang diimport di acl
 #print(501)
+                      # print("masuk pak eko")
                       return True
             elif(acl_class.type=='function'):
                 #print("parent",parent_class)
diff --git a/tests/tc1/class_views.py b/tests/tc1/class_views.py
index 6c600ed..4c97704 100644
--- a/tests/tc1/class_views.py
+++ b/tests/tc1/class_views.py
@@ -10,8 +10,8 @@ views = Blueprint('views', __name__)
 
 class Views():
 # @NoCheck
-    def __init__(self) -> None:
-        pass
+    def __init__(self,a) -> None:
+        self.a = a
     @views.route('/add', methods=['POST'])
     @login_required
     def class_add_note(self):
@@ -64,7 +64,7 @@ class Views():
         notes = Note.query.filter(Note.user_id==current_user.id)
         return jsonify(notes)
     @views.route('/logs', methods=['GET'])
-    @login_required
+    # @login_required
     def class_get_logs(self):
         if (RoleCheck().is_admin(current_user)):
             abort(403)
diff --git a/tests/tc1/views.py b/tests/tc1/views.py
index 7d69919..ac11e48 100644
--- a/tests/tc1/views.py
+++ b/tests/tc1/views.py
@@ -66,9 +66,10 @@ def get_note():
     return jsonify(notes)
 
 @views.route('/logs', methods=['GET'])
-@login_required
+# @login_required
 def get_logs():
-    if (RoleCheck().is_admin(current_user)):
+    rolecheck = RoleCheck(1)
+    if (rolecheck.is_admin(current_user)):
         abort(403)
     logs = Log.query.get()
 
diff --git a/todo.txt b/todo.txt
index 5a38c2d..254d508 100644
--- a/todo.txt
+++ b/todo.txt
@@ -1,4 +1,4 @@
-- Per NoCheck an belum dites
+- Per NoCheck an sudah dites
 function (V)
 class:
  - function (V)
@@ -7,6 +7,7 @@ module:
  - class (V)
  - class > function (V)
 
-
- routes kena nocheck?
- default gak cek __init__
\ No newline at end of file
+TODO
+- check kalau kelasnya disimpan di variabel (V) -> cuma kalau langsung manggil kelas
+- routes kena nocheck?
+- default gak cek __init__
\ No newline at end of file
-- 
GitLab