def _scan_site(self, wp_site: Site) -> Optional[ScanReport]: """ Helper method to wrap the scanning process of `WPWatcherScanner.scan_site` and add the following: - Find the last report in the database and launch the scan - Write it in DB after scan. - Print progress bar This function can be called asynchronously. """ last_wp_report = self.wp_reports.find(ScanReport(site=wp_site["url"])) # Launch scanner wp_report = self.scanner.scan_site(wp_site, last_wp_report) # Save report in global instance database and to file when a site has been scanned if wp_report: self.wp_reports.write([wp_report]) else: log.info(f"No report saved for site {wp_site['url']}") # Print progress print_progress_bar(len(self.scanner.scanned_sites), len(self.wp_sites)) return wp_report
def scan_site_wrapper(self, wp_site): """ Helper method to wrap the raw scanning process that offer WPWatcherScanner.scan_site() and add the following: - Handle site structure formatting - Find the last report in the database and launch the scan - Write it in DB after scan. - Print progress bar This function will be called asynchronously. Return one report """ wp_site = self.format_site(wp_site) last_wp_report = self.wp_reports.find_last_wp_report( {"site": wp_site["url"]}) # Launch scanner wp_report = self.scanner.scan_site(wp_site, last_wp_report) # Save report in global instance database and to file when a site has been scanned if wp_report: self.wp_reports.update_and_write_wp_reports([wp_report]) else: log.info("No report saved for site %s" % wp_site["url"]) # Print progress print_progress_bar(len(self.scanner.scanned_sites), len(self.wp_sites)) return wp_report
def scan_site_wrapper(self, wp_site, with_api_token=False): wp_site = self.format_site(wp_site) last_wp_report = self.wp_reports.find_last_wp_report( {'site': wp_site['url']}) if with_api_token: wp_site['wpscan_args'].extend( ["--api-token", self.scanner.api_token]) # Launch scanner wp_report = self.scanner.scan_site(wp_site, last_wp_report) # Save report in global instance database and to file when a site has been scanned if wp_report: self.wp_reports.update_and_write_wp_reports([wp_report]) else: log.info("No report saved for site %s" % wp_site['url']) # Print progress print_progress_bar(len(self.scanner.scanned_sites), len(self.wp_sites)) return (wp_report)
def scan_site(self, wp_site): wp_site=self.format_site(wp_site) # Init report variables wp_report={ "site":wp_site['url'], "status":None, "datetime": datetime.now().strftime('%Y-%m-%dT%H-%M-%S'), "last_email":None, "errors":[], "infos":[], "warnings":[], "alerts":[], "fixed":[], "wpscan_output":None # will be deleted } # Find last site result if any last_wp_report=[r for r in self.wp_reports if r['site']==wp_site['url']] if len(last_wp_report)>0: last_wp_report=last_wp_report[0] # Skip if the daemon mode is enabled and scan already happend in the last configured `daemon_loop_wait` if ( self.conf['daemon'] and datetime.strptime(wp_report['datetime'],'%Y-%m-%dT%H-%M-%S') - datetime.strptime(last_wp_report['datetime'],'%Y-%m-%dT%H-%M-%S') < self.conf['daemon_loop_sleep']): log.info("Daemon skipping site %s because already scanned in the last %s"%(wp_site['url'] , self.conf['daemon_loop_sleep'])) self.scanned_sites.append(None) return None else: last_wp_report=None # WPScan arguments wpscan_arguments=self.conf['wpscan_args']+wp_site['wpscan_args']+['--url', wp_site['url']] # Output log.info("Scanning site %s"%wp_site['url'] ) # Launch WPScan ------------------------------------------------------- (wpscan_exit_code, wp_report["wpscan_output"]) = self.wpscan.wpscan(*wpscan_arguments) # Exit code 0: all ok. Exit code 5: Vulnerable. Other exit code are considered as errors # Handle scan errors if wpscan_exit_code not in [0,5]: # Quick return if interrupting if self.interrupting: return None # Quick return if user cacelled scans if wpscan_exit_code in [2]: return None # Fail fast if self.conf['fail_fast']: if not self.interrupting: log.error("Failure") self.interrupt() else: return None # Interrupt will generate other errors # If WPScan error, add the error to the reports # This types if errors will be written into the Json database file if wpscan_exit_code in [1,3,4]: err_str="WPScan failed with exit code %s. \nWPScan arguments: %s. \nWPScan output: \n%s"%((wpscan_exit_code, safe_log_wpscan_args(wpscan_arguments), wp_report['wpscan_output'])) wp_report['errors'].append(err_str) log.error("Could not scan site %s"%wp_site['url']) # Try to handle error and return wp_report, handled = self.handle_wpscan_err(wp_site, wp_report) if handled: return wp_report # Other errors codes : -9, -2, 127, etc: Just return None right away else: return None # No errors with wpscan ----------------------------- else: # Write wpscan output wpscan_results_file=None if self.conf['wpscan_output_folder'] : wpscan_results_file=os.path.join(self.conf['wpscan_output_folder'], get_valid_filename('WPScan_results_%s_%s.txt' % (wp_site['url'], wp_report['datetime']))) with open(wpscan_results_file, 'w') as wpout: wpout.write(re.sub(r'(\x1b|\[[0-9][0-9]?m)','', str(wp_report['wpscan_output']))) log.debug("Parsing WPScan output") # Call parse_result from parser.py ------------------------ wp_report['infos'], wp_report['warnings'] , wp_report['alerts'] = parse_results(wp_report['wpscan_output'] , self.conf['false_positive_strings']+wp_site['false_positive_strings'] ) # Updating report entry with data from last scan if any if last_wp_report: self.update_report(wp_report, last_wp_report) # Print WPScan findings ------------------------------------------------------ for info in wp_report['infos']: log.info(oneline("** WPScan INFO %s ** %s" % (wp_site['url'], info ))) for fix in wp_report['fixed']: log.info(oneline("** FIXED %s ** %s" % (wp_site['url'], fix ))) for warning in wp_report['warnings']: log.warning(oneline("** WPScan WARNING %s ** %s" % (wp_site['url'], warning ))) for alert in wp_report['alerts']: log.critical(oneline("** WPScan ALERT %s ** %s" % (wp_site['url'], alert ))) if wpscan_results_file: log.info("WPScan results saved to file %s"%wpscan_results_file) # Report status ------------------------------------------------ if len(wp_report['errors'])>0:wp_report['status']="ERROR" elif len(wp_report['warnings'])>0 and len(wp_report['alerts']) == 0: wp_report['status']='WARNING' elif len(wp_report['alerts'])>0: wp_report['status']='ALERT' elif len(wp_report['fixed'])>0: wp_report['status']='FIXED' else: wp_report['status']='INFO' # Will print parsed readable Alerts, Warnings, etc as they will appear in email reports log.debug("\n%s\n"%(build_message(wp_report, warnings=self.conf['send_warnings'] or self.conf['send_infos'], # switches to include or not warnings and infos infos=self.conf['send_infos']))) # Notify recepients if match triggers and no errors self.notify(wp_site, wp_report, last_wp_report) # Save scanned site self.scanned_sites.append(wp_site['url']) # Discard wpscan_output from report del wp_report['wpscan_output'] # Save report in global instance database and to file when a site has been scanned self.update_and_write_wp_reports([wp_report]) # Print progress print_progress_bar(len(self.scanned_sites), len(self.conf['wp_sites'])) return(wp_report)