Passed
Pull Request — master (#100)
by
unknown
01:39
created

gmp.clients.pyshell.pretty()   A

Complexity

Conditions 5

Size

Total Lines 18
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 8
nop 1
dl 0
loc 18
rs 9.3333
c 0
b 0
f 0
1
# -*- coding: utf-8 -*-
0 ignored issues
show
Coding Style introduced by
This module should have a docstring.

The coding style of this project requires that you add a docstring to this code element. Below, you find an example for methods:

class SomeClass:
    def some_method(self):
        """Do x and return foo."""

If you would like to know more about docstrings, we recommend to read PEP-257: Docstring Conventions.

Loading history...
2
# Copyright (C) 2018 Greenbone Networks GmbH
3
#
4
# SPDX-License-Identifier: GPL-3.0-or-later
5
#
6
# This program is free software: you can redistribute it and/or modify
7
# it under the terms of the GNU General Public License as published by
8
# the Free Software Foundation, either version 3 of the License, or
9
# (at your option) any later version.
10
#
11
# This program is distributed in the hope that it will be useful,
12
# but WITHOUT ANY WARRANTY; without even the implied warranty of
13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
# GNU General Public License for more details.
15
#
16
# You should have received a copy of the GNU General Public License
17
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
18
19
import argparse
20
import code
21
import configparser
22
import getpass
23
import logging
24
import os
25
import sys
26
27
from lxml import etree
28
29
from gmp import Gmp, get_version
30
from gmp.connection import (SSHConnection,
31
                            TLSConnection,
32
                            UnixSocketConnection,
33
                            DEFAULT_UNIX_SOCKET_PATH,
34
                            DEFAULT_TIMEOUT,
35
                            DEFAULT_GVM_PORT)
36
from gmp.transform import EtreeCheckCommandTransform
37
38
39
__version__ = get_version()
40
41
logger = logging.getLogger(__name__)
0 ignored issues
show
Coding Style Naming introduced by
The name logger does not conform to the constant naming conventions ((([A-Z_][A-Z0-9_]*)|(__.*__))$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
42
43
help_text = """
0 ignored issues
show
Coding Style Naming introduced by
The name help_text does not conform to the constant naming conventions ((([A-Z_][A-Z0-9_]*)|(__.*__))$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
44
    gvm-pyshell {version} (C) 2017 Greenbone Networks GmbH
45
46
    This program is a command line tool to access services
47
    via GMP(Greenbone Management Protocol) and
48
    OSP(Open Scanner Protocol).
49
    It is possible to start a shell like the python interactive
50
    mode where you could type things like "tasks = gmp.get_task".
51
52
    At the moment only these commands are support in the interactive shell:
53
54
    gmp.get_version()
55
    gmp.authenticate([username], [password])
56
    gmp.get_tasks()
57
    gmp.get_reports()
58
    gmp.get_results()
59
    gmp.get_assets()
60
    gmp.get_port_lists()
61
62
    Example:
63
        gmp.authenticate('admin', 'admin')
64
        tasks = gmp.get_tasks()
65
66
        list = tasks.xpath('task')
67
68
        taskid = list[0].attrib
69
70
        load('my_commands.gmp')
71
72
    Good introduction in working with XPath is well described here:
73
    https://www.w3schools.com/xml/xpath_syntax.asp
74
75
    To get out of the shell enter:
76
        Ctrl + D on Linux  or
77
        Ctrl + Z on Windows
78
79
    Further Information about the GMP Protocol can be found at:
80
    http://docs.greenbone.net/index.html#api_documentation
81
    Note: "GMP" was formerly known as "OMP".
82
83
    This program is free software: you can redistribute it and/or modify
84
    it under the terms of the GNU General Public License as published by
85
    the Free Software Foundation, either version 3 of the License, or
86
    (at your option) any later version.
87
88
    This program is distributed in the hope that it will be useful,
89
    but WITHOUT ANY WARRANTY; without even the implied warranty of
90
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
91
    GNU General Public License for more details.
92
93
    You should have received a copy of the GNU General Public License
94
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
95
    """.format(version=__version__)
96
97
98
class Help(object):
0 ignored issues
show
Unused Code introduced by
The variable __class__ seems to be unused.
Loading history...
99
    """Help class to overwrite the help function from python itself.
100
    """
101
102
    def __repr__(self):
103
        # do pwd command
104
        return help_text
105
help = Help()
0 ignored issues
show
Bug Best Practice introduced by
This seems to re-define the built-in help.

It is generally discouraged to redefine built-ins as this makes code very hard to read.

Loading history...
Coding Style Naming introduced by
The name help does not conform to the constant naming conventions ((([A-Z_][A-Z0-9_]*)|(__.*__))$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
106
107
# gmp has to be global, so the load-function has the correct namespace
108
gmp = None
0 ignored issues
show
Coding Style Naming introduced by
The name gmp does not conform to the constant naming conventions ((([A-Z_][A-Z0-9_]*)|(__.*__))$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
109
110
111
def main():
0 ignored issues
show
Coding Style introduced by
This function should have a docstring.

The coding style of this project requires that you add a docstring to this code element. Below, you find an example for methods:

class SomeClass:
    def some_method(self):
        """Do x and return foo."""

If you would like to know more about docstrings, we recommend to read PEP-257: Docstring Conventions.

Loading history...
Comprehensibility introduced by
This function exceeds the maximum number of variables (20/15).
Loading history...
112
    parser = argparse.ArgumentParser(
113
        prog='gvm-pyshell',
114
        description=help_text,
115
        formatter_class=argparse.RawTextHelpFormatter,
116
        add_help=False,
117
        epilog="""
118
usage: gvm-pyshell [-h] [--version] [connection_type] ...
119
   or: gvm-pyshell connection_type --help""")
120
    subparsers = parser.add_subparsers(metavar='[connection_type]')
121
    subparsers.required = True
122
    subparsers.dest = 'connection_type'
123
124
    parser.add_argument(
125
        '-h', '--help', action='help',
126
        help='Show this help message and exit.')
127
128
    parent_parser = argparse.ArgumentParser(add_help=False)
129
130
    parent_parser.add_argument(
131
        '-c', '--config', nargs='?', const='~/.config/gvm-tools.conf',
132
        help='Configuration file path. Default: ~/.config/gvm-tools.conf')
133
    args_before, remaining_args = parent_parser.parse_known_args()
134
135
    defaults = {
136
        'gmp_username': '',
137
        'gmp_password': ''
138
    }
139
140
    # Retrieve data from config file
141
    if args_before.config:
142
        try:
143
            config = configparser.SafeConfigParser()
144
            path = os.path.expanduser(args_before.config)
145
            config.read(path)
146
            defaults = dict(config.items('Auth'))
147
        except Exception as e:
0 ignored issues
show
Best Practice introduced by
Catching very general exceptions such as Exception is usually not recommended.

Generally, you would want to handle very specific errors in the exception handler. This ensure that you do not hide other types of errors which should be fixed.

So, unless you specifically plan to handle any error, consider adding a more specific exception.

Loading history...
Coding Style Naming introduced by
The name e does not conform to the variable naming conventions ((([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
148
            print(str(e))
149
150
    parent_parser.set_defaults(**defaults)
151
152
    parent_parser.add_argument(
153
        '--timeout', required=False, default=DEFAULT_TIMEOUT, type=int,
154
        help='Wait <seconds> for response or if value -1, then wait '
155
             'continuously. Default: %(default)s')
156
    parent_parser.add_argument(
157
        '--log', nargs='?', dest='loglevel', const='INFO',
158
        choices=['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'],
159
        help='Activates logging. Default level: INFO.')
160
    parent_parser.add_argument(
161
        '-i', '--interactive', action='store_true', default=False,
162
        help='Start an interactive Python shell.')
163
    parent_parser.add_argument('--gmp-username', help='GMP username.')
164
    parent_parser.add_argument('--gmp-password', help='GMP password.')
165
    parent_parser.add_argument(
166
        'script', nargs='*',
167
        help='Preload gmp script. Example: myscript.gmp.')
168
169
    parser_ssh = subparsers.add_parser(
170
        'ssh', help='Use SSH connection for gmp service.',
171
        parents=[parent_parser])
172
    parser_ssh.add_argument('--hostname', required=True,
173
                            help='Hostname or IP-Address.')
174
    parser_ssh.add_argument('--port', required=False,
175
                            default=22, help='Port. Default: %(default)s.')
176
    parser_ssh.add_argument('--ssh-user', default='gmp',
177
                            help='SSH Username. Default: %(default)s.')
178
179
    parser_tls = subparsers.add_parser(
180
        'tls', help='Use TLS secured connection for gmp service.',
181
        parents=[parent_parser])
182
    parser_tls.add_argument('--hostname', required=True,
183
                            help='Hostname or IP-Address.')
184
    parser_tls.add_argument('--port', required=False,
185
                            default=DEFAULT_GVM_PORT,
186
                            help='Port. Default: %(default)s.')
187
188
    parser_socket = subparsers.add_parser(
189
        'socket', help='Use UNIX-Socket connection for gmp service.',
190
        parents=[parent_parser])
191
    parser_socket.add_argument(
192
        '--sockpath', nargs='?', default=DEFAULT_UNIX_SOCKET_PATH,
193
        help='Depreacted. Use --socketpath instead')
194
    parser_socket.add_argument(
195
        '--socketpath', nargs='?', default=DEFAULT_UNIX_SOCKET_PATH,
196
        help='UNIX-Socket path. Default: %(default)s.')
197
198
    parser.add_argument(
199
        '-V', '--version', action='version',
200
        version='%(prog)s {version}'.format(version=__version__),
201
        help='Show program\'s version number and exit')
202
203
    global args
0 ignored issues
show
Coding Style Naming introduced by
The name args does not conform to the constant naming conventions ((([A-Z_][A-Z0-9_]*)|(__.*__))$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
Bug introduced by
Global variable 'args' undefined at the module level
Loading history...
204
    args = parser.parse_args(remaining_args)
205
206
    # Sets the logging
207
    if args.loglevel is not None:
208
        level = logging.getLevelName(args.loglevel)
209
        logging.basicConfig(filename='gvm-pyshell.log', level=level)
210
211
    # If timeout value is -1, then the socket has no timeout for this session
212
    if args.timeout == -1:
213
        args.timeout = None
214
215
    # Open the right connection. SSH at last for default
216
    if 'socket' in args.connection_type:
217
        socketpath = args.socketpath
218
        if socketpath is None:
219
            socketpath = args.sockpath
220
221
        connection = UnixSocketConnection(path=socketpath,
222
                                          timeout=args.timeout)
223
    elif 'tls' in args.connection_type:
224
        connection = TLSConnection(hostname=args.hostname, port=args.port,
225
                                   timeout=args.timeout)
226
    else:
227
        connection = SSHConnection(hostname=args.hostname, port=args.port,
228
                                   timeout=args.timeout, username=args.ssh_user,
229
                                   password='')
230
231
    # Ask for login credentials if none are given
232
    if not args.gmp_username:
233
        while len(args.gmp_username) == 0:
0 ignored issues
show
Unused Code introduced by
Do not use len(SEQUENCE) as condition value
Loading history...
234
            args.gmp_username = input('Enter username: ')
235
236
    if not args.gmp_password:
237
        args.gmp_password = getpass.getpass(
238
            'Enter password for {0}: '.format(args.gmp_username))
239
240
    global gmp
0 ignored issues
show
Coding Style Naming introduced by
The name gmp does not conform to the constant naming conventions ((([A-Z_][A-Z0-9_]*)|(__.*__))$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
Coding Style introduced by
Usage of the global statement should be avoided.

Usage of global can make code hard to read and test, its usage is generally not recommended unless you are dealing with legacy code.

Loading history...
241
242
    # return an Etree at successful responses and raise execption on
243
    # unsuccessful ones
244
    gmp = Gmp(connection, transform=EtreeCheckCommandTransform())
245
246
    try:
247
        gmp.authenticate(args.gmp_username, args.gmp_password)
248
    except Exception as e: # pylint: disable=broad-except
0 ignored issues
show
Coding Style Naming introduced by
The name e does not conform to the variable naming conventions ((([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
249
        print('Please check your credentials!')
250
        print(e)
251
        sys.exit(1)
252
253
    with_script = args.script and len(args.script) > 0
254
    no_script_no_interactive = not args.interactive and not with_script
255
    script_and_interactive = args.interactive and with_script
256
    only_interactive = not with_script and args.interactive
257
    only_script = not args.interactive and with_script
258
259
    if no_script_no_interactive:
260
        enter_interactive_mode()
261
262
    if only_interactive:
263
        enter_interactive_mode()
264
265
    if script_and_interactive:
266
        load(args.script[0])
267
        enter_interactive_mode()
268
269
    if only_script:
270
        load(args.script[0])
271
272
    gmp.disconnect()
273
274
275
def enter_interactive_mode():
0 ignored issues
show
Coding Style introduced by
This function should have a docstring.

The coding style of this project requires that you add a docstring to this code element. Below, you find an example for methods:

class SomeClass:
    def some_method(self):
        """Do x and return foo."""

If you would like to know more about docstrings, we recommend to read PEP-257: Docstring Conventions.

Loading history...
276
    code.interact(
277
        banner='GVM Interactive Console. Type "help" to get information \
278
about functionality.',
279
        local=dict(globals(), **locals()))
280
281
282
def pretty(xml):
283
    """Prints beautiful XML-Code
284
285
    This function gets an object of list<lxml.etree._Element>
286
    or directly a lxml element.
287
    Print it with good readable format.
288
289
    Arguments:
290
        xml {obj} -- list<lxml.etree._Element> or directly a lxml element
291
    """
292
    if isinstance(xml, list):
293
        for item in xml:
294
            if etree.iselement(item):
0 ignored issues
show
Bug introduced by
The Module lxml.etree does not seem to have a member named iselement.

This check looks for calls to members that are non-existent. These calls will fail.

The member could have been renamed or removed.

Loading history...
295
                print(etree.tostring(item, pretty_print=True).decode('utf-8'))
0 ignored issues
show
Bug introduced by
The Module lxml.etree does not seem to have a member named tostring.

This check looks for calls to members that are non-existent. These calls will fail.

The member could have been renamed or removed.

Loading history...
296
            else:
297
                print(item)
298
    elif etree.iselement(xml):
0 ignored issues
show
Bug introduced by
The Module lxml.etree does not seem to have a member named iselement.

This check looks for calls to members that are non-existent. These calls will fail.

The member could have been renamed or removed.

Loading history...
299
        print(etree.tostring(xml, pretty_print=True).decode('utf-8'))
0 ignored issues
show
Bug introduced by
The Module lxml.etree does not seem to have a member named tostring.

This check looks for calls to members that are non-existent. These calls will fail.

The member could have been renamed or removed.

Loading history...
300
301
302
def load(path):
303
    """Loads a file into the interactive console
304
305
    Loads a file into the interactive console and execute it.
306
    TODO: Needs some security checks.
307
308
    Arguments:
309
        path {str} -- Path of file
310
    """
311
    try:
312
        file = open(path, 'r', newline='').read()
313
        exec(file, globals())
0 ignored issues
show
Coding Style Security introduced by
The use of exec is discouraged.

Execution of dynamic code might introduce security vulnerabilities. It is generally recommended to use this feature with care and only when necessary.

Loading history...
314
    except OSError as e:
0 ignored issues
show
Coding Style Naming introduced by
The name e does not conform to the variable naming conventions ((([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
315
        print(str(e))
316
317
318
if __name__ == '__main__':
319
    main()
320