Esempio n. 1
0
def delete_proposal(vuln_id: str = None):
    vulnerability_details = get_vulnerability_details(None,
                                                      vuln_id,
                                                      simplify_id=False)
    vuln = vulnerability_details.get_vulnerability()
    if not vuln:
        abort(404)

    if vuln.state == VulnerabilityState.PUBLISHED:
        flash_error("Can't delete a published entry w/o reverting it first")
        return redirect(url_for("profile.view_proposals"))

    if vuln.state == VulnerabilityState.ARCHIVED:
        flash_error("Can't delete an archived")
        return redirect(url_for("profile.view_proposals"))

    ensure(DELETE, vuln)

    if (request.method != "GET"
            and request.form.get("confirm", "false").lower() == "true"):
        db.session.delete(vuln)
        db.session.commit()
        flash("Entry deleted", "success")
        return redirect(url_for("profile.view_proposals"))
    return render_template("vulnerability/delete.html",
                           vuln_view=vulnerability_details.vulnerability_view)
Esempio n. 2
0
def update_proposal(vuln: Vulnerability, form: VulnerabilityDetailsForm):
    form.populate_obj(vuln)

    try:
        new_products = update_products(vuln)
    except InvalidProducts as ex:
        flash_error(ex.args[0])
        return None

    with db.session.no_autoflush:
        changes = vuln.model_changes()
    # ignore metadata
    clean_vulnerability_changes(changes)
    if not changes:
        flash_error("No changes detected. "
                    "Please modify the entry first to propose a change")
        return None
    log.debug("Detected changes: %r", changes)

    vuln.make_reviewable()
    db.session.add(vuln)
    db.session.commit()

    flash(
        "Your proposal is in the review queue. "
        "You can monitor progress in your Proposals Section.",
        "success",
    )
    return new_products
Esempio n. 3
0
def edit_proposal(vuln_id: str = None):
    vulnerability_details = _get_vulnerability_details(None,
                                                       vuln_id,
                                                       simplify_id=False)
    view = vulnerability_details.vulnerability_view
    vuln = vulnerability_details.get_or_create_vulnerability()
    ensure(EDIT, vuln)
    form = VulnerabilityDetailsForm(obj=vuln)

    # Populate the form data from the vulnerability view if necessary.
    if form.comment.data == "":
        form.comment.data = view.comment

    if request.method == 'POST' and not form.validate():
        flash_error("Your proposal contains invalid data, please correct.")

    form_submitted = form.validate_on_submit()
    if form_submitted and view.is_creator():
        new_products = update_proposal(vuln, form)
        if new_products is not None:
            view.products = [(p.vendor, p.product) for p in new_products]

    return render_template("profile/edit_proposal.html",
                           vulnerability_details=vulnerability_details,
                           form=form)
Esempio n. 4
0
def add_proposal(vuln: Vulnerability, view: VulnerabilityView,
                 form: VulnerabilityDetailsForm) -> Optional[Vulnerability]:
    """
    Attempts to create a proposal entry which is basically a copy of an existing Vulnerability entry.
    :param vuln:
    :param view:
    :param form:
    :return: A new Vulnerability copy of the existing entry.
    """
    vuln_clone = vuln.copy()
    form.populate_obj(vuln_clone)

    try:
        update_products(vuln_clone)
    except InvalidProducts as e:
        flash_error(e.args[0])
        return None

    with db.session.no_autoflush:
        changes = vuln.diff(vuln_clone)
    # ignore metadata
    changes.pop('date_modified', None)
    changes.pop('date_created', None)
    changes.pop('creator', None)
    changes.pop('state', None)
    changes.pop('version', None)
    changes.pop('prev_version', None)
    changes.pop('reviewer_id', None)
    changes.pop('reviewer', None)
    changes.pop('review_feedback', None)
    changes.pop('id', None)
    if not changes:
        flash_error(
            "No changes detected. Please modify the entry first to propose a change"
        )
        return None
    logging.debug("Detected changes: %r", changes)

    vuln_clone.version = None
    vuln_clone.prev_version = vuln.version
    vuln_clone.state = VulnerabilityState.READY
    vuln_clone.creator = g.user
    # Reset any previous feedback data.
    vuln_clone.reviewer_id = None
    vuln_clone.review_feedback = None

    db.session.add(vuln_clone)
    db.session.commit()
    if not vuln_clone.vcdb_id:
        # TODO: Improve this hack to assign a new vcdb_id here.
        #       Currently, we are just piggy backing on the auto increment of the primary key to ensure uniqueness.
        #       This will likely be prone to race conditions.
        vuln_clone.vcdb_id = vuln_clone.id
        db.session.add(vuln_clone)
        db.session.commit()

    flash("Your proposal will be reviewed soon.", "success")
    return vuln_clone
