├── .github └── keep ├── .gitignore ├── LICENSE ├── README.md ├── api ├── __init__.py ├── asgi.py ├── settings.py ├── urls.py └── wsgi.py ├── cli ├── __init__.py ├── admin.py ├── apps.py ├── migrations │ └── __init__.py ├── models.py ├── tests.py └── views.py ├── doc └── cli.md ├── execution ├── .gitignore └── README.md ├── helper.py ├── manage.py └── requirements.txt /.github/keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sherlock-project/api/27f207cee8cc87b3ba68de9fcde13fff7d5091ef/.github/keep -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | MANIFEST 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | .pytest_cache/ 49 | 50 | # Translations 51 | *.mo 52 | *.pot 53 | 54 | # Django stuff: 55 | *.log 56 | local_settings.py 57 | db.sqlite3 58 | 59 | # Flask stuff: 60 | instance/ 61 | .webassets-cache 62 | 63 | # Scrapy stuff: 64 | .scrapy 65 | 66 | # Sphinx documentation 67 | docs/_build/ 68 | 69 | # PyBuilder 70 | target/ 71 | 72 | # Jupyter Notebook 73 | .ipynb_checkpoints 74 | 75 | # pyenv 76 | .python-version 77 | 78 | # celery beat schedule file 79 | celerybeat-schedule 80 | 81 | # SageMath parsed files 82 | *.sage.py 83 | 84 | # Environments 85 | .env 86 | .venv 87 | env/ 88 | venv/ 89 | ENV/ 90 | env.bak/ 91 | venv.bak/ 92 | 93 | # Spyder project settings 94 | .spyderproject 95 | .spyproject 96 | 97 | # Rope project settings 98 | .ropeproject 99 | 100 | # mkdocs documentation 101 | /site 102 | 103 | # mypy 104 | .mypy_cache/ 105 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 sherlock-project 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Sherlock API 4 | 5 | [![Python Ver.](https://img.shields.io/badge/python-%3E=_3.6-green.svg)](https://www.python.org/downloads/) 6 | [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) 7 | 8 | Api for sherlock module. 9 | 10 | ## Prerequisites 11 | 12 | This REST API is built on top of [Django REST Framework](https://www.django-rest-framework.org/) 13 | (known as DRF). You would need at least the fundamental knowledge about DRF in 14 | order to do development to this project. 15 | 16 | ### Installation 17 | 18 | ```sh 19 | # clone the repo 20 | $ git clone https://github.com/sherlock-project/api.git 21 | 22 | # change the working directory to api 23 | $ cd api 24 | 25 | # install the requirements 26 | $ python3 -m pip install -r requirements.txt 27 | ``` 28 | 29 | *P.S. Please use `python` instead of `python3` if you are on Windows system* 30 | 31 | ### Run API Server 32 | 33 | ```sh 34 | # propagate modules 35 | $ python3 manage.py migrate 36 | 37 | # in the root of project diretory.. 38 | $ python3 manage.py runserver 0.0.0.0:8000 39 | ``` 40 | 41 | Start you browser and type [127.0.0.1:8000](http://127.0.0.1:8000/) in as 42 | your target URL and hit return. 43 | 44 | Now you should able to test your Sherlock API through DRF browser interface! 45 | 46 | ## License 47 | 48 | MIT © Sherlock Project 49 | 50 | Original Creator - [Shen, Jen-Chieh](https://github.com/jcs090218) 51 | -------------------------------------------------------------------------------- /api/__init__.py: -------------------------------------------------------------------------------- 1 | """Sherlock API Module 2 | 3 | This module is the core Django REST Framework (DRF)'s functionality. 4 | """ 5 | -------------------------------------------------------------------------------- /api/asgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | ASGI config for api project. 3 | 4 | It exposes the ASGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/3.1/howto/deployment/asgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.asgi import get_asgi_application 13 | 14 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'api.settings') 15 | 16 | application = get_asgi_application() 17 | -------------------------------------------------------------------------------- /api/settings.py: -------------------------------------------------------------------------------- 1 | """ 2 | Django settings for api project. 3 | 4 | Generated by 'django-admin startproject' using Django 3.1.4. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/3.1/topics/settings/ 8 | 9 | For the full list of settings and their values, see 10 | https://docs.djangoproject.com/en/3.1/ref/settings/ 11 | """ 12 | 13 | from pathlib import Path 14 | 15 | # Build paths inside the project like this: BASE_DIR / 'subdir'. 16 | BASE_DIR = Path(__file__).resolve().parent.parent 17 | 18 | 19 | # Quick-start development settings - unsuitable for production 20 | # See https://docs.djangoproject.com/en/3.1/howto/deployment/checklist/ 21 | 22 | # SECURITY WARNING: keep the secret key used in production secret! 23 | SECRET_KEY = '%a#x#-ara^lpz(ljb7_%i!bahh7yyfjtbud*0%%sc0v($8hveh' 24 | 25 | # SECURITY WARNING: don't run with debug turned on in production! 26 | DEBUG = True 27 | 28 | ALLOWED_HOSTS = [] 29 | 30 | 31 | # Application definition 32 | 33 | INSTALLED_APPS = [ 34 | 'django.contrib.admin', 35 | 'django.contrib.auth', 36 | 'django.contrib.contenttypes', 37 | 'django.contrib.sessions', 38 | 'django.contrib.messages', 39 | 'django.contrib.staticfiles', 40 | 41 | 'rest_framework', 42 | 'corsheaders', 43 | ] 44 | 45 | MIDDLEWARE = [ 46 | 'corsheaders.middleware.CorsMiddleware', 47 | 'django.middleware.security.SecurityMiddleware', 48 | 'django.contrib.sessions.middleware.SessionMiddleware', 49 | 'django.middleware.common.CommonMiddleware', 50 | 'django.middleware.csrf.CsrfViewMiddleware', 51 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 52 | 'django.contrib.messages.middleware.MessageMiddleware', 53 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', 54 | ] 55 | 56 | ROOT_URLCONF = 'api.urls' 57 | 58 | TEMPLATES = [ 59 | { 60 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', 61 | 'DIRS': [], 62 | 'APP_DIRS': True, 63 | 'OPTIONS': { 64 | 'context_processors': [ 65 | 'django.template.context_processors.debug', 66 | 'django.template.context_processors.request', 67 | 'django.contrib.auth.context_processors.auth', 68 | 'django.contrib.messages.context_processors.messages', 69 | ], 70 | }, 71 | }, 72 | ] 73 | 74 | WSGI_APPLICATION = 'api.wsgi.application' 75 | 76 | # Cross-Origin 77 | 78 | CORS_ORIGIN_ALLOW_ALL = True 79 | 80 | # Database 81 | # https://docs.djangoproject.com/en/3.1/ref/settings/#databases 82 | 83 | DATABASES = { 84 | 'default': { 85 | 'ENGINE': 'django.db.backends.sqlite3', 86 | 'NAME': ':memory:', 87 | } 88 | } 89 | 90 | 91 | # Password validation 92 | # https://docs.djangoproject.com/en/3.1/ref/settings/#auth-password-validators 93 | 94 | AUTH_PASSWORD_VALIDATORS = [ 95 | { 96 | 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', 97 | }, 98 | { 99 | 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', 100 | }, 101 | { 102 | 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', 103 | }, 104 | { 105 | 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', 106 | }, 107 | ] 108 | 109 | 110 | # Internationalization 111 | # https://docs.djangoproject.com/en/3.1/topics/i18n/ 112 | 113 | LANGUAGE_CODE = 'en-us' 114 | 115 | TIME_ZONE = 'UTC' 116 | 117 | USE_I18N = True 118 | 119 | USE_L10N = True 120 | 121 | USE_TZ = True 122 | 123 | 124 | # Static files (CSS, JavaScript, Images) 125 | # https://docs.djangoproject.com/en/3.1/howto/static-files/ 126 | 127 | STATIC_URL = '/static/' 128 | 129 | REST_FRAMEWORK = { 130 | 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination', 131 | 'PAGE_SIZE': 10, 132 | } 133 | -------------------------------------------------------------------------------- /api/urls.py: -------------------------------------------------------------------------------- 1 | """api URL Configuration 2 | 3 | The `urlpatterns` list routes URLs to views. For more information please see: 4 | https://docs.djangoproject.com/en/3.1/topics/http/urls/ 5 | Examples: 6 | Function views 7 | 1. Add an import: from my_app import views 8 | 2. Add a URL to urlpatterns: path('', views.home, name='home') 9 | Class-based views 10 | 1. Add an import: from other_app.views import Home 11 | 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') 12 | Including another URLconf 13 | 1. Import the include() function: from django.urls import include, path 14 | 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) 15 | """ 16 | from django.contrib import admin 17 | from django.urls import include, path 18 | 19 | from rest_framework import routers 20 | 21 | from cli import views 22 | 23 | router = routers.DefaultRouter() 24 | 25 | urlpatterns = [ 26 | path('', include(router.urls)), 27 | path('cli/', views.CliView.as_view()), 28 | path('data/', views.DataView.as_view()), 29 | ] 30 | -------------------------------------------------------------------------------- /api/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for api project. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/3.1/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.wsgi import get_wsgi_application 13 | 14 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'api.settings') 15 | 16 | application = get_wsgi_application() 17 | -------------------------------------------------------------------------------- /cli/__init__.py: -------------------------------------------------------------------------------- 1 | """Commnad Line Interface (CLI) Module 2 | 3 | This module contains the direct control of Sherlock module 4 | through the CLI. 5 | """ 6 | -------------------------------------------------------------------------------- /cli/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | # Register your models here. 4 | -------------------------------------------------------------------------------- /cli/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class CliConfig(AppConfig): 5 | name = 'cli' 6 | -------------------------------------------------------------------------------- /cli/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sherlock-project/api/27f207cee8cc87b3ba68de9fcde13fff7d5091ef/cli/migrations/__init__.py -------------------------------------------------------------------------------- /cli/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | # Create your models here. 4 | -------------------------------------------------------------------------------- /cli/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /cli/views.py: -------------------------------------------------------------------------------- 1 | import os 2 | from subprocess import * 3 | 4 | from rest_framework.response import Response 5 | from rest_framework.views import APIView 6 | 7 | from helper import * 8 | import json 9 | from django.http import HttpResponse, JsonResponse 10 | 11 | class CliView(APIView): 12 | """Pass in command directly to sherlock.""" 13 | def post(self, request): 14 | data = json.loads(request.body) 15 | args = data['args'] 16 | 17 | if valid_args(args) == False: 18 | output = "Invalid argument string" 19 | else: 20 | full_cmd = f"{py_command()} {sherlock_dir()}/sherlock {args}" 21 | proc = Popen(full_cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE, shell=True) 22 | outs, errs = proc.communicate() 23 | output = outs if outs else errs 24 | 25 | return Response({'output': output}) 26 | 27 | class DataView(APIView): 28 | """Request JSON data from sherlock resources.""" 29 | def get(self, request): 30 | return Response(sherlock_data()) 31 | -------------------------------------------------------------------------------- /doc/cli.md: -------------------------------------------------------------------------------- 1 | # Request CLI 2 | 3 | This is the most generic REST API form the server. This request 4 | accept any argument from project [sherlock](https://github.com/sherlock-project/sherlock). 5 | 6 | **TYPE:** POST 7 | **URL:** `http://127.0.0.1:8000/cli/` 8 | 9 | #### JSON format 10 | 11 | ``` 12 | { 13 | "args": "[sherlock arguments]" 14 | } 15 | ``` 16 | 17 | #### Example 18 | 19 | Return the version information from current server's sherlock project. 20 | 21 | ``` 22 | { 23 | "args": "--version" 24 | } 25 | ``` 26 | -------------------------------------------------------------------------------- /execution/.gitignore: -------------------------------------------------------------------------------- 1 | # ignore cloned sherlock 2 | sherlock/ 3 | -------------------------------------------------------------------------------- /execution/README.md: -------------------------------------------------------------------------------- 1 | Here is where you clone sherlock from GitHub and being ready to execute 2 | for REST API request. 3 | -------------------------------------------------------------------------------- /helper.py: -------------------------------------------------------------------------------- 1 | """Helper and Utility module. 2 | """ 3 | import os 4 | import json 5 | 6 | def py_command(): 7 | """Return valid Python executable command base on current OS.""" 8 | return "python" if os.name == 'nt' else "python3" 9 | 10 | def cmd_in_dir(newdir, cmd): 11 | """Preserve current working directory and execute command 12 | in new directory. 13 | 14 | Keyword Arguments: 15 | newdir -- Target directory to navigate to. 16 | cmd -- Command you would like to execute. 17 | """ 18 | prevdir = os.getcwd() 19 | os.chdir(os.path.expanduser(newdir)) 20 | try: 21 | print(os.getcwd()) 22 | os.system(cmd) 23 | finally: 24 | os.chdir(prevdir) 25 | 26 | # Security 27 | 28 | def valid_args(cmd): 29 | """Return true, if command is safe to execute.""" 30 | escape = [";", "&", "|", "<", ">" "\"" "\'"] 31 | for ch in escape: 32 | if ch in cmd: 33 | return False 34 | return True 35 | 36 | 37 | # Sherlock Related 38 | 39 | def sherlock_dir(): 40 | """Return sherlock project root directory.""" 41 | return "./execution/sherlock" 42 | 43 | def sherlock_data(): 44 | """Return `data.json` file from sherlock project.""" 45 | data_path = f"{sherlock_dir()}/sherlock/resources/data.json" 46 | with open(data_path, "r", encoding="utf-8") as data_file: 47 | data = json.load(data_file) 48 | return data 49 | -------------------------------------------------------------------------------- /manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """Django's command-line utility for administrative tasks.""" 3 | import os 4 | from os import path 5 | import sys 6 | 7 | from helper import * 8 | 9 | def init_sherlock(): 10 | """Get the newest Sherlock project.""" 11 | print("[INFO] Prepare for Sherlock Project..") 12 | if path.exists(sherlock_dir()): 13 | cmd_in_dir(sherlock_dir(), f"git pull") # Update to the newest version 14 | else: # Othersie, clone it once 15 | os.system(f"git clone https://github.com/sherlock-project/sherlock {sherlock_dir()}"); 16 | os.system(f"{py_command()} -m pip install -r {sherlock_dir()}/requirements.txt") 17 | os.system(f"{py_command()} {sherlock_dir()}/sherlock --version") 18 | 19 | def main(): 20 | """Run administrative tasks.""" 21 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'api.settings') 22 | try: 23 | from django.core.management import execute_from_command_line 24 | except ImportError as exc: 25 | raise ImportError( 26 | "Couldn't import Django. Are you sure it's installed and " 27 | "available on your PYTHONPATH environment variable? Did you " 28 | "forget to activate a virtual environment?" 29 | ) from exc 30 | execute_from_command_line(sys.argv) 31 | 32 | 33 | if __name__ == '__main__': 34 | init_sherlock() 35 | main() 36 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | djangorestframework>=3.12.2 2 | django-cors-headers>=3.6.0 3 | --------------------------------------------------------------------------------