Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
5c47b14
add unit test
paulOsinski Apr 8, 2026
21aab02
replace <pre> wrappers and update CSS on reports
paulOsinski Apr 8, 2026
5f13e4d
Update versions in application files
Apr 13, 2026
9f92409
Merge pull request #14679 from DefectDojo/master-into-bugfix/2.57.1-2…
rossops Apr 13, 2026
2b24200
Merge branch 'bugfix' into report-css-fix
Maffooch Apr 14, 2026
8a2100e
chore(deps): bump pillow from 12.1.1 to 12.2.0 (#14680)
dependabot[bot] Apr 15, 2026
76d8ed8
:tada: add mozilla foundation sec advice to vulnid
manuel-sommer Apr 16, 2026
aec3fef
Added ssrf utils file to check urls and applied it to risk recon pars…
Jino-T Apr 16, 2026
7f54671
Use RBAC for accept_risks API endpoints (#14632)
Jino-T Apr 16, 2026
eea3e47
Change to reactivating risk accepted findings (#14633)
Jino-T Apr 16, 2026
9d661d7
Add permission checks for moving engagements between products (#14634)
Jino-T Apr 16, 2026
4df60d0
Add CLAUDE.md with module reorganization playbook
Maffooch Apr 17, 2026
56c84a7
Update CLAUDE.md
Maffooch Apr 17, 2026
c1b2526
:tada: add fix_available and fix_version to govulncheck (#14681)
manuel-sommer Apr 17, 2026
4a0abbd
fix: clean up template rendering for endpoint user fields (#14682)
Maffooch Apr 17, 2026
4decd88
Validate consistency between ID-based and name-based identifiers in i…
Jino-T Apr 17, 2026
c51d018
tests: read raw template string
paulOsinski Apr 17, 2026
1cffc13
Merge branch 'report-css-fix' of https://github.com/paulOsinski/djang…
paulOsinski Apr 17, 2026
6b26aae
implement versioned_fixtures in test
paulOsinski Apr 17, 2026
8675647
Merge branch 'bugfix' into report-css-fix
paulOsinski Apr 17, 2026
a61ceeb
Merge pull request #14703 from manuel-sommer/mozilla_vulnid
rossops Apr 20, 2026
9e935c4
Merge pull request #14705 from Maffooch/chore/module-reorganization-p…
rossops Apr 20, 2026
412570f
Merge pull request #14666 from paulOsinski/report-css-fix
rossops Apr 20, 2026
43b2238
Add centralized banner system with OS messaging support (#14708)
Maffooch Apr 20, 2026
5cbf87b
Update versions in application files
Apr 20, 2026
3adf4cd
Merge pull request #14711 from DefectDojo/release/2.57.2
rossops Apr 20, 2026
7a00fbe
Update versions in application files
Apr 20, 2026
e507378
Merge branch 'dev' into master-into-dev/2.57.2-2.58.0-dev
rossops Apr 20, 2026
5024e7e
Merge pull request #14712 from DefectDojo/master-into-dev/2.57.2-2.58…
Maffooch Apr 21, 2026
15261f3
refactor: decouple Jira integration into dojo/jira package
Maffooch Apr 21, 2026
d52c76f
Merge pull request #14709 from DefectDojo/renovate/renovatebot-renova…
renovate[bot] Apr 22, 2026
ac8bd4c
Update postgres:18.3-alpine Docker digest from 18.3 to 18.3-alpine (d…
renovate[bot] Apr 22, 2026
a6282ab
Update python:3.13.13-alpine3.22 Docker digest from 3.13.13 to v (Doc…
renovate[bot] Apr 22, 2026
8beedaf
chore(deps): bump social-auth-core from 4.8.5 to 4.8.6 (#14723)
dependabot[bot] Apr 22, 2026
9ed69cb
chore(deps): bump ruff from 0.15.10 to 0.15.11 (#14726)
dependabot[bot] Apr 22, 2026
7228bc5
Update python:3.13.13-slim-trixie Docker digest from 3.13.13 to v (Do…
renovate[bot] Apr 22, 2026
13f82d8
Update dependency kubernetes from 1.33.10 to v1.33.11 (.github/workfl…
renovate[bot] Apr 22, 2026
c531dbd
Update dependency kubernetes/kubernetes from v1.35.3 to v1.35.4 (.git…
renovate[bot] Apr 22, 2026
54cb93b
Update actions/setup-node action from v6.3.0 to v6.4.0 (.github/workf…
renovate[bot] Apr 22, 2026
835b0bc
Update valkey Docker tag from 0.19.0 to v0.20.0 (helm/defectdojo/Char…
renovate[bot] Apr 22, 2026
6ee68a7
Merge branch 'dev' into jira-cleanup
Maffooch Apr 22, 2026
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
3 changes: 2 additions & 1 deletion .dryrunsecurity.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ sensitiveCodepaths:
- 'dojo/group/*.py'
- 'dojo/importers/*.py'
- 'dojo/importers/**/*.py'
- 'dojo/jira_link/*.py'
- 'dojo/jira/*.py'
- 'dojo/jira/**/*.py'
- 'dojo/metrics/*.py'
- 'dojo/note_type/*.py'
- 'dojo/notes/*.py'
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/gh-pages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ jobs:
extended: true

- name: Setup Node
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
node-version: '24.14.1' # TODO: Renovate helper might not be needed here - needs to be fully tested

Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/k8s-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ jobs:
# databases, broker and k8s are independent, so we don't need to test each combination
# lastest k8s version (https://kubernetes.io/releases/) and the oldest officially supported version
# are tested (https://kubernetes.io/releases/)
- k8s: 'v1.35.3' # renovate: datasource=github-releases depName=kubernetes/kubernetes versioning=loose
- k8s: 'v1.35.4' # renovate: datasource=github-releases depName=kubernetes/kubernetes versioning=loose
os: debian
- k8s: '1.33.10' # renovate: datasource=custom.endoflife-oldest-maintained depName=kubernetes
- k8s: '1.33.11' # renovate: datasource=custom.endoflife-oldest-maintained depName=kubernetes
os: debian
steps:
- name: Checkout
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/renovate.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,4 @@ jobs:
uses: suzuki-shunsuke/github-action-renovate-config-validator@ee9f69e1f683ed0d08225086482b34fc9abe9300 # v2.1.0
with:
strict: "true"
validator_version: 43.112.1 # renovate: datasource=github-releases depName=renovatebot/renovate
validator_version: 43.139.4 # renovate: datasource=github-releases depName=renovatebot/renovate
2 changes: 1 addition & 1 deletion .github/workflows/validate_docs_build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jobs:
extended: true

- name: Setup Node
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
node-version: '24.14.1' # TODO: Renovate helper might not be needed here - needs to be fully tested

Expand Down
249 changes: 249 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,249 @@
# DefectDojo Development Guide

## Project Overview

DefectDojo is a Django application (`dojo` app) for vulnerability management. The codebase is undergoing a modular reorganization to move from monolithic files toward self-contained domain modules.

## Module Reorganization

### Reference Pattern: `dojo/url/`

All domain modules should match the structure of `dojo/url/`. This is the canonical example of a fully reorganized module.

```
dojo/{module}/
├── __init__.py # import dojo.{module}.admin # noqa: F401
├── models.py # Domain models, constants, factory methods
├── admin.py # @admin.register() for the module's models
├── services.py # Business logic (no HTTP concerns)
├── queries.py # Complex DB aggregations/annotations
├── signals.py # Django signal handlers
├── [manager.py] # Custom QuerySet/Manager if needed
├── [validators.py] # Field-level validators if needed
├── [helpers.py] # Async task wrappers, tag propagation, etc.
├── ui/
│ ├── __init__.py # Empty
│ ├── forms.py # Django ModelForms
│ ├── filters.py # UI-layer django-filter classes
│ ├── views.py # Thin view functions — delegates to services.py
│ └── urls.py # URL routing
└── api/
├── __init__.py # path = "{module}"
├── serializer.py # DRF serializers
├── views.py # API ViewSets — delegates to services.py
├── filters.py # API-layer filters
└── urls.py # add_{module}_urls(router) registration
```

### Architecture Principles


**services.py is the critical layer**: Both `ui/views.py` and `api/views.py` call `services.py` for business logic. Services accept domain objects and primitives — never request/response objects, forms, or serializers.

**Backward-compatible re-exports**: When moving code out of monolithic files (`dojo/models.py`, `dojo/forms.py`, `dojo/filters.py`, `dojo/api_v2/serializers.py`, `dojo/api_v2/views.py`), always leave a re-export at the original location:
```python
from dojo.{module}.models import {Model} # noqa: F401 -- backward compat
```
Never remove re-exports until all consumers are updated in a dedicated cleanup pass.

### Current State

Modules in various stages of reorganization:

| Module | models.py | services.py | ui/ | api/ | Status |
|--------|-----------|-------------|-----|------|--------|
| **url** | In module | N/A | Done | Done | **Complete** |
| **location** | In module | N/A | N/A | Done | **Complete** |
| **product_type** | In dojo/models.py | Missing | Partial (views at root) | In dojo/api_v2/ | Needs work |
| **test** | In dojo/models.py | Missing | Partial (views at root) | In dojo/api_v2/ | Needs work |
| **engagement** | In dojo/models.py | Partial (32 lines) | Partial (views at root) | In dojo/api_v2/ | Needs work |
| **product** | In dojo/models.py | Missing | Partial (views at root) | In dojo/api_v2/ | Needs work |
| **finding** | In dojo/models.py | Missing | Partial (views at root) | In dojo/api_v2/ | Needs work |

### Monolithic Files Being Decomposed

These files still contain code for multiple modules. Extract code to the target module's subdirectory and leave a re-export stub.

- `dojo/models.py` (4,973 lines) — All model definitions
- `dojo/forms.py` (4,127 lines) — All Django forms
- `dojo/filters.py` (4,016 lines) — All UI and API filter classes
- `dojo/api_v2/serializers.py` (3,387 lines) — All DRF serializers
- `dojo/api_v2/views.py` (3,519 lines) — All API viewsets

---

## Reorganization Playbook

When asked to reorganize a module, follow these phases in order. Each phase should be independently verifiable.

### Phase 0: Pre-Flight (Read-Only)

Before any changes, identify all code to extract:

```bash
# 1. Model classes and line ranges in dojo/models.py
grep -n "class {Model}" dojo/models.py

# 2. Form classes in dojo/forms.py
grep -n "class.*{Module}" dojo/forms.py
grep -n "model = {Model}" dojo/forms.py

# 3. Filter classes in dojo/filters.py
grep -n "class.*{Module}\|class.*{Model}" dojo/filters.py

# 4. Serializer classes
grep -n "class.*{Model}" dojo/api_v2/serializers.py

# 5. ViewSet classes
grep -n "class.*{Model}\|class.*{Module}" dojo/api_v2/views.py

# 6. Admin registrations
grep -n "admin.site.register({Model}" dojo/models.py

# 7. All import sites (to verify backward compat)
grep -rn "from dojo.models import.*{Model}" dojo/ unittests/

# 8. Business logic in current views
# Scan dojo/{module}/views.py for: .save(), .delete(), create_notification(),
# jira_helper.*, dojo_dispatch_task(), multi-model workflows
```

### Phase 1: Extract Models

1. Create `dojo/{module}/models.py` with the model class(es) and associated constants
2. Create `dojo/{module}/admin.py` with `admin.site.register()` calls (remove from `dojo/models.py`)
3. Update `dojo/{module}/__init__.py` to `import dojo.{module}.admin # noqa: F401`
4. Add re-exports in `dojo/models.py`
5. Remove original model code (keep re-export line)

**Import rules for models.py:**
- Upward FKs (e.g., Test -> Engagement): import from `dojo.models` if not yet extracted, or `dojo.{module}.models` if already extracted
- Downward references (e.g., Product_Type querying Finding): use lazy imports inside method bodies
- Shared utilities (`copy_model_util`, `_manage_inherited_tags`, `get_current_date`, etc.): import from `dojo.models`
- Do NOT set `app_label` in Meta — all models inherit `dojo` app_label automatically

**Verify:**
```bash
python manage.py check
python manage.py makemigrations --check
python -c "from dojo.{module}.models import {Model}"
python -c "from dojo.models import {Model}"
```

### Phase 2: Extract Services

Create `dojo/{module}/services.py` with business logic extracted from UI views.

**What belongs in services.py:**
- State transitions (close, reopen, status changes)
- Multi-step creation/update workflows
- External integration calls (JIRA, GitHub)
- Notification dispatching
- Copy/clone operations
- Bulk operations
- Merge operations

**What stays in views:**
- HTTP request/response handling
- Form instantiation and validation
- Serialization/deserialization
- Authorization checks (`@user_is_authorized`, `user_has_permission_or_403`)
- Template rendering, redirects
- Pagination, breadcrumbs

**Service function pattern:**
```python
def close_engagement(engagement: Engagement, user: User) -> Engagement:
engagement.active = False
engagement.status = "Completed"
engagement.save()
if jira_helper.get_jira_project(engagement):
dojo_dispatch_task(jira_helper.close_epic, engagement.id, push_to_jira=True)
return engagement
```

Update UI views and API viewsets to call the service instead of containing logic inline.

### Phase 3: Extract Forms to `ui/forms.py`

1. Create `dojo/{module}/ui/__init__.py` (empty)
2. Create `dojo/{module}/ui/forms.py` — move form classes from `dojo/forms.py`
3. Add re-exports in `dojo/forms.py`

### Phase 4: Extract UI Filters to `ui/filters.py`

1. Create `dojo/{module}/ui/filters.py` — move module-specific filters from `dojo/filters.py`
2. Shared base classes (`DojoFilter`, `DateRangeFilter`, `ReportBooleanFilter`) stay in `dojo/filters.py`
3. Add re-exports in `dojo/filters.py`

### Phase 5: Move UI Views/URLs into `ui/`

1. Move `dojo/{module}/views.py` -> `dojo/{module}/ui/views.py`
2. Move `dojo/{module}/urls.py` -> `dojo/{module}/ui/urls.py`
3. Update URL imports:
- product: update `dojo/asset/urls.py`
- product_type: update `dojo/organization/urls.py`
- others: update the include in `dojo/urls.py`

### Phase 6: Extract API Serializers to `api/serializer.py`

1. Create `dojo/{module}/api/__init__.py` with `path = "{module}"`
2. Create `dojo/{module}/api/serializer.py` — move from `dojo/api_v2/serializers.py`
3. Add re-exports in `dojo/api_v2/serializers.py`

### Phase 7: Extract API Filters to `api/filters.py`

1. Create `dojo/{module}/api/filters.py` — move `Api{Model}Filter` from `dojo/filters.py`
2. Add re-exports

### Phase 8: Extract API ViewSets to `api/views.py`

1. Create `dojo/{module}/api/views.py` — move from `dojo/api_v2/views.py`
2. Add re-exports in `dojo/api_v2/views.py`

### Phase 9: Extract API URL Registration

1. Create `dojo/{module}/api/urls.py`:
```python
from dojo.{module}.api import path
from dojo.{module}.api.views import {ViewSet}

def add_{module}_urls(router):
router.register(path, {ViewSet}, path)
return router
```
2. Update `dojo/urls.py` — replace `v2_api.register(...)` with `add_{module}_urls(v2_api)`

### After Each Phase: Verify

```bash
python manage.py check
python manage.py makemigrations --check
python -m pytest unittests/ -x --timeout=120
```

---

## Cross-Module Dependencies

The model hierarchy is: Product_Type -> Product -> Engagement -> Test -> Finding

Extract in this order (top to bottom) so that upward FKs can import from already-extracted modules. The recommended order is: product_type, test, engagement, product, finding.

For downward references (e.g., Product_Type's cached properties querying Finding), always use lazy imports:
```python
@cached_property
def critical_present(self):
from dojo.models import Finding # lazy import
return Finding.objects.filter(test__engagement__product__prod_type=self, severity="Critical").exists()
```

---

## Key Technical Details

- **Single Django app**: Everything is under `app_label = "dojo"`. Moving models to subdirectories does NOT require migration changes.
- **Model discovery**: Triggered by `__init__.py` importing `admin.py`, which imports `models.py`. This is the same chain `dojo/url/` uses.
- **Signal registration**: Handled in `dojo/apps.py` via `import dojo.{module}.signals`. Already set up for test, engagement, product, product_type.
- **Watson search**: Uses `self.get_model("Product")` in `apps.py` — works via Django's model registry regardless of file location.
- **Admin registration**: Currently at the bottom of `dojo/models.py` (lines 4888-4973). Must be moved to `{module}/admin.py` and removed from `dojo/models.py` to avoid `AlreadyRegistered` errors.
2 changes: 1 addition & 1 deletion Dockerfile.django-alpine
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
# Dockerfile.nginx to use the caching mechanism of Docker.

# Ref: https://devguide.python.org/#branchstatus
FROM python:3.13.13-alpine3.22@sha256:d739d0ecd8cd56c6f842d4ce55087ed551c519ec88fa0b3b98a5717c22a083c9 AS base
FROM python:3.13.13-alpine3.22@sha256:e81548ac35b07a3bd4805f275107592ef458b1e893c0e04d45aedaa19416cca5 AS base
FROM base AS build
WORKDIR /app
RUN \
Expand Down
2 changes: 1 addition & 1 deletion Dockerfile.django-debian
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
# Dockerfile.nginx to use the caching mechanism of Docker.

# Ref: https://devguide.python.org/#branchstatus
FROM python:3.13.13-slim-trixie@sha256:2ba73a4dc380f21137fc75296abfa2add90b51fd10b609ce530b40cc097269b1 AS base
FROM python:3.13.13-slim-trixie@sha256:9213d136547f0602c3337ff48291e937f9cc43060b3e123402cf2aaff1a08b75 AS base
FROM base AS build
WORKDIR /app
RUN \
Expand Down
2 changes: 1 addition & 1 deletion Dockerfile.integration-tests-debian
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

FROM openapitools/openapi-generator-cli:v7.21.0@sha256:ce308310f3c1f8761e65338b8ab87b651bf4862c6acb80de510f381fffc4510b AS openapitools
# currently only supports x64, no arm yet due to chrome and selenium dependencies
FROM python:3.13.13-slim-trixie@sha256:2ba73a4dc380f21137fc75296abfa2add90b51fd10b609ce530b40cc097269b1 AS build
FROM python:3.13.13-slim-trixie@sha256:9213d136547f0602c3337ff48291e937f9cc43060b3e123402cf2aaff1a08b75 AS build
WORKDIR /app
RUN \
apt-get -y update && \
Expand Down
2 changes: 1 addition & 1 deletion Dockerfile.nginx-alpine
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
# Dockerfile.django-alpine to use the caching mechanism of Docker.

# Ref: https://devguide.python.org/#branchstatus
FROM python:3.13.13-alpine3.22@sha256:d739d0ecd8cd56c6f842d4ce55087ed551c519ec88fa0b3b98a5717c22a083c9 AS base
FROM python:3.13.13-alpine3.22@sha256:e81548ac35b07a3bd4805f275107592ef458b1e893c0e04d45aedaa19416cca5 AS base
FROM base AS build
WORKDIR /app
RUN \
Expand Down
2 changes: 1 addition & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ services:
source: ./docker/extra_settings
target: /app/docker/extra_settings
postgres:
image: postgres:18.3-alpine@sha256:4da1a4828be12604092fa55311276f08f9224a74a62dcb4708bd7439e2a03911
image: postgres:18.3-alpine@sha256:54451ecb8ab38c24c3ec123f2fd501303a3a1856a5c66e98cecf2460d5e1e9d7
environment:
PGDATA: /var/lib/postgresql/data
POSTGRES_DB: ${DD_DATABASE_NAME:-defectdojo}
Expand Down
Loading
Loading