{ "info": { "author": "Jamie Painter", "author_email": "jamie.painter@rackspace.com", "bugtrack_url": null, "classifiers": [ "Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", "License :: OSI Approved :: Apache Software License", "Operating System :: OS Independent", "Programming Language :: Python", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3.3", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Topic :: Security", "Topic :: Software Development :: Libraries :: Python Modules" ], "description": "Stoplight [![Build Status](https://api.travis-ci.org/painterjd/stoplight.png)](https://travis-ci.org/painterjd/stoplight)\n=========\n\nStoplight -- An Input Validation Framework for Python\n\nWhy Validate User Input?\n------------------------\nInput validation is the most basic, first step to adding security to any application that accepts untrusted user input. Volumes have been written on the subject, but the gist is to reduce the attack surface of your application by sanitizing all user input so that it meets a very tight set of criteria needed just for the application, and nothing more. The most common type of attack prevented by input validation is [Code Injection](http://en.wikipedia.org/wiki/Code_injection). \n\nA great number of user input vulnerabilities (i.e. [Shellshock](http://en.wikipedia.org/wiki/Shellshock_%28software_bug%29)) could be avoided almost entirely if user input were sanitized. \n\nExample\n-------\nLet's say that our application is accepting a US Phone Number only. In that case, our application should only need to accept NNN-NNN-NNNN where N is a digit from 0-9. If the user passes anything else, we can throw it away. \n\nThe problem that stoplight aims to address is the intermixing of input validation logic with application logic (in particular with RESTful/REST-like API frameworks). Sometimes they are inseparable, but in almost all cases, they are not. So let's look at the above-mentioned phone number example.\n\nAlmost all of today's API frameworks work in a similar manner -- you declare a function that defines an endpoint and the framework calls the function when an HTTP request comes in from a client.\n\n```python\ndef post(self, account_id, phone_number):\n if not is_valid_account_id(account_id):\n handle_bad_account_id() \n\n if not is_valid_phone_number(phone_number):\n handle_bad_phone_number() \n\n model.set_phone_number(account_id, phone_number)\n```\n\nThis is a simple, contrived example. Typically things start getting much more complex. For certain HTTP verbs, a user will want different responses returned. There may be other things to accomplish as well.\n\nIn Stoplight, we would validate the input like so:\n\n```python\n@validate(account_id=AccountIdRule, phone_number=PhoneNumberRule)\ndef post(self, account_id, phone_number):\n model.set_phone_number(account_id, phone_number)\n```\n\nThis allows us to effectively separate our \"input validation\" logic from \"business logic\". \n\nRules are fairly simple to create. For example, here is how one might declare the PhoneNumberRule\n\n```python\nPhoneNumberRule = Rule(is_validate_phone_number(), lambda: abort(404))\n```\n\nAnd of course, that leads us to is_valid_phone_number() declaration.\n\n```python\n@validation_function\ndef is_valid_phone_number(candidate):\n if (phone_regex.match(candidate) is None):\n msg = 'Not a valid phone number: {0}'\n msg = msg.format(candidate)\n raise ValidationFailed(msg)\n```\n\nThis allows us to separate validation from transports (imagine an API where you must support HTTP and ZMQ, for example). It also allows us to centralize validation logic and write separate tests for the validation rules.\n\nOther Features:\n---------------\n * Ensures that all parameters (positional and keyword) are all validated. If they are not validated, a ValidationProgrammingError is raised.\n * Allows validation of globally-scoped values (think items in thread local storage, as is done in the Pecan framework)\n\nCaveats (TODO):\n---------------\n * Overhead. Such is the nature of Python with decorators. \n\nDocumentation:\n--------------\nThe project is being documented at readthedocs [here](http://stoplight.readthedocs.org/en/latest/). For other examples, please see the unit tests. \n\n\n", "description_content_type": "", "docs_url": null, "download_url": "", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://pypi.org/project/stoplight/", "keywords": "", "license": "Apache License 2.0", "maintainer": "Benjamen Meyer", "maintainer_email": "bm_witness@yahoo.com", "name": "stoplight", "package_url": "https://pypi.org/project/stoplight/", "platform": "OS Independent", "project_url": "https://pypi.org/project/stoplight/", "project_urls": { "Documentation": "http://stoplight.readthedocs.io/en/latest/", "Homepage": "https://pypi.org/project/stoplight/", "Source": "https://github.com/painterjd/stoplight", "Tracker": "https://github.com/painterjd/stoplight/issues" }, "release_url": "https://pypi.org/project/stoplight/1.4.1/", "requires_dist": [ "six" ], "requires_python": "", "summary": "Input validation framework for Python", "version": "1.4.1" }, "last_serial": 4823021, "releases": { "0.9": [ { "comment_text": "", "digests": { "md5": "7f9ac5296d1f6e24ceb80cd207f5080e", "sha256": "b44240642ffb58b1b8eb3d914f698e6b9b6f3ef4b690b43be8dfca0e5380e407" }, "downloads": -1, "filename": "stoplight-0.9.tar.gz", "has_sig": false, "md5_digest": "7f9ac5296d1f6e24ceb80cd207f5080e", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 4516, "upload_time": "2014-09-16T21:34:05", "url": "https://files.pythonhosted.org/packages/ec/e3/a413b78560ce9cd4d53b6f5e0f782a8778fc4f78422dad39f39898e07633/stoplight-0.9.tar.gz" }, { "comment_text": "", "digests": { "md5": "3742a6ca5c0bcc0492fbc1e8d7d3ed4f", "sha256": "6e8afa26734ec3d627cb13d1ccc2a21de137d14d73d56bffd58f291ff001a313" }, "downloads": -1, "filename": "stoplight-1.0.1.tar.gz", "has_sig": false, "md5_digest": "3742a6ca5c0bcc0492fbc1e8d7d3ed4f", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 4503, "upload_time": "2014-10-10T20:35:49", "url": "https://files.pythonhosted.org/packages/a7/ab/2559f1c4649a701db306fcccd47b476ea46b845f5c251e73ed6e79fb7ba9/stoplight-1.0.1.tar.gz" }, { "comment_text": "", "digests": { "md5": "adc8ac1b623cf1cb8a29b0274b0b4251", "sha256": "af00336a4db07f2bf49addb3ca906bd3f53e958e819c010f2c9a771cbf80ebaf" }, "downloads": -1, "filename": "stoplight-1.0.tar.gz", "has_sig": false, "md5_digest": "adc8ac1b623cf1cb8a29b0274b0b4251", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 4515, "upload_time": "2014-10-06T17:49:06", "url": "https://files.pythonhosted.org/packages/b0/ca/9dbc59592434d811a33bf9587cc987e2455da01c87d7a71874fc82ca87ed/stoplight-1.0.tar.gz" }, { "comment_text": "", "digests": { "md5": "7b87bdfe76a050d2c03703316d90f779", "sha256": "700d80fd1334aa0c4258dd4875136b081b265e0cb623468f24b0c271ef23905f" }, "downloads": -1, "filename": "stoplight-1.1.0.tar.gz", "has_sig": false, "md5_digest": "7b87bdfe76a050d2c03703316d90f779", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 4592, "upload_time": "2014-10-31T18:03:50", "url": "https://files.pythonhosted.org/packages/6f/eb/6e17f1f93695ab561d33c56e4721d7870941976b367cb4d6f7fc09b8bc4d/stoplight-1.1.0.tar.gz" } ], "1.2.0": [ { "comment_text": "", "digests": { "md5": "3cbfd9cfeb34fbcac9920ffcf5a59cef", "sha256": "bbc97f66dce2f21b02d521c0125765603e6d90ecd84395d08755d681ffba1ef4" }, "downloads": -1, "filename": "stoplight-1.2.0.tar.gz", "has_sig": false, "md5_digest": "3cbfd9cfeb34fbcac9920ffcf5a59cef", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 8968, "upload_time": "2015-03-12T13:05:20", "url": "https://files.pythonhosted.org/packages/47/ca/09995427632f5104d0469f4b283da59befe1f48389d02683e6554a1d194f/stoplight-1.2.0.tar.gz" } ], "1.3.0": [ { "comment_text": "", "digests": { "md5": "18245380ebbf1e81c03405d1e9617981", "sha256": "9c1307f7ba81b30e2b6c9e45bd73c4e458474d50d38993a120a18ac96b55868b" }, "downloads": -1, "filename": "stoplight-1.3.0-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "18245380ebbf1e81c03405d1e9617981", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 15378, "upload_time": "2018-04-18T19:11:19", "url": "https://files.pythonhosted.org/packages/57/d3/ca99746aee803a37bdec03f00b00c909615f2f8b6b39c59bc8986bb039b4/stoplight-1.3.0-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "309721e1625a49206e399d29ba4abf96", "sha256": "f9e3d8b48a513b8aef78725e768134edc65d532e4039afc2223b4daf0884b23c" }, "downloads": -1, "filename": "stoplight-1.3.0.tar.gz", "has_sig": false, "md5_digest": "309721e1625a49206e399d29ba4abf96", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 11529, "upload_time": "2018-04-18T19:11:23", "url": "https://files.pythonhosted.org/packages/66/3f/9892c3269cda836b8c68265977faf9925b7eb769aca1ffb6942b35721d98/stoplight-1.3.0.tar.gz" } ], "1.4.1": [ { "comment_text": "", "digests": { "md5": "16801b3e60591109dd2cd0adc7e832a7", "sha256": "51797962ad4223baac78f0ce762954c9a09e29529285ee82633ce36e5875f7ae" }, "downloads": -1, "filename": "stoplight-1.4.1-py2-none-any.whl", "has_sig": false, "md5_digest": "16801b3e60591109dd2cd0adc7e832a7", "packagetype": "bdist_wheel", "python_version": "py2", "requires_python": null, "size": 15677, "upload_time": "2019-02-15T03:14:48", "url": "https://files.pythonhosted.org/packages/0c/34/6480bafa08c519fb767b51ddf55f095d1ee5771e877cdebb508390627f64/stoplight-1.4.1-py2-none-any.whl" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "16801b3e60591109dd2cd0adc7e832a7", "sha256": "51797962ad4223baac78f0ce762954c9a09e29529285ee82633ce36e5875f7ae" }, "downloads": -1, "filename": "stoplight-1.4.1-py2-none-any.whl", "has_sig": false, "md5_digest": "16801b3e60591109dd2cd0adc7e832a7", "packagetype": "bdist_wheel", "python_version": "py2", "requires_python": null, "size": 15677, "upload_time": "2019-02-15T03:14:48", "url": "https://files.pythonhosted.org/packages/0c/34/6480bafa08c519fb767b51ddf55f095d1ee5771e877cdebb508390627f64/stoplight-1.4.1-py2-none-any.whl" } ] }