# HG changeset patch # User Benoît Allard # Date 1414486528 -3600 # Node ID 6d3fb8592ff4d4316a852c05c374905acc287abf # Parent ce39a5267998e465638f7fb7f8a45c70323e2d29# Parent 81b6b71de62f10e8c615f36d23f4263187177dfc merged diff -r 81b6b71de62f -r 6d3fb8592ff4 CHANGES --- a/CHANGES Tue Oct 28 09:54:00 2014 +0100 +++ b/CHANGES Tue Oct 28 09:55:28 2014 +0100 @@ -1,3 +1,12 @@ +Farol next +========== + +Main changes since 0.2.2: +------------------------- +* Improve styling of the welcome page +* Add more client-side input validation (dates, version numbers) +* Add possibility to delete the current document + Farol 0.2.2 (2014-10-17) ======================== diff -r 81b6b71de62f -r 6d3fb8592ff4 farol/controller.py --- a/farol/controller.py Tue Oct 28 09:54:00 2014 +0100 +++ b/farol/controller.py Tue Oct 28 09:55:28 2014 +0100 @@ -35,10 +35,10 @@ except ImportError: from farolluz.py2 import FixedTimeZone as timezone -from flask import request +from flask import request, flash from farolluz.cvrf import CVRFNote, CVRFReference, CVRFAcknowledgment -from farolluz.parsers.cvrf import parseDate as parseXMLDate +from farolluz.parsers.cvrf import parseDate as parseXMLDate, parseVersion as parseXMLVersion def split_fields(field, separator=','): if not field: @@ -89,5 +89,15 @@ except AttributeError: pass # Absorb AttributeError, and try to parse it a second time ... m = re.match('(\d{4})-(\d{2})-(\d{2})', string) + if m is None: + flash('Cannot parse date: "%s"' % string, 'warning') + return None return datetime(int(m.group(1)), int(m.group(2)), int(m.group(3)), tzinfo=timezone(timedelta(hours=0, minutes=0))) + +def parseVersion(string): + """ An extended version, one that doesn't throw exceptions """ + try: return parseXMLVersion(string) + except ValueError: + flash('Cannot parse Version string: "%s"' % string) + return None diff -r 81b6b71de62f -r 6d3fb8592ff4 farol/document.py --- a/farol/document.py Tue Oct 28 09:54:00 2014 +0100 +++ b/farol/document.py Tue Oct 28 09:55:28 2014 +0100 @@ -25,7 +25,6 @@ from flask import (Blueprint, render_template, abort, redirect, request, url_for, flash) -from farolluz.parsers.cvrf import parseVersion from farolluz.cvrf import (CVRFNote, CVRFReference, CVRFPublisher, CVRFTracking, CVRFTrackingID, CVRFGenerator, CVRFRevision, CVRFAggregateSeverity) @@ -34,8 +33,8 @@ from .controller import (update_note_from_request, create_note_from_request, update_reference_from_request, create_reference_from_request, update_acknowledgment_from_request, create_acknowledgment_from_request, - split_fields, parseDate) -from .session import document_required, get_current + split_fields, parseDate, parseVersion) +from .session import document_required, get_current, del_current document = Blueprint('document', __name__) @@ -46,6 +45,11 @@ cvrf = get_current() return render_template('document/view.j2', cvrf=cvrf) +@document.route('/delete', methods=['POST']) +def delete(): + del_current() + return redirect(url_for('welcome')) + @document.route('/title/edit', methods=['GET', 'POST']) @document_required def edit_title(): @@ -87,7 +91,9 @@ aliases = split_fields(request.form['id_aliases']) tracking._identification._aliases = aliases tracking._status = request.form['status'] - tracking._version = parseVersion(request.form['version']) + version = parseVersion(request.form['version']) + if version is not None: + tracking._version = version tracking._initialDate = parseDate(request.form['initial']) tracking._currentDate = parseDate(request.form['current']) if wasNone: @@ -116,7 +122,9 @@ if request.method != 'POST': return render_template('document/edit_revision.j2', number='.'.join('%s'%v for v in revision._number), date=revision._date, description=revision._description, action='Update') - revision._number = parseVersion(request.form['number']) + version = parseVersion(request.form['number']) + if version is not None: + revision._number = version revision._date = parseDate(request.form['date']) revision._description = request.form['description'] return redirect(url_for('.view')) @@ -133,7 +141,7 @@ version = version[:-1] + (version[-1] + 1,) return render_template('document/edit_revision.j2', number='.'.join("%d"%v for v in version), date=utcnow(), action='Add') - version = parseVersion(request.form['number']) + version = parseVersion(request.form['number']) or (0,0) date = parseDate(request.form['date']) revision = CVRFRevision(version, date, request.form['description']) tracking.addRevision(revision) diff -r 81b6b71de62f -r 6d3fb8592ff4 farol/main.py --- a/farol/main.py Tue Oct 28 09:54:00 2014 +0100 +++ b/farol/main.py Tue Oct 28 09:55:28 2014 +0100 @@ -38,7 +38,7 @@ import flask from flask import (Flask, request, render_template, redirect, url_for, flash, - make_response) + make_response, abort) from werkzeug import secure_filename from . import __version__, cache @@ -87,9 +87,26 @@ def makeId(string): return secure_filename(string) +@app.errorhandler(400) +@app.errorhandler(404) +@app.errorhandler(405) +@app.errorhandler(500) +def error_page(error): + return render_template('error.j2', e=error), getattr(error, 'code', 500) + +@app.route('/500') +def boom(): + abort(500) + @app.route('/') def welcome(): - return render_template('welcome.j2') + return render_template('welcome.j2', + version=__version__, + imports=[('New', 100), ('CVRF', 100)], + exports=[('CVRF', 100), ('OpenVAS NASL from RHSA', 85), ('OVAL', 5) ], + use_cases=[('Create a security advisory and publish as CVRF', 100), + ('Edit a security advisory in CVRF format', 100)] + ) def set_url(url): try: content = urlopen(url).read() @@ -156,9 +173,6 @@ set_url(request.form['url']) elif 'local' in request.files: upload = request.files['local'] - if not upload.filename.endswith('.xml'): - flash('Uploaded files should end in .xml', 'danger') - return redirect(url_for('new')) fpath = os.path.join(app.instance_path, 'tmp', secure_filename(upload.filename)) if not os.path.exists(os.path.dirname(fpath)): diff -r 81b6b71de62f -r 6d3fb8592ff4 farol/producttree.py --- a/farol/producttree.py Tue Oct 28 09:54:00 2014 +0100 +++ b/farol/producttree.py Tue Oct 28 09:55:28 2014 +0100 @@ -62,7 +62,9 @@ @producttree.route('/delete', methods=['POST']) @producttree_required def delete(): - # XXX: We should first check if no PID and GID is used ... + if not cvrf.isProductTreeOrphan(): + flash('Not deleting the Product Tree, some Products are mentionned in the document', 'danger') + return redirect(url_for('.view')) get_current()._producttree = None return redirect(url_for('document.view')) @@ -142,7 +144,7 @@ cvrf = get_current() try: product = cvrf.getProductForID(productid) - except IndexError: + except KeyError: abort(404) return render_template('producttree/view_product.j2', product=product, groups=[g for g in cvrf._producttree._groups if productid in g._productids], @@ -194,6 +196,9 @@ # Link again product.link(ptree) + if (request.form['productid'] != product._productid) and not cvrf.isProductOrphan(product._productid): + flash('Also updating the ProductID for %s in this Document' % request.form['name'], 'info') + cvrf.changeProductID(product._productid, request.form['productid']) product._productid = request.form['productid'] product._name = request.form['name'] product._cpe = request.form['cpe'] or None @@ -237,6 +242,9 @@ product = cvrf.getProductForID(productid) except KeyError: abort(404) + if not cvrf.isProductOrphan(product._productid): + flash('Not deleting the Product, it is used in the Document.', 'danger') + return redirect(url_for('.view_product', productid=product._productid)) product.unlink() ptree._products.remove(product) del product @@ -308,13 +316,17 @@ @document_required @producttree_required def edit_group(groupid): + cvrf = get_current() try: - group = get_current().getGroupForID(groupid) + group = cvrf.getGroupForID(groupid) except KeyError: abort(404) if request.method != 'POST': return render_template('producttree/edit_group.j2', groupid=group._groupid, description=group._description, productids=group._productids) + if (request.form['groupid'] != group._groupid) and not cvrf.isGroupOrphan(group._groupid): + flash('Also updating the groupid in the whole document.', 'info') + cvrf.changeGroupID(group._groupid, request.form['groupid']) group._groupid = request.form['groupid'] group.setDescription(request.form['description'] or None) group._productids = [] @@ -347,6 +359,10 @@ flash('Group not found', 'danger') abort(404) + if not cvrf.isGroupOrphan(group._groupid): + flash('Not deleting group, it is mentionned in the document.', 'danger') + return redirect(url_for('.view')) + cvrf._producttree._groups.remove(group) return redirect(url_for('.view')) diff -r 81b6b71de62f -r 6d3fb8592ff4 farol/templates/about.j2 --- a/farol/templates/about.j2 Tue Oct 28 09:54:00 2014 +0100 +++ b/farol/templates/about.j2 Tue Oct 28 09:55:28 2014 +0100 @@ -30,14 +30,12 @@ {% block content %}
-

