Skip to content
Open
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
10 changes: 8 additions & 2 deletions .github/workflows/cicd-actions.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,13 @@ jobs:
uses: actions/checkout@v4

- name: Create .env file
env:
POSTGRES_PASSWORD: ${{ secrets.TEST_DB_PASSWORD }}
SALT: ${{ secrets.TEST_SALT }}
run: |
echo "NVD_API_KEY=${{ secrets.TEST_NVD_API_KEY }}" >> .env
echo 'DJANGO_SECRET_KEY="${{ secrets.TEST_DJANGO_SECRET_KEY }}"' >> .env
echo 'SALT="${{ secrets.TEST_SALT }}"' >> .env
echo "SALT=${SALT:-local-dev-salt}" >> .env
echo "ADMIN_USERNAME=admin@acme.de" >> .env
echo "ADMIN_PASSWORD=secure!" >> .env
echo "USER_USERNAME=user@acme.de" >> .env
Expand All @@ -41,7 +44,7 @@ jobs:
echo "POSTGRES_USER=securecheckplus" >> .env
echo "POSTGRES_DB=securecheckplus" >> .env
echo "POSTGRES_PORT=5432" >> .env
echo 'POSTGRES_PASSWORD="${{ secrets.TEST_DB_PASSWORD }}"' >> .env
echo "POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-scp_test_pass}" >> .env
echo "EMAIL_HOST=localhost" >> .env
echo "EMAIL_PORT=25" >> .env
echo 'LDAP_ORGANISATION="ACME"' >> .env
Expand Down Expand Up @@ -77,6 +80,7 @@ jobs:
path: backend

- name: Docker Login
if: env.DOCKER_USER != '' && env.DOCKER_KEY != ''
run: echo "$DOCKER_KEY" | docker login -u "$DOCKER_USER" --password-stdin

- name: Build Docker Compose
Expand Down Expand Up @@ -182,6 +186,7 @@ jobs:
path: backend/assets

- name: Docker Login
if: env.DOCKER_USER != '' && env.DOCKER_KEY != ''
run: echo "$DOCKER_KEY" | docker login -u "$DOCKER_USER" --password-stdin

- name: Extract metadata (tags, labels) for Docker
Expand Down Expand Up @@ -221,6 +226,7 @@ jobs:
path: backend

- name: Docker Login
if: env.DOCKER_USER != '' && env.DOCKER_KEY != ''
run: echo "$DOCKER_KEY" | docker login -u "$DOCKER_USER" --password-stdin

- name: Extract metadata (tags, labels) for Docker
Expand Down
65 changes: 65 additions & 0 deletions backend/analyzer/test/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
from unittest.mock import patch

import pytest

MOCK_NVD_RESPONSE = {
"vulnerabilities": [
{
"cve": {
"id": "CVE-2021-44228",
"published": "2025-01-01T00:00:00Z",
"lastModified": "2025-01-01T00:00:00Z",
"descriptions": [{"value": "A test vulnerability description."}],
"metrics": {
"cvssMetricV31": [
{
"cvssData": {
"baseScore": 7.5,
"baseSeverity": "HIGH",
"attackVector": "NETWORK",
"attackComplexity": "LOW",
"privilegesRequired": "NONE",
"userInteraction": "NONE",
"confidentialityImpact": "HIGH",
"integrityImpact": "NONE",
"availabilityImpact": "NONE",
"scope": "UNCHANGED",
}
}
]
},
"weaknesses": [{"description": [{"value": "CWE-94"}]}],
"references": [{"url": "https://example.com/advisory", "tags": ["Vendor Advisory"]}],
}
}
]
}

MOCK_EPSS_RESPONSE = {"data": [{"epss": 0.5}]}


def _mock_requests_get(url, **kwargs):
class MockResponse:
def __init__(self, json_data, status_code):
self.json_data = json_data
self.status_code = status_code

def json(self):
return self.json_data

url_str = str(url)
if "nvd.nist.gov" in url_str:
return MockResponse(MOCK_NVD_RESPONSE, 200)
if "api.first.org" in url_str:
return MockResponse(MOCK_EPSS_RESPONSE, 200)
raise ConnectionError(f"Unexpected request: {url_str}")


@pytest.fixture(autouse=True)
def no_nvd_network(request):
if request.node.get_closest_marker("nvd_integration"):
yield
return
with patch("analyzer.services.cve_fetcher.requests.get", side_effect=_mock_requests_get), \
patch("analyzer.services.cve_fetcher.time.sleep"):
yield
31 changes: 22 additions & 9 deletions backend/analyzer/test/test_cve_fetcher.py
Original file line number Diff line number Diff line change
@@ -1,29 +1,42 @@
from datetime import datetime

import pytest

from analyzer.services.cve_fetcher import CVEFetcher
from utilities.constants import BaseSeverity, AttackVector, AttackComplexity, UserInteraction, IntegrityImpact, \
AvailabilityImpact, ConfidentialityImpact, Scope, PrivilegesRequired

cve_id = "CVE-2021-44228"
cve_fetcher = CVEFetcher(cve_id=cve_id)
cve_data = cve_fetcher.generate()


def test_description():
@pytest.fixture
def cve_data():
fetcher = CVEFetcher(cve_id=cve_id)
return fetcher.generate()


@pytest.mark.nvd_integration
def test_real_nvd_api_contract():
fetcher = CVEFetcher(cve_id=cve_id)
data = fetcher.generate()
assert fetcher.successful
assert len(data["description"]) > 0
assert 0 < data["cve_attributes"]["baseScore"] <= 10


def test_description(cve_data):
assert len(cve_data["description"]) > 0


def test_dates():
def test_dates(cve_data):
published = cve_data["published"]
assert isinstance(published, datetime)

updated = cve_data["updated"]
assert isinstance(updated, datetime)


def test_cve_attributes_cvss_v3():
def test_cve_attributes_cvss_v3(cve_data):
attributes = cve_data["cve_attributes"]

assert 0 < attributes["baseScore"] <= 10
assert attributes["baseSeverity"] in BaseSeverity.names
assert attributes["attackVector"] in AttackVector.names
Expand All @@ -36,9 +49,9 @@ def test_cve_attributes_cvss_v3():
assert attributes["scope"] in Scope.names


def test_epss_score():
def test_epss_score(cve_data):
assert 0 <= float(cve_data["epss"]) <= 1.0


def test_vendor_reference():
def test_vendor_reference(cve_data):
assert len(cve_data["vendor_reference"]) >= 0
6 changes: 4 additions & 2 deletions backend/pytest.ini
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
[pytest]
addopts = --nomigrations --reuse-db
DJANGO_SETTINGS_MODULE = securecheckplus.settings
addopts = --nomigrations --reuse-db -m "not nvd_integration"
DJANGO_SETTINGS_MODULE = securecheckplus.settings
markers =
nvd_integration: tests that call the real NVD API (requires internet + API key)
Loading