├── AUTHORS ├── LICENSE ├── README.md ├── Vagrantfile ├── app ├── __init__.py ├── config.py ├── moflow.db └── seed.py ├── client ├── client.py ├── client │ ├── __init__.py │ ├── config.py │ ├── helper.py │ └── rest.py ├── engine │ ├── __init__.py │ ├── afl.py │ └── radamsa.py └── requirements.txt ├── controller ├── Arch.py ├── Crash.py ├── Engine.py ├── Hello.py ├── Host.py ├── Job.py ├── Log.py ├── Option.py ├── Platform.py ├── Script.py ├── Status.py ├── Target.py ├── Update.py ├── __init__.py └── _deprecated_Config.py ├── model ├── CallbackScript.py ├── FuzzingArch.py ├── FuzzingCrash.py ├── FuzzingEngine.py ├── FuzzingHost.py ├── FuzzingJob.py ├── FuzzingJobOption.py ├── FuzzingJobState.py ├── FuzzingOption.py ├── FuzzingOptionType.py ├── FuzzingPlatform.py ├── FuzzingScript.py ├── FuzzingTarget.py ├── __init__.py ├── _deprecated_FuzzingConfig.py ├── _not_used_CrashBucket.py ├── _not_used_CrashBucketFile.py ├── _not_used_CrashBucketNote.py ├── _not_used_CrashBucketState.py └── _not_used_CrashSample.py ├── nginx.conf ├── requirements.txt ├── server.py ├── static ├── client.zip └── frontend │ ├── .gitignore │ ├── app │ └── app.js │ ├── asset │ ├── bootstrap │ │ ├── css │ │ │ ├── bootstrap-theme.css │ │ │ ├── bootstrap-theme.css.map │ │ │ ├── bootstrap-theme.min.css │ │ │ ├── bootstrap-theme.min.css.map │ │ │ ├── bootstrap.css │ │ │ ├── bootstrap.css.map │ │ │ ├── bootstrap.min.css │ │ │ └── bootstrap.min.css.map │ │ ├── fonts │ │ │ ├── glyphicons-halflings-regular.eot │ │ │ ├── glyphicons-halflings-regular.svg │ │ │ ├── glyphicons-halflings-regular.ttf │ │ │ ├── glyphicons-halflings-regular.woff │ │ │ └── glyphicons-halflings-regular.woff2 │ │ └── js │ │ │ ├── bootstrap.js │ │ │ ├── bootstrap.min.js │ │ │ └── npm.js │ ├── css │ │ ├── bootstrap.vertical-tabs.min.css │ │ ├── ie10-viewport-bug-workaround.css │ │ ├── ng-tags-input.bootstrap.min.css │ │ ├── ng-tags-input.min.css │ │ └── style.css │ ├── image │ │ ├── ai1.png │ │ ├── ai2.png │ │ ├── ai5.png │ │ ├── ai6.png │ │ ├── banner.jpg │ │ ├── cssmenubg.png │ │ └── favicon.ico │ └── js │ │ ├── angular-file-model.js │ │ ├── angular-route.min.js │ │ ├── angular.min.js │ │ ├── highlight.min.js │ │ ├── html5shiv.min.js │ │ ├── ie-emulation-modes-warning.js │ │ ├── ie10-viewport-bug-workaround.js │ │ ├── ie8-responsive-file-warning.js │ │ ├── jquery-3.1.0.min.js │ │ ├── moment-duration-format.js │ │ ├── moment.min.js │ │ ├── ng-tags-input.min.js │ │ ├── notify.min.js │ │ └── respond.min.js │ ├── controller │ ├── config.js │ ├── crash.js │ ├── err.js │ ├── job.js │ ├── node.js │ └── overview.js │ ├── factory │ ├── arch.js │ ├── config.js │ ├── crash.js │ ├── engine.js │ ├── host.js │ ├── job.js │ ├── misc.js │ ├── option.js │ ├── platform.js │ ├── script.js │ ├── status.js │ └── target.js │ ├── index.htm │ ├── server.js │ └── view │ ├── config.htm │ ├── crash.htm │ ├── err.htm │ ├── job.htm │ ├── node.htm │ └── overview.htm ├── uwsgi.ini └── vagrant.sh /AUTHORS: -------------------------------------------------------------------------------- 1 | Copyright 2016 Cisco Talos Threat Intelligence & Research Group 2 | 3 | Richard Johnson - Initial version 4 | Daniel Moghimi - AngularJS rewrite 5 | 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 talos-vulndev 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FuzzFlow -------------------------------------------------------------------------------- /Vagrantfile: -------------------------------------------------------------------------------- 1 | Vagrant.configure("2") do |config| 2 | config.vm.box = "ubuntu/trusty64" 3 | #config.vm.network "forwarded_port", guest: 80, host: 1080 4 | #config.vm.network "forwarded_port", guest: 5000, host: 5000 5 | config.vm.network "private_network", type: "dhcp" 6 | config.vm.provider "virtualbox" do |vb| 7 | vb.memory = "2048" 8 | vb.cpus = "2" 9 | #vb.customize ["modifyvm", :id, "--nictype1", "Am79C973"] 10 | #vb.customize ["modifyvm", :id, "--natdnshostresolver1", "on"] 11 | #vb.customize ["modifyvm", :id, "--natdnsproxy1", "on"] 12 | end 13 | 14 | config.vm.provision "shell", privileged: false, path: "vagrant.sh" 15 | end -------------------------------------------------------------------------------- /app/__init__.py: -------------------------------------------------------------------------------- 1 | from flask import Flask, jsonify, request, json 2 | from flask_sqlalchemy import SQLAlchemy 3 | from flask_restful import Api 4 | import config, time, traceback, os, uuid 5 | 6 | app = Flask(__name__, static_folder='static', static_url_path='') 7 | 8 | logFile = "log-" + time.strftime("%Y-%m-%d-%H.%M.%S") + ".log" 9 | 10 | 11 | """ 12 | Setup DB Models 13 | """ 14 | app.config['db'] = db = SQLAlchemy(app) 15 | app.config['SQLALCHEMY_DATABASE_URI'] = config.DATABASE_URI 16 | 17 | 18 | from model.FuzzingArch import FuzzingArch 19 | from model.FuzzingPlatform import FuzzingPlatform 20 | from model.FuzzingEngine import FuzzingEngine 21 | from model.FuzzingHost import FuzzingHost 22 | #from model.FuzzingConfig import FuzzingConfig 23 | from model.FuzzingTarget import FuzzingTarget 24 | from model.FuzzingJobState import FuzzingJobState 25 | from model.FuzzingJobOption import FuzzingJobOption 26 | from model.FuzzingJob import FuzzingJob 27 | from model.FuzzingOptionType import FuzzingOptionType 28 | from model.FuzzingOption import FuzzingOption 29 | from model.FuzzingCrash import FuzzingCrash 30 | from model.CallbackScript import CallbackScript 31 | from model.FuzzingScript import FuzzingScript 32 | 33 | db.drop_all() 34 | db.create_all() 35 | import seed 36 | db.session.commit() 37 | 38 | """ 39 | @app.before_first_request 40 | def create_db(): 41 | None 42 | """ 43 | 44 | 45 | """ 46 | Setup Routes 47 | """ 48 | from controller.Hello import HelloCtrl 49 | from controller.Update import UpdateCtrl 50 | from controller.Job import JobCtrl 51 | from controller.Engine import EngineCtrl 52 | from controller.Arch import ArchCtrl 53 | from controller.Platform import PlatformCtrl 54 | from controller.Log import LogCtrl 55 | from controller.Host import HostCtrl 56 | from controller.Script import ScriptCtrl 57 | from controller.Target import TargetCtrl 58 | from controller.Crash import CrashCtrl 59 | from controller.Status import StatusCtrl 60 | #from controller.Config import ConfigCtrl 61 | from controller.Option import OptionCtrl 62 | 63 | 64 | api = Api(app) 65 | api.add_resource(HelloCtrl, '/') 66 | api.add_resource(UpdateCtrl, '/api/update/') 67 | api.add_resource(JobCtrl, '/api/job', '/api/job/') 68 | api.add_resource(EngineCtrl, '/api/engine', '/api/engine/') 69 | api.add_resource(ArchCtrl, '/api/arch', '/api/arch/') 70 | api.add_resource(PlatformCtrl, '/api/platform', '/api/platform/') 71 | api.add_resource(HostCtrl, '/api/host', '/api/host/') 72 | api.add_resource(ScriptCtrl, '/api/script', '/api/script/') 73 | api.add_resource(TargetCtrl, '/api/target', '/api/target/') 74 | # api.add_resource(CrashBucketCtrl, '/api/bucket', '/api/bucket/') 75 | api.add_resource(CrashCtrl, '/api/crash', '/api/crash/') 76 | #api.add_resource(ConfigCtrl, '/api/config', '/api/config/') 77 | api.add_resource(OptionCtrl, '/api/option', '/api/option/') 78 | api.add_resource(LogCtrl, '/api/log') 79 | api.add_resource(StatusCtrl, '/api/status') 80 | 81 | 82 | @app.errorhandler(Exception) 83 | def handle_invalid_usage(error): 84 | message = time.strftime("%Y-%m-%d-%H.%M.%S") + ": \n" + traceback.format_exc() + "\n\n" 85 | with open(logFile, 'a') as f: 86 | f.write(message) 87 | rv = {} 88 | rv['message'] = message 89 | response = jsonify(rv) 90 | response.status_code = 500 91 | return response 92 | 93 | 94 | @app.route('/api/upload', methods=['POST']) 95 | def upload_file(): 96 | if 'file' not in request.files: 97 | return json.dumps({"err": "invalid request"}), 400 98 | 99 | u_file = request.files['file'] 100 | if u_file.filename == '': 101 | return json.dumps({"err": "invalid request"}), 400 102 | 103 | path = os.path.join(config.UPLOAD_FOLDER, str(uuid.uuid4())) 104 | 105 | u_file.save(path) 106 | return json.dumps({"upload_path": path}), 200 107 | -------------------------------------------------------------------------------- /app/config.py: -------------------------------------------------------------------------------- 1 | import os 2 | DATABASE_URI = "sqlite:///moflow.db" 3 | CLIENT_FOLDER = "client" 4 | UPLOAD_FOLDER = 'static' + os.sep + 'upload' -------------------------------------------------------------------------------- /app/moflow.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/talos-vulndev/FuzzFlow/3f2de617c3658904f9640f53a05d5e4e90634e32/app/moflow.db -------------------------------------------------------------------------------- /app/seed.py: -------------------------------------------------------------------------------- 1 | from app import app 2 | from model.FuzzingPlatform import FuzzingPlatform 3 | from model.FuzzingArch import FuzzingArch 4 | from model.FuzzingEngine import FuzzingEngine 5 | from model.FuzzingJobState import FuzzingJobState 6 | from model.FuzzingTarget import FuzzingTarget 7 | from model.FuzzingScript import FuzzingScript 8 | from model.FuzzingOptionType import FuzzingOptionType 9 | from model.FuzzingOption import FuzzingOption 10 | 11 | db = app.config['db'] 12 | 13 | #Fuzzing Job Status 14 | db.session.add(FuzzingJobState('Queued')) #DO NOT CHANGE 15 | db.session.add(FuzzingJobState('Allocated')) #DO NOT CHANGE 16 | db.session.add(FuzzingJobState('Active')) #DO NOT CHANGE 17 | db.session.add(FuzzingJobState('Completed')) #DO NOT CHANGE 18 | db.session.add(FuzzingJobState('Paused')) #DO NOT CHANGE 19 | db.session.add(FuzzingJobState('Failed')) #DO NOT CHANGE 20 | #db.session.add(FuzzingJobState('Reserved')) #DO NOT CHANGE 21 | 22 | #Fuzzing Option 23 | field_type = FuzzingOptionType("FIELD") #DO NOT CHANGE 24 | file_type = FuzzingOptionType("FILE") #DO NOT CHANGE 25 | list_type = FuzzingOptionType("LIST") #DO NOT CHANGE 26 | checkbox_type = FuzzingOptionType("CHECKBOX") #DO NOT CHANGE 27 | db.session.add(field_type) 28 | db.session.add(file_type) 29 | db.session.add(list_type) 30 | db.session.add(checkbox_type) 31 | 32 | 33 | #Platforms 34 | unknown_plat = FuzzingPlatform('Unknown') 35 | db.session.add(unknown_plat) 36 | 37 | #Architectures 38 | unknown_arch = FuzzingArch('Unknown') 39 | db.session.add(unknown_arch) 40 | 41 | #Options 42 | afl_in_dir = FuzzingOption('afl_in_dir', field_type) 43 | afl_out_dir = FuzzingOption('afl_out_dir', field_type) 44 | afl_timeout = FuzzingOption('afl_timeout', field_type) 45 | db.session.add(afl_in_dir) 46 | db.session.add(afl_out_dir) 47 | db.session.add(afl_timeout) 48 | 49 | 50 | #Fuzzing Engines 51 | db.session.add( 52 | FuzzingEngine("afl", "/usr/local/bin/afl-fuzz", unknown_plat, unknown_arch, [afl_out_dir, afl_timeout, afl_in_dir ]) 53 | ) 54 | 55 | #Fuzzing Targets 56 | db.session.add(FuzzingTarget('libpng-1.5.27', "/home/vagrant/libpng-1.5.27/pngtest", unknown_plat, unknown_arch)) 57 | db.session.add(FuzzingTarget('tiff-4.0.6-fake-vulnerable', "/home/vagrant/tiff-4.0.6/tools/tiff2pdf", unknown_plat, unknown_arch)) 58 | 59 | 60 | #Fuzzing Script 61 | db.session.add(FuzzingScript('test-script', 62 | ''' 63 | 64 | go 65 | 66 | ''')) 67 | 68 | 69 | -------------------------------------------------------------------------------- /client/client.py: -------------------------------------------------------------------------------- 1 | import time, socket, sys, traceback 2 | 3 | from client.config import * 4 | from client.helper import Helper 5 | from client.rest import Rest 6 | from engine.afl import Fuzzer as afl 7 | from engine.radamsa import Fuzzer as radamsa 8 | 9 | 10 | 11 | engines = { 12 | 'afl' : afl, 13 | 'radamsa' : radamsa 14 | } 15 | 16 | def main(): 17 | helper = Helper() 18 | host = helper.register_host() 19 | print "\n\nFuzzing Client %s\n" % CLIENT_VERSION 20 | while True: 21 | fz = None 22 | job = None 23 | try: 24 | helper.update() 25 | job = Rest.get_job_by_host(host['id']) 26 | if job is not None: 27 | engine, target, options = helper.extract_job(job) 28 | engine_name = engine['name'] 29 | fz = engines[engine_name](job, engine, target, options) 30 | fz.start() 31 | continue 32 | 33 | except socket.error: 34 | pass 35 | 36 | except KeyboardInterrupt as e: 37 | if fz is not None: 38 | fz.fail(e.message) 39 | print "Execution interrupted. Quitting." 40 | sys.exit(0) 41 | 42 | except: 43 | print "Unhandled Exception\n" 44 | traceback.print_exc() 45 | print '' 46 | message = time.strftime("%Y-%m-%d-%H.%M.%S") + ": \n" + \ 47 | traceback.format_exc() 48 | try: 49 | helper.log_error(message) 50 | except socket.error: 51 | pass 52 | 53 | 54 | if __name__ == "__main__": 55 | main() 56 | -------------------------------------------------------------------------------- /client/client/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/talos-vulndev/FuzzFlow/3f2de617c3658904f9640f53a05d5e4e90634e32/client/client/__init__.py -------------------------------------------------------------------------------- /client/client/config.py: -------------------------------------------------------------------------------- 1 | SERVER_ENDPOINT = "http://172.28.128.3/" 2 | CLIENT_VERSION = "2016.8.15" 3 | DELAY = 10 4 | -------------------------------------------------------------------------------- /client/client/helper.py: -------------------------------------------------------------------------------- 1 | import time, socket, hashlib, zipfile, os, sys, shutil, glob, platform, base64, subprocess, tempfile 2 | from uuid import getnode 3 | 4 | from rest import Rest 5 | 6 | class Helper(object): 7 | def __init__(self): 8 | self.host = None 9 | self.states = None 10 | 11 | 12 | def get_mac(self): 13 | mac = getnode() 14 | return "%02x:%02x:%02x:%02x:%02x:%02x" % ( 15 | mac >> 40, 16 | mac >> 32 & 0xff, 17 | mac >> 24 & 0xff, 18 | mac >> 16 & 0xff, 19 | mac >> 8 & 0xff, 20 | mac & 0xff, 21 | ) 22 | 23 | 24 | def get_default_ip(self): 25 | s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 26 | try: 27 | s.connect(('moflow.org', 9)) 28 | client = s.getsockname()[0] 29 | except socket.error: 30 | client = "0.0.0.0" 31 | finally: 32 | del s 33 | return client 34 | 35 | def get_platform_id(self): 36 | plat_name = platform.system() + ' ' + platform.release() 37 | 38 | plats = Rest.list_platform() 39 | if plats is not None and len(plats) > 0: 40 | for p in plats: 41 | if p['name'] == plat_name: 42 | return p['id'] 43 | 44 | new_platform = Rest.create_platform(plat_name) 45 | return new_platform['id'] 46 | 47 | 48 | 49 | def get_arch_id(self): 50 | arch_name = platform.machine() 51 | 52 | arches = Rest.list_arch() 53 | if arches is not None and len(arches) > 0: 54 | for a in arches: 55 | if a['name'] == arch_name: 56 | return a['id'] 57 | 58 | new_arch = Rest.create_arch(arch_name) 59 | return new_arch['id'] 60 | 61 | def register_host(self): 62 | print "[*] Trying to register the host..." 63 | platform_id = self.get_platform_id() 64 | arch_id = self.get_arch_id() 65 | mac = self.get_mac() 66 | hostname, alias_list, addr_list = socket.gethostbyaddr(self.get_default_ip()) 67 | ip = addr_list[0] 68 | host_payload = { 69 | 'name' : hostname, 70 | 'mac' : mac, 71 | 'ip' : ip, 72 | 'platform_id': platform_id, 73 | 'arch_id': arch_id 74 | } 75 | 76 | self.host = Rest.get_host_by_mac(mac) 77 | if self.host is not None: 78 | print "[!] Host with mac address %s has already been registered." % mac 79 | Rest.update_host(self.host['id'], host_payload) 80 | else: 81 | self.host = Rest.create_host(host_payload) 82 | 83 | return self.host 84 | 85 | 86 | def update(self): 87 | print "[*] Trying to update the client..." 88 | try: 89 | with open("client.zip", "rb") as f: 90 | cli = f.read() 91 | hsh = hashlib.md5(cli).hexdigest() 92 | except IOError: 93 | hsh = "INVALID" 94 | 95 | path = Rest.get_update(hsh) 96 | if path is not None: 97 | shutil.rmtree('engine') 98 | shutil.rmtree('client') 99 | os.remove('client.py') 100 | os.remove('requirements.txt') 101 | 102 | f = zipfile.ZipFile(path, "r") 103 | f.extractall('tmp') 104 | f.close() 105 | 106 | root = 'tmp' + os.sep + 'client' + os.sep 107 | shutil.move(root + 'client', '.') 108 | shutil.move(root + 'engine', '.') 109 | shutil.move(root + 'client.py', '.') 110 | shutil.move(root + 'requirements.txt', '.') 111 | shutil.rmtree('tmp') 112 | 113 | args = sys.argv[:] 114 | print "[*] Update installed successfully, restarting...\n" 115 | 116 | args.insert(0, sys.executable) 117 | if sys.platform == 'win32': 118 | args = ['"%s"' % arg for arg in args] 119 | 120 | os.execv(sys.executable, args) 121 | sys.exit(1) 122 | 123 | else: 124 | print "[!] Client is already the latest version." 125 | 126 | 127 | def log_error(self, message): 128 | log_file = "log-" + time.strftime("%Y-%m-%d-%H.%M.%S") + ".log" 129 | with open(log_file, "a") as f: 130 | f.write(message) 131 | 132 | Rest.create_log(message) 133 | 134 | def extract_job(self, job): 135 | engine = Rest.get_engine(job['engine_id']) 136 | target = Rest.get_target(job['target_id']) 137 | options = {} 138 | for opt in engine['options']: 139 | for opt_val in job['options']: 140 | if opt['id'] == opt_val['option_id']: 141 | options[opt['name']] = opt_val['value'] 142 | 143 | return engine, target, options 144 | 145 | def get_state_id_by_name(self, name): 146 | if self.states is None: 147 | self.states = Rest.list_job_state() 148 | for state in self.states: 149 | if state['name'] == name: 150 | return state['id'] 151 | 152 | def get_state_name_by_id(self, id): 153 | if self.states is None: 154 | self.states = Rest.list_job_state() 155 | for state in self.states: 156 | if state['id'] == id: 157 | return state['name'] 158 | 159 | def update_job_state(self, job_id, state_id): 160 | return Rest.update_job(job_id, { 161 | 'state_id': state_id 162 | }) 163 | 164 | def move_job_state(self, job, current, next): 165 | if current == "*": 166 | new_state_id = self.get_state_id_by_name(next) 167 | return self.update_job_state(job['id'], new_state_id) 168 | 169 | state_name = self.get_state_name_by_id(job['state_id']) 170 | if state_name == current: 171 | new_state_id = self.get_state_id_by_name(next) 172 | return self.update_job_state(job['id'], new_state_id) 173 | return job 174 | 175 | def update_job_output(self, job_id, content): 176 | output = base64.encodestring(content) 177 | return Rest.update_job(job_id, { 178 | 'output': output 179 | }) 180 | 181 | def report_crash_sample(self, job, target, sample): 182 | if platform.system() == 'Linux': 183 | tmp = tempfile.NamedTemporaryFile() 184 | dump_file_name = tmp.name 185 | 186 | dbg_cmd = [ 187 | 'gdb', 188 | '-batch', 189 | '-ex', 'run', 190 | '-ex', 'set disassembly-flavor intel', 191 | '-ex', 'info registers', 192 | '-ex', 'bt full', 193 | '-ex', 'disass', 194 | '-ex', 'generate-core-file %s' % dump_file_name, 195 | '--args', 196 | target['path'], 197 | sample 198 | ] 199 | 200 | print dbg_cmd 201 | 202 | dbg_file = dump_file = None 203 | proc = subprocess.Popen(dbg_cmd, stdout=subprocess.PIPE) 204 | if proc.wait() == 0: 205 | try: 206 | dbg_file = Rest.upload_file(proc.stdout) 207 | dump_file = Rest.upload_file(open(dump_file_name, 'rb')) 208 | except: 209 | None 210 | 211 | 212 | 213 | crash_payload = {} 214 | crash_payload['job_id'] = job['id'] 215 | 216 | repro_file = Rest.upload_file(open(sample, 'rb')) 217 | if repro_file is not None: 218 | crash_payload['repro_file'] = repro_file['upload_path'] 219 | if dump_file is not None: 220 | crash_payload['dump_file'] = dump_file['upload_path'] 221 | if dbg_file is not None: 222 | crash_payload['dbg_file'] = dbg_file['upload_path'] 223 | 224 | print Rest.create_crash(crash_payload) 225 | 226 | 227 | else: 228 | print "No Implemented!!!" 229 | -------------------------------------------------------------------------------- /client/client/rest.py: -------------------------------------------------------------------------------- 1 | import requests 2 | 3 | from config import SERVER_ENDPOINT 4 | 5 | 6 | class Rest(object): 7 | @staticmethod 8 | def url(sections): 9 | return SERVER_ENDPOINT + '/'.join(sections) 10 | 11 | @staticmethod 12 | def post(sections, payload): 13 | r = requests.post(Rest.url(sections), json=payload) 14 | return r 15 | 16 | @staticmethod 17 | def get(sections, params=None): 18 | r = requests.get(Rest.url(sections), params=params) 19 | return r 20 | 21 | @staticmethod 22 | def download_file(url): 23 | filename = url.split('/')[-1] 24 | r = requests.get(url, stream=True) 25 | with open(filename, 'wb') as fd: 26 | for chunk in r.__iter__(): 27 | if chunk: 28 | fd.write(chunk) 29 | fd.flush() 30 | return filename 31 | 32 | @staticmethod 33 | def upload_file(f): 34 | files = {'file': f} 35 | r = requests.post(Rest.url(['api', 'upload']), files=files) 36 | if r.status_code == 200: 37 | return r.json() 38 | else: 39 | return None 40 | 41 | @staticmethod 42 | def create_log(message): 43 | payload = {"message": message} 44 | r = Rest.post(['api', 'log'], payload) 45 | print r.json() 46 | 47 | @staticmethod 48 | def list_platform(): 49 | r = Rest.get(['api', 'platform']) 50 | if r.status_code == 200: 51 | return r.json() 52 | else: 53 | return None 54 | 55 | @staticmethod 56 | def create_platform(name): 57 | r = Rest.post(['api', 'platform'], { 58 | "name" : name 59 | }) 60 | if r.status_code == 201: 61 | return r.json() 62 | else: 63 | return None 64 | 65 | @staticmethod 66 | def list_arch(): 67 | r = Rest.get(['api', 'arch']) 68 | if r.status_code == 200: 69 | return r.json() 70 | else: 71 | return None 72 | 73 | @staticmethod 74 | def create_arch(name): 75 | r = Rest.post(['api', 'arch'], { 76 | "name": name 77 | }) 78 | if r.status_code == 201: 79 | return r.json() 80 | else: 81 | return None 82 | 83 | @staticmethod 84 | def get_host_by_mac(mac): 85 | r = Rest.get(['api', 'host', mac], {'mac': 1}) 86 | if r.status_code == 200: 87 | return r.json() 88 | else: 89 | return None 90 | 91 | @staticmethod 92 | def create_host(host): 93 | r = Rest.post(['api', 'host'], host) 94 | if r.status_code == 201: 95 | return r.json() 96 | else: 97 | return None 98 | 99 | @staticmethod 100 | def update_host(host_id, host): 101 | r = Rest.post(['api', 'host', host_id], host) 102 | if r.status_code == 201: 103 | return r.json() 104 | else: 105 | return None 106 | 107 | @staticmethod 108 | def get_update(hash): 109 | r = Rest.get(['api', 'update', hash]) 110 | if r.status_code == 200: 111 | payload = r.json() 112 | return Rest.download_file(payload['url']) 113 | else: 114 | return None 115 | 116 | @staticmethod 117 | def get_job_by_host(host_id): 118 | r = Rest.get(['api', 'job', host_id], {'host': 1}) 119 | if r.status_code == 200: 120 | return r.json() 121 | else: 122 | return None 123 | 124 | @staticmethod 125 | def update_job(job_id, payload): 126 | r = Rest.post(['api', 'job', job_id], payload) 127 | if r.status_code == 201: 128 | return r.json() 129 | else: 130 | return None 131 | 132 | @staticmethod 133 | def list_job_state(): 134 | r = Rest.get(['api', 'job'], {'state': 1}) 135 | if r.status_code == 200: 136 | return r.json() 137 | else: 138 | return None 139 | 140 | @staticmethod 141 | def get_engine(engine_id): 142 | r = Rest.get(['api', 'engine', engine_id]) 143 | if r.status_code == 200: 144 | return r.json() 145 | else: 146 | return None 147 | 148 | @staticmethod 149 | def get_target(target_id): 150 | r = Rest.get(['api', 'target', target_id]) 151 | if r.status_code == 200: 152 | return r.json() 153 | else: 154 | return None 155 | 156 | @staticmethod 157 | def create_crash(crash): 158 | r = Rest.post(['api', 'crash'], crash) 159 | if r.status_code == 201: 160 | return r.json() 161 | else: 162 | return None 163 | 164 | -------------------------------------------------------------------------------- /client/engine/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/talos-vulndev/FuzzFlow/3f2de617c3658904f9640f53a05d5e4e90634e32/client/engine/__init__.py -------------------------------------------------------------------------------- /client/engine/afl.py: -------------------------------------------------------------------------------- 1 | import threading, os, subprocess, hashlib 2 | from client.helper import Helper 3 | 4 | 5 | class Fuzzer(): 6 | prev_hsh = '' 7 | helper = None 8 | timer = None 9 | def __init__(self, job, engine, target, options): 10 | self.job = job 11 | self.engine = engine 12 | self.target = target 13 | self.options = options 14 | self.helper = Helper() 15 | self.crashes = [] 16 | 17 | def fail(self, err=None): 18 | self.job = self.helper.move_job_state(self.job, '*', 'Failed') 19 | if err is not None: 20 | self.helper.update_job_output(self.job['id'], err) 21 | call_args = [ 22 | 'pkill', 23 | 'afl-fuzz' 24 | ] 25 | subprocess.call(call_args) 26 | if self.timer is not None: 27 | self.timer.cancel() 28 | 29 | def watch_for_crash(self, dir): 30 | for root, dirs, samples in os.walk(dir): 31 | for sample in samples: 32 | try: 33 | found = self.crashes.index(sample) 34 | except: 35 | self.crashes.append(sample) 36 | self.helper.report_crash_sample(self.job, self.target, dir + os.sep + sample) 37 | 38 | 39 | def start(self): 40 | def probe_afl(): 41 | out_dir = self.options['afl_out_dir'] 42 | self.watch_for_crash(out_dir + os.sep + 'crashes') 43 | try: 44 | raw_stats = open(out_dir + os.sep + 'fuzzer_stats').read() 45 | except Exception as e: 46 | self.fail(e.message) 47 | 48 | hsh = hashlib.md5(raw_stats) 49 | if hsh != self.prev_hsh: 50 | self.job = self.helper.move_job_state(self.job, 'Allocated', 'Active') 51 | self.helper.update_job_output(self.job['id'], raw_stats) 52 | self.prev_hsh = hsh 53 | 54 | def wrapper1(): 55 | probe_afl() 56 | e = threading.Event() 57 | while not e.wait(10): #We update the status of afl with server every 20 seconds 58 | probe_afl() 59 | 60 | self.timer = threading.Timer(5, wrapper1) 61 | self.timer.start() 62 | 63 | in_dir = self.options['afl_in_dir'] 64 | out_dir = self.options['afl_out_dir'] 65 | tout = self.options['afl_timeout'] 66 | call_args = [ 67 | self.engine['path'], 68 | '-i', 69 | in_dir, 70 | '-o', 71 | out_dir 72 | ] 73 | 74 | if tout is not None and len(tout) > 0: 75 | call_args.append('-t') 76 | call_args.append(tout) 77 | 78 | call_args.append('--') 79 | call_args.append(self.target['path']) 80 | call_args.append('@@') 81 | 82 | proc = subprocess.Popen(call_args, stdout=subprocess.PIPE) 83 | if proc.wait() != 0: 84 | output = proc.stdout.read() 85 | self.fail(output) 86 | 87 | -------------------------------------------------------------------------------- /client/engine/radamsa.py: -------------------------------------------------------------------------------- 1 | class Fuzzer(): 2 | def __init__(self, job, engine, target, options): 3 | None 4 | 5 | def start(self): 6 | print 'hello radamsa' -------------------------------------------------------------------------------- /client/requirements.txt: -------------------------------------------------------------------------------- 1 | requests==2.2.1 -------------------------------------------------------------------------------- /controller/Arch.py: -------------------------------------------------------------------------------- 1 | from flask_restful import Resource, reqparse 2 | from model.FuzzingArch import FuzzingArch 3 | from app import app 4 | 5 | db = app.config['db'] 6 | 7 | class ArchCtrl(Resource): 8 | def create(self, data): 9 | try: 10 | arch = FuzzingArch(data['name']) 11 | db.session.add(arch) 12 | db.session.commit() 13 | except Exception as e: 14 | return {"err" : "invalid request"}, 400 15 | 16 | return arch.as_dict(), 201 17 | 18 | def read(self, id): 19 | arch = FuzzingArch.query.filter_by(id=id).first() 20 | if arch is None: 21 | return {"err": "not found"}, 404 22 | return arch.as_dict(), 200 23 | 24 | def update(self, id, data): 25 | arch = FuzzingArch.query.filter_by(id=id).first() 26 | if arch is None: 27 | return {"err": "not found"}, 404 28 | 29 | if data['name'] is not None: 30 | arch.name = data['name'] 31 | 32 | try: 33 | db.session.commit() 34 | except Exception as e: 35 | return {"err": "invalid request"}, 400 36 | 37 | return arch.as_dict(), 201 38 | 39 | def delete(self, id): 40 | arch = FuzzingArch.query.filter_by(id=id).first() 41 | if arch is None: 42 | return {"err": "not found"}, 404 43 | try: 44 | db.session.delete(arch) 45 | db.session.commit() 46 | except Exception as e: 47 | return {"err": "invalid request"}, 400 48 | 49 | return {"msg" : "record removed successfully"}, 201 50 | 51 | def list(self, offset=0, limit=10000): 52 | arches= FuzzingArch.query.offset(offset).limit(limit).all() 53 | arches = [arch.as_dict() for arch in arches] 54 | return arches, 200 55 | 56 | def get(self, id=None): 57 | parser = reqparse.RequestParser() 58 | parser.add_argument('delete', type=int) 59 | parser.add_argument('offset', type=int) 60 | parser.add_argument('limit', type=int) 61 | args = parser.parse_args() 62 | if id is None: 63 | if args['offset'] is not None and args['limit'] is not None: 64 | return self.list(args['offset'], args['limit']) 65 | else: 66 | return self.list() 67 | else: 68 | if args['delete'] == 1: 69 | return self.delete(id) 70 | else: 71 | return self.read(id) 72 | 73 | def post(self, id=None): 74 | parser = reqparse.RequestParser() 75 | if id is None: 76 | parser.add_argument('name', required=True, location='json') 77 | return self.create(parser.parse_args()) 78 | else: 79 | parser.add_argument('name', location='json') 80 | return self.update(id, parser.parse_args()) -------------------------------------------------------------------------------- /controller/Crash.py: -------------------------------------------------------------------------------- 1 | from flask_restful import Resource, reqparse 2 | from model.FuzzingJob import FuzzingJob 3 | from model.FuzzingCrash import FuzzingCrash 4 | from app import app 5 | 6 | 7 | db = app.config['db'] 8 | 9 | 10 | class CrashCtrl(Resource): 11 | def create(self, data): 12 | job = FuzzingJob.query.filter_by(id=data['job_id']).first() 13 | if job is None: 14 | return {"err": "invalid request"}, 400 15 | 16 | try: 17 | crash = FuzzingCrash(job, data['repro_file']) 18 | 19 | if data['dump_file'] is not None: 20 | crash.dump_file = data['dump_file'] 21 | 22 | if data['dbg_file'] is not None: 23 | crash.dbg_file = data['dbg_file'] 24 | 25 | db.session.add(crash) 26 | db.session.commit() 27 | except Exception as e: 28 | return {"err" : "invalid request"}, 400 29 | 30 | return crash.as_dict(), 201 31 | 32 | 33 | def read(self, id): 34 | bucket = FuzzingCrash.query.filter_by(id=id).first() 35 | if bucket is None: 36 | return {"err": "not found"}, 404 37 | return bucket.as_dict(), 200 38 | 39 | def update(self, id, data): 40 | crash = FuzzingCrash.query.filter_by(id=id).first() 41 | if crash is None: 42 | return {"err": "not found"}, 404 43 | 44 | 45 | job = FuzzingJob.query.filter_by(id=data['job_id']).first() 46 | if job is not None: 47 | crash.job = job 48 | 49 | if data['repro_file'] is not None: 50 | crash.repro_file= data['dump_file'] 51 | 52 | if data['dump_file'] is not None: 53 | crash.dump_file = data['dump_file'] 54 | 55 | if data['dbg_file'] is not None: 56 | crash.dbg_file = data['dbg_file'] 57 | try: 58 | db.session.commit() 59 | except Exception as e: 60 | return {"err": "invalid request"}, 400 61 | 62 | return crash.as_dict(), 201 63 | 64 | 65 | def delete(self, id): 66 | crash = FuzzingCrash.query.filter_by(id=id).first() 67 | if crash is None: 68 | return {"err": "not found"}, 404 69 | try: 70 | db.session.delete(crash) 71 | db.session.commit() 72 | except Exception as e: 73 | return {"err": "invalid request"}, 400 74 | 75 | return {"msg" : "record removed successfully"}, 201 76 | 77 | def list(self, offset=0, limit=20): 78 | crashes = FuzzingCrash.query.offset(offset).limit(limit).all() 79 | crashes = [c.as_dict() for c in crashes ] 80 | return crashes, 200 81 | 82 | def get(self, id=None): 83 | parser = reqparse.RequestParser() 84 | parser.add_argument('delete', type=int) 85 | parser.add_argument('offset', type=int) 86 | parser.add_argument('limit', type=int) 87 | args = parser.parse_args() 88 | if id is None: 89 | if args['offset'] is not None and args['limit'] is not None: 90 | return self.list(args['offset'], args['limit']) 91 | else: 92 | return self.list() 93 | else: 94 | if args['delete'] == 1: 95 | return self.delete(id) 96 | else: 97 | return self.read(id) 98 | 99 | def post(self, id=None): 100 | parser = reqparse.RequestParser() 101 | if id is None: 102 | if id is None: 103 | parser.add_argument('job_id', required=True, location='json') 104 | parser.add_argument('repro_file', required=True, location='json') 105 | parser.add_argument('dump_file', location='json') 106 | parser.add_argument('dbg_file', location='json') 107 | return self.create(parser.parse_args()) 108 | else: 109 | parser.add_argument('note', location='json') 110 | parser.add_argument('repro_file', location='json') 111 | parser.add_argument('dump_file', location='json') 112 | parser.add_argument('dbg_file', location='json') 113 | return self.update(id, parser.parse_args()) -------------------------------------------------------------------------------- /controller/Engine.py: -------------------------------------------------------------------------------- 1 | from flask_restful import Resource, reqparse 2 | from model.FuzzingEngine import FuzzingEngine 3 | from model.FuzzingPlatform import FuzzingPlatform 4 | from model.FuzzingOption import FuzzingOption 5 | from model.FuzzingArch import FuzzingArch 6 | 7 | from app import app 8 | 9 | 10 | db = app.config['db'] 11 | 12 | 13 | class EngineCtrl(Resource): 14 | def create(self, data): 15 | platform = FuzzingPlatform.query.filter_by(id=data['platform_id']).first() 16 | if platform is None: 17 | return {"err" : "invalid request"}, 400 18 | 19 | arch = FuzzingArch.query.filter_by(id=data['arch_id']).first() 20 | if platform is None: 21 | return {"err" : "invalid request"}, 400 22 | 23 | try: 24 | engine = FuzzingEngine(data['name'], data['path'], platform, arch) 25 | 26 | if data['options'] is not None: 27 | for option_id in data['options']: 28 | options = FuzzingOption.query.filter_by(id=option_id).first() 29 | if options is not None: 30 | engine.options.append(options) 31 | 32 | db.session.add(engine) 33 | db.session.commit() 34 | except Exception as e: 35 | return {"err" : "invalid request"}, 400 36 | 37 | result = engine.as_dict() 38 | result['options'] = [option.as_dict() for option in engine.options] 39 | return result, 201 40 | 41 | 42 | def read(self, id): 43 | engine = FuzzingEngine.query.filter_by(id=id).first() 44 | if engine is None: 45 | return {"err": "not found"}, 404 46 | 47 | result = engine.as_dict() 48 | result['options'] = [option.as_dict() for option in engine.options] 49 | return result, 200 50 | 51 | def update(self, id, data): 52 | engine = FuzzingEngine.query.filter_by(id=id).first() 53 | if engine is None: 54 | return {"err": "not found"}, 404 55 | 56 | platform = FuzzingPlatform.query.filter_by(id=data['platform_id']).first() 57 | if platform is not None: 58 | engine.platform = platform 59 | 60 | arch = FuzzingArch.query.filter_by(id=data['arch_id']).first() 61 | if arch is not None: 62 | engine.arch = arch 63 | 64 | if data['name'] is not None: 65 | engine.name = data['name'] 66 | 67 | if data['path'] is not None: 68 | engine.path = data['path'] 69 | 70 | if data['options'] is not None: 71 | engine.options = [] 72 | for option_id in data['options']: 73 | options = FuzzingOption.query.filter_by(id=option_id).first() 74 | if options is not None: 75 | engine.options.append(options) 76 | 77 | try: 78 | db.session.commit() 79 | except Exception as e: 80 | return {"err": "invalid request"}, 400 81 | 82 | result = engine.as_dict() 83 | result['options'] = [option.as_dict() for option in engine.options] 84 | return result, 200 85 | 86 | def delete(self, id): 87 | engine = FuzzingEngine.query.filter_by(id=id).first() 88 | if engine is None: 89 | return {"err": "not found"}, 404 90 | try: 91 | db.session.delete(engine) 92 | db.session.commit() 93 | except Exception as e: 94 | return {"err": "invalid request"}, 400 95 | 96 | return {"msg" : "record removed successfully"}, 201 97 | 98 | def list(self, offset=0, limit=10000): 99 | engines = FuzzingEngine.query.offset(offset).limit(limit).all() 100 | result = [engine.as_dict() for engine in engines] 101 | for i in xrange(len(engines)): 102 | result[i]['options'] = engines[i].options 103 | result[i]['options'] = [option.as_dict() for option in result[i]['options']] 104 | 105 | return result, 200 106 | 107 | def get(self, id=None): 108 | parser = reqparse.RequestParser() 109 | parser.add_argument('delete', type=int) 110 | parser.add_argument('offset', type=int) 111 | parser.add_argument('limit', type=int) 112 | args = parser.parse_args() 113 | if id is None: 114 | if args['offset'] is not None and args['limit'] is not None: 115 | return self.list(args['offset'], args['limit']) 116 | else: 117 | return self.list() 118 | else: 119 | if args['delete'] == 1: 120 | return self.delete(id) 121 | else: 122 | return self.read(id) 123 | 124 | def post(self, id=None): 125 | parser = reqparse.RequestParser() 126 | if id is None: 127 | parser.add_argument('name', required=True, location='json') 128 | parser.add_argument('path', required=True, location='json') 129 | parser.add_argument('platform_id', required=True, location='json') 130 | parser.add_argument('arch_id', required=True, location='json') 131 | parser.add_argument('options', location='json') 132 | return self.create(parser.parse_args()) 133 | else: 134 | parser.add_argument('name', location='json') 135 | parser.add_argument('path', location='json') 136 | parser.add_argument('platform_id', location='json') 137 | parser.add_argument('arch_id', location='json') 138 | parser.add_argument('options', location='json') 139 | return self.update(id, parser.parse_args()) -------------------------------------------------------------------------------- /controller/Hello.py: -------------------------------------------------------------------------------- 1 | from flask_restful import Resource 2 | 3 | 4 | class HelloCtrl(Resource): 5 | def get(self): 6 | return {'hello': 'world'} 7 | 8 | -------------------------------------------------------------------------------- /controller/Host.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime 2 | from flask_restful import Resource, reqparse 3 | from model.FuzzingHost import FuzzingHost 4 | from model.FuzzingArch import FuzzingArch 5 | from model.FuzzingPlatform import FuzzingPlatform 6 | from controller.Job import JobManager 7 | from app import app 8 | 9 | 10 | db = app.config['db'] 11 | 12 | 13 | class HostCtrl(Resource): 14 | def create(self, data): 15 | platform = FuzzingPlatform.query.filter_by(id=data['platform_id']).first() 16 | if platform is None: 17 | return {"err" : "invalid request"}, 400 18 | 19 | arch = FuzzingArch.query.filter_by(id=data['arch_id']).first() 20 | if platform is None: 21 | return {"err" : "invalid request"}, 400 22 | 23 | try: 24 | host = FuzzingHost(data['name'], data['mac'], data['ip'], platform, arch) 25 | db.session.add(host) 26 | db.session.commit() 27 | except Exception as e: 28 | return {"err" : "invalid request"}, 400 29 | 30 | return host.as_dict(), 201 31 | 32 | def read(self, id, by=None): 33 | if by == 'mac': 34 | host = FuzzingHost.query.filter_by(mac=id).first() 35 | else: 36 | host = FuzzingHost.query.filter_by(id=id).first() 37 | if host is None: 38 | return {"err": "not found"}, 404 39 | return host.as_dict(), 200 40 | 41 | def update(self, id, data): 42 | host = FuzzingHost.query.filter_by(id=id).first() 43 | if host is None: 44 | return {"err": "not found"}, 404 45 | 46 | host.updated_at = datetime.now() 47 | 48 | if data['name'] is not None: 49 | host.name = data['name'] 50 | 51 | if data['mac'] is not None: 52 | host.mac = data['mac'] 53 | 54 | if data['ip'] is not None: 55 | host.ip = data['ip'] 56 | 57 | platform = FuzzingPlatform.query.filter_by(id=data['platform_id']).first() 58 | if platform is not None: 59 | host.platform = platform 60 | 61 | arch = FuzzingArch.query.filter_by(id=data['arch_id']).first() 62 | if arch is not None: 63 | host.arch = arch 64 | 65 | try: 66 | db.session.commit() 67 | except Exception as e: 68 | return {"err": "invalid request"}, 400 69 | 70 | return host.as_dict(), 201 71 | 72 | def delete(self, id): 73 | host = FuzzingHost.query.filter_by(id=id).first() 74 | if host is None: 75 | return {"err": "not found"}, 404 76 | try: 77 | db.session.delete(host) 78 | db.session.commit() 79 | except Exception as e: 80 | return {"err": "invalid request"}, 400 81 | 82 | return {"msg" : "record removed successfully"}, 201 83 | 84 | def list(self, offset=0, limit=10000): 85 | hosts = FuzzingHost.query.offset(offset).limit(limit).all() 86 | hosts = [h.as_dict() for h in hosts ] 87 | JobManager.assign_jobs() 88 | return { 'serverTime': str(datetime.now()), 'hosts' : hosts } , 200 89 | 90 | def get(self, id=None): 91 | parser = reqparse.RequestParser() 92 | parser.add_argument('delete', type=int) 93 | parser.add_argument('offset', type=int) 94 | parser.add_argument('limit', type=int) 95 | parser.add_argument('mac', type=int) 96 | args = parser.parse_args() 97 | if id is None: 98 | if args['offset'] is not None and args['limit'] is not None: 99 | return self.list(args['offset'], args['limit']) 100 | else: 101 | return self.list() 102 | else: 103 | if args['delete'] == 1: 104 | return self.delete(id) 105 | elif args['mac'] == 1: 106 | return self.read(id, 'mac') 107 | else: 108 | return self.read(id) 109 | 110 | def post(self, id=None): 111 | parser = reqparse.RequestParser() 112 | if id is None: 113 | parser.add_argument('name', required=True, location='json') 114 | parser.add_argument('mac', required=True, location='json') 115 | parser.add_argument('ip', required=True, location='json') 116 | parser.add_argument('platform_id', required=True, location='json') 117 | parser.add_argument('arch_id', required=True, location='json') 118 | return self.create(parser.parse_args()) 119 | else: 120 | parser.add_argument('name', location='json') 121 | parser.add_argument('mac', location='json') 122 | parser.add_argument('ip', location='json') 123 | parser.add_argument('platform_id', location='json') 124 | parser.add_argument('arch_id', location='json') 125 | return self.update(id, parser.parse_args()) -------------------------------------------------------------------------------- /controller/Job.py: -------------------------------------------------------------------------------- 1 | from flask_restful import Resource, reqparse 2 | 3 | from model import FuzzingJobOption 4 | from model.FuzzingJobOption import FuzzingJobOption 5 | from model.FuzzingJob import FuzzingJob 6 | from model.FuzzingOption import FuzzingOption 7 | from model.FuzzingJobState import FuzzingJobState 8 | from model.FuzzingEngine import FuzzingEngine 9 | from model.FuzzingTarget import FuzzingTarget 10 | from model.FuzzingHost import FuzzingHost 11 | from app import app 12 | import traceback 13 | 14 | db = app.config['db'] 15 | 16 | class JobCtrl(Resource): 17 | def create(self, data): 18 | try: 19 | job_state= FuzzingJobState.query.filter_by(id=data['state_id']).first() 20 | if job_state is None: 21 | return {"err": "invalid request"}, 400 22 | 23 | engine = FuzzingEngine.query.filter_by(id=data['engine_id']).first() 24 | if engine is None: 25 | return {"err": "invalid request"}, 400 26 | 27 | target = FuzzingTarget.query.filter_by(id=data['target_id']).first() 28 | if target is None: 29 | return {"err": "invalid request"}, 400 30 | 31 | 32 | job = FuzzingJob(data['name'], job_state, engine, target) 33 | 34 | host = FuzzingHost.query.filter_by(id=data['host_id']).first() 35 | if host is not None: 36 | job.host = host 37 | 38 | db.session.add(job) 39 | if data['options'] is not None: 40 | for o in data['options']: 41 | option = FuzzingOption.query.filter_by(id=o['id']).first() 42 | job_opt = FuzzingJobOption(job, option, o['value']) 43 | try: 44 | db.session.add(job_opt) 45 | 46 | except Exception as e: 47 | return {"err": "invalid request", "e" : e.message, 'x': 1}, 400 48 | 49 | if data['output'] is not None: 50 | job.output = data['output'] 51 | 52 | db.session.commit() 53 | except Exception as e: 54 | return {"err" : "invalid request", "e" : e.message}, 400 55 | 56 | options = FuzzingJobOption.query.filter_by(job_id=job.id).all() 57 | result = job.as_dict() 58 | result['options'] = [opt.as_dict() for opt in options] 59 | JobManager.assign_jobs() 60 | return result, 201 61 | 62 | def read(self, id, by): 63 | if by == 'host': 64 | job = FuzzingJob.query.filter_by(host_id=id).first() 65 | else: 66 | job = FuzzingJob.query.filter_by(id=id).first() 67 | if job is None: 68 | return {"err": "not found"}, 404 69 | options = FuzzingJobOption.query.filter_by(job_id=job.id).all() 70 | result = job.as_dict() 71 | result['options'] = [opt.as_dict() for opt in options] 72 | JobManager.assign_jobs() 73 | return result, 200 74 | 75 | def update(self, id, data): 76 | job = FuzzingJob.query.filter_by(id=id).first() 77 | if job is None: 78 | return {"err": "not found"}, 404 79 | 80 | if data['name'] is not None: 81 | job.name = data['name'] 82 | 83 | if data['output'] is not None: 84 | job.output = data['output'] 85 | 86 | job_state = FuzzingJobState.query.filter_by(id=data['state_id']).first() 87 | if job_state is not None: 88 | job.state = job_state 89 | 90 | engine = FuzzingEngine.query.filter_by(id=data['engine_id']).first() 91 | if engine is not None: 92 | job.engine = engine 93 | 94 | target = FuzzingTarget.query.filter_by(id=data['target_id']).first() 95 | if target is not None: 96 | job.target = target 97 | 98 | host = FuzzingHost.query.filter_by(id=data['host_id']).first() 99 | if host is not None: 100 | job.host = host 101 | 102 | try: 103 | db.session.commit() 104 | except Exception as e: 105 | return {"err": "invalid request"}, 400 106 | 107 | JobManager.assign_jobs() 108 | return job.as_dict(), 201 109 | 110 | def delete(self, id): 111 | job = FuzzingJob.query.filter_by(id=id).first() 112 | if job is None: 113 | return {"err": "not found"}, 404 114 | try: 115 | db.session.delete(job) 116 | options = FuzzingJobOption.query.filter_by(job_id=id).all() 117 | for opt in options: 118 | db.session.delete(opt) 119 | db.session.commit() 120 | except Exception as e: 121 | return {"err": "invalid request"}, 400 122 | 123 | JobManager.assign_jobs() 124 | return {"msg" : "record removed successfully"}, 201 125 | 126 | def list(self, offset=0, limit=10000): 127 | jobs = FuzzingJob.query.offset(offset).limit(limit).all() 128 | result = [job.as_dict() for job in jobs] 129 | for i in xrange(len(jobs)): 130 | options = FuzzingJobOption.query.filter_by(job_id=jobs[i].id).all() 131 | result[i]['options'] = [option.as_dict() for option in options] 132 | 133 | JobManager.assign_jobs() 134 | return result, 200 135 | 136 | def state(self, offset=0, limit=10000): 137 | states = FuzzingJobState.query.offset(offset).limit(limit).all() 138 | states = [job.as_dict() for job in states] 139 | JobManager.assign_jobs() 140 | return states , 200 141 | 142 | def get(self, id=None): 143 | parser = reqparse.RequestParser() 144 | parser.add_argument('delete', type=int) 145 | parser.add_argument('offset', type=int) 146 | parser.add_argument('limit', type=int) 147 | parser.add_argument('state', type=int) 148 | parser.add_argument('host', type=int) 149 | args = parser.parse_args() 150 | if id is None: 151 | if args['offset'] is not None and args['limit'] is not None: 152 | return self.list(args['offset'], args['limit']) 153 | elif args['state'] == 1: 154 | return self.state() 155 | else: 156 | return self.list() 157 | else: 158 | if args['delete'] == 1: 159 | return self.delete(id) 160 | elif args['host'] == 1: 161 | return self.read(id, 'host') 162 | else: 163 | return self.read(id) 164 | 165 | def post(self, id=None): 166 | parser = reqparse.RequestParser() 167 | if id is None: 168 | parser.add_argument('name', required=True, location='json') 169 | parser.add_argument('state_id', required=True, location='json') 170 | parser.add_argument('engine_id', required=True, location='json') 171 | parser.add_argument('target_id', required=True, location='json') 172 | parser.add_argument('options', type=list, location='json') 173 | parser.add_argument('host_id', location='json') 174 | parser.add_argument('output', location='json') 175 | return self.create(parser.parse_args()) 176 | else: 177 | parser.add_argument('name', location='json') 178 | parser.add_argument('state_id', location='json') 179 | parser.add_argument('engine_id', location='json') 180 | parser.add_argument('target_id', location='json') 181 | parser.add_argument('options', type=list, location='json') 182 | parser.add_argument('host_id', location='json') 183 | parser.add_argument('output', location='json') 184 | return self.update(id, parser.parse_args()) 185 | 186 | 187 | class JobManager(): 188 | @staticmethod 189 | def assign_jobs(): 190 | jobs = FuzzingJob.query.all() 191 | hosts = FuzzingHost.query.all() 192 | for job in jobs: 193 | state_name = job.state.name 194 | if state_name == 'Queued': 195 | for host in hosts: 196 | if len(host.jobs) == 0 \ 197 | and host.platform_id == job.engine.platform_id \ 198 | and host.arch_id == job.engine.arch_id \ 199 | and host.platform_id == job.target.platform_id \ 200 | and host.arch_id == job.target.arch_id: 201 | job.host = host 202 | job.state = FuzzingJobState.query.filter_by(name='Allocated').first() 203 | db.session.commit() 204 | 205 | elif state_name == 'Failed': 206 | job.host = None 207 | db.session.commit() 208 | 209 | -------------------------------------------------------------------------------- /controller/Log.py: -------------------------------------------------------------------------------- 1 | from flask_restful import Resource, reqparse 2 | from app import app 3 | from app import logFile 4 | 5 | db = app.config['db'] 6 | 7 | 8 | class LogCtrl(Resource): 9 | def post(self): 10 | parser = reqparse.RequestParser() 11 | parser.add_argument('message', required=True, location='json') 12 | args = parser.parse_args() 13 | with open(logFile, 'a') as f: 14 | f.write(args['message']) 15 | 16 | return { "msg" : "log created successfully" }, 201 17 | 18 | def get(self): 19 | try: 20 | log = open(logFile, 'r').read() 21 | except: 22 | return {"err": "not found"}, 404 23 | 24 | return { "log" : log }, 200 -------------------------------------------------------------------------------- /controller/Option.py: -------------------------------------------------------------------------------- 1 | from flask_restful import Resource, reqparse 2 | from model.FuzzingOptionType import FuzzingOptionType 3 | from model.FuzzingOption import FuzzingOption 4 | from app import app 5 | 6 | db = app.config['db'] 7 | 8 | 9 | class OptionCtrl(Resource): 10 | def create(self, data): 11 | try: 12 | option_type = FuzzingOptionType.query.filter_by(id=data['option_type_id']).first() 13 | if option_type is None: 14 | return {"err": "invalid request"}, 400 15 | opt = FuzzingOption(data['name'], option_type) 16 | db.session.add(opt) 17 | db.session.commit() 18 | except Exception as e: 19 | return {"err" : "invalid request"}, 400 20 | 21 | return opt.as_dict(), 201 22 | 23 | def read(self, id): 24 | opt = FuzzingOption.query.filter_by(id=id).first() 25 | if opt is None: 26 | return {"err": "not found"}, 404 27 | return opt.as_dict(), 200 28 | 29 | def update(self, id, data): 30 | opt = FuzzingOption.query.filter_by(id=id).first() 31 | if opt is None: 32 | return {"err": "not found"}, 404 33 | 34 | if data['name'] is not None: 35 | opt.name = data['name'] 36 | 37 | option_type = FuzzingOptionType.query.filter_by(id=data['option_type_id']).first() 38 | if option_type is not None: 39 | opt.type = option_type 40 | 41 | try: 42 | db.session.commit() 43 | except Exception as e: 44 | return {"err": "invalid request"}, 400 45 | 46 | return opt.as_dict(), 201 47 | 48 | def delete(self, id): 49 | opt = FuzzingOption.query.filter_by(id=id).first() 50 | if opt is None: 51 | return {"err": "not found"}, 404 52 | try: 53 | db.session.delete(opt) 54 | db.session.commit() 55 | except Exception as e: 56 | return {"err": "invalid request"}, 400 57 | 58 | return {"msg" : "record removed successfully"}, 201 59 | 60 | def list(self, offset=0, limit=10000): 61 | opts = FuzzingOption.query.offset(offset).limit(limit).all() 62 | opts = [arch.as_dict() for arch in opts] 63 | return opts , 200 64 | 65 | def type(self): 66 | types = FuzzingOptionType.query.all() 67 | types = [arch.as_dict() for arch in types] 68 | return types, 200 69 | 70 | def get(self, id=None): 71 | parser = reqparse.RequestParser() 72 | parser.add_argument('delete', type=int) 73 | parser.add_argument('type', type=int) 74 | parser.add_argument('offset', type=int) 75 | parser.add_argument('limit', type=int) 76 | args = parser.parse_args() 77 | if id is None: 78 | if args['offset'] is not None and args['limit'] is not None: 79 | return self.list(args['offset'], args['limit']) 80 | elif args['type'] == 1: 81 | return self.type() 82 | else: 83 | return self.list() 84 | else: 85 | if args['delete'] == 1: 86 | return self.delete(id) 87 | else: 88 | return self.read(id) 89 | 90 | def post(self, id=None): 91 | parser = reqparse.RequestParser() 92 | if id is None: 93 | parser.add_argument('name', required=True, location='json') 94 | parser.add_argument('option_type_id', required=True, location='json') 95 | return self.create(parser.parse_args()) 96 | else: 97 | parser.add_argument('name', location='json') 98 | parser.add_argument('option_type_id', location='json') 99 | return self.update(id, parser.parse_args()) -------------------------------------------------------------------------------- /controller/Platform.py: -------------------------------------------------------------------------------- 1 | from flask_restful import Resource, reqparse 2 | 3 | from model import FuzzingPlatform 4 | from model.FuzzingPlatform import FuzzingPlatform 5 | from app import app 6 | 7 | db = app.config['db'] 8 | 9 | class PlatformCtrl(Resource): 10 | def create(self, data): 11 | try: 12 | platform = FuzzingPlatform(data['name']) 13 | db.session.add(platform) 14 | db.session.commit() 15 | except Exception as e: 16 | return {"err" : "invalid request"}, 400 17 | 18 | return platform.as_dict(), 201 19 | 20 | def read(self, id): 21 | platform = FuzzingPlatform.query.filter_by(id=id).first() 22 | if platform is None: 23 | return {"err": "not found"}, 404 24 | return platform.as_dict(), 200 25 | 26 | def update(self, id, data): 27 | platform = FuzzingPlatform.query.filter_by(id=id).first() 28 | if platform is None: 29 | return {"err": "not found"}, 404 30 | 31 | if data['name'] is not None: 32 | platform.name = data['name'] 33 | 34 | try: 35 | db.session.commit() 36 | except Exception as e: 37 | return {"err": "invalid request"}, 400 38 | 39 | return platform.as_dict(), 201 40 | 41 | def delete(self, id): 42 | platform = FuzzingPlatform.query.filter_by(id=id).first() 43 | if platform is None: 44 | return {"err": "not found"}, 404 45 | try: 46 | db.session.delete(platform) 47 | db.session.commit() 48 | except Exception as e: 49 | return {"err": "invalid request"}, 400 50 | 51 | return {"msg" : "record removed successfully"}, 201 52 | 53 | def list(self, offset=0, limit=10000): 54 | platforms = FuzzingPlatform.query.offset(offset).limit(limit).all() 55 | platforms = [plat.as_dict() for plat in platforms] 56 | return platforms, 200 57 | 58 | def get(self, id=None): 59 | parser = reqparse.RequestParser() 60 | parser.add_argument('delete', type=int) 61 | parser.add_argument('offset', type=int) 62 | parser.add_argument('limit', type=int) 63 | args = parser.parse_args() 64 | if id is None: 65 | if args['offset'] is not None and args['limit'] is not None: 66 | return self.list(args['offset'], args['limit']) 67 | else: 68 | return self.list() 69 | else: 70 | if args['delete'] == 1: 71 | return self.delete(id) 72 | else: 73 | return self.read(id) 74 | 75 | def post(self, id=None): 76 | parser = reqparse.RequestParser() 77 | if id is None: 78 | parser.add_argument('name', required=True, location='json') 79 | return self.create(parser.parse_args()) 80 | else: 81 | parser.add_argument('name', location='json') 82 | return self.update(id, parser.parse_args()) -------------------------------------------------------------------------------- /controller/Script.py: -------------------------------------------------------------------------------- 1 | from flask_restful import Resource, reqparse 2 | from model.FuzzingScript import FuzzingScript 3 | from app import app 4 | 5 | db = app.config['db'] 6 | 7 | class ScriptCtrl(Resource): 8 | def create(self, data): 9 | try: 10 | if data['visible'] is not None: 11 | script = FuzzingScript(data['name'], data['script'], data['visible']) 12 | else: 13 | script = FuzzingScript(data['name'], data['script']) 14 | db.session.add(script) 15 | db.session.commit() 16 | except Exception as e: 17 | return {"err" : "invalid request"}, 400 18 | 19 | return script.as_dict(), 201 20 | 21 | def read(self, id): 22 | script = FuzzingScript.query.filter_by(id=id).first() 23 | if script is None: 24 | return {"err": "not found"}, 404 25 | return script.as_dict(), 200 26 | 27 | def update(self, id, data): 28 | script = FuzzingScript.query.filter_by(id=id).first() 29 | if script is None: 30 | return {"err": "not found"}, 404 31 | 32 | if data['name'] is not None: 33 | script.name = data['name'] 34 | 35 | if data['script'] is not None: 36 | script.script = data['script'] 37 | 38 | if data['visible'] is not None: 39 | script.visible = data['visible'] 40 | 41 | try: 42 | db.session.commit() 43 | except Exception as e: 44 | return {"err": "invalid request"}, 400 45 | 46 | return script.as_dict(), 201 47 | 48 | def delete(self, id): 49 | script = FuzzingScript.query.filter_by(id=id).first() 50 | if script is None: 51 | return {"err": "not found"}, 404 52 | try: 53 | db.session.delete(script) 54 | db.session.commit() 55 | except Exception as e: 56 | return {"err": "invalid request"}, 400 57 | 58 | return {"msg" : "record removed successfully"}, 201 59 | 60 | def list(self, offset=0, limit=10000): 61 | scripts = FuzzingScript.query.offset(offset).limit(limit).all() 62 | scripts = [s.as_dict() for s in scripts] 63 | return scripts, 200 64 | 65 | def get(self, id=None): 66 | parser = reqparse.RequestParser() 67 | parser.add_argument('delete', type=int) 68 | parser.add_argument('offset', type=int) 69 | parser.add_argument('limit', type=int) 70 | args = parser.parse_args() 71 | if id is None: 72 | if args['offset'] is not None and args['limit'] is not None: 73 | return self.list(args['offset'], args['limit']) 74 | else: 75 | return self.list() 76 | else: 77 | if args['delete'] == 1: 78 | return self.delete(id) 79 | else: 80 | return self.read(id) 81 | 82 | def post(self, id=None): 83 | parser = reqparse.RequestParser() 84 | if id is None: 85 | parser.add_argument('name', required=True, location='json') 86 | parser.add_argument('script', required=True, location='json') 87 | parser.add_argument('visible', type=int, location='json') 88 | return self.create(parser.parse_args()) 89 | else: 90 | parser.add_argument('name', location='json') 91 | parser.add_argument('script', location='json') 92 | parser.add_argument('visible', type=int, location='json') 93 | return self.update(id, parser.parse_args()) -------------------------------------------------------------------------------- /controller/Status.py: -------------------------------------------------------------------------------- 1 | from flask_restful import Resource 2 | from datetime import datetime 3 | from model.FuzzingJobState import FuzzingJobState 4 | from model.FuzzingJob import FuzzingJob 5 | from model.FuzzingHost import FuzzingHost 6 | from model.FuzzingCrash import FuzzingCrash 7 | 8 | class StatusCtrl(Resource): 9 | def get(self): 10 | status_active = FuzzingJobState.query.filter_by(name='Active').first() 11 | status_completed = FuzzingJobState.query.filter_by(name='Completed').first() 12 | status_queued = FuzzingJobState.query.filter_by(name='Queued').first() 13 | 14 | total_job_count = FuzzingJob.query.count() 15 | active_job_count = FuzzingJob.query.filter_by(state_id=status_active.id).count() 16 | completed_job_count = FuzzingJob.query.filter_by(state_id=status_completed.id).count() 17 | queued_job_count = FuzzingJob.query.filter_by(state_id=status_queued.id).count() 18 | crash_count = FuzzingCrash.query.count() 19 | node_count = FuzzingHost.query.count() 20 | return { 21 | 'total_job_count': total_job_count, 22 | 'active_job_count': active_job_count, 23 | 'completed_job_count': completed_job_count, 24 | 'queued_job_count': queued_job_count, 25 | 'crash_count': crash_count, 26 | 'node_count': node_count, 27 | 'serverTime' : str(datetime.now()) 28 | }, 200 29 | -------------------------------------------------------------------------------- /controller/Target.py: -------------------------------------------------------------------------------- 1 | from flask_restful import Resource, reqparse 2 | from model.FuzzingTarget import FuzzingTarget 3 | from model.FuzzingArch import FuzzingArch 4 | from model.FuzzingPlatform import FuzzingPlatform 5 | #from model.FuzzingConfig import FuzzingConfig 6 | from app import app 7 | 8 | 9 | db = app.config['db'] 10 | 11 | 12 | class TargetCtrl(Resource): 13 | def create(self, data): 14 | platform = FuzzingPlatform.query.filter_by(id=data['platform_id']).first() 15 | if platform is None: 16 | return {"err": "invalid request"}, 400 17 | 18 | arch = FuzzingArch.query.filter_by(id=data['arch_id']).first() 19 | if platform is None: 20 | return {"err": "invalid request"}, 400 21 | 22 | try: 23 | target = FuzzingTarget(data['name'], data['path'], platform, arch) 24 | if data['visible'] is not None: 25 | target.visible = data['visible'] 26 | 27 | db.session.add(target) 28 | db.session.commit() 29 | except Exception as e: 30 | return {"err" : "invalid request"}, 400 31 | 32 | return target.as_dict(), 201 33 | 34 | def read(self, id): 35 | target = FuzzingTarget.query.filter_by(id=id).first() 36 | if target is None: 37 | return {"err": "not found"}, 404 38 | return target.as_dict(), 200 39 | 40 | def update(self, id, data): 41 | target = FuzzingTarget.query.filter_by(id=id).first() 42 | if target is None: 43 | return {"err": "not found"}, 404 44 | 45 | if data['name'] is not None: 46 | target.name = data['name'] 47 | 48 | if data['path'] is not None: 49 | target.path = data['path'] 50 | 51 | if data['visible'] is not None: 52 | target.visible = data['visible'] 53 | 54 | platform = FuzzingPlatform.query.filter_by(id=data['platform_id']).first() 55 | if platform is not None: 56 | target.platform = platform 57 | 58 | arch = FuzzingArch.query.filter_by(id=data['arch_id']).first() 59 | if arch is not None: 60 | target.arch = arch 61 | 62 | try: 63 | db.session.commit() 64 | except Exception as e: 65 | return {"err": "invalid request"}, 400 66 | 67 | return target.as_dict(), 201 68 | 69 | def delete(self, id): 70 | target = FuzzingTarget.query.filter_by(id=id).first() 71 | if target is None: 72 | return {"err": "not found"}, 404 73 | try: 74 | db.session.delete(target) 75 | db.session.commit() 76 | except Exception as e: 77 | return {"err": "invalid request"}, 400 78 | 79 | return {"msg" : "record removed successfully"}, 201 80 | 81 | def list(self, offset=0, limit=10000): 82 | targets = FuzzingTarget.query.offset(offset).limit(limit).all() 83 | targets = [t.as_dict() for t in targets] 84 | return targets, 200 85 | 86 | def get(self, id=None): 87 | parser = reqparse.RequestParser() 88 | parser.add_argument('delete', type=int) 89 | parser.add_argument('offset', type=int) 90 | parser.add_argument('limit', type=int) 91 | args = parser.parse_args() 92 | if id is None: 93 | if args['offset'] is not None and args['limit'] is not None: 94 | return self.list(args['offset'], args['limit']) 95 | else: 96 | return self.list() 97 | else: 98 | if args['delete'] == 1: 99 | return self.delete(id) 100 | else: 101 | return self.read(id) 102 | 103 | def post(self, id=None): 104 | parser = reqparse.RequestParser() 105 | if id is None: 106 | parser.add_argument('name', required=True, location='json') 107 | parser.add_argument('path', required=True, location='json') 108 | parser.add_argument('platform_id', required=True, location='json') 109 | parser.add_argument('arch_id', required=True, location='json') 110 | parser.add_argument('visible', type=int, location='json') 111 | return self.create(parser.parse_args()) 112 | else: 113 | parser.add_argument('name', location='json') 114 | parser.add_argument('path', location='json') 115 | parser.add_argument('visible', type=int, location='json') 116 | parser.add_argument('platform_id', location='json') 117 | parser.add_argument('arch_id', location='json') 118 | return self.update(id, parser.parse_args()) 119 | 120 | -------------------------------------------------------------------------------- /controller/Update.py: -------------------------------------------------------------------------------- 1 | from flask_restful import Resource 2 | from flask import request 3 | from app.config import CLIENT_FOLDER 4 | import os, zipfile, hashlib 5 | 6 | class UpdateCtrl(Resource): 7 | def get(self, hash): 8 | path = 'static' + os.sep + 'client.zip' 9 | try: 10 | os.remove(path) 11 | except: 12 | None 13 | zip = zipfile.ZipFile(path, 'w', zipfile.ZIP_DEFLATED) 14 | for root, dirs, files in os.walk(CLIENT_FOLDER): 15 | for f in files: 16 | zip.write(os.path.join(root, f)) 17 | zip.close() 18 | 19 | client = open(path).read() 20 | 21 | if hash == hashlib.md5(client).hexdigest(): 22 | return {"err": "invalid request"}, 400 23 | else: 24 | return {"url": request.url_root + path}, 200 25 | 26 | -------------------------------------------------------------------------------- /controller/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/talos-vulndev/FuzzFlow/3f2de617c3658904f9640f53a05d5e4e90634e32/controller/__init__.py -------------------------------------------------------------------------------- /controller/_deprecated_Config.py: -------------------------------------------------------------------------------- 1 | from flask_restful import Resource, reqparse 2 | #from model.FuzzingConfig import FuzzingConfig 3 | from model.FuzzingPlatform import FuzzingPlatform 4 | from model.FuzzingArch import FuzzingArch 5 | from model.FuzzingHost import FuzzingHost 6 | from model.FuzzingEngine import FuzzingEngine 7 | from model.FuzzingTarget import FuzzingTarget 8 | from controller.Job import JobManager 9 | from app import app 10 | 11 | db = app.config['db'] 12 | 13 | 14 | class ConfigCtrl(Resource): 15 | def create(self, data): 16 | host = FuzzingHost.query.filter_by(id=data['host_id']).first() 17 | if host is None: 18 | return {"err": "invalid request"}, 400 19 | 20 | arch = FuzzingArch.query.filter_by(id=data['arch_id']).first() 21 | if arch is None: 22 | return {"err": "invalid request"}, 400 23 | 24 | platform = FuzzingPlatform.query.filter_by(id=data['platform_id']).first() 25 | if platform is None: 26 | return {"err": "invalid request"}, 400 27 | 28 | try: 29 | config = FuzzingConfig(host, arch, platform) 30 | if data['engines'] is not None: 31 | for engine_id in data['engines']: 32 | engine = FuzzingEngine.query.filter_by(id=engine_id).first() 33 | if engine is not None: 34 | config.engines.append(engine) 35 | 36 | if data['targets'] is not None: 37 | for target_id in data['targets']: 38 | target = FuzzingTarget.query.filter_by(id=target_id).first() 39 | if target is not None: 40 | config.targets.append(target) 41 | 42 | db.session.add(config) 43 | db.session.commit() 44 | 45 | except Exception as e: 46 | return {"err" : "invalid request"}, 400 47 | 48 | result = config.as_dict() 49 | result['engines'] = [engine.as_dict() for engine in config.engines.all()] 50 | result['targets'] = [target.as_dict() for target in config.targets.all()] 51 | return result, 201 52 | 53 | def read(self, id, by=None): 54 | if by == 'host': 55 | config = FuzzingConfig.query.filter_by(host_id=id).first() 56 | else: 57 | config = FuzzingConfig.query.filter_by(id=id).first() 58 | if config is None: 59 | return {"err": "not found"}, 404 60 | 61 | result = config.as_dict() 62 | result['engines'] = [engine.as_dict() for engine in config.engines.all()] 63 | result['targets'] = [target.as_dict() for target in config.targets.all()] 64 | return result, 200 65 | 66 | def update(self, id, data): 67 | config = FuzzingConfig.query.filter_by(id=id).first() 68 | if config is None: 69 | return {"err": "not found"}, 404 70 | 71 | platform = FuzzingPlatform.query.filter_by(id=data['platform_id']).first() 72 | if platform is not None: 73 | config.platform = platform 74 | 75 | arch = FuzzingArch.query.filter_by(id=data['arch_id']).first() 76 | if arch is not None: 77 | config.arch = arch 78 | 79 | host = FuzzingHost.query.filter_by(id=data['host_id']).first() 80 | if host is not None: 81 | config.host = host 82 | 83 | if data['engines'] is not None: 84 | config.engines = [] 85 | for engine_id in data['engines']: 86 | engine = FuzzingEngine.query.filter_by(id=engine_id).first() 87 | if engine is not None: 88 | config.engines.append(engine) 89 | 90 | if data['targets'] is not None: 91 | config.targets = [] 92 | for target_id in data['targets']: 93 | target = FuzzingTarget.query.filter_by(id=target_id).first() 94 | if target is not None: 95 | config.targets.append(target) 96 | 97 | try: 98 | db.session.commit() 99 | except Exception as e: 100 | return {"err": "invalid request"}, 400 101 | 102 | result = config.as_dict() 103 | result['engines'] = [engine.as_dict() for engine in config.engines.all()] 104 | result['targets'] = [target.as_dict() for target in config.targets.all()] 105 | return result, 201 106 | 107 | def delete(self, id): 108 | config = FuzzingConfig.query.filter_by(id=id).first() 109 | if config is None: 110 | return {"err": "not found"}, 404 111 | try: 112 | db.session.delete(config) 113 | db.session.commit() 114 | except Exception as e: 115 | return {"err": "invalid request"}, 400 116 | 117 | return {"msg" : "record removed successfully"}, 201 118 | 119 | def list(self, offset=0, limit=10000): 120 | configs = FuzzingConfig.query.offset(offset).limit(limit).all() 121 | configs = [conf.as_dict() for conf in configs] 122 | JobManager.assign_jobs() 123 | return configs, 200 124 | 125 | def get(self, id=None): 126 | parser = reqparse.RequestParser() 127 | parser.add_argument('delete', type=int) 128 | parser.add_argument('offset', type=int) 129 | parser.add_argument('limit', type=int) 130 | parser.add_argument('host', type=int) 131 | 132 | args = parser.parse_args() 133 | if id is None: 134 | if args['offset'] is not None and args['limit'] is not None: 135 | return self.list(args['offset'], args['limit']) 136 | else: 137 | return self.list() 138 | else: 139 | if args['delete'] == 1: 140 | return self.delete(id) 141 | elif args['host'] == 1: 142 | return self.read(id, 'host') 143 | else: 144 | return self.read(id) 145 | 146 | def post(self, id=None): 147 | parser = reqparse.RequestParser() 148 | if id is None: 149 | parser.add_argument('host_id', required=True, location='json') 150 | parser.add_argument('arch_id', required=True, location='json') 151 | parser.add_argument('platform_id', required=True, location='json') 152 | parser.add_argument('engines', location='json') 153 | parser.add_argument('targets', location='json') 154 | return self.create(parser.parse_args()) 155 | else: 156 | parser.add_argument('host_id', location='json') 157 | parser.add_argument('arch_id', location='json') 158 | parser.add_argument('platform_id', location='json') 159 | parser.add_argument('engines', location='json') 160 | parser.add_argument('targets', location='json') 161 | return self.update(id, parser.parse_args()) -------------------------------------------------------------------------------- /model/CallbackScript.py: -------------------------------------------------------------------------------- 1 | from app import app 2 | 3 | db = app.config['db'] 4 | 5 | 6 | class CallbackScript(db.Model): 7 | id = db.Column(db.Integer, primary_key=True) 8 | name = db.Column(db.String(256), unique=True, nullable=False) 9 | path = db.Column(db.String(1024), unique=True, nullable=False) 10 | visible = db.Column(db.Integer, default=1, nullable=False) 11 | 12 | def __init__(self, name, path): 13 | self.name = name 14 | self.path = path 15 | 16 | def as_dict(self): 17 | return {c.name: str(getattr(self, c.name)) for c in self.__table__.columns} -------------------------------------------------------------------------------- /model/FuzzingArch.py: -------------------------------------------------------------------------------- 1 | from app import app 2 | 3 | db = app.config['db'] 4 | 5 | 6 | class FuzzingArch(db.Model): 7 | id = db.Column(db.Integer, primary_key=True) 8 | name = db.Column(db.String(100), unique=True) 9 | 10 | def __init__(self, name): 11 | self.name = name 12 | 13 | def as_dict(self): 14 | return {c.name: str(getattr(self, c.name)) for c in self.__table__.columns} -------------------------------------------------------------------------------- /model/FuzzingCrash.py: -------------------------------------------------------------------------------- 1 | from app import app 2 | from datetime import * 3 | 4 | db = app.config['db'] 5 | 6 | ''' 7 | For simplicity I am just using one simple model for each crash and not using the previous db schema. We may want to move 8 | the crash system to a bugzilla friendly format. Based on the decision on the design of crash analysis (clientside or serverside) 9 | , improvement could be made. 10 | ''' 11 | 12 | 13 | class FuzzingCrash(db.Model): 14 | id = db.Column(db.Integer, primary_key=True) 15 | job_id = db.Column(db.Integer, db.ForeignKey('fuzzing_job.id')) 16 | job = db.relationship('FuzzingJob', foreign_keys=job_id) 17 | repro_file = db.Column(db.String(512), nullable=False) 18 | dump_file = db.Column(db.String(512), nullable=False) 19 | dbg_file = db.Column(db.String(512), nullable=False) 20 | created_at = db.Column(db.DateTime, 21 | nullable=False, 22 | default=datetime.utcnow) 23 | updated_at = db.Column(db.DateTime, 24 | nullable=False, 25 | default=datetime.utcnow, 26 | onupdate=datetime.utcnow) 27 | 28 | def __init__(self, job, repro): 29 | self.job = job 30 | self.repro_file = repro 31 | 32 | 33 | def as_dict(self): 34 | return {c.name: str(getattr(self, c.name)) for c in self.__table__.columns} -------------------------------------------------------------------------------- /model/FuzzingEngine.py: -------------------------------------------------------------------------------- 1 | from app import app 2 | from datetime import * 3 | 4 | 5 | db = app.config['db'] 6 | 7 | 8 | fuzzing_engine_option = db.Table('fuzzing_engine_option', 9 | db.Column('option_id', db.Integer, db.ForeignKey('fuzzing_option.id')), 10 | db.Column('engine_id', db.Integer, db.ForeignKey('fuzzing_engine.id')) 11 | ) 12 | 13 | # fuzzing_engine_config = db.Table('fuzzing_engine_config', 14 | # db.Column('config_id', db.Integer, db.ForeignKey('fuzzing_config.id')), 15 | # db.Column('engine_id', db.Integer, db.ForeignKey('fuzzing_engine.id')) 16 | # ) 17 | 18 | 19 | class FuzzingEngine(db.Model): 20 | id = db.Column(db.Integer, primary_key=True) 21 | name = db.Column(db.String(256), unique=True, nullable=False) 22 | path = db.Column(db.String(1024), nullable=False) 23 | visible = db.Column(db.Integer, default=1, nullable=False) 24 | platform_id = db.Column(db.Integer, db.ForeignKey('fuzzing_platform.id')) 25 | platform = db.relationship('FuzzingPlatform', foreign_keys=platform_id) 26 | arch_id = db.Column(db.Integer, db.ForeignKey('fuzzing_arch.id')) 27 | arch = db.relationship('FuzzingArch', foreign_keys=arch_id) 28 | created_at = db.Column(db.DateTime, 29 | nullable=False, 30 | default=datetime.utcnow) 31 | options = db.relationship('FuzzingOption', secondary=fuzzing_engine_option, 32 | backref=db.backref('engines', lazy='dynamic')) 33 | #configs = db.relationship('FuzzingConfig', secondary=fuzzing_engine_config, 34 | # backref=db.backref('engines', lazy='dynamic')) 35 | 36 | def __init__(self, name, path, platform, arch, options=None): 37 | self.name = name 38 | self.path = path 39 | self.platform = platform 40 | self.arch = arch 41 | if options is not None: 42 | self.options = options 43 | 44 | def as_dict(self): 45 | return {c.name: str(getattr(self, c.name)) for c in self.__table__.columns} 46 | -------------------------------------------------------------------------------- /model/FuzzingHost.py: -------------------------------------------------------------------------------- 1 | from app import app 2 | from datetime import datetime 3 | 4 | db = app.config['db'] 5 | 6 | 7 | class FuzzingHost(db.Model): 8 | id = db.Column(db.Integer, primary_key=True) 9 | name = db.Column(db.String(512), nullable=False) 10 | mac = db.Column(db.String(64), unique=True, nullable=False) 11 | ip = db.Column(db.String(32), nullable=False) 12 | platform_id = db.Column(db.Integer, db.ForeignKey('fuzzing_platform.id')) 13 | platform = db.relationship('FuzzingPlatform', foreign_keys=platform_id) 14 | arch_id = db.Column(db.Integer, db.ForeignKey('fuzzing_arch.id')) 15 | arch = db.relationship('FuzzingArch', foreign_keys=arch_id) 16 | created_at = db.Column(db.DateTime, 17 | nullable=False, 18 | default=datetime.now()) 19 | updated_at = db.Column(db.DateTime, 20 | nullable=False, 21 | default=datetime.now(), 22 | onupdate=datetime.now()) 23 | 24 | def __init__(self, name, mac, ip, platform, arch): 25 | self.name = name 26 | self.mac = mac 27 | self.ip = ip 28 | self.platform = platform 29 | self.arch = arch 30 | 31 | def as_dict(self): 32 | return {c.name: str(getattr(self, c.name)) for c in self.__table__.columns} -------------------------------------------------------------------------------- /model/FuzzingJob.py: -------------------------------------------------------------------------------- 1 | from app import app 2 | from datetime import * 3 | from model.FuzzingJobState import FuzzingJobState 4 | 5 | db = app.config['db'] 6 | 7 | 8 | class FuzzingJob(db.Model): 9 | id = db.Column(db.Integer, primary_key=True) 10 | name = db.Column(db.String(256), unique=True, nullable=False) 11 | output = db.Column(db.String(8192)) 12 | state_id = db.Column(db.Integer, db.ForeignKey('fuzzing_job_state.id')) 13 | state = db.relationship('FuzzingJobState', foreign_keys=state_id) 14 | engine_id = db.Column(db.Integer, db.ForeignKey('fuzzing_engine.id')) 15 | engine = db.relationship('FuzzingEngine', foreign_keys=engine_id) 16 | target_id = db.Column(db.Integer, db.ForeignKey('fuzzing_target.id')) 17 | target = db.relationship('FuzzingTarget', foreign_keys=target_id) 18 | host_id = db.Column(db.Integer, db.ForeignKey('fuzzing_host.id')) 19 | host = db.relationship('FuzzingHost', foreign_keys=host_id, backref='jobs') 20 | 21 | 22 | created_at = db.Column(db.DateTime, 23 | nullable=False, 24 | default=datetime.utcnow) 25 | updated_at = db.Column(db.DateTime, 26 | nullable=False, 27 | default=datetime.utcnow, 28 | onupdate=datetime.utcnow) 29 | 30 | def __init__(self, name, state, engine, target): 31 | self.name = name 32 | self.state = state 33 | self.engine = engine 34 | self.target = target 35 | 36 | def as_dict(self): 37 | return {c.name: str(getattr(self, c.name)) for c in self.__table__.columns} -------------------------------------------------------------------------------- /model/FuzzingJobOption.py: -------------------------------------------------------------------------------- 1 | from app import app 2 | 3 | db = app.config['db'] 4 | 5 | 6 | class FuzzingJobOption(db.Model): 7 | id = db.Column(db.Integer, primary_key=True) 8 | job_id = db.Column(db.Integer, db.ForeignKey('fuzzing_job.id')) 9 | job = db.relationship('FuzzingJob', foreign_keys=job_id) 10 | option_id = db.Column(db.Integer, db.ForeignKey('fuzzing_option.id')) 11 | option = db.relationship('FuzzingOption', foreign_keys=option_id) 12 | value = db.Column(db.String(256), nullable=False) 13 | 14 | 15 | def __init__(self, job, option, value): 16 | self.job = job 17 | self.option = option 18 | self.value = value 19 | 20 | def as_dict(self): 21 | return {c.name: str(getattr(self, c.name)) for c in self.__table__.columns} -------------------------------------------------------------------------------- /model/FuzzingJobState.py: -------------------------------------------------------------------------------- 1 | from app import app 2 | 3 | db = app.config['db'] 4 | 5 | 6 | class FuzzingJobState(db.Model): 7 | id = db.Column(db.Integer, primary_key=True) 8 | name = db.Column(db.String(64), unique=True, nullable=False) 9 | 10 | def __init__(self, name): 11 | self.name = name 12 | 13 | def as_dict(self): 14 | return {c.name: str(getattr(self, c.name)) for c in self.__table__.columns} -------------------------------------------------------------------------------- /model/FuzzingOption.py: -------------------------------------------------------------------------------- 1 | from app import app 2 | 3 | db = app.config['db'] 4 | 5 | 6 | class FuzzingOption(db.Model): 7 | id = db.Column(db.Integer, primary_key=True) 8 | name = db.Column(db.String(256), unique=True, nullable=False) 9 | type_id = db.Column(db.Integer, db.ForeignKey('fuzzing_option_type.id')) 10 | type = db.relationship('FuzzingOptionType', foreign_keys=type_id) 11 | 12 | def __init__(self, name, type): 13 | self.name = name 14 | self.type = type 15 | 16 | def as_dict(self): 17 | return {c.name: str(getattr(self, c.name)) for c in self.__table__.columns} -------------------------------------------------------------------------------- /model/FuzzingOptionType.py: -------------------------------------------------------------------------------- 1 | from app import app 2 | 3 | db = app.config['db'] 4 | 5 | 6 | class FuzzingOptionType(db.Model): 7 | id = db.Column(db.Integer, primary_key=True) 8 | name = db.Column(db.String(64), unique=True, nullable=False) 9 | 10 | def __init__(self, name): 11 | self.name = name 12 | 13 | def as_dict(self): 14 | return {c.name: str(getattr(self, c.name)) for c in self.__table__.columns} -------------------------------------------------------------------------------- /model/FuzzingPlatform.py: -------------------------------------------------------------------------------- 1 | from app import app 2 | 3 | db = app.config['db'] 4 | 5 | 6 | class FuzzingPlatform(db.Model): 7 | id = db.Column(db.Integer, primary_key=True) 8 | name = db.Column(db.String(64), unique=True) 9 | 10 | def __init__(self, name): 11 | self.name = name 12 | 13 | def as_dict(self): 14 | return {c.name: str(getattr(self, c.name)) for c in self.__table__.columns} -------------------------------------------------------------------------------- /model/FuzzingScript.py: -------------------------------------------------------------------------------- 1 | from app import app 2 | 3 | db = app.config['db'] 4 | 5 | 6 | class FuzzingScript(db.Model): 7 | id = db.Column(db.Integer, primary_key=True) 8 | name = db.Column(db.String(256), unique=True, nullable=False) 9 | script = db.Column(db.String(8192), nullable=False) 10 | visible = db.Column(db.Integer, default=1, nullable=False) 11 | 12 | def __init__(self, name, script, visible=1): 13 | self.name = name 14 | self.script = script 15 | self.visible = visible 16 | 17 | def as_dict(self): 18 | return {c.name: str(getattr(self, c.name)) for c in self.__table__.columns} -------------------------------------------------------------------------------- /model/FuzzingTarget.py: -------------------------------------------------------------------------------- 1 | from app import app 2 | 3 | db = app.config['db'] 4 | 5 | # fuzzing_target_config = db.Table('fuzzing_target_config ', 6 | # db.Column('config_id', db.Integer, db.ForeignKey('fuzzing_config.id')), 7 | # db.Column('target_id', db.Integer, db.ForeignKey('fuzzing_target.id')) 8 | #) 9 | 10 | 11 | class FuzzingTarget(db.Model): 12 | id = db.Column(db.Integer, primary_key=True) 13 | name = db.Column(db.String(256), unique=True, nullable=False) 14 | path = db.Column(db.String(1024), unique=True, nullable=False) 15 | visible = db.Column(db.Integer, default=1, nullable=False) 16 | platform_id = db.Column(db.Integer, db.ForeignKey('fuzzing_platform.id')) 17 | platform = db.relationship('FuzzingPlatform', foreign_keys=platform_id) 18 | arch_id = db.Column(db.Integer, db.ForeignKey('fuzzing_arch.id')) 19 | arch = db.relationship('FuzzingArch', foreign_keys=arch_id) 20 | 21 | #configs = db.relationship('FuzzingConfig', secondary=fuzzing_target_config, 22 | # backref=db.backref('targets', lazy='dynamic')) 23 | 24 | def __init__(self, name, path, platform, arch): 25 | self.name = name 26 | self.path = path 27 | self.platform = platform 28 | self.arch = arch 29 | self.visible = 1 30 | #if configs is not None: 31 | # self.configs = configs 32 | 33 | def as_dict(self): 34 | return {c.name: str(getattr(self, c.name)) for c in self.__table__.columns} -------------------------------------------------------------------------------- /model/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/talos-vulndev/FuzzFlow/3f2de617c3658904f9640f53a05d5e4e90634e32/model/__init__.py -------------------------------------------------------------------------------- /model/_deprecated_FuzzingConfig.py: -------------------------------------------------------------------------------- 1 | from app import app 2 | from model.FuzzingEngine import fuzzing_engine_config 3 | db = app.config['db'] 4 | 5 | 6 | class FuzzingConfig(db.Model): 7 | id = db.Column(db.Integer, primary_key=True) 8 | host_id = db.Column(db.Integer, db.ForeignKey('fuzzing_host.id')) 9 | host = db.relationship('FuzzingHost', foreign_keys=host_id) 10 | arch_id= db.Column(db.Integer, db.ForeignKey('fuzzing_arch.id')) 11 | arch = db.relationship('FuzzingArch', foreign_keys=arch_id) 12 | platform_id= db.Column(db.Integer, db.ForeignKey('fuzzing_platform.id')) 13 | platform = db.relationship('FuzzingPlatform', foreign_keys=platform_id) 14 | 15 | def __init__(self, host, arch, platform): 16 | self.host = host 17 | self.arch = arch 18 | self.platform = platform 19 | 20 | def as_dict(self): 21 | return {c.name: str(getattr(self, c.name)) for c in self.__table__.columns} -------------------------------------------------------------------------------- /model/_not_used_CrashBucket.py: -------------------------------------------------------------------------------- 1 | from app import app 2 | from datetime import * 3 | 4 | db = app.config['db'] 5 | 6 | 7 | class CrashBucket(db.Model): 8 | id = db.Column(db.Integer, primary_key=True) 9 | name = db.Column(db.String(256), unique=True, nullable=False) 10 | info = db.Column(db.String(512)) 11 | job_id = db.Column(db.Integer, db.ForeignKey('fuzzing_job.id')) 12 | job = db.relationship('FuzzingJob', foreign_keys=job_id) 13 | # state_id = db.Column(db.Integer, db.ForeignKey('crash_bucket_state.id')) 14 | # state = db.relationship('CrashBucketState', foreign_keys=state_id) 15 | created_at = db.Column(db.DateTime, 16 | nullable=False, 17 | default=datetime.utcnow) 18 | updated_at = db.Column(db.DateTime, 19 | nullable=False, 20 | default=datetime.utcnow, 21 | onupdate=datetime.utcnow) 22 | 23 | def __init__(self, name, info, job, state): 24 | self.name = name 25 | if info is not None: 26 | self.info = info 27 | if job is not None: 28 | self.job = job 29 | if state is not None: 30 | self.state = state 31 | 32 | def as_dict(self): 33 | return {c.name: str(getattr(self, c.name)) for c in self.__table__.columns} -------------------------------------------------------------------------------- /model/_not_used_CrashBucketFile.py: -------------------------------------------------------------------------------- 1 | from app import app 2 | 3 | db = app.config['db'] 4 | 5 | 6 | class CrashBucketFile(db.Model): 7 | id = db.Column(db.Integer, primary_key=True) 8 | name = db.Column(db.String(256), unique=True, nullable=False) 9 | path = db.Column(db.String(512), unique=True, nullable=False) 10 | 11 | def __init__(self, name, path): 12 | self.name = name 13 | self.path = path 14 | 15 | def as_dict(self): 16 | return {c.name: str(getattr(self, c.name)) for c in self.__table__.columns} -------------------------------------------------------------------------------- /model/_not_used_CrashBucketNote.py: -------------------------------------------------------------------------------- 1 | from app import app 2 | from datetime import * 3 | 4 | db = app.config['db'] 5 | 6 | 7 | class CrashBucketNote(db.Model): 8 | id = db.Column(db.Integer, primary_key=True) 9 | note = db.Column(db.String(2048)) 10 | crash_bucket_id = db.Column(db.Integer, db.ForeignKey('crash_bucket_file.id')) 11 | crash_bucket = db.relationship('CrashBucketFile', foreign_keys=crash_bucket_id) 12 | created_at = db.Column(db.DateTime, 13 | nullable=False, 14 | default=datetime.utcnow) 15 | updated_at = db.Column(db.DateTime, 16 | nullable=False, 17 | default=datetime.utcnow, 18 | onupdate=datetime.utcnow) 19 | 20 | def __init__(self, note): 21 | self.note = note 22 | 23 | def as_dict(self): 24 | return {c.name: str(getattr(self, c.name)) for c in self.__table__.columns} -------------------------------------------------------------------------------- /model/_not_used_CrashBucketState.py: -------------------------------------------------------------------------------- 1 | from app import app 2 | 3 | db = app.config['db'] 4 | 5 | 6 | class CrashBucketState(db.Model): 7 | id = db.Column(db.Integer, primary_key=True) 8 | name = db.Column(db.String(32), unique=True, nullable=False) 9 | 10 | def __init__(self, name): 11 | self.name = name 12 | 13 | def as_dict(self): 14 | return {c.name: str(getattr(self, c.name)) for c in self.__table__.columns} -------------------------------------------------------------------------------- /model/_not_used_CrashSample.py: -------------------------------------------------------------------------------- 1 | from app import app 2 | from datetime import * 3 | 4 | db = app.config['db'] 5 | 6 | 7 | class CrashSample(db.Model): 8 | id = db.Column(db.Integer, primary_key=True) 9 | note = db.Column(db.String(2048)) 10 | job_id = db.Column(db.Integer, db.ForeignKey('fuzzing_job.id')) 11 | job = db.relationship('FuzzingJob', foreign_keys=job_id) 12 | bucket_id = db.Column(db.Integer, db.ForeignKey('crash_bucket.id')) 13 | bucket = db.relationship('CrashBucket', foreign_keys=bucket_id) 14 | repro_file = db.Column(db.String(512), nullable=False) 15 | created_at = db.Column(db.DateTime, 16 | nullable=False, 17 | default=datetime.utcnow) 18 | updated_at = db.Column(db.DateTime, 19 | nullable=False, 20 | default=datetime.utcnow, 21 | onupdate=datetime.utcnow) 22 | 23 | def __init__(self, note, job, bucket, iteration, inst, repro): 24 | if note is not None: 25 | self.note = note 26 | 27 | self.job = job 28 | self.bucket = bucket 29 | 30 | if iteration is not None: 31 | self.iteration = iteration 32 | 33 | if inst is not None: 34 | self.instruction = inst 35 | 36 | if repro is not None: 37 | self.repro_file = repro 38 | 39 | def as_dict(self): 40 | return {c.name: str(getattr(self, c.name)) for c in self.__table__.columns} -------------------------------------------------------------------------------- /nginx.conf: -------------------------------------------------------------------------------- 1 | server { 2 | listen 80; 3 | server_tokens off; 4 | server_name localhost; 5 | 6 | location / { 7 | include uwsgi_params; 8 | uwsgi_pass unix:/tmp/moflow.sock; 9 | 10 | if ($request_method = 'OPTIONS') { 11 | add_header 'Access-Control-Allow-Origin' '*'; 12 | add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS'; 13 | # 14 | # Custom headers and headers various browsers *should* be OK with but aren't 15 | # 16 | add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type'; 17 | # 18 | # Tell client that this pre-flight info is valid for 20 days 19 | # 20 | add_header 'Access-Control-Max-Age' 1728000; 21 | add_header 'Content-Type' 'text/plain charset=UTF-8'; 22 | add_header 'Content-Length' 0; 23 | return 204; 24 | } 25 | if ($request_method = 'POST') { 26 | #add_header 'Access-Control-Allow-Origin' '*'; 27 | #add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS'; 28 | #add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type'; 29 | more_set_headers 'Access-Control-Allow-Origin: *'; 30 | } 31 | if ($request_method = 'GET') { 32 | #add_header 'Access-Control-Allow-Origin' '*'; 33 | #add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS'; 34 | #add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type'; 35 | more_set_headers 'Access-Control-Allow-Origin: *'; 36 | } 37 | } 38 | 39 | location /static { 40 | alias /vagrant/static; 41 | expires off; #development config 42 | 43 | if ($request_method = 'OPTIONS') { 44 | add_header 'Access-Control-Allow-Origin' '*'; 45 | add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS'; 46 | # 47 | # Custom headers and headers various browsers *should* be OK with but aren't 48 | # 49 | add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type'; 50 | # 51 | # Tell client that this pre-flight info is valid for 20 days 52 | # 53 | add_header 'Access-Control-Max-Age' 1728000; 54 | add_header 'Content-Type' 'text/plain charset=UTF-8'; 55 | add_header 'Content-Length' 0; 56 | return 204; 57 | } 58 | if ($request_method = 'POST') { 59 | #add_header 'Access-Control-Allow-Origin' '*'; 60 | #add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS'; 61 | #add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type'; 62 | more_set_headers 'Access-Control-Allow-Origin: *'; 63 | } 64 | if ($request_method = 'GET') { 65 | #add_header 'Access-Control-Allow-Origin' '*'; 66 | #add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS'; 67 | #add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type'; 68 | more_set_headers 'Access-Control-Allow-Origin: *'; 69 | } 70 | 71 | } 72 | } -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | flask 2 | Flask-SQLAlchemy 3 | Flask-RESTful 4 | Flask-Security -------------------------------------------------------------------------------- /server.py: -------------------------------------------------------------------------------- 1 | from app import app 2 | 3 | if __name__ == '__main__': 4 | app.run(host='0.0.0.0') -------------------------------------------------------------------------------- /static/client.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/talos-vulndev/FuzzFlow/3f2de617c3658904f9640f53a05d5e4e90634e32/static/client.zip -------------------------------------------------------------------------------- /static/frontend/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /static/frontend/app/app.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var server_ip = '172.28.128.3'; 4 | 5 | 6 | angular 7 | .module('frontApp', [ 8 | 'ngRoute', 9 | 'ngTagsInput', 10 | 'file-model', 11 | ]) 12 | .value('config', { 13 | server: server_ip, 14 | baseURL: "http://" + server_ip + "/api", 15 | refreshTime: 5000 // Timer to update info from server 16 | }) 17 | .config(function ($routeProvider) { 18 | $routeProvider 19 | .when('/', { 20 | templateUrl: 'view/overview.htm', 21 | controller: 'OverviewCtrl', 22 | title: "Overview", 23 | }) 24 | .when('/node', { 25 | templateUrl: 'view/node.htm', 26 | controller: 'NodeManagerCtrl', 27 | title: "Node Manager", 28 | }) 29 | .when('/crash', { 30 | templateUrl: 'view/crash.htm', 31 | controller: 'CrashManagerCtrl', 32 | title: "Crash Manager", 33 | }) 34 | .when('/job', { 35 | templateUrl: 'view/job.htm', 36 | controller: 'JobManagerCtrl', 37 | title: "Job Manager", 38 | }) 39 | .when('/err', { 40 | templateUrl: 'view/err.htm', 41 | controller: 'ErrLogCtrl', 42 | title: "Error Log", 43 | }) 44 | .when('/config', { 45 | templateUrl: 'view/config.htm', 46 | controller: 'ConfigManagerCtrl', 47 | title: "Configuration", 48 | }) 49 | .otherwise({ 50 | redirectTo: '/' 51 | }); 52 | }) 53 | .run(['$location', '$rootScope', '$interval', 54 | function($location, $rootScope, $interval) { 55 | $rootScope.navActiveLink = function(path){ 56 | return ($location.$$path === path) ? 'active' : ''; 57 | } 58 | $rootScope.$on('$routeChangeSuccess', function (event, current, previous) { 59 | $interval.cancel($rootScope.nodeManagerTimer); 60 | $interval.cancel($rootScope.overviewTimer); 61 | $interval.cancel($rootScope.jobManagerTimer); 62 | $interval.cancel($rootScope.crashManagerTimer); 63 | $interval.cancel($rootScope.errLogTimer); 64 | 65 | if (current.hasOwnProperty('$$route')) { 66 | $rootScope.title = current.$$route.title; 67 | } 68 | }); 69 | 70 | }]); 71 | -------------------------------------------------------------------------------- /static/frontend/asset/bootstrap/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/talos-vulndev/FuzzFlow/3f2de617c3658904f9640f53a05d5e4e90634e32/static/frontend/asset/bootstrap/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /static/frontend/asset/bootstrap/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/talos-vulndev/FuzzFlow/3f2de617c3658904f9640f53a05d5e4e90634e32/static/frontend/asset/bootstrap/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /static/frontend/asset/bootstrap/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/talos-vulndev/FuzzFlow/3f2de617c3658904f9640f53a05d5e4e90634e32/static/frontend/asset/bootstrap/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /static/frontend/asset/bootstrap/fonts/glyphicons-halflings-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/talos-vulndev/FuzzFlow/3f2de617c3658904f9640f53a05d5e4e90634e32/static/frontend/asset/bootstrap/fonts/glyphicons-halflings-regular.woff2 -------------------------------------------------------------------------------- /static/frontend/asset/bootstrap/js/npm.js: -------------------------------------------------------------------------------- 1 | // This file is autogenerated via the `commonjs` Grunt task. You can require() this file in a CommonJS environment. 2 | require('../../js/transition.js') 3 | require('../../js/alert.js') 4 | require('../../js/button.js') 5 | require('../../js/carousel.js') 6 | require('../../js/collapse.js') 7 | require('../../js/dropdown.js') 8 | require('../../js/modal.js') 9 | require('../../js/tooltip.js') 10 | require('../../js/popover.js') 11 | require('../../js/scrollspy.js') 12 | require('../../js/tab.js') 13 | require('../../js/affix.js') -------------------------------------------------------------------------------- /static/frontend/asset/css/bootstrap.vertical-tabs.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * bootstrap-vertical-tabs - v1.2.1 3 | * https://dbtek.github.io/bootstrap-vertical-tabs 4 | * 2014-11-07 5 | * Copyright (c) 2014 İsmail Demirbilek 6 | * License: MIT 7 | */ 8 | 9 | .tabs-left,.tabs-right{border-bottom:none;padding-top:2px}.tabs-left{border-right:1px solid #ddd}.tabs-right{border-left:1px solid #ddd}.tabs-left>li,.tabs-right>li{float:none;margin-bottom:2px}.tabs-left>li{margin-right:-1px}.tabs-right>li{margin-left:-1px}.tabs-left>li.active>a,.tabs-left>li.active>a:focus,.tabs-left>li.active>a:hover{border-bottom-color:#ddd;border-right-color:transparent}.tabs-right>li.active>a,.tabs-right>li.active>a:focus,.tabs-right>li.active>a:hover{border-bottom:1px solid #ddd;border-left-color:transparent}.tabs-left>li>a{border-radius:4px 0 0 4px;margin-right:0;display:block}.tabs-right>li>a{border-radius:0 4px 4px 0;margin-right:0}.sideways{margin-top:50px;border:none;position:relative}.sideways>li{height:20px;width:120px;margin-bottom:100px}.sideways>li>a{border-bottom:1px solid #ddd;border-right-color:transparent;text-align:center;border-radius:4px 4px 0 0}.sideways>li.active>a,.sideways>li.active>a:focus,.sideways>li.active>a:hover{border-bottom-color:transparent;border-right-color:#ddd;border-left-color:#ddd}.sideways.tabs-left{left:-50px}.sideways.tabs-right{right:-50px}.sideways.tabs-right>li{-webkit-transform:rotate(90deg);-moz-transform:rotate(90deg);-ms-transform:rotate(90deg);-o-transform:rotate(90deg);transform:rotate(90deg)}.sideways.tabs-left>li{-webkit-transform:rotate(-90deg);-moz-transform:rotate(-90deg);-ms-transform:rotate(-90deg);-o-transform:rotate(-90deg);transform:rotate(-90deg)} -------------------------------------------------------------------------------- /static/frontend/asset/css/ie10-viewport-bug-workaround.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * IE10 viewport hack for Surface/desktop Windows 8 bug 3 | * Copyright 2014-2015 Twitter, Inc. 4 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 5 | */ 6 | 7 | /* 8 | * See the Getting Started docs for more information: 9 | * http://getbootstrap.com/getting-started/#support-ie10-width 10 | */ 11 | @-ms-viewport { width: device-width; } 12 | @-o-viewport { width: device-width; } 13 | @viewport { width: device-width; } 14 | -------------------------------------------------------------------------------- /static/frontend/asset/css/ng-tags-input.bootstrap.min.css: -------------------------------------------------------------------------------- 1 | tags-input{box-shadow:none;border:none;padding:0;min-height:34px}tags-input .host{margin:0}tags-input .tags{-moz-appearance:none;-webkit-appearance:none;border:1px solid #ccc;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075);-webkit-transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s;-moz-transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s;transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s}tags-input .tags .tag-item{color:#fff;background:#428bca;border:1px solid #357ebd;border-radius:4px}tags-input .tags .tag-item.selected{color:#fff;background:#d9534f;border:1px solid #d43f3a}tags-input .tags .tag-item .remove-button:hover{text-decoration:none}tags-input .tags.focused{border:1px solid #66afe9;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6);box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6)}tags-input .autocomplete{border-radius:4px}tags-input .autocomplete .suggestion-item.selected,tags-input .autocomplete .suggestion-item.selected em{color:#262626;background-color:#f5f5f5}tags-input .autocomplete .suggestion-item em{color:#000;background-color:#fff}tags-input.ng-invalid .tags{border-color:#843534;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483;-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483}tags-input[disabled] .tags{background-color:#eee}tags-input[disabled] .tags .tag-item{background:#337ab7;opacity:.65}tags-input[disabled] .tags .input{background-color:#eee}.input-group tags-input{padding:0;display:table-cell}.input-group tags-input:not(:first-child) .tags{border-top-left-radius:0;border-bottom-left-radius:0}.input-group tags-input:not(:last-child) .tags{border-top-right-radius:0;border-bottom-right-radius:0}.input-group-lg tags-input:first-child .tags{border-top-left-radius:6px;border-bottom-left-radius:6px}.input-group-lg tags-input:last-child .tags{border-top-right-radius:6px;border-bottom-right-radius:6px}.input-group-sm tags-input:first-child .tags{border-top-left-radius:3px;border-bottom-left-radius:3px}.input-group-sm tags-input:last-child .tags{border-top-right-radius:3px;border-bottom-right-radius:3px}.input-group-lg tags-input,tags-input.ti-input-lg{min-height:46px}.input-group-lg tags-input .tags,tags-input.ti-input-lg .tags{border-radius:6px}.input-group-lg tags-input .tags .tag-item,tags-input.ti-input-lg .tags .tag-item{height:38px;line-height:37px;font-size:18px;border-radius:6px}.input-group-lg tags-input .tags .tag-item .remove-button,tags-input.ti-input-lg .tags .tag-item .remove-button{font-size:20px}.input-group-lg tags-input .tags .input,tags-input.ti-input-lg .tags .input{height:38px;font-size:18px}.input-group-sm tags-input,tags-input.ti-input-sm{min-height:30px}.input-group-sm tags-input .tags,tags-input.ti-input-sm .tags{border-radius:3px}.input-group-sm tags-input .tags .tag-item,tags-input.ti-input-sm .tags .tag-item{height:22px;line-height:21px;font-size:12px;border-radius:3px}.input-group-sm tags-input .tags .tag-item .remove-button,tags-input.ti-input-sm .tags .tag-item .remove-button{font-size:16px}.input-group-sm tags-input .tags .input,tags-input.ti-input-sm .tags .input{height:22px;font-size:12px}.has-feedback tags-input .tags{padding-right:30px}.has-success tags-input .tags{border-color:#3c763d}.has-success tags-input .tags.focused{border-color:#2b542c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168;-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168}.has-error tags-input .tags{border-color:#a94442}.has-error tags-input .tags.focused{border-color:#843534;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483;-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483}.has-warning tags-input .tags{border-color:#8a6d3b}.has-warning tags-input .tags.focused{border-color:#66512c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b;-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b} -------------------------------------------------------------------------------- /static/frontend/asset/css/ng-tags-input.min.css: -------------------------------------------------------------------------------- 1 | tags-input{display:block}tags-input *,tags-input :after,tags-input :before{-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box}tags-input .host{position:relative;margin-top:5px;margin-bottom:5px;height:100%}tags-input .host:active{outline:0}tags-input .tags{-moz-appearance:textfield;-webkit-appearance:textfield;padding:1px;overflow:hidden;word-wrap:break-word;cursor:text;background-color:#fff;border:1px solid #a9a9a9;box-shadow:1px 1px 1px 0 #d3d3d3 inset;height:100%}tags-input .tags.focused{outline:0;-webkit-box-shadow:0 0 3px 1px rgba(5,139,242,.6);-moz-box-shadow:0 0 3px 1px rgba(5,139,242,.6);box-shadow:0 0 3px 1px rgba(5,139,242,.6)}tags-input .tags .tag-list{margin:0;padding:0;list-style-type:none}tags-input .tags .tag-item{margin:2px;padding:0 5px;display:inline-block;float:left;font:14px "Helvetica Neue",Helvetica,Arial,sans-serif;height:26px;line-height:25px;border:1px solid #acacac;border-radius:3px;background:-webkit-linear-gradient(top,#f0f9ff 0,#cbebff 47%,#a1dbff 100%);background:linear-gradient(to bottom,#f0f9ff 0,#cbebff 47%,#a1dbff 100%)}tags-input .tags .tag-item.selected{background:-webkit-linear-gradient(top,#febbbb 0,#fe9090 45%,#ff5c5c 100%);background:linear-gradient(to bottom,#febbbb 0,#fe9090 45%,#ff5c5c 100%)}tags-input .tags .tag-item .remove-button{margin:0 0 0 5px;padding:0;border:none;background:0 0;cursor:pointer;vertical-align:middle;font:700 16px Arial,sans-serif;color:#585858}tags-input .tags .input.invalid-tag,tags-input .tags .tag-item .remove-button:active{color:red}tags-input .tags .input{border:0;outline:0;margin:2px;padding:0 0 0 5px;float:left;height:26px;font:14px "Helvetica Neue",Helvetica,Arial,sans-serif}tags-input .tags .input::-ms-clear{display:none}tags-input.ng-invalid .tags{-webkit-box-shadow:0 0 3px 1px rgba(255,0,0,.6);-moz-box-shadow:0 0 3px 1px rgba(255,0,0,.6);box-shadow:0 0 3px 1px rgba(255,0,0,.6)}tags-input[disabled] .host:focus{outline:0}tags-input[disabled] .tags{background-color:#eee;cursor:default}tags-input[disabled] .tags .tag-item{opacity:.65;background:-webkit-linear-gradient(top,#f0f9ff 0,rgba(203,235,255,.75) 47%,rgba(161,219,255,.62) 100%);background:linear-gradient(to bottom,#f0f9ff 0,rgba(203,235,255,.75) 47%,rgba(161,219,255,.62) 100%)}tags-input[disabled] .tags .tag-item .remove-button{cursor:default}tags-input[disabled] .tags .tag-item .remove-button:active{color:#585858}tags-input[disabled] .tags .input{background-color:#eee;cursor:default}tags-input .autocomplete{margin-top:5px;position:absolute;padding:5px 0;z-index:999;width:100%;background-color:#fff;border:1px solid rgba(0,0,0,.2);-webkit-box-shadow:0 5px 10px rgba(0,0,0,.2);-moz-box-shadow:0 5px 10px rgba(0,0,0,.2);box-shadow:0 5px 10px rgba(0,0,0,.2)}tags-input .autocomplete .suggestion-list{margin:0;padding:0;list-style-type:none;max-height:280px;overflow-y:auto;position:relative}tags-input .autocomplete .suggestion-item{padding:5px 10px;cursor:pointer;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;font:16px "Helvetica Neue",Helvetica,Arial,sans-serif;color:#000;background-color:#fff}tags-input .autocomplete .suggestion-item.selected,tags-input .autocomplete .suggestion-item.selected em{color:#fff;background-color:#0097cf}tags-input .autocomplete .suggestion-item em{font:normal 700 16px "Helvetica Neue",Helvetica,Arial,sans-serif;color:#000;background-color:#fff} -------------------------------------------------------------------------------- /static/frontend/asset/css/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | border-top: 10px solid #333; 3 | font: 12px/22px Verdana, sans-serif; 4 | background: #333; 5 | } 6 | 7 | .navbar-static-top { 8 | margin-bottom: 19px; 9 | } 10 | 11 | 12 | header { 13 | background: #000 url(../image/banner.jpg) no-repeat 0px 0; 14 | border-bottom: 1px solid #333; 15 | height: 100px; 16 | margin: 0 0 px 0; 17 | padding: 0 10px; 18 | position: relative; 19 | border-top: 1px solid #A9A796; 20 | border-left: 1px solid #A9A796; 21 | } 22 | 23 | header a { 24 | background: #333; 25 | color: #FFAC00; 26 | font-size: 16px; 27 | margin: 0 0 0 10px; 28 | padding: 5px 10px; 29 | } 30 | 31 | h1, h3, h4, h5, h6 { 32 | background: url(../image/ai6.png) no-repeat 0 2px; 33 | border-bottom: 1px dotted #A9A796; 34 | color: #222; 35 | font-weight: bold; 36 | margin: 0 0 10px; 37 | padding: 0 0 0 25px; 38 | } 39 | 40 | h1 { 41 | background: url(../image/ai6.png) no-repeat 0 4px; 42 | font-size: 15px; 43 | } 44 | 45 | 46 | h3, h4, h5, h6 { 47 | color: #333; 48 | font-size: 13px; 49 | } 50 | 51 | 52 | .col-* ul li { 53 | background: url(../image/ai2.png) no-repeat 0 8px; 54 | padding: 0 0 0 17px; 55 | list-style: none; 56 | } 57 | 58 | .col-* ul li a { 59 | border-bottom: 1px dotted #504B2A; 60 | color: #504b2a; 61 | text-decoration: none; 62 | } 63 | 64 | .col-* ul li a:hover { 65 | color : #000; 66 | } 67 | .col-* ul li *:TARGET { 68 | color : #FF9900; 69 | } 70 | .container-flow { 71 | background: #edebe1; 72 | border: 1px solid #A9A796; 73 | padding: 20px; 74 | } 75 | 76 | nav .container-flow{ 77 | background: #002000; 78 | border-bottom: 1px solid #333; 79 | border-left: 1px solid #A9A796; 80 | border-top: 0; 81 | padding: 0px; 82 | margin: 0px; 83 | } 84 | .navbar-default .navbar-nav>li>a { 85 | color: #A79787; 86 | } 87 | 88 | .navbar-default .navbar-nav>li>a:hover, 89 | .navbar-default .navbar-nav>li.active{ 90 | color:#fff; 91 | } 92 | 93 | 94 | .navbar-static-top { 95 | margin-bottom: 0px; 96 | } 97 | 98 | .navbar-nav>li>a { 99 | padding-top: 5px; 100 | padding-bottom: 5px; 101 | } 102 | 103 | .navbar { 104 | min-height: 0px; 105 | } 106 | 107 | .navbar-default .navbar-nav>.active>a, 108 | .navbar-default .navbar-nav>.active>a:focus, 109 | .navbar-default .navbar-nav>.active>a:hover { 110 | color:#fff; 111 | background: #002000; 112 | border: 1px dashed; 113 | } 114 | 115 | .table-overflow { 116 | height: 500px !important; 117 | overflow: scroll; 118 | }​ 119 | 120 | table { 121 | table-layout: fixed; 122 | word-wrap: break-word; 123 | } 124 | 125 | tags-input-small { 126 | width: 120px; 127 | padding: 0; 128 | margin: 0; 129 | } 130 | 131 | #jobOutputModal > .modal-dialog, 132 | #crashOutputModal > .modal-dialog { 133 | width: 100%; 134 | height: 100%; 135 | margin: 0; 136 | padding: 0; 137 | 138 | } 139 | #jobOutputModal > .modal-dialog > .modal-content, 140 | #crashOutputModal > .modal-dialog > .modal-content{ 141 | height: auto; 142 | min-height: 100%; 143 | border-radius: 0; 144 | } -------------------------------------------------------------------------------- /static/frontend/asset/image/ai1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/talos-vulndev/FuzzFlow/3f2de617c3658904f9640f53a05d5e4e90634e32/static/frontend/asset/image/ai1.png -------------------------------------------------------------------------------- /static/frontend/asset/image/ai2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/talos-vulndev/FuzzFlow/3f2de617c3658904f9640f53a05d5e4e90634e32/static/frontend/asset/image/ai2.png -------------------------------------------------------------------------------- /static/frontend/asset/image/ai5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/talos-vulndev/FuzzFlow/3f2de617c3658904f9640f53a05d5e4e90634e32/static/frontend/asset/image/ai5.png -------------------------------------------------------------------------------- /static/frontend/asset/image/ai6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/talos-vulndev/FuzzFlow/3f2de617c3658904f9640f53a05d5e4e90634e32/static/frontend/asset/image/ai6.png -------------------------------------------------------------------------------- /static/frontend/asset/image/banner.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/talos-vulndev/FuzzFlow/3f2de617c3658904f9640f53a05d5e4e90634e32/static/frontend/asset/image/banner.jpg -------------------------------------------------------------------------------- /static/frontend/asset/image/cssmenubg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/talos-vulndev/FuzzFlow/3f2de617c3658904f9640f53a05d5e4e90634e32/static/frontend/asset/image/cssmenubg.png -------------------------------------------------------------------------------- /static/frontend/asset/image/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/talos-vulndev/FuzzFlow/3f2de617c3658904f9640f53a05d5e4e90634e32/static/frontend/asset/image/favicon.ico -------------------------------------------------------------------------------- /static/frontend/asset/js/angular-file-model.js: -------------------------------------------------------------------------------- 1 | // 2 | // angular-file-model 3 | // ================== 4 | // 5 | // Directive that makes the inputs with type `file` to be 6 | // available in the `$scope` and be assigned to a model. 7 | // 8 | (function () { 9 | 'use strict'; 10 | 11 | angular.module('file-model', []) 12 | 13 | .directive('fileModel', [ 14 | '$parse', 15 | function ($parse) { 16 | return { 17 | restrict: 'A', 18 | link: function(scope, element, attrs) { 19 | var model = $parse(attrs.fileModel); 20 | var modelSetter = model.assign; 21 | 22 | element.bind('change', function(){ 23 | scope.$apply(function(){ 24 | if (attrs.multiple) { 25 | modelSetter(scope, element[0].files); 26 | } 27 | else { 28 | modelSetter(scope, element[0].files[0]); 29 | } 30 | }); 31 | }); 32 | } 33 | }; 34 | } 35 | ]); 36 | 37 | })(); 38 | -------------------------------------------------------------------------------- /static/frontend/asset/js/angular-route.min.js: -------------------------------------------------------------------------------- 1 | /* 2 | AngularJS v1.5.6 3 | (c) 2010-2016 Google, Inc. http://angularjs.org 4 | License: MIT 5 | */ 6 | (function(C,d){'use strict';function w(s,h,f){return{restrict:"ECA",terminal:!0,priority:400,transclude:"element",link:function(a,e,b,g,y){function k(){n&&(f.cancel(n),n=null);l&&(l.$destroy(),l=null);m&&(n=f.leave(m),n.then(function(){n=null}),m=null)}function z(){var b=s.current&&s.current.locals;if(d.isDefined(b&&b.$template)){var b=a.$new(),g=s.current;m=y(b,function(b){f.enter(b,null,m||e).then(function(){!d.isDefined(u)||u&&!a.$eval(u)||h()});k()});l=g.scope=b;l.$emit("$viewContentLoaded"); 7 | l.$eval(r)}else k()}var l,m,n,u=b.autoscroll,r=b.onload||"";a.$on("$routeChangeSuccess",z);z()}}}function v(d,h,f){return{restrict:"ECA",priority:-400,link:function(a,e){var b=f.current,g=b.locals;e.html(g.$template);var y=d(e.contents());if(b.controller){g.$scope=a;var k=h(b.controller,g);b.controllerAs&&(a[b.controllerAs]=k);e.data("$ngControllerController",k);e.children().data("$ngControllerController",k)}a[b.resolveAs||"$resolve"]=g;y(a)}}}var r=d.module("ngRoute",["ng"]).provider("$route",function(){function s(a, 8 | e){return d.extend(Object.create(a),e)}function h(a,d){var b=d.caseInsensitiveMatch,g={originalPath:a,regexp:a},f=g.keys=[];a=a.replace(/([().])/g,"\\$1").replace(/(\/)?:(\w+)(\*\?|[\?\*])?/g,function(a,d,b,e){a="?"===e||"*?"===e?"?":null;e="*"===e||"*?"===e?"*":null;f.push({name:b,optional:!!a});d=d||"";return""+(a?"":d)+"(?:"+(a?d:"")+(e&&"(.+?)"||"([^/]+)")+(a||"")+")"+(a||"")}).replace(/([\/$\*])/g,"\\$1");g.regexp=new RegExp("^"+a+"$",b?"i":"");return g}var f={};this.when=function(a,e){var b= 9 | d.copy(e);d.isUndefined(b.reloadOnSearch)&&(b.reloadOnSearch=!0);d.isUndefined(b.caseInsensitiveMatch)&&(b.caseInsensitiveMatch=this.caseInsensitiveMatch);f[a]=d.extend(b,a&&h(a,b));if(a){var g="/"==a[a.length-1]?a.substr(0,a.length-1):a+"/";f[g]=d.extend({redirectTo:a},h(g,b))}return this};this.caseInsensitiveMatch=!1;this.otherwise=function(a){"string"===typeof a&&(a={redirectTo:a});this.when(null,a);return this};this.$get=["$rootScope","$location","$routeParams","$q","$injector","$templateRequest", 10 | "$sce",function(a,e,b,g,h,k,r){function l(q){var c=t.current;(A=(p=w())&&c&&p.$$route===c.$$route&&d.equals(p.pathParams,c.pathParams)&&!p.reloadOnSearch&&!x)||!c&&!p||a.$broadcast("$routeChangeStart",p,c).defaultPrevented&&q&&q.preventDefault()}function m(){var q=t.current,c=p;if(A)q.params=c.params,d.copy(q.params,b),a.$broadcast("$routeUpdate",q);else if(c||q)x=!1,(t.current=c)&&c.redirectTo&&(d.isString(c.redirectTo)?e.path(v(c.redirectTo,c.params)).search(c.params).replace():e.url(c.redirectTo(c.pathParams, 11 | e.path(),e.search())).replace()),g.when(c).then(n).then(function(e){c==t.current&&(c&&(c.locals=e,d.copy(c.params,b)),a.$broadcast("$routeChangeSuccess",c,q))},function(d){c==t.current&&a.$broadcast("$routeChangeError",c,q,d)})}function n(a){if(a){var c=d.extend({},a.resolve);d.forEach(c,function(a,b){c[b]=d.isString(a)?h.get(a):h.invoke(a,null,null,b)});a=u(a);d.isDefined(a)&&(c.$template=a);return g.all(c)}}function u(a){var c,b;d.isDefined(c=a.template)?d.isFunction(c)&&(c=c(a.params)):d.isDefined(b= 12 | a.templateUrl)&&(d.isFunction(b)&&(b=b(a.params)),d.isDefined(b)&&(a.loadedTemplateUrl=r.valueOf(b),c=k(b)));return c}function w(){var a,c;d.forEach(f,function(b,g){var f;if(f=!c){var h=e.path();f=b.keys;var l={};if(b.regexp)if(h=b.regexp.exec(h)){for(var k=1,n=h.length;k",d.insertBefore(c.lastChild,d.firstChild)}function d(){var a=t.elements;return"string"==typeof a?a.split(" "):a}function e(a,b){var c=t.elements;"string"!=typeof c&&(c=c.join(" ")),"string"!=typeof a&&(a=a.join(" ")),t.elements=c+" "+a,j(b)}function f(a){var b=s[a[q]];return b||(b={},r++,a[q]=r,s[r]=b),b}function g(a,c,d){if(c||(c=b),l)return c.createElement(a);d||(d=f(c));var e;return e=d.cache[a]?d.cache[a].cloneNode():p.test(a)?(d.cache[a]=d.createElem(a)).cloneNode():d.createElem(a),!e.canHaveChildren||o.test(a)||e.tagUrn?e:d.frag.appendChild(e)}function h(a,c){if(a||(a=b),l)return a.createDocumentFragment();c=c||f(a);for(var e=c.frag.cloneNode(),g=0,h=d(),i=h.length;i>g;g++)e.createElement(h[g]);return e}function i(a,b){b.cache||(b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag()),a.createElement=function(c){return t.shivMethods?g(c,a,b):b.createElem(c)},a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+d().join().replace(/[\w\-:]+/g,function(a){return b.createElem(a),b.frag.createElement(a),'c("'+a+'")'})+");return n}")(t,b.frag)}function j(a){a||(a=b);var d=f(a);return!t.shivCSS||k||d.hasCSS||(d.hasCSS=!!c(a,"article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}mark{background:#FF0;color:#000}template{display:none}")),l||i(a,d),a}var k,l,m="3.7.3",n=a.html5||{},o=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,p=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,q="_html5shiv",r=0,s={};!function(){try{var a=b.createElement("a");a.innerHTML="",k="hidden"in a,l=1==a.childNodes.length||function(){b.createElement("a");var a=b.createDocumentFragment();return"undefined"==typeof a.cloneNode||"undefined"==typeof a.createDocumentFragment||"undefined"==typeof a.createElement}()}catch(c){k=!0,l=!0}}();var t={elements:n.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output picture progress section summary template time video",version:m,shivCSS:n.shivCSS!==!1,supportsUnknownElements:l,shivMethods:n.shivMethods!==!1,type:"default",shivDocument:j,createElement:g,createDocumentFragment:h,addElements:e};a.html5=t,j(b),"object"==typeof module&&module.exports&&(module.exports=t)}("undefined"!=typeof window?window:this,document); -------------------------------------------------------------------------------- /static/frontend/asset/js/ie-emulation-modes-warning.js: -------------------------------------------------------------------------------- 1 | // NOTICE!! DO NOT USE ANY OF THIS JAVASCRIPT 2 | // IT'S JUST JUNK FOR OUR DOCS! 3 | // ++++++++++++++++++++++++++++++++++++++++++ 4 | /*! 5 | * Copyright 2014-2015 Twitter, Inc. 6 | * 7 | * Licensed under the Creative Commons Attribution 3.0 Unported License. For 8 | * details, see https://creativecommons.org/licenses/by/3.0/. 9 | */ 10 | // Intended to prevent false-positive bug reports about Bootstrap not working properly in old versions of IE due to folks testing using IE's unreliable emulation modes. 11 | (function () { 12 | 'use strict'; 13 | 14 | function emulatedIEMajorVersion() { 15 | var groups = /MSIE ([0-9.]+)/.exec(window.navigator.userAgent) 16 | if (groups === null) { 17 | return null 18 | } 19 | var ieVersionNum = parseInt(groups[1], 10) 20 | var ieMajorVersion = Math.floor(ieVersionNum) 21 | return ieMajorVersion 22 | } 23 | 24 | function actualNonEmulatedIEMajorVersion() { 25 | // Detects the actual version of IE in use, even if it's in an older-IE emulation mode. 26 | // IE JavaScript conditional compilation docs: https://msdn.microsoft.com/library/121hztk3%28v=vs.94%29.aspx 27 | // @cc_on docs: https://msdn.microsoft.com/library/8ka90k2e%28v=vs.94%29.aspx 28 | var jscriptVersion = new Function('/*@cc_on return @_jscript_version; @*/')() // jshint ignore:line 29 | if (jscriptVersion === undefined) { 30 | return 11 // IE11+ not in emulation mode 31 | } 32 | if (jscriptVersion < 9) { 33 | return 8 // IE8 (or lower; haven't tested on IE<8) 34 | } 35 | return jscriptVersion // IE9 or IE10 in any mode, or IE11 in non-IE11 mode 36 | } 37 | 38 | var ua = window.navigator.userAgent 39 | if (ua.indexOf('Opera') > -1 || ua.indexOf('Presto') > -1) { 40 | return // Opera, which might pretend to be IE 41 | } 42 | var emulated = emulatedIEMajorVersion() 43 | if (emulated === null) { 44 | return // Not IE 45 | } 46 | var nonEmulated = actualNonEmulatedIEMajorVersion() 47 | 48 | if (emulated !== nonEmulated) { 49 | window.alert('WARNING: You appear to be using IE' + nonEmulated + ' in IE' + emulated + ' emulation mode.\nIE emulation modes can behave significantly differently from ACTUAL older versions of IE.\nPLEASE DON\'T FILE BOOTSTRAP BUGS based on testing in IE emulation modes!') 50 | } 51 | })(); 52 | -------------------------------------------------------------------------------- /static/frontend/asset/js/ie10-viewport-bug-workaround.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * IE10 viewport hack for Surface/desktop Windows 8 bug 3 | * Copyright 2014-2015 Twitter, Inc. 4 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 5 | */ 6 | 7 | // See the Getting Started docs for more information: 8 | // http://getbootstrap.com/getting-started/#support-ie10-width 9 | 10 | (function () { 11 | 'use strict'; 12 | 13 | if (navigator.userAgent.match(/IEMobile\/10\.0/)) { 14 | var msViewportStyle = document.createElement('style') 15 | msViewportStyle.appendChild( 16 | document.createTextNode( 17 | '@-ms-viewport{width:auto!important}' 18 | ) 19 | ) 20 | document.querySelector('head').appendChild(msViewportStyle) 21 | } 22 | 23 | })(); 24 | -------------------------------------------------------------------------------- /static/frontend/asset/js/ie8-responsive-file-warning.js: -------------------------------------------------------------------------------- 1 | // NOTICE!! DO NOT USE ANY OF THIS JAVASCRIPT 2 | // IT'S JUST JUNK FOR OUR DOCS! 3 | // ++++++++++++++++++++++++++++++++++++++++++ 4 | /*! 5 | * Copyright 2011-2015 Twitter, Inc. 6 | * 7 | * Licensed under the Creative Commons Attribution 3.0 Unported License. For 8 | * details, see https://creativecommons.org/licenses/by/3.0/. 9 | */ 10 | // Intended to prevent false-positive bug reports about responsive styling supposedly not working in IE8. 11 | if (window.location.protocol == 'file:') { 12 | window.alert('ERROR: Bootstrap\'s responsive CSS is disabled!\nSee getbootstrap.com/getting-started/#respond-file-proto for details.') 13 | } 14 | -------------------------------------------------------------------------------- /static/frontend/asset/js/notify.min.js: -------------------------------------------------------------------------------- 1 | (function(e){typeof define=="function"&&define.amd?define(["jquery"],e):typeof module=="object"&&module.exports?module.exports=function(t,n){return n===undefined&&(typeof window!="undefined"?n=require("jquery"):n=require("jquery")(t)),e(n),n}:e(jQuery)})(function(e){function A(t,n,i){typeof i=="string"&&(i={className:i}),this.options=E(w,e.isPlainObject(i)?i:{}),this.loadHTML(),this.wrapper=e(h.html),this.options.clickToHide&&this.wrapper.addClass(r+"-hidable"),this.wrapper.data(r,this),this.arrow=this.wrapper.find("."+r+"-arrow"),this.container=this.wrapper.find("."+r+"-container"),this.container.append(this.userContainer),t&&t.length&&(this.elementType=t.attr("type"),this.originalElement=t,this.elem=N(t),this.elem.data(r,this),this.elem.before(this.wrapper)),this.container.hide(),this.run(n)}var t=[].indexOf||function(e){for(var t=0,n=this.length;t\n
\n
\n',css:"."+r+"-corner {\n position: fixed;\n margin: 5px;\n z-index: 1050;\n}\n\n."+r+"-corner ."+r+"-wrapper,\n."+r+"-corner ."+r+"-container {\n position: relative;\n display: block;\n height: inherit;\n width: inherit;\n margin: 3px;\n}\n\n."+r+"-wrapper {\n z-index: 1;\n position: absolute;\n display: inline-block;\n height: 0;\n width: 0;\n}\n\n."+r+"-container {\n display: none;\n z-index: 1;\n position: absolute;\n}\n\n."+r+"-hidable {\n cursor: pointer;\n}\n\n[data-notify-text],[data-notify-html] {\n position: relative;\n}\n\n."+r+"-arrow {\n position: absolute;\n z-index: 2;\n width: 0;\n height: 0;\n}"},p={"border-radius":["-webkit-","-moz-"]},d=function(e){return c[e]},v=function(e){if(!e)throw"Missing Style name";c[e]&&delete c[e]},m=function(t,i){if(!t)throw"Missing Style name";if(!i)throw"Missing Style definition";if(!i.html)throw"Missing Style HTML";var s=c[t];s&&s.cssElem&&(window.console&&console.warn(n+": overwriting style '"+t+"'"),c[t].cssElem.remove()),i.name=t,c[t]=i;var o="";i.classes&&e.each(i.classes,function(t,n){return o+="."+r+"-"+i.name+"-"+t+" {\n",e.each(n,function(t,n){return p[t]&&e.each(p[t],function(e,r){return o+=" "+r+t+": "+n+";\n"}),o+=" "+t+": "+n+";\n"}),o+="}\n"}),i.css&&(o+="/* styles for "+i.name+" */\n"+i.css),o&&(i.cssElem=g(o),i.cssElem.attr("id","notify-"+i.name));var u={},a=e(i.html);y("html",a,u),y("text",a,u),i.fields=u},g=function(t){var n,r,i;r=x("style"),r.attr("type","text/css"),e("head").append(r);try{r.html(t)}catch(s){r[0].styleSheet.cssText=t}return r},y=function(t,n,r){var s;return t!=="html"&&(t="text"),s="data-notify-"+t,b(n,"["+s+"]").each(function(){var n;n=e(this).attr(s),n||(n=i),r[n]=t})},b=function(e,t){return e.is(t)?e:e.find(t)},w={clickToHide:!0,autoHide:!0,autoHideDelay:5e3,arrowShow:!0,arrowSize:5,breakNewLines:!0,elementPosition:"bottom",globalPosition:"top right",style:"bootstrap",className:"error",showAnimation:"slideDown",showDuration:400,hideAnimation:"slideUp",hideDuration:200,gap:5},E=function(t,n){var r;return r=function(){},r.prototype=t,e.extend(!0,new r,n)},S=function(t){return e.extend(w,t)},x=function(t){return e("<"+t+">")},T={},N=function(t){var n;return t.is("[type=radio]")&&(n=t.parents("form:first").find("[type=radio]").filter(function(n,r){return e(r).attr("name")===t.attr("name")}),t=n.first()),t},C=function(e,t,n){var r,i;if(typeof n=="string")n=parseInt(n,10);else if(typeof n!="number")return;if(isNaN(n))return;return r=s[f[t.charAt(0)]],i=t,e[r]!==undefined&&(t=s[r.charAt(0)],n=-n),e[t]===undefined?e[t]=n:e[t]+=n,null},k=function(e,t,n){if(e==="l"||e==="t")return 0;if(e==="c"||e==="m")return n/2-t/2;if(e==="r"||e==="b")return n-t;throw"Invalid alignment"},L=function(e){return L.e=L.e||x("div"),L.e.text(e).html()};A.prototype.loadHTML=function(){var t;t=this.getStyle(),this.userContainer=e(t.html),this.userFields=t.fields},A.prototype.show=function(e,t){var n,r,i,s,o;r=function(n){return function(){!e&&!n.elem&&n.destroy();if(t)return t()}}(this),o=this.container.parent().parents(":hidden").length>0,i=this.container.add(this.arrow),n=[];if(o&&e)s="show";else if(o&&!e)s="hide";else if(!o&&e)s=this.options.showAnimation,n.push(this.options.showDuration);else{if(!!o||!!e)return r();s=this.options.hideAnimation,n.push(this.options.hideDuration)}return n.push(r),i[s].apply(i,n)},A.prototype.setGlobalPosition=function(){var t=this.getPosition(),n=t[0],i=t[1],o=s[n],u=s[i],a=n+"|"+i,f=T[a];if(!f||!document.contains(f[0])){f=T[a]=x("div");var l={};l[o]=0,u==="middle"?l.top="45%":u==="center"?l.left="45%":l[u]=0,f.css(l).addClass(r+"-corner"),e("body").append(f)}return f.prepend(this.wrapper)},A.prototype.setElementPosition=function(){var n,r,i,l,c,h,p,d,v,m,g,y,b,w,E,S,x,T,N,L,A,O,M,_,D,P,H,B,j;H=this.getPosition(),_=H[0],O=H[1],M=H[2],g=this.elem.position(),d=this.elem.outerHeight(),y=this.elem.outerWidth(),v=this.elem.innerHeight(),m=this.elem.innerWidth(),j=this.wrapper.position(),c=this.container.height(),h=this.container.width(),T=s[_],L=f[_],A=s[L],p={},p[A]=_==="b"?d:_==="r"?y:0,C(p,"top",g.top-j.top),C(p,"left",g.left-j.left),B=["top","left"];for(w=0,S=B.length;w=0&&C(r,s[O],i*2)}t.call(u,_)>=0?(C(p,"left",k(O,h,y)),r&&C(r,"left",k(O,i,m))):t.call(o,_)>=0&&(C(p,"top",k(O,c,d)),r&&C(r,"top",k(O,i,v))),this.container.is(":visible")&&(p.display="block"),this.container.removeAttr("style").css(p);if(r)return this.arrow.removeAttr("style").css(r)},A.prototype.getPosition=function(){var e,n,r,i,s,f,c,h;h=this.options.position||(this.elem?this.options.elementPosition:this.options.globalPosition),e=l(h),e.length===0&&(e[0]="b");if(n=e[0],t.call(a,n)<0)throw"Must be one of ["+a+"]";if(e.length===1||(r=e[0],t.call(u,r)>=0)&&(i=e[1],t.call(o,i)<0)||(s=e[0],t.call(o,s)>=0)&&(f=e[1],t.call(u,f)<0))e[1]=(c=e[0],t.call(o,c)>=0)?"m":"l";return e.length===2&&(e[2]=e[1]),e},A.prototype.getStyle=function(e){var t;e||(e=this.options.style),e||(e="default"),t=c[e];if(!t)throw"Missing style: "+e;return t},A.prototype.updateClasses=function(){var t,n;return t=["base"],e.isArray(this.options.className)?t=t.concat(this.options.className):this.options.className&&t.push(this.options.className),n=this.getStyle(),t=e.map(t,function(e){return r+"-"+n.name+"-"+e}).join(" "),this.userContainer.attr("class",t)},A.prototype.run=function(t,n){var r,s,o,u,a;e.isPlainObject(n)?e.extend(this.options,n):e.type(n)==="string"&&(this.options.className=n);if(this.container&&!t){this.show(!1);return}if(!this.container&&!t)return;s={},e.isPlainObject(t)?s=t:s[i]=t;for(o in s){r=s[o],u=this.userFields[o];if(!u)continue;u==="text"&&(r=L(r),this.options.breakNewLines&&(r=r.replace(/\n/g,"
"))),a=o===i?"":"="+o,b(this.userContainer,"[data-notify-"+u+a+"]").html(r)}this.updateClasses(),this.elem?this.setElementPosition():this.setGlobalPosition(),this.show(!0),this.options.autoHide&&(clearTimeout(this.autohideTimer),this.autohideTimer=setTimeout(this.show.bind(this,!1),this.options.autoHideDelay))},A.prototype.destroy=function(){this.wrapper.data(r,null),this.wrapper.remove()},e[n]=function(t,r,i){return t&&t.nodeName||t.jquery?e(t)[n](r,i):(i=r,r=t,new A(null,r,i)),t},e.fn[n]=function(t,n){return e(this).each(function(){var i=N(e(this)).data(r);i&&i.destroy();var s=new A(e(this),t,n)}),this},e.extend(e[n],{defaults:S,addStyle:m,removeStyle:v,pluginOptions:w,getStyle:d,insertCSS:g}),m("bootstrap",{html:"
\n\n
",classes:{base:{"font-weight":"bold",padding:"8px 15px 8px 14px","text-shadow":"0 1px 0 rgba(255, 255, 255, 0.5)","background-color":"#fcf8e3",border:"1px solid #fbeed5","border-radius":"4px","white-space":"nowrap","padding-left":"25px","background-repeat":"no-repeat","background-position":"3px 7px"},error:{color:"#B94A48","background-color":"#F2DEDE","border-color":"#EED3D7","background-image":"url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAtRJREFUeNqkVc1u00AQHq+dOD+0poIQfkIjalW0SEGqRMuRnHos3DjwAH0ArlyQeANOOSMeAA5VjyBxKBQhgSpVUKKQNGloFdw4cWw2jtfMOna6JOUArDTazXi/b3dm55socPqQhFka++aHBsI8GsopRJERNFlY88FCEk9Yiwf8RhgRyaHFQpPHCDmZG5oX2ui2yilkcTT1AcDsbYC1NMAyOi7zTX2Agx7A9luAl88BauiiQ/cJaZQfIpAlngDcvZZMrl8vFPK5+XktrWlx3/ehZ5r9+t6e+WVnp1pxnNIjgBe4/6dAysQc8dsmHwPcW9C0h3fW1hans1ltwJhy0GxK7XZbUlMp5Ww2eyan6+ft/f2FAqXGK4CvQk5HueFz7D6GOZtIrK+srupdx1GRBBqNBtzc2AiMr7nPplRdKhb1q6q6zjFhrklEFOUutoQ50xcX86ZlqaZpQrfbBdu2R6/G19zX6XSgh6RX5ubyHCM8nqSID6ICrGiZjGYYxojEsiw4PDwMSL5VKsC8Yf4VRYFzMzMaxwjlJSlCyAQ9l0CW44PBADzXhe7xMdi9HtTrdYjFYkDQL0cn4Xdq2/EAE+InCnvADTf2eah4Sx9vExQjkqXT6aAERICMewd/UAp/IeYANM2joxt+q5VI+ieq2i0Wg3l6DNzHwTERPgo1ko7XBXj3vdlsT2F+UuhIhYkp7u7CarkcrFOCtR3H5JiwbAIeImjT/YQKKBtGjRFCU5IUgFRe7fF4cCNVIPMYo3VKqxwjyNAXNepuopyqnld602qVsfRpEkkz+GFL1wPj6ySXBpJtWVa5xlhpcyhBNwpZHmtX8AGgfIExo0ZpzkWVTBGiXCSEaHh62/PoR0p/vHaczxXGnj4bSo+G78lELU80h1uogBwWLf5YlsPmgDEd4M236xjm+8nm4IuE/9u+/PH2JXZfbwz4zw1WbO+SQPpXfwG/BBgAhCNZiSb/pOQAAAAASUVORK5CYII=)"},success:{color:"#468847","background-color":"#DFF0D8","border-color":"#D6E9C6","background-image":"url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAutJREFUeNq0lctPE0Ecx38zu/RFS1EryqtgJFA08YCiMZIAQQ4eRG8eDGdPJiYeTIwHTfwPiAcvXIwXLwoXPaDxkWgQ6islKlJLSQWLUraPLTv7Gme32zoF9KSTfLO7v53vZ3d/M7/fIth+IO6INt2jjoA7bjHCJoAlzCRw59YwHYjBnfMPqAKWQYKjGkfCJqAF0xwZjipQtA3MxeSG87VhOOYegVrUCy7UZM9S6TLIdAamySTclZdYhFhRHloGYg7mgZv1Zzztvgud7V1tbQ2twYA34LJmF4p5dXF1KTufnE+SxeJtuCZNsLDCQU0+RyKTF27Unw101l8e6hns3u0PBalORVVVkcaEKBJDgV3+cGM4tKKmI+ohlIGnygKX00rSBfszz/n2uXv81wd6+rt1orsZCHRdr1Imk2F2Kob3hutSxW8thsd8AXNaln9D7CTfA6O+0UgkMuwVvEFFUbbAcrkcTA8+AtOk8E6KiQiDmMFSDqZItAzEVQviRkdDdaFgPp8HSZKAEAL5Qh7Sq2lIJBJwv2scUqkUnKoZgNhcDKhKg5aH+1IkcouCAdFGAQsuWZYhOjwFHQ96oagWgRoUov1T9kRBEODAwxM2QtEUl+Wp+Ln9VRo6BcMw4ErHRYjH4/B26AlQoQQTRdHWwcd9AH57+UAXddvDD37DmrBBV34WfqiXPl61g+vr6xA9zsGeM9gOdsNXkgpEtTwVvwOklXLKm6+/p5ezwk4B+j6droBs2CsGa/gNs6RIxazl4Tc25mpTgw/apPR1LYlNRFAzgsOxkyXYLIM1V8NMwyAkJSctD1eGVKiq5wWjSPdjmeTkiKvVW4f2YPHWl3GAVq6ymcyCTgovM3FzyRiDe2TaKcEKsLpJvNHjZgPNqEtyi6mZIm4SRFyLMUsONSSdkPeFtY1n0mczoY3BHTLhwPRy9/lzcziCw9ACI+yql0VLzcGAZbYSM5CCSZg1/9oc/nn7+i8N9p/8An4JMADxhH+xHfuiKwAAAABJRU5ErkJggg==)"},info:{color:"#3A87AD","background-color":"#D9EDF7","border-color":"#BCE8F1","background-image":"url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3QYFAhkSsdes/QAAA8dJREFUOMvVlGtMW2UYx//POaWHXg6lLaW0ypAtw1UCgbniNOLcVOLmAjHZolOYlxmTGXVZdAnRfXQm+7SoU4mXaOaiZsEpC9FkiQs6Z6bdCnNYruM6KNBw6YWewzl9z+sHImEWv+vz7XmT95f/+3/+7wP814v+efDOV3/SoX3lHAA+6ODeUFfMfjOWMADgdk+eEKz0pF7aQdMAcOKLLjrcVMVX3xdWN29/GhYP7SvnP0cWfS8caSkfHZsPE9Fgnt02JNutQ0QYHB2dDz9/pKX8QjjuO9xUxd/66HdxTeCHZ3rojQObGQBcuNjfplkD3b19Y/6MrimSaKgSMmpGU5WevmE/swa6Oy73tQHA0Rdr2Mmv/6A1n9w9suQ7097Z9lM4FlTgTDrzZTu4StXVfpiI48rVcUDM5cmEksrFnHxfpTtU/3BFQzCQF/2bYVoNbH7zmItbSoMj40JSzmMyX5qDvriA7QdrIIpA+3cdsMpu0nXI8cV0MtKXCPZev+gCEM1S2NHPvWfP/hL+7FSr3+0p5RBEyhEN5JCKYr8XnASMT0xBNyzQGQeI8fjsGD39RMPk7se2bd5ZtTyoFYXftF6y37gx7NeUtJJOTFlAHDZLDuILU3j3+H5oOrD3yWbIztugaAzgnBKJuBLpGfQrS8wO4FZgV+c1IxaLgWVU0tMLEETCos4xMzEIv9cJXQcyagIwigDGwJgOAtHAwAhisQUjy0ORGERiELgG4iakkzo4MYAxcM5hAMi1WWG1yYCJIcMUaBkVRLdGeSU2995TLWzcUAzONJ7J6FBVBYIggMzmFbvdBV44Corg8vjhzC+EJEl8U1kJtgYrhCzgc/vvTwXKSib1paRFVRVORDAJAsw5FuTaJEhWM2SHB3mOAlhkNxwuLzeJsGwqWzf5TFNdKgtY5qHp6ZFf67Y/sAVadCaVY5YACDDb3Oi4NIjLnWMw2QthCBIsVhsUTU9tvXsjeq9+X1d75/KEs4LNOfcdf/+HthMnvwxOD0wmHaXr7ZItn2wuH2SnBzbZAbPJwpPx+VQuzcm7dgRCB57a1uBzUDRL4bfnI0RE0eaXd9W89mpjqHZnUI5Hh2l2dkZZUhOqpi2qSmpOmZ64Tuu9qlz/SEXo6MEHa3wOip46F1n7633eekV8ds8Wxjn37Wl63VVa+ej5oeEZ/82ZBETJjpJ1Rbij2D3Z/1trXUvLsblCK0XfOx0SX2kMsn9dX+d+7Kf6h8o4AIykuffjT8L20LU+w4AZd5VvEPY+XpWqLV327HR7DzXuDnD8r+ovkBehJ8i+y8YAAAAASUVORK5CYII=)"},warn:{color:"#C09853","background-color":"#FCF8E3","border-color":"#FBEED5","background-image":"url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAMAAAC6V+0/AAABJlBMVEXr6eb/2oD/wi7/xjr/0mP/ykf/tQD/vBj/3o7/uQ//vyL/twebhgD/4pzX1K3z8e349vK6tHCilCWbiQymn0jGworr6dXQza3HxcKkn1vWvV/5uRfk4dXZ1bD18+/52YebiAmyr5S9mhCzrWq5t6ufjRH54aLs0oS+qD751XqPhAybhwXsujG3sm+Zk0PTwG6Shg+PhhObhwOPgQL4zV2nlyrf27uLfgCPhRHu7OmLgAafkyiWkD3l49ibiAfTs0C+lgCniwD4sgDJxqOilzDWowWFfAH08uebig6qpFHBvH/aw26FfQTQzsvy8OyEfz20r3jAvaKbhgG9q0nc2LbZxXanoUu/u5WSggCtp1anpJKdmFz/zlX/1nGJiYmuq5Dx7+sAAADoPUZSAAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQfdBgUBGhh4aah5AAAAlklEQVQY02NgoBIIE8EUcwn1FkIXM1Tj5dDUQhPU502Mi7XXQxGz5uVIjGOJUUUW81HnYEyMi2HVcUOICQZzMMYmxrEyMylJwgUt5BljWRLjmJm4pI1hYp5SQLGYxDgmLnZOVxuooClIDKgXKMbN5ggV1ACLJcaBxNgcoiGCBiZwdWxOETBDrTyEFey0jYJ4eHjMGWgEAIpRFRCUt08qAAAAAElFTkSuQmCC)"}}}),e(function(){g(h.css).attr("id","core-notify"),e(document).on("click","."+r+"-hidable",function(t){e(this).trigger("notify-hide")}),e(document).on("notify-hide","."+r+"-wrapper",function(t){var n=e(this).data(r);n&&n.show(!1)})})}) -------------------------------------------------------------------------------- /static/frontend/asset/js/respond.min.js: -------------------------------------------------------------------------------- 1 | /*! Respond.js v1.4.2: min/max-width media query polyfill * Copyright 2013 Scott Jehl 2 | * Licensed under https://github.com/scottjehl/Respond/blob/master/LICENSE-MIT 3 | * */ 4 | 5 | !function(a){"use strict";a.matchMedia=a.matchMedia||function(a){var b,c=a.documentElement,d=c.firstElementChild||c.firstChild,e=a.createElement("body"),f=a.createElement("div");return f.id="mq-test-1",f.style.cssText="position:absolute;top:-100em",e.style.background="none",e.appendChild(f),function(a){return f.innerHTML='­',c.insertBefore(e,d),b=42===f.offsetWidth,c.removeChild(e),{matches:b,media:a}}}(a.document)}(this),function(a){"use strict";function b(){u(!0)}var c={};a.respond=c,c.update=function(){};var d=[],e=function(){var b=!1;try{b=new a.XMLHttpRequest}catch(c){b=new a.ActiveXObject("Microsoft.XMLHTTP")}return function(){return b}}(),f=function(a,b){var c=e();c&&(c.open("GET",a,!0),c.onreadystatechange=function(){4!==c.readyState||200!==c.status&&304!==c.status||b(c.responseText)},4!==c.readyState&&c.send(null))};if(c.ajax=f,c.queue=d,c.regex={media:/@media[^\{]+\{([^\{\}]*\{[^\}\{]*\})+/gi,keyframes:/@(?:\-(?:o|moz|webkit)\-)?keyframes[^\{]+\{(?:[^\{\}]*\{[^\}\{]*\})+[^\}]*\}/gi,urls:/(url\()['"]?([^\/\)'"][^:\)'"]+)['"]?(\))/g,findStyles:/@media *([^\{]+)\{([\S\s]+?)$/,only:/(only\s+)?([a-zA-Z]+)\s?/,minw:/\([\s]*min\-width\s*:[\s]*([\s]*[0-9\.]+)(px|em)[\s]*\)/,maxw:/\([\s]*max\-width\s*:[\s]*([\s]*[0-9\.]+)(px|em)[\s]*\)/},c.mediaQueriesSupported=a.matchMedia&&null!==a.matchMedia("only all")&&a.matchMedia("only all").matches,!c.mediaQueriesSupported){var g,h,i,j=a.document,k=j.documentElement,l=[],m=[],n=[],o={},p=30,q=j.getElementsByTagName("head")[0]||k,r=j.getElementsByTagName("base")[0],s=q.getElementsByTagName("link"),t=function(){var a,b=j.createElement("div"),c=j.body,d=k.style.fontSize,e=c&&c.style.fontSize,f=!1;return b.style.cssText="position:absolute;font-size:1em;width:1em",c||(c=f=j.createElement("body"),c.style.background="none"),k.style.fontSize="100%",c.style.fontSize="100%",c.appendChild(b),f&&k.insertBefore(c,k.firstChild),a=b.offsetWidth,f?k.removeChild(c):c.removeChild(b),k.style.fontSize=d,e&&(c.style.fontSize=e),a=i=parseFloat(a)},u=function(b){var c="clientWidth",d=k[c],e="CSS1Compat"===j.compatMode&&d||j.body[c]||d,f={},o=s[s.length-1],r=(new Date).getTime();if(b&&g&&p>r-g)return a.clearTimeout(h),h=a.setTimeout(u,p),void 0;g=r;for(var v in l)if(l.hasOwnProperty(v)){var w=l[v],x=w.minw,y=w.maxw,z=null===x,A=null===y,B="em";x&&(x=parseFloat(x)*(x.indexOf(B)>-1?i||t():1)),y&&(y=parseFloat(y)*(y.indexOf(B)>-1?i||t():1)),w.hasquery&&(z&&A||!(z||e>=x)||!(A||y>=e))||(f[w.media]||(f[w.media]=[]),f[w.media].push(m[w.rules]))}for(var C in n)n.hasOwnProperty(C)&&n[C]&&n[C].parentNode===q&&q.removeChild(n[C]);n.length=0;for(var D in f)if(f.hasOwnProperty(D)){var E=j.createElement("style"),F=f[D].join("\n");E.type="text/css",E.media=D,q.insertBefore(E,o.nextSibling),E.styleSheet?E.styleSheet.cssText=F:E.appendChild(j.createTextNode(F)),n.push(E)}},v=function(a,b,d){var e=a.replace(c.regex.keyframes,"").match(c.regex.media),f=e&&e.length||0;b=b.substring(0,b.lastIndexOf("/"));var g=function(a){return a.replace(c.regex.urls,"$1"+b+"$2$3")},h=!f&&d;b.length&&(b+="/"),h&&(f=1);for(var i=0;f>i;i++){var j,k,n,o;h?(j=d,m.push(g(a))):(j=e[i].match(c.regex.findStyles)&&RegExp.$1,m.push(RegExp.$2&&g(RegExp.$2))),n=j.split(","),o=n.length;for(var p=0;o>p;p++)k=n[p],l.push({media:k.split("(")[0].match(c.regex.only)&&RegExp.$2||"all",rules:m.length-1,hasquery:k.indexOf("(")>-1,minw:k.match(c.regex.minw)&&parseFloat(RegExp.$1)+(RegExp.$2||""),maxw:k.match(c.regex.maxw)&&parseFloat(RegExp.$1)+(RegExp.$2||"")})}u()},w=function(){if(d.length){var b=d.shift();f(b.href,function(c){v(c,b.href,b.media),o[b.href]=!0,a.setTimeout(function(){w()},0)})}},x=function(){for(var b=0;b 0 ; i--){ 31 | if(rel[i] === '/'){ 32 | break 33 | } 34 | } 35 | return rel.substring(i + 1 , rel.length); 36 | } 37 | 38 | $scope.formatDateTime = function(timeString){ 39 | return moment(timeString).format('YYYY-MM-DD h:mm:ss A'); 40 | }; 41 | 42 | $scope.toggleShowCrashOutputModal = function(crash){ 43 | $scope.selectedCrash = crash; 44 | MiscAPI.download($scope.getStaticURL(crash.dbg_file)).then(function(data){ 45 | $scope.selectedCrash.dbg_file_output = data; 46 | $('#crashOutputModal').modal('show'); 47 | }, function(){ 48 | 49 | }) 50 | } 51 | 52 | $scope.initCrashes(); 53 | $rootScope.crashManagerTimer = $interval($scope.initCrashes, config.refreshTime); 54 | }); 55 | -------------------------------------------------------------------------------- /static/frontend/controller/err.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('frontApp') 4 | .controller('ErrLogCtrl', 5 | function ($scope, $rootScope, $interval, MiscAPI, config) { 6 | $scope.initLog = function() { 7 | MiscAPI.log().then(function(data){ 8 | $scope.log = data.log; 9 | console.log($scope.log); 10 | }) 11 | }; 12 | 13 | 14 | $scope.initLog(); 15 | $rootScope.errLogTimer = $interval($scope.initLog, config.refreshTime); 16 | }); 17 | -------------------------------------------------------------------------------- /static/frontend/controller/job.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('frontApp') 4 | .controller('JobManagerCtrl', 5 | function ($scope, $rootScope, JobAPI, TargetAPI, EngineAPI, OptionAPI, HostAPI, MiscAPI, $q, config, $interval) { 6 | 7 | $scope.shortenName = function(name){ 8 | if(angular.isDefined(name)){ 9 | if (name.length > 25){ 10 | return name.substring(0, 22) + '...' 11 | } 12 | else{ 13 | return name; 14 | }; 15 | } 16 | } 17 | 18 | $scope.toggleJobModal = function(){ 19 | 20 | $scope.newJob = {}; 21 | $('#addJobModal').modal('show'); 22 | 23 | } 24 | 25 | $scope.listJob = function(){ 26 | JobAPI.list().then(function(data){ 27 | console.log(data); 28 | $scope.jobs = data; 29 | }) 30 | }; 31 | 32 | $scope.getOptionType = function(opt){ 33 | if(angular.isDefined($scope.option_types)){ 34 | for(var i = 0; i < $scope.option_types.length; i++){ 35 | if(opt.type_id === $scope.option_types[i].id){ 36 | return $scope.option_types[i].name; 37 | }; 38 | }; 39 | }; 40 | }; 41 | 42 | $scope.getOptionById = function(id){ 43 | if(angular.isDefined($scope.options)){ 44 | for(var i = 0; i < $scope.options.length; i++){ 45 | if($scope.options[i].id === id){ 46 | return $scope.options[i]; 47 | } 48 | } 49 | } 50 | } 51 | 52 | $scope.listOption = function(){ 53 | OptionAPI.list().then(function(data){ 54 | $scope.options = data; 55 | OptionAPI.type().then(function(data){ 56 | $scope.option_types = data; 57 | $scope.options.forEach(function(option){ 58 | for(var i = 0; i < $scope.option_types.length; i++){ 59 | if(option.type_id === $scope.option_types[i].id){ 60 | option.type = $scope.option_types[i]; 61 | break; 62 | }; 63 | }; 64 | }); 65 | }); 66 | }); 67 | }; 68 | 69 | $scope.get_state_id_by_name = function(name){ 70 | for(var i = 0 ; i < $scope.states.length; i++){ 71 | if($scope.states[i].name === name) 72 | return $scope.states[i].id; 73 | } 74 | return -1; 75 | } 76 | 77 | $scope.createJob = function(job){ 78 | if(angular.isDefined(job.nodeCount) 79 | && angular.isDefined(job.target) 80 | && angular.isDefined(job.name) 81 | && angular.isDefined(job.engine)){ 82 | 83 | var state_id = $scope.get_state_id_by_name('Queued'); 84 | var promises = []; 85 | var options = []; 86 | job.engine.options.forEach(function(opt){ 87 | switch($scope.getOptionType(opt)){ 88 | case 'FIELD': 89 | var val = angular.isDefined(opt.value)? opt.value.toString() : ''; 90 | promises.push({ 91 | id: opt.id, 92 | value: val 93 | }); 94 | break; 95 | case 'CHECKBOX': 96 | var val = angular.isDefined(opt.value)? opt.value.toString() : 'false'; 97 | promises.push({ 98 | id: opt.id, 99 | value: val 100 | }); 101 | break; 102 | case 'LIST': 103 | var lst = []; 104 | if(angular.isDefined(opt.list)){ 105 | opt.list.forEach(function(tag){ 106 | lst.push(tag.text); 107 | }) 108 | } 109 | promises.push({ 110 | id: opt.id, 111 | value: lst.toString() 112 | }); 113 | break; 114 | case 'FILE': 115 | if(angular.isDefined(opt.value)){ 116 | promises.push(MiscAPI.upload(opt.value, { 117 | id: opt.id, 118 | value: '' 119 | })) 120 | } 121 | else{ 122 | promises.push({ 123 | id: opt.id, 124 | value: '' 125 | }); 126 | } 127 | break; 128 | } 129 | }) 130 | $q.all(promises).then(function(result){ 131 | options = result 132 | 133 | if(state_id === -1){ 134 | $.notify("invalid input.", "error"); 135 | return; 136 | } 137 | 138 | promises = []; 139 | for(var i = 0; i< job.nodeCount; i++){ 140 | promises.push(JobAPI.create({ 141 | name: job.name + '_' + (i+1), 142 | target_id: job.target.id, 143 | engine_id: job.engine.id, 144 | state_id: state_id, 145 | options: options 146 | })); 147 | } 148 | 149 | $q.all(promises).then(function(){ 150 | $scope.listJob(); 151 | $('#addJobModal').modal('hide'); 152 | }, function(){ 153 | $.notify("invalid input.", "error"); 154 | $('#addJobModal').modal('hide'); 155 | }); 156 | }, function(){ 157 | $.notify("invalid input.", "error"); 158 | $('#addJobModal').modal('hide'); 159 | }) 160 | } 161 | else{ 162 | $.notify("invalid input.", "error"); 163 | }; 164 | } 165 | 166 | $scope.getTargetName = function(job){ 167 | for(var i = 0; i < $scope.targets.length; i++){ 168 | if($scope.targets[i].id === job.target_id){ 169 | return $scope.targets[i].name; 170 | } 171 | } 172 | 173 | } 174 | 175 | $scope.getHostName = function(job){ 176 | for(var i = 0; i < $scope.hosts.length; i++){ 177 | if($scope.hosts[i].id === job.host_id){ 178 | return $scope.hosts[i].name + '(id=' + $scope.hosts[i].id + ')'; 179 | } 180 | } 181 | return "" 182 | } 183 | 184 | $scope.getEngineName = function(job){ 185 | for(var i = 0; i < $scope.engines.length; i++){ 186 | if($scope.engines[i].id === job.engine_id){ 187 | return $scope.engines[i].name; 188 | } 189 | } 190 | } 191 | 192 | $scope.getStateName = function(job){ 193 | for(var i = 0; i < $scope.states.length; i++){ 194 | if($scope.states[i].id === job.state_id){ 195 | return $scope.states[i].name; 196 | } 197 | } 198 | } 199 | 200 | $scope.formatDateTime = function(timeString){ 201 | return moment(timeString).format('YYYY-MM-DD h:mm:ss A'); 202 | }; 203 | 204 | $scope.initJobs = function(){ 205 | if(angular.isUndefined($scope.options)){ 206 | $scope.listOption(); 207 | } 208 | HostAPI.list().then(function(data){ 209 | $scope.hosts = data.hosts; 210 | JobAPI.state().then(function(data){ 211 | $scope.states = data; 212 | EngineAPI.list().then(function(data){ 213 | $scope.engines = data; 214 | TargetAPI.list().then(function(data){ 215 | $scope.targets= data; 216 | $scope.listJob(); 217 | }); 218 | }); 219 | }) 220 | }) 221 | 222 | 223 | $scope.nodeCountRange = []; 224 | for(var i = 1 ; i <= 100; i++){ 225 | $scope.nodeCountRange.push(i); 226 | } 227 | } 228 | 229 | $scope.toggleShowJobOutputModal = function(job){ 230 | $scope.selectedJob = job; 231 | $('#jobOutputModal').modal('show'); 232 | } 233 | 234 | $scope.formatJobOutput = function(output){ 235 | if (angular.isDefined(output) && output.length > 0 && output !== 'None'){ 236 | return atob(output) 237 | } 238 | return "" 239 | } 240 | 241 | $scope.repeatJob = function(job){ 242 | var state_id = $scope.get_state_id_by_name('Queued'); 243 | JobAPI.update(job.id, { 244 | state_id : state_id 245 | }).then(function(){ 246 | $scope.listJob(); 247 | }) 248 | } 249 | 250 | $scope.removeJob = function(job){ 251 | JobAPI.remove(job.id).then(function(){ 252 | $scope.listJob(); 253 | }) 254 | } 255 | 256 | $scope.initJobs(); 257 | $rootScope.jobManagerTimer = $interval($scope.initJobs, config.refreshTime); 258 | }); 259 | -------------------------------------------------------------------------------- /static/frontend/controller/node.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('frontApp') 4 | .controller('NodeManagerCtrl', 5 | function ($scope, $rootScope, ArchAPI, PlatformAPI, HostAPI, EngineAPI, TargetAPI, ConfigAPI, config, $interval) { 6 | $scope.listHost = function(){ 7 | HostAPI.list().then(function(data){ 8 | $scope.result = data; 9 | $scope.nodes = $scope.result.hosts; 10 | }) 11 | } 12 | $scope.initNodes = function() { 13 | if(angular.isUndefined($scope.arches)){ 14 | ArchAPI.list().then(function(data){ 15 | $scope.arches = data; 16 | if(angular.isUndefined($scope.platforms)){ 17 | PlatformAPI.list().then(function(data){ 18 | $scope.platforms = data; 19 | $scope.listHost(); 20 | }); 21 | }; 22 | }); 23 | } 24 | else{ 25 | $scope.listHost(); 26 | }; 27 | 28 | // if(angular.isUndefined($scope.engines)){ 29 | // EngineAPI.list().then(function(data){ 30 | // $scope.engines = data; 31 | // 32 | // }); 33 | // }; 34 | // if(angular.isUndefined($scope.targets)){ 35 | // TargetAPI.list().then(function(data){ 36 | // $scope.targets = data; 37 | // }); 38 | // }; 39 | }; 40 | $scope.formatDateTime = function(timeString){ 41 | return moment(timeString).format('YYYY-MM-DD h:mm:ss A'); 42 | }; 43 | $scope.getPlatformName = function(node){ 44 | for(var i = 0; i < $scope.platforms.length; i++){ 45 | if($scope.platforms[i].id === node.platform_id){ 46 | return $scope.platforms[i].name; 47 | } 48 | } 49 | }; 50 | $scope.getArchName = function(node){ 51 | for(var i = 0; i < $scope.arches.length; i++){ 52 | if($scope.arches[i].id === node.arch_id){ 53 | return $scope.arches[i].name; 54 | } 55 | } 56 | }; 57 | $scope.uptime= function(node){ 58 | if(angular.isDefined($scope.result)){ 59 | var serverMoment = moment($scope.result.serverTime) 60 | var updateMoment = moment(node.updated_at) 61 | var duration = moment.duration(serverMoment.diff(updateMoment)); 62 | return duration.format("HH:mm") 63 | } 64 | } 65 | // $scope.setSelectedNode = function(node){ 66 | // $scope.selectedNode = node; 67 | // ConfigAPI.read_by_host_id($scope.selectedNode.id) 68 | // .then( 69 | // function(data){ 70 | // $scope.selectedNode.config = data; 71 | // for(var i = 0; i < $scope.platforms.length; i++){ 72 | // if($scope.platforms[i].id == data.platform_id){ 73 | // $scope.selectedNode.platform = $scope.platforms[i]; 74 | // break; 75 | // }; 76 | // }; 77 | // for(var i = 0; i < $scope.arches.length; i++){ 78 | // if($scope.arches[i].id == data.arch_id){ 79 | // $scope.selectedNode.arch = $scope.arches[i]; 80 | // break; 81 | // }; 82 | // }; 83 | // $scope.selectedNode.engine_tags = []; 84 | // $scope.selectedNode.config.engines.forEach(function(engine){ 85 | // $scope.selectedNode.engine_tags.push({ 86 | // text: engine.name, 87 | // engine: engine, 88 | // }); 89 | // }); 90 | // $scope.selectedNode.target_tags = []; 91 | // $scope.selectedNode.config.targets.forEach(function(target){ 92 | // $scope.selectedNode.target_tags.push({ 93 | // text: target.name, 94 | // target: target, 95 | // }); 96 | // }); 97 | // $('#nodeConfigModal').modal('show'); 98 | // }, 99 | // function(err){ 100 | // $scope.selectedNode.engine_tags = []; 101 | // $scope.selectedNode.target_tags = []; 102 | // $('#nodeConfigModal').modal('show'); 103 | // } 104 | // ); 105 | // } 106 | // 107 | // $scope.shortenName = function(name){ 108 | // if(angular.isDefined(name)){ 109 | // if (name.length > 25){ 110 | // return name.substring(0, 22) + '...' 111 | // } 112 | // else{ 113 | // return name; 114 | // }; 115 | // } 116 | // } 117 | // $scope.saveNodeConfig = function(){ 118 | // var node = $scope.selectedNode; 119 | // var engines = []; 120 | // for(var i = 0; i < node.engine_tags.length; i++){ 121 | // engines.push(node.engine_tags[i].engine.id); 122 | // }; 123 | // var targets = []; 124 | // for(var i = 0; i < node.target_tags.length; i++){ 125 | // targets.push(node.target_tags[i].target.id); 126 | // }; 127 | // 128 | // ConfigAPI.update($scope.selectedNode.config.id, { 129 | // host_id: node.id, 130 | // platform_id: node.platform.id, 131 | // arch_id: node.arch.id, 132 | // engines: engines, 133 | // targets: targets 134 | // }).then(function(data){ 135 | // $.notify("Config updated successfully.", "success"); 136 | // $('#nodeConfigModal').modal('hide'); 137 | // }, function(fail){ 138 | // $.notify("Config failed.", "error"); 139 | // }); 140 | // } 141 | // 142 | // $scope.addNodeConfig = function(){ 143 | // var node = $scope.selectedNode; 144 | // var engines = []; 145 | // for(var i = 0; i < node.engine_tags.length; i++){ 146 | // engines.push(node.engine_tags[i].engine.id); 147 | // }; 148 | // var targets = []; 149 | // for(var i = 0; i < node.target_tags.length; i++){ 150 | // targets.push(node.target_tags[i].target.id); 151 | // }; 152 | // 153 | // ConfigAPI.create({ 154 | // host_id: node.id, 155 | // platform_id: node.platform.id, 156 | // arch_id: node.arch.id, 157 | // engines: engines, 158 | // targets: targets 159 | // }).then(function(data){ 160 | // $.notify("Config added successfully.", "success"); 161 | // $('#nodeConfigModal').modal('hide'); 162 | // }, function(fail){ 163 | // $.notify("Config failed.", "error"); 164 | // }); 165 | // } 166 | // 167 | // $scope.queryEngineTags = function(q){ 168 | // var result = []; 169 | // $scope.engines.forEach(function(engine){ 170 | // if(engine.name.startsWith(q)){ 171 | // result.push( 172 | // { 173 | // text: engine.name, 174 | // engine: engine, 175 | // } 176 | // ); 177 | // } 178 | // }); 179 | // return result; 180 | // }; 181 | // $scope.queryTargetTags = function(q){ 182 | // var result = []; 183 | // $scope.targets.forEach(function(target){ 184 | // if(target.name.startsWith(q)){ 185 | // result.push( 186 | // { 187 | // text: target.name, 188 | // target: target, 189 | // } 190 | // ); 191 | // } 192 | // }); 193 | // return result; 194 | // }; 195 | 196 | $scope.initNodes(); 197 | $rootScope.nodeManagerTimer = $interval($scope.initNodes, config.refreshTime); 198 | }); 199 | -------------------------------------------------------------------------------- /static/frontend/controller/overview.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('frontApp') 4 | .controller('OverviewCtrl', 5 | function ($scope, $rootScope, $interval, $location, StatusAPI, config) { 6 | $scope.initOverview = function() { 7 | StatusAPI.read().then(function(data){ 8 | $scope.status = data; 9 | console.log($scope.status); 10 | }) 11 | }; 12 | 13 | $scope.formatDateTime = function(timeString){ 14 | return moment(timeString).format('YYYY-MM-DD h:mm:ss A'); 15 | }; 16 | 17 | $scope.initOverview(); 18 | $rootScope.overviewTimer = $interval($scope.initOverview, config.refreshTime); 19 | }); 20 | -------------------------------------------------------------------------------- /static/frontend/factory/arch.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('frontApp') 4 | .factory('ArchAPI', 5 | function (config, $http, $q) { 6 | var list = function(){ 7 | var deferred = $q.defer(); 8 | $http.get(config.baseURL + '/arch') 9 | .success(function(data, status){ 10 | deferred.resolve(data); 11 | }) 12 | .error(function(error, status){ 13 | deferred.reject(error); 14 | }) 15 | return deferred.promise; 16 | } 17 | var remove = function(id){ 18 | var deferred = $q.defer(); 19 | $http.get(config.baseURL + '/arch/' + id + '?delete=1') 20 | .success(function(data, status){ 21 | deferred.resolve(data); 22 | }) 23 | .error(function(error, status){ 24 | deferred.reject(error); 25 | }) 26 | return deferred.promise; 27 | } 28 | var update = function(id, data){ 29 | var deferred = $q.defer(); 30 | $http.post(config.baseURL + '/arch/' + id, data) 31 | .success(function(data, status){ 32 | deferred.resolve(data); 33 | }) 34 | .error(function(error, status){ 35 | deferred.reject(error); 36 | }) 37 | return deferred.promise; 38 | } 39 | var create = function(data){ 40 | var deferred = $q.defer(); 41 | $http.post(config.baseURL + '/arch', data) 42 | .success(function(data, status){ 43 | deferred.resolve(data); 44 | }) 45 | .error(function(error, status){ 46 | deferred.reject(error); 47 | }) 48 | return deferred.promise; 49 | } 50 | return { 51 | list: list, 52 | remove: remove, 53 | update: update, 54 | create: create 55 | } 56 | }); 57 | -------------------------------------------------------------------------------- /static/frontend/factory/config.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('frontApp') 4 | .factory('ConfigAPI', 5 | function (config, $http, $q) { 6 | var create = function(data){ 7 | var deferred = $q.defer(); 8 | $http.post(config.baseURL + '/config', data) 9 | .success(function(data, status){ 10 | deferred.resolve(data); 11 | }) 12 | .error(function(error, status){ 13 | deferred.reject(error); 14 | }) 15 | return deferred.promise; 16 | } 17 | var update = function(id, data){ 18 | var deferred = $q.defer(); 19 | $http.post(config.baseURL + '/config/' + id, data) 20 | .success(function(data, status){ 21 | deferred.resolve(data); 22 | }) 23 | .error(function(error, status){ 24 | deferred.reject(error); 25 | }) 26 | return deferred.promise; 27 | } 28 | var list = function(){ 29 | var deferred = $q.defer(); 30 | $http.get(config.baseURL + '/config') 31 | .success(function(data, status){ 32 | deferred.resolve(data); 33 | }) 34 | .error(function(error, status){ 35 | deferred.reject(error); 36 | }) 37 | return deferred.promise; 38 | } 39 | var read = function(id){ 40 | var deferred = $q.defer(); 41 | $http.get(config.baseURL + '/config/' + id) 42 | .success(function(data, status){ 43 | deferred.resolve(data); 44 | }) 45 | .error(function(error, status){ 46 | deferred.reject(error); 47 | }) 48 | return deferred.promise; 49 | } 50 | var read_by_host_id = function(id){ 51 | var deferred = $q.defer(); 52 | $http.get(config.baseURL + '/config/' + id + '?host=1') 53 | .success(function(data, status){ 54 | deferred.resolve(data, status); 55 | }) 56 | .error(function(error, status){ 57 | deferred.reject(error, status); 58 | }) 59 | return deferred.promise; 60 | } 61 | return { 62 | create: create, 63 | update: update, 64 | list: list, 65 | read: read, 66 | read_by_host_id: read_by_host_id 67 | } 68 | }); 69 | -------------------------------------------------------------------------------- /static/frontend/factory/crash.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('frontApp') 4 | .factory('CrashAPI', 5 | function (config, $http, $q) { 6 | var list = function(){ 7 | var deferred = $q.defer(); 8 | $http.get(config.baseURL + '/crash') 9 | .success(function(data, status){ 10 | deferred.resolve(data); 11 | }) 12 | .error(function(error, status){ 13 | deferred.reject(error); 14 | }) 15 | return deferred.promise; 16 | } 17 | var remove = function(id){ 18 | var deferred = $q.defer(); 19 | $http.get(config.baseURL + '/crash/' + id + '?delete=1') 20 | .success(function(data, status){ 21 | deferred.resolve(data); 22 | }) 23 | .error(function(error, status){ 24 | deferred.reject(error); 25 | }) 26 | return deferred.promise; 27 | } 28 | var update = function(id, data){ 29 | var deferred = $q.defer(); 30 | $http.post(config.baseURL + '/crash/' + id, data) 31 | .success(function(data, status){ 32 | deferred.resolve(data); 33 | }) 34 | .error(function(error, status){ 35 | deferred.reject(error); 36 | }) 37 | return deferred.promise; 38 | } 39 | var create = function(data){ 40 | var deferred = $q.defer(); 41 | $http.post(config.baseURL + '/crash', data) 42 | .success(function(data, status){ 43 | deferred.resolve(data); 44 | }) 45 | .error(function(error, status){ 46 | deferred.reject(error); 47 | }) 48 | return deferred.promise; 49 | } 50 | return { 51 | list: list, 52 | remove: remove, 53 | update: update, 54 | create: create 55 | } 56 | }); 57 | -------------------------------------------------------------------------------- /static/frontend/factory/engine.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('frontApp') 4 | .factory('EngineAPI', 5 | function (config, $http, $q) { 6 | var list = function(){ 7 | var deferred = $q.defer(); 8 | $http.get(config.baseURL + '/engine') 9 | .success(function(data, status){ 10 | deferred.resolve(data); 11 | }) 12 | .error(function(error, status){ 13 | deferred.reject(error); 14 | }) 15 | return deferred.promise; 16 | } 17 | var read = function(id){ 18 | var deferred = $q.defer(); 19 | $http.get(config.baseURL + '/engine/' + id) 20 | .success(function(data, status){ 21 | deferred.resolve(data); 22 | }) 23 | .error(function(error, status){ 24 | deferred.reject(error); 25 | }) 26 | return deferred.promise; 27 | } 28 | var remove = function(id){ 29 | var deferred = $q.defer(); 30 | $http.get(config.baseURL + '/engine/' + id + '?delete=1') 31 | .success(function(data, status){ 32 | deferred.resolve(data); 33 | }) 34 | .error(function(error, status){ 35 | deferred.reject(error); 36 | }) 37 | return deferred.promise; 38 | } 39 | var update = function(id, data){ 40 | var deferred = $q.defer(); 41 | $http.post(config.baseURL + '/engine/' + id, data) 42 | .success(function(data, status){ 43 | deferred.resolve(data); 44 | }) 45 | .error(function(error, status){ 46 | deferred.reject(error); 47 | }) 48 | return deferred.promise; 49 | } 50 | var create = function(data){ 51 | var deferred = $q.defer(); 52 | $http.post(config.baseURL + '/engine', data) 53 | .success(function(data, status){ 54 | deferred.resolve(data); 55 | }) 56 | .error(function(error, status){ 57 | deferred.reject(error); 58 | }) 59 | return deferred.promise; 60 | } 61 | return { 62 | list: list, 63 | read: read, 64 | remove: remove, 65 | update: update, 66 | create: create 67 | } 68 | }); 69 | -------------------------------------------------------------------------------- /static/frontend/factory/host.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('frontApp') 4 | .factory('HostAPI', 5 | function (config, $http, $q) { 6 | var list = function(){ 7 | var deferred = $q.defer(); 8 | $http.get(config.baseURL + '/host') 9 | .success(function(data, status){ 10 | deferred.resolve(data); 11 | }) 12 | .error(function(error, status){ 13 | deferred.reject(error); 14 | }) 15 | return deferred.promise; 16 | } 17 | return { 18 | list: list 19 | } 20 | }); 21 | -------------------------------------------------------------------------------- /static/frontend/factory/job.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('frontApp') 4 | .factory('JobAPI', 5 | function (config, $http, $q) { 6 | var list = function(){ 7 | var deferred = $q.defer(); 8 | $http.get(config.baseURL + '/job') 9 | .success(function(data, status){ 10 | deferred.resolve(data); 11 | }) 12 | .error(function(error, status){ 13 | deferred.reject(error); 14 | }) 15 | return deferred.promise; 16 | } 17 | var state = function(){ 18 | var deferred = $q.defer(); 19 | $http.get(config.baseURL + '/job?state=1') 20 | .success(function(data, status){ 21 | deferred.resolve(data); 22 | }) 23 | .error(function(error, status){ 24 | deferred.reject(error); 25 | }) 26 | return deferred.promise; 27 | } 28 | var remove = function(id){ 29 | var deferred = $q.defer(); 30 | $http.get(config.baseURL + '/job/' + id + '?delete=1') 31 | .success(function(data, status){ 32 | deferred.resolve(data); 33 | }) 34 | .error(function(error, status){ 35 | deferred.reject(error); 36 | }) 37 | return deferred.promise; 38 | } 39 | var update = function(id, data){ 40 | var deferred = $q.defer(); 41 | $http.post(config.baseURL + '/job/' + id, data) 42 | .success(function(data, status){ 43 | deferred.resolve(data); 44 | }) 45 | .error(function(error, status){ 46 | deferred.reject(error); 47 | }) 48 | return deferred.promise; 49 | } 50 | var create = function(data){ 51 | var deferred = $q.defer(); 52 | $http.post(config.baseURL + '/job', data) 53 | .success(function(data, status){ 54 | deferred.resolve(data); 55 | }) 56 | .error(function(error, status){ 57 | deferred.reject(error); 58 | }) 59 | return deferred.promise; 60 | } 61 | return { 62 | list: list, 63 | state: state, 64 | remove: remove, 65 | update: update, 66 | create: create 67 | } 68 | }); 69 | -------------------------------------------------------------------------------- /static/frontend/factory/misc.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('frontApp') 4 | .factory('MiscAPI', 5 | function (config, $http, $q) { 6 | var upload = function(file, template){ 7 | var deferred = $q.defer(); 8 | var fd = new FormData(); 9 | fd.append('file', file); 10 | $http.post(config.baseURL + '/upload', fd, 11 | { 12 | transformRequest: angular.identity, 13 | headers: {'Content-Type': undefined} 14 | }) 15 | .success(function(data){ 16 | if(angular.isDefined(template)){ 17 | for(var k in template){ 18 | if(template[k] === '') 19 | template[k] = data.upload_path 20 | } 21 | deferred.resolve(template); 22 | } 23 | else{ 24 | deferred.resolve(data); 25 | } 26 | 27 | }) 28 | .error(function(error){ 29 | deferred.reject(error); 30 | }); 31 | return deferred.promise; 32 | }; 33 | var download = function(url){ 34 | var deferred = $q.defer(); 35 | console.log(url); 36 | $http({ 37 | url: url, 38 | method: 'GET', 39 | transformResponse: [function (data) { 40 | return data; 41 | }] 42 | }) 43 | .success(function(data){ 44 | deferred.resolve(data); 45 | }) 46 | .error(function(error){ 47 | deferred.reject(error); 48 | }); 49 | return deferred.promise; 50 | } 51 | var log= function(){ 52 | var deferred = $q.defer(); 53 | $http.get(config.baseURL + '/log') 54 | .success(function(data, status){ 55 | deferred.resolve(data); 56 | }) 57 | .error(function(error, status){ 58 | deferred.reject(error); 59 | }) 60 | return deferred.promise; 61 | } 62 | 63 | return { 64 | upload: upload, 65 | download: download, 66 | log: log 67 | } 68 | }); 69 | -------------------------------------------------------------------------------- /static/frontend/factory/option.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('frontApp') 4 | .factory('OptionAPI', 5 | function (config, $http, $q) { 6 | var list = function(){ 7 | var deferred = $q.defer(); 8 | $http.get(config.baseURL + '/option') 9 | .success(function(data, status){ 10 | deferred.resolve(data); 11 | }) 12 | .error(function(error, status){ 13 | deferred.reject(error); 14 | }) 15 | return deferred.promise; 16 | } 17 | var type = function(){ 18 | var deferred = $q.defer(); 19 | $http.get(config.baseURL + '/option?type=1') 20 | .success(function(data, status){ 21 | deferred.resolve(data); 22 | }) 23 | .error(function(error, status){ 24 | deferred.reject(error); 25 | }) 26 | return deferred.promise; 27 | } 28 | var remove = function(id){ 29 | var deferred = $q.defer(); 30 | $http.get(config.baseURL + '/option/' + id + '?delete=1') 31 | .success(function(data, status){ 32 | deferred.resolve(data); 33 | }) 34 | .error(function(error, status){ 35 | deferred.reject(error); 36 | }) 37 | return deferred.promise; 38 | } 39 | var update = function(id, data){ 40 | var deferred = $q.defer(); 41 | $http.post(config.baseURL + '/option/' + id, data) 42 | .success(function(data, status){ 43 | deferred.resolve(data); 44 | }) 45 | .error(function(error, status){ 46 | deferred.reject(error); 47 | }) 48 | return deferred.promise; 49 | } 50 | var create = function(data){ 51 | var deferred = $q.defer(); 52 | $http.post(config.baseURL + '/option', data) 53 | .success(function(data, status){ 54 | deferred.resolve(data); 55 | }) 56 | .error(function(error, status){ 57 | deferred.reject(error); 58 | }) 59 | return deferred.promise; 60 | } 61 | return { 62 | list: list, 63 | type: type, 64 | remove: remove, 65 | update: update, 66 | create: create 67 | } 68 | }); 69 | -------------------------------------------------------------------------------- /static/frontend/factory/platform.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('frontApp') 4 | .factory('PlatformAPI', 5 | function (config, $http, $q) { 6 | var list = function(){ 7 | var deferred = $q.defer(); 8 | $http.get(config.baseURL + '/platform') 9 | .success(function(data, status){ 10 | deferred.resolve(data); 11 | }) 12 | .error(function(error, status){ 13 | deferred.reject(error); 14 | }) 15 | return deferred.promise; 16 | } 17 | var remove = function(id){ 18 | var deferred = $q.defer(); 19 | $http.get(config.baseURL + '/platform/' + id + '?delete=1') 20 | .success(function(data, status){ 21 | deferred.resolve(data); 22 | }) 23 | .error(function(error, status){ 24 | deferred.reject(error); 25 | }) 26 | return deferred.promise; 27 | } 28 | var update = function(id, data){ 29 | var deferred = $q.defer(); 30 | $http.post(config.baseURL + '/platform/' + id, data) 31 | .success(function(data, status){ 32 | deferred.resolve(data); 33 | }) 34 | .error(function(error, status){ 35 | deferred.reject(error); 36 | }) 37 | return deferred.promise; 38 | } 39 | var create = function(data){ 40 | var deferred = $q.defer(); 41 | $http.post(config.baseURL + '/platform', data) 42 | .success(function(data, status){ 43 | deferred.resolve(data); 44 | }) 45 | .error(function(error, status){ 46 | deferred.reject(error); 47 | }) 48 | return deferred.promise; 49 | } 50 | return { 51 | list: list, 52 | remove: remove, 53 | update: update, 54 | create: create 55 | } 56 | }); 57 | -------------------------------------------------------------------------------- /static/frontend/factory/script.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('frontApp') 4 | .factory('ScriptAPI', 5 | function (config, $http, $q) { 6 | var list = function(){ 7 | var deferred = $q.defer(); 8 | $http.get(config.baseURL + '/script') 9 | .success(function(data, status){ 10 | deferred.resolve(data); 11 | }) 12 | .error(function(error, status){ 13 | deferred.reject(error); 14 | }) 15 | return deferred.promise; 16 | } 17 | var remove = function(id){ 18 | var deferred = $q.defer(); 19 | $http.get(config.baseURL + '/script/' + id + '?delete=1') 20 | .success(function(data, status){ 21 | deferred.resolve(data); 22 | }) 23 | .error(function(error, status){ 24 | deferred.reject(error); 25 | }) 26 | return deferred.promise; 27 | } 28 | var update = function(id, data){ 29 | var deferred = $q.defer(); 30 | $http.post(config.baseURL + '/script/' + id, data) 31 | .success(function(data, status){ 32 | deferred.resolve(data); 33 | }) 34 | .error(function(error, status){ 35 | deferred.reject(error); 36 | }) 37 | return deferred.promise; 38 | } 39 | var create = function(data){ 40 | var deferred = $q.defer(); 41 | $http.post(config.baseURL + '/script', data) 42 | .success(function(data, status){ 43 | deferred.resolve(data); 44 | }) 45 | .error(function(error, status){ 46 | deferred.reject(error); 47 | }) 48 | return deferred.promise; 49 | } 50 | return { 51 | list: list, 52 | remove: remove, 53 | update: update, 54 | create: create 55 | } 56 | }); 57 | -------------------------------------------------------------------------------- /static/frontend/factory/status.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('frontApp') 4 | .factory('StatusAPI', 5 | function (config, $http, $q) { 6 | var read = function(){ 7 | var deferred = $q.defer(); 8 | $http.get(config.baseURL + '/status') 9 | .success(function(data, status){ 10 | deferred.resolve(data); 11 | }) 12 | .error(function(error, status){ 13 | deferred.reject(error); 14 | }) 15 | return deferred.promise; 16 | } 17 | return { 18 | read: read 19 | } 20 | }); 21 | -------------------------------------------------------------------------------- /static/frontend/factory/target.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('frontApp') 4 | .factory('TargetAPI', 5 | function (config, $http, $q) { 6 | var list = function(){ 7 | var deferred = $q.defer(); 8 | $http.get(config.baseURL + '/target') 9 | .success(function(data, status){ 10 | deferred.resolve(data); 11 | }) 12 | .error(function(error, status){ 13 | deferred.reject(error); 14 | }) 15 | return deferred.promise; 16 | } 17 | var remove = function(id){ 18 | var deferred = $q.defer(); 19 | $http.get(config.baseURL + '/target/' + id + '?delete=1') 20 | .success(function(data, status){ 21 | deferred.resolve(data); 22 | }) 23 | .error(function(error, status){ 24 | deferred.reject(error); 25 | }) 26 | return deferred.promise; 27 | } 28 | var update = function(id, data){ 29 | var deferred = $q.defer(); 30 | $http.post(config.baseURL + '/target/' + id, data) 31 | .success(function(data, status){ 32 | deferred.resolve(data); 33 | }) 34 | .error(function(error, status){ 35 | deferred.reject(error); 36 | }) 37 | return deferred.promise; 38 | } 39 | var create = function(data){ 40 | var deferred = $q.defer(); 41 | $http.post(config.baseURL + '/target', data) 42 | .success(function(data, status){ 43 | deferred.resolve(data); 44 | }) 45 | .error(function(error, status){ 46 | deferred.reject(error); 47 | }) 48 | return deferred.promise; 49 | } 50 | return { 51 | list: list, 52 | remove: remove, 53 | update: update, 54 | create: create 55 | } 56 | }); 57 | -------------------------------------------------------------------------------- /static/frontend/index.htm: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 35 | 36 | 37 | 38 | 39 | 40 |
41 | Moflow Fuzz Control 42 |
43 | 84 | 85 | 86 |
87 | 88 |
89 | 90 | 91 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | -------------------------------------------------------------------------------- /static/frontend/server.js: -------------------------------------------------------------------------------- 1 | var http = require('http'); 2 | 3 | var finalhandler = require('finalhandler'); 4 | var serveStatic = require('serve-static'); 5 | 6 | var serve = serveStatic("./"); 7 | 8 | var server = http.createServer(function(req, res) { 9 | var done = finalhandler(req, res); 10 | serve(req, res, done); 11 | }); 12 | 13 | server.listen(8000); -------------------------------------------------------------------------------- /static/frontend/view/crash.htm: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
Crash 5 | 6 | 9 |
10 |
11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 37 | 38 | 39 |
#JobSampleDumpCreatedUpdated
{{crash.id}}{{getJobName(crash)}}{{getFileName(crash.repro_file)}}{{getFileName(crash.dump_file)}}{{formatDateTime(crash.created_at)}}{{formatDateTime(crash.updated_at)}} 33 | 36 |
40 |
41 | 42 |
43 |
44 |
45 |
46 | 47 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /static/frontend/view/err.htm: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
Error 5 |
6 |
7 |
8 |
{{log}}
9 |
10 |
11 |
12 |
13 |
-------------------------------------------------------------------------------- /static/frontend/view/job.htm: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
Job 5 | 6 | 9 |
10 |
11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 43 | 54 | 55 | 56 |
#NameTargetEngineHostStateCreatedUpdatedOptions
{{job.id}}{{job.name}}{{getTargetName(job)}}{{getEngineName(job)}}{{getHostName(job)}}{{getStateName(job)}}{{formatDateTime(job.created_at)}}{{formatDateTime(job.updated_at)}} 38 | 39 | {{getOptionById(opt.option_id).name}}={{opt.value}} 40 |
41 |
42 |
44 | 47 | 50 | 53 |
57 |
58 | 59 |
60 |
61 |
62 |
63 | 64 | 65 | 126 | 127 | 149 | 150 | 151 | -------------------------------------------------------------------------------- /static/frontend/view/node.htm: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
Node 5 | 6 |
7 |
8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 |
#HostnameIP AddressMAC AddressPlatformArchCreatedHost IdLast UpdateUptime
{{$index+1}}{{node.name}}{{node.ip}}{{node.mac}}{{getPlatformName(node)}}{{getArchName(node)}}{{formatDateTime (node.created_at)}}{{node.id}}{{formatDateTime(node.updated_at)}}{{uptime(node)}}
47 |
48 | 49 |
50 |
51 | 52 |
53 |
54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | -------------------------------------------------------------------------------- /static/frontend/view/overview.htm: -------------------------------------------------------------------------------- 1 |
2 |
3 |

