Repository: LMFDB/lmfdb

Issue:
ECNF field validation issue
The ECNF code seems to try to construct a curve even if the label doesn't make sense.
```
Exception on /EllipticCurve/Q"ulno'ewnc/15680/bz/2/download/sage [GET]
Traceback (most recent call last):
  File "/home/sage/sage-9.1/local/lib/python3.7/site-packages/flask/app.py", line 2447, in wsgi_app
    response = self.full_dispatch_request()
  File "/home/sage/sage-9.1/local/lib/python3.7/site-packages/flask/app.py", line 1952, in full_dispatch_request
    rv = self.handle_user_exception(e)
  File "/home/sage/sage-9.1/local/lib/python3.7/site-packages/flask/app.py", line 1821, in handle_user_exception
    reraise(exc_type, exc_value, tb)
  File "/home/sage/sage-9.1/local/lib/python3.7/site-packages/flask/_compat.py", line 39, in reraise
    raise value
  File "/home/sage/sage-9.1/local/lib/python3.7/site-packages/flask/app.py", line 1950, in full_dispatch_request
    rv = self.dispatch_request()
  File "/home/sage/sage-9.1/local/lib/python3.7/site-packages/flask/app.py", line 1936, in dispatch_request
    return self.view_functions[rule.endpoint](**req.view_args)
  File "/home/lmfdb/lmfdb-git-web/lmfdb/ecnf/main.py", line 678, in ecnf_code_download
    response = make_response(ecnf_code(**args))
  File "/home/lmfdb/lmfdb-git-web/lmfdb/ecnf/main.py", line 711, in ecnf_code
    Ecode = E.code()
AttributeError: 'NoneType' object has no attribute 'code'
[2020-09-09 07:50:20 UTC] 500 error on URL https://www.lmfdb.org/EllipticCurve/Q%22ulno%27ewnc/15680/bz/2/download/sage?label=15680.bz2 ()
```
and
```
Exception on /EllipticCurve/Qx"l'xexfwv/1650/m/1 [GET]
Traceback (most recent call last):
  File "/home/lmfdb/lmfdb-git-web/lmfdb/ecnf/main.py", line 353, in show_ecnf
    nf_label = nf_string_to_label(nf)
  File "/home/lmfdb/lmfdb-git-web/lmfdb/utils/search_parsing.py", line 751, in nf_string_to_label
    raise SearchParsingError('It is not a valid field name or label, or a defining polynomial.')
lmfdb.backend.utils.SearchParsingError: It is not a valid field name or label, or a defining polynomial.

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/sage/sage-9.1/local/lib/python3.7/site-packages/flask/app.py", line 2447, in wsgi_app
    response = self.full_dispatch_request()
  File "/home/sage/sage-9.1/local/lib/python3.7/site-packages/flask/app.py", line 1952, in full_dispatch_request
    rv = self.handle_user_exception(e)
  File "/home/sage/sage-9.1/local/lib/python3.7/site-packages/flask/app.py", line 1821, in handle_user_exception
    reraise(exc_type, exc_value, tb)
  File "/home/sage/sage-9.1/local/lib/python3.7/site-packages/flask/_compat.py", line 39, in reraise
    raise value
  File "/home/sage/sage-9.1/local/lib/python3.7/site-packages/flask/app.py", line 1950, in full_dispatch_request
    rv = self.dispatch_request()
  File "/home/sage/sage-9.1/local/lib/python3.7/site-packages/flask/app.py", line 1936, in dispatch_request
    return self.view_functions[rule.endpoint](**req.view_args)
  File "/home/lmfdb/lmfdb-git-web/lmfdb/ecnf/main.py", line 355, in show_ecnf
    return search_input_error()
  File "/home/lmfdb/lmfdb-git-web/lmfdb/ecnf/main.py", line 548, in search_input_error
    bread = [('Elliptic curves', url_for(".index")) ('Search results', '.')]
TypeError: 'tuple' object is not callable
[2020-09-09 09:11:22 UTC] 500 error on URL https://www.lmfdb.org/EllipticCurve/Qx%22l%27xexfwv/1650/m/1
```

ps: this must have been a bot, but we should fix it regardless


Discussion:
I am working on this now

Resolving patch:
diff --git a/lmfdb/ecnf/WebEllipticCurve.py b/lmfdb/ecnf/WebEllipticCurve.py
--- a/lmfdb/ecnf/WebEllipticCurve.py
+++ b/lmfdb/ecnf/WebEllipticCurve.py
@@ -260,7 +260,7 @@ def by_label(label):
         data = db.ec_nfcurves.lookup(label)
         if data:
             return ECNF(data)
