diff --git a/CHANGELOG.md b/CHANGELOG.md
index b010bc8..640ec77 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,10 +1,26 @@
# VINCE Changelog
+## Version 2.0.5 2023-01-04
+
+* Update to CVE2.1 Services Publish using CVE5 JSON
+* More Async functions for vendor status views
+* Added more common libraries to lib/vince/utils
+* Added a mute_lib.py to support mute a Case for a user in automated way
+* Fixed a number of small bugs in max length in FORM submissions and S3 sensitive filenames
+
+## Version 2.0.4: 2022-12-20
+
+* Added Filter to CaseView in VinceComm
+* Addition of more Async functions for non-interactive queries
+* Fixing of slow performance on allvendors view to use Django Aggregate and Filter/Q functions
+* Friendly errors and fixes for logging to add IP address of remote client
+
+
## Version 2.0.3: 2022-12-14
* Major upgrade to Django 3.2 LTS target end byt 2024. Fixes related to Django upgrade in all libraries.
-* Aded new QuerySet Paging library for performance extend chain with chainqs for QuerySet
+* Aded new QuerySet Paging library for performance extend chain with chainqs for QuerySet
* Asynchronous calls for most vinny/views via JSON through asyncLoad class
* Provide API Views 404 with JSON generic error
* Allow Session or API Token authentication to support API access from browser
diff --git a/bigvince/settings_.py b/bigvince/settings_.py
index 845db1a..acb61f6 100644
--- a/bigvince/settings_.py
+++ b/bigvince/settings_.py
@@ -56,7 +56,7 @@
ROOT_DIR = environ.Path(__file__) - 3
# any change that requires database migrations is a minor release
-VERSION = "2.0.3"
+VERSION = "2.0.5"
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/2.1/howto/deployment/checklist/
diff --git a/lib/vince/mutelib.py b/lib/vince/mutelib.py
new file mode 100644
index 0000000..9c11f2d
--- /dev/null
+++ b/lib/vince/mutelib.py
@@ -0,0 +1,39 @@
+from django.contrib.auth.models import User
+from vinny.views import VinceProfile as vp
+
+
+
+def mute_user(useremail,case_id,interactive=False):
+ """ Mute case for a user with `useremail` identified for a `case_id`
+ on success it return 0 (no need to update) or 1. If the user is not
+ found or user has nor profile, it returns -ve number repsectively.
+ You should use this with try/except block for web/API usage
+ """
+ q = User.objects.filter(username=useremail).using('vincecomm').first()
+ l = vp.objects.filter(user=q).first()
+ if not q:
+ if interactive:
+ print(f"User {useremail} not found")
+ return -1
+ if not l:
+ if interactive:
+ print(f"User {useremail} Profile not found")
+ return -2
+ d = q.vinceprofile.settings.copy()
+ if 'muted_cases' in d:
+ if case_id in d['muted_cases']:
+ if interactive:
+ print(f"Case id {case_id} already muted for {useremail}")
+ print(d)
+ return 0
+ else:
+ d['muted_cases'] += [case_id]
+ else:
+ d['muted_cases'] = [case_id]
+ l._set_settings(d)
+ l.save()
+ if interactive:
+ print("Updated profile settings are ")
+ print(l._get_settings())
+ return 1
+
diff --git a/lib/vince/utils.py b/lib/vince/utils.py
new file mode 100644
index 0000000..8076f0b
--- /dev/null
+++ b/lib/vince/utils.py
@@ -0,0 +1,60 @@
+import inspect
+import pathlib
+import mimetypes
+import uuid
+import re
+#Utilities for VINCE to use that are generic
+
+def get_ip(request):
+ """ GET IP address of a request object and find it using simple
+ method of the first X-Forwarded-For header IP from proxy/web server
+ or the REMOTE_ADDR environment setup by the appserver. Returns a
+ string not an IP validated item/object.
+ """
+ try:
+ if request.META.get('HTTP_X_FORWARDED_FOR'):
+ return request.META.get('HTTP_X_FORWARDED_FOR').split(',')[0]
+ elif request.META.get('REMOTE_ADDR'):
+ return request.META.get('REMOTE_ADDR')
+ else:
+ return "Unknown"
+ except Exception as e:
+ return f"IP lookup Exception {e}"
+ return "Unknown"
+
+
+def deepGet(obj,idir):
+ """ Given an object of any kind find if it is a dictionary
+ or a list or an abstract object or instance of a class
+ that has a burried element.
+ """
+ x = obj
+ for s in idir.split("."):
+ if not x:
+ return None
+ if isinstance(x,dict) and s in x:
+ x = x[s]
+ elif isinstance(x,list) and s.isdigit() and int(s) < len(x):
+ x = x[int(s)]
+ elif hasattr(x,s):
+ x = getattr(x,s)
+ if callable(x) and not inspect.isclass(x):
+ x = x()
+ else:
+ return None
+ return x
+
+def safe_filename(filename,file_uuid=str(uuid.uuid4()),mime_type="application/octet-stream"):
+ filename = filename.replace("\r"," ").replace("\n"," ").strip()
+ if re.search(r'[^\x00-\x7F]+',filename):
+ #non-ascii filenames use uuid and extension
+ if file_uuid == None:
+ file_uuid = uuid.uuid4()
+ file_extension = "".join(pathlib.Path(filename).suffixes)
+ if file_extension:
+ filename = file_uuid + file_extension
+ elif mimetypes.guess_extension(mime_type):
+ filename = file_uuid + mimetypes.guess_extension(mime_type)
+ else:
+ filename = file_uuid
+ return filename
diff --git a/vince/forms.py b/vince/forms.py
index cd5481e..234c1c0 100644
--- a/vince/forms.py
+++ b/vince/forms.py
@@ -2344,7 +2344,7 @@ class CVEAffectedProductForm(forms.ModelForm):
widget = forms.Select(attrs={'class': 'form-control'}),
label=_('Version Affected'),
required=False,
- choices=([('None', None), ('<', '< (affects X versions prior to n)'), ('<=', '<= (affects X versions up to n)'), ('=', '= (affects n)'), ('>', '> (affects X versions above n)'), ('>=', '>= (affects X versions n and above)')])
+ choices=([('None', None), ('lessThan', '< (affects X versions prior to n)'), ('lessThanOrEqual', '<= (affects X versions up to n)')])
)
class Meta:
@@ -2358,13 +2358,13 @@ class CVEReferencesForm(forms.Form):
widget = forms.Select(attrs={'class': 'form-control'}),
label=_('Reference Source'),
required=True,
- choices=([('URL', 'URL'),('CERT-VN', 'CERT-VN'), ('MISC', 'MISC'), ('CONFIRM', 'CONFIRM')])
+ choices=([('URL', 'URL')])
)
reference = forms.URLField(
label=_('Reference'),
widget = forms.TextInput(attrs={'placeholder': 'e.g., https://dhs.gov.'}),
- help_text = 'Please provide reference URL.',
+ help_text = 'Please provide reference URL. Do not add kb.cert.org reference. It will be automatically generated by the VU#',
max_length=500
)
diff --git a/vince/models.py b/vince/models.py
index 08e5d05..475eeef 100644
--- a/vince/models.py
+++ b/vince/models.py
@@ -48,6 +48,7 @@
#Django 3 and up
from django.db.models import JSONField
import io
+from lib.vince import utils as vinceutils
logger = logging.getLogger(__name__)
@@ -1268,7 +1269,8 @@ def __str__(self):
return '%s' % self.filename
def _get_access_url(self):
- url = self.file.storage.url(self.file.name, parameters={'ResponseContentDisposition': f'attachment; filename="{self.filename}"'})
+ filename = vinceutils.safe_filename(self.filename,str(self.uuid),self.mime_type)
+ url = self.file.storage.url(self.file.name, parameters={'ResponseContentDisposition': f'attachment; filename="{filename}"'})
return url
access_url = property(_get_access_url)
@@ -1680,8 +1682,6 @@ class VulNoteRevision(BaseRevisionMixin, models.Model):
search_vector = SearchVectorField(null=True)
def __str__(self):
- logger.debug("in revision")
- logger.debug(self.content)
if self.revision_number:
return "%s (%d)" % (self.title, self.revision_number)
else:
@@ -3821,7 +3821,6 @@ def complete(self):
else:
return False
if self.cve_name and self.date_public and len(refs) and len(cwes):
- logger.debug(len(self.references))
return True
else:
return False
@@ -3836,19 +3835,19 @@ class CVEAffectedProduct(models.Model):
max_length=200)
version_name = models.CharField(
- _('Affected Version'),
+ _('Version Range End'),
blank=True,
null=True,
max_length=100)
version_affected = models.CharField(
- _('Version Affected'),
+ _('Version Range Type'),
blank=True,
null=True,
max_length=10)
version_value = models.CharField(
- _('Affected Version Value'),
+ _('Affected Version or Start'),
max_length=100)
organization = models.CharField(
diff --git a/vince/static/vince/css/overrides.css b/vince/static/vince/css/overrides.css
index 7001e1c..c8ac0c8 100644
--- a/vince/static/vince/css/overrides.css
+++ b/vince/static/vince/css/overrides.css
@@ -24,11 +24,11 @@
height: auto !important;
}
#offCanvas ul.vertical.menu > li.menu-close {
- position: fixed;
- right: 15%;
- top: 0px;
- border: none;
- padding-top: 0px;
+ position: fixed;
+ right: 15%;
+ top: 0px;
+ border: none;
+ padding-top: 0px;
}
#header {
height: auto !important;
@@ -48,3 +48,74 @@
top: 0%;
}
}
+nav.cdown {
+ display:inline-block;
+}
+
+nav.cdown ul {
+ background: #ff8c00;
+ list-style: none;
+ margin: 0;
+ padding-left: 0;
+}
+
+nav.cdown li {
+ color: #fff;
+ background: #ff8c00;
+ display: block;
+ float: left;
+ font-size: 0.8rem;
+ padding: 0.3rem 0.4rem 0.1rem 0.4rem;
+ position: relative;
+ text-decoration: none;
+ transition-duration: 0.5s;
+}
+
+nav.cdown li a {
+ color: #fff;
+}
+
+nav.cdown li:hover {
+ background: #dc3545;
+ cursor: pointer;
+}
+
+nav.cdown ul li ul {
+ background: #ffa500;
+ visibility: hidden;
+ opacity: 0;
+ min-width: 9rem;
+ position: absolute;
+ transition: all 0.5s ease;
+ margin-top: 1rem;
+ right: 0;
+ display: none;
+ top: 0.55rem;
+}
+
+nav.cdown ul li:hover > ul,
+nav.cdown ul li:focus-within > ul,
+nav.cdown ul li ul:hover {
+ visibility: visible;
+ opacity: 1;
+ display: block;
+}
+
+nav.cdown ul li ul li {
+ clear: both;
+ width: 100%;
+}
+nav.cdown .fa-check {
+ font-size: 0.7rem;
+ opacity: 0;
+}
+nav.cdown .all .fa-check {
+ opacity: 1;
+}
+nav.cdown li.affected {
+ background-color: #990033;
+}
+nav.cdown li.not_affected {
+ background-color: #3adb76;
+}
+
diff --git a/vince/static/vince/js/addvuls.js b/vince/static/vince/js/addvuls.js
index 51e77d3..98ce5ef 100644
--- a/vince/static/vince/js/addvuls.js
+++ b/vince/static/vince/js/addvuls.js
@@ -129,6 +129,11 @@ function del_tag(taggle, tag, modal){
$(document).ready(function() {
+ if($('#largemodal').length < 1) {
+ $('body').prepend('
');
+ }
+ let _ = new Foundation.Reveal($('#largemodal'));
var modal = $("#largemodal");
var vul_tags = [];
diff --git a/vince/static/vince/js/case.js b/vince/static/vince/js/case.js
index ef49993..cd60b48 100644
--- a/vince/static/vince/js/case.js
+++ b/vince/static/vince/js/case.js
@@ -1,38 +1,43 @@
/*#########################################################################
-# VINCE
-#
-# Copyright 2022 Carnegie Mellon University.
-#
-# NO WARRANTY. THIS CARNEGIE MELLON UNIVERSITY AND SOFTWARE ENGINEERING
-# INSTITUTE MATERIAL IS FURNISHED ON AN "AS-IS" BASIS. CARNEGIE MELLON
-# UNIVERSITY MAKES NO WARRANTIES OF ANY KIND, EITHER EXPRESSED OR IMPLIED,
-# AS TO ANY MATTER INCLUDING, BUT NOT LIMITED TO, WARRANTY OF FITNESS FOR
-# PURPOSE OR MERCHANTABILITY, EXCLUSIVITY, OR RESULTS OBTAINED FROM USE OF THE
-# MATERIAL. CARNEGIE MELLON UNIVERSITY DOES NOT MAKE ANY WARRANTY OF ANY KIND
-# WITH RESPECT TO FREEDOM FROM PATENT, TRADEMARK, OR COPYRIGHT INFRINGEMENT.
-#
-# Released under a MIT (SEI)-style license, please see license.txt or contact
-# permission@sei.cmu.edu for full terms.
-#
-# [DISTRIBUTION STATEMENT A] This material has been approved for public
-# release and unlimited distribution. Please see Copyright notice for non-US
-# Government use and distribution.
-#
-# Carnegie Mellon®, CERT® and CERT Coordination Center® are registered in the
-# U.S. Patent and Trademark Office by Carnegie Mellon University.
-#
-# This Software includes and/or makes use of Third-Party Software each subject
-# to its own license.
-#
-# DM21-1126
-########################################################################
+ # VINCE
+ #
+ # Copyright 2022 Carnegie Mellon University.
+ #
+ # NO WARRANTY. THIS CARNEGIE MELLON UNIVERSITY AND SOFTWARE ENGINEERING
+ # INSTITUTE MATERIAL IS FURNISHED ON AN "AS-IS" BASIS. CARNEGIE MELLON
+ # UNIVERSITY MAKES NO WARRANTIES OF ANY KIND, EITHER EXPRESSED OR IMPLIED,
+ # AS TO ANY MATTER INCLUDING, BUT NOT LIMITED TO, WARRANTY OF FITNESS FOR
+ # PURPOSE OR MERCHANTABILITY, EXCLUSIVITY, OR RESULTS OBTAINED FROM USE OF THE
+ # MATERIAL. CARNEGIE MELLON UNIVERSITY DOES NOT MAKE ANY WARRANTY OF ANY KIND
+ # WITH RESPECT TO FREEDOM FROM PATENT, TRADEMARK, OR COPYRIGHT INFRINGEMENT.
+ #
+ # Released under a MIT (SEI)-style license, please see license.txt or contact
+ # permission@sei.cmu.edu for full terms.
+ #
+ # [DISTRIBUTION STATEMENT A] This material has been approved for public
+ # release and unlimited distribution. Please see Copyright notice for non-US
+ # Government use and distribution.
+ #
+ # Carnegie Mellon®, CERT® and CERT Coordination Center® are registered in the
+ # U.S. Patent and Trademark Office by Carnegie Mellon University.
+ #
+ # This Software includes and/or makes use of Third-Party Software each subject
+ # to its own license.
+ #
+ # DM21-1126
+ ########################################################################
*/
+function alertmodal(modal,msg) {
+ modal.html("
" +
+ msg + "
")
+ .foundation('open');
+}
function permissionDenied(modal) {
-
- modal.html("
Error: You are not permitted to perform this action
").foundation('open');
-
+ alertmodal(modal,"Error: You are not permitted to perform this action");
}
function init_modal_markdown() {
@@ -85,10 +90,10 @@ function reloadVendorStats(case_id) {
function reloadVendors(case_id, tablet) {
tablet.replaceData();
/*$.ajax({
- url: "/vince/ajax_calls/case/vendors/"+case_id+"/",
- success: function(data) {
+ url: "/vince/ajax_calls/case/vendors/"+case_id+"/",
+ success: function(data) {
- }});*/
+ }});*/
reloadVendorStats(case_id);
}
@@ -161,7 +166,7 @@ function searchComms(e) {
error: function() {
lockunlock(false,'div.mainbody,div.vtmainbody','#timeline');
console.log(arguments);
- alert("Search failed or canceled! See console log for details.");
+ alert("Search failed or canceled! See console log for details.");
},
complete: function() {
/* Just safety net */
@@ -200,7 +205,7 @@ function searchTasks(e, tablet) {
error: function() {
console.log(arguments);
lockunlock(false,'div.mainbody,div.vtmainbody','#case_tasks');
- alert("Search failed or canceled! See console log for details.");
+ alert("Search failed or canceled! See console log for details.");
},
complete: function() {
/* Just safety net */
@@ -247,12 +252,12 @@ function auto(data, taggle, tag_url, modal) {
}
$(document).ready(function() {
- $('a').each(function () {
+ $('a').each(function () {
$(this).qtip({
content: $(this).attr("title"),
style: {classes: 'qtip-youtube'}
})
- });
+ });
var input = document.getElementById("id_keyword");
if (input) {
@@ -458,6 +463,7 @@ $(document).ready(function() {
var largemodal = $("#largemodal");
$(document).on("submit", '#addvendorform', function(event) {
+ /* the jquery autocomplete should use UUID or PKIDs*/
event.preventDefault();
var reload = $(this).attr("reload");
var vendors = [];
@@ -494,7 +500,7 @@ $(document).ready(function() {
})
.done(function() {
addmodal.foundation('close');
- });
+ });
});
@@ -645,20 +651,141 @@ $(document).ready(function() {
}
});
});
+ async function notify_all() {
+ let i = 1;
+ let hm = get_modal();
+ hm.append("
Start all-vendor Notifications
")
+ hm.append("
Do not hit Escape or click outside this window!
");
+ hm.append("
See Javascript console log for any failures
");
+ let caseid = $('.addvulmodal').attr('caseid');
+ if(!caseid) {
+ hm.append("Error no CaseID found");
+ return false;
+ }
+
+ window.allvendors = [];
+ var max = 2;
+ hm.append("
Fetching vendors list by Page " +
+ "[0] of " +
+ "2
Initiating contact for vendor # " +
+ "[0] of " +
+ window.allvendors.length + " Vendors in " +
+ "this Case, please wait
");
+ hm.append("
Starting Contact
");
+ for( let i = 0; i < window.allvendors.length; i++) {
+ hm.find("li:last-child").fadeOut('10000');
+ let si = String(i+1);
+ hm.find('.mdcounter').html(si);
+ let v = window.allvendors[i];
+ let csrf = getCookie('csrftoken');
+ let vuid = "VU#Private";
+ if($('#vuid').val())
+ vuid = $('#vuid').val();
+ let fsubmit = { subject : vuid+": New Vulnerability Report",
+ email_template: "",
+ email_body: "We have new information about a " +
+ "vulnerability that may affect your products. " +
+ "Please login to the CERT/CC VINCE portal for " +
+ "more information about this vulnerability." }
+ fsubmit['csrfmiddlewaretoken'] = csrf;
+ if(v.contact_date) {
+ console.log("Already Contacted vendor : " + v.vendor);
+ hm.append("
");
+}
+
$(function () {
/*$('span[title]').qtip({
@@ -380,6 +432,148 @@ $(function () {
.find('input[type="checkbox"]')
.prop('checked',$(e.target).prop('checked'));
});
-
+ function json_pretty(d) {
+ return JSON.stringify(d,null,'\t')
+ }
+ function msg_card(el,msg,level) {
+ $(el).html(msg).removeClass('hide').addClass('card-'+level);
+ if(level == "bad") {
+ $(el).on('click',function() { $('#xmodal').foundation('close'); })
+ }
+ }
+ function load_cvedata(cvetype) {
+ $('a.cvetab').attr("aria-selected","false");
+ $('a.cvetab.'+cvetype).attr("aria-selected","true");
+ let cvenew = $('#cve5data').data(cvetype);
+ if(cvenew && deepGet(cvenew,'containers.cna')) {
+ $('#cve5data').val(json_pretty(cvenew.containers.cna));
+ }
+ }
+ $('#rejectcve5').on('click', function() {
+ if(confirm("Reject this CVE and remove any public reference "+
+ " in the CVE Program?")) {
+ let href = $('#cve5json').attr('href');
+ }
+
+ });
+ $('.cve_status').each(function(_,el) {
+ if($(el).data("origin")) {
+ $.getJSON($(el).data("origin")).done(function(d) {
+ if(('state' in d) && (d.state != "PUBLISHED"))
+ return;
+ if(('dateUpdated' in d) && d.dateUpdated) {
+ try {
+ let depoch = Date.parse(d.dateUpdated);
+ let dtext = new Date(depoch).toLocaleString();
+ $(el).html(" ")
+ .append($('')
+ .html("PUBLISHED on " + dtext)
+ .addClass("goodtext"));
+ } catch(err) {
+ console.log("Ignore error on published data "+err)
+ }
+ }
+ });
+ }
+ });
+ $('#publishcve5').on('click',function() {
+ let hm = get_modal();
+ hm.append("
Submit CVE5 JSON To CVE Program
");
+ hm.append($('')
+ .html("If needed edit and update JSON before submission. " +
+ "Use the Tabs to compare CVE data in VINCE and " +
+ "in the CVE Services API"));
+ hm.append($('').addClass("dashboard-nav-card hide"));
+ hm.append($('