├── .editorconfig ├── .github └── ISSUE_TEMPLATE.md ├── .gitignore ├── .travis.yml ├── HISTORY.rst ├── LICENSE ├── MANIFEST.in ├── README.md ├── insecure.png ├── pyup_django ├── __init__.py ├── admin.py ├── apps.py ├── models.py ├── settings.py └── templates │ ├── admin │ └── base.html │ └── insecure.html ├── requirements_dev.txt ├── safety-django.jpg ├── setup.cfg ├── setup.py ├── tests ├── __init__.py └── test_safety_django.py └── tox.ini /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | 3 | root = true 4 | 5 | [*] 6 | indent_style = space 7 | indent_size = 4 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | charset = utf-8 11 | end_of_line = lf 12 | 13 | [*.bat] 14 | indent_style = tab 15 | end_of_line = crlf 16 | 17 | [LICENSE] 18 | insert_final_newline = false 19 | 20 | [Makefile] 21 | indent_style = tab 22 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | * pyup-django version: 2 | * Django version: 3 | * Python version: 4 | * Operating System: 5 | 6 | ### Description 7 | 8 | Describe what you were trying to get done. 9 | Tell us what happened, what went wrong, and what you expected to happen. 10 | 11 | ### What I Did 12 | 13 | 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | 27 | # PyInstaller 28 | # Usually these files are written by a python script from a template 29 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 30 | *.manifest 31 | *.spec 32 | 33 | # Installer logs 34 | pip-log.txt 35 | pip-delete-this-directory.txt 36 | 37 | # Unit test / coverage reports 38 | htmlcov/ 39 | .tox/ 40 | .coverage 41 | .coverage.* 42 | .cache 43 | nosetests.xml 44 | coverage.xml 45 | *,cover 46 | .hypothesis/ 47 | 48 | # Translations 49 | *.mo 50 | *.pot 51 | 52 | # Django stuff: 53 | *.log 54 | 55 | # Sphinx documentation 56 | docs/_build/ 57 | 58 | # PyBuilder 59 | target/ 60 | 61 | # pyenv python configuration file 62 | .python-version 63 | test_project/ 64 | travis_pypi_setup.py 65 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # This file was autogenerated and will overwrite each time you run travis_pypi_setup.py 2 | deploy: 3 | true: 4 | condition: $TOXENV == py27 5 | repo: pyupio/pyup-django 6 | tags: true 7 | distributions: sdist bdist_wheel 8 | password: 9 | secure: YOMMgNF5AO5ctb7XJYAFIuz3CPxyv1ayr+4Bd++KZmjQTN/OGQ5cfmSip3VQo4EJj9y5SJ97siARqpfjWTxA1OhqqAcHg7MLrFPpViCF2jRoWrd3Mn+mObQjeV5bX9nBPCs4j4rX1Jl3j8IRO7Dzefi6fO750pnp7YaI+VZvJNw0A29nf9q/AQ8JQo5mywr9WdiaEnDWdKlnoIRyHM1ZFa/wxHxfEVokXvWO1R21xawUht+8t+QxdjkC5PCO5FYCQDxr6nM9WJF19XtKAx9I5uFtp0oiM63tHkkOxyZVnavojINmfNyb78GkKfnRTZcKN7qfSWdDGnjoruVhfwvTQg9HMWA+6o6CS8ueZsrkK3/kJGeRtHnJ4t37Vq3l+0gZ1qZUENaIli7s4emgGiqJONKgYnMf9vAB12IgxXJUYrglUx9sR2MxiGtJuW9JtJas6VjWK6UE14TorGS+ZE5+lJ/Y3rs6DgxxJoV5QcfaqGFOaiqcLvXjkAtOAg86VsreMBKu5t6Au+eEaWI+HQpFFA/tvCWwmD6Wel9zjEurBQnYKYCrhjZKw4QNAYF7RtwU8v/kaB3N/us4AsjK6F3bvjsZyTSYO+rpsLW3mC2wcVUEJ90c4fdk5b5R+J1Glx5l2MuI4qSo1zWGHtDQOWeicA0NM0wIM+aWbKwWdbnpjas= 10 | provider: pypi 11 | user: jayfk 12 | env: 13 | - TOXENV=py35 14 | - TOXENV=py34 15 | - TOXENV=py33 16 | - TOXENV=py27 17 | - TOXENV=py26 18 | - TOXENV=pypy 19 | install: pip install -U tox 20 | language: python 21 | python: 3.5 22 | script: tox -e ${TOXENV} 23 | -------------------------------------------------------------------------------- /HISTORY.rst: -------------------------------------------------------------------------------- 1 | ======= 2 | History 3 | ======= 4 | 5 | 0.4.0 (2017-5-15) 6 | ------------------ 7 | 8 | * Added support for Django 1.11 9 | 10 | 0.3.0 (2016-2-2) 11 | ------------------ 12 | 13 | * Continue to check if the release has been flagged as insecure. 14 | 15 | 0.2.1 (2016-2-2) 16 | ------------------ 17 | 18 | * Now correctly gets the API URL from settings 19 | 20 | 0.2.0 (2016-2-2) 21 | ------------------ 22 | 23 | * Renamed the package to pyup-django 24 | * Removed safety dependency 25 | 26 | 0.1.1 (2016-11-9) 27 | ------------------ 28 | 29 | * Fixed packaging bug (@emilkjer) 30 | 31 | 0.1.0 (2016-10-28) 32 | ------------------ 33 | 34 | * First release on PyPI. 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | MIT License 3 | 4 | Copyright (c) 2016, pyup.io 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 7 | 8 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 9 | 10 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 11 | 12 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | 2 | include HISTORY.rst 3 | include LICENSE 4 | include README.md 5 | 6 | recursive-include tests * 7 | recursive-exclude * __pycache__ 8 | recursive-exclude * *.py[co] 9 | 10 | recursive-include docs *.rst conf.py Makefile make.bat *.jpg *.png *.gif 11 | recursive-include pyup_django/templates * 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![safety](https://raw.githubusercontent.com/pyupio/pyup-django/master/safety-django.jpg)](https://pyup.io/safety/) 2 | 3 | 4 | [![PyPi](https://img.shields.io/pypi/v/pyup-django.svg)](https://pypi.python.org/pypi/pyup-django) 5 | [![Travis](https://img.shields.io/travis/pyupio/pyup-django.svg)](https://travis-ci.org/pyupio/pyup-django) 6 | 7 | # About 8 | 9 | Displays a red warning banner if you are running an insecure Django release. 10 | 11 | ![insecure](insecure.png) 12 | 13 | # Installation 14 | 15 | Install `pyup-django` with pip: 16 | 17 | ``` 18 | pip install pyup-django 19 | ``` 20 | 21 | and add it to your `INSTALLED_APPS`, before `django.contrib.admin` 22 | 23 | ``` 24 | INSTALLED_APPS = [ 25 | 'pyup_django', 26 | 'django.contrib.admin', 27 | ] 28 | ``` 29 | 30 | # How does it work? 31 | 32 | `pyup-django` extends the admin base template and checks [https://pyup.io/api/v1/insecure/django/](https://pyup.io/api/v1/insecure/django/) 33 | if the currently installed Django release is insecure. 34 | 35 | Requests to the API are cached for 24 hours and won't leak any sensitive 36 | information other than the servers IP address. 37 | 38 | 39 | # Support 40 | 41 | If you are using `pyup-django` in one of your projects, please consider getting a paid 42 | [pyup.io](https://pyup.io) account. This is what makes projects like this possible. 43 | -------------------------------------------------------------------------------- /insecure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyupio/pyup-django/4a3ec4e1b6195c03610caa13320b1a29298c174d/insecure.png -------------------------------------------------------------------------------- /pyup_django/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | __author__ = """pyup.io""" 4 | __email__ = 'support@pyup.io' 5 | __version__ = '0.4.0' 6 | -------------------------------------------------------------------------------- /pyup_django/admin.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import absolute_import, print_function, unicode_literals 3 | import json 4 | from django.contrib import admin 5 | from django.conf.urls import url 6 | from django.http import HttpResponse 7 | from django.template.loader import render_to_string 8 | from django import VERSION 9 | import time 10 | from .models import Status 11 | from .settings import settings 12 | import requests 13 | from packaging.specifiers import SpecifierSet 14 | 15 | 16 | class StatusAdmin(admin.ModelAdmin): 17 | 18 | def has_module_permission(self, request): 19 | # don't display the app on the index site 20 | return False 21 | 22 | def get_urls(self): 23 | urls = super(StatusAdmin, self).get_urls() 24 | return [ 25 | url(r'^pyup/$', self.admin_site.admin_view(self.registration_status_view), 26 | name="pyup"), 27 | ] + urls 28 | 29 | def is_insecure(self, version): 30 | r = requests.get(settings.API_URL) 31 | if r.status_code == 200: 32 | for spec_str in r.json()['insecure']: 33 | spec = SpecifierSet(spec_str) 34 | if spec.contains(version): 35 | return True 36 | return False 37 | 38 | def registration_status_view(self, request): 39 | request.current_app = self.admin_site.name 40 | 41 | version = "{}.{}.{}".format(VERSION[0], VERSION[1], VERSION[2]) 42 | 43 | insecure = self.is_insecure(version) 44 | 45 | data = { 46 | "django_version": version, 47 | "insecure": insecure, 48 | } 49 | 50 | template = "".join(render_to_string("insecure.html", data).splitlines()) 51 | 52 | data["template"] = template 53 | data["run_again_at"] = time.time() + 60 * 60 * 24 54 | request.session["pyup_django"] = data 55 | return HttpResponse(json.dumps(data), content_type="application/json") 56 | 57 | admin.site.register(Status, StatusAdmin) 58 | -------------------------------------------------------------------------------- /pyup_django/apps.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from django.apps import AppConfig 3 | 4 | 5 | class DjangoUpdaterAppConfig(AppConfig): 6 | 7 | name = 'pyup_django' 8 | verbose_name = "pyup.io for Django" 9 | -------------------------------------------------------------------------------- /pyup_django/models.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from django.db import models 3 | 4 | 5 | # this module contains an empty model to use Django's ModelAdmin to add a view to the admin 6 | # without too much hassle 7 | class Status(models.Model): 8 | pass 9 | -------------------------------------------------------------------------------- /pyup_django/settings.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | class Settings(object): 3 | 4 | API_URL = "https://pyup.io/api/v1/insecure/django/" 5 | 6 | settings = Settings() 7 | -------------------------------------------------------------------------------- /pyup_django/templates/admin/base.html: -------------------------------------------------------------------------------- 1 | {% extends "admin/base.html" %} 2 | {% block extrastyle %} 3 | {{ block.super }} 4 | 35 | {% endblock %} 36 | {% block extrahead %} 37 | {{ block.super }} 38 | 84 | {% endblock %} 85 | -------------------------------------------------------------------------------- /pyup_django/templates/insecure.html: -------------------------------------------------------------------------------- 1 | {% if insecure %} 2 |
3 | Django {{ django_version }} has known security vulnerabilities. 4 |
5 | {% endif %} 6 | -------------------------------------------------------------------------------- /requirements_dev.txt: -------------------------------------------------------------------------------- 1 | pip==8.1.2 2 | wheel==0.29.0 3 | flake8==2.6.0 4 | tox==2.3.1 5 | coverage==4.1 6 | Sphinx==1.4.8 7 | cryptography==1.4 8 | PyYAML==3.11 9 | 10 | -------------------------------------------------------------------------------- /safety-django.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyupio/pyup-django/4a3ec4e1b6195c03610caa13320b1a29298c174d/safety-django.jpg -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [bumpversion] 2 | current_version = 0.1.0 3 | commit = True 4 | tag = True 5 | 6 | [bumpversion:file:setup.py] 7 | search = version='{current_version}' 8 | replace = version='{new_version}' 9 | 10 | [bumpversion:file:pyup_django/__init__.py] 11 | search = __version__ = '{current_version}' 12 | replace = __version__ = '{new_version}' 13 | 14 | [bdist_wheel] 15 | universal = 1 16 | 17 | [flake8] 18 | exclude = docs 19 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | from setuptools import setup 5 | 6 | with open('README.md') as readme_file: 7 | readme = readme_file.read() 8 | 9 | requirements = [ 10 | 'packaging', 11 | 'requests' 12 | ] 13 | 14 | test_requirements = [ 15 | # TODO: put package test requirements here 16 | ] 17 | 18 | setup( 19 | name='pyup-django', 20 | version='0.4.0', 21 | description="pyup-django checks your installed dependencies for known security vulnerabilities and displays them in the admin area.", 22 | long_description=readme, 23 | author="pyup.io", 24 | author_email='support@pyup.io', 25 | url='https://github.com/pyupio/pyup-django', 26 | packages=[ 27 | 'pyup_django', 28 | ], 29 | package_dir={'pyup_django': 30 | 'pyup_django'}, 31 | include_package_data=True, 32 | install_requires=requirements, 33 | license="MIT license", 34 | zip_safe=False, 35 | keywords='pyup_django', 36 | classifiers=[ 37 | 'Development Status :: 2 - Pre-Alpha', 38 | 'Intended Audience :: Developers', 39 | 'License :: OSI Approved :: MIT License', 40 | 'Natural Language :: English', 41 | "Programming Language :: Python :: 2", 42 | 'Programming Language :: Python :: 2.6', 43 | 'Programming Language :: Python :: 2.7', 44 | 'Programming Language :: Python :: 3', 45 | 'Programming Language :: Python :: 3.3', 46 | 'Programming Language :: Python :: 3.4', 47 | 'Programming Language :: Python :: 3.5', 48 | ], 49 | test_suite='tests', 50 | tests_require=test_requirements 51 | ) 52 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | -------------------------------------------------------------------------------- /tests/test_safety_django.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | test_safety_django 6 | ---------------------------------- 7 | 8 | Tests for `safety_django` module. 9 | """ 10 | 11 | 12 | import sys 13 | import unittest 14 | 15 | 16 | class TestSafety_django(unittest.TestCase): 17 | 18 | def setUp(self): 19 | pass 20 | 21 | def tearDown(self): 22 | pass 23 | 24 | def test_000_something(self): 25 | pass 26 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | envlist = py26, py27, py33, py34, py35, flake8 3 | 4 | [testenv:flake8] 5 | basepython=python 6 | deps=flake8 7 | commands=flake8 pyup_django 8 | 9 | [testenv] 10 | setenv = 11 | PYTHONPATH = {toxinidir}:{toxinidir}/pyup_django 12 | 13 | commands = python setup.py test 14 | 15 | ; If you want to make tox run the tests with the same versions, create a 16 | ; requirements.txt with the pinned versions and uncomment the following lines: 17 | ; deps = 18 | ; -r{toxinidir}/requirements.txt 19 | --------------------------------------------------------------------------------