view farol/main.py @ 167:000114da182d

New lifting for the 'new' page
author Benoît Allard <benoit.allard@greenbone.net>
date Mon, 05 Jan 2015 11:38:46 +0100
parents 4d8218fbe686
children 964d7caf70b0
line wrap: on
line source
# -*- encoding: utf-8 -*-
# Description:
# Farol Web Application
#
# Authors:
# BenoƮt Allard <benoit.allard@greenbone.net>
#
# Copyright:
# Copyright (C) 2014 Greenbone Networks GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.

import os
import logging
from logging import FileHandler
import platform
import urllib2
from xml.etree import ElementTree as ET

import farolluz
from farolluz.cvrf import CVRF, ValidationError
from farolluz.parsers.cve import parse_CVE_from_GSA
from farolluz.parsers.cvrf import parse
from farolluz.renderer import render as render_cvrf
from farolluz.utils import utcnow

import flask
from flask import (Flask, request, render_template, redirect, url_for, flash,
     make_response, abort)
from werkzeug import secure_filename
import jinja2

from . import __version__, cache
from .document import document
from .session import get_current, set_current, has_current, document_required
from .vulnerability import vulnerability
from .producttree import producttree

app = Flask(__name__, instance_relative_config=True)
app.config.from_object('farol.config.Config')
app.config.from_pyfile('farol.cfg', silent=True)

app.register_blueprint(cache.mod, url_prefix='/cache')
app.register_blueprint(document, url_prefix='/document')
app.register_blueprint(vulnerability, url_prefix='/vulnerability')
app.register_blueprint(producttree, url_prefix='/producttree')

file_handler = FileHandler(os.path.join(app.instance_path, 'farol.log'))
file_handler.setLevel(logging.WARNING)
app.logger.addHandler(file_handler)

@app.context_processor
def cache_content():
    """ List the documents in cache """
    return dict(caching=cache.caching_type(),
                cache=cache.cache_content())

@app.context_processor
def doc_properties():
    if not has_current():
        return {'has_current': False}
    cvrf = get_current()
    vulns = [(v.getTitle(), v._ordinal) for v in cvrf._vulnerabilities]
    prods = []
    if cvrf._producttree:
        prods = [(p._name, p._productid) for p in cvrf._producttree._products]
    try:
        cvrf.validate()
        error = None
    except ValidationError as ve:
        error = ve
    return dict(has_current=True, vulnerabilities=vulns, products=prods,
                error=error, current_id=cvrf.getDocId())

@app.template_filter('secure_filename')
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',
        version=__version__,
        imports=[('New', 100), ('CVRF', 100), ('CVE', 90)],
        exports=[('CVRF', 100), ('OpenVAS NASL from RHSA', 85), ('HTML', 80), ('OVAL', 5) ],
        use_cases=[('Create a security advisory and publish as CVRF', 100),
                   ('Edit a security advisory in CVRF format', 100)]
    )

def download_url(url):
    request = urllib2.Request(url)
    request.add_header('User-Agent',
                       app.config.get('USER_AGENT',
                                      'Farol %s / FarolLuz %s' % (
        __version__, farolluz.__version__)))
    proxy_host = app.config.get('PROXY_HOST', '')
    if proxy_host:
        proxy = urllib2.ProxyHandler({'http': proxy_host, 'https': proxy_host})
        opener = urllib2.build_opener(proxy)
        urllib2.install_opener(opener)
    try: content = urllib2.urlopen(url).read()
    except urllib2.HTTPError as e:
        flash('Unable to retrieve %s: %s' % (url, e))
        return
    set_text(content)

def set_RHSA(id_):
    # validate input
    if ':' not in id_:
        flash('Wrong RHSA id: %s' % id_)
        return
    year, index = id_.split(':', 1)
    try:
        int(year)
        int(index)
    except ValueError:
        flash('Wrong RHSA id: %s' % id_)
        return
    # Process it
    download_url("https://www.redhat.com/security/data/cvrf/%(year)s/cvrf-rhsa-%(year)s-%(index)s.xml" % {'year': year, 'index': index})

def set_oracle(id_):
    try: int(id_)
    except ValueError:
        flash('Wrong Oracle id: %s' % id_)
        return
    download_url("http://www.oracle.com/ocom/groups/public/@otn/documents/webcontent/%s.xml" % id_)

def set_cisco(id_):
    if id_.count('-') < 2:
        flash('Wrong cisco id: %s' % id_)
        return
    kind, date, name = id_.split('-', 2)
    try: kind = {'sa': 'Advisory', 'sr': 'Response'}[kind]
    except KeyError:
        flash('Wrong cisco id: %s' % id_)
        return
    download_url("http://tools.cisco.com/security/center/contentxml/CiscoSecurity%(kind)s/cisco-%(id)s/cvrf/cisco-%(id)s_cvrf.xml" % {'kind': kind, 'id': id_})