Esempio n. 5
0
def _can_add_proposal(vuln):
    existing_user_proposals = Vulnerability.query.filter(
        Vulnerability.vcdb_id == vuln.vcdb_id, Vulnerability.creator == g.user,
        Vulnerability.state != VulnerabilityState.PUBLISHED).first()
    if existing_user_proposals:
        flash_error(
            "You already have a pending/unprocessed proposal. Please go to your proposals section."
        )
        return False
    return True
Esempio n. 6
0
def _create_vuln_internal(vcdb_id=None):
    try:
        vulnerability_details = VulnerabilityDetails(vcdb_id)
        vulnerability = vulnerability_details.get_or_create_vulnerability()
    except InvalidIdentifierException as err:
        return flash_error(str(err), "frontend.serve_index")

    if vulnerability.id:
        logging.debug("Preexisting vulnerability entry found: %r",
                      vulnerability.id)
        delete_form = VulnerabilityDeleteForm()
        if delete_form.validate_on_submit():
            db.session.delete(vulnerability)
            # Remove the entry.
            db.session.commit()
            flash("The entry was deleted.", "success")
            return redirect("/")

    form = VulnerabilityDetailsForm(obj=vulnerability)
    commit = form.data["commits"][0]
    if not commit["repo_name"]:
        logging.info("Empty repository name. %r", commit)
        repo_url = commit["repo_url"]
        vcs_handler = get_vcs_handler(None, repo_url)
        if vcs_handler:
            logging.info("Found name. %r", vcs_handler.repo_name)
            form.commits[0].repo_name.process_data(vcs_handler.repo_name)

    if form.validate_on_submit():
        try:
            form.populate_obj(vulnerability)
            db.session.add(vulnerability)
            db.session.commit()
            # TODO: Improve this hack to assign a new vcdb_id here.
            #       Currently, we are just piggy backing on the auto increment
            #       of the primary key to ensure uniqueness.
            #       This will likely be prone to race conditions.
            vulnerability.vcdb_id = vulnerability.id
            db.session.add(vulnerability)
            db.session.commit()

            logging.debug("Successfully created/updated entry: %r",
                          vulnerability.id)
            flash("Successfully created/updated entry.", "success")
            return redirect(
                url_for("vuln.vuln_view", vcdb_id=vulnerability.vcdb_id))
        except InvalidIdentifierException as err:
            flash_error(str(err))

    return render_template(
        "vulnerability/create.html",
        vulnerability_details=vulnerability_details,
        form=form,
    )
Esempio n. 7
0
def forbidden_error(err):
    if g.user is None:
        return unauthorized_error(err)
    if cfg.DEBUG and not request.args.get("prod") == "true":
        return generic_error_page("Forbidden", err), 403

    location = request.referrer
    if not location or not location.startswith(request.url_root):
        location = "/"
    flash_error("This action is not allowed")
    return redirect(location)
