Skip to content

Commit

Permalink
Merge pull request #30 from DinoTools/module_status
Browse files Browse the repository at this point in the history
Support module status
  • Loading branch information
phibos committed Nov 11, 2015
2 parents 603d4ad + c24c954 commit 11f008a
Show file tree
Hide file tree
Showing 21 changed files with 221 additions and 27 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ __pycache__
/dist/
/docs/build/
*.egg-info/
.idea/
15 changes: 8 additions & 7 deletions sslscan/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,9 @@
__uri__, __version__
)
from sslscan.config import ScanConfig
from sslscan.exception import ModuleNotFound
from sslscan.exception import ModuleLoadStatus, ModuleNotFound
from sslscan.kb import KnowledgeBase
from sslscan.module import STATUS_OK
from sslscan.module.handler import BaseHandler
from sslscan.module.rating import BaseRating
from sslscan.module.report import BaseReport
Expand Down Expand Up @@ -88,6 +89,7 @@ class Scanner(object):
}
)
]

def __init__(self, module_manager=None):
global modules
self._module_manager = module_manager
Expand Down Expand Up @@ -115,12 +117,11 @@ def append_load(self, name, config, base_class=None):
:param String name: Name of the module to load
:param Mixed config: Config of the module
:param class base_class: Module lookup filter
:return: False if module not found
"""

module = self._module_manager.get(name, base_class=base_class)
if module is None:
raise ModuleNotFound(name=name,base_class=base_class)
if module.status != STATUS_OK:
raise ModuleLoadStatus(name, base_class=base_class, module=module)

module = module(scanner=self)
module.config.set_values(config)
Expand Down Expand Up @@ -270,7 +271,7 @@ def get(self, name, base_class=None):
:param String name: Name of the module
:param class base_class: The filter
:return: If module exists return it or if not return None
:return: If module exists return it
:rtype: Mixed
"""

Expand All @@ -282,14 +283,14 @@ def get(self, name, base_class=None):
if module.alias and name in module.alias:
return module

return None
raise ModuleNotFound(name=name, base_class=base_class)

def get_modules(self, base_class=None):
"""
Return a list of available modules. Use the base_class as filter option
:param class base_class: The filter
:rtype: List
:rtype: BaseScan[]
"""

result = []
Expand Down
86 changes: 86 additions & 0 deletions sslscan/_helper/__init__.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
import sys

try:
from colorama import Fore
from colorama import init as colorama_init
except ImportError:
colorama_init = None

from sslscan.module import STATUS_ERROR, STATUS_OK, STATUS_WARNING


def rating2color(color, level):
# ToDo:
if level == None:
Expand All @@ -17,6 +22,87 @@ def rating2color(color, level):
return color.RESET


class Console(object):
def __init__(self):
self.icon = ConsoleIcons(self)
self.color = ColorConsole()

@property
def is_terminal(self):
return sys.stdout.encoding is not None

@property
def encoding(self):
if sys.stdout.encoding is None:
return ""
else:
return sys.stdout.encoding.lower()

def map_module_status(self, status):
"""
Map a module status to a color and an icon
:param Integer status: Module status code
:return: Status color and icon
:rtype: Tuple
"""
color = ""
icon = ""
if status == STATUS_OK:
color = self.color.OK
icon = self.icon.OK
elif status == STATUS_WARNING:
color = self.color.WARNING
icon = self.icon.WARNING
elif status == STATUS_ERROR:
color = self.color.ERROR
icon = self.icon.ERROR

return color, icon


class ConsoleIcons(object):
def __init__(self, console):
"""
:param Console console:
"""
self._console = console

self._mapped_characters = {
"default": {
"ERROR": "E",
"OK": "O",
"WARNING": "W"
},
"utf8": {
"ERROR": "\u2715",
"OK": "\u2713",
"WARNING": "\u26A0"
}
}

@property
def scheme(self):
# ToDo: get scheme from config
if self._console.is_terminal and self._console.encoding == "utf-8":
return "utf8"
return "default"

def __getattr__(self, name):
characters = self._mapped_characters.get(self.scheme)
if characters is None:
characters = self._mapped_characters["default"]

icon = characters.get(name)
if icon is None:
icon = self._mapped_characters["default"].get(name)
if icon is None:
raise KeyError("Icon not found")

return icon


class ColorConsole(object):
def __init__(self):
if colorama_init:
Expand Down
17 changes: 16 additions & 1 deletion sslscan/_helper/openssl.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,20 @@
import six

import flextls
from OpenSSL import SSL

openssl_enabled = False
version_pyopenssl = None
version_openssl = None
try:
import OpenSSL
from OpenSSL import SSL
version_pyopenssl = OpenSSL.__version__
version_openssl = SSL.SSLeay_version(0)
if isinstance(version_openssl, six.binary_type):
version_openssl = version_openssl.decode('ascii')
except ImportError:
OpenSSL = None


