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
6 changes: 3 additions & 3 deletions .github/workflows/codeql-analysis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ jobs:

# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
uses: github/codeql-action/init@v3
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
Expand All @@ -56,7 +56,7 @@ jobs:
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v2
uses: github/codeql-action/autobuild@v3

# Command-line programs to run using the OS shell.
# See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
Expand All @@ -69,4 +69,4 @@ jobs:
# ./location_of_script_within_repo/buildscript.sh

- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2
uses: github/codeql-action/analyze@v3
127 changes: 127 additions & 0 deletions .github/workflows/pr-tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
name: PR Tests

on:
pull_request:
branches:
- main

concurrency:
group: pr-tests-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true

jobs:
django-tests:
name: Django PR tests
runs-on: ubuntu-latest
timeout-minutes: 45
permissions:
contents: read

services:
postgres:
image: postgres:15
env:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
ports:
- 5432:5432
options: >-
--health-cmd "pg_isready -U postgres"
--health-interval 10s
--health-timeout 5s
--health-retries 5

env:
DJANGO_SETTINGS_MODULE: bigvince.settings_
AUTH_BACKEND_MODE: local
DEBUG: "True"
VINCE_DB_SSL_MODE: disable
# SECRET_KEY and GOOGLE_RECAPTCHA_SECRET_KEY are generated ephemerally
# in the "Generate ephemeral test secrets" step below.
GOOGLE_SITE_KEY: test-site-key
VINCE_DEV_SYSTEM: "1"
VINCE_TRACK_DB_HOST: 127.0.0.1
VINCE_TRACK_DB_PORT: "5432"
VINCE_TRACK_DB_USER: vince
VINCE_TRACK_DB_PASS: vince
VINCE_TRACK_DB_NAME: vincetest
VINCE_COMM_DB_HOST: 127.0.0.1
VINCE_COMM_DB_PORT: "5432"
VINCE_COMM_DB_USER: vince
VINCE_COMM_DB_PASS: vince
VINCE_COMM_DB_NAME: vincecommtest
VINCE_PUB_DB_HOST: 127.0.0.1
VINCE_PUB_DB_PORT: "5432"
VINCE_PUB_DB_USER: vince
VINCE_PUB_DB_PASS: vince
VINCE_PUB_DB_NAME: vincepubtest
NO_REPLY_EMAIL: noreply@example.com
REPLY_EMAIL: support@example.com

steps:
- name: Check out repository
uses: actions/checkout@v4

- name: Generate ephemeral test secrets
run: |
# Use Python stdlib (no Django needed yet) to generate a 50-char
# URL-safe random string. These values are thrown away after the run.
echo "SECRET_KEY=$(python3 -c 'import secrets; print(secrets.token_urlsafe(50))')" >> $GITHUB_ENV
echo "GOOGLE_RECAPTCHA_SECRET_KEY=ci-ephemeral-recaptcha-key" >> $GITHUB_ENV

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.11"
cache: pip

- name: Install OS dependencies
run: |
sudo apt-get update
sudo apt-get install -y postgresql-client swig libpq-dev libssl-dev

- name: Install Python build tools
run: pip install --upgrade wheel cython

- name: Install dependencies
run: pip install -r requirements.txt

- name: Create application databases
env:
PGPASSWORD: postgres
run: |
psql -h 127.0.0.1 -U postgres -d postgres <<'SQL'
DO $$
BEGIN
IF NOT EXISTS (SELECT FROM pg_roles WHERE rolname = 'vince') THEN
CREATE ROLE vince LOGIN PASSWORD 'vince' CREATEDB;
END IF;
END
$$;
SELECT 'CREATE DATABASE vincetest OWNER vince'
WHERE NOT EXISTS (SELECT FROM pg_database WHERE datname = 'vincetest')\gexec
SELECT 'CREATE DATABASE vincecommtest OWNER vince'
WHERE NOT EXISTS (SELECT FROM pg_database WHERE datname = 'vincecommtest')\gexec
SELECT 'CREATE DATABASE vincepubtest OWNER vince'
WHERE NOT EXISTS (SELECT FROM pg_database WHERE datname = 'vincepubtest')\gexec
SQL

