import gobject import gtk import sys import re from zenmapGUI.higwidgets.higwindows import HIGWindow from zenmapGUI.higwidgets.higboxes import HIGVBox, HIGHBox, HIGSpacer, hig_box_space_holder from zenmapGUI.higwidgets.higlabels import HIGSectionLabel, HIGEntryLabel from zenmapGUI.higwidgets.higscrollers import HIGScrolledWindow from zenmapGUI.higwidgets.higtextviewers import HIGTextView from zenmapGUI.higwidgets.higbuttons import HIGButton from zenmapGUI.higwidgets.higtables import HIGTable from zenmapGUI.higwidgets.higdialogs import HIGAlertDialog, HIGDialog from zenmapCore.ScriptMetadata import * from zenmapCore.ScriptargsParser import parse_script_args_dict from zenmapCore.NmapCommand import NmapCommand from zenmapCore.NmapOptions import NmapOptions from zenmapCore.Paths import Path NMAPDATADIR = Path.nmap_dir #NMAPDATADIR = "/usr/local/share/nmap" #print NMAPDATADIR class ScriptInterface: # Timeout, in milliseconds, after the user stops typing and we update the # interface from --script. SCRIPT_LIST_DELAY = 500 NMAP_DELAY = 200 # Timer to execute Nmap CHK_NMAP_DELAY = 200 # Timer to check the version of nmap during start. def __init__(self,root_tabs,ops,update_command,update_help): self.hmainbox = HIGHBox(False,0) self.notscripttab = False # to show profile editor that it is a script tab self.nmap_process = None self.chk_nmap_process = None self.script_list_timeout_id = None self.nmap_timeout_id = None self.chk_nmap_timeout_id = None self.nmap_stat = None self.script_entries = None self.ops = ops self.update_command = update_command self.update_help = update_help self.selected_scripts = [] self.arg_values = {} self.current_arguments = [] self.scriptbypath = [] self.check_nmap() if self.ops["--script"]: self.update_script_list_from_spec(self.ops["--script"]) if self.ops["--script-args"]: self.update_argument_values(self.ops["--script-args"]) self.set_help_texts() self.vleftbox = HIGVBox(False,0) self.vrightbox = HIGVBox(False,5) self.create_list_scripts() self.create_description_box() self.create_arguments_window() self.hmainbox.pack_start(self.vleftbox,False,False,0) self.hmainbox.pack_end(self.vrightbox,True,True,0) self.prev_script_spec = None self.script_expr = re.compile("[0-9a-zA-Z\-]*\.nse") #creating Tags self.text_buffer.create_tag("Description") self.text_buffer.create_tag("Usage",font = "Monospace",background="#008080") self.text_buffer.create_tag("Output",font = "Monospace",background="#FFFF00") self.text_buffer.create_tag("Error",font = "Monospace",background="#FFFFFF") self.focussedentry = None def init_nmap(self,rules): """ This method starts nmap as a subprocess and sets the Timer""" if rules is not None: ops = NmapOptions() ops["-d"] = 2 ops["--script"] = rules command_string = "nmap " + ops.render_string() self.nmap_process = NmapCommand(command_string) self.nmap_process.run_scan() def chk_init_nmap(self,rules): """ This method starts nmap as a subprocess and sets the Timer""" if rules is not None: ops = NmapOptions() ops["-d"] = 2 ops["--script"] = rules command_string = "nmap " +ops.render_string() self.chk_nmap_process = NmapCommand(command_string) self.chk_nmap_process.run_scan() def is_in_list(self,script_list,script_name): """ Checks if the script_list already contains the script_name""" for i in script_list: if i == script_name: return 1 return 0 def update_script_list_from_spec(self, spec): """ call back method for user edit delay""" self.init_nmap(spec) if self.nmap_timeout_id: gobject.source_remove(self.nmap_timeout_id) self.nmap_timeout_id = gobject.timeout_add(self.NMAP_DELAY, self.nmap_parse_op) def nmap_parse_op(self): """ Call back method for Nmap subprocess""" script_list = [] self.scriptbypath = [] if self.nmap_process is not None: # recent sacn try: status = self.nmap_process.scan_state() if status == True: #still running return True else: # success nmap_output =self.nmap_process.get_output() for line in nmap_output.split("\n"): if (line.find("NSE:") != -1) and (line.find(NMAPDATADIR) != -1): grp_script = self.script_expr.search(line) if grp_script: filename = grp_script.group() if(not self.is_in_list(script_list,filename)): script_list.append(filename) elif (line.find("NSE: Loaded") != -1) and (line.find(NMAPDATADIR)==-1): grp_script = self.script_expr.search(line) if grp_script: line = line.strip("NSE: Loaded") line = line.replace("'","") if line in self.scriptbypath: pass else: self.scriptbypath.append(line) #print self.scriptbypath except: pass if script_list is None: self.selected_scripts = [] self.refresh_list_scripts() else: self.selected_scripts = script_list self.refresh_list_scripts() return False def get_hmain_box(self): """ returns main Hbox to ProfileEditor""" return self.hmainbox def check_nmap(self): """ This method will be called to check the version of nmap""" self.chk_init_nmap("all") if self.chk_nmap_timeout_id: gobject.source_remove(self.chk_nmap_timeout_id) self.chk_nmap_timeout_id = gobject.timeout_add(self.CHK_NMAP_DELAY, self.check_nmap_cb) def check_nmap_cb(self): """ This is the callback method for checking nmap version""" if self.chk_nmap_process is not None: # recent sacn try: status = self.chk_nmap_process.scan_state() if status == True: #still running return True else: # success nmap_output =self.chk_nmap_process.get_output() for i in nmap_output.split("\n"): grp_script = self.script_expr.search(i) if grp_script is not None: self.script_entries = get_script_entries() self.refresh_list_scripts() return False except: pass #Remove all the components self.hmainbox.remove(self.vleftbox) self.text_buffer.set_text(u"") tags = ["Error"] content = "\nTry updating to latest version of nmap.\nThe current version installed cannot support\nScript selection interface\n" self.text_buffer.insert_with_tags_by_name(self.text_buffer.get_end_iter(),content, *tags) self.vrightbox.remove(self.arg_window) self.vrightbox.remove(self.arglabel) return False def update(self): """ updates the Interface when the command entry is changed""" #updates list of scripts script_list = [] rules = self.ops["--script"] if (self.prev_script_spec != rules): self.renew_script_list_timer(rules) self.prev_script_spec = rules #updates arguments.. raw_argument = self.ops["--script-args"] if raw_argument is not None: self.parse_script_args(raw_argument) self.arg_liststore.clear() for arg in self.current_arguments: if self.arg_values.has_key(arg): if self.arg_values[arg] is None: self.arg_liststore.append([arg,None]) else: self.arg_liststore.append([arg,self.arg_values[arg]]) else: self.arg_liststore.append([arg,None]) def renew_script_list_timer(self, spec): """Restart the timer to update the script list when the user edits the command. Because updating the script list is an expensive operation involving the creation of a subprocess, we don't do it for every typed character.""" if self.script_list_timeout_id: gobject.source_remove(self.script_list_timeout_id) self.script_list_timeout_id = gobject.timeout_add(self.SCRIPT_LIST_DELAY, self.update_script_list_from_spec, spec) def parse_script_args(self,raw_argument): """ When the command line is edited,--script-args section is parsed using the logic in nse_main.lua. According to the value, the argument widget is updated""" arg_dict = parse_script_args_dict(raw_argument) if arg_dict is None: # if there is parsing error args_dict holds none self.arg_values.clear() else: for key in arg_dict.keys(): self.arg_values[key] = arg_dict[key] def update_argument_values(self,raw_argument): """ When scripting tab starts up,Argument values are updated""" if raw_argument is not None: self.parse_script_args(raw_argument) def validate_select_list(self,script_name): """When the script is already selected it is removed from selected list, else it is added to selected list script_name is without .nse extension. self.selected_scripts will contain scripts with .nse extension""" for i in self.selected_scripts: if i == script_name+".nse": self.selected_scripts.remove(script_name+".nse") return script_name = script_name + ".nse" self.selected_scripts.append(script_name) def set_help_texts(self): self.list_scripts_help = _("List of scripts\n\nA list of all installed scripts. Activate or deactivate a script by clicking the box next to the script name.") self.description_help = _("Description\n\nThis box shows the categories a script belongs to. In addition, it gives a detailed description of the script which is present in script. A URL points to online NSEDoc documentation.") self.argument_help = _("Arguments\n\nA list of arguments that affect the selected script. Enter a value by clicking in the value field beside the argument name.") self.select_file_help = _("Select script file\n\nThis option allows you to select a script from the file system. Use this to select a script that is not in Nmap's script database.") def create_list_scripts(self): """ creates and packs widgets associated with left hand side of Interface""" self.scrolled_window = HIGScrolledWindow() self.scrolled_window.set_policy(gtk.POLICY_ALWAYS,gtk.POLICY_ALWAYS) self.scrolled_window.set_size_request(175,-1) # vertically it expands. self.previewentry = gtk.Entry() #create liststore self.liststore = gtk.ListStore(str,'gboolean',object) #get data from scriptmetadata.py #self.script_entries = get_script_entries() #moved to chk_nmap_cb subprocess #self.refresh_list_scripts() #create listview self.listview = gtk.TreeView(self.liststore) self.listview.connect("motion-notify-event",self.update_help_ls_cb) selection = self.listview.get_selection() selection.connect("changed",self.selection_changed_cb) #create cell renderer self.cell = gtk.CellRendererText() self.togglecell = gtk.CellRendererToggle() self.togglecell.set_property('activatable',True) self.togglecell.connect('toggled',self.toggled_cb,self.liststore) #create listview column self.col = gtk.TreeViewColumn(_('Names')) self.col.set_sizing(gtk.TREE_VIEW_COLUMN_GROW_ONLY) #self.col.set_fixed_width(125) self.col.set_resizable(True) self.togglecol = gtk.TreeViewColumn(None, self.togglecell) self.togglecol.add_attribute(self.togglecell,"active",1) self.listview.append_column(self.togglecol) self.listview.append_column(self.col) self.col.pack_start(self.cell,True) self.col.add_attribute(self.cell,'text',0) self.scrolled_window.add(self.listview) self.scrolled_window.show() #self.vleftbox.pack_start(self.previewentry,False,False,5) self.vleftbox.pack_start(self.scrolled_window,True,True,0) #create file button self.filebutton = HIGButton(_("Select file")) self.filebutton.connect('enter',self.update_help_sel_cb) #self.vleftbox.pack_start(self.filebutton,False,False,0) def refresh_list_scripts(self): """ The list of script selected are refreshed in list store""" self.liststore.clear() if self.script_entries: for entry in self.script_entries: scriptname = self.strip_file_name(entry.Filename) if entry.Filename in self.selected_scripts: self.liststore.append([scriptname,True,entry]) else: self.liststore.append([scriptname,False,entry]) def strip_file_name(self,filename): if(filename.endswith(".nse")): return filename[:-4] else: return filename def toggled_cb(self,cell,path,model): """ Call back method, called when the check box in list of scripts is toggled""" model[path][1] = not model[path][1] script_name = model[path][0] self.validate_select_list(script_name) #if(model[path][1] == True): # Argument value is active only when the check box is toggled.(True) # self.value.set_property('editable',True) #else: # self.value.set_property('editable',False) if len(self.selected_scripts) == 0: self.ops["--script"] = None self.update_command() else: scriptsname = [] for i in self.selected_scripts: # removing .nse from filenames scriptsname.append(self.strip_file_name(i)) if self.scriptbypath: scriptsname = scriptsname + self.scriptbypath self.ops["--script"] = ",".join(scriptsname) self.update_command() return def create_description_box(self): """ Creates and packs widgets related to displaying description box""" self.label = gtk.Label(_("Description")) self.label.set_justify(gtk.JUSTIFY_LEFT) #self.vrightbox.pack_start(self.label,False,False,0) self.sw = HIGScrolledWindow() self.sw.set_policy(gtk.POLICY_AUTOMATIC,gtk.POLICY_ALWAYS) self.sw.set_size_request(-1,150) self.sw.set_shadow_type(gtk.SHADOW_OUT) self.sw.set_border_width(5) #text view self.text_view = gtk.TextView() self.text_view.connect('motion-notify-event',self.update_help_desc_cb) self.text_buffer = self.text_view.get_buffer() self.text_view.set_wrap_mode(gtk.WRAP_WORD) self.text_view.set_editable(False) self.text_view.set_justification(gtk.JUSTIFY_LEFT) self.sw.add(self.text_view) self.vrightbox.pack_start(self.sw,True,True,0) def create_arguments_window(self): """creates and packs widgets related to arguments box""" self.arglabel = gtk.Label(_("Arguments")) self.label.set_justify(gtk.JUSTIFY_LEFT) self.vrightbox.pack_start(self.arglabel,False,False,0) self.arg_window = HIGScrolledWindow() self.arg_window.set_policy(gtk.POLICY_AUTOMATIC,gtk.POLICY_ALWAYS) self.arg_window.set_size_request(-1,165) self.arg_window.set_shadow_type(gtk.SHADOW_OUT) self.create_arg_list_store() self.arg_window.add(self.arg_listview) self.vrightbox.pack_start(self.arg_window,True,True,0) def create_arg_list_store(self): """ Liststore for displaying argument and its values""" self.arg_liststore = gtk.ListStore(str,str) self.arg_listview = gtk.TreeView(self.arg_liststore) #help self.arg_listview.connect("motion-notify-event",self.update_help_arg_cb) #cell renderer self.argument = gtk.CellRendererText() self.value = gtk.CellRendererText() self.value.connect("edited",self.value_edited_cb,self.arg_liststore) #treeview column self.arg_col = gtk.TreeViewColumn("Arguments\t") self.val_col = gtk.TreeViewColumn("values") self.arg_listview.append_column(self.arg_col) self.arg_listview.append_column(self.val_col) self.arg_col.pack_start(self.argument,True) self.arg_col.add_attribute(self.argument,'text',0) self.val_col.pack_start(self.value,True) self.val_col.add_attribute(self.value,'text',1) def value_edited_cb(self,cell,path,new_text,model): """ Called when the argument cell is edited""" self.arg_list = [] model[path][1] = new_text argument_name = model[path][0] self.arg_values[argument_name] = new_text self.update_arg_values() def update_arg_values(self): """ When the widget is updated with argument value,correspondingly the command line is also updated""" for key in self.arg_values.keys(): if len(self.arg_values[key]) == 0: del self.arg_values[key] else: self.arg_list.append(key + "=" + self.arg_values[key]) if len(self.arg_list) == 0: self.ops["--script-args"] = None self.arg_values.clear() else: self.ops["--script-args"] = ",".join(self.arg_list) self.update_command() def selection_changed_cb(self,selection): """ Called back when list of scripts is selected""" model,selection = selection.get_selected_rows() for path in selection: check_value = model.get_value(model.get_iter(path),1) entry = model.get_value(model.get_iter(path),2) self.set_description(entry) self.populate_arg_list(entry) self.focussedentry = entry #Remember the currently pointing script entry def update_help_ls_cb(self,widget,extra): # list of scripts """callback method for displaying list of scripts help""" self.update_help(self.list_scripts_help) def update_help_desc_cb(self,widget,extra): """ callback method for displaying description""" self.update_help(self.description_help) def update_help_arg_cb(self,widget,area): """ callback method for displaying argument help""" content = self.argument_help content = content + "\n\n" if self.focussedentry: for arg in self.focussedentry.ScriptArguments: content = content + arg + ":" + "\n" content = content + self.focussedentry.Argsdescription[arg] + "\n\n" self.update_help(content) def update_help_sel_cb(self,user_param=None): """ callback method for diaplaying select script help""" self.update_help(self.select_file_help) def set_description(self,entry): """ Sets the content that is to be displayed in the description box""" self.text_buffer.set_text(u"") tags = ["Description"] content = """\ Categories: %(cats)s %(desc)s %(url)s """ % {"cats": ", ".join(entry.Categories), "desc": entry.Description, "url": entry.NSEDocURL} #self.text_buffer.set_text(content) self.text_buffer.insert_with_tags_by_name(self.text_buffer.get_end_iter(), content, *tags) tags = ["Usage"] self.text_buffer.insert_with_tags_by_name(self.text_buffer.get_end_iter(),entry.Usage, *tags) tags = ["Output"] self.text_buffer.insert_with_tags_by_name(self.text_buffer.get_end_iter(),entry.Output, *tags) def populate_arg_list(self,entry): """ Called when a particular script is pointed to display its argument and values(if any)""" self.arg_liststore.clear() self.current_arguments = [] self.value.set_property('editable',True) #if entry.Filename in self.selected_scripts: # self.value.set_property('editable',True) #else: # self.value.set_property('editable',False) for arg in entry.Arguments: self.current_arguments.append(arg) if self.arg_values.has_key(arg): if len(self.arg_values[arg]) == 0: self.arg_liststore.append([arg,None]) else: self.arg_liststore.append([arg,self.arg_values[arg]]) else: self.arg_liststore.append([arg,None])