Fix download forgery

This commit is contained in:
rubenwardy 2019-11-18 21:42:56 +00:00
parent 3f666d2302
commit ff2a74367f
5 changed files with 32 additions and 17 deletions

@ -25,13 +25,15 @@ from flask_github import GitHub
from flask_wtf.csrf import CsrfProtect from flask_wtf.csrf import CsrfProtect
from flask_flatpages import FlatPages from flask_flatpages import FlatPages
from flask_babel import Babel from flask_babel import Babel
import os import os, redis
app = Flask(__name__, static_folder="public/static") app = Flask(__name__, static_folder="public/static")
app.config["FLATPAGES_ROOT"] = "flatpages" app.config["FLATPAGES_ROOT"] = "flatpages"
app.config["FLATPAGES_EXTENSION"] = ".md" app.config["FLATPAGES_EXTENSION"] = ".md"
app.config.from_pyfile(os.environ["FLASK_CONFIG"]) app.config.from_pyfile(os.environ["FLASK_CONFIG"])
r = redis.Redis.from_url(app.config["REDIS_URL"])
menu.Menu(app=app) menu.Menu(app=app)
markdown = Markdown(app, extensions=["fenced_code"], safe_mode=True, output_format="html5") markdown = Markdown(app, extensions=["fenced_code"], safe_mode=True, output_format="html5")
github = GitHub(app) github = GitHub(app)

@ -20,6 +20,7 @@ from flask_user import *
from . import bp from . import bp
from app.rediscache import has_key, set_key, make_download_key
from app.models import * from app.models import *
from app.tasks.importtasks import makeVCSRelease from app.tasks.importtasks import makeVCSRelease
from app.utils import * from app.utils import *
@ -123,14 +124,12 @@ def download_release(package, id):
if release is None or release.package != package: if release is None or release.package != package:
abort(404) abort(404)
if release is None: ip = request.headers.get("X-Forwarded-For") or request.remote_addr
if "application/zip" in request.accept_mimetypes and \ if ip is not None:
not "text/html" in request.accept_mimetypes: key = make_download_key(ip, release.package)
return "", 204 if not has_key(key):
else: set_key(key, "true")
flash("No download available.", "error")
return redirect(package.getDetailsURL())
else:
PackageRelease.query.filter_by(id=release.id).update({ PackageRelease.query.filter_by(id=release.id).update({
"downloads": PackageRelease.downloads + 1 "downloads": PackageRelease.downloads + 1
}) })

13
app/rediscache.py Normal file

@ -0,0 +1,13 @@
from . import r
# This file acts as a facade between the releases code and redis,
# and also means that the releases code avoids knowing about `app`
def make_download_key(ip, package):
return ("{}/{}/{}").format(ip, package.author.username, package.name)
def set_key(key, v):
r.set(key, v)
def has_key(key):
return r.exists(key)

@ -10,8 +10,9 @@ SQLALCHEMY_DATABASE_URI = "sqlite:///../db.sqlite"
GITHUB_CLIENT_ID = "" GITHUB_CLIENT_ID = ""
GITHUB_CLIENT_SECRET = "" GITHUB_CLIENT_SECRET = ""
CELERY_BROKER_URL='redis://localhost:6379' REDIS_URL='redis://redis:6379'
CELERY_RESULT_BACKEND='redis://localhost:6379' CELERY_BROKER_URL='redis://redis:6379'
CELERY_RESULT_BACKEND='redis://redis:6379'
USER_ENABLE_REGISTER = False USER_ENABLE_REGISTER = False
USER_ENABLE_CHANGE_USERNAME = False USER_ENABLE_CHANGE_USERNAME = False

@ -16,7 +16,7 @@ celery==4.1.1
kombu==4.2.0 kombu==4.2.0
GitPython~=2.1 GitPython~=2.1
lxml~=4.2 lxml~=4.2
pillow~=5.3 pillow~=6.2
pyScss~=1.3 pyScss~=1.3
redis==2.10.6 redis==2.10.6
psycopg2~=2.7 psycopg2~=2.7