From 77e894d958c102593edacb6f0102c7892c9ad0be Mon Sep 17 00:00:00 2001 From: Brad Ison Date: Thu, 8 Feb 2018 13:20:56 -0500 Subject: [PATCH 1/6] Remove old cnr from requirements-nover.txt --- requirements-nover.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/requirements-nover.txt b/requirements-nover.txt index c7cf76009..9fc296f29 100644 --- a/requirements-nover.txt +++ b/requirements-nover.txt @@ -77,5 +77,4 @@ tzlocal xhtml2pdf recaptcha2 mockredispy -cnr yapf From f0d6b697298862cdba3d4843e550c32a50c75302 Mon Sep 17 00:00:00 2001 From: Brad Ison Date: Thu, 8 Feb 2018 13:37:42 -0500 Subject: [PATCH 2/6] Pin cnr_server version --- requirements-nover.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-nover.txt b/requirements-nover.txt index 9fc296f29..5226a7d01 100644 --- a/requirements-nover.txt +++ b/requirements-nover.txt @@ -8,7 +8,7 @@ -e git+https://github.com/coreos/pyapi-gitlab.git@timeout#egg=pyapi-gitlab -e git+https://github.com/coreos/resumablehashlib.git#egg=resumablehashlib -e git+https://github.com/jepcastelein/marketo-rest-python.git#egg=marketorestpython --e git+https://github.com/app-registry/appr-server.git#egg=cnr-server +-e git+https://github.com/app-registry/appr-server.git@c2ef3b88afe926a92ef5f2e11e7d4a259e286a17#egg=cnr_server # naming has changed APScheduler==3.0.5 Flask-Login Flask-Mail From 5da8744ddfb2e241cbbeedd57cb69a19a2038d3b Mon Sep 17 00:00:00 2001 From: Brad Ison Date: Thu, 8 Feb 2018 15:52:37 -0500 Subject: [PATCH 3/6] Reject JWTs with future issued-at times PyJWT stopped doing this in 1.5.0 because it's not part of the spec, and there are legitimate reasons to issue future tokens. We still want to reject these though as we don't have that need. --- util/security/jwtutil.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/util/security/jwtutil.py b/util/security/jwtutil.py index dc190838f..7e2bc6cee 100644 --- a/util/security/jwtutil.py +++ b/util/security/jwtutil.py @@ -1,5 +1,6 @@ import re +from calendar import timegm from datetime import datetime, timedelta from jwt import PyJWT from jwt.exceptions import ( @@ -42,6 +43,9 @@ class StrictJWT(PyJWT): # Do all of the other checks super(StrictJWT, self)._validate_claims(payload, options, audience, issuer, leeway, **kwargs) + now = timegm(datetime.utcnow().utctimetuple()) + self._reject_future_iat(payload, now, leeway) + if 'exp' in payload and options.get('exp_max_s') is not None: # Validate that the expiration was not more than exp_max_s seconds after the issue time # or in the absense of an issue time, more than exp_max_s in the future from now @@ -58,6 +62,16 @@ class StrictJWT(PyJWT): raise InvalidTokenError('Token was signed for more than %s seconds from %s', max_signed_s, start_time) + def _reject_future_iat(self, payload, now, leeway): + try: + iat = int(payload['iat']) + except ValueError: + raise DecodeError('Issued At claim (iat) must be an integer.') + + if iat > (now + leeway): + raise InvalidIssuedAtError('Issued At claim (iat) cannot be in' + ' the future.') + def exp_max_s_option(max_exp_s): return { From d1ba2dcfc33139fe0c29e1e2af7c114fd0ca04db Mon Sep 17 00:00:00 2001 From: Brad Ison Date: Thu, 8 Feb 2018 15:54:54 -0500 Subject: [PATCH 4/6] Add labels to test cases for invalid JWTs --- auth/test/test_registry_jwt.py | 43 +++++++++++++++++----------------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/auth/test/test_registry_jwt.py b/auth/test/test_registry_jwt.py index 12000a95a..78ac029bc 100644 --- a/auth/test/test_registry_jwt.py +++ b/auth/test/test_registry_jwt.py @@ -110,30 +110,31 @@ def test_token_with_access(access, initialized_db): @pytest.mark.parametrize('token', [ - (_token( + pytest.param(_token( _token_data(access=[{ 'toipe': 'repository', 'namesies': 'somens/somerepo', - 'akshuns': ['pull', 'push', '*']}]))), - (_token(_token_data(audience='someotherapp'))), - (_token(_delete_field(_token_data(), 'aud'))), - (_token(_token_data(nbf=int(time.time()) + 600))), - (_token(_delete_field(_token_data(), 'nbf'))), - (_token(_token_data(iat=int(time.time()) + 600))), - (_token(_delete_field(_token_data(), 'iat'))), - (_token(_token_data(exp=int(time.time()) + MAX_SIGNED_S * 2))), - (_token(_token_data(exp=int(time.time()) - 60))), - (_token(_delete_field(_token_data(), 'exp'))), - (_token(_delete_field(_token_data(), 'sub'))), - (_token(_token_data(iss='badissuer'))), - (_token(_delete_field(_token_data(), 'iss'))), - (_token(_token_data(), skip_header=True)), - (_token(_token_data(), key_id='someunknownkey')), - (_token(_token_data(), key_id='kid7')), - (_token(_token_data(), alg='none', private_key=None)), - ('some random token'), - ('Bearer: sometokenhere'), - ('\nBearer: dGVzdA'),]) + 'akshuns': ['pull', 'push', '*']}])), id='bad access'), + pytest.param(_token(_token_data(audience='someotherapp')), id='bad aud'), + pytest.param(_token(_delete_field(_token_data(), 'aud')), id='no aud'), + pytest.param(_token(_token_data(nbf=int(time.time()) + 600)), id='future nbf'), + pytest.param(_token(_delete_field(_token_data(), 'nbf')), id='no nbf'), + pytest.param(_token(_token_data(iat=int(time.time()) + 600)), id='future iat'), + pytest.param(_token(_delete_field(_token_data(), 'iat')), id='no iat'), + pytest.param(_token(_token_data(exp=int(time.time()) + MAX_SIGNED_S * 2)), id='exp too long'), + pytest.param(_token(_token_data(exp=int(time.time()) - 60)), id='expired'), + pytest.param(_token(_delete_field(_token_data(), 'exp')), id='no exp'), + pytest.param(_token(_delete_field(_token_data(), 'sub')), id='no sub'), + pytest.param(_token(_token_data(iss='badissuer')), id='bad iss'), + pytest.param(_token(_delete_field(_token_data(), 'iss')), id='no iss'), + pytest.param(_token(_token_data(), skip_header=True), id='no header'), + pytest.param(_token(_token_data(), key_id='someunknownkey'), id='bad key'), + pytest.param(_token(_token_data(), key_id='kid7'), id='bad key :: kid7'), + pytest.param(_token(_token_data(), alg='none', private_key=None), id='none alg'), + pytest.param('some random token', id='random token'), + pytest.param('Bearer: sometokenhere', id='extra bearer'), + pytest.param('\nBearer: dGVzdA', id='leading newline'), +]) def test_invalid_jwt(token, initialized_db): with pytest.raises(InvalidJWTException): _parse_token(token) From d4ee938a5dff47b076327fbd40ca6a2f25cae1ea Mon Sep 17 00:00:00 2001 From: Brad Ison Date: Thu, 8 Feb 2018 16:43:40 -0500 Subject: [PATCH 5/6] Fix license test in Dockerfile --- Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 5a1bf0a1e..f319fe048 100644 --- a/Dockerfile +++ b/Dockerfile @@ -10,7 +10,7 @@ COPY requirements.txt requirements-tests.txt ./ # Due to the following bug, pip results must be piped to a file before grepping: # https://github.com/pypa/pip/pull/3304 RUN cat requirements.txt | grep -v "^-e" | awk -F'==' '{print $1}' | xargs venv/bin/pip --disable-pip-version-check show > pipinfo.txt && \ - test -z $(cat pipinfo.txt | grep GPL | grep -v LGPL) && \ + test -z "$(cat pipinfo.txt | grep GPL | grep -v LGPL)" && \ rm pipinfo.txt RUN virtualenv --distribute venv \ @@ -57,4 +57,4 @@ RUN PYTHONPATH=$QUAYPATH venv/bin/alembic heads | grep -E '^[0-9a-f]+ \(head\)$' RUN ./scripts/detect-config.sh -CMD ./quay-entrypoint.sh \ No newline at end of file +CMD ./quay-entrypoint.sh From 6cfa96bdeb193e81ef703141925f8ee24ede9510 Mon Sep 17 00:00:00 2001 From: Brad Ison Date: Mon, 26 Feb 2018 13:03:04 -0500 Subject: [PATCH 6/6] Update all dependencies --- requirements.txt | 164 +++++++++++++++++++++++++---------------------- 1 file changed, 86 insertions(+), 78 deletions(-) diff --git a/requirements.txt b/requirements.txt index 3dfb81be3..36043a933 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,139 +1,147 @@ aiowsgi==0.6 -alembic==0.9.1 +alembic==0.9.8 -e git+https://github.com/coreos/mockldap.git@59a46efbe8c7cd8146a87a7c4f2b09746b953e11#egg=mockldap --e git+https://github.com/coreos/py-bitbucket.git@07a80f63388d004f05f58441983bdf195f9b666e#egg=py_bitbucket +-e git+https://github.com/coreos/py-bitbucket.git@55a1ada645f2fb6369147996ec71edd7828d91c8#egg=py_bitbucket -e git+https://github.com/coreos/pyapi-gitlab.git@136c3970d591136a4f766a846c5d22aad52e124f#egg=pyapi_gitlab -e git+https://github.com/coreos/resumablehashlib.git@b1b631249589b07adf40e0ee545b323a501340b4#egg=resumablehashlib -e git+https://github.com/DevTable/aniso8601-fake.git@bd7762c7dea0498706d3f57db60cd8a8af44ba90#egg=aniso8601 -e git+https://github.com/DevTable/anunidecode.git@d59236a822e578ba3a0e5e5abbd3855873fa7a88#egg=anunidecode -e git+https://github.com/app-registry/appr-server.git@c2ef3b88afe926a92ef5f2e11e7d4a259e286a17#egg=cnr_server --e git+https://github.com/DevTable/container-cloud-config.git@bce675537904175f6975024a4c89269027ea6792#egg=container_cloud_config +-e git+https://github.com/DevTable/container-cloud-config.git@8e8ae177c3d5cd6608f64250fcd8770022d61562#egg=container_cloud_config -e git+https://github.com/DevTable/python-etcd.git@f1168cb02a2a8c83bec1108c6fcd8615ef463b14#egg=python_etcd --e git+https://github.com/jarus/flask-testing.git@18baff32969a0634a414ce61d2dd4a77433817a8#egg=Flask_Testing --e git+https://github.com/jepcastelein/marketo-rest-python.git@e26af0c5acd3d4ad899383442703a053120aa7ab#egg=marketorestpython +-e git+https://github.com/jarus/flask-testing.git@17f19d7fee0e1e176703fc7cb04917a77913ba1a#egg=Flask_Testing +-e git+https://github.com/jepcastelein/marketo-rest-python.git@6134d1129f2435b313c4301503a4b74974d79a42#egg=marketorestpython -e git+https://github.com/NateFerrero/oauth2lib.git@d161b010f8a596826050a09e5e94d59443cc12d9#egg=oauth2lib -appdirs==1.4.3 APScheduler==3.0.5 -asn1crypto==0.22.0 +asn1crypto==0.24.0 autobahn==0.9.3.post3 azure-common==1.1.8 +azure-nspkg==2.0.0 azure-storage-blob==1.1.0 -Babel==2.4.0 -beautifulsoup4==4.5.3 +azure-storage-common==1.1.0 +azure-storage-nspkg==3.0.0 +Babel==2.5.3 +beautifulsoup4==4.6.0 bencode==1.0 -bintrees==2.0.6 +bintrees==2.0.7 bitmath==1.3.1.2 blinker==1.4 -boto==2.46.1 -boto3==1.4.7 +boto==2.48.0 +boto3==1.5.36 +botocore==1.8.50 cachetools==1.1.6 -certifi==2017.11.5 -cffi==1.10.0 +certifi==2018.1.18 +cffi==1.11.4 +chardet==3.0.4 click==6.7 -contextlib2==0.5.4 -cryptography==1.8.1 -debtcollector==1.13.0 -decorator==4.0.11 +contextlib2==0.5.5 +cryptography==2.1.4 +debtcollector==1.19.0 +decorator==4.2.1 enum34==1.1.6 -Flask-Login==0.4.0 +Flask-Cors==3.0.3 +Flask-Login==0.4.1 Flask-Mail==0.9.1 Flask-Principal==0.4.0 -Flask-RESTful==0.3.5 -Flask==0.12.1 +Flask-RESTful==0.3.6 +Flask==0.12.2 funcparserlib==0.3.6 funcsigs==1.0.2 functools32==3.2.3.post2 -furl==1.0.0 +furl==1.0.1 future==0.16.0 -futures==3.0.5 -geoip2==2.5.0 -gevent==1.2.1 +futures==3.2.0 +geoip2==2.7.0 +gevent==1.2.2 gipc==0.6.0 -greenlet==0.4.12 +greenlet==0.4.13 gunicorn==18.0 hiredis==0.2.0 html5lib==0.9999999 httmock==1.2.6 +httplib2==0.10.3 httpretty==0.8.10 -idna==2.5 -ipaddress==1.0.18 -iso8601==0.1.11 +idna==2.6 +ipaddress==1.0.19 +iso8601==0.1.12 itsdangerous==0.24 -Jinja2==2.9.6 +Jinja2==2.10 +jmespath==0.9.3 jsonpath-rw==1.4.0 jsonschema==2.6.0 -keystoneauth1==2.19.0 -Mako==1.0.6 +keystoneauth1==3.4.0 +Mako==1.0.7 marisa-trie==0.7.4 MarkupSafe==1.0 +maxminddb==1.3.0 mixpanel==4.3.2 mock==2.0.0 mockredispy==2.9.3 -monotonic==1.3 +monotonic==1.4 moto==0.4.25 -msgpack-python==0.4.8 +msgpack==0.5.6 namedlist==1.7 -ndg-httpsclient==0.4.2 +ndg-httpsclient==0.4.4 netaddr==0.7.19 -netifaces==0.10.5 -oauthlib==2.0.2 -olefile==0.44 +netifaces==0.10.6 +oauthlib==2.0.6 orderedmultidict==0.7.11 -oslo.config==3.24.0 -oslo.i18n==3.15.0 -oslo.serialization==2.18.0 -oslo.utils==3.25.0 -packaging==16.8 -pathvalidate==0.15.0 -pbr==2.0.0 +oslo.config==5.2.0 +oslo.i18n==3.19.0 +oslo.serialization==2.24.0 +oslo.utils==3.35.0 +pathvalidate==0.16.3 +pbr==3.1.1 peewee==2.8.1 -Pillow==4.1.0 -ply==3.10 -positional==1.1.1 -psutil==5.2.1 -psycopg2==2.7.1 -pyasn1==0.3.7 +Pillow==5.0.0 +ply==3.11 +psutil==5.4.3 +psycopg2==2.7.4 +pyasn1==0.4.2 py-bcrypt==0.4 -pycparser==2.17 -pycryptodome==3.4.5 -pycryptodomex==3.4.5 -PyGithub==1.34 +pycparser==2.18 +pycryptodome==3.4.11 +pycryptodomex==3.4.12 +PyGithub==1.36 pygpgme==0.3 -pyjwkest==1.3.2 -PyJWT==1.4.2 +pyjwkest==1.4.0 +PyJWT==1.5.3 PyMySQL==0.6.7 -pyOpenSSL==16.2.0 +pyOpenSSL==17.5.0 pyparsing==2.2.0 PyPDF2==1.26.0 -python-dateutil==2.6.0 +python-dateutil==2.6.1 python-editor==1.0.3 -python-keystoneclient==3.10.0 -python-ldap==2.4.32 -python-magic==0.4.13 -python-swiftclient==3.3.0 -pytz==2017.2 +python-keystoneclient==3.15.0 +python-ldap==2.5.2 +python-magic==0.4.15 +python-swiftclient==3.5.0 +pytz==2018.3 PyYAML==3.12 -raven==6.0.0 +raven==6.5.0 recaptcha2==0.1 -redis==2.10.5 +redis==2.10.6 redlock==1.2.0 reportlab==2.7 requests-oauthlib==0.8.0 requests[security]==2.18.4 -rfc3986==0.4.1 +rfc3986==1.1.0 +s3transfer==0.1.13 semantic-version==2.6.0 -six==1.10.0 +simplejson==3.13.2 +six==1.11.0 SQLAlchemy==1.1.5 -stevedore==1.21.0 +stevedore==1.28.0 stringscore==0.1.0 -stripe==1.51.0 +stripe==1.79.0 toposort==1.5 trollius==2.1 -tzlocal==1.3 -urllib3==1.20 -waitress==1.0.2 -WebOb==1.7.2 -Werkzeug==0.12.1 -wrapt==1.10.10 -xhtml2pdf==0.0.6 -xmltodict==0.10.2 +tzlocal==1.5.1 +urllib3==1.22 +waitress==1.1.0 +WebOb==1.7.4 +Werkzeug==0.14.1 +wrapt==1.10.11 +xhtml2pdf==0.2.1 +xmltodict==0.11.0 +yapf==0.20.2