Security Advisories have existed for ever. Whenever someone discovered a danger, a vulnerability, he immediately started spreading the words about it.

-

In the IT World, each Party involved with security vulnerabilities have its own way of dealing with the matter, and although standards exist they aren't used much to their full extend.

-

This Platform is an attempt at bringing all those worlds together.

-

In the current version, Advisories not currently saved are kept in memory of the running process. If the process terminates, and they are not saved, documents are lost.

+

This web platform offers to review, create, edit and transform security advisories supporting various input and output formats.

+

During your session the advisory is stored in a cache from which you should save your changes to your local file system.

{% if config.DEBUG and not config.DEBUG_SURE %}

Debug Mode

diff -r 81b6b71de62f -r 6d3fb8592ff4 farol/templates/base.j2 --- a/farol/templates/base.j2 Tue Oct 28 09:54:00 2014 +0100 +++ b/farol/templates/base.j2 Tue Oct 28 09:55:28 2014 +0100 @@ -23,107 +23,83 @@ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. -#} +{% extends "layout.j2" %} + {% from "macros.j2" import modal, POST_button -%} - +{% block navbar %} + {% if has_current %} + Document + {% endif %} + {% if products %} + + {% endif %} + {% if vulnerabilities %} + + {% endif %} + {% if has_current %} +
  • + {% if error %} + + {% else %} + + {% endif %} +
  • + {% endif %} +{% endblock %} - - - - Farol - {% block title %}{% endblock %} - - - - - - - + {% endfor %} + + + {% endif %} +{% endblock %} + +{% block pre_content %} {% if has_current %} {% for element in cache %} {# Put the modals for the load action here #} @@ -137,32 +113,20 @@ {% endcall %} {% endfor %} {% endif %} -
    - {% with messages = get_flashed_messages(with_categories=True) %} - {% if messages %} -
    - {% for category, message in messages %} - {% if category == 'message' %}{% set category = "info" %}{% endif %} -
    {{ message }}
    - {% endfor %} -
    - {% endif %} - {% endwith %} -
    - - {% block content %}{% endblock %} + {% with messages = get_flashed_messages(with_categories=True) %} + {% if messages %} +
    + {% for category, message in messages %} + {% if category == 'message' %}{% set category = "info" %}{% endif %} +
    {{ message }}
    + {% endfor %}
    - {% if config.DEBUG and not config.DEBUG_SURE %} -
    DEBUG: This application is running in debug mode. See the about page for more Details
    {% endif %} -
    - - - + {% endwith %} +{% endblock %} + +{% block post_content %} + {% if config.DEBUG and not config.DEBUG_SURE %} +
    DEBUG: This application is running in debug mode. See the about page for more Details
    + {% endif %} +{% endblock %} diff -r 81b6b71de62f -r 6d3fb8592ff4 farol/templates/document/edit_revision.j2 --- a/farol/templates/document/edit_revision.j2 Tue Oct 28 09:54:00 2014 +0100 +++ b/farol/templates/document/edit_revision.j2 Tue Oct 28 09:55:28 2014 +0100 @@ -30,7 +30,7 @@ {% block content %}

    Revision contains all the elements required to track the evolution of a CVRF document. Each change to a CVRF document should be accompanied by Number, Date, and Description elements.

    - {% call textinput("number", "Number", "a.b.c.d", number, required=True) %} + {% call textinput("number", "Number", "a.b.c.d", number, required=True, regex='(0|[1-9][0-9]*)(\.(0|[1-9][0-9]*)){0,3}') %}

    Number should contain the numeric version of the document. Like the Version element above, it is a numeric tokenized field of the format “nn” with up to four fields “nn.nn.nn.nn”. It is recommended that this be a monotonically increasing value. Minor revisions should be used for less-significant changes (for example, 1.0.0.0 to 1.0.0.1). Major, actionable changes should lead to a major increase of the version number (for example, 1.0 to 2.0).

    Examples of such changes include:

      diff -r 81b6b71de62f -r 6d3fb8592ff4 farol/templates/document/edit_tracking.j2 --- a/farol/templates/document/edit_tracking.j2 Tue Oct 28 09:54:00 2014 +0100 +++ b/farol/templates/document/edit_tracking.j2 Tue Oct 28 09:55:28 2014 +0100 @@ -52,7 +52,7 @@

      Issuing parties are strongly recommended to set Status to Draft when initiating a new document and to implement procedures to ensure that the status is changed to the appropriate value before the document is released.

      {% endcall %} -{% call textinput("version", "Version", value=version, required=True) %} +{% call textinput("version", "Version", value=version, required=True, regex='(0|[1-9][0-9]*)(\.(0|[1-9][0-9]*)){0,3}') %}

      Version is a simple counter to track the version of the document. This is a numeric tokenized field of the format “nn” – “nn.nn.nn.nn”. It may be incremented in either major or minor notation to denote clearly the evolution of the content of the document. Issuing parties must ensure that this field is incremented appropriately, even for the least editorial or grammatical changes, when the field is used. It is validated using the following regular expression: (0|[1-9][0-9]*)(\.(0|[1-9][0-9]*)){0,3}.

      {{ examples(['1.0', '1.0.1', '1.0.0.1']) }} {% endcall %} diff -r 81b6b71de62f -r 6d3fb8592ff4 farol/templates/document/view.j2 --- a/farol/templates/document/view.j2 Tue Oct 28 09:54:00 2014 +0100 +++ b/farol/templates/document/view.j2 Tue Oct 28 09:55:28 2014 +0100 @@ -169,4 +169,13 @@ {% endcall %}
    +
    delete
    +{% call modal('delete_modal', 'Delete document') %} +

    This will delete the document {{ current_id }}.

    +

    Are you sure ?

    + +