From 001eb4b55598abbe0319148abbe0e47214f42a40 Mon Sep 17 00:00:00 2001 From: BarsTiger Date: Sun, 23 Jan 2022 21:38:12 +0200 Subject: [PATCH] Initial commit --- .gitignore | 8 +++ auth.py | 33 +++++++++++ build.bat | 1 + build.bat.bak | 1 + console.py | 5 ++ horsy.py | 75 +++++++++++++++++++++++++ horsy.spec | 40 +++++++++++++ icon.ico | Bin 0 -> 21569 bytes manager.py | 138 +++++++++++++++++++++++++++++++++++++++++++++ path.py | 31 +++++++++++ requirements.txt | 6 ++ tui.py | 19 +++++++ uploader.py | 142 +++++++++++++++++++++++++++++++++++++++++++++++ vars.py | 2 + virustotal.py | 53 ++++++++++++++++++ 15 files changed, 554 insertions(+) create mode 100644 .gitignore create mode 100644 auth.py create mode 100644 build.bat create mode 100644 build.bat.bak create mode 100644 console.py create mode 100644 horsy.py create mode 100644 horsy.spec create mode 100644 icon.ico create mode 100644 manager.py create mode 100644 path.py create mode 100644 requirements.txt create mode 100644 tui.py create mode 100644 uploader.py create mode 100644 vars.py create mode 100644 virustotal.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..57786ad --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +# Client +**/__pycache__/ +/Source/Client/apps/ +/Source/Client/build/ +/Source/Client/sources/ +/Source/Client/*.spec +/Source/Client/*.bak +/Source/Client/config.cfg \ No newline at end of file diff --git a/auth.py b/auth.py new file mode 100644 index 0000000..015644d --- /dev/null +++ b/auth.py @@ -0,0 +1,33 @@ +import json + + +def get_auth(): + with open('config.cfg') as f: + config = json.load(f) + + try: + if config['auth']: + return config['auth'] + else: + raise Exception('No auth found') + except: + print('[!] No auth found, please login first') + print('email') + email = input('> ') + print('password') + password = input('> ') + config['auth'] = {'email': email, 'password': password} + with open('config.cfg', 'w') as f: + json.dump(config, f) + print('[OK] Auth created') + return config['auth'] + + +def del_auth(): + with open('config.cfg') as f: + config = json.load(f) + if config['auth']: + config['auth'] = None + with open('config.cfg', 'w') as f: + json.dump(config, f) + print('[OK] Auth deleted') diff --git a/build.bat b/build.bat new file mode 100644 index 0000000..0350cdf --- /dev/null +++ b/build.bat @@ -0,0 +1 @@ +pyinstaller --noconfirm --icon "icon.ico" --console --onefile horsy.py \ No newline at end of file diff --git a/build.bat.bak b/build.bat.bak new file mode 100644 index 0000000..83175ae --- /dev/null +++ b/build.bat.bak @@ -0,0 +1 @@ +pyinstaller --nocinfirm --icon "icon.ico" --console --onefile horsy.py \ No newline at end of file diff --git a/console.py b/console.py new file mode 100644 index 0000000..d2807c7 --- /dev/null +++ b/console.py @@ -0,0 +1,5 @@ +import os + + +def cls(): + os.system('cls' if os.name == 'nt' else 'clear') diff --git a/horsy.py b/horsy.py new file mode 100644 index 0000000..f7dd239 --- /dev/null +++ b/horsy.py @@ -0,0 +1,75 @@ +import argparse +import tui as tui +from console import cls +from manager import * +from virustotal import add_to_cfg +from uploader import upload + +# Getting the arguments +parser = argparse.ArgumentParser(description='horsy - the best package manager') +parser.add_argument('option', help='options for horsy (install/i | uninstall/un | source/s | update/u | list/l | ' + 'upload)', + choices=['install', 'i', 'uninstall', 'un', 'source', 's', 'update', 'u', 'list', 'l', 'upload'], + nargs='?') +parser.add_argument('app', help='app to install/uninstall/download source', nargs='?') +parser.add_argument('--vt', help='your virustotal api key (account -> api key in VT)', dest='vt_key') + +args = parser.parse_args() +option = args.option +app = args.app + +# Checking if the user has a new VT key +if args.vt_key: + if args.vt_key != 'disable': + add_to_cfg(args.vt_key) + else: + add_to_cfg(None) + + +# Checking directories and files +if not os.path.exists('apps'): + os.makedirs('apps') +if not os.path.exists('sources'): + os.makedirs('sources') +if not os.path.isfile('config.cfg'): + with open('config.cfg', 'w') as f: + f.write('{}') + +# Displaying the logo +os.system('title horsy') +cls() +print(''' + __ __ _______ ______ _______ __ __ +| | | || || _ | | || | | | +| |_| || _ || | || | _____|| |_| | +| || | | || |_||_ | |_____ | | +| || |_| || __ ||_____ ||_ _| +| _ || || | | | _____| | | | +|__| |__||_______||___| |_||_______| |___| +''') +isNoArgs = False + +# Checking if arguments are empty to use in-app CLI +if not args.option: + option = ['install', 'uninstall', 'source', 'update', 'list', 'upload'][tui.menu(['install app', 'uninstall app', + 'get source', 'update app', + 'list of installed apps', + 'upload your app'])] + isNoArgs = True + +if not args.app: + if option not in ['list', 'upload']: + print('\n') + app = tui.get(f'Select app to {option}') + +if option in 'upload': + upload() + +if option in ['install', 'i']: + install(app) + +if option in ['uninstall', 'un']: + uninstall(app) + +if isNoArgs: + input('[EXIT] Press enter to exit horsy...') diff --git a/horsy.spec b/horsy.spec new file mode 100644 index 0000000..c1090af --- /dev/null +++ b/horsy.spec @@ -0,0 +1,40 @@ +# -*- mode: python ; coding: utf-8 -*- + + +block_cipher = None + + +a = Analysis(['horsy.py'], + pathex=[], + binaries=[], + datas=[], + hiddenimports=[], + hookspath=[], + hooksconfig={}, + runtime_hooks=[], + excludes=[], + win_no_prefer_redirects=False, + win_private_assemblies=False, + cipher=block_cipher, + noarchive=False) +pyz = PYZ(a.pure, a.zipped_data, + cipher=block_cipher) + +exe = EXE(pyz, + a.scripts, + a.binaries, + a.zipfiles, + a.datas, + [], + name='horsy', + debug=False, + bootloader_ignore_signals=False, + strip=False, + upx=True, + upx_exclude=[], + runtime_tmpdir=None, + console=True, + disable_windowed_traceback=False, + target_arch=None, + codesign_identity=None, + entitlements_file=None , icon='icon.ico') diff --git a/icon.ico b/icon.ico new file mode 100644 index 0000000000000000000000000000000000000000..59e68440d6da6ac97018772b6a5b6033f97300a8 GIT binary patch literal 21569 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 0: + print(f"[red]Dependency can be malicious. It may run now, if this added to installation " + f"config[/]") + input("Press enter if you want continue, or ctrl+c to exit") + + if r['install']: + print(f"Found install option") + threading.Thread(target=os.system, args=('{2}apps/{0}/{1}'.format(r['name'], r['install'], horsypath),)) \ + .start() + + print(f"Generating launch script") + + with open('{1}apps/{0}.bat'.format(r['name'], horsypath), 'w') as f: + f.write(f"@ECHO off\n") + f.write(f"{horsypath}apps/{r['name']}/{r['run']} %*\n") + + print(f"[green][OK] All done![/]") + print(f"[green]You can run your app by entering [italic white]{r['name']}[/] in terminal[/]") + + except: + print("[red]Unexpected error[/]") + raise + return + + +def uninstall(package, is_gui=False): + horsypath = os.popen('echo %HORSYPATH%').read().replace('\n', '') + '/' + if not is_gui: + if os.path.exists('{1}apps/{0}'.format(package, horsypath)): + os.system('rmdir /s /q "{1}apps/{0}"'.format(package, horsypath)) + print(f"[green][OK] Files deleted[/]") + else: + print(f"[red]App {package} is not installed or doesn't have files[/]") + if os.path.isfile('{1}apps/{0}.bat'.format(package, horsypath)): + os.remove("{1}apps/{0}.bat".format(package, horsypath)) + print(f"[green][OK] Launch script deleted[/]") + else: + print(f"[red]App {package} is not installed or doesn't have launch script[/]") diff --git a/path.py b/path.py new file mode 100644 index 0000000..da0f0a6 --- /dev/null +++ b/path.py @@ -0,0 +1,31 @@ +# Module for PATH actions +import os + + +def add_to_path(program_path: str): + import winreg + + with winreg.ConnectRegistry(None, winreg.HKEY_CURRENT_USER) as root: # Get the current user's registry + with winreg.OpenKey(root, "Environment", 0, winreg.KEY_ALL_ACCESS) as key: # Open the environment key + existing_path_value = os.popen('echo %PATH%').read() # Get the existing path value + print(existing_path_value) + new_path_value = existing_path_value + ";" + program_path # Connect the new path to the existing path + winreg.SetValueEx(key, "PATH", 0, winreg.REG_EXPAND_SZ, new_path_value) # Update the path value + + +def delete_from_path(program_path: str): + import winreg + + with winreg.ConnectRegistry(None, winreg.HKEY_CURRENT_USER) as root: # Get the current user's registry + with winreg.OpenKey(root, "Environment", 0, winreg.KEY_ALL_ACCESS) as key: # Open the environment key + existing_path_value = os.popen('echo %PATH%').read() # Get the existing path value + new_path_value = existing_path_value.replace(program_path + ";", "") # Remove the program path from path + winreg.SetValueEx(key, "PATH", 0, winreg.REG_EXPAND_SZ, new_path_value) # Update the path value + + +def add_var(horsy_path: str): + import winreg + + with winreg.ConnectRegistry(None, winreg.HKEY_CURRENT_USER) as root: # Get the current user's registry + with winreg.OpenKey(root, "Environment", 0, winreg.KEY_ALL_ACCESS) as key: # Open the environment key + winreg.SetValueEx(key, "HORSYPATH", 0, winreg.REG_EXPAND_SZ, horsy_path) # Update the path value diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..8747cca --- /dev/null +++ b/requirements.txt @@ -0,0 +1,6 @@ +rich +requests +Cryptography +Pyinstaller +tqdm +vt-py \ No newline at end of file diff --git a/tui.py b/tui.py new file mode 100644 index 0000000..424b02a --- /dev/null +++ b/tui.py @@ -0,0 +1,19 @@ +def menu(options: list) -> int: + for i in range(len(options)): + print(str(i) + ' - ' + options[i]) + user_input = None + while user_input is None: + try: + user_input = int(input('\n> ')) + if user_input < 0 or user_input >= len(options): + user_input = None + print('Choose number between 0 and ' + str(len(options) - 1)) + except ValueError: + print('Choose number option') + + return user_input + + +def get(description: str) -> str: + print(description) + return input('> ') diff --git a/uploader.py b/uploader.py new file mode 100644 index 0000000..71c21f1 --- /dev/null +++ b/uploader.py @@ -0,0 +1,142 @@ +import json +import time +import requests +from rich import print +from auth import get_auth, del_auth +import re +import vars +import os + + +def matches(s): + return re.match("^[a-z_-]*$", s) is not None + + +def urlmatch(s): + return re.match("^https?://.*.(?:zip|exe)$", s) is not None + + +def upload(): + print('Welcome to the uploader') + print('Before starting, please make sure you have done your project and [blink]uploaded[/] it to any hosting ' + 'service or file sharing service') + input('[OK] Press enter to continue...') + auth = get_auth() + print('Please enter the name of your project. It should contain only lowercase letters, ' + 'underscores and dashes') + project_name = None + while project_name is None: + project_name = input('> ') + if not matches(project_name) or len(project_name) > 64 or len(project_name) < 3: + print('[red]Invalid project name[/red]') + project_name = None + + print('Please paste there project description. It should be a short text under 256 characters') + description = None + while description is None: + description = input('> ') + if len(description) > 256: + print('[red]Description is too long[/red]') + description = None + + print('Please paste there url of executable file. It should be a link to exe or zip file hosted somewhere. ' + 'If app needs dependencies or specific launch options (python, node, etc), you can add them later') + url = None + while url is None: + url = input('> ') + if not urlmatch(url): + print('[red]Invalid file url, also it should end on .exe or .zip[/red]') + url = None + + print('Please paste there url of your project on GitHub or somewhere else. It should be a link to source code ' + 'of your app. It can be archive, repository, site, whatever you want, optional but highly recommended.' + 'If you don\'t want to add it, just press Enter') + source_url = input('> ') + source_url = None if source_url == '' else source_url + + print('If your app needs any dependencies, please paste its link here. It can be exe of installer from official ' + 'site. If you don\'t want to add it, just press Enter') + download = None + while download is None: + download = input('> ') + if download == '': + download = None + break + if not urlmatch(download): + print('[red]Invalid download url[/red]') + download = None + + print('Please add which files should be run during installation. It should be an executable file name.' + 'If you don\'t want to add it, just press Enter') + install = input('> ') + install = None if install == '' else install + + print('Please specify main executable command. It can be executable file name (some-file.exe) or command, that ' + 'launches your script (python some-file.py, etc)') + run = None + while run is None: + run = input('> ') + if run == '': + print('[red]Please, specify runtime[/red]') + run = None + + request = { + 'auth': auth, + 'name': project_name, + 'description': description, + 'url': url, + 'sourceUrl': source_url, + 'download': download, + 'install': install, + 'run': run + } + + # request = { + # "auth": {"email": "meshko_a@dlit.dp.ua", "password": "VeryGoodPassword"}, + # "name": "testapp", + # "description": "Very good description", + # # "url": "https://github.com/Cactus-0/cabanchik/raw/main/dist/cabanchik.exe", + # "sourceUrl": "https://github.com/Cactus-0/cabanchik", + # "download": "https://www.python.org/ftp/python/3.10.2/python-3.10.2-amd64.exe", + # "install": "python-3.10.2-amd64.exe", + # "run": "cabanchik.exe" + # } + + r = None + while r is None: + try: + r = requests.post(vars.protocol + vars.server_url + '/packages/new', json=request).text + r = json.loads(r) + + if r['message'] == 'Unauthorized': + print('[red]Invalid credentials[/red]') + print('Deleting auth from config') + del_auth() + request['auth'] = get_auth() + print(r) + r = None + + elif r['message'] == 'Internal server error': + print('[red]Internal server error, request is broken[/red]') + break + + elif r['message'] == 'Invalid body': + print('[red]Invalid request body, try again[/red]') + break + + elif r['message'] == 'Success': + print('[green]Success, your project is created. You can install it by running[/] ' + '[i]horsy install {0}[/]'.format(request['name'])) + break + + else: + print('[red]Unknown error[/red]') + print('Server response:') + print(r) + break + except: + with open(f'error_{time.time()}.txt', 'w') as f: + f.write(str(r)) + print(f'[red]Something went wrong with unsupported error. You can check servers response in ' + f'{os.getcwd()}/{f.name}[/red]') + break diff --git a/vars.py b/vars.py new file mode 100644 index 0000000..8f3ba9b --- /dev/null +++ b/vars.py @@ -0,0 +1,2 @@ +protocol = "http://" +server_url = 'localhost:60666' diff --git a/virustotal.py b/virustotal.py new file mode 100644 index 0000000..ede231b --- /dev/null +++ b/virustotal.py @@ -0,0 +1,53 @@ +import json +import requests +import os +import hashlib + + +def add_to_cfg(key): + with open('config.cfg') as f: + config = json.load(f) + + config['vt-key'] = key + + with open('config.cfg', 'w') as f: + json.dump(config, f) + + +def get_key(): + with open('config.cfg') as f: + config = json.load(f) + + try: + return config['vt-key'] + except KeyError: + return None + + +def scan_file(filename): + api_url = 'https://www.virustotal.com/api/v3/files' + headers = {'x-apikey': get_key()} + with open(filename, 'rb') as file: + files = {'file': (filename, file)} + if os.path.getsize(filename) < 33554432: + response = requests.post(api_url, headers=headers, files=files) + return response.json()['data']['id'] + else: + api_url = 'https://www.virustotal.com/api/v3/files/upload_url' + response = requests.get(api_url, headers=headers) + response = requests.post(response.json()['data'], headers=headers, files=files) + return response.json()['data']['id'] + + +def get_report(filename): + hash_md5 = hashlib.md5() + with open(filename, "rb") as f: + for chunk in iter(lambda: f.read(4096), b""): + hash_md5.update(chunk) + api_url = 'https://www.virustotal.com/api/v3/files/' + hash_md5.hexdigest() + headers = {'x-apikey': get_key()} + response = requests.get(api_url, headers=headers) + analysis = dict() + analysis['detect'] = response.json()['data']['attributes']['last_analysis_stats'] + analysis['link'] = 'https://www.virustotal.com/gui/file/' + response.json()['data']['id'] + return analysis