mirror of
https://github.com/onionshare/onionshare.git
synced 2024-12-24 23:09:42 -05:00
Support sending a custom Content-Security-Policy header in Website mode
This commit is contained in:
commit
a4985e7029
@ -150,7 +150,13 @@ def main(cwd=None):
|
||||
action="store_true",
|
||||
dest="disable_csp",
|
||||
default=False,
|
||||
help="Publish website: Disable Content Security Policy header (allows your website to use third-party resources)",
|
||||
help="Publish website: Disable the default Content Security Policy header (allows your website to use third-party resources)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--custom_csp",
|
||||
metavar="custom_csp",
|
||||
default=None,
|
||||
help="Publish website: Set a custom Content Security Policy header",
|
||||
)
|
||||
# Other
|
||||
parser.add_argument(
|
||||
@ -189,6 +195,7 @@ def main(cwd=None):
|
||||
disable_text = args.disable_text
|
||||
disable_files = args.disable_files
|
||||
disable_csp = bool(args.disable_csp)
|
||||
custom_csp = args.custom_csp
|
||||
verbose = bool(args.verbose)
|
||||
|
||||
# Verbose mode?
|
||||
@ -234,7 +241,15 @@ def main(cwd=None):
|
||||
mode_settings.set("receive", "disable_text", disable_text)
|
||||
mode_settings.set("receive", "disable_files", disable_files)
|
||||
if mode == "website":
|
||||
mode_settings.set("website", "disable_csp", disable_csp)
|
||||
if disable_csp and custom_csp:
|
||||
print("You cannot disable the CSP and set a custom one. Either set --disable-csp or --custom-csp but not both.")
|
||||
sys.exit()
|
||||
if disable_csp:
|
||||
mode_settings.set("website", "disable_csp", True)
|
||||
mode_settings.set("website", "custom_csp", None)
|
||||
if custom_csp:
|
||||
mode_settings.set("website", "custom_csp", custom_csp)
|
||||
mode_settings.set("website", "disable_csp", False)
|
||||
else:
|
||||
# See what the persistent mode was
|
||||
mode = mode_settings.get("persistent", "mode")
|
||||
|
@ -55,7 +55,11 @@ class ModeSettings:
|
||||
"disable_text": False,
|
||||
"disable_files": False,
|
||||
},
|
||||
"website": {"disable_csp": False, "filenames": []},
|
||||
"website": {
|
||||
"disable_csp": False,
|
||||
"custom_csp": None,
|
||||
"filenames": []
|
||||
},
|
||||
"chat": {"room": "default"},
|
||||
}
|
||||
self._settings = {}
|
||||
|
@ -199,11 +199,18 @@ class Web:
|
||||
for header, value in self.security_headers:
|
||||
r.headers.set(header, value)
|
||||
# Set a CSP header unless in website mode and the user has disabled it
|
||||
if not self.settings.get("website", "disable_csp") or self.mode != "website":
|
||||
default_csp = "default-src 'self'; frame-ancestors 'none'; form-action 'self'; base-uri 'self'; img-src 'self' data:;"
|
||||
if self.mode != "website" or (not self.settings.get("website", "disable_csp") and not self.settings.get("website", "custom_csp")):
|
||||
r.headers.set(
|
||||
"Content-Security-Policy",
|
||||
"default-src 'self'; frame-ancestors 'none'; form-action 'self'; base-uri 'self'; img-src 'self' data:;",
|
||||
default_csp
|
||||
)
|
||||
else:
|
||||
if self.settings.get("website", "custom_csp"):
|
||||
r.headers.set(
|
||||
"Content-Security-Policy",
|
||||
self.settings.get("website", "custom_csp")
|
||||
)
|
||||
return r
|
||||
|
||||
@self.app.errorhandler(404)
|
||||
|
@ -203,7 +203,8 @@
|
||||
"mode_settings_receive_disable_text_checkbox": "Disable submitting text",
|
||||
"mode_settings_receive_disable_files_checkbox": "Disable uploading files",
|
||||
"mode_settings_receive_webhook_url_checkbox": "Use notification webhook",
|
||||
"mode_settings_website_disable_csp_checkbox": "Don't send Content Security Policy header (allows your website to use third-party resources)",
|
||||
"mode_settings_website_disable_csp_checkbox": "Don't send default Content Security Policy header (allows your website to use third-party resources)",
|
||||
"mode_settings_website_custom_csp_checkbox": "Send a custom Content Security Policy header",
|
||||
"gui_all_modes_transfer_finished_range": "Transferred {} - {}",
|
||||
"gui_all_modes_transfer_finished": "Transferred {}",
|
||||
"gui_all_modes_transfer_canceled_range": "Canceled {} - {}",
|
||||
@ -232,4 +233,4 @@
|
||||
"moat_captcha_error": "The solution is not correct. Please try again.",
|
||||
"moat_solution_empty_error": "You must enter the characters from the image",
|
||||
"mode_tor_not_connected_label": "OnionShare is not connected to the Tor network"
|
||||
}
|
||||
}
|
||||
|
@ -49,6 +49,7 @@ class WebsiteMode(Mode):
|
||||
self.web = Web(self.common, True, self.settings, "website")
|
||||
|
||||
# Settings
|
||||
# Disable CSP option
|
||||
self.disable_csp_checkbox = QtWidgets.QCheckBox()
|
||||
self.disable_csp_checkbox.clicked.connect(self.disable_csp_checkbox_clicked)
|
||||
self.disable_csp_checkbox.setText(
|
||||
@ -63,6 +64,26 @@ class WebsiteMode(Mode):
|
||||
self.disable_csp_checkbox
|
||||
)
|
||||
|
||||
# Custom CSP option
|
||||
self.custom_csp_checkbox = QtWidgets.QCheckBox()
|
||||
self.custom_csp_checkbox.clicked.connect(self.custom_csp_checkbox_clicked)
|
||||
self.custom_csp_checkbox.setText(strings._("mode_settings_website_custom_csp_checkbox"))
|
||||
if self.settings.get("website", "custom_csp") and not self.settings.get("website", "disable_csp"):
|
||||
self.custom_csp_checkbox.setCheckState(QtCore.Qt.Checked)
|
||||
else:
|
||||
self.custom_csp_checkbox.setCheckState(QtCore.Qt.Unchecked)
|
||||
self.custom_csp = QtWidgets.QLineEdit()
|
||||
self.custom_csp.setPlaceholderText(
|
||||
"default-src 'self'; frame-ancestors 'none'; form-action 'self'; base-uri 'self'; img-src 'self' data:;"
|
||||
)
|
||||
self.custom_csp.editingFinished.connect(self.custom_csp_editing_finished)
|
||||
|
||||
custom_csp_layout = QtWidgets.QHBoxLayout()
|
||||
custom_csp_layout.setContentsMargins(0, 0, 0, 0)
|
||||
custom_csp_layout.addWidget(self.custom_csp_checkbox)
|
||||
custom_csp_layout.addWidget(self.custom_csp)
|
||||
self.mode_settings_widget.mode_specific_layout.addLayout(custom_csp_layout)
|
||||
|
||||
# File selection
|
||||
self.file_selection = FileSelection(
|
||||
self.common,
|
||||
@ -181,11 +202,42 @@ class WebsiteMode(Mode):
|
||||
|
||||
def disable_csp_checkbox_clicked(self):
|
||||
"""
|
||||
Save disable CSP setting to the tab settings
|
||||
Save disable CSP setting to the tab settings. Uncheck 'custom CSP'
|
||||
setting if disabling CSP altogether.
|
||||
"""
|
||||
self.settings.set(
|
||||
"website", "disable_csp", self.disable_csp_checkbox.isChecked()
|
||||
)
|
||||
if self.disable_csp_checkbox.isChecked():
|
||||
self.custom_csp_checkbox.setCheckState(QtCore.Qt.Unchecked)
|
||||
self.custom_csp_checkbox.setEnabled(False)
|
||||
else:
|
||||
self.custom_csp_checkbox.setEnabled(True)
|
||||
|
||||
def custom_csp_checkbox_clicked(self):
|
||||
"""
|
||||
Uncheck 'disable CSP' setting if custom CSP is used.
|
||||
"""
|
||||
if self.custom_csp_checkbox.isChecked():
|
||||
self.disable_csp_checkbox.setCheckState(QtCore.Qt.Unchecked)
|
||||
self.disable_csp_checkbox.setEnabled(False)
|
||||
self.settings.set(
|
||||
"website", "custom_csp", self.custom_csp
|
||||
)
|
||||
else:
|
||||
self.disable_csp_checkbox.setEnabled(True)
|
||||
self.custom_csp.setText("")
|
||||
self.settings.set(
|
||||
"website", "custom_csp", None
|
||||
)
|
||||
|
||||
def custom_csp_editing_finished(self):
|
||||
if self.custom_csp.text().strip() == "":
|
||||
self.custom_csp.setText("")
|
||||
self.settings.set("website", "custom_csp", None)
|
||||
else:
|
||||
custom_csp = self.custom_csp.text()
|
||||
self.settings.set("website", "custom_csp", custom_csp)
|
||||
|
||||
def get_stop_server_autostop_timer_text(self):
|
||||
"""
|
||||
|
@ -22,8 +22,10 @@ class TestWebsite(GuiBaseTest):
|
||||
QtTest.QTest.qWait(500, self.gui.qtapp)
|
||||
if tab.settings.get("website", "disable_csp"):
|
||||
self.assertFalse("Content-Security-Policy" in r.headers)
|
||||
elif tab.settings.get("website", "custom_csp"):
|
||||
self.assertEqual(tab.settings.get("website", "custom_csp"), r.headers["Content-Security-Policy"])
|
||||
else:
|
||||
self.assertTrue("Content-Security-Policy" in r.headers)
|
||||
self.assertEqual("default-src 'self'; frame-ancestors 'none'; form-action 'self'; base-uri 'self'; img-src 'self' data:;", r.headers["Content-Security-Policy"])
|
||||
|
||||
def run_all_website_mode_setup_tests(self, tab):
|
||||
"""Tests in website mode prior to starting a share"""
|
||||
@ -77,12 +79,24 @@ class TestWebsite(GuiBaseTest):
|
||||
self.run_all_website_mode_download_tests(tab)
|
||||
self.close_all_tabs()
|
||||
|
||||
def test_csp_enabled(self):
|
||||
def test_csp_disabled(self):
|
||||
"""
|
||||
Test disabling CSP
|
||||
"""
|
||||
tab = self.new_website_tab()
|
||||
tab.get_mode().disable_csp_checkbox.click()
|
||||
self.assertFalse(tab.get_mode().custom_csp_checkbox.isEnabled())
|
||||
self.run_all_website_mode_download_tests(tab)
|
||||
self.close_all_tabs()
|
||||
|
||||
def test_csp_custom(self):
|
||||
"""
|
||||
Test a custom CSP
|
||||
"""
|
||||
tab = self.new_website_tab()
|
||||
tab.get_mode().custom_csp_checkbox.click()
|
||||
self.assertFalse(tab.get_mode().disable_csp_checkbox.isEnabled())
|
||||
tab.settings.set("website", "custom_csp", "default-src 'self'")
|
||||
self.run_all_website_mode_download_tests(tab)
|
||||
self.close_all_tabs()
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user