def parse_cve_from_gsa(id_):
    url = 'https://secinfo.greenbone.net/omp?cmd=get_info&info_type=cve&info_id=%s&details=1&token=guest&xml=1' % id_
    try: content = urllib2.urlopen(url).read()
    except urllib2.HTTPError as e:
        flash('Unable to download CVE %s: %s' % (url, e))
        return
    doc = parse_CVE_from_GSA(content)
    set_current(doc)

def set_text(text):
    try: doc = parse(text)
    except ET.ParseError as e:
        flash('Unable to parse Document: %s' % e)
        return
    set_current(doc)

@app.route('/new', methods=['GET', 'POST'])
def new():
    if request.method != 'POST':
        input_choices = [
            ('RHSA', 'YYYY:nnnn', '2014:0981', """
RedHat publishes their advisories in CVRF format since May 2012
covering all of their products.

Redhat provides a FAQ about the CVRF support here:
https://access.redhat.com/articles/124913

Farol downloads the CVRF documents from this location:
https://www.redhat.com/security/data/cvrf/
"""),
            ('Oracle', 'nnnnnnn', '2188432', """
Oracle uses the CVRF format to publish their Critical Patch Updates (CPUs).

Oracle published an article about adopting CVRF:
https://blogs.oracle.com/security/entry/use_of_the_common_vulnerability

The FAQ for the CPUs is available here:
http://www.oracle.com/technetwork/topics/security/cpufaq-098434.html

Farol downloads the CVRF documents from this location:
http://www.oracle.com/ocom/groups/public/@otn/documents/webcontent
"""),
            ('Cisco', 'sa-YYYYMMDD-xxx', 'sa-20140605-openssl', """
Cisco was one of the main actors driving the CVRF format.

See also the Blog post at CISCO:
http://blogs.cisco.com/tag/cvrf

Farol downloads the CVRF documents from this location:
http://tools.cisco.com/security/center/contentxml
"""),
            ('CVE', 'CVE-YYYY-NNNN', 'CVE-2014-7088', """
It is possible to convert CVE information into CVRF format.

Read here about Common Vulnerabilities and Exposures (CVEs):
http://cve.mitre.org/

MITRE publishes CVE in CVRF Format:
https://cve.mitre.org/cve/cvrf.html

However, those CVRF documents do not cover all of the CVE content.
Therefore, Farol downloads the XML object of CVEs from Greenbone's
SecInfo Portal via the web interface "Greenbone Security Assistant".
The CVE XML data retrieved from there are identical to the CVE
publication by NIST.

Greenbone's SecInfo Portal:
https://secinfo.greenbone.net
"""),
            ('URL', 'https://...', 'http://www.greenbone.net/download/gbsa/gbsa2013-01.cvrf', """
Farol can download a given URL for a CVRF document.

The provided example is a Greenbone Security Advisory from
http://www.greenbone.net/technology/security.html
""")
        ]
        return render_template('new.j2', input_choices=input_choices, has_document=has_current(), now=utcnow())

    if 'rhsa' in request.form:
        set_RHSA(request.form['id'])
    elif 'oracle' in request.form:
        set_oracle(request.form['id'])
    elif 'cisco' in request.form:
        set_cisco(request.form['id'])
    elif 'nasl' in request.form:
        flash("I'm not able to parse NASL scripts yet", 'danger')
        return redirect(url_for('new'))
    elif 'url' in request.form:
        download_url(request.form['id'])
    elif 'cve' in request.form:
        parse_cve_from_gsa(request.form['id'])
    elif 'local' in request.files:
        upload = request.files['local']
        fpath = os.path.join(app.instance_path, 'tmp',
                             secure_filename(upload.filename))
        if not os.path.exists(os.path.dirname(fpath)):
            os.makedirs(os.path.dirname(fpath))
        upload.save(fpath)
        with open(fpath, 'rt') as f:
            set_text(f.read())
        os.remove(fpath)
    elif 'text' in request.form:
        set_text(request.form['text'].encode('utf-8'))
    else:
        set_current(CVRF(request.form['title'], request.form['type']))
    return redirect(url_for('document.view'))

@app.route('/render/<format_>')
@document_required
def render(format_):
    cvrf = get_current()
    doc = render_cvrf(cvrf, format_ + '.j2')
    if 'raw' not in request.args:
        return render_template('render.j2', format_=format_, title=cvrf._title, type_=cvrf._type, doc=doc)
    response = make_response(doc)
    filename = secure_filename(cvrf.getDocId()) + "." + format_
    response.headers["content-disposition"] = 'attachement; filename=' + filename
    response.headers["content-type"] = 'text/plain'
    return response

@app.route('/about')
def about():
    versions = ((platform.python_implementation(), platform.python_version()),
                ('Farol', __version__), ('FarolLuz', farolluz.__version__),
                ('Flask', flask.__version__), ('Jinja', jinja2.__version__))
    return render_template('about.j2', instance_dir=app.instance_path, versions=versions)

http://farol.wald.intevation.org