diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..f6506d5 --- /dev/null +++ b/Makefile @@ -0,0 +1,24 @@ +NAME = codebro +IMGNAME = $(NAME) +ACTIONS = help clean mrproper build run shell + +help: + echo "Usage: make $(ACTIONS)" + +clean: + docker ps -q -a -f name=$(NAME) | xargs docker rm -f + +mrproper: + docker images -q -a $(IMGNAME) | xargs docker rmi -f + +build: + docker build -t $(IMGNAME) . + +run: + docker run -d --name=$(NAME) -p 127.0.0.1:8000:8000 $(IMGNAME) + +shell: + docker exec -i -t $(NAME) /bin/bash + +.PHONY: $(ACTIONS) +.SILENT: help diff --git a/codebro/analyzer/analysis.py b/codebro/analyzer/analysis.py index fd02002..8a721e4 100644 --- a/codebro/analyzer/analysis.py +++ b/codebro/analyzer/analysis.py @@ -22,10 +22,10 @@ def clang_parse_file(request, project, file): def add_function_declaration(project, file, data): funcname, filename, line, rtype, args = data - function,created = Function.objects.get_or_create(name = funcname, - file = file, - project = project) - function.line = line + function, created = Function.objects.get_or_create(name=funcname, + file=file, + project=project) + function.line = line function.rtype = rtype function.save() @@ -35,7 +35,7 @@ def add_function_declaration(project, file, data): arg.function = function arg.save() - if settings.DEBUG : + if settings.DEBUG: print "%s %s is declared in %s:%d" % (function.name, function.args, function.file.relative_name, @@ -46,13 +46,13 @@ def add_function_declaration(project, file, data): def add_function_call(project, file, data): (caller, infos) = data - caller, created = Function.objects.get_or_create(name = caller, - file = file, - project = project) + caller, created = Function.objects.get_or_create(name=caller, + file=file, + project=project) - callee, created = Function.objects.get_or_create(name = infos["name"], - file = file, - project = project) + callee, created = Function.objects.get_or_create(name=infos["name"], + file=file, + project=project) xref = Xref() xref.project = project @@ -62,54 +62,55 @@ def add_function_call(project, file, data): xref.save() - if settings.DEBUG : + if settings.DEBUG: print "%s calls %s at %s:%d" % (xref.calling_function.name, xref.called_function.name, xref.calling_function.file.relative_name, xref.called_function_line) - + return @transaction.commit_manually def generate_file_xref(request, project, file, cparser=None): local_instance = False - + if file.is_parsed: return False - if cparser is None : + if cparser is None: cparser = ClangParser(project) local_instance = True - - try: + + try: for out in cparser.get_xref_calls(file.name): if len(out) == 5: # FUNC_DECL add_function_declaration(project, file, out) - + elif len(out) == 2: # CALL_EXPR add_function_call(project, file, out) file.is_parsed = True file.save() - - except Exception, e: + + except Exception as e: if settings.DEBUG: print "An exception occured", e transaction.rollback() return False - + else: transaction.commit() if local_instance: save_diagnostics(cparser, project) - + return True - + + @transaction.commit_manually def save_diagnostics(cparser, project): - try: + try: for cat, fil, lin, error_msg in cparser.diags: dbg = Debug() dbg.category = cat @@ -118,11 +119,11 @@ def save_diagnostics(cparser, project): dbg.message = error_msg dbg.project = project dbg.save() - except: + except: transaction.rollback() else: transaction.commit() - + return @@ -131,20 +132,19 @@ def generate_project_xref(request, project): generate call xrefs in the project (i.e. on all files), and store them in database """ cparser = ClangParser(project) - + for file in project.file_set.all(): generate_file_xref(request, project, file, cparser) - - save_diagnostics(cparser, project) + + save_diagnostics(cparser, project) project.is_parsed = True project.save() - - if project.xref_set.count() : + + if project.xref_set.count(): messages.success(request, "Successfully xref-ed") return True - - else : + + else: messages.error(request, "No xref have been established") return False - diff --git a/codebro/analyzer/clangparse.py b/codebro/analyzer/clangparse.py index e86420a..349810b 100644 --- a/codebro/analyzer/clangparse.py +++ b/codebro/analyzer/clangparse.py @@ -1,5 +1,5 @@ import unipath -import clang +import clang from os import access, R_OK, walk, path @@ -10,138 +10,137 @@ from modules.format_string import FormatStringModule - class ClangParser: """ - + """ def __init__(self, project, clang_args=[]): """ - + """ - clang.cindex.Config.set_library_file("/usr/lib/llvm-3.4/lib/libclang-3.4.so.1") + clang.cindex.Config.set_library_file( + "/usr/lib/llvm-3.4/lib/libclang-3.4.so.1") self.project = project self.root_dir = unipath.Path(self.project.code_path) self.index = Index.create() self.parser = None - + self.clang_args = settings.CLANG_PARSE_OPTIONS - self.clang_args+= self.include_sub_dirs() - self.clang_args+= clang_args - + self.clang_args += self.include_sub_dirs() + self.clang_args += clang_args + self.diags = [] self.modules = {} - self.register_modules( [FormatStringModule,] ) - + self.register_modules([FormatStringModule, ]) def register_modules(self, modules): """ - + """ - for module in modules : + for module in modules: m = module(self) # check for existing module id already_exists = False - for mod in self.modules.values() : - if m.uid == mod.uid : - print("Module Id %d already declared for module '%s', cannot add" % (mod.uid, mod.name)) + for mod in self.modules.values(): + if m.uid == mod.uid: + print( + "Module Id %d already declared for module '%s', cannot add" % + (mod.uid, mod.name)) already_exists = True break if not already_exists: m.register() - - if settings.DEBUG : - print("Using module '%s' on project '%s'" % (m.name, m.module.project.name)) - + if settings.DEBUG: + print( + "Using module '%s' on project '%s'" % + (m.name, m.module.project.name)) + def include_sub_dirs(self): """ append subdirs to clang include options """ - subdirs = [ "-I"+ p.absolute() for p in self.root_dir.walk(filter=unipath.DIRS_NO_LINKS)] + subdirs = ["-I" + p.absolute() + for p in self.root_dir.walk(filter=unipath.DIRS_NO_LINKS)] return subdirs - def enumerate_files(root_dir, extensions): """ - + """ - for root, dirs, files in walk(root_dir, topdown=True, followlinks=False): - for f in files : + for root, dirs, files in walk( + root_dir, topdown=True, followlinks=False): + for f in files: fpath = path.join(root, f) if not access(fpath, R_OK): continue - - for ext in extensions : + + for ext in extensions: if fpath.endswith(ext): yield(fpath) - def inspect(self, node, caller): """ - + """ - if node.kind == CursorKind.FUNCTION_DECL : + if node.kind == CursorKind.FUNCTION_DECL: caller = node.spelling - - if node.location.file and not node.location.file.name.endswith(".h") : + + if node.location.file and not node.location.file.name.endswith( + ".h"): return_type = node.type.get_result() args = [] - + for c in node.get_children(): if c.kind == CursorKind.PARM_DECL: - args.append( (c.type.kind.spelling, c.displayname) ) - + args.append((c.type.kind.spelling, c.displayname)) + func = [node.spelling, node.location.file.name, node.location.line, return_type.kind.spelling, - args ] - + args] + yield(func) - elif node.kind == CursorKind.CALL_EXPR: infos = {} infos['name'] = node.displayname infos['line'] = node.location.line - for module in self.modules[CursorKind.CALL_EXPR] : + for module in self.modules[CursorKind.CALL_EXPR]: module.run(node) - - yield( (caller, infos) ) - + yield((caller, infos)) + for n in node.get_children(): - for i in self.inspect(n, caller) : + for i in self.inspect(n, caller): yield i - def get_xref_calls(self, filename): """ - + """ - + self.parser = self.index.parse(filename, args=self.clang_args) if self.parser: - if len(self.parser.diagnostics) : + if len(self.parser.diagnostics): for d in self.parser.diagnostics: cat, loc, msg = d.category_number, d.location, d.spelling - if loc is None : + if loc is None: file, line = "Unknown", 0 - elif loc.file is None : + elif loc.file is None: file, line = "Unknown", loc.line - else : + else: file, line = loc.file.name, loc.line - + self.diags.append((cat, file, line, msg)) - + for node in self.inspect(self.parser.cursor, ""): yield node - diff --git a/codebro/analyzer/models.py b/codebro/analyzer/models.py index b9f1354..b2ce224 100644 --- a/codebro/analyzer/models.py +++ b/codebro/analyzer/models.py @@ -13,35 +13,34 @@ class File(models.Model): """ """ - name = models.CharField(max_length = 1024, - validators = [validate_PathNotEmpty]) - project = models.ForeignKey(Project) - is_parsed = models.BooleanField(default = False) - - + name = models.CharField(max_length=1024, + validators=[validate_PathNotEmpty]) + project = models.ForeignKey(Project) + is_parsed = models.BooleanField(default=False) + def __unicode__(self): return self.name @property def relative_name(self): - return self.name.replace(SRC_PATH+"/", "") + return self.name.replace(SRC_PATH + "/", "") @property def link(self): link = reverse('browser.views.project_detail', args=(self.project.id,)) - link+= "?file={0}".format(self.name) + link += "?file={0}".format(self.name) return link - + def grep(self, pattern): """ shitty under-optimized grep function : search a pattern inside a file """ blocks = [] - with open(self.name, 'r') as fd : - + with open(self.name, 'r') as fd: + line_num = 1 - for line in fd.xreadlines(): + for line in fd: match = pattern.search(line) if match is not None: blocks.append([line_num, match.string]) @@ -49,7 +48,6 @@ def grep(self, pattern): return blocks - @staticmethod def search(pattern, files, project=None): """ @@ -58,23 +56,23 @@ def search(pattern, files, project=None): blocks = {} compiled_pattern = re.compile(pattern, re.IGNORECASE) - - for file in files : + + for file in files: ret = file.grep(compiled_pattern) if len(ret) > 0: if file.project not in blocks: blocks[file.project] = {} - + if file not in blocks[file.project]: blocks[file.project][file] = {} - blocks[file.project][file].update(ret) + blocks[file.project][file].update(ret) total = 0 - + for project in blocks.keys(): for e in blocks[project].keys(): total += len(blocks[project][e]) - + ctx = {} ctx["pattern"] = pattern ctx["num_matches"] = total @@ -82,7 +80,7 @@ def search(pattern, files, project=None): if project is not None: ctx['project'] = project - + return ctx @@ -90,13 +88,13 @@ class Function(models.Model): """ """ - name = models.CharField(max_length=1024, - validators=[validate_PathNotEmpty]) - project = models.ForeignKey(Project) - file = models.ForeignKey(File, null=True) - line = models.PositiveIntegerField(null=True, default=0) - rtype = models.CharField(max_length=16, null=True) - + name = models.CharField(max_length=1024, + validators=[validate_PathNotEmpty]) + project = models.ForeignKey(Project) + file = models.ForeignKey(File, null=True) + line = models.PositiveIntegerField(null=True, default=0) + rtype = models.CharField(max_length=16, null=True) + def __unicode__(self): return "%s:%d - <%s> %s (%s)" % (self.file.relative_name, self.line if self.line is not None else 0, @@ -112,67 +110,66 @@ def args(self): @property def link(self): link = reverse('browser.views.project_detail', args=(self.project.id,)) - link+= "?file={0}&hl={1}#line-{1}".format(self.file.name, self.line) + link += "?file={0}&hl={1}#line-{1}".format(self.file.name, self.line) return link - + class Argument(models.Model): """ """ - name = models.CharField(max_length=32) - type = models.CharField(max_length=16) - function = models.ForeignKey(Function) - + name = models.CharField(max_length=32) + type = models.CharField(max_length=16) + function = models.ForeignKey(Function) + def __unicode__(self): return "<%s> %s" % (self.name, self.type) - + class Xref(models.Model): """ """ - project = models.ForeignKey(Project) - calling_function = models.ForeignKey(Function, related_name='caller') - called_function = models.ForeignKey(Function, related_name='callee') - called_function_line = models.PositiveIntegerField() - + project = models.ForeignKey(Project) + calling_function = models.ForeignKey(Function, related_name='caller') + called_function = models.ForeignKey(Function, related_name='callee') + called_function_line = models.PositiveIntegerField() + def __unicode__(self): return "%s -> %s (l.%d)" % (self.calling_function.name, self.called_function.name, self.called_function_line if self.called_function_line is not None else 0) - class Diagnostic(models.Model): """ """ - filepath = models.CharField(max_length=1024) - line = models.PositiveIntegerField() - message = models.TextField() + filepath = models.CharField(max_length=1024) + line = models.PositiveIntegerField() + message = models.TextField() class Meta: abstract = True - + class Debug(Diagnostic): """ """ DIAG_LEVELS = ( - (clang.cindex.Diagnostic.Ignored, "IGNORED"), - (clang.cindex.Diagnostic.Note, "NOTE"), - (clang.cindex.Diagnostic.Warning, "WARNING"), - (clang.cindex.Diagnostic.Error, "ERROR"), - (clang.cindex.Diagnostic.Fatal, "FATAL"), - ) - - project = models.ForeignKey(Project) - category = models.PositiveIntegerField(choices=DIAG_LEVELS, - default=clang.cindex.Diagnostic.Note) - - + (clang.cindex.Diagnostic.Ignored, "IGNORED"), + (clang.cindex.Diagnostic.Note, "NOTE"), + (clang.cindex.Diagnostic.Warning, "WARNING"), + (clang.cindex.Diagnostic.Error, "ERROR"), + (clang.cindex.Diagnostic.Fatal, "FATAL"), + ) + + project = models.ForeignKey(Project) + category = models.PositiveIntegerField( + choices=DIAG_LEVELS, + default=clang.cindex.Diagnostic.Note) + @staticmethod def level2str(level): d = dict(Debug.DIAG_LEVELS) @@ -181,29 +178,25 @@ def level2str(level): except KeyError: return "" - def __unicode__(self): return "[%d] %s:%s - %s" % (self.category, self.project.name, self.filepath, self.message) - class Module(models.Model): """ """ - name = models.CharField(max_length=64) - project = models.ForeignKey(Project) - + name = models.CharField(max_length=64) + project = models.ForeignKey(Project) + - class ModuleDiagnostic(Diagnostic): """ """ - module = models.ForeignKey(Module) - + module = models.ForeignKey(Module) + def __unicode__(self): return "[%d] %s:%s - %s" % (self.name, self.module.project.name, self.filepath, self.message) - diff --git a/codebro/analyzer/renderer.py b/codebro/analyzer/renderer.py index 3b8a164..7e76ada 100644 --- a/codebro/analyzer/renderer.py +++ b/codebro/analyzer/renderer.py @@ -11,12 +11,12 @@ class CodeBroHtmlFormatter(HtmlFormatter): """ - + """ - + def __init__(self, **options): """ - + """ HtmlFormatter.__init__(self, **options) self.project = options['codebro_project'] @@ -24,12 +24,11 @@ def __init__(self, **options): self.file = None self.functions = [] self.func_idx = 0 - - + def _wrap_lineanchors(self, inner): """ - - """ + + """ s = self.lineanchors i = self.linenostart - 1 for t, line in inner: @@ -37,98 +36,98 @@ def _wrap_lineanchors(self, inner): i += 1 # shifting down 75px coz of header yield 1, '' % (s, i) + line - + else: yield 0, line - def set_filename(self, name): """ - + """ - try : + try: self.file = File.objects.get(name=name, project=self.project) self.is_xrefed = self.file.is_parsed - + except File.DoesNotExist: self.functions = [] self.func_idx = 0 self.file = None return - + self.functions = [] self.func_idx = 0 - + for f in self.file.function_set.iterator(): self.functions.append(f) - def is_called_function(self, funcname): """ - + """ for f in self.functions: - if f.name == funcname : + if f.name == funcname: return True - - return False + return False def get_called_function(self, funcname): """ - + """ for f in self.functions: - if f.name == funcname : + if f.name == funcname: return f - + return None - def insert_function_ref(self, funcname, cls, style, depth=1): """ - + """ self.func_idx += 1 fmt_str = "?function={0}&file={1}&depth={2}" - args = [ escape(x) for x in [funcname, self.file.name, depth] ] - url_to_graph = reverse("browser.views.project_draw", args=(self.project.id,)) - url_to_graph+= fmt_str.format( *args ) + args = [escape(x) for x in [funcname, self.file.name, depth]] + url_to_graph = reverse( + "browser.views.project_draw", args=( + self.project.id,)) + url_to_graph += fmt_str.format(*args) url_to_definition = "" f = self.get_called_function(funcname) if f is not None and (f.file is not None and f.line != 0): fmt_str = "?file={0}&hl={1}" - args = [ escape(x) for x in [f.file.name, f.line] ] - url_to_definition = reverse("browser.views.project_detail", args=(self.project.id,)) - url_to_definition+= fmt_str.format( *args ) - + args = [escape(x) for x in [f.file.name, f.line]] + url_to_definition = reverse( + "browser.views.project_detail", args=( + self.project.id,)) + url_to_definition += fmt_str.format(*args) + link = '' % c2s[cclass][0] or '' - + else: cls = self._get_css_class(ttype) - - if cls=='nf' and self.is_xrefed and self.file is not None : + + if cls == 'nf' and self.is_xrefed and self.file is not None: link = self.insert_calling_function_ref(value, cls) cspan = cls and link or '' - - elif cls=='n' and self.is_xrefed and self.is_called_function(value): + + elif cls == 'n' and self.is_xrefed and self.is_called_function(value): link = self.insert_called_function_ref(value, cls) cspan = cls and link or '' - + else: cspan = cls and '' % cls or '' - + parts = value.translate(escape_table) parts = parts.split('\n') - - for part in parts[:-1]: + for part in parts[:-1]: if line: if lspan != cspan: line += (lspan and '') + cspan + part + \ (cspan and '') + lsep - + else: line += part + (lspan and '') + lsep yield 1, line line = '' - + elif part: - + yield 1, cspan + part + (cspan and '') + lsep - + else: yield 1, lsep @@ -198,19 +196,18 @@ def _format_lines(self, tokensource): class CodeBroRenderer: """ - + """ - - def __init__(self, codebro_project, highlight_lines = []): + + def __init__(self, codebro_project, highlight_lines=[]): """ - + """ self.lexer = get_lexer_by_name("c", stripall=True) self.formatter = None self.project = codebro_project self.hl = highlight_lines - def render(self, filename): self.formatter = CodeBroHtmlFormatter(linenos="inline", cssclass="codebro", @@ -219,8 +216,8 @@ def render(self, filename): codebro_project=self.project, hl_lines=self.hl) self.formatter.set_filename(filename) - - with open(filename, 'r') as f : + + with open(filename, 'r') as f: data = highlight(f.read(), self.lexer, self.formatter) return data.split('\n') diff --git a/codebro/autopep8.sh b/codebro/autopep8.sh new file mode 100755 index 0000000..68f1c12 --- /dev/null +++ b/codebro/autopep8.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash +set -e +path="." +test -z "$1" || path="$1" +for file in $(find "${path}" -name "*.py"); do + echo "(pep8) >> $file" + autopep8 --in-place --aggressive --aggressive "${file}" +done +exit $? diff --git a/codebro/browser/ajax.py b/codebro/browser/ajax.py index bc69d6a..1bb1de2 100644 --- a/codebro/browser/ajax.py +++ b/codebro/browser/ajax.py @@ -17,30 +17,29 @@ from browser.helpers import generate_graph - @dajaxice_register def update_files(request, value, project_id): """ - + """ dajax = Dajax() - - try : + + try: p = Project.objects.get(pk=project_id) except Project.DoesNotExist: return dajax.json() - + out = [] - + for f in Function.objects.filter(name__contains=value, project=p): fmt_str = "" line = f.line if f.line is not None else 0 filename = f.file.name if f.file is not None else "Unknown" funcname = f.name - args = [serializers.serialize("xml", [f,]), ] - args+= [ escape(x) for x in [funcname, filename, line] ] - line = fmt_str.format( *args ) + args = [serializers.serialize("xml", [f, ]), ] + args += [escape(x) for x in [funcname, filename, line]] + line = fmt_str.format(*args) out.append(line) dajax.assign('#function_files', 'innerHTML', '\n'.join(out)) @@ -50,10 +49,10 @@ def update_files(request, value, project_id): @dajaxice_register def ajax_project_parse(request, project_id): """ - + """ project = get_object_or_404(Project, pk=project_id) - ctx = {"status" : -1, "message": ""} + ctx = {"status": -1, "message": ""} if project.is_parsed: ctx["status"] = 1 @@ -61,64 +60,68 @@ def ajax_project_parse(request, project_id): return json.dumps(ctx) if clang_parse_project(request, project): - ctx["status"] = 0 + ctx["status"] = 0 ctx["message"] = "Successfully parsed ... Reloading page" - else : - ctx["status"] = 1 + else: + ctx["status"] = 1 ctx["message"] = "Failed to parse..." - + return json.dumps(ctx) @dajaxice_register def ajax_file_parse(request, project_id, filename): """ - + """ project = get_object_or_404(Project, pk=project_id) - ctx = {"status" : -1, "message": ""} + ctx = {"status": -1, "message": ""} - filerefs = project.file_set.filter( name=filename ) + filerefs = project.file_set.filter(name=filename) if len(filerefs) == 0: ctx["status"] = 1 ctx["message"] = "Invalid filename %s" % filename return json.dumps(ctx) fileref = filerefs[0] - + if project.is_parsed or fileref.is_parsed: ctx["status"] = 1 ctx["message"] = "Already parsed" return json.dumps(ctx) - + if clang_parse_file(request, project, fileref): - ctx["status"] = 0 + ctx["status"] = 0 ctx["message"] = "Successfully parsed ... Reloading page" - else : - ctx["status"] = 1 + else: + ctx["status"] = 1 ctx["message"] = "Failed to parse..." - + return json.dumps(ctx) @dajaxice_register def ajax_project_unparse(request, project_id): """ - + """ project = get_object_or_404(Project, pk=project_id) - ctx = {"status" : -1, "message": ""} - - for i in ModuleDiagnostic.objects.filter(module__project=project): i.delete() - for i in project.module_set.all(): i.delete() - for i in project.xref_set.all(): i.delete() - for i in project.debug_set.all(): i.delete() - project.file_set.all().update( is_parsed=False ) - + ctx = {"status": -1, "message": ""} + + for i in ModuleDiagnostic.objects.filter(module__project=project): + i.delete() + for i in project.module_set.all(): + i.delete() + for i in project.xref_set.all(): + i.delete() + for i in project.debug_set.all(): + i.delete() + project.file_set.all().update(is_parsed=False) + project.is_parsed = False project.save() - - ctx["status"] = 0 + + ctx["status"] = 0 ctx["message"] = "Successfully unparsed ... Reloading page" return json.dumps(ctx) @@ -126,59 +129,61 @@ def ajax_project_unparse(request, project_id): @dajaxice_register def ajax_add_funcgraph_link(request, f, d, x): """ - + """ - valid_method_or_404(request, ["POST",]) + valid_method_or_404(request, ["POST", ]) dajax = Dajax() - try : depth = int(d) - except ValueError : depth = -1 + try: + depth = int(d) + except ValueError: + depth = -1 xref = x if x is not None else True - - try : - for obj in serializers.deserialize("xml", f): + + try: + for obj in serializers.deserialize("xml", f): data = obj data.save() break - - caller_f = data.object - - if not isinstance(caller_f, Function): - return dajax.json() - - project = caller_f.project - - if not project.is_parsed: - return dajax.json() - + + caller_f = data.object + + if not isinstance(caller_f, Function): + return dajax.json() + + project = caller_f.project + + if not project.is_parsed: + return dajax.json() + except xml.sax.SAXParseException: return dajax.json() - project = caller_f.project if f: - base = settings.CACHED_SVG_FMT % (project.id, caller_f.id, caller_f.id, "up", depth) - else : - base = settings.CACHED_SVG_FMT % (project.id, caller_f.id, caller_f.id, "down", depth) + base = settings.CACHED_SVG_FMT % ( + project.id, caller_f.id, caller_f.id, "up", depth) + else: + base = settings.CACHED_SVG_FMT % ( + project.id, caller_f.id, caller_f.id, "down", depth) hashed_basename = hashlib.sha256(base).hexdigest() + ".svg" pic_name = unipath.Path(settings.CACHE_PATH + "/" + hashed_basename) if not pic_name.isfile(): ret, err = generate_graph(pic_name, project, caller_f, xref, depth) - if ret==False : + if not ret: return dajax.json() fmt_str = "" - fmt_str+= "{0}" - fmt_str+= "{1}" - fmt_str+= "{2}" - fmt_str+= "{0}" - fmt_str+= "" - - line = fmt_str.format(caller_f.name, - xref, - depth, - reverse('browser.views.get_cache',args=(hashed_basename,))) + fmt_str += "{0}" + fmt_str += "{1}" + fmt_str += "{2}" + fmt_str += "{0}" + fmt_str += "" + + line = fmt_str.format( + caller_f.name, xref, depth, reverse( + 'browser.views.get_cache', args=( + hashed_basename,))) dajax.assign('#table-graphs', 'innerHTML', line) return dajax.json() - diff --git a/codebro/browser/forms.py b/codebro/browser/forms.py index 577e2df..fb100d6 100644 --- a/codebro/browser/forms.py +++ b/codebro/browser/forms.py @@ -5,19 +5,22 @@ from browser.models import Language from browser.models import Project + class LanguageForm(ModelForm): + class Meta: model = Language - + + class ProjectForm(ModelForm): - + class Meta: model = Project exclude = ("source_path", "is_parsed") - widgets = { "description": Textarea(attrs={"rows": 3}), } - + + class NewProjectForm(ProjectForm): file = FileField(required=False) diff --git a/codebro/browser/helpers.py b/codebro/browser/helpers.py index 279bc4a..19d0b1c 100644 --- a/codebro/browser/helpers.py +++ b/codebro/browser/helpers.py @@ -1,6 +1,6 @@ -import tempfile +import tempfile import zipfile -import tarfile +import tarfile import unipath import os import pydot @@ -15,36 +15,52 @@ class Archive: ZIP_FILE = 1 TGZ_FILE = 2 TBZ_FILE = 3 - + handlers = { - ZIP_FILE : [zipfile.ZipFile, zipfile.ZipFile.close, zipfile.ZipFile.extractall, 'r', zipfile.is_zipfile], - TGZ_FILE : [tarfile.TarFile.open, tarfile.TarFile.close, tarfile.TarFile.extractall, 'r:gz', tarfile.is_tarfile], - TBZ_FILE : [tarfile.TarFile.open, tarfile.TarFile.close, tarfile.TarFile.extractall, 'r:bz2', tarfile.is_tarfile], - } + ZIP_FILE: [ + zipfile.ZipFile, + zipfile.ZipFile.close, + zipfile.ZipFile.extractall, + 'r', + zipfile.is_zipfile], + TGZ_FILE: [ + tarfile.TarFile.open, + tarfile.TarFile.close, + tarfile.TarFile.extractall, + 'r:gz', + tarfile.is_tarfile], + TBZ_FILE: [ + tarfile.TarFile.open, + tarfile.TarFile.close, + tarfile.TarFile.extractall, + 'r:bz2', + tarfile.is_tarfile], + } extensions = { - ZIP_FILE : [".zip",], - TGZ_FILE : [".tar.gz", ".tgz"], - TBZ_FILE : [".tar.bz2", ".tbz2"], - } - - + ZIP_FILE: [".zip", ], + TGZ_FILE: [".tar.gz", ".tgz"], + TBZ_FILE: [".tar.bz2", ".tbz2"], + } + def __init__(self, fname, t): self.name = fname self.type = t self.handler = Archive.handlers[self.type] - + def extract(self, path): handle_open, handle_close, handle_extract, handle_mode, handle_check = self.handler if handle_check(self.name) == False: - raise Exception("Invalid file type %#x '%s'" % (self.type, self.name)) - + raise Exception( + "Invalid file type %#x '%s'" % + (self.type, self.name)) + p = handle_open(self.name, handle_mode) handle_extract(p, path) handle_close(p) return True - + def is_int(s): try: int(s) @@ -52,33 +68,36 @@ def is_int(s): except ValueError: return False - + def get_file_extension(name): - for ext in Archive.extensions : + for ext in Archive.extensions: for suffix in Archive.extensions[ext]: - if name.endswith(suffix) : + if name.endswith(suffix): return ext return None def is_valid_file(f): - return (f.size < settings.MAX_UPLOAD_SIZE) and (get_file_extension(f.name) is not None) + return ( + f.size < settings.MAX_UPLOAD_SIZE) and ( + get_file_extension( + f.name) is not None) def extract_archive(archive_name, project_name, extension): """ extract archive to specified directory """ - path = unipath.Path( '/'.join([settings.SRC_PATH, project_name]) ).absolute() - + path = unipath.Path('/'.join([settings.SRC_PATH, project_name])).absolute() + try: - path.mkdir(mode=0755) - + path.mkdir(mode=0o755) + except OSError: path.rmtree() return None - + archive = Archive(archive_name, extension) archive.extract(path) unipath.Path(archive_name).remove() @@ -96,98 +115,125 @@ def handle_uploaded_file(file_o, project_name, extension): def valid_method_or_404(request, methods): - if not request.method in methods: + if request.method not in methods: raise Http404 - + def generate_graph(outfile, project, caller, xref_to, depth): """ - + """ title = "Callgraph" - title+= "To" if xref_to==True else "From" - title+= ": %s:%s" % (project.name, caller.name) + title += "To" if xref_to else "From" + title += ": %s:%s" % (project.name, caller.name) - if xref_to : + if xref_to: graph = pydot.Dot(graph_type="graph", graph_name=title, suppress_disconnected=False, simplify=False, rankdir='TB', splines='ortho') - else : + else: graph = pydot.Dot(graph_type="graph", graph_name=title, suppress_disconnected=False, simplify=False, rankdir='BT', splines='ortho', ratio="fill") - + link_node(graph, project, caller, xref_to, depth) - - try : + + try: graph.write_svg(outfile) - - except pydot.InvocationException, ie: + + except pydot.InvocationException as ie: return (False, ie) return (True, None) - - + + def link_node(graph, project, caller_f, xref_from, depth): """ - + """ - if depth is not None : + if depth is not None: if depth == 0: return else: depth -= 1 - - caller_n = pydot.Node(caller_f.name, height=0.80, width=0.80, shape="ellipse") + + caller_n = pydot.Node( + caller_f.name, + height=0.80, + width=0.80, + shape="ellipse") graph.add_node(caller_n) if xref_from: - xrefs = project.xref_set.filter(project=project, calling_function=caller_f) + xrefs = project.xref_set.filter( + project=project, calling_function=caller_f) else: - xrefs = project.xref_set.filter(project=project, called_function=caller_f) - - for xref in xrefs : + xrefs = project.xref_set.filter( + project=project, called_function=caller_f) + + for xref in xrefs: if xref_from: called_function = xref.called_function - callee_n = pydot.Node(called_function.name, height=0.80, width=0.80, shape="ellipse") - sub_xrefs = project.xref_set.filter(project=project, calling_function=called_function) + callee_n = pydot.Node( + called_function.name, + height=0.80, + width=0.80, + shape="ellipse") + sub_xrefs = project.xref_set.filter( + project=project, calling_function=called_function) else: called_function = xref.calling_function - callee_n = pydot.Node(called_function.name, height=0.80, width=0.80, shape="ellipse") - sub_xrefs = project.xref_set.filter(project=project, called_function=called_function) + callee_n = pydot.Node( + called_function.name, + height=0.80, + width=0.80, + shape="ellipse") + sub_xrefs = project.xref_set.filter( + project=project, called_function=called_function) if sub_xrefs.count(): - url_to_decl = reverse('browser.views.project_detail', args=(project.id, )) + url_to_decl = reverse( + 'browser.views.project_detail', args=( + project.id, )) args = "?file=%s&hl=%d" - args%= (called_function.file.name, called_function.line) + args %= (called_function.file.name, called_function.line) callee_n.set_URL(url_to_decl) if xref_from: - link_node(graph, project, xref.called_function, xref_from, depth) + link_node( + graph, + project, + xref.called_function, + xref_from, + depth) else: - link_node(graph, project, xref.calling_function, xref_from, depth) - + link_node( + graph, + project, + xref.calling_function, + xref_from, + depth) + graph.add_node(callee_n) - if xref_from : + if xref_from: edge = pydot.Edge(caller_n, callee_n) else: edge = pydot.Edge(callee_n, caller_n) # edge label lbl = xref.calling_function.file.relative_name - lbl+= '+' - lbl+= str(xref.called_function_line) + lbl += '+' + lbl += str(xref.called_function_line) edge.set_labelfontsize(4) edge.set_label(lbl) edge.set_labelfontname("Courier") - + # edge url url = reverse('browser.views.project_detail', args=(project.id, )) args = "?file=%s&hl=%s" - args%= (xref.calling_function.file.name, xref.called_function_line) + args %= (xref.calling_function.file.name, xref.called_function_line) edge.set_URL(url + args) - + edge.set_rank("same") graph.add_edge(edge) - diff --git a/codebro/browser/models.py b/codebro/browser/models.py index 6c3c749..f0175d0 100644 --- a/codebro/browser/models.py +++ b/codebro/browser/models.py @@ -9,38 +9,39 @@ class TimeStampedModel(models.Model): """ - + """ - created = models.DateTimeField(auto_now_add = True) - modified = models.DateTimeField(auto_now = True) - + created = models.DateTimeField(auto_now_add=True) + modified = models.DateTimeField(auto_now=True) + class Meta: abstract = True - + class Language(models.Model): """ """ - name = models.CharField(max_length=64) - extension = models.CharField(max_length=10) - + name = models.CharField(max_length=64) + extension = models.CharField(max_length=10) + def __unicode__(self): return "%s (%s)" % (self.name, self.extension) - + class Project(TimeStampedModel): """ """ - name = models.CharField(max_length = 64, - unique = True, - validators = [validate_IsValidName]) - description = models.TextField(max_length = 256) - language = models.ForeignKey(Language) - source_path = models.TextField(max_length = 256) - is_parsed = models.BooleanField(default = False) - + name = models.CharField( + max_length=64, + unique=True, + validators=[validate_IsValidName]) + description = models.TextField(max_length=256) + language = models.ForeignKey(Language) + source_path = models.TextField(max_length=256) + is_parsed = models.BooleanField(default=False) + @property def code_path(self): return settings.SRC_PATH + "/" + self.source_path @@ -48,7 +49,6 @@ def code_path(self): def __unicode__(self): return "%s (%s) : %s" % (self.name, self.language.name, self.code_path) - @staticmethod def create(values): project = Project() @@ -57,24 +57,24 @@ def create(values): for item in values.keys(): setattr(project, item, values[item]) - + project.full_clean() project.save() project.insert_files() - + return project - def enumerate_files(self, extensions=[]): """ enumerates all files in project path that match specified extensions if extensions is empty, then it matches all extensions """ - for path in unipath.Path(self.code_path).absolute().walk(filter=unipath.FILES_NO_LINKS): - if (len(extensions)==0) or (path.ext in extensions): + for path in unipath.Path( + self.code_path).absolute().walk( + filter=unipath.FILES_NO_LINKS): + if (len(extensions) == 0) or (path.ext in extensions): yield path - @transaction.commit_manually def insert_files(self): @@ -83,72 +83,68 @@ def insert_files(self): """ from analyzer.models import File - try : - for filename in self.enumerate_files( [self.language.extension, ".h"] ): - ref, created = File.objects.get_or_create(name=filename, project=self) + try: + for filename in self.enumerate_files( + [self.language.extension, ".h"]): + ref, created = File.objects.get_or_create( + name=filename, project=self) if created: ref.save() - - except Exception, e: + + except Exception as e: if settings.DEBUG: print "An exception occured", e transaction.rollback() - + else: transaction.commit() - + return - - + def list_directory(self, dir): subdirs = [] - files= [] + files = [] def arrange(e): - relname = e.absolute().replace(settings.SRC_PATH+"/", "") - + relname = e.absolute().replace(settings.SRC_PATH + "/", "") + if e.isfile(): files.append((relname, e.absolute())) elif e.isdir: subdirs.append(("[d] " + relname + "/", e.absolute())) map(arrange, dir.listdir()) - + subdirs.sort() files.sort() - res = [("..", dir.parent.absolute()),] - res+= subdirs - res+= files + res = [("..", dir.parent.absolute()), ] + res += subdirs + res += files return res - def browse_file(self, filepath, highlight_lines=[]): from analyzer.renderer import CodeBroRenderer - + renderer = CodeBroRenderer(self, highlight_lines) return renderer.render(filepath) - - + @transaction.commit_manually def remove_file_instances(self): try: - for file_instance in self.file_set.all() : + for file_instance in self.file_set.all(): file_instance.delete() - - except Exception, e: + + except Exception as e: if settings.DEBUG: print "An exception occured", e transaction.rollback() - + else: transaction.commit() return - - + def remove_files(self): path = unipath.Path(self.code_path) path.rmtree(parents=False) return - - diff --git a/codebro/browser/tests.py b/codebro/browser/tests.py index 501deb7..75f2f59 100644 --- a/codebro/browser/tests.py +++ b/codebro/browser/tests.py @@ -9,6 +9,7 @@ class SimpleTest(TestCase): + def test_basic_addition(self): """ Tests that 1 + 1 always equals 2. diff --git a/codebro/browser/validators.py b/codebro/browser/validators.py index b458f14..5e7f40a 100644 --- a/codebro/browser/validators.py +++ b/codebro/browser/validators.py @@ -5,30 +5,29 @@ def validate_PathNotEmpty(value): """ - + """ path = unipath.Path(value).strip() if len(path) == 0: raise ValidationError(u'String must not be empty') - + def validate_PathInScope(value): """ - + """ path = unipath.Path(value) path.absolute() if not abspath.startswith(settings.SRC_PATH) or not path.isdir(abspath): raise ValidationError(u'Invalid path for source code') - + def validate_IsValidName(value): """ - + """ validate_PathNotEmpty(value) path = unipath.Path(value) - + if not path.isalnum(): raise ValidationError(u'Project name must be alnum') - diff --git a/codebro/browser/views.py b/codebro/browser/views.py index f997b9a..06a9151 100644 --- a/codebro/browser/views.py +++ b/codebro/browser/views.py @@ -1,5 +1,5 @@ import unipath -import hashlib +import hashlib from django.http import HttpResponse, Http404 from django.shortcuts import render @@ -16,12 +16,12 @@ from browser.helpers import handle_uploaded_file from browser.helpers import is_valid_file, is_int, get_file_extension from browser.helpers import generate_graph +from browser.helpers import Archive from browser.forms import NewProjectForm, ProjectForm from codebro import settings - def index(request): """ index page @@ -35,7 +35,7 @@ def project_search(request, project_id): perform search inside a specific project """ project = get_object_or_404(Project, pk=project_id) - filelist = File.objects.filter(project=project) + filelist = File.objects.filter(project=project) return generic_search(request, filelist) @@ -51,7 +51,7 @@ def generic_search(request, filelist=None): if filelist is None: filelist = File.objects.all() context = File.search(pattern, filelist) - + return render(request, "search.html", context) @@ -59,20 +59,20 @@ def project_list(request): """ enumerates all projects """ - + projects = Project.objects.all().order_by('-id') paginator = Paginator(projects, 10) page = request.GET.get('page') try: projects = paginator.page(page) - + except PageNotAnInteger: projects = paginator.page(1) - + except EmptyPage: projects = paginator.page(paginator.num_pages) - + ctx = {'projects': projects} return render(request, 'list.html', ctx) @@ -83,7 +83,7 @@ def project_detail(request, project_id): """ content = [] ctx = {} - + project = get_object_or_404(Project, pk=project_id) path = unipath.Path(request.GET.get('file', project.code_path)).absolute() @@ -92,32 +92,32 @@ def project_detail(request, project_id): elif path.islink(): messages.error(request, "Cannot browse symlink") - + elif path.isdir(): - content = project.list_directory(path) + content = project.list_directory(path) - elif path.isfile() : - hl = [ int(i) for i in request.GET.get('hl', '').split(",") if is_int(i) ] - hl.sort() + elif path.isfile(): + hl = sorted([int(i) for i in request.GET.get( + 'hl', '').split(",") if is_int(i)]) content = project.browse_file(path, hl) if len(hl) > 0: - ctx['jump_to'] = hl[0] + ctx['jump_to'] = hl[0] ctx["is_parsed"] = False - refs = project.file_set.filter( name = path ) + refs = project.file_set.filter(name=path) if len(refs) > 0: ref = refs[0] - ctx["is_parsed"] = ref.is_parsed - - else : + ctx["is_parsed"] = ref.is_parsed + + else: messages.error(request, "Inexistant path") - - ctx['project'] = project - ctx['path'] = path - ctx['lines'] = content - ctx['is_dir'] = path.isdir() - ctx['parent_dir'] = path.parent - + + ctx['project'] = project + ctx['path'] = path + ctx['lines'] = content + ctx['is_dir'] = path.isdir() + ctx['parent_dir'] = path.parent + return render(request, 'project/detail.html', ctx) @@ -127,46 +127,60 @@ def project_new(request): """ valid_method_or_404(request, ['GET', 'POST']) - + if request.method == 'POST': form = NewProjectForm(request.POST, request.FILES) - + if form.is_valid(): if 'file' in request.FILES: - file = request.FILES['file'] - - if is_valid_file(file): - ext = get_file_extension(file.name) - if handle_uploaded_file(file, form.cleaned_data['name'], ext) is not None : - form.cleaned_data['source_path'] = form.cleaned_data['name'] + uploaded_file = request.FILES['file'] + + if is_valid_file(uploaded_file): + ext = get_file_extension(uploaded_file.name) + if handle_uploaded_file( + uploaded_file, + form.cleaned_data['name'], + ext) is not None: + form.cleaned_data[ + 'source_path'] = form.cleaned_data['name'] project = Project.create(form.cleaned_data) if project: messages.success(request, "Successfully added") - return redirect(reverse('browser.views.project_detail', args=(project.id, ))) + return redirect( + reverse( + 'browser.views.project_detail', + args=( + project.id, + ))) else: - form.errors['project']= ["Failed to create project"] - else : - form.errors['extension']= ["File extension is invalid"] - else : - form.errors['file']= ["Error while handling uploaded file"] - else : + form.errors['project'] = [ + "Failed to create project"] + else: + form.errors['extension'] = [ + "File extension is invalid. Allowed extensions: {0}".format(Archive.extensions.values())] + else: + form.errors['file'] = ["Error while handling uploaded file"] + else: form.errors['file'] = ["File is not valid"] - + msg = "Invalid form: " - msg+= ", ".join(["'%s': %s"%(k,v[0]) for k,v in form.errors.iteritems()]) + msg += ", ".join(["'%s': %s" % (k, v[0]) + for k, v in form.errors.iteritems()]) messages.error(request, msg) - - return render(request, 'project/new.html', {'form': form, 'project_id': -1}) - else : # request.method == 'GET' + return render(request, 'project/new.html', + {'form': form, 'project_id': -1}) + + else: # request.method == 'GET' form = NewProjectForm() - return render(request, 'project/new.html', {'form': form, 'project_id': -1}) + return render(request, 'project/new.html', + {'form': form, 'project_id': -1}) def project_edit(request, project_id): """ edit a project - """ + """ valid_method_or_404(request, ['GET', 'POST']) project = get_object_or_404(Project, pk=project_id) @@ -176,78 +190,122 @@ def project_edit(request, project_id): if not name.isalnum: messages.error(request, "name must be alnum") - + elif not description.isalnum: messages.error(request, "description must be alnum") - - else : + + else: project.name = escape(name) project.description = escape(description) project.save() - - return redirect(reverse('browser.views.project_detail', args=(project.id, ))) - - else: # request.method == 'GET' + + return redirect( + reverse( + 'browser.views.project_detail', + args=( + project.id, + ))) + + else: # request.method == 'GET' form = ProjectForm(instance=project) - return render(request, 'project/new.html', {'form': form, 'project_id': project.id}) + return render(request, 'project/new.html', + {'form': form, 'project_id': project.id}) def project_delete(request, project_id): """ delete a project """ - + project = get_object_or_404(Project, pk=project_id) if project.xref_set.count() > 0 or project.debug_set.count() > 0: - messages.error(request, "Project '%s' must be unparsed first" % project.name) - return redirect( reverse("browser.views.project_detail", args=(project.id,))) + messages.error( + request, + "Project '%s' must be unparsed first" % + project.name) + return redirect( + reverse( + "browser.views.project_detail", + args=( + project.id, + ))) project.remove_file_instances() project.remove_files() project.delete() - - messages.success(request, "Project '%s' successfully deleted" % project.name) - return redirect(reverse("browser.views.project_list")) + messages.success( + request, + "Project '%s' successfully deleted" % + project.name) + return redirect(reverse("browser.views.project_list")) def project_draw(request, project_id): """ - + """ - valid_method_or_404(request, ["GET",]) - + valid_method_or_404(request, ["GET", ]) + project = get_object_or_404(Project, pk=project_id) if "file" not in request.GET or "function" not in request.GET: messages.error(request, "Missing argument") - return redirect( reverse("browser.views.project_detail", args=(project.id,))) - - files = project.file_set.filter( name=request.GET["file"] ) - if len(files) < 1 : - messages.error(request, "Cannot find %s in project" % request.GET["file"]) - return redirect( reverse("browser.views.project_detail", args=(project.id,))) + return redirect( + reverse( + "browser.views.project_detail", + args=( + project.id, + ))) + + files = project.file_set.filter(name=request.GET["file"]) + if len(files) < 1: + messages.error( + request, + "Cannot find %s in project" % + request.GET["file"]) + return redirect( + reverse( + "browser.views.project_detail", + args=( + project.id, + ))) if not files[0].is_parsed and not project.is_parsed: messages.error(request, "Project must be xref-ed first") - return redirect( reverse("browser.views.project_detail", args=(project.id,))) - + return redirect( + reverse( + "browser.views.project_detail", + args=( + project.id, + ))) + callers = Function.objects.filter(project=project, name=request.GET["function"], file=files[0]) if callers.count() == 0: messages.error(request, "No function matching criterias") - return redirect( reverse("browser.views.project_detail", args=(project.id,))) - + return redirect( + reverse( + "browser.views.project_detail", + args=( + project.id, + ))) + elif callers.count() > 1: messages.error(request, "More than one function match criterias") - return redirect( reverse("browser.views.project_detail", args=(project.id,))) + return redirect( + reverse( + "browser.views.project_detail", + args=( + project.id, + ))) caller_f = callers[0] - try : + try: depth = int(request.GET.get('depth', -1)) except ValueError: depth = -1 @@ -255,86 +313,93 @@ def project_draw(request, project_id): # xref_to = request.GET.get("xref", True) # xref_to = False if request.GET.get('xref')=='1' else True - if request.GET.get('xref', '1')=='1': + if request.GET.get('xref', '1') == '1': xref_to = True - base = settings.CACHED_SVG_FMT % (project.id, caller_f.id, caller_f.id, "up", depth) + base = settings.CACHED_SVG_FMT % ( + project.id, caller_f.id, caller_f.id, "up", depth) else: xref_to = True - base = settings.CACHED_SVG_FMT % (project.id, caller_f.id, caller_f.id, "down", depth) - - + base = settings.CACHED_SVG_FMT % ( + project.id, caller_f.id, caller_f.id, "down", depth) + basename = hashlib.sha256(base).hexdigest() + ".svg" pic_name = unipath.Path(settings.CACHE_PATH + "/" + basename).absolute() if not pic_name.isfile(): # if no file in cache, create it ret, err = generate_graph(pic_name, project, caller_f, xref_to, depth) - if ret==False : + if not ret: messages.error(request, "Failed to create png graph: %s" % err) - return redirect( reverse("browser.views.project_detail", args=(project.id,))) + return redirect( + reverse( + "browser.views.project_detail", + args=( + project.id, + ))) return redirect(reverse("browser.views.get_cache", args=(basename,))) def project_functions(request, project_id): """ - + """ project = get_object_or_404(Project, pk=project_id) functions = Function.objects.filter(project=project, file__isnull=False, - line__gt = 0 ).order_by('file__name') + line__gt=0).order_by('file__name') paginator = Paginator(functions, 50) page = request.GET.get('page') try: functions = paginator.page(page) - + except PageNotAnInteger: functions = paginator.page(1) - + except EmptyPage: functions = paginator.page(paginator.num_pages) - - ctx = {'project' : project, + + ctx = {'project': project, 'functions': functions} - - + return render(request, 'project/functions.html', ctx) def project_analysis(request, project_id): """ - + """ project = get_object_or_404(Project, pk=project_id) dbgs = [] + class D: category = None filename = None line = None message = None link = None - + for d in project.debug_set.iterator(): o = D() o.category = Debug.level2str(d.category) - o.filename = d.filepath.replace(settings.SRC_PATH+'/','') + o.filename = d.filepath.replace(settings.SRC_PATH + '/', '') o.line = d.line o.message = d.message o.link = reverse("browser.views.project_detail", args=(project.id,)) - o.link+= "?file=%s&hl=%d" % (d.filepath, d.line) + o.link += "?file=%s&hl=%d" % (d.filepath, d.line) dbgs.append(o) - - ctx = {'project' : project , - 'dbgs' : dbgs} + + ctx = {'project': project, + 'dbgs': dbgs} + return render(request, 'project/analysis.html', ctx) def get_cache(request, filename): """ - + """ fullpath = unipath.Path(settings.CACHE_PATH + '/' + filename) if not fullpath.isfile(): @@ -345,6 +410,5 @@ def get_cache(request, filename): http = HttpResponse(content_type="image/svg+xml") http.write(data) - - return http + return http diff --git a/codebro/codebro/settings.py b/codebro/codebro/settings.py index 93ea17a..eb2c11c 100644 --- a/codebro/codebro/settings.py +++ b/codebro/codebro/settings.py @@ -5,49 +5,49 @@ from unipath import Path -APP_PATH = Path(__file__).ancestor(2) -SRC_PATH = APP_PATH.child("sources") -CACHE_PATH = APP_PATH.child("cache") -DB_PATH = APP_PATH.child("db").child("codebro.sqlite3") -MAX_UPLOAD_SIZE = 100 * 1024 * 1024 -DEBUG = True -TEMPLATE_DEBUG = DEBUG -SECRET_KEY = "c0d3Br0_k1ck_@$$" -ROOT_URLCONF = "codebro.urls" +APP_PATH = Path(__file__).ancestor(2) +SRC_PATH = APP_PATH.child("sources") +CACHE_PATH = APP_PATH.child("cache") +DB_PATH = APP_PATH.child("db").child("codebro.sqlite3") +MAX_UPLOAD_SIZE = 100 * 1024 * 1024 +DEBUG = True +TEMPLATE_DEBUG = DEBUG +SECRET_KEY = "c0d3Br0_k1ck_@$$" +ROOT_URLCONF = "codebro.urls" WSGI_APPLICATION = "codebro.wsgi.application" -TIME_ZONE = "Australia/Melbourne" -LANGUAGE_CODE = "en-us" -SITE_ID = 1 -USE_I18N = True -USE_L10N = True -USE_TZ = True -MEDIA_ROOT = APP_PATH.child("media") -MEDIA_URL = "/media/" -STATIC_ROOT = APP_PATH.child("static") -STATIC_URL = "/static/" -ADMINS = ( ("hugsy", "hugsy@pyc.li"), ) -MANAGERS = ADMINS -TEMPLATE_DIRS = ( APP_PATH.child("templates"), ) -STATICFILES_DIRS = ( APP_PATH.child("assets"), ) -CACHED_SVG_FMT = "project#%d-fromFunc#%d-fu%d-%s@depth#%d" +TIME_ZONE = "Europe/Berlin" +LANGUAGE_CODE = "en-GB" +SITE_ID = 1 +USE_I18N = True +USE_L10N = True +USE_TZ = True +MEDIA_ROOT = APP_PATH.child("media") +MEDIA_URL = "/media/" +STATIC_ROOT = APP_PATH.child("static") +STATIC_URL = "/static/" +ADMINS = (("admin", "devnull@libcrack.so"), ) +MANAGERS = ADMINS +TEMPLATE_DIRS = (APP_PATH.child("templates"), ) +STATICFILES_DIRS = (APP_PATH.child("assets"), ) +CACHED_SVG_FMT = "project#%d-fromFunc#%d-fu%d-%s@depth#%d" -CLANG_PARSE_OPTIONS = [ "-Wextra", - "-O0", - "-Wall", - "-Wunused-function", - "-Wtautological-compare", - "-Wformat-security", - "-I/usr/lib/gcc/x86_64-linux-gnu/4.8.1/include", - "-I/usr/include", - "-I/usr/local/lib/clang/3.4/include" - ] +CLANG_PARSE_OPTIONS = ["-Wextra", + "-O0", + "-Wall", + "-Wunused-function", + "-Wtautological-compare", + "-Wformat-security", + "-I/usr/lib/gcc/x86_64-unknown-linux-gnu/5.3.0/include", + "-I/usr/include", + "-I/usr/lib/clang/3.7.0/include" + ] DATABASES = { "default": { - "ENGINE": "django.db.backends.sqlite3", - "NAME": DB_PATH, - } + "ENGINE": "django.db.backends.sqlite3", + "NAME": DB_PATH, + } } @@ -89,7 +89,7 @@ "()": "django.utils.log.RequireDebugFalse" } }, - + "handlers": { "mail_admins": { "level": "ERROR", @@ -97,13 +97,13 @@ "class": "django.utils.log.AdminEmailHandler" } }, - + "loggers": { "django.request": { "handlers": ["mail_admins"], "level": "ERROR", "propagate": True, - }, + }, - } + } } diff --git a/codebro/codebro/urls.py b/codebro/codebro/urls.py index 2ca90c3..e3fbaeb 100644 --- a/codebro/codebro/urls.py +++ b/codebro/codebro/urls.py @@ -11,15 +11,15 @@ url(r'^projects/list$', 'project_list'), url(r'^projects/new$', 'project_new'), - url(r'^projects/(?P\d+)/?$', 'project_detail'), - url(r'^projects/(?P\d+)/edit$', 'project_edit'), - url(r'^projects/(?P\d+)/delete$', 'project_delete'), - url(r'^projects/(?P\d+)/draw$', 'project_draw'), + url(r'^projects/(?P\d+)/?$', 'project_detail'), + url(r'^projects/(?P\d+)/edit$', 'project_edit'), + url(r'^projects/(?P\d+)/delete$', 'project_delete'), + url(r'^projects/(?P\d+)/draw$', 'project_draw'), url(r'^projects/(?P\d+)/functions$', 'project_functions'), - url(r'^projects/(?P\d+)/analysis$', 'project_analysis'), - url(r'^projects/(?P\d+)/search$', 'project_search'), - + url(r'^projects/(?P\d+)/analysis$', 'project_analysis'), + url(r'^projects/(?P\d+)/search$', 'project_search'), + url(r'^cache/(?P.+)$', 'get_cache'), - + url(dajaxice_config.dajaxice_url, include('dajaxice.urls')), ) diff --git a/codebro/modules/__init__.py b/codebro/modules/__init__.py index aab3725..69107b8 100644 --- a/codebro/modules/__init__.py +++ b/codebro/modules/__init__.py @@ -4,14 +4,13 @@ def __init__(self, parser, name): self.parser = parser self.name = name required_functions = ('__init__', 'run', ) - + # check for required functions in modules for func in required_functions: - try : + try: getattr(self, func) - - except AttributeError: - print ("[-] Module %s : Missing function %s" % (self.name, func)) - - + except AttributeError: + print( + "[-] Module %s : Missing function %s" % + (self.name, func)) diff --git a/codebro/modules/format_string.py b/codebro/modules/format_string.py index b9ff716..46f3eb7 100644 --- a/codebro/modules/format_string.py +++ b/codebro/modules/format_string.py @@ -1,5 +1,5 @@ import re -import clang.cindex +import clang.cindex from modules import Module from analyzer.models import ModuleDiagnostic @@ -10,16 +10,14 @@ class FormatStringModule(Module): name = "Format String" - def __init__(self, parser): Module.__init__(self, parser, self.name) self.hook_on = clang.cindex.CursorKind.CALL_EXPR - - + def register(self): if self.hook_on not in self.parser.modules.keys(): self.parser.modules[self.hook_on] = [] - + self.parser.modules[self.hook_on].append(self) self.module = ModuleDB() @@ -27,16 +25,15 @@ def register(self): self.module.project = self.parser.project self.module.save() - def run(self, node): if node.kind != self.hook_on: return - + caller_name = node.displayname - + # fmt str method 1 : lookup for known functions (printf, fprintf, sprintf, etc.) # obsolete : handled by -Wformat-security - + # fmt str method 2 : compare args in static strings at function call number_of_args = -1 line = "" @@ -48,29 +45,32 @@ def run(self, node): found = False arg_ref_node = unexposed_refs[i] - for subref in arg_ref_node.get_children() : + for subref in arg_ref_node.get_children(): if subref.kind == clang.cindex.CursorKind.STRING_LITERAL: line = self.solve_string(subref) - if "%" in line : + if "%" in line: number_of_args = len(unexposed_refs) - i - 1 found = True break - if found : + if found: break - - if len(line) == 0 : return # no static string literal found - if "%" not in line : return # no format symbol found - if line.count("%%") == 2*line.count("%") : return # no static string literal found - num_singles = line.count("%") - 2*line.count("%%") + if len(line) == 0: + return # no static string literal found + if "%" not in line: + return # no format symbol found + if line.count("%%") == 2 * line.count("%"): + return # no static string literal found + + num_singles = line.count("%") - 2 * line.count("%%") if number_of_args < num_singles: msg = "%s : args number mismatch (%d declared in string, %d found in function)" - msg%= (caller_name, num_singles, number_of_args) + msg %= (caller_name, num_singles, number_of_args) print '++++', msg - + m = ModuleDiagnostic() m.name = self.name m.project = self.parser.project @@ -79,8 +79,7 @@ def run(self, node): m.line = node.location.line m.message = msg m.save() - - + def solve_string(self, node): start = node.extent.start.offset end = node.extent.end.offset @@ -91,4 +90,3 @@ def solve_string(self, node): print len(res), ":", res return res -