Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,60 @@ VINCE Coordination platform code
## Description
VINCE Coordination platform

Version 3.0.43 2026-06-25

* dependabot update recommendations: `PyJWT` 2.12.0 to 2.13.0, `cryptography` 46.0.7 to 48.0.1, `bleach` from 5.0.1 to 6.4.0
* updated `packaging` 22.0 to 24.2
* increased number of workers and established connection pooling (Internal-857)


Version 3.0.42 2026-06-01

* dependabot update recommendation: `idna` 3.7 to 3.15
* anonymized coordinator names in case request activity and file uploads shown to submitters before case creation (Internal-850)
* improved vulnerability note template formatting to add proper spacing before author attribution (Internal-853)
* updated security.txt disclosure policy link to point to new documentation location (Internal-816)
* added due date display to group case view showing time remaining until due date or "TBD" if not set (Internal-849)
* rephrased vulnerability status language throughout VINCE Comm case pages (Internal-852)
* added detailed logging for debugging HMAC mismatches



Version 3.0.41 2026-05-18

* modified code for filtering out spoofed emails to allow legititmately encoded headers through vetting process (Internal-846)



Version 3.0.40 2026-05-12

* dependabot update recommendation: `urllib3` 1.26.19 to 2.7.0
* added code to properly record identity of users who submit vul reports for VINCE Comm (Internal-821)
* fixed VINCE Comm login code to prevent "login loop" caused in rare cases by corrupted session objects (Internal-847)
* added SSVC assessment tool to the Case Reqeust page in VINCE Track (Internal-840)



Version 3.0.39 2026-05-06

* fixed issue preventing users from making lists and tables in VINCE Comm comments (VIN-845)
* fixed bug reported by user affecting VINCE's vetting of incoming email ticket reports (VIN-846)



Version 3.0.38 2026-05-05

* fixed issue preventing vulnote tab from displaying in certain circumstances (Internal-828)


Version 3.0.37 2026-05-04

* updates: `jsonschema` 4.17.0 to 4.25.1, `pydantic` 1.10.13 to 2.11.7, `attrs` 22.1.0 to 23.1.0, `packaging` 22.0 to 24.0
* fixed review link to appear even if case review not complete (Internal-828)
* added active status filter to autoassign function (Internal-844)
* fixed security issue caused by debug endpoint


Version 3.0.36 2026-04-20

* dependabot update recommendation: `sqlparse` 0.5.0 to 0.5.4, `PyJWT` 2.6.0 to 2.12.0, `markdown` 3.5 to 3.8.1, `pyasn1` 0.4.8 to 0.6.3, `awscli` 1.26.85 to 1.44.38, `Django` 4.2.28 to 4.2.30, `cryptography` 46.0.6 to 46.0.7
Expand Down
2 changes: 1 addition & 1 deletion Procfile
Original file line number Diff line number Diff line change
@@ -1 +1 @@
web: gunicorn bigvince.wsgi:application --bind 0.0.0.0:8000 --timeout 600
web: gunicorn bigvince.wsgi:application --bind 0.0.0.0:8000 --workers 5 --timeout 600
7 changes: 6 additions & 1 deletion bigvince/settings_.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@
ROOT_DIR = environ.Path(__file__) - 3

# any change that requires database migrations is a minor release
VERSION = "3.0.36"
VERSION = "3.0.43"

# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/2.1/howto/deployment/checklist/
Expand Down Expand Up @@ -419,6 +419,7 @@ def get_secret(secret_arn):
"OPTIONS": {
"sslmode": "require",
},
"CONN_MAX_AGE": int(os.environ.get("DB_CONN_MAX_AGE", "60")),
}

if VINCE_NAMESPACE in ["vince", "vinny"]:
Expand All @@ -429,6 +430,7 @@ def get_secret(secret_arn):
"PASSWORD": vincecomm_password,
"HOST": os.environ.get("VINCE_COMM_DB_HOST", "localhost"),
"PORT": os.environ.get("VINCE_COMM_DB_PORT", 5432),
"CONN_MAX_AGE": int(os.environ.get("DB_CONN_MAX_AGE", "60")),
}

DATABASES["vincepub"] = {
Expand All @@ -438,6 +440,7 @@ def get_secret(secret_arn):
"PASSWORD": vincepub_password,
"HOST": os.environ.get("VINCE_PUB_DB_HOST", "localhost"),
"PORT": os.environ.get("VINCE_PUB_DB_PORT", 5432),
"CONN_MAX_AGE": int(os.environ.get("DB_CONN_MAX_AGE", "60")),
}

