From d3e47abb91f02ae78c124fa1ccf21d68ea2d1052 Mon Sep 17 00:00:00 2001
From: lare <lare@lare.cc>
Date: Wed, 23 Nov 2022 16:47:55 +0100
Subject: [PATCH] implement server side form validation

---
 web/backend/main.py            | 94 ++++++++++++++++++++++++++++++++++
 web/frontend/peerings-new.html |  4 +-
 2 files changed, 96 insertions(+), 2 deletions(-)

diff --git a/web/backend/main.py b/web/backend/main.py
index 4fa8403..9abca5a 100644
--- a/web/backend/main.py
+++ b/web/backend/main.py
@@ -1,6 +1,7 @@
 #! /usr/bin/env python3
 
 from flask import Flask, Response, redirect, render_template, request, session, abort
+import werkzeug.exceptions as werkzeug_exceptions
 import json, os, base64, logging
 from functools import wraps
 from ipaddress import ip_address, ip_network
@@ -188,6 +189,99 @@ def peerings_new():
         else: 
             return render_template("peerings-new.html",  session=session,config=config, peerings=peerings)
     elif request.method == "POST":
+        print(request.args)
+        print(request.form)
+        if not "node" in request.args or not request.args["node"]:
+            return render_template("peerings-new.html",  session=session,config=config, peerings=peerings, msg="no node specified, please click one of the buttons above")
+
+        new_peering = {}
+        # errors = 0
+        
+        ## check if all required (and enabled) options are specified
+        try:
+            new_peering["peer-wgkey"] = request.form["peer-wgkey"]
+            if  request.form["peer-endpoint-enabled"] == "on":
+                new_peering["peer-endpoint"] = request.form["peer-endpoint"]
+                if new_peering["peer-endpoint"] == "":
+                    raise ValueError("peer-endpoint")
+            else:
+                new_peering["peer-endpoint"] = None
+            
+            if "peer-v6ll-enabled" in request.form and request.form["peer-v6ll-enabled"] == "on":
+                new_peering["peer-v6ll"] = request.form["peer-v6ll"]
+                if new_peering["peer-v6ll"] == "":
+                    raise ValueError("peer-v6ll")
+            else:
+                new_peering["peer-v6ll"] = None
+            if "peer-v4-enabled" in request.form and request.form["peer-v4-enabled"] == "on":
+                new_peering["peer-v4"] = request.form["peer-v4"]
+                if new_peering["peer-v4"] == "":
+                    raise ValueError("peer-v4")
+            else:
+                new_peering["peer-v4"] = None
+            if "peer-v6-enabled" in request.form and request.form["peer-v6-enabled"] == "on":
+                new_peering["peer-v6"] = request.form["peer-v6"]
+                if new_peering["peer-v6"] == "":
+                    raise ValueError("peer-v6")
+            else:
+                new_peering["peer-v6"] = None
+            #new_peering[""] = request.form["peer-wgkey"]
+        except ValueError as e:
+            print(f"error: {e.args}")
+            return render_template("peerings-new.html",  session=session,config=config, peerings=peerings, msg="at least one of the required/enabled fields was not filled out"), 400
+
+        print(new_peering)
+
+        ## check wireguard key
+        wg_key_invalid = False
+        if len(new_peering["peer-wgkey"]) != 44:
+            wg_key_invalid = True
+        try:
+            base64.b64decode(new_peering["peer-wgkey"])
+        except:
+            wg_key_invalid = True
+        if wg_key_invalid: 
+            return render_template("peerings-new.html",  session=session,config=config, peerings=peerings, msg="invalid wireguard Key"), 400
+
+        ## check endpoint 
+        if new_peering["peer-endpoint"]:
+            if not new_peering["peer-endpoint"].split(":")[-1].isnumeric():
+                return render_template("peerings-new.html",  session=session,config=config, peerings=peerings, msg="no port number in endpoint"), 400
+            elif len(new_peering["peer-endpoint"].split(":")) < 2 and not "." in new_peering["peer-endpoint"]:
+                return render_template("peerings-new.html",  session=session,config=config, peerings=peerings, msg="endpoint doesn't look like a ip address or fqdn"), 400
+
+        ## check if at least one ip is specified/enabled
+        try:
+            if not (new_peering["peer-v6ll"] or new_peering["peer-v4"] or new_peering["peer-v6"]):
+                return render_template("peerings-new.html",  session=session,config=config, peerings=peerings, msg="at least one of the ip addresses must be enabled and specified"), 400
+        except KeyError:
+            return render_template("peerings-new.html",  session=session,config=config, peerings=peerings, msg="one of the values isn't valid"), 400
+
+        ## check if supplied ip addresses are valid
+        try:
+            if new_peering["peer-v6ll"]:
+                ipv6ll = ip_address(new_peering["peer-v6ll"])
+                if not ipv6ll.version == 6: raise ValueError()
+                if not ipv6ll.is_link_local: raise ValueError()
+            if new_peering["peer-v4"]:
+                ipv4 = ip_address(new_peering["peer-v4"])
+                if not ipv4.version == 4: raise ValueError()
+                if ipv4.is_private: 
+                    if not (ipv4.compressed.startswith("172.2") or ipv4.compressed.startswith("10.")):
+                        raise ValueError()
+                elif ipv4.is_link_local:
+                    pass
+                else: raise ValueError()
+            if new_peering["peer-v6"]:
+                ipv6 = ip_address(new_peering["peer-v6"])
+                if not ipv6.version == 6: raise ValueError()
+                if not ipv6.is_private: raise ValueError()
+            
+        except ValueError:
+            return render_template("peerings-new.html",  session=session,config=config, peerings=peerings, msg="invalid ip address(es) supplied"), 400
+
+
+
         return """<div>creating peerings is not (yet) implemented</div><div><a href="../">return</a>"""
         return f"{request.method} /peerings/new {str(request.args)}{str(request.form)}"
 @app.route("/peerings", methods=["GET","POST","DELETE"])
diff --git a/web/frontend/peerings-new.html b/web/frontend/peerings-new.html
index 650b64a..83fc9a8 100644
--- a/web/frontend/peerings-new.html
+++ b/web/frontend/peerings-new.html
@@ -25,7 +25,7 @@
                 msg += "<li>endpoint enabled but non specified</li>";
             } else if (isNaN(endpoint.split(":").at(-1))) {
                 msg += "<li>endpoint doesn't end with a (port)number</li>";
-            } else if (endpoint.split(":").length > 2 || endpoint.indexOf(".") == -1) {
+            } else if (endpoint.split(":").length < 2 && endpoint.indexOf(".") == -1) {
                 msg += "<li>endpoint doesn't look like a ip address or fqdn</li>";
             }
         }
@@ -139,7 +139,7 @@
     {% endfor %}
 </div>
 <form action="" method="post" class="flex" onsubmit="return form_validate(this)">
-    <div id="peer-invalid-note" style="background-color:red;"></div>
+    <div id="peer-invalid-note" style="background-color:red;">{% if msg %}{{msg}}{%endif%}</div>
     <table>
         <tr>
             <td><label for="peer-asn">Your ASN</label></td>