Esempio n. 8
0
def _edit_vuln_internal(vcdb_id: str = None):
    vulnerability_details = get_vulnerability_details(vcdb_id, simplify_id=False)
    view = vulnerability_details.vulnerability_view
    vuln = vulnerability_details.get_or_create_vulnerability()

    if not _can_add_proposal(vuln):
        return redirect(url_for("vuln.vuln_view", vcdb_id=vcdb_id))

    # Populate the form data from the vulnerability view if necessary.
    # Updating the vuln instance allows to easier diff the changes.
    if vuln.comment == "":
        vuln.comment = view.comment
    form = VulnerabilityDetailsForm(obj=vuln)

    form_submitted = form.validate_on_submit()
    commit = form.data["commits"][0]

    # TODO: https://github.com/google/vulncode-db/issues/95 -
    #       Add support for non github.com entries long-term again.
    if commit["commit_link"] and "github.com" not in commit["commit_link"]:
        flash_error("Entries without a github.com link are currently not supported.")
        return redirect(url_for("vuln.vuln_view", vcdb_id=vcdb_id))

    if form_submitted and commit["commit_link"]:
        vcs_handler = get_vcs_handler(None, commit["commit_link"])
        if not vcs_handler:
            flash_error("Invalid commit link specified.")
            return render_template(
                "vulnerability/edit.html",
                vulnerability_details=vulnerability_details,
                form=form,
            )

        logging.info("Found name. %r", vcs_handler.repo_name)
        form.commits[0].repo_name.process_data(vcs_handler.repo_name)
        form.commits[0].repo_url.process_data(vcs_handler.repo_url)
        form.commits[0].commit_hash.process_data(vcs_handler.commit_hash)

    if form_submitted:
        proposal_vuln = add_proposal(vuln, form)
        if proposal_vuln:
            return redirect(
                url_for(
                    "vuln.vuln_review", vcdb_id=view.id, vuln_id=proposal_vuln.vcdb_id
                )
            )

    with db.session.no_autoflush:
        return render_template(
            "vulnerability/edit.html",
            vulnerability_details=vulnerability_details,
            form=form,
        )
Esempio n. 9
0
def delete_vuln(vcdb_id=None):
    # TODO implement revert & delete
    del vcdb_id
    abort(404)

    vulnerability_details = get_vulnerability_details(vcdb_id, None, simplify_id=False)
    vuln = vulnerability_details.get_vulnerability()
    if not vuln:
        abort(404)

    if vuln.state == VulnerabilityState.PUBLISHED:
        flash_error("Can't delete a published entry w/o reverting it first")
        return redirect(url_for("vuln.vuln_view"))
    if vuln.state == VulnerabilityState.ARCHIVED:
        flash_error("Can't delete an archived")
        return redirect(url_for("vuln.vuln_view"))
Esempio n. 10
0
def _can_add_proposal(vuln):
    # Conditions for creating a proposal:
    """
    - No pending open proposals by the same user.
    - Proposals can only be made for currently PUBLISHED entries only.
    """
    # TODO: Simplify or move away the query below.
    existing_user_proposals = Vulnerability.query.filter(
        or_(Vulnerability.vcdb_id == vuln.vcdb_id, Vulnerability.cve_id == vuln.cve_id), Vulnerability.creator == g.user,
        Vulnerability.state != VulnerabilityState.PUBLISHED,
        Vulnerability.state != VulnerabilityState.ARCHIVED).first()
    if existing_user_proposals:
        flash_error(
            "You already have a pending proposal for this entry. Please go to your proposals section."
        )
        return False
    return True
Esempio n. 11
0
def view_vuln(vcdb_id, use_template):
    try:
        vulnerability_details = VulnerabilityDetails(vcdb_id)
        vulnerability_details.validate_and_simplify_id()
        if not vulnerability_details.vulnerability_view:
            abort(404)
    except InvalidIdentifierException as err:
        return flash_error(str(err), "frontend.serve_index")
    return render_template(use_template, vulnerability_details=vulnerability_details)
