Test Failed
Push — master ( 8e9f07...05aaee )
by Nicolas
02:05
created

glances.secure.__secure_popen()   C

Complexity

Conditions 9

Size

Total Lines 43
Code Lines 26

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 9
eloc 26
nop 1
dl 0
loc 43
rs 6.6666
c 0
b 0
f 0
1
# -*- coding: utf-8 -*-
2
#
3
# This file is part of Glances.
4
#
5
# SPDX-FileCopyrightText: 2022 Nicolas Hennion <[email protected]>
6
#
7
# SPDX-License-Identifier: LGPL-3.0-only
8
#
9
10
"""Secures functions for Glances"""
11
12
from glances.compat import nativestr
13
from subprocess import Popen, PIPE
14
import re
15
16
17
def secure_popen(cmd):
18
    """A more or less secure way to execute system commands
19
20
    Multiple command should be separated with a &&
21
22
    :return: the result of the commands
23
    """
24
    ret = ''
25
26
    # Split by multiple commands '&&'
27
    for c in cmd.split('&&'):
28
        ret += __secure_popen(c)
29
30
    return ret
31
32
33
def __secure_popen(cmd):
34
    """A more or less secure way to execute system command
35
36
    Manage redirection (>) and pipes (|)
37
    """
38
    # Split by redirection '>'
39
    cmd_split_redirect = cmd.split('>')
40
    if len(cmd_split_redirect) > 2:
41
        return 'Glances error: Only one file redirection allowed ({})'.format(cmd)
42
    elif len(cmd_split_redirect) == 2:
43
        stdout_redirect = cmd_split_redirect[1].strip()
44
        cmd = cmd_split_redirect[0]
45
    else:
46
        stdout_redirect = None
47
48
    sub_cmd_stdin = None
49
    p_last = None
50
    # Split by pipe '|'
51
    for sub_cmd in cmd.split('|'):
52
        # Split by space character, but do no split spaces within quotes (remove surrounding quotes, though)
53
        tmp_split = [_ for _ in list(filter(None, re.split(r'(\s+)|(".*?"+?)|(\'.*?\'+?)', sub_cmd))) if _ != ' ']
54
        sub_cmd_split = [_[1:-1] if (_[0]==_[-1]=='"') or (_[0]==_[-1]=='\'') else _ for _ in tmp_split]
55
        p = Popen(sub_cmd_split, shell=False, stdin=sub_cmd_stdin, stdout=PIPE, stderr=PIPE)
56
        if p_last is not None:
57
            # Allow p_last to receive a SIGPIPE if p exits.
58
            p_last.stdout.close()
59
        p_last = p
60
        sub_cmd_stdin = p.stdout
61
62
    p_ret = p_last.communicate()
63
64
    if nativestr(p_ret[1]) == '':
65
        # No error
66
        ret = nativestr(p_ret[0])
67
        if stdout_redirect is not None:
68
            # Write result to redirection file
69
            with open(stdout_redirect, "w") as stdout_redirect_file:
70
                stdout_redirect_file.write(ret)
71
    else:
72
        # Error
73
        ret = nativestr(p_ret[1])
74
75
    return ret
76