Skip to content

Commit e0abd59

Browse files
authored
Merge branch 'bugfix' into tests
2 parents a0fdbfa + a7abba8 commit e0abd59

6 files changed

Lines changed: 1968 additions & 119 deletions

File tree

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
---
2+
title: "Adding the dd-orch Database on Upgrade"
3+
toc_hide: true
4+
weight: -20260501
5+
description: "Provisioning the dojodb-ddorch PostgreSQL database and pointing DefectDojo Pro at it on an existing self-hosted installation."
6+
audience: pro
7+
---
8+
9+
Starting with 2.57.3, DefectDojo Pro requires a second PostgreSQL database, `dojodb-ddorch`, used by the new `ddorch` orchestrator service. The existing `dojodb` database continues to be used by the main Django application.
10+
11+
This guide walks through adding `dojodb-ddorch` to an existing self-hosted PostgreSQL instance and pointing DefectDojo at it.
12+
13+
## Prerequisites
14+
15+
- PostgreSQL 16 is already installed and running on the DB server.
16+
- The `dojodbusr` role already exists with a known password.
17+
- `dojodb` is already created and reachable from the DefectDojo app server.
18+
- `listen_addresses` in `postgresql.conf` is already configured for remote access.
19+
- You have upgraded to the release that ships the `ddorch` and `ddorch-workers` services.
20+
21+
> **A note on the database name:** `dojodb-ddorch` contains a hyphen, so it must be double-quoted in every SQL statement (`"dojodb-ddorch"`).
22+
23+
## Part 1: Provision the Database
24+
25+
### 1. Create the new database
26+
27+
On the PostgreSQL server, open a `psql` session as the `postgres` superuser:
28+
29+
```bash
30+
sudo -i -u postgres psql --username postgres
31+
```
32+
33+
Create the database, grant privileges to the existing `dojodbusr` role, and transfer ownership:
34+
35+
```sql
36+
CREATE DATABASE "dojodb-ddorch";
37+
GRANT ALL PRIVILEGES ON DATABASE "dojodb-ddorch" TO dojodbusr;
38+
ALTER DATABASE "dojodb-ddorch" OWNER TO dojodbusr;
39+
\q
40+
```
41+
42+
**Example session:**
43+
44+
```
45+
root@dbserver:~# sudo -i -u postgres psql --username postgres
46+
psql (16.8)
47+
Type "help" for help.
48+
49+
postgres=# CREATE DATABASE "dojodb-ddorch";
50+
CREATE DATABASE
51+
postgres=# GRANT ALL PRIVILEGES ON DATABASE "dojodb-ddorch" TO dojodbusr;
52+
GRANT
53+
postgres=# ALTER DATABASE "dojodb-ddorch" OWNER TO dojodbusr;
54+
ALTER DATABASE
55+
postgres=# \q
56+
```
57+
58+
> **PostgreSQL 15+ note:** Ownership covers schema rights for the owner, but if you ever connect as a non-owner role you will also need to grant schema privileges inside the new database:
59+
>
60+
> ```sql
61+
> \c "dojodb-ddorch"
62+
> GRANT ALL ON SCHEMA public TO dojodbusr;
63+
> ```
64+
65+
### 2. Allow the app server to connect
66+
67+
Edit `/etc/postgresql/16/main/pg_hba.conf` and add a new line for `dojodb-ddorch` next to the existing `dojodb` entry.
68+
69+
**(a) Preferred — restrict to the DefectDojo app server's IP.**
70+
71+
Supposing the app server's IP is `9.9.9.9`, add:
72+
73+
```
74+
host dojodb-ddorch dojodbusr 9.9.9.9/32 scram-sha-256
75+
host postgres dojodbusr 9.9.9.9/32 scram-sha-256
76+
```
77+
78+
**(b) Alternative — allow from any host.**
79+
80+
```
81+
host dojodb-ddorch dojodbusr 0.0.0.0/0 scram-sha-256
82+
host postgres dojodbusr 0.0.0.0/0 scram-sha-256
83+
```
84+
85+
> **Note:** The lines in `pg_hba.conf` are whitespace-delimited. The easiest way to add this line is to copy/paste the existing `dojodb` line and change the database name.
86+
87+
**Alternative using `echo` (if no text editor is available):**
88+
89+
```bash
90+
# For specific IP (replace 9.9.9.9 with your app server IP):
91+
echo "host dojodb-ddorch dojodbusr 9.9.9.9/32 scram-sha-256" | sudo tee -a /etc/postgresql/16/main/pg_hba.conf
92+
echo "host postgres dojodbusr 9.9.9.9/32 scram-sha-256" | sudo tee -a /etc/postgresql/16/main/pg_hba.conf
93+
94+
# OR for all hosts:
95+
echo "host dojodb-ddorch dojodbusr 0.0.0.0/0 scram-sha-256" | sudo tee -a /etc/postgresql/16/main/pg_hba.conf
96+
echo "host postgres dojodbusr 0.0.0.0/0 scram-sha-256" | sudo tee -a /etc/postgresql/16/main/pg_hba.conf
97+
98+
```
99+
100+
### 3. Reload PostgreSQL
101+
102+
Changes to `pg_hba.conf` only require a reload — no restart is needed:
103+
104+
```bash
105+
sudo systemctl reload postgresql
106+
```
107+
108+
Verify the reload was picked up:
109+
110+
```bash
111+
sudo systemctl status postgresql
112+
```
113+
114+
### 4. Verify connectivity from the app server
115+
116+
From the **DefectDojo app server**, confirm `dojodbusr` can reach the new database. Replace `<db-server-ip>` with your DB server's IP and `<password>` with the password set for `dojodbusr`:
117+
118+
```bash
119+
psql "host=<db-server-ip> dbname=dojodb-ddorch user=dojodbusr password=<password>" -c "SELECT 1;"
120+
```
121+
122+
A successful response of `?column?` with a value of `1` confirms the database is reachable and the credentials are valid.
123+
124+
## Part 2: Point DefectDojo at the New Database
125+
126+
Only the `ddorch` service connects to the new database directly. The main Django application reaches the orchestrator over gRPC, so `DD_DATABASE_URL` does **not** change.
127+
128+
### 1. Set the orchestrator database URL
129+
130+
The `ddorch` service reads its connection string from the `DD_DATABASE_URL` environment variable and **automatically appends `-ddorch` to the database name** in whatever URL you pass it. This means you can reuse the same connection string you already use for the main Django application — no need to construct a second URL by hand.
131+
132+
On startup, ddorch rewrites the database name in this URL from `dojodb` to `dojodb-ddorch` and connects to the database you created in Part 1.
133+
134+
### 2. Restart the orchestrator services
135+
136+
From the deployment directory, recreate the two orchestrator containers so they pick up the new environment:
137+
138+
```bash
139+
docker compose up -d ddorch ddorch-workers
140+
```
141+
142+
Docker Compose will detect the environment change and recreate the containers. The `ddorch` service runs its own schema migrations against `dojodb-ddorch` on startup — no manual migration command is required.
143+
144+
### 3. Verify ddorch connected and migrated the new database
145+
146+
The most direct signal that the database is correctly wired up is the ddorch startup log. Check the last hundred lines:
147+
148+
```bash
149+
docker compose logs ddorch --tail=100
150+
```
151+
152+
Look for three log lines in sequence:
153+
154+
```
155+
{"level":"INFO","msg":"Appending database name to DATABASE_URL","from":"dojodb","to":"dojodb-ddorch"}
156+
INFO Running migrations current_schema_version=<N> next_version=<M> migrations_to_apply=<K>
157+
{"level":"INFO","msg":"starting server","port":9871}
158+
```
159+
160+
What each line proves:
161+
162+
- **`Appending database name to DATABASE_URL ... to: dojodb-ddorch`** — ddorch received your URL and derived the orch database name correctly.
163+
- **`Running migrations ... migrations_to_apply=0`** — ddorch connected to `dojodb-ddorch` and found the schema at the expected version. On a first-ever boot against a fresh database you may see `migrations_to_apply=<N>` with a non-zero value and no subsequent error — this means ddorch just created the tables from scratch. Both outcomes indicate success.
164+
- **`starting server ... port:9871`** — ddorch is up and listening.
165+
166+
If instead you see an error such as `FATAL: password authentication failed`, `no pg_hba.conf entry for host`, or `database "dojodb-ddorch" does not exist`, the database is not reachable — revisit Part 1 before proceeding.
167+
168+
Also confirm both orchestrator containers are running:
169+
170+
```bash
171+
docker compose ps ddorch ddorch-workers
172+
```
173+
174+
Both should report `Up`. With ddorch migrated and the workers container running, your installation is now using the new `dojodb-ddorch` database.