Fuzzing Report

4 | Moflow fuzzing report for {{ formatDateTime(status.date) }} 5 |
6 |
7 | 8 |

Jobs

9 |
    10 |
  • 11 | There are currently {{ status.active_job_count}} active jobs 12 |
  • 13 |
  • 14 | There are currently {{ status.queued_job_count}} queued jobs 15 |
  • 16 |
  • 17 | There are currently {{ status.completed_job_count}} jobs completed 18 |
  • 19 |
20 |

Crashes

21 |
    22 | 23 | 24 | 25 | 26 | 27 | 28 |
  • 29 | There are a total of {{ status.crash_count}} crashes 30 |
  • 31 |
32 |

Fuzzing Nodes

33 |
    34 |
  • 35 | There are a total of {{ status.node_count}} Nodes 36 | 37 |
  • 38 | 46 |
47 |
48 |
49 | -------------------------------------------------------------------------------- /uwsgi.ini: -------------------------------------------------------------------------------- 1 | [uwsgi] 2 | vhost = true 3 | socket = /tmp/moflow.sock 4 | venv = /vagrant/.env 5 | chdir = /vagrant 6 | module = server 7 | callable = app 8 | -------------------------------------------------------------------------------- /vagrant.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | sudo add-apt-repository -y ppa:nginx/stable 3 | sudo apt-get update 4 | sudo apt-get install -y build-essential python-dev python-pip nginx nginx-extras uwsgi uwsgi-plugin-python 5 | sudo pip install virtualenv 6 | cd /vagrant 7 | virtualenv .env --always-copy --no-site-packages 8 | source .env/bin/activate 9 | pip install -r requirements.txt 10 | deactivate 11 | touch /tmp/moflow.sock 12 | sudo chown www-data:www-data /tmp/moflow.sock 13 | sudo rm -rf /etc/nginx/sites-available/default 14 | sudo cp /vagrant/nginx.conf /etc/nginx/sites-available/moflow 15 | sudo ln -s /etc/nginx/sites-available/moflow /etc/nginx/sites-enabled/moflow 16 | sudo cp /vagrant/uwsgi.ini /etc/uwsgi/apps-available/moflow.ini 17 | sudo ln -s /etc/uwsgi/apps-available/moflow.ini /etc/uwsgi/apps-enabled/moflow.ini 18 | sudo service nginx restart 19 | sudo service uwsgi restart 20 | --------------------------------------------------------------------------------