Esempio n. 12
0
def _edit_vuln_internal(vcdb_id: str = None):
    vulnerability_details = _get_vulnerability_details(vcdb_id,
                                                       simplify_id=False)
    view = vulnerability_details.vulnerability_view
    vuln = vulnerability_details.get_or_create_vulnerability()

    if not _can_add_proposal(vuln):
        return redirect(url_for("vuln.vuln_view", vcdb_id=vcdb_id))

    # Populate the form data from the vulnerability view if necessary.
    # Updating the vuln instance allows to easier diff the changes.
    if vuln.comment == "":
        vuln.comment = view.comment
    form = VulnerabilityDetailsForm(obj=vuln)

    form_submitted = form.validate_on_submit()
    commit = form.data["commits"][0]
    if form_submitted and commit["commit_link"]:
        vcs_handler = get_vcs_handler(None, commit["commit_link"])
        if not vcs_handler:
            flash_error("Invalid commit link specified.")
            return render_template("vulnerability/edit.html",
                                   vulnerability_details=vulnerability_details,
                                   form=form)

        logging.info("Found name. %r", vcs_handler.repo_name)
        form.commits[0].repo_name.process_data(vcs_handler.repo_name)
        form.commits[0].repo_url.process_data(vcs_handler.repo_url)
        form.commits[0].commit_hash.process_data(vcs_handler.commit_hash)

    if form_submitted:
        proposal_vuln = add_proposal(vuln, view, form)
        if proposal_vuln:
            return redirect(
                url_for('vuln.vuln_review',
                        vcdb_id=view.id,
                        vuln_id=proposal_vuln.vcdb_id))

    with db.session.no_autoflush:
        return render_template("vulnerability/edit.html",
                               vulnerability_details=vulnerability_details,
                               form=form)
Esempio n. 13
0
def _edit_vuln_internal(vcdb_id: str = None):
    vulnerability_details = _get_vulnerability_details(vcdb_id,
                                                       simplify_id=False)
    view = vulnerability_details.vulnerability_view
    vuln = vulnerability_details.get_or_create_vulnerability()

    if not _can_add_proposal(vuln):
        return redirect(url_for("vuln.vuln_view", vcdb_id=vcdb_id))

    form = VulnerabilityDetailsForm(obj=vuln)

    # Populate the form data from the vulnerability view if necessary.
    if form.comment.data == "":
        form.comment.data = view.comment
    if form.comment.data == "":
        form.comment.data = view.comment

    form_submitted = form.validate_on_submit()
    commit = form.data["commits"][0]
    if form_submitted and commit["commit_link"]:
        vcs_handler = get_vcs_handler(None, commit["commit_link"])
        if not vcs_handler:
            flash_error("Invalid commit link specified.")
            return render_template("vulnerability/edit.html",
                                   vulnerability_details=vulnerability_details,
                                   form=form)

        logging.info("Found name. %r", vcs_handler.repo_name)
        form.commits[0].repo_name.process_data(vcs_handler.repo_name)
        form.commits[0].repo_url.process_data(vcs_handler.repo_url)
        form.commits[0].commit_hash.process_data(vcs_handler.commit_hash)

    if form_submitted:
        add_proposal(vuln, form)
        return redirect(url_for("vuln.vuln_view", vcdb_id=vcdb_id))

    return render_template("vulnerability/edit.html",
                           vulnerability_details=vulnerability_details,
                           form=form)
Esempio n. 14
0
def update_proposal(vuln: Vulnerability, form: VulnerabilityDetailsForm):
    form.populate_obj(vuln)

    try:
        new_products = update_products(vuln)
    except InvalidProducts as e:
        flash_error(e.args[0])
        return None

    with db.session.no_autoflush:
        changes = vuln.model_changes()
    # ignore metadata
    changes.pop('date_modified', None)
    changes.pop('date_created', None)
    changes.pop('creator', None)
    changes.pop('state', None)
    changes.pop('version', None)
    changes.pop('prev_version', None)
    changes.pop('reviewer_id', None)
    changes.pop('reviewer', None)
    changes.pop('review_feedback', None)
    changes.pop('id', None)
    if not changes:
        flash_error(
            "No changes detected. Please modify the entry first to propose a change"
        )
        return None
    log.debug('Detected changes: %r', changes)

    vuln.make_reviewable()
    db.session.add(vuln)
    db.session.commit()

    flash(
        "Your proposal is in the review queue. You can monitor progress in your Proposals Section.",
        "success")
    return new_products
