├── .gitignore ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── api ├── .dockerignore ├── Dockerfile ├── docbleach.py ├── docbleach │ ├── __init__.py │ ├── config │ │ └── __init__.py │ ├── controllers │ │ ├── __init__.py │ │ ├── base.py │ │ ├── ping.py │ │ ├── static.py │ │ ├── task.py │ │ └── upload.py │ ├── tasks │ │ └── __init__.py │ └── utils │ │ └── __init__.py ├── requirements.txt └── static │ ├── css │ ├── bootstrap-3.3.7.css │ ├── docbleach.css │ └── sweetalert2.min.css │ ├── favicon │ ├── android-chrome-192x192.png │ ├── android-chrome-512x512.png │ ├── apple-touch-icon.png │ ├── browserconfig.xml │ ├── favicon-16x16.png │ ├── favicon-32x32.png │ ├── favicon.ico │ ├── manifest.json │ ├── mstile-144x144.png │ ├── mstile-150x150.png │ ├── mstile-310x150.png │ ├── mstile-310x310.png │ ├── mstile-70x70.png │ └── safari-pinned-tab.svg │ ├── index.html │ ├── js │ ├── docbleach.js │ ├── html5shiv.js │ ├── jquery.min.js │ ├── respond.min.js │ └── sweetalert2.min.js │ ├── swagger.yaml │ └── swagger │ ├── docbleach-conf.js │ ├── index.html │ ├── swagger-ui-bundle.js │ ├── swagger-ui-bundle.js.map │ ├── swagger-ui-standalone-preset.js │ ├── swagger-ui-standalone-preset.js.map │ ├── swagger-ui.css │ ├── swagger-ui.css.map │ ├── swagger-ui.js │ └── swagger-ui.js.map ├── autoscale ├── .dockerignore ├── Dockerfile ├── autoscale2.py ├── entrypoint.sh └── requirements.txt ├── docker-compose.yml ├── docs ├── Chooseafile.png ├── clickhere.png └── downloadyourfile.png └── worker ├── .dockerignore ├── Dockerfile ├── docbleach ├── __init__.py ├── celeryconfig.py └── tasks │ └── __init__.py ├── entrypoint.sh └── requirements.txt /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.gitignore.io/api/pycharm,python,virtualenv,intellij+iml 3 | 4 | ### Intellij+iml ### 5 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm 6 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 7 | 8 | # User-specific stuff: 9 | .idea/**/workspace.xml 10 | .idea/**/tasks.xml 11 | .idea/dictionaries 12 | 13 | # Sensitive or high-churn files: 14 | .idea/**/dataSources/ 15 | .idea/**/dataSources.ids 16 | .idea/**/dataSources.xml 17 | .idea/**/dataSources.local.xml 18 | .idea/**/sqlDataSources.xml 19 | .idea/**/dynamic.xml 20 | .idea/**/uiDesigner.xml 21 | 22 | # Gradle: 23 | .idea/**/gradle.xml 24 | .idea/**/libraries 25 | 26 | # Mongo Explorer plugin: 27 | .idea/**/mongoSettings.xml 28 | 29 | ## File-based project format: 30 | *.iws 31 | 32 | ## Plugin-specific files: 33 | 34 | # IntelliJ 35 | /out/ 36 | 37 | # mpeltonen/sbt-idea plugin 38 | .idea_modules/ 39 | 40 | # JIRA plugin 41 | atlassian-ide-plugin.xml 42 | 43 | # Cursive Clojure plugin 44 | .idea/replstate.xml 45 | 46 | # Crashlytics plugin (for Android Studio and IntelliJ) 47 | com_crashlytics_export_strings.xml 48 | crashlytics.properties 49 | crashlytics-build.properties 50 | fabric.properties 51 | 52 | ### Intellij+iml Patch ### 53 | # Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023 54 | 55 | *.iml 56 | modules.xml 57 | .idea/misc.xml 58 | *.ipr 59 | 60 | ### PyCharm ### 61 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm 62 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 63 | 64 | # User-specific stuff: 65 | 66 | # Sensitive or high-churn files: 67 | 68 | # Gradle: 69 | 70 | # Mongo Explorer plugin: 71 | 72 | ## File-based project format: 73 | 74 | ## Plugin-specific files: 75 | 76 | # IntelliJ 77 | 78 | # mpeltonen/sbt-idea plugin 79 | 80 | # JIRA plugin 81 | 82 | # Cursive Clojure plugin 83 | 84 | # Crashlytics plugin (for Android Studio and IntelliJ) 85 | 86 | ### PyCharm Patch ### 87 | # Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 88 | 89 | # *.iml 90 | # modules.xml 91 | # .idea/misc.xml 92 | # *.ipr 93 | 94 | # Sonarlint plugin 95 | .idea/sonarlint 96 | 97 | ### Python ### 98 | # Byte-compiled / optimized / DLL files 99 | __pycache__/ 100 | *.py[cod] 101 | *$py.class 102 | 103 | # C extensions 104 | *.so 105 | 106 | # Distribution / packaging 107 | .Python 108 | env/ 109 | build/ 110 | develop-eggs/ 111 | dist/ 112 | downloads/ 113 | eggs/ 114 | .eggs/ 115 | lib/ 116 | lib64/ 117 | parts/ 118 | sdist/ 119 | var/ 120 | wheels/ 121 | *.egg-info/ 122 | .installed.cfg 123 | *.egg 124 | 125 | # PyInstaller 126 | # Usually these files are written by a python script from a template 127 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 128 | *.manifest 129 | *.spec 130 | 131 | # Installer logs 132 | pip-log.txt 133 | pip-delete-this-directory.txt 134 | 135 | # Unit test / coverage reports 136 | htmlcov/ 137 | .tox/ 138 | .coverage 139 | .coverage.* 140 | .cache 141 | nosetests.xml 142 | coverage.xml 143 | *,cover 144 | .hypothesis/ 145 | 146 | # Translations 147 | *.mo 148 | *.pot 149 | 150 | # Django stuff: 151 | *.log 152 | local_settings.py 153 | 154 | # Flask stuff: 155 | instance/ 156 | .webassets-cache 157 | 158 | # Scrapy stuff: 159 | .scrapy 160 | 161 | # Sphinx documentation 162 | docs/_build/ 163 | 164 | # PyBuilder 165 | target/ 166 | 167 | # Jupyter Notebook 168 | .ipynb_checkpoints 169 | 170 | # pyenv 171 | .python-version 172 | 173 | # celery beat schedule file 174 | celerybeat-schedule 175 | 176 | # SageMath parsed files 177 | *.sage.py 178 | 179 | # dotenv 180 | .env 181 | 182 | # virtualenv 183 | .venv 184 | venv/ 185 | ENV/ 186 | 187 | # Spyder project settings 188 | .spyderproject 189 | 190 | # Rope project settings 191 | .ropeproject 192 | 193 | ### VirtualEnv ### 194 | # Virtualenv 195 | # http://iamzed.com/2009/05/07/a-primer-on-virtualenv/ 196 | [Bb]in 197 | [Ii]nclude 198 | [Ll]ib 199 | [Ll]ib64 200 | [Ll]ocal 201 | [Ss]cripts 202 | pyvenv.cfg 203 | pip-selfcheck.json 204 | 205 | # End of https://www.gitignore.io/api/pycharm,python,virtualenv,intellij+iml -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | :tada: :+1: *Thanks for reading this \o/ You're already helping us!* :tada: 4 | 5 | 6 | DocBleach is a young project, but contributing means a lot of different things. 7 | Providing feedback on a feature, reporting bugs or even fixing typos in the code are contributions. 8 | Knowing how to code is great, but not a requirement. 9 | 10 | When contributing to this repository, please first discuss the change you wish to make via issue, 11 | email, or any other method with the owners of this repository before making a change. 12 | 13 | Please note we have a code of conduct, please follow it in all your interactions with the project. 14 | 15 | 16 | ## Pull Request Process 17 | 18 | 1. Relax, take your time and be fearless. The PR Process is there to ensure the project moves in one 19 | direction, not to challenge you ;-) 20 | 2. Ensure your code works. 21 | 3. Match the existing code style (PEP 8 for Python files). 22 | 4. Send the PR on GitHub \o/ 23 | 24 | 25 | 26 | ## Code of Conduct 27 | 28 | ### Our Pledge 29 | 30 | In the interest of fostering an open and welcoming environment, we as 31 | contributors and maintainers pledge to making participation in our project and 32 | our community a harassment-free experience for everyone, regardless of age, body 33 | size, disability, ethnicity, gender identity and expression, level of experience, 34 | nationality, personal appearance, race, religion, or sexual identity and 35 | orientation. 36 | 37 | ### Our Standards 38 | 39 | Examples of behavior that contributes to creating a positive environment 40 | include: 41 | 42 | * Using welcoming and inclusive language 43 | * Being respectful of differing viewpoints and experiences 44 | * Gracefully accepting constructive criticism 45 | * Focusing on what is best for the community 46 | * Showing empathy towards other community members 47 | 48 | Examples of unacceptable behavior by participants include: 49 | 50 | * The use of sexualized language or imagery and unwelcome sexual attention or 51 | advances 52 | * Trolling, insulting/derogatory comments, and personal or political attacks 53 | * Public or private harassment 54 | * Publishing others' private information, such as a physical or electronic 55 | address, without explicit permission 56 | * Other conduct which could reasonably be considered inappropriate in a 57 | professional setting 58 | 59 | ### Our Responsibilities 60 | 61 | Project maintainers are responsible for clarifying the standards of acceptable 62 | behavior and are expected to take appropriate and fair corrective action in 63 | response to any instances of unacceptable behavior. 64 | 65 | Project maintainers have the right and responsibility to remove, edit, or 66 | reject comments, commits, code, wiki edits, issues, and other contributions 67 | that are not aligned to this Code of Conduct, or to ban temporarily or 68 | permanently any contributor for other behaviors that they deem inappropriate, 69 | threatening, offensive, or harmful. 70 | 71 | ### Scope 72 | 73 | This Code of Conduct applies both within project spaces and in public spaces 74 | when an individual is representing the project or its community. Examples of 75 | representing a project or community include using an official project e-mail 76 | address, posting via an official social media account, or acting as an appointed 77 | representative at an online or offline event. Representation of a project may be 78 | further defined and clarified by project maintainers. 79 | 80 | ### Enforcement 81 | 82 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 83 | reported by contacting the project team at [INSERT EMAIL ADDRESS]. All 84 | complaints will be reviewed and investigated and will result in a response that 85 | is deemed necessary and appropriate to the circumstances. The project team is 86 | obligated to maintain confidentiality with regard to the reporter of an incident. 87 | Further details of specific enforcement policies may be posted separately. 88 | 89 | Project maintainers who do not follow or enforce the Code of Conduct in good 90 | faith may face temporary or permanent repercussions as determined by other 91 | members of the project's leadership. 92 | 93 | ### Attribution 94 | 95 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 96 | available at [http://contributor-covenant.org/version/1/4][version]. 97 | 98 | [homepage]: http://contributor-covenant.org 99 | [version]: http://contributor-covenant.org/version/1/4/ -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Maxime Guerreiro 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. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DocBleach aaS - Sanitize your documents in the cloud™ 2 | 3 | [DocBleach][github] allows you to sanitize your Word, Excel, PowerPoint, PDF, ... documents. This repository contains the 4 | DocBleach Web API, packaged as a docker service. Two clicks and you'll feel safer. 5 | 6 | [![Codacy Badge](https://api.codacy.com/project/badge/Grade/d44e774ed4df461a97e86e96f6b802c9)](https://www.codacy.com/app/DocBleach/DocBleach-Web?utm_source=github.com&utm_medium=referral&utm_content=docbleach/DocBleach-Web&utm_campaign=Badge_Grade) 7 | 8 | You have files to sanitize but you can't install Java, may it be because you run on an embedded system or you deploy 9 | your PHP app on a shared hosting and Java is not available ... This package is the solution to your issues! 10 | 11 | # Installation 12 | 13 | Three modules are present in this repository: 14 | - *api* 15 | contains the Web API, a lightweight Python Flask app that 16 | receives the files and responds to status requests. 17 | - *worker* contains DocBleach and launches it using Python Celery. 18 | - *autoscale* auto-scaling for Mesos Marathon, documentation still has to be written. 19 | 20 | Thanks to Docker, you are able to easily deploy this app. 21 | 22 | Celery requires a message broker and a backend to store its results. 23 | Using something as easy to setup as Redis is fine, as it is able to serve 24 | both roles, but you may use [another broker][1] or another [backend storage][backend_celery]. 25 | 26 | Configuration is given to each component thru environment variables, 27 | namely `CELERY_BROKER` and `CELERY_RESULT_BACKEND`. They accept URI as 28 | valid values, for instance to use a local Redis server protected by the 29 | password `PASS123` you would use this: 30 | `CELERY_BROKER=redis://:PASS123@127.0.0.1/`. 31 | 32 | In order to pass the files from the API to the Worker, a storage backend is required. 33 | Plik is used, because it is easy to setup (unlike OpenStack/AWS), Open-Source and supports `expire` out of the box. 34 | 35 | By default using Docker Compose, an internal Plik instance is started and the sanitized files are stored on plik.root.gg 36 | 37 | You may change this using the `INTERNAL_PLIK_SERVER` and `FINAL_PLIK_SERVER` env variables. 38 | 39 | # Howto's 40 | To run the containers using Docker Compose, just call `docker-compose up -d` in this project's directory. 41 | Docker will take care of the boring stuff for you! :) 42 | ```bash 43 | $ docbleach-rest$ docker-compose up -d 44 | Starting docbleachrest_worker_1 45 | Starting docbleachrest_plik_1 46 | Starting docbleachrest_web_1 47 | Starting docbleachrest_redis_1 48 | $ docker ps 49 | CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 50 | 5d258cfac1d6 docbleach/api:latest "/usr/src/app/entrypo" About a minute ago Up 41 seconds 0.0.0.0:9000->5000/tcp docbleachrest_web_1 51 | 5c3d91acba01 docbleach/worker:latest "/usr/src/app/entrypo" About a minute ago Up 41 seconds docbleachrest_worker_1 52 | ebcef5f636d5 rootgg/plik "/bin/sh -c ./plikd" 9 days ago Up 41 seconds 8080/tcp docbleachrest_plik_1 53 | eb69034e73f5 redis "docker-entrypoint.sh" 10 days ago Up 41 seconds 6379/tcp docbleachrest_redis_1 54 | ``` 55 | 56 | As you can see, DocBleach-api is running on your port `5000`. 57 | Just open `127.0.0.1:5000` in your browser to try it out :wink:. 58 | 59 | 60 | ## Get the sources 61 | 62 | ```bash 63 | git clone https://github.com/docbleach/DocBleach-Web.git 64 | cd DocBleach-Web 65 | ``` 66 | 67 | A `Dockerfile` makes it easy to hack on a part of this project. 68 | 69 | You've developed a new cool feature ? Fixed an annoying bug ? We'd be happy 70 | to hear from you ! 71 | 72 | 73 | Documentation uses [Swagger][swagger], and thus is rendered client-side using the specifications in 74 | `api/static/swagger.yaml`. 75 | 76 | # How to Sanitize 77 | 78 | to start sanitizing your file, click on the "pick a file" button 79 | ![click here](docs/clickhere.png) 80 | then choose the file you want to sanitize 81 | ![click here](docs/Chooseafile.png) 82 | Finaly, click "Download the sanitized file" to get your file back 83 | ![click here](docs/downloadyourfile.png) 84 | 85 | # Related links 86 | 87 | * Contribute: https://github.com/docbleach/DocBleach-Web 88 | * Report bugs: https://github.com/docbleach/DocBleach-Web/issues 89 | * Get latest version: https://hub.docker.com/u/docbleach/ 90 | 91 | # License 92 | 93 | See https://github.com/docbleach/DocBleach-Web/blob/master/LICENSE 94 | 95 | # Project status 96 | 97 | This project works, but would be greatly improved with little tweaks. 98 | 99 | For instance, it would be really great to: 100 | - Write more documentation, because there's never enough of it. :-( 101 | - Have an improved design. 102 | - Improve the API once the [code base is rewamped](issue_codebase), to give an extended output, allow configuration... 103 | - Remove dependencies, having Java and Python code in a docker file is a bad practice. For now, it works. 104 | 105 | 106 | 107 | [1]: http://docs.celeryproject.org/en/latest/getting-started/brokers/ 108 | [backend_celery]: http://docs.celeryproject.org/en/latest/getting-started/first-steps-with-celery.html#keeping-results 109 | [swagger]: https://swagger.io/ 110 | [github]: https://github.com/docbleach/DocBleach 111 | [logo_link]: https://github.com/docbleach/DocBleach/404 112 | [issue_codebase]: https://github.com/docbleach/DocBleach/issues/2 113 | -------------------------------------------------------------------------------- /api/.dockerignore: -------------------------------------------------------------------------------- 1 | .dockerignore 2 | *.iml 3 | .idea 4 | .venv 5 | .git 6 | .gitignore 7 | Dockerfile -------------------------------------------------------------------------------- /api/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.7-alpine 2 | 3 | EXPOSE 5000 4 | HEALTHCHECK CMD curl --fail http://localhost:5000/ping 5 | 6 | ENV INTERNAL_PLIK_SERVER https://plik.root.gg 7 | 8 | # We add the Plik binary file 9 | ADD https://plik.root.gg/clients/linux-amd64/plik /usr/bin/plik 10 | 11 | RUN apk add -t build --no-cache --update-cache \ 12 | # Having up to date SSL certificates is always a good thing. :) 13 | ca-certificates \ 14 | openssl \ 15 | # For the healthcheck 16 | curl \ 17 | && \ 18 | update-ca-certificates && \ 19 | 20 | # Add glibc, wanted by plik (compiled with Go) 21 | wget -q -O /etc/apk/keys/sgerrand.rsa.pub https://alpine-pkgs.sgerrand.com/sgerrand.rsa.pub && \ 22 | wget https://github.com/sgerrand/alpine-pkg-glibc/releases/download/2.23-r3/glibc-2.23-r3.apk && \ 23 | apk add glibc-2.23-r3.apk && \ 24 | 25 | # Remove openssl, only needed for wget 26 | apk del build wget unzip && \ 27 | 28 | # We add an user 29 | adduser -S -u 1000 worker && \ 30 | 31 | # Add default config for Plik, required 32 | chmod o+x /usr/bin/plik && \ 33 | echo 'URL = "https://plik.root.gg"' > /home/worker/.plikrc 34 | 35 | 36 | # Install the python dependencies 37 | ADD requirements.txt /app/ 38 | WORKDIR /app/ 39 | 40 | RUN pip3 install --no-cache-dir -r requirements.txt 41 | 42 | COPY . /app/ 43 | RUN chown -R worker /app/; chmod -R 770 /app/ 44 | 45 | USER worker 46 | ENTRYPOINT ["python3", "/app/docbleach.py"] 47 | -------------------------------------------------------------------------------- /api/docbleach.py: -------------------------------------------------------------------------------- 1 | import tornado.ioloop 2 | 3 | from docbleach import application 4 | 5 | if __name__ == "__main__": 6 | print("Listening on port 5000") 7 | application.listen(5000) 8 | tornado.ioloop.IOLoop.instance().start() 9 | -------------------------------------------------------------------------------- /api/docbleach/__init__.py: -------------------------------------------------------------------------------- 1 | import tornado.web 2 | from tornado.log import enable_pretty_logging 3 | 4 | from .config import debug_mode 5 | from .controllers import * 6 | from .utils import static 7 | 8 | application = tornado.web.Application([ 9 | (r'/static/(.*)', StaticFileHandler, {'path': static()}), 10 | (r'/doc/()', StaticFileHandler, {'path': static('swagger', 'index.html')}), 11 | (r'/doc/(.*)', StaticFileHandler, {'path': static('swagger')}), 12 | (r'/()', StaticFileHandler, {'path': static('index.html')}), 13 | 14 | (r'/ping', PingHandler), 15 | (r'/v1/ping', PingHandler), 16 | (r'/v1/tasks', UploadHandler), 17 | (r'/v1/tasks/(.*)', TaskHandler), 18 | ], debug=debug_mode) 19 | 20 | enable_pretty_logging() 21 | -------------------------------------------------------------------------------- /api/docbleach/config/__init__.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | __UPLOADS__ = 'uploads/' 4 | 5 | internal_plik_server = os.getenv('INTERNAL_PLIK_SERVER', 'https://plik.root.gg') 6 | 7 | celery_broker = os.getenv('CELERY_BROKER') 8 | 9 | celery_result_backend = os.getenv('CELERY_RESULT_BACKEND') 10 | 11 | debug_mode = os.getenv('DEBUG', 'true').lower() == 'true' 12 | 13 | advertise_server = os.getenv('DOCBLEACH_AS_SERVER_HEADER', 'false') == 'true' 14 | 15 | PLIK_COMMAND = [ 16 | 'plik', 17 | '--server', internal_plik_server, 18 | '-t', '3h', 19 | '-q' 20 | ] 21 | -------------------------------------------------------------------------------- /api/docbleach/controllers/__init__.py: -------------------------------------------------------------------------------- 1 | from .base import BaseHandler 2 | from .ping import PingHandler 3 | from .static import StaticFileHandler 4 | from .task import TaskHandler 5 | from .upload import UploadHandler 6 | -------------------------------------------------------------------------------- /api/docbleach/controllers/base.py: -------------------------------------------------------------------------------- 1 | import tornado.web 2 | 3 | from ..config import advertise_server 4 | 5 | CSP_POLICY = "default-src 'self' https:;" + \ 6 | "style-src 'self' 'unsafe-inline' https:;" + \ 7 | "img-src 'self' data: https:;" + \ 8 | "frame-ancestors 'none';" + \ 9 | "form-action *;" 10 | 11 | REFERRER_POLICY = 'no-referrer, strict-origin-when-cross-origin' 12 | 13 | 14 | class BaseHandler(tornado.web.RequestHandler): 15 | def set_default_headers(self): 16 | super(BaseHandler, self).set_default_headers() 17 | 18 | if advertise_server: 19 | self.set_header('Server', 'DocBleach') 20 | else: 21 | self.clear_header('Server') 22 | 23 | self.set_header('X-Frame-Options', 'DENY') 24 | self.set_header('Referrer-Policy', REFERRER_POLICY) 25 | self.set_header('Content-Security-Policy', CSP_POLICY) 26 | self.set_header('X-Content-Type-Options', 'nosniff') 27 | self.set_header('X-XSS-Protection', '1; mode=block') 28 | 29 | 30 | class BaseApiHandler(BaseHandler): 31 | def set_default_headers(self): 32 | super(BaseApiHandler, self).set_default_headers() 33 | self.set_header('Access-Control-Allow-Origin', '*') 34 | self.set_header('Access-Control-Allow-Headers', 'x-requested-with') 35 | self.set_header('Access-Control-Allow-Methods', 'POST, GET, OPTIONS') 36 | 37 | def options(self, *args, **kwargs): 38 | self.set_status(204) 39 | self.finish() 40 | 41 | def write_error(self, status_code, **kwargs): 42 | if status_code in [500, 503]: 43 | self.write('Error %s' % status_code) 44 | -------------------------------------------------------------------------------- /api/docbleach/controllers/ping.py: -------------------------------------------------------------------------------- 1 | import tornado.web 2 | 3 | 4 | class PingHandler(tornado.web.RequestHandler): 5 | def get(self): 6 | self.finish('pong') 7 | -------------------------------------------------------------------------------- /api/docbleach/controllers/static.py: -------------------------------------------------------------------------------- 1 | import tornado.web 2 | 3 | from .base import BaseHandler 4 | 5 | 6 | class StaticFileHandler(tornado.web.StaticFileHandler, BaseHandler): 7 | pass 8 | -------------------------------------------------------------------------------- /api/docbleach/controllers/task.py: -------------------------------------------------------------------------------- 1 | import tornado.gen 2 | 3 | from .base import BaseApiHandler 4 | from ..tasks import cel 5 | 6 | 7 | class TaskHandler(BaseApiHandler): 8 | @tornado.gen.coroutine 9 | def get(self, task_id): 10 | data = yield self.get_task_meta(task_id) 11 | result_data = {'result': data['result'], 'status': data['status']} 12 | 13 | self.finish(result_data) 14 | 15 | @staticmethod 16 | @tornado.gen.coroutine 17 | def get_task_meta(task_id): 18 | return cel.backend.get_task_meta(task_id) 19 | -------------------------------------------------------------------------------- /api/docbleach/controllers/upload.py: -------------------------------------------------------------------------------- 1 | from subprocess import Popen, PIPE 2 | 3 | import tornado.gen 4 | import tornado.web 5 | 6 | from .base import BaseApiHandler 7 | from ..config import PLIK_COMMAND 8 | from ..tasks import cel 9 | from ..utils import secure_uuid 10 | 11 | 12 | class UploadHandler(BaseApiHandler): 13 | @tornado.gen.coroutine 14 | def post(self): 15 | if 'file' not in self.request.files: 16 | raise tornado.web.HTTPError(404) 17 | 18 | fileinfo = self.request.files['file'] 19 | if isinstance(fileinfo, list): 20 | fileinfo = fileinfo[0] 21 | 22 | filename = str(fileinfo['filename'].strip()) 23 | 24 | link = yield self.store_on_plik(fileinfo) 25 | async_res = yield self.add_task(link, filename) 26 | 27 | self.set_status(202) 28 | self.finish({'task_id': async_res.id}) 29 | 30 | @staticmethod 31 | @tornado.gen.coroutine 32 | def store_on_plik(fileinfo): 33 | p = Popen(PLIK_COMMAND, stdout=PIPE, stdin=PIPE, stderr=PIPE) 34 | link, err = p.communicate(input=fileinfo.body) 35 | 36 | if err: 37 | print(err) 38 | 39 | link = link.strip().decode('utf-8') 40 | 41 | return link 42 | 43 | @staticmethod 44 | @tornado.gen.coroutine 45 | def add_task(link, filename): 46 | task_id = secure_uuid() 47 | args = [link, filename] 48 | return cel.send_task('sanitize', 49 | task_id=task_id, 50 | # TTL for the event, equal to Plik's TTL 51 | expires=3 * 3600, 52 | args=args, 53 | kwargs={}) 54 | -------------------------------------------------------------------------------- /api/docbleach/tasks/__init__.py: -------------------------------------------------------------------------------- 1 | from celery import Celery 2 | 3 | from ..config import celery_broker, celery_result_backend 4 | 5 | cel = Celery( 6 | 'docbleach', 7 | broker=celery_broker, 8 | backend=celery_result_backend 9 | ) 10 | -------------------------------------------------------------------------------- /api/docbleach/utils/__init__.py: -------------------------------------------------------------------------------- 1 | import os 2 | import string 3 | from random import SystemRandom 4 | 5 | cryptogen = SystemRandom() 6 | 7 | 8 | def secure_uuid(): 9 | """ 10 | Strength: 6*3 random characters from a list of 62, approx. 64^18 possible 11 | strings, or 2^100. Should be enough to prevent a successful bruteforce, as 12 | download links are only valid for 3 hours 13 | :return: 14 | """ 15 | return id_generator() + "-" + id_generator() + "-" + id_generator() 16 | 17 | 18 | def id_generator(size=6, chars=string.ascii_letters + string.digits): 19 | return ''.join(cryptogen.choice(chars) for _ in range(size)) 20 | 21 | 22 | def static(*args): 23 | return os.path.join('static', *args) 24 | -------------------------------------------------------------------------------- /api/requirements.txt: -------------------------------------------------------------------------------- 1 | celery[redis]~=4.0.2 2 | tornado~=4.4 3 | -------------------------------------------------------------------------------- /api/static/css/docbleach.css: -------------------------------------------------------------------------------- 1 | .docbleach_severe { 2 | color: darkred; 3 | font-weight: 600; 4 | } 5 | 6 | .docbleach_warning { 7 | color: red; 8 | } 9 | 10 | .docbleach_info { 11 | color: #0091D7; 12 | } 13 | 14 | /* Used by docbleach's drop zone */ 15 | 16 | .js .inputfile, 17 | .js #subButton { 18 | width: 0; 19 | height: 0; 20 | opacity: 0; 21 | overflow: hidden; 22 | position: absolute; 23 | z-index: -1; 24 | } 25 | 26 | .inputfile + label { 27 | max-width: 80%; 28 | font-size: 1.25rem; 29 | font-weight: 700; 30 | text-overflow: ellipsis; 31 | white-space: nowrap; 32 | cursor: pointer; 33 | display: inline-block; 34 | overflow: hidden; 35 | padding: 0.625rem 1.25rem; 36 | } 37 | 38 | .inputfile + label { 39 | color: #f1e5e6; 40 | background-color: #d3394c; 41 | } 42 | 43 | .inputfile:focus + label, 44 | .inputfile.has-focus + label, 45 | .inputfile + label:hover { 46 | background-color: #722040; 47 | } -------------------------------------------------------------------------------- /api/static/css/sweetalert2.min.css: -------------------------------------------------------------------------------- 1 | .swal2-container,body.swal2-iosfix{position:fixed;left:0;right:0}body.swal2-in{overflow-y:hidden}.swal2-container{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;top:0;bottom:0;padding:10px;background-color:transparent;z-index:1060}.swal2-container:not(.swal2-in){pointer-events:none}.swal2-container.swal2-fade{-webkit-transition:background-color .1s;transition:background-color .1s}.swal2-container.swal2-in{background-color:rgba(0,0,0,.4)}.swal2-modal{background-color:#fff;font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;border-radius:5px;box-sizing:border-box;text-align:center;margin:auto;overflow-x:hidden;overflow-y:auto;display:none;position:relative}.swal2-modal:focus{outline:0}.swal2-modal.swal2-loading{overflow-y:hidden}.swal2-modal .swal2-title{color:#595959;font-size:30px;text-align:center;font-weight:600;text-transform:none;position:relative;margin:0;padding:0;line-height:60px;display:block}.swal2-modal .swal2-spacer{height:10px;color:transparent;border:0}.swal2-modal .swal2-styled{border:0;border-radius:3px;box-shadow:none;color:#fff;cursor:pointer;font-size:17px;font-weight:500;margin:0 5px;padding:10px 32px}.swal2-modal .swal2-styled:not(.swal2-loading)[disabled]{opacity:.4;cursor:no-drop}.swal2-modal .swal2-styled.swal2-loading{box-sizing:border-box;border:4px solid transparent;width:40px;height:40px;padding:0;margin:-2px 30px;vertical-align:top;background-color:transparent!important;color:transparent;cursor:default;border-radius:100%;-webkit-animation:rotate-loading 1.5s linear 0s infinite normal;animation:rotate-loading 1.5s linear 0s infinite normal;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.swal2-modal :not(.swal2-styled).swal2-loading::after{display:inline-block;content:'';margin-left:5px;vertical-align:-1px;height:6px;width:6px;border:3px solid #999;border-right-color:transparent;border-radius:50%;-webkit-animation:rotate-loading 1.5s linear 0s infinite normal;animation:rotate-loading 1.5s linear 0s infinite normal}.swal2-modal .swal2-checkbox input,.swal2-modal .swal2-checkbox span,.swal2-modal .swal2-radio input,.swal2-modal .swal2-radio span{vertical-align:middle}.swal2-modal .swal2-image{margin:20px auto;max-width:100%}.swal2-modal .swal2-close{font-size:36px;line-height:36px;font-family:serif;position:absolute;top:5px;right:13px;cursor:pointer;color:#ccc;-webkit-transition:color .1s ease;transition:color .1s ease}.swal2-modal .swal2-close:hover{color:#d55}.swal2-modal>.swal2-checkbox,.swal2-modal>.swal2-file,.swal2-modal>.swal2-input,.swal2-modal>.swal2-radio,.swal2-modal>.swal2-select,.swal2-modal>.swal2-textarea{display:none}.swal2-modal .swal2-content{font-size:18px;text-align:center;font-weight:300;position:relative;float:none;margin:0;padding:0;line-height:normal;color:#545454}.swal2-modal .swal2-checkbox,.swal2-modal .swal2-file,.swal2-modal .swal2-input,.swal2-modal .swal2-radio,.swal2-modal .swal2-select,.swal2-modal .swal2-textarea{margin:20px auto}.swal2-modal .swal2-file,.swal2-modal .swal2-input,.swal2-modal .swal2-textarea{width:100%;box-sizing:border-box;border-radius:3px;border:1px solid #d9d9d9;font-size:18px;box-shadow:inset 0 1px 1px rgba(0,0,0,.06);-webkit-transition:border-color box-shadow .3s;transition:border-color box-shadow .3s}.swal2-modal .swal2-file.swal2-inputerror,.swal2-modal .swal2-input.swal2-inputerror,.swal2-modal .swal2-textarea.swal2-inputerror{border-color:#f06e57}.swal2-modal .swal2-file:focus,.swal2-modal .swal2-input:focus,.swal2-modal .swal2-textarea:focus{outline:0;box-shadow:0 0 3px #c4e6f5;border:1px solid #b4dbed}.swal2-modal .swal2-file:focus::-webkit-input-placeholder,.swal2-modal .swal2-input:focus::-webkit-input-placeholder,.swal2-modal .swal2-textarea:focus::-webkit-input-placeholder{-webkit-transition:opacity .3s .03s ease;transition:opacity .3s .03s ease;opacity:.8}.swal2-modal .swal2-file:focus::-moz-placeholder,.swal2-modal .swal2-input:focus::-moz-placeholder,.swal2-modal .swal2-textarea:focus::-moz-placeholder{-webkit-transition:opacity .3s .03s ease;transition:opacity .3s .03s ease;opacity:.8}.swal2-modal .swal2-file:focus:-ms-input-placeholder,.swal2-modal .swal2-input:focus:-ms-input-placeholder,.swal2-modal .swal2-textarea:focus:-ms-input-placeholder{-webkit-transition:opacity .3s .03s ease;transition:opacity .3s .03s ease;opacity:.8}.swal2-modal .swal2-file:focus::placeholder,.swal2-modal .swal2-input:focus::placeholder,.swal2-modal .swal2-textarea:focus::placeholder{-webkit-transition:opacity .3s .03s ease;transition:opacity .3s .03s ease;opacity:.8}.swal2-modal .swal2-file::-webkit-input-placeholder,.swal2-modal .swal2-input::-webkit-input-placeholder,.swal2-modal .swal2-textarea::-webkit-input-placeholder{color:#e6e6e6}.swal2-modal .swal2-file::-moz-placeholder,.swal2-modal .swal2-input::-moz-placeholder,.swal2-modal .swal2-textarea::-moz-placeholder{color:#e6e6e6}.swal2-modal .swal2-file:-ms-input-placeholder,.swal2-modal .swal2-input:-ms-input-placeholder,.swal2-modal .swal2-textarea:-ms-input-placeholder{color:#e6e6e6}.swal2-modal .swal2-file::placeholder,.swal2-modal .swal2-input::placeholder,.swal2-modal .swal2-textarea::placeholder{color:#e6e6e6}.swal2-modal .swal2-range input{float:left;width:80%}.swal2-modal .swal2-range output{float:right;width:20%;font-size:20px;font-weight:600;text-align:center}.swal2-modal .swal2-range input,.swal2-modal .swal2-range output{height:43px;line-height:43px;vertical-align:middle;margin:20px auto;padding:0}.swal2-modal .swal2-input{height:43px;padding:0 12px}.swal2-modal .swal2-input[type=number]{max-width:150px}.swal2-modal .swal2-file{font-size:20px}.swal2-modal .swal2-textarea{height:108px;padding:12px}.swal2-modal .swal2-select{color:#545454;font-size:inherit;padding:5px 10px;min-width:40%;max-width:100%}.swal2-modal .swal2-radio{border:0}.swal2-modal .swal2-radio label:not(:first-child){margin-left:20px}.swal2-modal .swal2-radio input{margin:0 3px 0 0}.swal2-modal .swal2-checkbox{color:#545454}.swal2-modal .swal2-validationerror{background-color:#f0f0f0;margin:0 -20px;overflow:hidden;padding:10px;color:gray;font-size:16px;font-weight:300;display:none}.swal2-modal .swal2-validationerror::before{content:'!';display:inline-block;width:24px;height:24px;border-radius:50%;background-color:#ea7d7d;color:#fff;line-height:24px;text-align:center;margin-right:10px}.swal2-icon.swal2-info,.swal2-icon.swal2-question,.swal2-icon.swal2-warning{font-size:60px;line-height:80px;text-align:center}@supports (-ms-accelerator:true){.swal2-range input{width:100%!important}.swal2-range output{display:none}}@media all and (-ms-high-contrast:none),(-ms-high-contrast:active){.swal2-range input{width:100%!important}.swal2-range output{display:none}}.swal2-icon{width:80px;height:80px;border:4px solid transparent;border-radius:50%;margin:20px auto 30px;padding:0;position:relative;box-sizing:content-box;cursor:default;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.swal2-icon.swal2-error{border-color:#f27474}.swal2-icon.swal2-error .x-mark{position:relative;display:block}.swal2-icon.swal2-error .line{position:absolute;height:5px;width:47px;background-color:#f27474;display:block;top:37px;border-radius:2px}.swal2-icon.swal2-error .line.left{-webkit-transform:rotate(45deg);transform:rotate(45deg);left:17px}.swal2-icon.swal2-error .line.right{-webkit-transform:rotate(-45deg);transform:rotate(-45deg);right:16px}.swal2-icon.swal2-warning{font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;color:#f8bb86;border-color:#facea8}.swal2-icon.swal2-info{font-family:'Open Sans',sans-serif;color:#3fc3ee;border-color:#9de0f6}.swal2-icon.swal2-question{font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;color:#87adbd;border-color:#c9dae1}.swal2-icon.swal2-success{border-color:#a5dc86}.swal2-icon.swal2-success::after,.swal2-icon.swal2-success::before{content:'';position:absolute;width:60px;height:120px;background:#fff}.swal2-icon.swal2-success::before{border-radius:120px 0 0 120px;top:-7px;left:-33px;-webkit-transform:rotate(-45deg);transform:rotate(-45deg);-webkit-transform-origin:60px 60px;transform-origin:60px 60px}.swal2-icon.swal2-success::after{border-radius:0 120px 120px 0;top:-11px;left:30px;-webkit-transform:rotate(-45deg);transform:rotate(-45deg);-webkit-transform-origin:0 60px;transform-origin:0 60px}.swal2-icon.swal2-success .placeholder{width:80px;height:80px;border:4px solid rgba(165,220,134,.2);border-radius:50%;box-sizing:content-box;position:absolute;left:-4px;top:-4px;z-index:2}.swal2-icon.swal2-success .fix{width:7px;height:90px;background-color:#fff;position:absolute;left:28px;top:8px;z-index:1;-webkit-transform:rotate(-45deg);transform:rotate(-45deg)}.swal2-icon.swal2-success .line{height:5px;background-color:#a5dc86;display:block;border-radius:2px;position:absolute;z-index:2}.swal2-icon.swal2-success .line.tip{width:25px;left:14px;top:46px;-webkit-transform:rotate(45deg);transform:rotate(45deg)}.swal2-icon.swal2-success .line.long{width:47px;right:8px;top:38px;-webkit-transform:rotate(-45deg);transform:rotate(-45deg)}.swal2-progresssteps{font-weight:600;margin:0 0 20px;padding:0}.swal2-progresssteps li{display:inline-block;position:relative}.swal2-progresssteps .swal2-progresscircle{background:#3085d6;border-radius:2em;color:#fff;height:2em;line-height:2em;text-align:center;width:2em;z-index:20}.swal2-progresssteps .swal2-progresscircle:first-child{margin-left:0}.swal2-progresssteps .swal2-progresscircle:last-child{margin-right:0}.swal2-progresssteps .swal2-progresscircle.swal2-activeprogressstep{background:#3085d6}.swal2-progresssteps .swal2-progresscircle.swal2-activeprogressstep~.swal2-progresscircle,.swal2-progresssteps .swal2-progresscircle.swal2-activeprogressstep~.swal2-progressline{background:#add8e6}.swal2-progresssteps .swal2-progressline{background:#3085d6;height:.4em;margin:0 -1px;z-index:10}[class^=swal2]{-webkit-tap-highlight-color:transparent}@-webkit-keyframes showSweetAlert{0%{-webkit-transform:scale(.7);transform:scale(.7)}45%{-webkit-transform:scale(1.05);transform:scale(1.05)}80%{-webkit-transform:scale(.95);transform:scale(.95)}100%{-webkit-transform:scale(1);transform:scale(1)}}@keyframes showSweetAlert{0%{-webkit-transform:scale(.7);transform:scale(.7)}45%{-webkit-transform:scale(1.05);transform:scale(1.05)}80%{-webkit-transform:scale(.95);transform:scale(.95)}100%{-webkit-transform:scale(1);transform:scale(1)}}@-webkit-keyframes hideSweetAlert{0%{-webkit-transform:scale(1);transform:scale(1);opacity:1}100%{-webkit-transform:scale(.5);transform:scale(.5);opacity:0}}@keyframes hideSweetAlert{0%{-webkit-transform:scale(1);transform:scale(1);opacity:1}100%{-webkit-transform:scale(.5);transform:scale(.5);opacity:0}}.swal2-show{-webkit-animation:showSweetAlert .3s;animation:showSweetAlert .3s}.swal2-show.swal2-noanimation{-webkit-animation:none;animation:none}.swal2-hide{-webkit-animation:hideSweetAlert .15s forwards;animation:hideSweetAlert .15s forwards}.swal2-hide.swal2-noanimation{-webkit-animation:none;animation:none}@-webkit-keyframes animate-success-tip{0%,54%{width:0;left:1px;top:19px}70%{width:50px;left:-8px;top:37px}84%{width:17px;left:21px;top:48px}100%{width:25px;left:14px;top:45px}}@keyframes animate-success-tip{0%,54%{width:0;left:1px;top:19px}70%{width:50px;left:-8px;top:37px}84%{width:17px;left:21px;top:48px}100%{width:25px;left:14px;top:45px}}@-webkit-keyframes animate-success-long{0%,65%{width:0;right:46px;top:54px}84%{width:55px;right:0;top:35px}100%{width:47px;right:8px;top:38px}}@keyframes animate-success-long{0%,65%{width:0;right:46px;top:54px}84%{width:55px;right:0;top:35px}100%{width:47px;right:8px;top:38px}}@-webkit-keyframes rotatePlaceholder{0%,5%{-webkit-transform:rotate(-45deg);transform:rotate(-45deg)}100%,12%{-webkit-transform:rotate(-405deg);transform:rotate(-405deg)}}@keyframes rotatePlaceholder{0%,5%{-webkit-transform:rotate(-45deg);transform:rotate(-45deg)}100%,12%{-webkit-transform:rotate(-405deg);transform:rotate(-405deg)}}.animate-success-tip{-webkit-animation:animate-success-tip .75s;animation:animate-success-tip .75s}.animate-success-long{-webkit-animation:animate-success-long .75s;animation:animate-success-long .75s}.swal2-success.animate::after{-webkit-animation:rotatePlaceholder 4.25s ease-in;animation:rotatePlaceholder 4.25s ease-in}@-webkit-keyframes animate-error-icon{0%{-webkit-transform:rotateX(100deg);transform:rotateX(100deg);opacity:0}100%{-webkit-transform:rotateX(0);transform:rotateX(0);opacity:1}}@keyframes animate-error-icon{0%{-webkit-transform:rotateX(100deg);transform:rotateX(100deg);opacity:0}100%{-webkit-transform:rotateX(0);transform:rotateX(0);opacity:1}}.animate-error-icon{-webkit-animation:animate-error-icon .5s;animation:animate-error-icon .5s}@-webkit-keyframes animate-x-mark{0%,50%{-webkit-transform:scale(.4);transform:scale(.4);margin-top:26px;opacity:0}80%{-webkit-transform:scale(1.15);transform:scale(1.15);margin-top:-6px}100%{-webkit-transform:scale(1);transform:scale(1);margin-top:0;opacity:1}}@keyframes animate-x-mark{0%,50%{-webkit-transform:scale(.4);transform:scale(.4);margin-top:26px;opacity:0}80%{-webkit-transform:scale(1.15);transform:scale(1.15);margin-top:-6px}100%{-webkit-transform:scale(1);transform:scale(1);margin-top:0;opacity:1}}.animate-x-mark{-webkit-animation:animate-x-mark .5s;animation:animate-x-mark .5s}@-webkit-keyframes pulse-warning{0%{border-color:#f8d486}100%{border-color:#f8bb86}}@keyframes pulse-warning{0%{border-color:#f8d486}100%{border-color:#f8bb86}}.pulse-warning{-webkit-animation:pulse-warning .75s infinite alternate;animation:pulse-warning .75s infinite alternate}@-webkit-keyframes rotate-loading{0%{-webkit-transform:rotate(0);transform:rotate(0)}100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes rotate-loading{0%{-webkit-transform:rotate(0);transform:rotate(0)}100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}} -------------------------------------------------------------------------------- /api/static/favicon/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docbleach/DocBleach-Web/13657e45d41f7deae689cf6fae0e62d3d7c947df/api/static/favicon/android-chrome-192x192.png -------------------------------------------------------------------------------- /api/static/favicon/android-chrome-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docbleach/DocBleach-Web/13657e45d41f7deae689cf6fae0e62d3d7c947df/api/static/favicon/android-chrome-512x512.png -------------------------------------------------------------------------------- /api/static/favicon/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docbleach/DocBleach-Web/13657e45d41f7deae689cf6fae0e62d3d7c947df/api/static/favicon/apple-touch-icon.png -------------------------------------------------------------------------------- /api/static/favicon/browserconfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | #2b5797 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /api/static/favicon/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docbleach/DocBleach-Web/13657e45d41f7deae689cf6fae0e62d3d7c947df/api/static/favicon/favicon-16x16.png -------------------------------------------------------------------------------- /api/static/favicon/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docbleach/DocBleach-Web/13657e45d41f7deae689cf6fae0e62d3d7c947df/api/static/favicon/favicon-32x32.png -------------------------------------------------------------------------------- /api/static/favicon/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docbleach/DocBleach-Web/13657e45d41f7deae689cf6fae0e62d3d7c947df/api/static/favicon/favicon.ico -------------------------------------------------------------------------------- /api/static/favicon/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "", 3 | "icons": [ 4 | { 5 | "src": "\/android-chrome-192x192.png?v=GvJQap5OwL", 6 | "sizes": "192x192", 7 | "type": "image\/png" 8 | }, 9 | { 10 | "src": "\/android-chrome-512x512.png?v=GvJQap5OwL", 11 | "sizes": "512x512", 12 | "type": "image\/png" 13 | } 14 | ], 15 | "theme_color": "#ffffff", 16 | "display": "standalone", 17 | "orientation": "portrait" 18 | } 19 | -------------------------------------------------------------------------------- /api/static/favicon/mstile-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docbleach/DocBleach-Web/13657e45d41f7deae689cf6fae0e62d3d7c947df/api/static/favicon/mstile-144x144.png -------------------------------------------------------------------------------- /api/static/favicon/mstile-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docbleach/DocBleach-Web/13657e45d41f7deae689cf6fae0e62d3d7c947df/api/static/favicon/mstile-150x150.png -------------------------------------------------------------------------------- /api/static/favicon/mstile-310x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docbleach/DocBleach-Web/13657e45d41f7deae689cf6fae0e62d3d7c947df/api/static/favicon/mstile-310x150.png -------------------------------------------------------------------------------- /api/static/favicon/mstile-310x310.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docbleach/DocBleach-Web/13657e45d41f7deae689cf6fae0e62d3d7c947df/api/static/favicon/mstile-310x310.png -------------------------------------------------------------------------------- /api/static/favicon/mstile-70x70.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docbleach/DocBleach-Web/13657e45d41f7deae689cf6fae0e62d3d7c947df/api/static/favicon/mstile-70x70.png -------------------------------------------------------------------------------- /api/static/favicon/safari-pinned-tab.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 7 | 8 | Created by potrace 1.11, written by Peter Selinger 2001-2013 9 | 10 | 12 | 17 | 23 | 27 | 30 | 34 | 37 | 43 | 61 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /api/static/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | DocBleach 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 33 | 34 | 35 | 36 | 37 | 38 | 68 | 69 | 70 |
71 |
72 |
73 |

