Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use Talisman to set HTTP security headers #308

Merged
merged 2 commits into from
Jun 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions files/ansible/install-deps.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
- python3-pip
- python3-flask
- python3-flask-cors
- python3-flask-talisman
- python3-cachetools
- python3-requests
- yarnpkg
Expand Down
2 changes: 0 additions & 2 deletions files/scripts/run_httpd.sh
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,6 @@ exec mod_wsgi-express-3 start-server \
--access-log \
--log-to-terminal \
--https-port 8443 \
--https-only \
--hsts-policy "max-age=31536000;includeSubDomains" \
--ssl-certificate-file /secrets/fullchain.pem \
--ssl-certificate-key-file /secrets/privkey.pem \
--server-name ${SERVER_NAME} \
Expand Down
26 changes: 14 additions & 12 deletions packit_dashboard/api/routes.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
from flask import Blueprint, jsonify, request, escape
from flask_cors import CORS
from packit_dashboard.utils import return_json
from packit_dashboard.config import API_URL
from datetime import datetime, timedelta

from cachetools.func import ttl_cache
from flask import Blueprint, request, escape
from flask_cors import CORS

from packit_dashboard.config import API_URL
from packit_dashboard.utils import make_response

api = Blueprint("api", __name__)
CORS(api)
Expand All @@ -23,23 +25,23 @@ def copr_builds():
page = escape(request.args.get("page"))
per_page = escape(request.args.get("per_page"))
url = f"{API_URL}/copr-builds?page={page}&per_page={per_page}"
return jsonify(return_json(url))
return make_response(url)


@api.route("/api/testing-farm/")
def testing_farm():
page = escape(request.args.get("page"))
per_page = escape(request.args.get("per_page"))
url = f"{API_URL}/testing-farm/results?page={page}&per_page={per_page}"
return jsonify(return_json(url))
return make_response(url)


@api.route("/api/projects/")
def projects():
page = escape(request.args.get("page"))
per_page = escape(request.args.get("per_page"))
url = f"{API_URL}/projects?page={page}&per_page={per_page}"
return jsonify(return_json(url))
return make_response(url)


@api.route("/api/projects/<forge>/<namespace>/<repo_name>/prs/")
Expand All @@ -50,7 +52,7 @@ def project_prs(forge, namespace, repo_name):
page = escape(request.args.get("page"))
per_page = escape(request.args.get("per_page"))
url = f"{API_URL}/projects/{forge}/{namespace}/{repo_name}/prs?page={page}&per_page={per_page}"
return jsonify(return_json(url))
return make_response(url)


@api.route("/api/projects/<forge>/<namespace>/<repo_name>/releases/")
Expand All @@ -59,7 +61,7 @@ def project_releases(forge, namespace, repo_name):
namespace = escape(namespace)
repo_name = escape(repo_name)
url = f"{API_URL}/projects/{forge}/{namespace}/{repo_name}/releases"
return jsonify(return_json(url))
return make_response(url)


@api.route("/api/projects/<forge>/<namespace>/<repo_name>/issues/")
Expand All @@ -68,15 +70,15 @@ def project_issues(forge, namespace, repo_name):
namespace = escape(namespace)
repo_name = escape(repo_name)
url = f"{API_URL}/projects/{forge}/{namespace}/{repo_name}/issues"
return jsonify(return_json(url))
return make_response(url)


@api.route("/api/propose-downstream/")
def propose_downstream():
page = escape(request.args.get("page"))
per_page = escape(request.args.get("per_page"))
url = f"{API_URL}/propose-downstream?page={page}&per_page={per_page}"
return jsonify(return_json(url))
return make_response(url)


def _get_usage_data_from_packit_api(usage_from=None, usage_to=None, top=5):
Expand All @@ -85,7 +87,7 @@ def _get_usage_data_from_packit_api(usage_from=None, usage_to=None, top=5):
url += f"&from={usage_from}"
if usage_to:
url += f"&to={usage_to}"
return jsonify(return_json(url))
return make_response(url)


@api.route("/api/usage/past-day")
Expand Down
8 changes: 8 additions & 0 deletions packit_dashboard/app.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from os import getenv

from flask import Flask
from flask_talisman import Talisman

from packit_dashboard.api.routes import api
from packit_dashboard.home.routes import home
Expand All @@ -16,5 +17,12 @@
app.register_blueprint(home)


csp = {
"default-src": ["'self'", "*.packit.dev"],
"object-src": "'none'",
"style-src": ["'unsafe-inline'", "'self'"],
}
Talisman(app, content_security_policy=csp)

if __name__ == "__main__":
app.run(debug=getenv("DEPLOYMENT") != "prod")
27 changes: 15 additions & 12 deletions packit_dashboard/utils.py
Original file line number Diff line number Diff line change
@@ -1,23 +1,26 @@
import requests
import json
from flask import jsonify, Response as FlaskResponse
from requests import request, Response as RequestsResponse


# Common utility functions used in multiple files in the packit_dashboard package
# Returns python parsable json object from URL
def return_json(url, method="GET", **kwargs):
output = None
""" Common utility functions used in multiple files in the packit_dashboard package. """


def make_response(url, method="GET", **kwargs) -> FlaskResponse:
"""Returns a response from URL

:param url: API URL to request
:param method: HTTP method
:return: Flask Response
"""
tries = 6
for i in range(tries):
try:
response = requests.request(method=method, url=url, **kwargs)
output = json.loads(response.content)
req_response: RequestsResponse = request(method=method, url=url, **kwargs)
response: FlaskResponse = jsonify(req_response.json())
except Exception:
if i < tries - 1:
continue
else:
output = None
raise
raise
break

return output
return response