Esempio n. 15
0
def _edit_vuln_internal(vcdb_id: str = None):
    try:
        vulnerability_details = VulnerabilityDetails(vcdb_id)
        vulnerability_view = vulnerability_details.vulnerability_view
        vulnerability = vulnerability_details.get_or_create_vulnerability()
    except InvalidIdentifierException as err:
        return flash_error(str(err), "frontend.serve_index")
    form = VulnerabilityDetailsForm(obj=vulnerability)

    # Populate the form data from the vulnerability view if necessary.
    if form.comment.data == "":
        form.comment.data = vulnerability_view.comment

    form_submitted = form.validate_on_submit()
    if form_submitted and _can_add_proposal(vulnerability):
        add_proposal(vulnerability, form)

    return render_template("vulnerability/edit.html",
                           vulnerability_details=vulnerability_details,
                           form=form)
Esempio n. 16
0
def _edit_vuln_internal(vcdb_id: str = None):
    try:
        vulnerability_details = VulnerabilityDetails(vcdb_id)
        vulnerability_view = vulnerability_details.vulnerability_view
        vulnerability = vulnerability_details.get_or_create_vulnerability()
    except InvalidIdentifierException as err:
        return flash_error(str(err), "frontend.serve_index")
    form = VulnerabilityDetailsForm(obj=vulnerability)

    # Populate the form data from the vulnerability view if necessary.
    if form.comment.data == "":
        form.comment.data = vulnerability_view.comment

    form_submitted = form.validate_on_submit()
    if form_submitted and _can_add_proposal(vulnerability):
        # TODO: This is incomplete/incorrect as the attached relationships such a GitCommit objects get updated.
        #       We have to ensure everything is properly detached and gets copied before any modifications happen.
        #       Currently, this will incorrectly update relationship objects instead of copying them.
        form.populate_obj(vulnerability)
        add_proposal(vulnerability)

    return render_template("vulnerability/edit.html",
                           vulnerability_details=vulnerability_details,
                           form=form)
Esempio n. 17
0
def vuln_review(vcdb_id, vuln_id):
    vulnerability_details = get_vulnerability_details(vcdb_id, simplify_id=False)
    vuln = vulnerability_details.get_or_create_vulnerability()

    proposal_vulnerability_details = get_vulnerability_details(
        None, vuln_id=vuln_id, simplify_id=False
    )
    proposal_vuln = proposal_vulnerability_details.get_or_create_vulnerability()

    ensure(READ, proposal_vuln)

    form_reject = VulnerabilityProposalReject()
    form_approve = VulnerabilityProposalApprove()
    form_assign = VulnerabilityProposalAssign()
    form_unassign = VulnerabilityProposalUnassign()
    form_publish = VulnerabilityProposalPublish()

    if request.method == "POST":
        if (
            request.form["review_response"] == "assign"
            and form_assign.validate_on_submit()
        ):
            ensure(ASSIGN, proposal_vuln)
            if proposal_vuln.is_reviewable:
                proposal_vuln.accept_review(g.user)
                db.session.add(proposal_vuln)
                db.session.commit()
                flash("The review was successfully assigned to you.", "success")
                return redirect(request.url)

            flash_error("This entry is not in a reviewable state.")

        if (
            request.form["review_response"] == "unassign"
            and form_unassign.validate_on_submit()
        ):
            ensure(ASSIGN, proposal_vuln)
            if proposal_vuln.is_reviewer(g.user):
                proposal_vuln.deny_review()
                db.session.add(proposal_vuln)
                db.session.commit()
                flash(
                    "You successfully unassigned yourself from this review.", "success"
                )
                return redirect(request.url)

            flash_error("This entry is not assigned to you.")

        if (
            request.form["review_response"] == "approve"
            and form_approve.validate_on_submit()
        ):
            ensure(APPROVE, proposal_vuln)
            proposal_vuln.accept_change()
            db.session.add(proposal_vuln)
            db.session.commit()
            flash(
                "You approved the proposal. "
                "Waiting for the entry to be published by an admin.",
                "success",
            )
            return redirect(request.url)

        if (
            request.form["review_response"] == "reject"
            and form_reject.validate_on_submit()
        ):
            ensure(REJECT, proposal_vuln)
            proposal_vuln.deny_change(g.user, form_reject.data["review_feedback"])
            db.session.add(proposal_vuln)
            db.session.commit()
            flash("Waiting for the author to address your feedback.", "success")
            return redirect(request.url)

        if (
            request.form["review_response"] == "publish"
            and form_publish.validate_on_submit()
        ):
            ensure("PUBLISH", proposal_vuln)
            proposal_vuln.publish_change()
            db.session.add(proposal_vuln)
            db.session.commit()
            # This might be the first entry of its kind
            # so no archiving is necessary.
            if vuln.state:
                vuln.archive_entry()
                db.session.add(vuln)
                db.session.commit()
            flash("Entry was successfully published.", "success")
            return redirect(request.url)

    # Published entries can't be reviewed.
    # if view.state == VulnerabilityState.PUBLISHED:
    #    raise RequestRedirect("/" + str(vcdb_id))
    return render_template(
        "vulnerability/review/review.html",
        proposal_vulnerability_details=proposal_vulnerability_details,
        vulnerability_details=vulnerability_details,
        form_assign=form_assign,
        form_unassign=form_unassign,
        form_reject=form_reject,
        form_approve=form_approve,
        form_publish=form_publish,
    )