if VINCE_NAMESPACE == "vincepub":
Expand All @@ -450,6 +453,7 @@ def get_secret(secret_arn):
"PASSWORD": vincepub_password,
"HOST": os.environ.get("VINCE_PUB_DB_HOST", "localhost"),
"PORT": os.environ.get("VINCE_PUB_DB_PORT", 5432),
"CONN_MAX_AGE": int(os.environ.get("DB_CONN_MAX_AGE", "60")),
}


Expand All @@ -462,6 +466,7 @@ def get_secret(secret_arn):
"PASSWORD": vincecomm_password,
"HOST": os.environ.get("VINCE_COMM_DB_HOST", "localhost"),
"PORT": os.environ.get("VINCE_COMM_DB_PORT", 5432),
"CONN_MAX_AGE": int(os.environ.get("DB_CONN_MAX_AGE", "60")),
}

# Each application has their own database, so we can set permissions
Expand Down
14 changes: 14 additions & 0 deletions cogauth/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -563,6 +563,11 @@ def get_context_data(self, **kwargs):
if settings.DEBUG:
context["token_login"] = True

# Pass the 'next' parameter to the template context
# This is required for the hidden input field in the login form
next_page = self.request.GET.get("next", "")
context["next"] = next_page

return context

def form_valid(self, form):
Expand Down Expand Up @@ -791,6 +796,15 @@ class MFAAuthRequiredView(FormView, AccessMixin):

def dispatch(self, request, *args, **kwargs):
if not (request.session.get("MFAREQUIRED") and request.session.get("username")):
# Check for potential redirect loop: if user came from login page and is trying
# to access MFA page, but session check failed, redirect to dashboard instead
# of creating a loop where next=/mfa/
referer = request.META.get('HTTP_REFERER', '')
if '/login/' in referer and '/mfa/' in request.path:
logger.debug(
f"Detected potential MFA redirect loop. Referer: {referer}, Path: {request.path}. Session missing MFAREQUIRED or username. Redirecting to dashboard to break loop."
)
return redirect(settings.LOGIN_REDIRECT_URL)
return self.handle_no_permission()
return super(MFAAuthRequiredView, self).dispatch(request, *args, **kwargs)

Expand Down
18 changes: 16 additions & 2 deletions lib/vince/markdown_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,10 +144,24 @@ def markdown(value):
markdown_attrs['a'].append("class")
markdown_attrs['img'].append("width")
markdown_attrs['img'].append("height")
return bleach.clean(md.markdown(value, extensions=['toc', 'markdown.extensions.fenced_code', UserMentionExtension(), MathExtension()]), generally_xss_safe, markdown_attrs)
# Add table-related tags to allow markdown tables to render
markdown_attrs.setdefault('table', [])
markdown_attrs.setdefault('thead', [])
markdown_attrs.setdefault('tbody', [])
markdown_attrs.setdefault('tr', [])
markdown_attrs.setdefault('th', [])
markdown_attrs.setdefault('td', [])
return bleach.clean(md.markdown(value, extensions=['toc', 'markdown.extensions.fenced_code', 'tables', 'nl2br', UserMentionExtension(), MathExtension()]), generally_xss_safe, markdown_attrs)

