From df58597d6b3fc3af8cec1370601bfd46f2a3d7a7 Mon Sep 17 00:00:00 2001 From: Greg Tyler Date: Tue, 15 Oct 2024 11:57:40 +0100 Subject: [PATCH] Fix various issues reported by sonarcloud Dockerfiles: - Run containers as non-root user Python: - Add CSRF protection - Split safe and unsafe routes - Don't run in debug mode JavaScript: - Don't use asynchronous constructor side-effects - Add integrity check to external resources #patch --- fixtures/Dockerfile | 5 +- fixtures/app.py | 95 +++++++++++++---------- fixtures/requirements.txt | 1 + fixtures/static/js/json-schema-editor.mjs | 12 ++- fixtures/static/js/main.mjs | 2 +- fixtures/static/js/uid-generator.mjs | 7 ++ fixtures/templates/index.html | 8 +- lambda/Dockerfile | 3 + mock-apigw/Dockerfile | 3 + 9 files changed, 90 insertions(+), 46 deletions(-) diff --git a/fixtures/Dockerfile b/fixtures/Dockerfile index 5e57d685..b49dbd06 100644 --- a/fixtures/Dockerfile +++ b/fixtures/Dockerfile @@ -4,7 +4,7 @@ WORKDIR /app COPY fixtures/package.json package.json COPY fixtures/package-lock.json package-lock.json -RUN npm ci +RUN npm ci --ignore-scripts FROM python:3-alpine3.17 @@ -26,4 +26,7 @@ COPY docs/schemas static/schemas EXPOSE 80 +RUN addgroup -S app && adduser -S -g app app +USER app + CMD [ "flask", "run", "--host", "0.0.0.0", "--port", "80"] diff --git a/fixtures/app.py b/fixtures/app.py index 5c805750..449be013 100644 --- a/fixtures/app.py +++ b/fixtures/app.py @@ -1,10 +1,18 @@ -import requests, os, logging, sys, json +import requests, os, logging, sys, json, uuid from lib.aws_auth import AwsAuth from lib.jwt import generate_jwt +from urllib.parse import quote from flask import Flask, render_template, request, jsonify +from flask_wtf import CSRFProtect app = Flask(__name__, static_url_path="/assets") +app.config.update( + SECRET_KEY=uuid.uuid4().__str__(), +) + +csrf = CSRFProtect() +csrf.init_app(app) logger = logging.getLogger() logger.setLevel(logging.DEBUG) @@ -49,52 +57,57 @@ def health_check_dependencies(): return jsonify({"ok": False}) -@app.route("/", methods=["GET", "POST"]) -def index(): - aws_auth = AwsAuth() - - uid = request.form.get("uid", "") - json_data = request.form.get("json-data", "{}") +@app.route("/", methods=["GET"]) +def get_index(): base_url = os.environ["BASE_URL"] - template_data = { - "base_url": base_url, - "uid": uid, - "json_data": json_data, - } - - if request.method == "GET": - return render_template("index.html", **template_data) - - if request.method == "POST": - url = base_url + "/lpas/" + uid - - if aws_auth.is_authed: - headers = aws_auth.get_headers(method="PUT", url=url, data=json_data) - else: - headers = {} + return render_template( + "index.html", + **{ + "base_url": base_url, + "json_data": "{}", + }, + ) - token = generate_jwt(os.environ["JWT_SECRET_KEY"]) - resp = requests.put( - url, - json_data, - headers={ - **headers, - "Content-Type": "application/json", - "X-Jwt-Authorization": "Bearer " + token, - }, - ) +@app.route("/", methods=["POST"]) +def post_index(): + aws_auth = AwsAuth() - return render_template( - "index.html", - **template_data, - success=resp.status_code < 400, - error=json.loads(resp.text), - ) + uid = request.form.get("uid", "") + json_data = request.form.get("json-data", "{}") + base_url = os.environ["BASE_URL"] - return "error" + url = base_url + "/lpas/" + quote(uid) + + if aws_auth.is_authed: + headers = aws_auth.get_headers(method="PUT", url=url, data=json_data) + else: + headers = {} + + token = generate_jwt(os.environ["JWT_SECRET_KEY"]) + + resp = requests.put( + url, + json_data, + headers={ + **headers, + "Content-Type": "application/json", + "X-Jwt-Authorization": "Bearer " + token, + }, + ) + + return render_template( + "index.html", + **{ + "base_url": base_url, + "uid": uid, + "json_data": json_data, + }, + success=resp.status_code < 400, + error=json.loads(resp.text), + ) if __name__ == "__main__": - app.run(debug=True, host="0.0.0.0", port=80) + app.run(host="0.0.0.0", port=80) diff --git a/fixtures/requirements.txt b/fixtures/requirements.txt index d0aead73..bc7a141c 100644 --- a/fixtures/requirements.txt +++ b/fixtures/requirements.txt @@ -1,4 +1,5 @@ Flask==3.0.3 +Flask-WTF==1.2.1 requests==2.32.3 Jinja2==3.1.4 jsonschema==4.23.0 diff --git a/fixtures/static/js/json-schema-editor.mjs b/fixtures/static/js/json-schema-editor.mjs index edb2485f..9c1c34d2 100644 --- a/fixtures/static/js/json-schema-editor.mjs +++ b/fixtures/static/js/json-schema-editor.mjs @@ -36,8 +36,6 @@ export class JsonSchemaEditor { } this.$module = $module; - - this.init(); } async init() { @@ -319,4 +317,14 @@ export class JsonSchemaEditor { return $container; } + + /** + * @param {Element} $module + */ + static async create($module) { + const $editor = new JsonSchemaEditor($module); + $editor.init(); + + return $editor; + } } diff --git a/fixtures/static/js/main.mjs b/fixtures/static/js/main.mjs index 4b81c25e..d630fb46 100644 --- a/fixtures/static/js/main.mjs +++ b/fixtures/static/js/main.mjs @@ -10,6 +10,6 @@ export function initAll() { Object.entries(initiators).forEach(([name, Component]) => { const $elements = document.querySelectorAll(`[data-module="${name}"]`); - $elements.forEach(($element) => new Component($element)); + $elements.forEach(($element) => Component.create($element)); }); } diff --git a/fixtures/static/js/uid-generator.mjs b/fixtures/static/js/uid-generator.mjs index 22a5e2fd..8298e36f 100644 --- a/fixtures/static/js/uid-generator.mjs +++ b/fixtures/static/js/uid-generator.mjs @@ -52,4 +52,11 @@ export class UidGenerator { `M-` + [value.slice(0, 4), value.slice(4, 8), value.slice(8, 12)].join("-"); } + + /** + * @param {Element} $module + */ + static async create($module) { + return new UidGenerator($module); + } } diff --git a/fixtures/templates/index.html b/fixtures/templates/index.html index e7c5e923..f7ab7fef 100644 --- a/fixtures/templates/index.html +++ b/fixtures/templates/index.html @@ -63,6 +63,8 @@

{{ error.detail }}

{% endif %} {% endif %}
+ +
{{ error.detail }}
- +