From d20f196b682813f88971ad8882bb559a88002c8c Mon Sep 17 00:00:00 2001 From: Michael Warkentin Date: Thu, 2 Jul 2015 22:05:06 -0400 Subject: [PATCH 1/6] #36: Add test to ensure watchman version not leaked without auth --- tests/test_views.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tests/test_views.py b/tests/test_views.py index a84c172..709356b 100644 --- a/tests/test_views.py +++ b/tests/test_views.py @@ -146,6 +146,17 @@ def test_login_not_required(self): self.assertEqual(response.status_code, 200) + @override_settings(WATCHMAN_TOKEN='ABCDE') + def test_version_header_not_included_when_token_auth_fails(self): + # Have to manually reload settings here because override_settings + # happens after self.setUp(), but before self.tearDown() + reload_settings() + request = RequestFactory().get('/') + + response = views.status(request) + self.assertEqual(response.status_code, 403) + self.assertFalse(response.has_header('X-Watchman-Version')) + @override_settings(WATCHMAN_AUTH_DECORATOR='django.contrib.auth.decorators.login_required') def test_response_when_login_required_is_redirect(self): # Have to manually reload settings here because override_settings From c60309f97779f847cf07444d33c8066dcfbc8124 Mon Sep 17 00:00:00 2001 From: Michael Warkentin Date: Thu, 2 Jul 2015 22:23:45 -0400 Subject: [PATCH 2/6] #36: WIP --- README.rst | 14 ++++++++++++-- tests/test_views.py | 18 ++++++++++++++++++ watchman/settings.py | 2 ++ watchman/views.py | 11 ++++++++--- 4 files changed, 40 insertions(+), 5 deletions(-) diff --git a/README.rst b/README.rst index c02c3e8..1c1d5e1 100644 --- a/README.rst +++ b/README.rst @@ -63,6 +63,11 @@ Quickstart Features -------- +Human-friendly dashboard (new in 0.6.0) +*************************************** + +TBD + Token based authentication ************************** @@ -129,8 +134,8 @@ querystring should be run, eg:: curl -XGET http://127.0.0.1:8080/watchman/?skip=watchman.checks.email -Django management command (new in ``django-watchman 0.5``) -********************************************************** +Django management command +************************* You can also run your checks without starting the webserver and making requests. This can be useful for testing your configuration before enabling a server, @@ -167,6 +172,11 @@ Use ``-h`` to see a full list of options:: python manage.py watchman -h +X-Watchman-Version response header +********************************** + +TBD + Available checks ---------------- diff --git a/tests/test_views.py b/tests/test_views.py index 709356b..ccdccd7 100644 --- a/tests/test_views.py +++ b/tests/test_views.py @@ -182,7 +182,16 @@ def test_response_when_login_required(self): response = views.status(request) self.assertEqual(response.status_code, 200) + def test_response_version_header_missing_by_default(self): + request = RequestFactory().get('/') + response = views.status(request) + self.assertFalse(response.has_header('X-Watchman-Version')) + + @override_settings(WATCHMAN_VERSION_HEADER=True) def test_response_version_header(self): + # Have to manually reload settings here because override_settings + # happens after self.setUp() + reload_settings() request = RequestFactory().get('/') response = views.status(request) self.assertTrue(response.has_header('X-Watchman-Version')) @@ -201,7 +210,16 @@ def test_dashboard_response_code(self): response = views.dashboard(request) self.assertEqual(response.status_code, 200) + def test_response_version_header_missing_by_default(self): + request = RequestFactory().get('/') + response = views.dashboard(request) + self.assertFalse(response.has_header('X-Watchman-Version')) + + @override_settings(WATCHMAN_VERSION_HEADER=True) def test_response_version_header(self): + # Have to manually reload settings here because override_settings + # happens after self.setUp() + reload_settings() request = RequestFactory().get('/') response = views.dashboard(request) self.assertTrue(response.has_header('X-Watchman-Version')) diff --git a/watchman/settings.py b/watchman/settings.py index 421b817..dea1e1e 100644 --- a/watchman/settings.py +++ b/watchman/settings.py @@ -19,3 +19,5 @@ DEFAULT_CHECKS = DEFAULT_CHECKS + PAID_CHECKS WATCHMAN_CHECKS = getattr(settings, 'WATCHMAN_CHECKS', DEFAULT_CHECKS) + +WATCHMAN_VERSION_HEADER = getattr(settings, 'WATCHMAN_VERSION_HEADER', False) diff --git a/watchman/views.py b/watchman/views.py index 389b518..2c6e440 100644 --- a/watchman/views.py +++ b/watchman/views.py @@ -7,7 +7,7 @@ from django.utils.translation import ugettext as _ from jsonview.decorators import json_view -from watchman import __version__ +from watchman import __version__, settings from watchman.decorators import auth from watchman.utils import get_checks @@ -42,7 +42,10 @@ def status(request): if len(response) == 0: raise Http404(_('No checks found')) - return response, 200, {WATCHMAN_VERSION_HEADER: __version__} + if settings.WATCHMAN_VERSION_HEADER: + return response, 200, {WATCHMAN_VERSION_HEADER: __version__} + else: + return response @auth @@ -127,5 +130,7 @@ def dashboard(request): 'overall_status': overall_status }) - response[WATCHMAN_VERSION_HEADER] = __version__ + if settings.WATCHMAN_VERSION_HEADER: + response[WATCHMAN_VERSION_HEADER] = __version__ + return response From 8b3133c01f0fc273f638b7219caae19896214fe5 Mon Sep 17 00:00:00 2001 From: Michael Warkentin Date: Mon, 9 Nov 2015 16:07:38 -0500 Subject: [PATCH 3/6] Update readme for #36 * Add "dashboard" feature * Add version header feature --- README.rst | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/README.rst b/README.rst index 9b87d7b..1adbf84 100644 --- a/README.rst +++ b/README.rst @@ -70,10 +70,11 @@ Quickstart Features -------- -Human-friendly dashboard (new in 0.6.0) -*************************************** +Human-friendly dashboard +************************ -TBD +Visit ``http://127.0.0.1:8000/watchman/dashboard/`` to get a human-friendly HTML +representation of all of your watchman checks. Token based authentication ************************** @@ -185,7 +186,13 @@ Use ``-h`` to see a full list of options:: X-Watchman-Version response header ********************************** -TBD +Watchman can return the version of watchman which is running to help you keep +track of whether or not your sites are using an up-to-date version. This is +disabled by default to prevent any unintended information leakage for websites +without authentication. To enable, update the ``WATCHMAN_VERSION_HEADER`` +setting:: + + WATCHMAN_VERSION_HEADER = True Custom response code ******************** From a29d98710e9aba9c7ba1b335609af433d2c96cff Mon Sep 17 00:00:00 2001 From: Michael Warkentin Date: Tue, 27 Feb 2018 16:21:35 -0500 Subject: [PATCH 4/6] Various updates: * Rename setting from `WATCHMAN_VERSION_HEADER` to `EXPOSE_WATCHMAN_VERSION` * Add watchman version to dashboard * Update tests --- README.rst | 4 ++-- sample_project/sample_project/settings.py | 4 ++++ tests/test_views.py | 10 ++++++---- watchman/settings.py | 2 +- watchman/templates/watchman/dashboard.html | 16 +++++++++------- watchman/views.py | 10 ++++++---- 6 files changed, 28 insertions(+), 18 deletions(-) diff --git a/README.rst b/README.rst index 84cab4f..8e533c1 100644 --- a/README.rst +++ b/README.rst @@ -233,10 +233,10 @@ X-Watchman-Version response header Watchman can return the version of watchman which is running to help you keep track of whether or not your sites are using an up-to-date version. This is disabled by default to prevent any unintended information leakage for websites -without authentication. To enable, update the ``WATCHMAN_VERSION_HEADER`` +without authentication. To enable, update the ``EXPOSE_WATCHMAN_VERSION`` setting:: - WATCHMAN_VERSION_HEADER = True + EXPOSE_WATCHMAN_VERSION = True Custom response code ******************** diff --git a/sample_project/sample_project/settings.py b/sample_project/sample_project/settings.py index 7ff40d9..a26a9dd 100644 --- a/sample_project/sample_project/settings.py +++ b/sample_project/sample_project/settings.py @@ -27,6 +27,10 @@ ALLOWED_HOSTS = [] +# Watchman configuration + +EXPOSE_WATCHMAN_VERSION = True + # Application definition diff --git a/tests/test_views.py b/tests/test_views.py index fb4c441..1d682d1 100644 --- a/tests/test_views.py +++ b/tests/test_views.py @@ -259,7 +259,7 @@ def test_response_version_header_missing_by_default(self): response = views.status(request) self.assertFalse(response.has_header('X-Watchman-Version')) - @override_settings(WATCHMAN_VERSION_HEADER=True) + @override_settings(EXPOSE_WATCHMAN_VERSION=True) def test_response_version_header(self): # Have to manually reload settings here because override_settings # happens after self.setUp() @@ -356,19 +356,21 @@ def test_dashboard_response_code(self): response = views.dashboard(request) self.assertEqual(response.status_code, 200) - def test_response_version_header_missing_by_default(self): + def test_response_version_header_and_html_missing_by_default(self): request = RequestFactory().get('/') response = views.dashboard(request) self.assertFalse(response.has_header('X-Watchman-Version')) + self.assertNotIn('Watchman version:', response.content) - @override_settings(WATCHMAN_VERSION_HEADER=True) - def test_response_version_header(self): + @override_settings(EXPOSE_WATCHMAN_VERSION=True) + def test_response_has_version_header_and_html(self): # Have to manually reload settings here because override_settings # happens after self.setUp() reload_settings() request = RequestFactory().get('/') response = views.dashboard(request) self.assertTrue(response.has_header('X-Watchman-Version')) + self.assertIn('Watchman version:', response.content) class TestPing(unittest.TestCase): diff --git a/watchman/settings.py b/watchman/settings.py index 4abe2f9..576e7df 100644 --- a/watchman/settings.py +++ b/watchman/settings.py @@ -31,4 +31,4 @@ WATCHMAN_CHECKS = getattr(settings, 'WATCHMAN_CHECKS', DEFAULT_CHECKS) -WATCHMAN_VERSION_HEADER = getattr(settings, 'WATCHMAN_VERSION_HEADER', False) +EXPOSE_WATCHMAN_VERSION = getattr(settings, 'EXPOSE_WATCHMAN_VERSION', False) diff --git a/watchman/templates/watchman/dashboard.html b/watchman/templates/watchman/dashboard.html index 8b87362..881a215 100644 --- a/watchman/templates/watchman/dashboard.html +++ b/watchman/templates/watchman/dashboard.html @@ -33,12 +33,6 @@