-        print("No such curve in the database: %s" % label)
+        raise ValueError("Elliptic curve not found: %s" % label)
 
     def make_E(self):
         #print("Creating ECNF object for {}".format(self.label))
diff --git a/lmfdb/ecnf/main.py b/lmfdb/ecnf/main.py
--- a/lmfdb/ecnf/main.py
+++ b/lmfdb/ecnf/main.py
@@ -8,7 +8,7 @@
 import time
 from six.moves.urllib_parse import quote, unquote
 
-from flask import render_template, request, url_for, redirect, send_file, make_response
+from flask import render_template, request, url_for, redirect, send_file, make_response, abort
 from markupsafe import Markup, escape
 
 from lmfdb import db
@@ -31,7 +31,10 @@
 # The conductor label seems to only have three parts for the trivial ideal (1.0.1)
 # field 3.1.23.1 uses upper case letters for isogeny class
 LABEL_RE = re.compile(r"\d+\.\d+\.\d+\.\d+-\d+\.\d+(\.\d+)?-(CM)?[a-zA-Z]+\d+")
+SHORT_LABEL_RE = re.compile(r"\d+\.\d+(\.\d+)?-(CM)?[a-zA-Z]+\d+")
 CLASS_LABEL_RE = re.compile(r"\d+\.\d+\.\d+\.\d+-\d+\.\d+(\.\d+)?-(CM)?[a-zA-Z]+")
