def test_24_prompt_short_2(self): """ U24 | short_prompt = 2 should show full dir path """ expected = '%s: %s$ ' % (getuser(), os.getcwd()) args = self.args + ['--prompt_short=2'] userconf = CheckConfig(args).returnconf() currentpath = "%s/foo" % userconf['home_path'] prompt = updateprompt(currentpath, userconf) # sort lists to compare return self.assertEqual(prompt, expected)
def test_22_prompt_short_0(self): """ U22 | short_prompt = 0 should show dir compared to home dir """ expected = '%s:~/foo$ ' % getuser() args = self.args + ['--prompt_short=0'] userconf = CheckConfig(args).returnconf() currentpath = "%s/foo" % userconf['home_path'] prompt = updateprompt(currentpath, userconf) # sort lists to compare return self.assertEqual(prompt, expected)
def test_23_prompt_short_1(self): """ U23 | short_prompt = 1 should show only current dir """ expected = '%s: foo$ ' % getuser() args = self.args + ['--prompt_short=1'] userconf = CheckConfig(args).returnconf() currentpath = "%s/foo" % userconf['home_path'] prompt = updateprompt(currentpath, userconf) # sort lists to compare return self.assertEqual(prompt, expected)
def cd(directory, conf): """ implementation of the "cd" command """ # expand user's ~ directory = os.path.expanduser(directory) # remove quotes if present directory = directory.strip("'").strip('"') if len(directory) >= 1: # add wildcard completion support to cd if directory.find('*'): # get all files and directories matching wildcard wildall = glob.glob(directory) wilddir = [] # filter to only directories for item in wildall: if os.path.isdir(item): wilddir.append(item) # sort results wilddir.sort() # if any results are returned, pick first one if len(wilddir) >= 1: directory = wilddir[0] # go previous directory if directory == '-': directory = conf['oldpwd'] # store current directory in oldpwd variable conf['oldpwd'] = os.getcwd() # change directory try: os.chdir(os.path.realpath(directory)) conf['promptprint'] = utils.updateprompt(os.getcwd(), conf) except OSError as excp: sys.stdout.write("lshell: %s: %s\n" % (directory, excp.strerror)) return excp.errno, conf else: os.chdir(conf['home_path']) conf['promptprint'] = utils.updateprompt(os.getcwd(), conf) return 0, conf
def __init__(self, userconf, args, stdin=None, stdout=None, stderr=None, g_cmd=None, g_line=None): if stdin is None: self.stdin = sys.stdin else: self.stdin = stdin if stdout is None: self.stdout = sys.stdout else: self.stdout = stdout if stderr is None: self.stderr = sys.stderr else: self.stderr = stderr self.args = args self.conf = userconf self.log = self.conf['logpath'] # Set timer if self.conf['timer'] > 0: self.mytimer(self.conf['timer']) self.identchars = self.identchars + '+./-' self.log.error('Logged in') cmd.Cmd.__init__(self) # set prompt self.conf['promptprint'] = utils.updateprompt(os.getcwd(), self.conf) self.intro = self.conf['intro'] # initialize oldpwd variable to home directory self.conf['oldpwd'] = self.conf['home_path'] # initialize cli variables self.g_cmd = g_cmd self.g_line = g_line self.g_arg = None # initialize return code self.retcode = 0
def __init__(self, userconf, args, stdin=None, stdout=None, stderr=None, g_cmd=None, g_line=None): if stdin is None: self.stdin = sys.stdin else: self.stdin = stdin if stdout is None: self.stdout = sys.stdout else: self.stdout = stdout if stderr is None: self.stderr = sys.stderr else: self.stderr = stderr self.args = args self.conf = userconf self.log = self.conf['logpath'] # Set timer if self.conf['timer'] > 0: self.mytimer(self.conf['timer']) self.identchars = self.identchars + '+./-' self.log.error('Logged in') cmd.Cmd.__init__(self) # set prompt self.conf['promptprint'] = utils.updateprompt(os.getcwd(), self.conf) self.intro = self.conf['intro'] # initialize oldpwd variable to home directory self.conf['oldpwd'] = self.conf['home_path'] # initialize cli variables self.g_cmd = g_cmd self.g_line = g_line # initialize return code self.retcode = 0
def check_path(line, conf, completion=None, ssh=None, strict=None): """ Check if a path is entered in the line. If so, it checks if user are allowed to see this path. If user is not allowed, it calls warn_count. In case of completion, it only returns 0 or 1. """ allowed_path_re = str(conf['path'][0]) denied_path_re = str(conf['path'][1][:-1]) # split line depending on the operators sep = re.compile(r'\ |;|\||&') line = line.strip() line = sep.split(line) for item in line: # remove potential quotes or back-ticks item = re.sub(r'^["\'`]|["\'`]$', '', item) # remove potential $(), ${}, `` item = re.sub(r'^\$[\(\{]|[\)\}]$', '', item) # if item has been converted to something other than a string # or an int, reconvert it to a string if type(item) not in ['str', 'int']: item = str(item) # replace "~" with home path item = os.path.expanduser(item) # expand shell wildcards using "echo" # i know, this a bit nasty... if re.findall('\$|\*|\?', item): # remove quotes if available item = re.sub("\"|\'", "", item) import subprocess p = subprocess.Popen("`which echo` %s" % item, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) cout = p.stdout try: item = cout.readlines()[0].decode('utf8').split(' ')[0] item = item.strip() item = os.path.expandvars(item) except IndexError: conf['logpath'].critical('*** Internal error: command not ' 'executed') return 1, conf tomatch = os.path.realpath(item) if os.path.isdir(tomatch) and tomatch[-1] != '/': tomatch += '/' match_allowed = re.findall(allowed_path_re, tomatch) if denied_path_re: match_denied = re.findall(denied_path_re, tomatch) else: match_denied = None # if path not allowed # case path executed: warn, and return 1 # case completion: return 1 if not match_allowed or match_denied: if not completion: ret, conf = warn_count('path', tomatch, conf, strict=strict, ssh=ssh) return 1, conf if not completion: if not re.findall(allowed_path_re, os.getcwd() + '/'): ret, conf = warn_count('path', tomatch, conf, strict=strict, ssh=ssh) os.chdir(conf['home_path']) conf['promptprint'] = utils.updateprompt(os.getcwd(), conf) return 1, conf return 0, conf
def __getattr__(self, attr): """ This method actually takes care of all the called method that are not resolved (i.e not existing methods). It actually will simulate the existence of any method entered in the 'allowed' variable list. e.g. You just have to add 'uname' in list of allowed commands in the 'allowed' variable, and lshell will react as if you had added a do_uname in the ShellCmd class! """ # expand environment variables in command line self.g_cmd = os.path.expandvars(self.g_cmd) self.g_line = os.path.expandvars(self.g_line) self.g_arg = os.path.expandvars(self.g_arg) # in case the configuration file has been modified, reload it if self.conf['config_mtime'] != os.path.getmtime( self.conf['configfile']): from lshell.checkconfig import CheckConfig self.conf = CheckConfig(['--config', self.conf['configfile']], refresh=1).returnconf() self.conf['promptprint'] = utils.updateprompt( os.getcwd(), self.conf) self.log = self.conf['logpath'] if self.g_cmd in ['quit', 'exit', 'EOF']: self.log.error('Exited') if self.g_cmd == 'EOF': self.stdout.write('\n') if self.conf['disable_exit'] != 1: sys.exit(0) # check that commands/chars present in line are allowed/secure ret_check_secure, self.conf = sec.check_secure( self.g_line, self.conf, strict=self.conf['strict']) if ret_check_secure == 1: # see http://tldp.org/LDP/abs/html/exitcodes.html self.retcode = 126 return object.__getattribute__(self, attr) # check that path present in line are allowed/secure ret_check_path, self.conf = sec.check_path(self.g_line, self.conf, strict=self.conf['strict']) if ret_check_path == 1: # see http://tldp.org/LDP/abs/html/exitcodes.html self.retcode = 126 # in case request was sent by WinSCP, return error code has to be # sent via an specific echo command if self.conf['winscp'] and re.search('WinSCP: this is end-of-file', self.g_line): utils.exec_cmd('echo "WinSCP: this is end-of-file: %s"' % self.retcode) return object.__getattribute__(self, attr) if self.g_cmd in self.conf['allowed']: if self.conf['timer'] > 0: self.mytimer(0) self.g_arg = re.sub('^~$|^~/', '%s/' % self.conf['home_path'], self.g_arg) self.g_arg = re.sub(' ~/', ' %s/' % self.conf['home_path'], self.g_arg) # replace previous command exit code # in case multiple commands (using separators), only replace first # command. Regex replaces all occurrences of $?, before ;,&,| if re.search('[;&|]', self.g_line): p = re.compile("(\s|^)(\$\?)([\s|$]?[;&|].*)") else: p = re.compile("(\s|^)(\$\?)(\s|$)") self.g_line = p.sub(r' %s \3' % self.retcode, self.g_line) if type(self.conf['aliases']) == dict: self.g_line = utils.get_aliases(self.g_line, self.conf['aliases']) self.log.info('CMD: "%s"' % self.g_line) if self.g_cmd == 'cd': # split cd <dir> and rest of command cmd_split = re.split(';|&&|&|\|\||\|', self.g_line, 1) # in case the are commands following cd, first change the # directory, then execute the command if len(cmd_split) == 2: directory, command = cmd_split # only keep cd's argument directory = directory.split('cd', 1)[1].strip() # change directory then, if success, execute the rest of # the cmd line self.retcode, self.conf = builtins.cd(directory, self.conf) if self.retcode == 0: self.retcode = utils.exec_cmd(command) else: # set directory to command line argument and change dir directory = self.g_arg self.retcode, self.conf = builtins.cd(directory, self.conf) # built-in lpath function: list all allowed path elif self.g_cmd == 'lpath': self.retcode = builtins.lpath(self.conf) # built-in lsudo function: list all allowed sudo commands elif self.g_cmd == 'lsudo': self.retcode = builtins.lsudo(self.conf) # built-in history function: print command history elif self.g_cmd == 'history': self.retcode = builtins.history(self.conf, self.log) # built-in export function elif self.g_cmd == 'export': self.retcode, var = builtins.export(self.g_line) if self.retcode == 1: self.log.critical( "** forbidden environment variable '%s'" % var) # case 'cd' is in an alias e.g. {'toto':'cd /var/tmp'} elif self.g_line[0:2] == 'cd': self.g_cmd = self.g_line.split()[0] directory = ' '.join(self.g_line.split()[1:]) self.retcode, self.conf = builtins.cd(directory, self.conf) else: self.retcode = utils.exec_cmd(self.g_line) elif self.g_cmd not in ['', '?', 'help', None]: self.log.warn('INFO: unknown syntax -> "%s"' % self.g_line) self.stderr.write('*** unknown syntax: %s\n' % self.g_cmd) self.g_cmd, self.g_arg, self.g_line = ['', '', ''] if self.conf['timer'] > 0: self.mytimer(self.conf['timer']) return object.__getattribute__(self, attr)
def __getattr__(self, attr): """ This method actually takes care of all the called method that are not resolved (i.e not existing methods). It actually will simulate the existence of any method entered in the 'allowed' variable list. e.g. You just have to add 'uname' in list of allowed commands in the 'allowed' variable, and lshell will react as if you had added a do_uname in the ShellCmd class! """ # expand environment variables in command line self.g_cmd = os.path.expandvars(self.g_cmd) self.g_line = os.path.expandvars(self.g_line) self.g_arg = os.path.expandvars(self.g_arg) # in case the configuration file has been modified, reload it if self.conf['config_mtime'] != os.path.getmtime( self.conf['configfile']): from lshell.checkconfig import CheckConfig self.conf = CheckConfig(['--config', self.conf['configfile']], refresh=1).returnconf() self.conf['promptprint'] = utils.updateprompt(os.getcwd(), self.conf) self.log = self.conf['logpath'] if self.g_cmd in ['quit', 'exit', 'EOF']: self.log.error('Exited') if self.g_cmd == 'EOF': self.stdout.write('\n') if self.conf['disable_exit'] != 1: sys.exit(0) # check that commands/chars present in line are allowed/secure ret_check_secure, self.conf = sec.check_secure( self.g_line, self.conf, strict=self.conf['strict']) if ret_check_secure == 1: # see http://tldp.org/LDP/abs/html/exitcodes.html self.retcode = 126 return object.__getattribute__(self, attr) # check that path present in line are allowed/secure ret_check_path, self.conf = sec.check_path(self.g_line, self.conf, strict=self.conf['strict']) if ret_check_path == 1: # see http://tldp.org/LDP/abs/html/exitcodes.html self.retcode = 126 # in case request was sent by WinSCP, return error code has to be # sent via an specific echo command if self.conf['winscp'] and re.search('WinSCP: this is end-of-file', self.g_line): utils.exec_cmd('echo "WinSCP: this is end-of-file: %s"' % self.retcode) return object.__getattribute__(self, attr) if self.g_cmd in self.conf['allowed']: if self.conf['timer'] > 0: self.mytimer(0) self.g_arg = re.sub('^~$|^~/', '%s/' % self.conf['home_path'], self.g_arg) self.g_arg = re.sub(' ~/', ' %s/' % self.conf['home_path'], self.g_arg) # replace previous command exit code # in case multiple commands (using separators), only replace first # command. Regex replaces all occurrences of $?, before ;,&,| if re.search('[;&\|]', self.g_line): p = re.compile("(\s|^)(\$\?)([\s|$]?[;&|].*)") else: p = re.compile("(\s|^)(\$\?)(\s|$)") self.g_line = p.sub(r' %s \3' % self.retcode, self.g_line) if type(self.conf['aliases']) == dict: self.g_line = utils.get_aliases(self.g_line, self.conf['aliases']) self.log.info('CMD: "%s"' % self.g_line) if self.g_cmd == 'cd': # split cd <dir> and rest of command cmd_split = re.split(';|&&|&|\|\||\|', self.g_line, 1) # in case the are commands following cd, first change the # directory, then execute the command if len(cmd_split) == 2: directory, command = cmd_split # only keep cd's argument directory = directory.split('cd', 1)[1].strip() # change directory then, if success, execute the rest of # the cmd line self.retcode, self.conf = builtins.cd(directory, self.conf) if self.retcode == 0: self.retcode = utils.exec_cmd(command) else: # set directory to command line argument and change dir directory = self.g_arg self.retcode, self.conf = builtins.cd(directory, self.conf) # built-in lpath function: list all allowed path elif self.g_cmd == 'lpath': self.retcode = builtins.lpath(self.conf) # built-in lsudo function: list all allowed sudo commands elif self.g_cmd == 'lsudo': self.retcode = builtins.lsudo(self.conf) # built-in history function: print command history elif self.g_cmd == 'history': self.retcode = builtins.history(self.conf, self.log) # built-in export function elif self.g_cmd == 'export': self.retcode, var = builtins.export(self.g_line) if self.retcode == 1: self.log.critical("** forbidden environment variable '%s'" % var) # case 'cd' is in an alias e.g. {'toto':'cd /var/tmp'} elif self.g_line[0:2] == 'cd': self.g_cmd = self.g_line.split()[0] directory = ' '.join(self.g_line.split()[1:]) self.retcode, self.conf = builtins.cd(directory, self.conf) else: self.retcode = utils.exec_cmd(self.g_line) elif self.g_cmd not in ['', '?', 'help', None]: self.log.warn('INFO: unknown syntax -> "%s"' % self.g_line) self.stderr.write('*** unknown syntax: %s\n' % self.g_cmd) self.g_cmd, self.g_arg, self.g_line = ['', '', ''] if self.conf['timer'] > 0: self.mytimer(self.conf['timer']) return object.__getattribute__(self, attr)