dojo/tools/dependency_track/parser.py

Lines changed: 8 additions & 117 deletions
Original file line numberDiff line numberDiff line change
@@ -13,106 +13,6 @@
1313

1414
class DependencyTrackParser:
1515

16-
r"""
17-
A class that can be used to parse the JSON Finding Packaging Format (FPF) export from OWASP Dependency Track.
18-
19-
See here for more info on this JSON format: https://docs.dependencytrack.org/integrations/file-formats/
20-
21-
A typical Finding Packaging Format (FPF) export looks like the following:
22-
23-
{
24-
"version": "1.3",
25-
"meta" : {
26-
"application": "Dependency-Track",
27-
"version": "4.5.0",
28-
"timestamp": "2022-02-18T23:31:42Z",
29-
"baseUrl": "http://dtrack.example.org"
30-
},
31-
"project" : {
32-
"uuid": "ca4f2da9-0fad-4a13-92d7-f627f3168a56",
33-
"name": "Acme Example",
34-
"version": "1.0",
35-
"description": "A sample application"
36-
},
37-
"findings" : [
38-
{
39-
"component": {
40-
"uuid": "b815b581-fec1-4374-a871-68862a8f8d52",
41-
"name": "timespan",
42-
"version": "2.3.0",
43-
"purl": "pkg:npm/timespan@2.3.0",
44-
"latestVersion": "3.2.0"
45-
},
46-
"vulnerability": {
47-
"uuid": "115b80bb-46c4-41d1-9f10-8a175d4abb46",
48-
"source": "NPM",
49-
"vulnId": "533",
50-
"title": "Regular Expression Denial of Service",
51-
"subtitle": "timespan",
52-
"severity": "LOW",
53-
"severityRank": 3,
54-
"cvssV2Vector": "CVSS:2.0/AV:N/AC:L/Au:N/C:P/I:P/A:P",
55-
"cvssV3Vector": "CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
56-
"cvssV4Vector": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:L/SC:N/SI:N/SA:N",
57-
"references": "* [https://example.com](https://example.com)\n* [https://example.org](https://example.org)",
58-
"published": "2025-07-11 03:16:03.563",
59-
"cweId": 400,
60-
"cweName": "Uncontrolled Resource Consumption ('Resource Exhaustion')",
61-
"cwes": [
62-
{
63-
"cweId": 400,
64-
"name": "Uncontrolled Resource Consumption ('Resource Exhaustion')"
65-
}
66-
],
67-
"description": "Affected versions of `timespan`...",
68-
"recommendation": "No direct patch is available..."
69-
},
70-
"analysis": {
71-
"state": "NOT_SET",
72-
"isSuppressed": false
73-
},
74-
"matrix": "ca4f2da9-0fad-4a13-92d7-f627f3168a56:b815b581-fec1-4374-a871-68862a8f8d52:115b80bb-46c4-41d1-9f10-8a175d4abb46"
75-
},
76-
{
77-
"component": {
78-
"uuid": "979f87f5-eaf5-4095-9d38-cde17bf9228e",
79-
"name": "uglify-js",
80-
"version": "2.4.24",
81-
"purl": "pkg:npm/uglify-js@2.4.24"
82-
},
83-
"vulnerability": {
84-
"uuid": "701a3953-666b-4b7a-96ca-e1e6a3e1def3",
85-
"source": "NPM",
86-
"vulnId": "48",
87-
"aliases": [
88-
{
89-
"cveId": "CVE-2022-2053",
90-
"ghsaId": "GHSA-95rf-557x-44g5"
91-
}
92-
],
93-
"title": "Regular Expression Denial of Service",
94-
"subtitle": "uglify-js",
95-
"severity": "LOW",
96-
"severityRank": 3,
97-
"cweId": 400,
98-
"cweName": "Uncontrolled Resource Consumption ('Resource Exhaustion')",
99-
"cwes": [
100-
{
101-
"cweId": 400,
102-
"name": "Uncontrolled Resource Consumption ('Resource Exhaustion')"
103-
}
104-
],
105-
"description": "Versions of `uglify-js` prior to...",
106-
"recommendation": "Update to version 2.6.0 or later."
107-
},
108-
"analysis": {
109-
"isSuppressed": false
110-
},
111-
"matrix": "ca4f2da9-0fad-4a13-92d7-f627f3168a56:979f87f5-eaf5-4095-9d38-cde17bf9228e:701a3953-666b-4b7a-96ca-e1e6a3e1def3"
112-
}]
113-
}
114-
"""
115-
11616
def _convert_dependency_track_severity_to_dojo_severity(self, dependency_track_severity):
11717
"""
11818
Converts a Dependency Track severity to a DefectDojo severity.
@@ -171,23 +71,14 @@ def _convert_dependency_track_finding_to_dojo_finding(self, dependency_track_fin
17171

17272
title = f"{component_name}:{version_description} affected by: {vuln_id} ({source})"
17373

174-
# We should collect all the vulnerability ids, the FPF format can add additional IDs as aliases
175-
# we add these aliases in the vulnerability_id list making sure duplicate findings get correctly deduplicated
176-
# older version of Dependency-track might not include these field therefore lets check first
177-
if dependency_track_finding["vulnerability"].get("aliases"):
178-
# There can be multiple alias entries
179-
set_of_ids = set()
180-
set_of_sources = {"cveId", "sonatypeId", "ghsaId", "osvId", "snykId", "gsdId", "vulnDbId"}
181-
for alias in dependency_track_finding["vulnerability"]["aliases"]:
182-
for source in set_of_sources:
183-
if source in alias:
184-
set_of_ids.add(alias[source])
185-
vulnerability_id = list(set_of_ids)
186-
else:
187-
# The vulnId is not always a CVE (e.g. if the vulnerability is not from the NVD source)
188-
# So here we set the cve for the DefectDojo finding to null unless the source of the
189-
# Dependency Track vulnerability is NVD
190-
vulnerability_id = [vuln_id] if source is not None and source.upper() == "NVD" else None
74+
# Collect all vulnerability IDs: vulnId itself plus any aliases
75+
set_of_ids = {vuln_id}
76+
set_of_alias_sources = {"cveId", "sonatypeId", "ghsaId", "osvId", "snykId", "gsdId", "vulnDbId"}
77+
for alias in dependency_track_finding["vulnerability"].get("aliases") or []:
78+
for alias_source in set_of_alias_sources:
79+
if alias_source in alias:
80+
set_of_ids.add(alias[alias_source])
81+
vulnerability_id = list(set_of_ids)
19182

19283
# Default CWE to CWE-1035 Using Components with Known Vulnerabilities if there is no CWE
19384
if "cweId" in dependency_track_finding["vulnerability"] and dependency_track_finding["vulnerability"]["cweId"] is not None:

0 commit comments

Comments
 (0)