DocBleach

74 |

Sanitizes a potentially dangerous file (Office Document, PDF), by removing macros and other active contents.

75 |
76 |
77 | 78 |
79 |
80 |

Friendly reminder: do NOT post sensitive documents here, unless you trust this page owner.

81 |
82 |
83 | 84 |
85 |
86 |

Sanitize a file

87 |
88 | 89 | 92 | 93 |
94 |
95 |
96 |
97 | 98 | 99 | 100 | 101 | 102 | 103 | -------------------------------------------------------------------------------- /api/static/js/docbleach.js: -------------------------------------------------------------------------------- 1 | document.body.classList.add('js'); 2 | var docbleach_endpoint = "/v1/"; 3 | 4 | (function () { 5 | var formElement = document.querySelector("form#uploadForm"); 6 | var labelElement = document.querySelector("form#uploadForm label span"); 7 | var inputElement = document.querySelector("input#file"); 8 | 9 | if (window.FileReader) { 10 | addEventHandler(labelElement, 'dragover', function (e) { 11 | e.preventDefault(); 12 | }); 13 | 14 | addEventHandler(labelElement, 'dragleave', function (e) { 15 | e.preventDefault(); 16 | }); 17 | 18 | addEventHandler(labelElement, 'drop', function (e) { 19 | e = e || window.event; // get window.event if e argument missing (in IE) 20 | e.preventDefault(); 21 | 22 | var dt = e.dataTransfer; 23 | var files = dt.files; 24 | for (var i = 0; i < files.length; i++) { 25 | var file = files[i]; 26 | 27 | var formData = new FormData(); 28 | formData.append("file", file); 29 | convertFile(formData); 30 | } 31 | return false; 32 | }); 33 | } 34 | 35 | addEventHandler(inputElement, "change", function (e) { 36 | convertFile(new FormData(formElement)); 37 | }, false); 38 | 39 | function addEventHandler(obj, evt, handler) { 40 | if (obj === null) { 41 | return; 42 | } 43 | if (obj.addEventListener) { 44 | // W3C method 45 | obj.addEventListener(evt, handler, false); 46 | } else if (obj.attachEvent) { 47 | // IE method. 48 | obj.attachEvent('on' + evt, handler); 49 | } else { 50 | // Old school method. 51 | obj['on' + evt] = handler; 52 | } 53 | } 54 | 55 | function convertFile(formData) { 56 | swal({ 57 | title: "Uploading file", 58 | text: "...", 59 | type: "info", 60 | allowOutsideClick: false, 61 | allowEscapeKey: false 62 | }); 63 | swal.showLoading(); 64 | $.ajax({ 65 | type: "POST", 66 | url: docbleach_endpoint + "tasks", 67 | data: formData, 68 | processData: false, 69 | contentType: false, 70 | success: function (data) { 71 | swal({ 72 | title: "Sanitizing file", 73 | text: "...", 74 | type: "info", 75 | allowOutsideClick: false, 76 | allowEscapeKey: false 77 | }); 78 | swal.showLoading(); 79 | var task_id = data["task_id"]; 80 | console.log("Got a task id: " + task_id); 81 | setTimeout(function () { 82 | checkFileStatus(task_id); 83 | }, 250); 84 | } 85 | }); 86 | } 87 | 88 | function checkFileStatus(task_id) { 89 | $.ajax({ 90 | type: "GET", 91 | url: docbleach_endpoint + "tasks/" + task_id, 92 | success: function (data) { 93 | console.log(data); 94 | console.log("Got a task id state: " + task_id); 95 | if (data.status === "PENDING") { 96 | setTimeout(function () { 97 | checkFileStatus(task_id); 98 | }, 250); 99 | return; 100 | } 101 | displayResponse( 102 | {infos: [data.result.output], link: data.result.final_file}); 103 | } 104 | }); 105 | } 106 | })(); 107 | 108 | function displayResponse(response) { 109 | if (response.errors && response.errors.join) { 110 | console.log(response.errors); 111 | swal("An error occured", "", "error"); 112 | return; 113 | } 114 | if (response.infos && response.infos.join) { 115 | var htmlCode = parseDocbleach(response.infos.join("\n")); 116 | 117 | var modalData = { 118 | title: "Results", 119 | html: htmlCode, 120 | type: "success", 121 | allowOutsideClick: true, 122 | confirmButtonColor: "#73B6D6", 123 | width: "60%" 124 | }; 125 | if (response.link) { 126 | modalData["confirmButtonText"] = "Download the sanitized file"; 127 | } else { 128 | modalData["timer"] = 2000; 129 | } 130 | swal.hideLoading(); 131 | swal(modalData).then(function () { 132 | if (response.link) { 133 | download(response.link); 134 | } 135 | }); 136 | } else if (response.link) { 137 | download(response.link); 138 | swal.hideLoading(); 139 | swal({ 140 | text: "Download started", 141 | type: "success", 142 | timer: 2000 143 | }); 144 | } 145 | } 146 | 147 | function parseDocbleach(infos) { 148 | var parent = document.createElement("div"); 149 | var lines = infos.split(/\r?\n/); // Allow multiple lines per blob 150 | 151 | lines.forEach(function (line) { 152 | var child = document.createElement("p"); 153 | 154 | if (line.indexOf("WARN ") === 0) { 155 | child.classList.add("docbleach_warning"); 156 | line = line.substring("WARN ".length); 157 | } else if (line.indexOf("INFO ") === 0) { 158 | child.classList.add("docbleach_info"); 159 | line = line.substring("INFO ".length); 160 | } else if (line.indexOf("ERROR ") === 0) { 161 | child.classList.add("docbleach_severe"); 162 | line = line.substring("ERROR ".length); 163 | } else if (line.indexOf("FATAL ") === 0) { 164 | child.classList.add("docbleach_severe"); 165 | line = line.substring("FATAL ".length); 166 | } 167 | child.innerText = line; 168 | parent.appendChild(child); 169 | }); 170 | return parent.innerHTML; 171 | } 172 | 173 | function download(link) { 174 | var anchor = document.createElement('a'); 175 | anchor.href = link; 176 | // anchor.target = '_blank'; 177 | anchor.setAttribute("download", ""); 178 | document.body.appendChild(anchor); 179 | anchor.click(); 180 | } 181 | -------------------------------------------------------------------------------- /api/static/js/html5shiv.js: -------------------------------------------------------------------------------- 1 | /* 2 | HTML5 Shiv v3.7.0 | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed 3 | */ 4 | (function(l,f){function m(){var a=e.elements;return"string"==typeof a?a.split(" "):a}function i(a){var b=n[a[o]];b||(b={},h++,a[o]=h,n[h]=b);return b}function p(a,b,c){b||(b=f);if(g)return b.createElement(a);c||(c=i(b));b=c.cache[a]?c.cache[a].cloneNode():r.test(a)?(c.cache[a]=c.createElem(a)).cloneNode():c.createElem(a);return b.canHaveChildren&&!s.test(a)?c.frag.appendChild(b):b}function t(a,b){if(!b.cache)b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag(); 5 | a.createElement=function(c){return!e.shivMethods?b.createElem(c):p(c,a,b)};a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+m().join().replace(/[\w\-]+/g,function(a){b.createElem(a);b.frag.createElement(a);return'c("'+a+'")'})+");return n}")(e,b.frag)}function q(a){a||(a=f);var b=i(a);if(e.shivCSS&&!j&&!b.hasCSS){var c,d=a;c=d.createElement("p");d=d.getElementsByTagName("head")[0]||d.documentElement;c.innerHTML="x"; 6 | c=d.insertBefore(c.lastChild,d.firstChild);b.hasCSS=!!c}g||t(a,b);return a}var k=l.html5||{},s=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,r=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,j,o="_html5shiv",h=0,n={},g;(function(){try{var a=f.createElement("a");a.innerHTML="";j="hidden"in a;var b;if(!(b=1==a.childNodes.length)){f.createElement("a");var c=f.createDocumentFragment();b="undefined"==typeof c.cloneNode|| 7 | "undefined"==typeof c.createDocumentFragment||"undefined"==typeof c.createElement}g=b}catch(d){g=j=!0}})();var e={elements:k.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output progress section summary template time video",version:"3.7.0",shivCSS:!1!==k.shivCSS,supportsUnknownElements:g,shivMethods:!1!==k.shivMethods,type:"default",shivDocument:q,createElement:p,createDocumentFragment:function(a,b){a||(a=f); 8 | if(g)return a.createDocumentFragment();for(var b=b||i(a),c=b.frag.cloneNode(),d=0,e=m(),h=e.length;d #mq-test-1 { width: 42px; }',c.insertBefore(e,d),b=42===f.offsetWidth,c.removeChild(e),{matches:b,media:a}}}(a.document)}(this),function(a){"use strict";function b(){u(!0)}var c={};a.respond=c,c.update=function(){};var d=[],e=function(){var b=!1;try{b=new a.XMLHttpRequest}catch(c){b=new a.ActiveXObject("Microsoft.XMLHTTP")}return function(){return b}}(),f=function(a,b){var c=e();c&&(c.open("GET",a,!0),c.onreadystatechange=function(){4!==c.readyState||200!==c.status&&304!==c.status||b(c.responseText)},4!==c.readyState&&c.send(null))};if(c.ajax=f,c.queue=d,c.regex={media:/@media[^\{]+\{([^\{\}]*\{[^\}\{]*\})+/gi,keyframes:/@(?:\-(?:o|moz|webkit)\-)?keyframes[^\{]+\{(?:[^\{\}]*\{[^\}\{]*\})+[^\}]*\}/gi,urls:/(url\()['"]?([^\/\)'"][^:\)'"]+)['"]?(\))/g,findStyles:/@media *([^\{]+)\{([\S\s]+?)$/,only:/(only\s+)?([a-zA-Z]+)\s?/,minw:/\([\s]*min\-width\s*:[\s]*([\s]*[0-9\.]+)(px|em)[\s]*\)/,maxw:/\([\s]*max\-width\s*:[\s]*([\s]*[0-9\.]+)(px|em)[\s]*\)/},c.mediaQueriesSupported=a.matchMedia&&null!==a.matchMedia("only all")&&a.matchMedia("only all").matches,!c.mediaQueriesSupported){var g,h,i,j=a.document,k=j.documentElement,l=[],m=[],n=[],o={},p=30,q=j.getElementsByTagName("head")[0]||k,r=j.getElementsByTagName("base")[0],s=q.getElementsByTagName("link"),t=function(){var a,b=j.createElement("div"),c=j.body,d=k.style.fontSize,e=c&&c.style.fontSize,f=!1;return b.style.cssText="position:absolute;font-size:1em;width:1em",c||(c=f=j.createElement("body"),c.style.background="none"),k.style.fontSize="100%",c.style.fontSize="100%",c.appendChild(b),f&&k.insertBefore(c,k.firstChild),a=b.offsetWidth,f?k.removeChild(c):c.removeChild(b),k.style.fontSize=d,e&&(c.style.fontSize=e),a=i=parseFloat(a)},u=function(b){var c="clientWidth",d=k[c],e="CSS1Compat"===j.compatMode&&d||j.body[c]||d,f={},o=s[s.length-1],r=(new Date).getTime();if(b&&g&&p>r-g)return a.clearTimeout(h),h=a.setTimeout(u,p),void 0;g=r;for(var v in l)if(l.hasOwnProperty(v)){var w=l[v],x=w.minw,y=w.maxw,z=null===x,A=null===y,B="em";x&&(x=parseFloat(x)*(x.indexOf(B)>-1?i||t():1)),y&&(y=parseFloat(y)*(y.indexOf(B)>-1?i||t():1)),w.hasquery&&(z&&A||!(z||e>=x)||!(A||y>=e))||(f[w.media]||(f[w.media]=[]),f[w.media].push(m[w.rules]))}for(var C in n)n.hasOwnProperty(C)&&n[C]&&n[C].parentNode===q&&q.removeChild(n[C]);n.length=0;for(var D in f)if(f.hasOwnProperty(D)){var E=j.createElement("style"),F=f[D].join("\n");E.type="text/css",E.media=D,q.insertBefore(E,o.nextSibling),E.styleSheet?E.styleSheet.cssText=F:E.appendChild(j.createTextNode(F)),n.push(E)}},v=function(a,b,d){var e=a.replace(c.regex.keyframes,"").match(c.regex.media),f=e&&e.length||0;b=b.substring(0,b.lastIndexOf("/"));var g=function(a){return a.replace(c.regex.urls,"$1"+b+"$2$3")},h=!f&&d;b.length&&(b+="/"),h&&(f=1);for(var i=0;f>i;i++){var j,k,n,o;h?(j=d,m.push(g(a))):(j=e[i].match(c.regex.findStyles)&&RegExp.$1,m.push(RegExp.$2&&g(RegExp.$2))),n=j.split(","),o=n.length;for(var p=0;o>p;p++)k=n[p],l.push({media:k.split("(")[0].match(c.regex.only)&&RegExp.$2||"all",rules:m.length-1,hasquery:k.indexOf("(")>-1,minw:k.match(c.regex.minw)&&parseFloat(RegExp.$1)+(RegExp.$2||""),maxw:k.match(c.regex.maxw)&&parseFloat(RegExp.$1)+(RegExp.$2||"")})}u()},w=function(){if(d.length){var b=d.shift();f(b.href,function(c){v(c,b.href,b.media),o[b.href]=!0,a.setTimeout(function(){w()},0)})}},x=function(){for(var b=0;b