-
-
-
-
-
-
@@ -82,7 +76,6 @@

{% endblock %}{# status_content #} - {% block error_content %} {% for type_name, type in checks.items %} {% for status in type.statuses %} @@ -108,4 +101,13 @@

{{ status.error }}

{% endfor %}{# for status in type.statuses #} {% endfor %}{# for type_name, type in checks.items #} {% endblock %}{# error_content #} + +{% if expose_watchman_version %} +
+
+

Watchman version: {{ watchman_version }}

+
+
+{% endif %} + {% endblock %}{# content #} diff --git a/watchman/views.py b/watchman/views.py index 4281bd5..717e891 100644 --- a/watchman/views.py +++ b/watchman/views.py @@ -82,11 +82,11 @@ def status(request): if not checks: raise Http404(_('No checks found')) - + http_code = 200 if ok else settings.WATCHMAN_ERROR_CODE response_headers = {} - if settings.WATCHMAN_VERSION_HEADER: + if settings.EXPOSE_WATCHMAN_VERSION: response_headers[WATCHMAN_VERSION_HEADER] = __version__ return checks, http_code, response_headers @@ -164,10 +164,12 @@ def dashboard(request): response = render(request, 'watchman/dashboard.html', { 'checks': expanded_checks, - 'overall_status': overall_status + 'overall_status': overall_status, + 'watchman_version': __version__, + 'expose_watchman_version': settings.EXPOSE_WATCHMAN_VERSION, }) - if settings.WATCHMAN_VERSION_HEADER: + if settings.EXPOSE_WATCHMAN_VERSION: response[WATCHMAN_VERSION_HEADER] = __version__ return response From 165dbb6d3957d024312c2834d1782d656a3e4145 Mon Sep 17 00:00:00 2001 From: Michael Warkentin Date: Tue, 27 Feb 2018 16:44:06 -0500 Subject: [PATCH 5/6] Update history.rst --- HISTORY.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/HISTORY.rst b/HISTORY.rst index e8d98c4..eecf30e 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -9,6 +9,7 @@ History * [`#114 `_] Add "bare" status view (@jamesmallen) * [`#115 `_] Adds ``WATCHMAN_DISABLE_APM`` option (@xfxf) +* [`#63 `_] Disable watchman version output by default, add ``EXPOSE_WATCHMAN_VERSION`` setting (@mwarkentin) 0.14.0 (2018-01-09) ------------------- From 6738fde568a935f4b682da66a315ffaf88b5d38d Mon Sep 17 00:00:00 2001 From: Michael Warkentin Date: Tue, 27 Feb 2018 16:48:52 -0500 Subject: [PATCH 6/6] Fix py3 tests? --- tests/test_views.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_views.py b/tests/test_views.py index 1d682d1..41c33b0 100644 --- a/tests/test_views.py +++ b/tests/test_views.py @@ -360,7 +360,7 @@ def test_response_version_header_and_html_missing_by_default(self): request = RequestFactory().get('/') response = views.dashboard(request) self.assertFalse(response.has_header('X-Watchman-Version')) - self.assertNotIn('Watchman version:', response.content) + self.assertNotIn('Watchman version:', response.content.decode()) @override_settings(EXPOSE_WATCHMAN_VERSION=True) def test_response_has_version_header_and_html(self): @@ -370,7 +370,7 @@ def test_response_has_version_header_and_html(self): request = RequestFactory().get('/') response = views.dashboard(request) self.assertTrue(response.has_header('X-Watchman-Version')) - self.assertIn('Watchman version:', response.content) + self.assertIn('Watchman version:', response.content.decode()) class TestPing(unittest.TestCase):