Execute the vulnerability scan
(self, scan_id: str, target: str, scan_type: ScanType, options: Dict)
| 1034 | def get_available_scanners(self) -> Dict[str, bool]: |
| 1035 | """Get status of available scanners""" |
| 1036 | # ZAP counts as "available" only when it is both installed and this |
| 1037 | # board has enough RAM to run it (zap_enabled). The frontend uses |
| 1038 | # zap_installed / zap_ram_ok to tell "not installed" apart from |
| 1039 | # "needs 8GB" so it can grey the panel with the right reason. |
| 1040 | zap_installed = self._tool_paths.get('zap') is not None |
| 1041 | zap_ram_ok = bool(getattr(self, '_zap_enabled', False)) |
| 1042 | zap_available = zap_installed and zap_ram_ok |
| 1043 | zap_running = self._is_zap_running() if zap_available else False |
| 1044 | # Nuclei, like ZAP, is gated on RAM: installed AND enough memory. The |
| 1045 | # frontend uses nuclei_installed / nuclei_ram_ok to grey it with the |
| 1046 | # right reason ("needs 900MB") and steer to a mesh unit. |
| 1047 | nuclei_installed = self._tool_paths.get('nuclei') is not None |
| 1048 | nuclei_ram_ok = bool(getattr(self, '_nuclei_enabled', False)) |
| 1049 | nuclei_available = nuclei_installed and nuclei_ram_ok |
| 1050 | return { |
| 1051 | 'nuclei': nuclei_available, |
| 1052 | 'nikto': self._tool_paths.get('nikto') is not None, |
| 1053 | 'sqlmap': self._tool_paths.get('sqlmap') is not None, |
| 1054 | 'nmap_vuln': self._tool_paths.get('nmap') is not None, |
| 1055 | 'whatweb': self._tool_paths.get('whatweb') is not None, |
| 1056 | 'zap': zap_available, |
| 1057 | 'zap_installed': zap_installed, |
| 1058 | 'zap_ram_ok': zap_ram_ok, |
| 1059 | 'zap_running': zap_running, |
| 1060 | 'nuclei_installed': nuclei_installed, |
| 1061 | 'nuclei_ram_ok': nuclei_ram_ok, |
| 1062 | 'nuclei_installing': bool(getattr(self, '_nuclei_installing', False)), |
| 1063 | 'nuclei_templates_updating': (self._nuclei_update_lock.locked() |
| 1064 | or bool(getattr(self, '_nuclei_autofetch_active', False))), |
| 1065 | 'ajax_spider_browser': getattr(self, '_detected_browser', None), |
| 1066 | } |
| 1067 | |
| 1068 | def start_scan(self, target: str, scan_type: ScanType = ScanType.NUCLEI, |
| 1069 | options: Dict = None) -> str: |
| 1070 | """Start a vulnerability scan""" |
| 1071 | if not self.is_available(): |
| 1072 | raise RuntimeError("Advanced vulnerability scanning not available") |
| 1073 | |
| 1074 | options = options or {} |
| 1075 | |
| 1076 | # URL-based scanners (ZAP/Nikto/WhatWeb/Nuclei) need a scheme. If the |
| 1077 | # user typed a bare IP/host, default to http:// so it "just works"; also |
| 1078 | # lowercase an explicit scheme ('HTTP://Host' -> 'http://Host'), keeping |
| 1079 | # host/path casing. nmap takes a raw host, so it's left untouched. |
| 1080 | if isinstance(target, str) and scan_type != ScanType.NMAP_VULN: |
| 1081 | t = target.strip() |
| 1082 | if '://' in t: |
| 1083 | _scheme, _rest = t.split('://', 1) |
| 1084 | if _scheme.lower() in ('http', 'https'): |
| 1085 | t = _scheme.lower() + '://' + _rest |
| 1086 | elif t: |
| 1087 | t = 'http://' + t |
| 1088 | target = t |
| 1089 | # If no explicit port was given, probe for a live web port so ZAP |
| 1090 | # scans the service that's actually listening instead of assuming |
| 1091 | # :80 and hard-failing on an HTTPS-only or alt-port host. Skipped |
| 1092 | # for nmap (raw host) by the scan_type guard above. |
| 1093 | target = self._resolve_web_target(target) |
nothing calls this directly
no test coverage detected