+SHORT_CLASS_LABEL_RE = re.compile(r"\d+\.\d+(\.\d+)?-(CM)?[a-zA-Z]+")
+FIELD_RE = re.compile(r"\d+\.\d+\.\d+\.\d+")
 
 def split_full_label(lab):
     r""" Split a full curve label into 4 components
@@ -51,7 +54,7 @@ def split_short_label(lab):
     r""" Split a short curve label into 3 components
     (conductor_label,isoclass_label,curve_number)
     """
-    if not CLASS_LABEL_RE.fullmatch(lab):
+    if not SHORT_LABEL_RE.fullmatch(lab):
         raise ValueError(Markup("<span style='color:black'>%s</span> is not a valid elliptic curve label. It must be of the form (conductor label) - (isogeny class label) - (curve identifier) separated by dashes, such as 31.1-a1" % escape(lab)))
     data = lab.split("-")
     conductor_label = data[0]
@@ -64,9 +67,9 @@ def split_class_label(lab):
     r""" Split a class label into 3 components
     (field_label, conductor_label,isoclass_label)
     """
+    if not CLASS_LABEL_RE.fullmatch(lab):
+        raise ValueError(Markup("<span style='color:black'>%s</span> is not a valid elliptic curve label. It must be of the form (conductor label) - (isogeny class label) - (curve identifier) separated by dashes, such as 31.1-a1" % escape(lab)))
     data = lab.split("-")
-    if len(data) != 3:
-        raise ValueError(Markup("<span style='color:black'>%s</span> is not a valid isogeny class label. It must be of the form (number field label) - (conductor label) - (isogeny class label) (separated by dashes), such as 2.2.5.1-31.1-a" % escape(lab)))
     field_label = data[0]
     conductor_label = data[1]
     isoclass_label = data[2]
@@ -77,9 +80,9 @@ def split_short_class_label(lab):
     r""" Split a short class label into 2 components
     (conductor_label,isoclass_label)
     """
+    if not SHORT_CLASS_LABEL_RE.fullmatch(lab):
+        raise ValueError(Markup("<span style='color:black'>%s</span> is not a valid elliptic curve label. It must be of the form (conductor label) - (isogeny class label) - (curve identifier) separated by dashes, such as 31.1-a1" % escape(lab)))
     data = lab.split("-")
-    if len(data) != 2:
-        raise ValueError(Markup("<span style='color:black'>%s</span> is not a valid isogeny class label. It must be of the form (conductor label) - (isogeny class label) (separated by dashes), such as 31.1-a" % escape(lab)))
     conductor_label = data[0]
     isoclass_label = data[1]
     return (conductor_label, isoclass_label)
@@ -249,16 +252,18 @@ def statistics():
 
 @ecnf_page.route("/<nf>/")
 def show_ecnf1(nf):
-    if "-" in nf:
-        try:
-            nf, cond_label, iso_label, number = split_full_label(nf.strip())
-        except ValueError:
-            return redirect(url_for("ecnf.index"))
+    if LABEL_RE.fullmatch(nf):
+        nf, cond_label, iso_label, number = split_full_label(nf)
         return redirect(url_for(".show_ecnf", nf=nf, conductor_label=cond_label, class_label=iso_label, number=number), 301)
+    if CLASS_LABEL_RE.fullmatch(nf):
+        nf, cond_label, iso_label = split_class_label(nf)
+        return redirect(url_for(".show_ecnf_isoclass", nf=nf, conductor_label=cond_label, class_label=iso_label), 301)
+    if not FIELD_RE.fullmatch(nf):
+        return abort(404)
     try:
         nf_label, nf_pretty = get_nf_info(nf)
     except ValueError:
-        return redirect(url_for(".index"))
+        return abort(404)
     if nf_label == '1.1.1.1':
         return redirect(url_for("ec.rational_elliptic_curves", **request.args), 301)
     info = to_dict(request.args, search_array=ECNFSearchArray())
@@ -275,6 +280,8 @@ def show_ecnf1(nf):
 
 @ecnf_page.route("/<nf>/<conductor_label>/")
 def show_ecnf_conductor(nf, conductor_label):
+    if not FIELD_RE.fullmatch(nf):
+        return abort(404)
     conductor_label = unquote(conductor_label)
     conductor_label = convert_IQF_label(nf,conductor_label)
     try:
@@ -299,6 +306,8 @@ def show_ecnf_conductor(nf, conductor_label):
 
 @ecnf_page.route("/<nf>/<conductor_label>/<class_label>/")
 def show_ecnf_isoclass(nf, conductor_label, class_label):
+    if not FIELD_RE.fullmatch(nf):
+        return abort(404)
     conductor_label = unquote(conductor_label)
     conductor_label = convert_IQF_label(nf,conductor_label)
     try:
@@ -328,6 +337,8 @@ def show_ecnf_isoclass(nf, conductor_label, class_label):
 
 @ecnf_page.route("/<nf>/<conductor_label>/<class_label>/<number>")
 def show_ecnf(nf, conductor_label, class_label, number):
+    if not FIELD_RE.fullmatch(nf):
+        return abort(404)
     conductor_label = unquote(conductor_label)
     conductor_label = convert_IQF_label(nf,conductor_label)
     try:
@@ -660,7 +671,10 @@ def download_ECNF_all(nf,conductor_label,class_label,number):
 
 @ecnf_page.route('/<nf>/<conductor_label>/<class_label>/<number>/download/<download_type>')
 def ecnf_code_download(**args):
-    response = make_response(ecnf_code(**args))
+    try:
+        response = make_response(ecnf_code(**args))
+    except ValueError:
+        return abort(404)
     response.headers['Content-type'] = 'text/plain'
     return response
 
@@ -692,7 +706,12 @@ def ecnf_code_download(**args):
 
 def ecnf_code(**args):
     label = "".join(["-".join([args['nf'], args['conductor_label'], args['class_label']]), args['number']])
-    E = ECNF.by_label(label)
+    if not LABEL_RE.fullmatch(label):
+        return abort(404)
+    try:
+        E = ECNF.by_label(label)
+    except ValueError:
+        return abort(404)
     Ecode = E.code()
     lang = args['download_type']
     code = "{} {} code for working with elliptic curve {}\n\n".format(Comment[lang],Fullname[lang],label)
diff --git a/lmfdb/elliptic_curves/elliptic_curve.py b/lmfdb/elliptic_curves/elliptic_curve.py
--- a/lmfdb/elliptic_curves/elliptic_curve.py
+++ b/lmfdb/elliptic_curves/elliptic_curve.py
@@ -680,6 +680,10 @@ def ec_code_download(**args):
 def ec_code(**args):
     label = curve_lmfdb_label(args['conductor'], args['iso'], args['number'])
     E = WebEC.by_label(label)
+    if E == "Invalid label":
+        return elliptic_curve_jump_error(label, {}, wellformed_label=False)
+    if E == "Curve not found":
+        return elliptic_curve_jump_error(label, {}, wellformed_label=True, missing_curve=True)
     Ecode = E.code()
     lang = args['download_type']
     code = "%s %s code for working with elliptic curve %s\n\n" % (Comment[lang],Fullname[lang],label)


Test patch: