def test_add_cve_does_not_overwrite_existing_cve(db, client): resp = client.post(url_for('tracker.add_cve'), follow_redirects=True, data=default_issue_dict(dict( cve=DEFAULT_ISSUE_ID, issue_type=issue_types[1], severity=Severity.critical.name, remote=Remote.remote.name, description='deadbeef', reference='https://security.archlinux.org', notes='very secret'))) assert 200 == resp.status_code assert CVE_MERGED.format(DEFAULT_ISSUE_ID) in resp.data.decode() form = CVEForm() unmerged_fields = [form.issue_type.label.text, form.severity.label.text, form.remote.label.text, form.description.label.text, form.notes.label.text] assert CVE_MERGED_PARTIALLY.format(DEFAULT_ISSUE_ID, ', '.join(unmerged_fields)) in resp.data.decode() cve = CVE.query.get(DEFAULT_ISSUE_ID) assert DEFAULT_ISSUE_ID == cve.id assert issue_types[3] == cve.issue_type assert Severity.low == cve.severity assert Remote.local == cve.remote assert 'foobar' == cve.description assert 'https://archlinux.org\nhttps://security.archlinux.org' == cve.reference assert 'the cake is a lie' == cve.notes
def copy_issue(issue): cve = db.get(CVE, id=issue) if not cve: return not_found() form = CVEForm() form.cve.data = cve.id form.description.data = cve.description form.issue_type.data = cve.issue_type form.notes.data = cve.notes form.reference.data = cve.reference form.remote.data = cve.remote.name form.severity.data = cve.severity.name return render_template('form/cve.html', title='Add CVE', form=form, CVE=CVE, action='/cve/add')
def edit_cve(cve): entries = (db.session.query(CVE, CVEGroup, Advisory) .filter(CVE.id == cve) .outerjoin(CVEGroupEntry, CVEGroupEntry.cve_id == CVE.id) .outerjoin(CVEGroup, CVEGroupEntry.group) .outerjoin(CVEGroupPackage, CVEGroup.packages) .outerjoin(Advisory, Advisory.group_package_id == CVEGroupPackage.id)).all() if not entries: return not_found() cve = entries[0][0] groups = set(group for (cve, group, advisory) in entries if group) advisories = set(advisory for (cve, group, advisory) in entries if advisory) if not user_can_edit_issue(advisories): return forbidden() form = CVEForm(edit=True) if not form.is_submitted(): form.cve.data = cve.id form.issue_type.data = cve.issue_type form.description.data = cve.description form.severity.data = cve.severity.name form.remote.data = cve.remote.name form.reference.data = cve.reference form.notes.data = cve.notes if not form.validate_on_submit(): if advisories: flash('WARNING: This is referenced by an already published advisory!', 'warning') return render_template('form/cve.html', title='Edit {}'.format(cve), form=form, CVE=CVE) severity = Severity.fromstring(form.severity.data) severity_changed = cve.severity != severity issue_type_changed = cve.issue_type != form.issue_type.data cve.issue_type = form.issue_type.data cve.description = form.description.data cve.severity = severity cve.remote = Remote.fromstring(form.remote.data) cve.reference = form.reference.data cve.notes = form.notes.data if severity_changed or issue_type_changed: # update cached group severity for all goups containing this issue group_ids = [group.id for group in groups] issues = (db.session.query(CVEGroup, CVE) .join(CVEGroupEntry, CVEGroup.issues) .join(CVE, CVEGroupEntry.cve) .group_by(CVEGroup.id).group_by(CVE.id)) if group_ids: issues = issues.filter(CVEGroup.id.in_(group_ids)) issues = (issues).all() if severity_changed: group_severity = defaultdict(list) for group, issue in issues: group_severity[group].append(issue.severity) for group, severities in group_severity.items(): group.severity = highest_severity(severities) # update scheduled advisories if the issue type changes if advisories and issue_type_changed: group_issue_type = defaultdict(set) for group, issue in issues: group_issue_type[group].add(issue.issue_type) for advisory in advisories: if Publication.published == advisory.publication: continue issue_types = group_issue_type[advisory.group_package.group] issue_type = 'multiple issues' if len(issue_types) > 1 else next(iter(issue_types)) advisory.advisory_type = issue_type if db.session.is_modified(cve): flash('Edited {}'.format(cve.id)) db.session.commit() return redirect('/{}'.format(cve.id))
def add_cve(): form = CVEForm() if not form.validate_on_submit(): return render_template('form/cve.html', title='Add CVE', form=form, CVE=CVE) cve = db.get(CVE, id=form.cve.data) if cve is not None: advisories = (db.session.query(Advisory).join( CVEGroupEntry, CVEGroupEntry.cve_id == cve.id).join( CVEGroup, CVEGroupEntry.group).join( CVEGroupPackage, CVEGroup.packages).filter( Advisory.group_package_id == CVEGroupPackage.id) ).all() if not user_can_edit_issue(advisories): flash(ERROR_ISSUE_REFERENCED_BY_ADVISORY.format(cve.id), 'error') return forbidden() not_merged = [] merged = False # try to merge issue_type if 'unknown' != form.issue_type.data: if 'unknown' == cve.issue_type: cve.issue_type = form.issue_type.data merged = True elif form.issue_type.data != cve.issue_type: not_merged.append(form.issue_type) form.issue_type.data = cve.issue_type # try to merge severity form_severity = Severity.fromstring(form.severity.data) if Severity.unknown != form_severity: if Severity.unknown == cve.severity: cve.severity = form_severity merged = True elif form_severity != cve.severity: not_merged.append(form.severity) form.severity.data = cve.severity.name # try to merge remote form_remote = Remote.fromstring(form.remote.data) if Remote.unknown != form_remote: if Remote.unknown == cve.remote: cve.remote = form_remote merged = True elif form_remote != cve.remote: not_merged.append(form.remote) form.remote.data = cve.remote.name # try to merge description if form.description.data: if not cve.description: cve.description = form.description.data merged = True elif form.description.data != cve.description: not_merged.append(form.description) form.description.data = cve.description # try to merge references references = cve.reference.splitlines() if cve.reference else [] old_references = references.copy() form_references = form.reference.data.splitlines( ) if form.reference.data else [] for reference in form_references: if reference not in references: references.append(reference) merged = True if old_references != references: cve.reference = '\n'.join(references) form.reference.data = cve.reference # try to merge notes if form.notes.data: if not cve.notes: cve.notes = form.notes.data merged = True elif form.notes.data != cve.notes: not_merged.append(form.notes) form.notes.data = cve.notes # if something got merged, commit and flash if merged: db.session.commit() flash(CVE_MERGED.format(cve.id)) # warn if something failed to be merged if not_merged: for field in not_merged: field.errors.append(ERROR_UNMERGEABLE) not_merged_labels = [field.label.text for field in not_merged] flash( CVE_MERGED_PARTIALLY.format(cve.id, ', '.join(not_merged_labels)), 'warning') return render_template('form/cve.html', title='Edit {}'.format(cve), form=form, CVE=CVE, action='{}/edit'.format(cve.id)) return redirect('/{}'.format(cve.id)) cve = CVE() cve.id = form.cve.data cve.issue_type = form.issue_type.data cve.description = form.description.data cve.severity = Severity.fromstring(form.severity.data) cve.remote = Remote.fromstring(form.remote.data) cve.reference = form.reference.data cve.notes = form.notes.data db.session.add(cve) db.session.commit() flash('Added {}'.format(cve.id)) return redirect('/{}'.format(cve.id))
def edit_cve(cve): entries = (db.session.query( CVE, CVEGroup, Advisory).filter(CVE.id == cve).outerjoin( CVEGroupEntry, CVEGroupEntry.cve_id == CVE.id).outerjoin( CVEGroup, CVEGroupEntry.group).outerjoin( CVEGroupPackage, CVEGroup.packages).outerjoin( Advisory, Advisory.group_package_id == CVEGroupPackage.id) ).all() if not entries: return not_found() cve: CVE = entries[0][0] groups = set(group for (cve, group, advisory) in entries if group) advisories = set(advisory for (cve, group, advisory) in entries if advisory) if not user_can_edit_issue(advisories): flash(ERROR_ISSUE_REFERENCED_BY_ADVISORY.format(cve.id), 'error') return forbidden() form = CVEForm(edit=True) if not form.is_submitted(): form.cve.data = cve.id form.issue_type.data = cve.issue_type form.description.data = cve.description form.severity.data = cve.severity.name form.remote.data = cve.remote.name form.reference.data = cve.reference form.notes.data = cve.notes form.changed.data = str(cve.changed) form.changed_latest.data = str(cve.changed) concurrent_modification = str(cve.changed) != form.changed.data if not form.validate_on_submit() or ( concurrent_modification and not (form.force_submit.data and str(cve.changed) == form.changed_latest.data)): if advisories: flash( 'WARNING: This is referenced by an already published advisory!', 'warning') issue = None code = 200 if concurrent_modification: flash('WARNING: The remote data has changed!', 'warning') code = Conflict.code issue = CVE() issue.id = form.cve.data issue.issue_type = form.issue_type.data issue.issue_type_mod = cve.issue_type != issue.issue_type issue.description = form.description.data issue.description_mod = cve.description != issue.description issue.severity = Severity.fromstring(form.severity.data) issue.severity_mod = cve.severity != issue.severity issue.remote = Remote.fromstring(form.remote.data) issue.remote_mod = cve.remote != issue.remote issue.reference = form.reference.data issue.reference_mod = cve.reference != issue.reference issue.notes = form.notes.data issue.notes_mod = cve.notes != issue.notes if form.changed_latest.data != cve.changed: form.force_submit.data = False form.changed_latest.data = str(cve.changed) Transaction = versioning_manager.transaction_cls VersionClassCVE = version_class(CVE) version = (db.session.query( Transaction, VersionClassCVE).outerjoin( VersionClassCVE, Transaction.id == VersionClassCVE.transaction_id).filter( VersionClassCVE.id == cve.id).order_by( Transaction.issued_at.desc())).first()[1] issue.transaction = version.transaction issue.operation_type = version.operation_type issue.previous = cve return render_template( 'form/cve.html', title='Edit {}'.format(cve), form=form, CVE=CVE, issue=issue, concurrent_modification=concurrent_modification, can_watch_user_log=user_can_watch_user_log()), code severity = Severity.fromstring(form.severity.data) severity_changed = cve.severity != severity issue_type_changed = cve.issue_type != form.issue_type.data cve.issue_type = form.issue_type.data cve.description = form.description.data cve.severity = severity cve.remote = Remote.fromstring(form.remote.data) cve.reference = form.reference.data cve.notes = form.notes.data if severity_changed or issue_type_changed: # update cached group severity for all goups containing this issue group_ids = [group.id for group in groups] issues = (db.session.query( CVEGroup, CVE).join(CVEGroupEntry, CVEGroup.issues).join( CVE, CVEGroupEntry.cve).group_by(CVEGroup.id).group_by(CVE.id)) if group_ids: issues = issues.filter(CVEGroup.id.in_(group_ids)) issues = (issues).all() if severity_changed: group_severity = defaultdict(list) for group, issue in issues: group_severity[group].append(issue.severity) for group, severities in group_severity.items(): group.severity = highest_severity(severities) # update scheduled advisories if the issue type changes if advisories and issue_type_changed: group_issue_type = defaultdict(set) for group, issue in issues: group_issue_type[group].add(issue.issue_type) for advisory in advisories: if Publication.published == advisory.publication: continue issue_types = group_issue_type[advisory.group_package.group] issue_type = 'multiple issues' if len( issue_types) > 1 else next(iter(issue_types)) advisory.advisory_type = issue_type if db.session.is_modified(cve) or severity_changed or issue_type_changed: cve.changed = datetime.utcnow() flash('Edited {}'.format(cve.id)) db.session.commit() return redirect('/{}'.format(cve.id))