def notify(self, db, collection, iid, action, _parent): """ Callback for the observer implemented in mongo.py. Each time an object is inserted, updated or deleted the standard way, this function will be called. Args: collection: the collection that has been modified iid: the mongo ObjectId _id that was modified/inserted/deleted action: string "update" or "insert" or "delete". It was the action performed on the iid _parent: Not used. the mongo ObjectId of the parent. Only if action in an insert. Not used anymore """ apiclient = APIClient.getInstance() if not apiclient.getCurrentPentest() != "": return if apiclient.getCurrentPentest() != db: return if action == "update": if collection == "defects": defect_m = Defect.fetchObject({"_id": ObjectId(iid)}) self.updateDefectInTreevw(defect_m, ) elif action == "insert": view = None res = Defect.fetchObject({"_id": ObjectId(iid)}) # Defect insertion takes place in calendarTreeview, # Remarks don't appear in the treeview, only in this module, so must notify here if collection == "remarks": self.addRemark(Remark.fetchObject({"_id": ObjectId(iid)}))
def fillWithDefects(self): """ Fetch defects that are global (not assigned to an ip) and fill the defect table with them. """ table = Defect.getDefectTable() for line in table: self.addDefect(Defect(line))
def removeItem(self, toDeleteIid): """ Remove defect from given iid in defect treeview Args: toDeleteIid: database ID of defect to delete """ try: item = self.treevw.item(toDeleteIid) except tk.TclError: return dialog = ChildDialogQuestion( self.parent, "DELETE WARNING", "Are you sure you want to delete defect " + str(item["text"]) + " ?", ["Delete", "Cancel"]) self.parent.wait_window(dialog.app) if dialog.rvalue != "Delete": return self.treevw.delete(toDeleteIid) defectToDelete = Defect.fetchObject({ "title": item["text"], "ip": "", "port": "", "proto": "" }) if defectToDelete is not None: defectToDelete.delete() self.resizeDefectTreeview()
def findDefectTemplateByTitle(self, title, multi=False): apiclient = APIClient.getInstance() defects_matching, msg = apiclient.searchDefect(title) if defects_matching is not None: if len(defects_matching) >= 1 and not multi: return Defect(defects_matching[0]) else: return defects_matching
def _insertChildrenDefects(self): """Insert every children defect in database as DefectView under this node""" defects = self.controller.getDefects() for defect in defects: defect_o = DefectController(Defect(defect)) defect_vw = DefectView(self.appliTw, self.appliViewFrame, self.mainApp, defect_o) defect_vw.addInTreeview(str(self.controller.getDbId()))
def updateRiskBox(self, _event=None): """Callback when ease or impact is modified. Calculate new resulting risk value Args _event: mandatory but not used """ ease = self.easeForm.getValue() impact = self.impactForm.getValue() risk = Defect.getRisk(ease, impact) self.riskForm.setValue(risk)
def createDefectCallback(self, _event=None): """Callback for tool click #TODO move to ToolController Creates an empty defect view and open it's insert window with notes = tools notes. """ modelData = self.controller.getData() toExport = modelData["notes"] for widget in self.appliViewFrame.winfo_children(): widget.destroy() dv = DefectView(self.appliTw, self.appliViewFrame, self.mainApp, DefectController(Defect(modelData))) dv.openInsertWindow(toExport)
def setMainRedactor(self): """Sets a main redactor for a pentest. Each not assigned defect will be assigned to him/her""" self.settings.reloadSettings() dialog = ChildDialogCombo(self.parent, self.settings.getPentesters() + ["N/A"], "Set main redactor", "N/A") newVal = self.parent.wait_window(dialog.app) if newVal is None: return if not newVal or newVal.strip() == "": return columnRedactor = self.treevw['columns'].index("redactor") for it in self.treevw.get_children(): oldValues = self.treevw.item(it)["values"] if oldValues[columnRedactor] == "N/A": oldValues[columnRedactor] = newVal self.treevw.item(it, values=oldValues) d_o = Defect({"_id": it}) d_o.update({"redactor": newVal}) self.mainRedac = newVal
def addDefectCallback(self, _event): """ Create an empty defect model and its attached view. Open this view insert window. Args: event: Automatically generated with a button Callback, not used but mandatory. """ for widget in self.appliViewFrame.winfo_children(): widget.destroy() modelData = self.controller.getData() dv = DefectView(self.appliTw, self.appliViewFrame, self.mainApp, DefectController(Defect(modelData))) dv.openInsertWindow()
def OnDoubleClick(self, event): """ Callback for double click on treeview. Opens a window to update the double clicked defect view. Args: event: automatically created with the event catch. stores data about line in treeview that was double clicked. """ item = self.treevw.identify("item", event.x, event.y) if item is None or item == '': return defect_m = Defect.fetchObject({"_id": ObjectId(item)}) dialog = ChildDialogDefectView(self.tkApp, self.settings, defect_m) self.parent.wait_window(dialog.app) self.updateDefectInTreevw(defect_m)
def addInTreeview(self, parentNode=None, addChildren=True): """Add this view in treeview. Also stores infos in application treeview. Args: parentNode: if None, will calculate the parent. If setted, forces the node to be inserted inside given parentNode. addChildren: If False, skip the tool and defects insert. Useful when displaying search results """ if parentNode is None: parentNode = self.getParentNode() nodeText = str(self.controller.getModelRepr()) elif parentNode == '': nodeText = self.controller.getDetailedString() else: nodeText = str(self.controller.getModelRepr()) self.appliTw.views[str(self.controller.getDbId())] = {"view": self} try: self.appliTw.insert(parentNode, "end", str(self.controller.getDbId()), text=nodeText, tags=self.controller.getTags(), image=self.getClassIcon()) except TclError: pass if addChildren: defects = self.controller.getDefects() for defect in defects: defect_o = DefectController(Defect(defect)) defect_vw = DefectView(self.appliTw, self.appliViewFrame, self.mainApp, defect_o) defect_vw.addInTreeview(str(self.controller.getDbId())) tools = self.controller.getTools() for tool in tools: tool_o = ToolController(Tool(tool)) tool_vw = ToolView(self.appliTw, self.appliViewFrame, self.mainApp, tool_o) tool_vw.addInTreeview(str(self.controller.getDbId())) self.appliTw.sort(parentNode) if "hidden" in self.controller.getTags(): self.hide()
def multi_insert(self): values = self.browse_down_treevw.getValue() for title in values: results, msg = APIClient.searchDefect(title) if results is not None: result = results[0] d_o = Defect() types = result["type"].split(",") d_o.initialize("", "", "", result["title"], result["synthesis"], result["description"], result["ease"], result["impact"], result["risk"], "N/A", types, result["language"], "", result["fixes"]) d_o.addInDb() else: tk.messagebox.showerror("Could not saerch defect", msg) return True
def __init__(self, parent, settings, defectModel=None, multi=False): """ Open a child dialog of a tkinter application to choose autoscan settings. Args: parent: the tkinter parent view to use for this window construction. defectModel : A Defect Model object to load default values. None to have empty fields, default is None. """ self.app = tk.Toplevel(parent) if defectModel is not None: if defectModel.isTemplate: self.app.title("Edit a security defect template") else: self.app.title("Edit a security defect") else: self.app.title("Add a security defect") self.app.resizable(True, True) self.app.geometry("800x600") container = ttk.Frame(self.app) container.columnconfigure(0, weight=1) container.rowconfigure(0, weight=1) self.rvalue = None self.canvas = tk.Canvas(container, bg="white") self.appFrame = ttk.Frame(self.canvas) self.myscrollbar = tk.Scrollbar(container, orient="vertical", command=self.canvas.yview) self.canvas.bind('<Enter>', self.boundToMousewheel) self.canvas.bind('<Leave>', self.unboundToMousewheel) self.canvas.bind( '<Configure>', lambda e: self.canvas.configure(scrollregion=self. canvas.bbox("all"))) self.canvas_main_frame = self.canvas.create_window( (0, 0), window=self.appFrame, anchor='nw') self.canvas.configure(yscrollcommand=self.myscrollbar.set) self.isInsert = defectModel is None self.multi = multi if self.isInsert: defectModel = Defect() self.defect_vw = DefectView(None, self.appFrame, parent, DefectController(defectModel)) if self.isInsert: if multi: self.defect_vw.openMultiInsertWindow(addButtons=False) else: self.defect_vw.openInsertWindow(addButtons=False) else: self.defect_vw.openModifyWindow(addButtons=False) ok_button = ttk.Button(self.appFrame, text="OK") ok_button.pack(side="right", padx=5, pady=10) ok_button.bind('<Button-1>', self.okCallback) cancel_button = ttk.Button(self.appFrame, text="Cancel") cancel_button.pack(side="right", padx=5, pady=10, ipadx=10) cancel_button.bind('<Button-1>', self.cancel) self.canvas.configure(scrollregion=self.canvas.bbox("all")) self.canvas.bind("<Configure>", self.resizeAppFrame) self.canvas.grid(column=0, row=0, sticky="nsew") self.myscrollbar.grid(column=1, row=0, sticky="ns") container.pack(fill=tk.BOTH, ipady=10, ipadx=10, expand=True) # self.appFrame.pack(fill=tk.X, ipady=10, ipadx=10, expand=True) this break the canvas drawing with scrollbar try: self.app.wait_visibility() self.app.transient(parent) self.app.grab_set() self.app.focus_force() self.app.lift() except tk.TclError: pass
def _load(self): """ Load the treeview with database information """ apiclient = APIClient.getInstance() dialog = ChildDialogProgress( self.appli, "Loading " + str(apiclient.getCurrentPentest()), "Opening " + str(apiclient.getCurrentPentest()) + ". Please wait for a few seconds.", 200, "determinate") step = 0 dialog.show(100) nbObjects = apiclient.count("waves", ) nbObjects += apiclient.count("scopes") nbObjects += apiclient.count("intervals") nbObjects += apiclient.count("scopes") nbObjects += apiclient.count("ips") nbObjects += apiclient.count("ports") nbObjects += apiclient.count("tools") nbObjects += apiclient.count("commands") onePercentNbObject = nbObjects // 100 if nbObjects > 100 else 1 nbObjectTreated = 0 self.delete(*self.get_children()) self._hidden = [] self._detached = [] self.waves_node = self.insert("", "end", str("waves"), text="Waves", image=WaveView.getClassIcon()) # Loading every category separatly is faster than recursivly. # This is due to cursor.next function calls in pymongo # Adding wave objects self.commands_node = self.insert("", "end", "commands", text="Commands", image=CommandView.getClassIcon()) self.group_command_node = self.insert( "", "end", "groupcommands", text="Command Groups", image=CommandGroupView.getClassIcon()) self.my_group_command_node = self.insert( self.group_command_node, "end", "mygroupcommands", text="My Command Groups", image=CommandGroupView.getClassIcon()) self.worker_group_command_node = self.insert( self.group_command_node, "end", "workergroupcommands", text="Worker Command Groups", image=CommandGroupView.getClassIcon()) self.my_commands_node = self.insert(self.commands_node, "end", "mycommands", text="My commands", image=CommandView.getClassIcon()) self.worker_commands_node = self.insert( self.commands_node, "end", "workercommands", text="Worker commands", image=CommandView.getClassIcon()) self.others_commands_node = self.insert( self.commands_node, "end", "otherscommands", text="Others commands", image=CommandView.getClassIcon()) commands = Command.fetchObjects({}, apiclient.getCurrentPentest()) for command in commands: command_vw = CommandView(self, self.appli.viewframe, self.appli, CommandController(command)) command_vw.addInTreeview() group_commands = CommandGroup.fetchObjects( {}, apiclient.getCurrentPentest()) for command_groupe_vw in group_commands: command_groupe_vw = CommandGroupView( self, self.appli.viewframe, self.appli, CommandGroupController(command_groupe_vw)) command_groupe_vw.addInTreeview() waves = Wave.fetchObjects({}) for wave in waves: wave_o = WaveController(wave) wave_vw = WaveView(self, self.appli.viewframe, self.appli, wave_o) wave_vw.addInTreeview(self.waves_node, False) nbObjectTreated += 1 if nbObjectTreated % onePercentNbObject == 0: step += 1 dialog.update(step) scopes = Scope.fetchObjects({}) for scope in scopes: scope_o = ScopeController(scope) scope_vw = ScopeView(self, self.appli.viewframe, self.appli, scope_o) scope_vw.addInTreeview(None, False) nbObjectTreated += 1 if nbObjectTreated % onePercentNbObject == 0: step += 1 dialog.update(step) intervals = Interval.fetchObjects({}) for interval in intervals: interval_o = IntervalController(interval) interval_vw = IntervalView(self, self.appli.viewframe, self.appli, interval_o) interval_vw.addInTreeview(None, False) nbObjectTreated += 1 if nbObjectTreated % onePercentNbObject == 0: step += 1 dialog.update(step) #Adding ip objects self.ips_node = self.insert("", "end", str("ips"), text="IPs", image=IpView.getClassIcon()) ips = Ip.fetchObjects({}) for ip in ips: ip_o = IpController(ip) ip_vw = IpView(self, self.appli.viewframe, self.appli, ip_o) ip_vw.addInTreeview(None, False) self.appli.statusbar.notify(ip_vw.controller.getTags()) nbObjectTreated += 1 if nbObjectTreated % onePercentNbObject == 0: step += 1 dialog.update(step) # Adding port objects ports = Port.fetchObjects({}) for port in ports: port_o = PortController(port) port_vw = PortView(self, self.appli.viewframe, self.appli, port_o) port_vw.addInTreeview(None, False) self.appli.statusbar.notify(port_vw.controller.getTags()) nbObjectTreated += 1 if nbObjectTreated % onePercentNbObject == 0: step += 1 dialog.update(step) # Adding defect objects defects = Defect.fetchObjects({"ip": {"$ne": ""}}) for defect in defects: defect_o = DefectController(defect) defect_vw = DefectView(self, self.appli.viewframe, self.appli, defect_o) defect_vw.addInTreeview(None) nbObjectTreated += 1 if nbObjectTreated % onePercentNbObject == 0: step += 1 dialog.update(step) # Adding tool objects tools = Tool.fetchObjects({}) for tool in tools: tool_o = ToolController(tool) tool_vw = ToolView(self, self.appli.viewframe, self.appli, tool_o) tool_vw.addInTreeview(None, False) self.appli.statusbar.notify(tool_vw.controller.getTags()) nbObjectTreated += 1 if nbObjectTreated % onePercentNbObject == 0: step += 1 dialog.update(step) self.sort(self.ips_node) self.appli.statusbar.update() dialog.destroy()
def notify(self, db, collection, iid, action, _parent): """ Callback for the observer implemented in mongo.py. Each time an object is inserted, updated or deleted the standard way, this function will be called. Args: collection: the collection that has been modified iid: the mongo ObjectId _id that was modified/inserted/deleted action: string "update" or "insert" or "delete". It was the action performed on the iid _parent: Not used. the mongo ObjectId of the parent. Only if action in an insert. Not used anymore """ apiclient = APIClient.getInstance() if not apiclient.getCurrentPentest() != "": return if db == "pollenisator": if collection == "settings": self.configureTags() return if apiclient.getCurrentPentest() != db: return # Delete if action == "delete": if collection == "defects": view = self.getViewFromId(str(iid)) if view is not None: self.appli.statusbar.notify([], view.controller.getTags()) try: self.delete(ObjectId(iid)) except tk.TclError: pass # item was not inserted in the treeview # Insert if action == "insert": view = None res = apiclient.find(collection, {"_id": ObjectId(iid)}, False) if collection == "tools": view = ToolView(self, self.appli.viewframe, self.appli, ToolController(Tool(res))) elif collection == "waves": view = WaveView(self, self.appli.viewframe, self.appli, WaveController(Wave(res))) elif collection == "scopes": view = ScopeView(self, self.appli.viewframe, self.appli, ScopeController(Scope(res))) elif collection == "ports": view = PortView(self, self.appli.viewframe, self.appli, PortController(Port(res))) elif collection == "ips": view = IpView(self, self.appli.viewframe, self.appli, IpController(Ip(res))) elif collection == "intervals": view = IntervalView(self, self.appli.viewframe, self.appli, IntervalController(Interval(res))) elif collection == "defects": view = DefectView(self, self.appli.viewframe, self.appli, DefectController(Defect(res))) elif collection == "commands": view = CommandView(self, self.appli.viewframe, self.appli, CommandController(Command(res))) elif collection == "group_commands": view = CommandGroupView( self, self.appli.viewframe, self.appli, CommandGroupController(CommandGroup(res))) try: if view is not None: view.addInTreeview() view.insertReceived() self.appli.statusbar.notify(view.controller.getTags()) except tk.TclError: pass if action == "update": try: view = self.getViewFromId(str(iid)) if view is not None: item = self.item(str(iid)) oldTags = item["tags"] view.controller.actualize() self.appli.statusbar.notify(view.controller.getTags(), oldTags) self.item(str(iid), text=str(view.controller.getModelRepr()), image=view.getIcon()) except tk.TclError: if view is not None: view.addInTreeview() if str(self.appli.openedViewFrameId) == str(iid): for widget in self.appli.viewframe.winfo_children(): widget.destroy() view.openModifyWindow() if view is not None: view.updateReceived() self.appli.statusbar.update()
def openInsertWindow(self, notes="", addButtons=True): """ Creates a tkinter form using Forms classes. This form aims to insert a new Defect Args: notes: default notes to be written in notes text input. Default is "" addButtons: boolean value indicating that insertion buttons should be visible. Default to True """ settings = self.mainApp.settings settings.reloadSettings() modelData = self.controller.getData() topPanel = self.form.addFormPanel(grid=True) s = topPanel.addFormSearchBar("Search Defect", APIClient.searchDefect, self.form, row=0, column=0) topPanel.addFormLabel("Search Language", row=0, column=1) lang = topPanel.addFormStr("lang", row=0, column=2) s.addOptionForm(lang) topPanel = self.form.addFormPanel(grid=True) topPanel.addFormLabel("Title") topPanel.addFormStr("Title", r".+", "", column=1, width=50) topPanel = self.form.addFormPanel(grid=True) topPanel.addFormLabel("Ease") self.easeForm = topPanel.addFormCombo( "Ease", Defect.getEases(), width=10, column=1, binds={"<<ComboboxSelected>>": self.updateRiskBox}) topPanel.addFormHelper( "0: Trivial to exploit, no tool required\n1: Simple technics and public tools needed to exploit\n2: public vulnerability exploit requiring security skills and/or the development of simple tools.\n3: Use of non-public exploits requiring strong skills in security and/or the development of targeted tools", column=2) topPanel.addFormLabel("Impact", column=3) self.impactForm = topPanel.addFormCombo( "Impact", Defect.getImpacts(), width=10, column=4, binds={"<<ComboboxSelected>>": self.updateRiskBox}) topPanel.addFormHelper( "0: No direct impact on system security\n1: Impact isolated on precise locations of pentested system security\n2: Impact restricted to a part of the system security.\n3: Global impact on the pentested system security.", column=5) topPanel.addFormLabel("Risk", column=6) self.riskForm = topPanel.addFormCombo("Risk", Defect.getRisks(), modelData["risk"], width=10, column=7) topPanel.addFormHelper( "0: small risk that might be fixed\n1: moderate risk that need a planed fix\n2: major risk that need to be fixed quickly.\n3: critical risk that need an immediate fix or an immediate interruption.", column=8) topPanel = self.form.addFormPanel(grid=True) topPanel.addFormLabel("Redactor", row=1) topPanel.addFormCombo("Redactor", self.mainApp.settings.getPentesters() + ["N/A"], "N/A", row=1, column=1) topPanel.addFormLabel("Language", row=1, column=2) topPanel.addFormStr("Language", "", "en", row=1, column=3) chklistPanel = self.form.addFormPanel(grid=True) defectTypes = settings.getPentestTypes() if defectTypes is not None: defectTypes = defectTypes.get(settings.getPentestType(), []) if len(defectTypes) == 0: defectTypes = ["N/A"] else: defectTypes = ["N/A"] chklistPanel.addFormChecklist("Type", defectTypes, ["N/A"]) proofsPanel = self.form.addFormPanel(grid=True) proofsPanel.addFormFile("Proof", r"", text="Add proof", width=90, height=4) topPanel = self.form.addFormPanel() topPanel.addFormText( "Synthesis", r"", "Synthesis", state="readonly" if self.controller.isAssigned() else "", height=2, side="top") if not self.controller.isAssigned(): topPanel.addFormText("Description", r"", "Description", side="top") else: topPanel.addFormHidden("Description", modelData.get("description", "")) notesPanel = self.form.addFormPanel() notesPanel.addFormLabel("Notes", side="top") notesPanel.addFormText("Notes", r"", notes, None, side="top") self.form.addFormHidden("ip", modelData["ip"]) self.form.addFormHidden("proto", modelData["proto"]) self.form.addFormHidden("port", modelData["port"]) self.form.addFormHidden("Fixes", []) if addButtons: self.completeInsertWindow() else: self.showForm()
def openModifyWindow(self, addButtons=True): """ Creates a tkinter form using Forms classes. This form aims to update or delete an existing Defect Args: addButtons: boolean value indicating that insertion buttons should be visible. Default to True """ modelData = self.controller.getData() settings = self.mainApp.settings settings.reloadSettings() globalPanel = self.form.addFormPanel(side=tk.TOP, fill=tk.X, pady=5) topPanel = globalPanel.addFormPanel(grid=True) row = 0 if modelData.get("ip", "") != "": topPanel.addFormLabel("IP", row=row, column=0) topPanel.addFormStr("IP", '', modelData["ip"], None, column=1, row=row, state="readonly") row += 1 if modelData.get("port", "") != "" and modelData["proto"] is not None: topPanel.addFormLabel("Port", row=row, column=0) port_str = modelData["proto"] + \ "/" if modelData["proto"] != "tcp" else "" port_str += modelData["port"] topPanel.addFormStr("Port", '', port_str, None, column=1, row=row, state="readonly") row += 1 if not self.controller.isAssigned(): if not self.controller.model.isTemplate: topPanel.addFormSearchBar("Search Defect", APIClient.searchDefect, globalPanel, row=row, column=1, autofocus=False) row += 1 topPanel.addFormLabel("Title", row=row, column=0) topPanel.addFormStr("Title", ".+", modelData["title"], width=50, row=row, column=1) row += 1 topPanel = globalPanel.addFormPanel(grid=True) row = 0 topPanel.addFormLabel("Ease", row=row) self.easeForm = topPanel.addFormCombo( "Ease", Defect.getEases(), modelData["ease"], width=10, row=row, column=1, binds={"<<ComboboxSelected>>": self.updateRiskBox}) topPanel.addFormHelper( "0: Trivial to exploit, no tool required\n1: Simple technics and public tools needed to exploit\n2: public vulnerability exploit requiring security skills and/or the development of simple tools.\n3: Use of non-public exploits requiring strong skills in security and/or the development of targeted tools", row=row, column=2) topPanel.addFormLabel("Impact", row=row, column=3) self.impactForm = topPanel.addFormCombo( "Impact", Defect.getImpacts(), modelData["impact"], width=10, row=row, column=4, binds={"<<ComboboxSelected>>": self.updateRiskBox}) topPanel.addFormHelper( "0: No direct impact on system security\n1: Impact isolated on precise locations of pentested system security\n2: Impact restricted to a part of the system security.\n3: Global impact on the pentested system security.", row=row, column=5) topPanel.addFormLabel("Risk", row=row, column=6) self.riskForm = topPanel.addFormCombo("Risk", Defect.getRisks(), modelData["risk"], width=10, row=row, column=7) topPanel.addFormHelper( "0: small risk that might be fixed\n1: moderate risk that need a planed fix\n2: major risk that need to be fixed quickly.\n3: critical risk that need an immediate fix or an immediate interruption.", row=row, column=8) row += 1 chklistPanel = globalPanel.addFormPanel(grid=True) defect_types = settings.getPentestTypes()[ settings.getPentestType()] for savedType in modelData["type"]: if savedType.strip() not in defect_types: defect_types.insert(0, savedType) chklistPanel.addFormChecklist("Type", defect_types, modelData["type"]) topPanel = globalPanel.addFormPanel(grid=True) row = 0 if not self.controller.model.isTemplate: topPanel.addFormLabel("Redactor", row=row) topPanel.addFormCombo( "Redactor", list( set(self.mainApp.settings.getPentesters() + ["N/A"] + [modelData["redactor"]])), modelData["redactor"], row=row, column=1) topPanel.addFormLabel("Language", row=row, column=2) topPanel.addFormStr("Language", "", modelData["language"], row=row, column=3) row += 1 topPanel = globalPanel.addFormPanel() topPanel.addFormText( "Synthesis", r"", modelData.get("synthesis", "Synthesis"), state="readonly" if self.controller.isAssigned() else "", height=2, side="top") topPanel.addFormText("Description", r"", modelData.get("description", "Description"), side="top") topPanel.addFormButton("Edit fixes", self.openFixesWindow) else: topPanel.addFormHidden("Title", modelData.get("title", "")) topPanel.addFormHidden("Ease", modelData.get("ease", "")) topPanel.addFormHidden("Impact", modelData.get("impact", "")) topPanel.addFormHidden("Risk", modelData.get("risk", "")) types = modelData.get("type", []) type_dict = dict() for type in types: type_dict[type] = 1 topPanel.addFormHidden("Type", type_dict) topPanel.addFormHidden("Language", modelData.get("language", "")) topPanel.addFormHidden("Synthesis", modelData.get("synthesis", "")) topPanel.addFormHidden("Description", modelData.get("description", "")) notesPanel = globalPanel.addFormPanel() notesPanel.addFormLabel("Notes", side="top") notesPanel.addFormText("Notes", r"", modelData["notes"], None, side="top", height=10) if not self.controller.model.isTemplate: proofPanel = globalPanel.addFormPanel(grid=True) i = 0 for proof in modelData["proofs"]: proofPanel.addFormLabel("Proof " + str(i), proof, row=i, column=0) proofPanel.addFormButton( "View", lambda event, obj=i: self.viewProof(event, obj), row=i, column=1) proofPanel.addFormButton( "Delete", lambda event, obj=i: self.deleteProof(event, obj), row=i, column=2) i += 1 proofPanel = globalPanel.addFormPanel() self.formFile = proofPanel.addFormFile("Add proofs", r"", "", width=100, height=3) self.formFixes = globalPanel.addFormHidden("Fixes", modelData["fixes"]) if not self.controller.model.isTemplate: actionsPan = globalPanel.addFormPanel() actionsPan.addFormButton("Create defect template from this", self.saveAsDefectTemplate) if addButtons: self.completeModifyWindow(addTags=False) else: self.showForm()