def markdown_filter(value, users):
markdown_attrs['a'].append("class")
markdown_attrs['img'].append("width")
markdown_attrs['img'].append("height")
return bleach.clean(md.markdown(value, extensions=['toc', 'markdown.extensions.fenced_code', UserMentionExtension(users), MathExtension()]), generally_xss_safe, markdown_attrs)
# Add table-related tags to allow markdown tables to render
markdown_attrs.setdefault('table', [])
markdown_attrs.setdefault('thead', [])
markdown_attrs.setdefault('tbody', [])
markdown_attrs.setdefault('tr', [])
markdown_attrs.setdefault('th', [])
markdown_attrs.setdefault('td', [])
return bleach.clean(md.markdown(value, extensions=['toc', 'markdown.extensions.fenced_code', 'tables', 'nl2br', UserMentionExtension(users), MathExtension()]), generally_xss_safe, markdown_attrs)
15 changes: 7 additions & 8 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ awscli==1.44.38
backports.zoneinfo;python_version<"3.9"
beautifulsoup4==4.11.1
billiard==4.0.2
bleach==5.0.1
bleach==6.4.0
bleach-whitelist==0.0.11
boto==2.49.0
boto3==1.42.48
Expand All @@ -20,7 +20,7 @@ chardet==5.0.0
charset-normalizer==2.1.1
click==8.1.3
colorama==0.4.4
cryptography==46.0.7
cryptography==48.0.1
cvelib==1.3.0
Deprecated==1.2.13
dictdiffer==0.9.0
Expand All @@ -40,7 +40,7 @@ envs==1.4
fs==2.4.16
fs-s3fs==1.1.1
gunicorn==22.0.0
idna==3.7
idna==3.15
importlib-metadata==5.0.0
importlib-resources==5.10.0
pip-install==1.3.5
Expand All @@ -50,7 +50,7 @@ kombu==5.2.4
M2Crypto==0.47.0
Markdown==3.8.1
openpyxl==3.1.5
packaging==22.0
packaging==24.2
pinax-messages==3.0.0
pip-autoremove==0.10.0
pkgutil-resolve-name==1.3.10
Expand All @@ -60,7 +60,7 @@ pyasn1==0.6.3
pycparser==2.21
pycryptodome==3.19.1
pydantic==1.10.13
PyJWT==2.12.0
PyJWT==2.13.0
pyparsing==3.0.9
pyrsistent==0.19.2
python-dateutil==2.8.2
Expand All @@ -80,10 +80,9 @@ six==1.16.0
soupsieve==2.3.2.post1
sqlparse==0.5.4
typing-extensions>=4.9.0
# (urllib3 is currently at 1.26.19. Dependabot recommends urllib3 2.6.3, but that breaks when combined with any currently available version of botocore.)
urllib3==1.26.19
urllib3==2.7.0
vine==5.0.0
watchtower==3.0.0
webencodings==0.5.1
wrapt==1.14.1
zipp==3.19.1
zipp==3.19.1
69 changes: 67 additions & 2 deletions vince/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -287,13 +287,13 @@ def __init__(self, *args, **kwargs):
if self.case.team_owner.groupsettings.vulnote_template:
if self.case.get_assigned_to:
self.fields["content"].initial = (
f"{self.case.team_owner.groupsettings.vulnote_template}This document was written by {self.case.get_assigned_to}.\r\n"
f"{self.case.team_owner.groupsettings.vulnote_template}\r\n\r\nThis document was written by {self.case.get_assigned_to}.\r\n"
)
else:
self.fields["content"].initial = f"{self.case.team_owner.groupsettings.vulnote_template}.\r\n"
elif self.case.get_assigned_to:
self.fields["content"].initial = (
f"{VULNOTE_TEMPLATE}This document was written by {self.case.get_assigned_to}.\r\n"
f"{VULNOTE_TEMPLATE}\r\n\r\nThis document was written by {self.case.get_assigned_to}.\r\n"
)


Expand Down Expand Up @@ -3203,3 +3203,68 @@ class CVEFilterForm(forms.Form):
)

vince = forms.BooleanField(required=False, label="Search VINCE CVEs")


class SSVCAssessmentForm(forms.Form):
"""
Form for SSVC Coordinator Triage Assessment.
"""

report_public = forms.MultipleChoiceField(
label="Is a viable report of the vulnerability already publicly available?",
choices=[('Y', 'Yes'), ('N', 'No')],
widget=forms.CheckboxSelectMultiple(attrs={"class": "ul_nobullet"}),
required=False
)

supplier_contacted = forms.MultipleChoiceField(
label="Has the reporter made a good-faith effort to contact the supplier?",
choices=[('Y', 'Yes'), ('N', 'No')],
widget=forms.CheckboxSelectMultiple(attrs={"class": "ul_nobullet"}),
required=False
)

report_credibility = forms.MultipleChoiceField(
label="Is the report credible?",
choices=[('C', 'Credible'), ('NC', 'Not Credible')],
widget=forms.CheckboxSelectMultiple(attrs={"class": "ul_nobullet"}),
required=False
)

supplier_cardinality = forms.MultipleChoiceField(
label="How many suppliers are responsible for the vulnerable component?",
choices=[('O', 'One'), ('M', 'Multiple')],
widget=forms.CheckboxSelectMultiple(attrs={"class": "ul_nobullet"}),
required=False
)

supplier_engagement = forms.MultipleChoiceField(
label="Is the supplier responding and actively participating?",
choices=[('A', 'Active'), ('U', 'Unresponsive')],
widget=forms.CheckboxSelectMultiple(attrs={"class": "ul_nobullet"}),
required=False
)

utility = forms.MultipleChoiceField(
label="What is the usefulness of the exploit to the adversary?",
choices=[
('L', 'Laborious'),
('E', 'Efficient'),
('S', 'Super Effective')
],
widget=forms.CheckboxSelectMultiple(attrs={"class": "ul_nobullet"}),
required=False
)

public_safety_impact = forms.MultipleChoiceField(
label="What is the impact to public safety?",
choices=[('M', 'Minimal'), ('S', 'Significant')],
widget=forms.CheckboxSelectMultiple(attrs={"class": "ul_nobullet"}),
required=False
)

notes = forms.CharField(
label="Assessment Notes (optional)",
widget=forms.Textarea(attrs={'rows': 4, 'class': 'form-control'}),
required=False
)
Loading
Loading