def convert_version2method(protocol_version):
"""
Expand Down
14 changes: 14 additions & 0 deletions sslscan/exception.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,20 @@ def __str__(self):
return "Module '{}' not found".format(self.name)


class ModuleLoadStatus(Exception):
def __init__(self, name="", base_class=None, module=None):
self.name = name
self.base_class = base_class
self.module = module

def __str__(self):
status = "Unknown"
if self.module:
from sslscan.module import STATUS_NAMES
status = STATUS_NAMES.get(self.module.status, status)
return "Unable to load module '{}' with status '{}'".format(self.name, status)


class BaseConfigError(Exception):
pass

Expand Down
11 changes: 11 additions & 0 deletions sslscan/module/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
from sslscan.config import ModuleConfig

STATUS_OK = 1
STATUS_WARNING = 2
STATUS_ERROR = 3
STATUS_NAMES = {
1: "OK",
2: "Warning",
3: "Error"
}


class BaseModule(object):
"""
Expand All @@ -9,6 +18,8 @@ class BaseModule(object):
"""

alias = None
status = STATUS_OK
status_messages = None
config_options = []

def __init__(self, scanner=None, config=None):
Expand Down
1 change: 1 addition & 0 deletions sslscan/module/scan/protocol_http.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ class ProtocolHTTP(BaseInfoScan):
"""

name = "protocol.http"
alias = ("http",)

def run(self):
if self._scanner.handler.name != "http":
Expand Down
1 change: 1 addition & 0 deletions sslscan/module/scan/protocol_imap.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ class ProtocolIMAP(BaseInfoScan):
"""

name = "protocol.imap"
alias = ("imap",)

def run(self):
if self._scanner.handler.name != "imap":
Expand Down
1 change: 1 addition & 0 deletions sslscan/module/scan/protocol_pop3.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ class ProtocolPOP3(BaseInfoScan):
"""

name = "protocol.pop3"
alias = ("pop3",)

def run(self):
if self._scanner.handler.name != "pop3":
Expand Down
1 change: 1 addition & 0 deletions sslscan/module/scan/protocol_smtp.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ class ProtocolSMTP(BaseInfoScan):
"""

name = "protocol.smtp"
alias = ("smtp",)

def run(self):
if self._scanner.handler.name != "smtp":
Expand Down
1 change: 1 addition & 0 deletions sslscan/module/scan/server_alpn.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ class ServerApplicationLayerProtocolNegotiation(BaseScan):
"""

name = "server.alpn"
alias = ("alpn",)

def _scan_alpn_tls(self, protocol_version):
def hook_alpn(record, protocols=None):
Expand Down
28 changes: 18 additions & 10 deletions sslscan/module/scan/server_certificate.py
Original file line number Diff line number Diff line change
@@ -1,25 +1,34 @@
import flextls
from flextls.protocol.handshake import Handshake, ServerCertificate, DTLSv10Handshake

from sslscan import modules
from sslscan._helper.openssl import version_openssl, version_pyopenssl
from sslscan.module import STATUS_OK, STATUS_ERROR
from sslscan.module.scan import BaseScan

openssl_enabled = False
version_info = []
try:
from OpenSSL import crypto

openssl_enabled = True
except:
if version_pyopenssl:
version_info.append("pyOpenSSL version {}".format(version_pyopenssl))
if version_openssl:
version_info.append("OpenSSL version {}".format(version_openssl))
except ImportError:
pass

import flextls
from flextls.protocol.handshake import Handshake, ServerCertificate, DTLSv10Handshake

from sslscan import modules
from sslscan.exception import Timeout
from sslscan.module.scan import BaseScan


class ScanServerCertificate(BaseScan):
"""
Extract certificate information.
"""

name = "server.certificate"
alias = ("certificate",)
status = STATUS_OK if openssl_enabled else STATUS_ERROR
status_messages = ["OpenSSL is {}".format("available" if openssl_enabled else "missing")] + version_info

def __init__(self, **kwargs):
BaseScan.__init__(self, **kwargs)
Expand Down Expand Up @@ -72,5 +81,4 @@ def run(self):
kb.set("server.certificate_chain", cert_chain)


if openssl_enabled is True:
modules.register(ScanServerCertificate)
modules.register(ScanServerCertificate)
1 change: 1 addition & 0 deletions sslscan/module/scan/server_ciphers.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ class ServerCiphers(BaseScan):
"""

name = "server.ciphers"
alias = ("ciphers",)

def __init__(self, **kwargs):
BaseScan.__init__(self, **kwargs)
Expand Down
1 change: 1 addition & 0 deletions sslscan/module/scan/server_compression.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ class ServerCompression(BaseScan):
"""

name = "server.compression"
alias = ("compression",)

def run(self):
def stop_condition(record, records):
Expand Down
1 change: 1 addition & 0 deletions sslscan/module/scan/server_elliptic_curves.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ class ServerEllipticCurves(BaseScan):
"""

name = "server.elliptic_curves"
alias = ("elliptic_curves",)

def __init__(self, **kwargs):
BaseScan.__init__(self, **kwargs)
Expand Down
1 change: 1 addition & 0 deletions sslscan/module/scan/server_next_protocol.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ class ServerNextProtocolNegotiation(BaseScan):
"""

name = "server.next_protocol"
alias = ("next_protocol",)

def _scan_next_protocol_tls(self, protocol_version):
def hook_next_protocol(record):
Expand Down
1 change: 1 addition & 0 deletions sslscan/module/scan/server_preferred_ciphers.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ class ServerPreferredCiphers(ServerCiphers):
"""

name = "server.preferred_ciphers"
alias = ("preferred_ciphers",)

def _add_cipher_to_kb(self, protocol_version, cipher_suite, status):
kb = self._scanner.get_knowledge_base()
Expand Down
Loading

0 comments on commit 11f008a

Please sign in to comment.