Esempio n. 18
0
def vuln_review(vcdb_id, vuln_id):
    vulnerability_details = _get_vulnerability_details(vcdb_id,
                                                       simplify_id=False)
    view = vulnerability_details.vulnerability_view
    vuln = vulnerability_details.get_or_create_vulnerability()

    proposal_vulnerability_details = _get_vulnerability_details(
        None, vuln_id=vuln_id, simplify_id=False)
    proposal_view = proposal_vulnerability_details.vulnerability_view
    proposal_vuln = proposal_vulnerability_details.get_or_create_vulnerability(
    )

    form_reject = VulnerabilityProposalReject()
    form_approve = VulnerabilityProposalApprove()
    form_assign = VulnerabilityProposalAssign()
    form_publish = VulnerabilityProposalPublish()

    if request.method == 'POST':
        # TODO: Add proper ACL changes to all actions here.
        if request.form[
                "review_response"] == "assign" and form_assign.validate_on_submit(
                ):
            ensure('ASSIGN', proposal_vuln)
            if proposal_vuln.is_reviewable():
                proposal_vuln.accept_review(g.user)
                db.session.add(proposal_vuln)
                db.session.commit()
                flash("The review was successfully assigned to you.",
                      "success")
                return redirect(request.url)
            else:
                flash_error("This entry is not in a reviewable state.")

        if request.form[
                "review_response"] == "approve" and form_approve.validate_on_submit(
                ):
            ensure('APRROVE', proposal_vuln)
            proposal_vuln.accept_change()
            db.session.add(proposal_vuln)
            db.session.commit()
            flash(
                "You approved the proposal. Waiting for the entry to be published by an admin.",
                "success")
            return redirect(request.url)

        if request.form[
                "review_response"] == "reject" and form_reject.validate_on_submit(
                ):
            ensure('REJECT', proposal_vuln)
            proposal_vuln.deny_change(form_reject.data["review_feedback"])
            db.session.add(proposal_vuln)
            db.session.commit()
            flash("Waiting for the author to address your feedback.",
                  "success")
            return redirect(request.url)

        if request.form[
                "review_response"] == "publish" and form_publish.validate_on_submit(
                ):
            ensure('PUBLISH', proposal_vuln)
            proposal_vuln.publish_change()
            db.session.add(proposal_vuln)
            vuln.archive_entry()
            db.session.add(vuln)
            db.session.commit()
            flash("Entry was successfully published.", "success")
            return redirect(request.url)

    # Published entries can't be reviewed.
    # if view.state == VulnerabilityState.PUBLISHED:
    #    raise RequestRedirect("/" + str(vcdb_id))
    return render_template(
        "vulnerability/review.html",
        proposal_vulnerability_details=proposal_vulnerability_details,
        vulnerability_details=vulnerability_details,
        form_assign=form_assign,
        form_reject=form_reject,
        form_approve=form_approve,
        form_publish=form_publish)