- name: Clean-slate migrations and regenerate
run: |
for d in */migrations/*.py; do
if [ "$(basename "$d")" != "__init__.py" ]; then
rm -f "$d"
fi
done

python -W ignore manage.py makemigrations

- name: Run migrations
run: |
python manage.py migrate
python manage.py migrate --database=vincecomm
python manage.py migrate --database=vincepub

- name: Run all tests vince and vinny
run: python manage.py test vince vinny --verbosity=2

26 changes: 23 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,27 @@ VINCE Coordination platform code


## Description
VINCE Coordination platform
VINCE Coordination platform

Version 3.0.44 2026-08-12

* dependabot update recommendations: `awscli` 1.44.38 to 1.45.28 with corresponding updates - `boto3` to 1.43.28, `botocore` to 1.43.28, `s3transfer` 0.16.0 to 0.18.0
* Added GitHub workflow for `.github/workflows/pr-tests.yml` and a number of Test Cases
* Added `vince/auth` override for local authentication to support IDAM and OpenID usage and building related Test Cases
* CSAF intake capability is now introduced with the `CommVulReportAPIView` auto-detect Form or API JSON submission (GH-Issue #24 ongoing)
* CSAF JSON view (using Ace Editor library ) supported to help adoption in Vulnerability Request form (VRF) submission using VRF profile of CSAF
* dependabot update recommendations: `soupsieve` 2.3.2.post1 to 2.8.4, `pyasn1` 0.6.3 to 0.6.4, `cryptography` 48.0.1 to 50.0.0
* added logging to address login loop issue (Internal-847)
* modified regexes to avoid syntax error caused by Python 3.12 update (Internal-859)
* disabled view for TCR API endpoint view (Internal-856)
* proper fix for revisited CaseFilterResults View add Due Date in template vince/templates/vince/searchresults.html (Internal-849)
* CVE-2026-18744 - poor permissions management in vinny/views.py: (GetStatementView) not checking member_id of user
* CVE-2026-18749 - poor permissions management in vinny/views.py: (VinceAttachmentView type=track).
check if attachment is `shared` before allowing it.
* CVE-2026-18750 - poor permissions management in vinny/views.py: (ModifyEmailNotifications).
Check the record's contact belongs to the requesting group-admin.
* CSAFSerializer relaxed check on origin to start with "https://" instead of .find("https://") > -1
* Updates of dependencies djangorestframework==3.15.2 redis==4.5.5 from vulnerability scan.

Version 3.0.43 2026-06-25

Expand Down Expand Up @@ -43,8 +63,8 @@ Version 3.0.40 2026-05-12

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)
* fixed issue preventing users from making lists and tables in VINCE Comm comments (Internal-845)
* fixed bug reported by user affecting VINCE's vetting of incoming email ticket reports (Internal-846)



Expand Down
48 changes: 37 additions & 11 deletions 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.43"
VERSION = "3.0.44"

# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/2.1/howto/deployment/checklist/
Expand All @@ -68,6 +68,20 @@

LOCALSTACK = os.environ.get("LOCALSTACK")

# ---------------------------------------------------------------------------
# Auth backend mode
# ---------------------------------------------------------------------------
# Set AUTH_BACKEND_MODE=local in .env (or the environment) to bypass Cognito
# for local development and automated tests. The production default is
# "cognito". Any other value raises a ValueError at startup so
# misconfigurations are caught early.
AUTH_BACKEND_MODE = os.getenv("AUTH_BACKEND_MODE", "cognito").lower()
if AUTH_BACKEND_MODE not in {"cognito", "local"}:
raise ValueError(
f"Invalid AUTH_BACKEND_MODE={AUTH_BACKEND_MODE!r}. "
"Valid choices are 'cognito' (default) and 'local'."
)

TERMS_URL = os.environ.get(
"TERMS_URL", "https://docs.aws.amazon.com/cognito/latest/developerguide/data-protection.html"
)
Expand Down Expand Up @@ -393,6 +407,7 @@ def get_secret(secret_arn):
# Check environment variables for database credentials
else:
VINCE_NAMESPACE = "vince"
MFA_REDIRECT_URL = "vince:mfaauth"
SUPERUSER = {"username": "superuser@example.com", "password": "SavingTheWorldWithPerl"}
vincetrack_user = os.environ.get("VINCE_TRACK_DB_USER", "vincetrack")
vincetrack_password = os.environ.get("VINCE_TRACK_DB_PASS", "vincetrack")
Expand All @@ -417,7 +432,7 @@ def get_secret(secret_arn):
"HOST": os.environ.get("VINCE_TRACK_DB_HOST", "localhost"),
"PORT": os.environ.get("VINCE_TRACK_DB_PORT", 5432),
"OPTIONS": {
"sslmode": "require",
"sslmode": os.environ.get("VINCE_DB_SSL_MODE", "require"),
},
"CONN_MAX_AGE": int(os.environ.get("DB_CONN_MAX_AGE", "60")),
}
Expand Down Expand Up @@ -482,9 +497,15 @@ def get_secret(secret_arn):

DATABASE_ROUTERS = ["vince.dbrouter.BigVinceRouter"]

AUTHENTICATION_BACKENDS = [
"cogauth.backend.CognitoAuthenticate",
]
if AUTH_BACKEND_MODE == "local":
# Local mode: plain Django model-based auth, no Cognito dependency.
AUTHENTICATION_BACKENDS = [
"django.contrib.auth.backends.ModelBackend",
]
else:
AUTHENTICATION_BACKENDS = [
"cogauth.backend.CognitoAuthenticate",
]

# Cognito Settings - these can be found in the AWS Cognito Console.
# The user pool must be setup prior to deploying
Expand Down Expand Up @@ -514,10 +535,11 @@ def get_secret(secret_arn):
# "vincetrack" local group
COGNITO_VINCETRACK_GROUPS = os.environ.get("AWS_COGNITO_VINCETRACK_GROUPS", default=COGNITO_ADMIN_GROUP)

# Any user in this group will automatically be promoted to superuser
# Choose wisely - ideally this should be a more select set than the
# VINCETrack group
COGNITO_SUPERUSER_GROUP = os.environ.get("AWS_COGNITO_SUPERUSER_GROUP", COGNITO_ADMIN_GROUP)
# If you uncomment this line ny user in this group will automatically
# be promoted to superuser. Choose wisely - ideally this should be
#a more select set than the VINCETrack group like "SUPERADMIN"
#COGNITO_SUPERUSER_GROUP = os.environ.get("AWS_COGNITO_SUPERUSER_GROUP", COGNITO_ADMIN_GROUP)
COGNITO_SUPERUSER_GROUP = os.environ.get("AWS_COGNITO_SUPERUSER_GROUP", "SADMIN")

# COGNITO_LIMITED_ACCESS_GROUPS can be used to give special permission to views
# in VINCECOMM
Expand Down Expand Up @@ -718,8 +740,12 @@ def get_secret(secret_arn):
REST_FRAMEWORK = {
"DEFAULT_AUTHENTICATION_CLASSES": [
"cogauth.backend.HashedTokenAuthentication",
#'rest_framework.authentication.TokenAuthentication',
# 'cogauth.backend.JSONWebTokenAuthentication',
#If you want to support fallback in REST request from a browser
#using a local authenticated session
#with Local Auth uncomment the first and with Cognito uncomment
#the second line
#"rest_framework.authentication.SessionAuthentication",
#"cogauth.backend.CognitoAuthenticateAPI",
],
"DEFAULT_THROTTLE_RATES": {
"user": "100/hour", # Authenticated users can make 100 requests per hour
Expand Down
1 change: 1 addition & 0 deletions bigvince/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@
re_path('^vulfeed/?$', LatestVulReportActivity()),
path('vince/admin/', admin.site.urls),
path('vince/comm/', include(('vinny.urls', 'vinny'), namespace="vinny")),
path('vince/auth/', include(('vince.auth.urls', 'localauth'), namespace='localauth')),
path('vince/',include(('vince.urls', 'vince'), namespace="vince")),
path('vince/comm/auth/', include(('cogauth.urls', 'cogauth'), namespace='cogauth')),
path('vince/comm/admin/', vinnyadmin.urls),
Expand Down
2 changes: 1 addition & 1 deletion cogauth/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ def clean(self):
# phone numbers vary greatly world-wide, so best we can do is verify
# that no "weird" characters are entered.
def validate_phone_number(value):
phone_re = "\+[^0-9+]" # set of all things except phone number characters and commas
phone_re = r"\+[^0-9+]" # set of all things except phone number characters and commas
if search(phone_re, value) is not None:
raise forms.ValidationError("%s contains non-telephone characters" % value)

Expand Down
5 changes: 3 additions & 2 deletions cogauth/templates/cogauth/profile.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{% extends VINCECOMM_BASE_TEMPLATE %}
{% extends 'vinny/base.html' %}
{% load i18n static %}
{% block js %}
{{ block.super }}
Expand Down Expand Up @@ -123,7 +123,8 @@ <h3>{{ coguser.preferred_username }}</h3>
<tr>
<td>API Key</td>
<td>
<button id="gentoken" class="button default getaction" action="{% url 'cogauth:gentoken' %}" {% if coguser.api_key %} preaction="{% url 'cogauth:deltoken' %}" data-confirm="Would you like to create a new API key and revoke your old API key?">Refresh API Key{% else %}>Generate API Key{% endif %}</button>
<button id="gentoken" class="button default getaction" action="{% url 'cogauth:gentoken' %}" {% if coguser.api_key or user.vinceprofile.api_key %} preaction="{% url 'cogauth:deltoken' %}" data-confirm="Would you like to create a new API key and revoke your old API key?">Refresh API Key{% else %}>Generate API Key{% endif %}</button>

</td>
</tr>
</table>
Expand Down
7 changes: 7 additions & 0 deletions cogauth/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,13 @@ def add_permissions(user):

def cognito_check_track_permissions(request):
logger.debug(f"=== cognito_check_track_permissions called for user {request.user.username if request.user.is_authenticated else 'ANONYMOUS'} ===")
if getattr(settings, "AUTH_BACKEND_MODE", None) == "local":
logger.debug(f"Bypass cognito checks for user {request.user.username} to local checks")
return (
request.user is not None
and request.user.is_authenticated
and request.user.is_active
)
old_user = False
access_token = request.session.get('ACCESS_TOKEN')
logger.debug(f"ACCESS_TOKEN present in session: {access_token is not None}")
Expand Down
Loading
Loading