mirror of
https://github.com/freedomofpress/dangerzone.git
synced 2025-04-28 09:52:37 +02:00
Initial commit
This commit is contained in:
parent
deba465a5b
commit
0b9823a34e
10 changed files with 276 additions and 2 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -127,3 +127,6 @@ dmypy.json
|
|||
|
||||
# Pyre type checker
|
||||
.pyre/
|
||||
|
||||
# Other
|
||||
.vscode
|
14
Pipfile
Normal file
14
Pipfile
Normal file
|
@ -0,0 +1,14 @@
|
|||
[[source]]
|
||||
url = "https://pypi.python.org/simple"
|
||||
verify_ssl = true
|
||||
name = "pypi"
|
||||
|
||||
[packages]
|
||||
PyQt5 = "*"
|
||||
click = "*"
|
||||
|
||||
[dev-packages]
|
||||
black = "*"
|
||||
|
||||
[requires]
|
||||
python_version = "3.7"
|
|
@ -1,2 +1,7 @@
|
|||
# bastion
|
||||
Taking arbitrary untrusted PDFs, office documents, or images and convert them to a trusted PDF
|
||||
# dangerzone
|
||||
|
||||
Take arbitrary untrusted PDFs, office documents, or images and convert them to a trusted PDF.
|
||||
|
||||
## Development environment
|
||||
|
||||
You need [podman](https://podman.io/getting-started/installation) ([these instructions](https://kushaldas.in/posts/podman-on-debian-buster.html) are useful for installing Debian/Ubuntu).
|
||||
|
|
44
dangerzone/__init__.py
Normal file
44
dangerzone/__init__.py
Normal file
|
@ -0,0 +1,44 @@
|
|||
from PyQt5 import QtCore, QtWidgets
|
||||
import sys
|
||||
import signal
|
||||
import click
|
||||
|
||||
from .common import Common
|
||||
from .main_window import MainWindow
|
||||
|
||||
dangerzone_version = "0.1.0"
|
||||
|
||||
|
||||
@click.command()
|
||||
@click.option("--filename", default="", help="Document filename")
|
||||
def main(filename):
|
||||
print(f"dangerzone {dangerzone_version}")
|
||||
|
||||
# Allow Ctrl-C to smoothly quit the program instead of throwing an exception
|
||||
signal.signal(signal.SIGINT, signal.SIG_DFL)
|
||||
|
||||
# Create the Qt app
|
||||
app = QtWidgets.QApplication(sys.argv)
|
||||
app.setQuitOnLastWindowClosed(False)
|
||||
|
||||
# Common object
|
||||
common = Common()
|
||||
|
||||
# Main window
|
||||
main_window = MainWindow(app, common)
|
||||
|
||||
# If a filename wasn't passed in, get with with a dialog
|
||||
if filename == "":
|
||||
filename = QtWidgets.QFileDialog.getOpenFileName(
|
||||
main_window,
|
||||
"Open document",
|
||||
filter="Documents (*.pdf *.docx *.doc *.xlsx *.xls *.pptx *.ppt *.odt *.fodt *.ods *.fods *.odp *.fodp *.odg *.fodg *.odf)",
|
||||
)
|
||||
if filename[0] == "":
|
||||
print("No document was not selected")
|
||||
return
|
||||
|
||||
filename = filename[0]
|
||||
|
||||
main_window.start(filename)
|
||||
sys.exit(app.exec_())
|
29
dangerzone/common.py
Normal file
29
dangerzone/common.py
Normal file
|
@ -0,0 +1,29 @@
|
|||
import sys
|
||||
import os
|
||||
import inspect
|
||||
|
||||
|
||||
class Common(object):
|
||||
"""
|
||||
The Common class is a singleton of shared functionality throughout the app
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def get_resource_path(self, filename):
|
||||
if getattr(sys, "dangerzone_dev", False):
|
||||
# Look for resources directory relative to python file
|
||||
prefix = os.path.join(
|
||||
os.path.dirname(
|
||||
os.path.dirname(
|
||||
os.path.abspath(inspect.getfile(inspect.currentframe()))
|
||||
)
|
||||
),
|
||||
"share",
|
||||
)
|
||||
else:
|
||||
print("Error, can only run in dev mode so far")
|
||||
|
||||
resource_path = os.path.join(prefix, filename)
|
||||
return resource_path
|
75
dangerzone/main_window.py
Normal file
75
dangerzone/main_window.py
Normal file
|
@ -0,0 +1,75 @@
|
|||
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||
|
||||
from .tasks import PullImageTask, BuildContainerTask
|
||||
|
||||
|
||||
class MainWindow(QtWidgets.QMainWindow):
|
||||
def __init__(self, app, common):
|
||||
super(MainWindow, self).__init__()
|
||||
self.app = app
|
||||
self.common = common
|
||||
|
||||
self.setWindowTitle("dangerzone")
|
||||
self.setMinimumWidth(600)
|
||||
self.setMinimumHeight(500)
|
||||
|
||||
self.task_label = QtWidgets.QLabel()
|
||||
self.task_label.setAlignment(QtCore.Qt.AlignCenter)
|
||||
self.task_label.setStyleSheet("QLabel { font-weight: bold; font-size: 20px; }")
|
||||
|
||||
font = QtGui.QFontDatabase.systemFont(QtGui.QFontDatabase.FixedFont)
|
||||
self.task_details = QtWidgets.QLabel()
|
||||
self.task_details.setStyleSheet(
|
||||
"QLabel { background-color: #ffffff; font-size: 12px; padding: 10px; }"
|
||||
)
|
||||
self.task_details.setFont(font)
|
||||
self.task_details.setAlignment(QtCore.Qt.AlignTop)
|
||||
|
||||
self.details_scrollarea = QtWidgets.QScrollArea()
|
||||
self.details_scrollarea.setWidgetResizable(True)
|
||||
self.details_scrollarea.setWidget(self.task_details)
|
||||
self.details_scrollarea.verticalScrollBar().rangeChanged.connect(
|
||||
self.scroll_to_bottom
|
||||
)
|
||||
|
||||
layout = QtWidgets.QVBoxLayout()
|
||||
layout.addWidget(self.task_label)
|
||||
layout.addWidget(self.details_scrollarea, stretch=1)
|
||||
|
||||
central_widget = QtWidgets.QWidget()
|
||||
central_widget.setLayout(layout)
|
||||
self.setCentralWidget(central_widget)
|
||||
|
||||
self.tasks = [PullImageTask, BuildContainerTask]
|
||||
|
||||
def start(self, filename):
|
||||
print(f"Input document: {filename}")
|
||||
self.document_filename = filename
|
||||
self.show()
|
||||
|
||||
self.next_task()
|
||||
|
||||
def next_task(self):
|
||||
if len(self.tasks) == 0:
|
||||
print("Tasks finished")
|
||||
return
|
||||
|
||||
self.current_task = self.tasks.pop(0)(self.common)
|
||||
self.current_task.update_label.connect(self.update_label)
|
||||
self.current_task.update_details.connect(self.update_details)
|
||||
self.current_task.thread_finished.connect(self.next_task)
|
||||
self.current_task.start()
|
||||
|
||||
def update_label(self, s):
|
||||
self.task_label.setText(s)
|
||||
|
||||
def update_details(self, s):
|
||||
self.task_details.setText(s)
|
||||
|
||||
def scroll_to_bottom(self, minimum, maximum):
|
||||
self.details_scrollarea.verticalScrollBar().setValue(maximum)
|
||||
|
||||
def closeEvent(self, e):
|
||||
print("closing")
|
||||
e.accept()
|
||||
self.app.quit()
|
62
dangerzone/tasks.py
Normal file
62
dangerzone/tasks.py
Normal file
|
@ -0,0 +1,62 @@
|
|||
import subprocess
|
||||
import time
|
||||
from PyQt5 import QtCore, QtWidgets, QtGui
|
||||
|
||||
|
||||
class TaskBase(QtCore.QThread):
|
||||
thread_finished = QtCore.pyqtSignal()
|
||||
update_label = QtCore.pyqtSignal(str)
|
||||
update_details = QtCore.pyqtSignal(str)
|
||||
|
||||
def __init__(self):
|
||||
super(TaskBase, self).__init__()
|
||||
|
||||
def execute_podman(self, args, watch="stdout"):
|
||||
print(f"Executing: {' '.join(args)}")
|
||||
output = ""
|
||||
with subprocess.Popen(
|
||||
args,
|
||||
stdin=None,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
bufsize=1,
|
||||
universal_newlines=True,
|
||||
) as p:
|
||||
if watch == "stdout":
|
||||
pipe = p.stdout
|
||||
else:
|
||||
pipe = p.stderr
|
||||
|
||||
for line in pipe:
|
||||
output += line
|
||||
self.update_details.emit(output)
|
||||
|
||||
output += p.stdout.read()
|
||||
self.update_details.emit(output)
|
||||
|
||||
|
||||
class PullImageTask(TaskBase):
|
||||
def __init__(self, common):
|
||||
super(PullImageTask, self).__init__()
|
||||
self.common = common
|
||||
|
||||
def run(self):
|
||||
self.update_label.emit("Pulling container image")
|
||||
self.update_details.emit("")
|
||||
args = ["podman", "pull", "ubuntu:18.04"]
|
||||
self.execute_podman(args, watch="stderr")
|
||||
self.thread_finished.emit()
|
||||
|
||||
|
||||
class BuildContainerTask(TaskBase):
|
||||
def __init__(self, common):
|
||||
super(BuildContainerTask, self).__init__()
|
||||
self.common = common
|
||||
|
||||
def run(self):
|
||||
containerfile = self.common.get_resource_path("Containerfile")
|
||||
self.update_label.emit("Building container")
|
||||
self.update_details.emit("")
|
||||
args = ["podman", "build", "-t", "dangerzone", "-f", containerfile]
|
||||
self.execute_podman(args)
|
||||
self.thread_finished.emit()
|
10
dev_scripts/dangerzone
Executable file
10
dev_scripts/dangerzone
Executable file
|
@ -0,0 +1,10 @@
|
|||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Load dangerzone module and resources from the source code tree
|
||||
import os, sys
|
||||
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
sys.dangerzone_dev = True
|
||||
|
||||
import dangerzone
|
||||
dangerzone.main()
|
24
setup.py
Normal file
24
setup.py
Normal file
|
@ -0,0 +1,24 @@
|
|||
#!/usr/bin/env python3
|
||||
import setuptools
|
||||
import os
|
||||
import sys
|
||||
from dangerzone import dangerzone_version
|
||||
|
||||
|
||||
setuptools.setup(
|
||||
name="dangerzone",
|
||||
version=dangerzone_version,
|
||||
author="Micah Lee",
|
||||
author_email="micah.lee@theintercept.com",
|
||||
license="MIT",
|
||||
description="Take arbitrary untrusted PDFs, office documents, or images and convert them to a trusted PDF",
|
||||
url="https://github.com/firstlookmedia/dangerzone",
|
||||
packages=["dangerzone"],
|
||||
classifiers=(
|
||||
"Development Status :: 4 - Beta",
|
||||
"Programming Language :: Python",
|
||||
"Intended Audience :: End Users/Desktop",
|
||||
"Operating System :: OS Independent",
|
||||
),
|
||||
entry_points={"console_scripts": ["dangerzone = dangerzone:main"]},
|
||||
)
|
8
share/Containerfile
Normal file
8
share/Containerfile
Normal file
|
@ -0,0 +1,8 @@
|
|||
FROM ubuntu:18.04
|
||||
|
||||
RUN apt-get update && \
|
||||
apt-get install -y poppler-utils imagemagick
|
||||
|
||||
RUN useradd -ms /bin/bash user
|
||||
USER user:user
|
||||
|
Loading…
Reference in a new issue