Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
126 commits
Select commit Hold shift + click to select a range
cc09eba
Update versions in application files
Aug 3, 2026
e668c87
Merge pull request #15491 from DefectDojo/master-into-dev/3.2.0-3.3.0…
rossops Aug 3, 2026
54dff17
feat(parsers): add fifty-six file parsers for vendors with no importe…
devGregA Aug 4, 2026
44d93fc
feat(dedupe): let a false-positive-history hook supply candidates, no…
devGregA Aug 4, 2026
32ca6e1
ci(migrations): bring the migration graph check to dev (#15504)
devGregA Aug 4, 2026
fdd6538
refactor(finding): extract save()'s field derivation so batched write…
devGregA Aug 5, 2026
98c39d3
ci: port the bugfix CI stack to dev (#15549)
devGregA Aug 5, 2026
810c453
fix(reimporter): always dispatch the final post-processing batch (#15…
devGregA Aug 6, 2026
403ed95
chore(deps): bump pillow from 12.2.0 to 12.3.0 (#15302)
dependabot[bot] Aug 7, 2026
8008030
chore(deps): update dependency node from 24.18.0 to v24.18.1 (.github…
renovate[bot] Aug 7, 2026
90d713c
chore(deps): update dependency renovatebot/renovate from 43.288.0 to …
renovate[bot] Aug 7, 2026
5b96096
chore(deps): update valkey docker tag from 0.24.6 to v0.25.0 (helm/de…
renovate[bot] Aug 7, 2026
d9eed6f
chore(deps): bump redis from 8.0.1 to 8.1.0 (#15536)
dependabot[bot] Aug 7, 2026
63169f6
chore(deps): bump django-polymorphic from 4.11.6 to 4.11.7 (#15538)
dependabot[bot] Aug 7, 2026
ba58a0d
chore(deps): update dependency django-test-migrations from 1.5.0 to v…
renovate[bot] Aug 7, 2026
7149fab
chore(deps): update python docker tag from 3.14.6 to v3.14.7 (dockerf…
renovate[bot] Aug 7, 2026
7bb9cac
docs(universal parser): field mappings can be edited, with identity c…
devGregA Aug 7, 2026
54d04b0
chore(deps): bump markdown from 3.10.2 to 3.10.3 (#15540)
dependabot[bot] Aug 7, 2026
a971f6c
chore(deps): bump ruff from 0.16.0 to 0.16.1 (#15543)
dependabot[bot] Aug 7, 2026
fa6a340
chore(deps): bump pyopenssl from 26.3.0 to 26.4.0 (#15535)
dependabot[bot] Aug 7, 2026
69201ef
chore(deps): bump drf-spectacular-sidecar from 2026.7.1 to 2026.8.1 (…
dependabot[bot] Aug 7, 2026
4ba07fb
chore(deps): update dependency node from 24.18.1 to v24.19.0 (.github…
renovate[bot] Aug 7, 2026
5c815c0
chore(deps): bump django-crispy-forms from 2.6 to 2.7 (#15534)
dependabot[bot] Aug 7, 2026
0bff3b6
docs(notifications): note where notification settings live in the Pro…
Maffooch Aug 7, 2026
fa6bdfb
fix(engagement): allow saving engagements with an empty status (#15472)
Maffooch Aug 7, 2026
b93b3f7
chore(deps): bump cryptography from 49.0.0 to 50.0.0 (#15541)
dependabot[bot] Aug 7, 2026
460889b
docs(import): DISA STIG checklist import, and the CCI control crosswa…
devGregA Aug 7, 2026
4c7d8da
refactor(ui): remove the classic Bootstrap UI (#15565)
Maffooch Aug 7, 2026
62695c6
docs(psirt): PSIRT module documentation (#15555)
devGregA Aug 8, 2026
9887d71
feat(api-tokens): API token expiry and revoke-by-key endpoint (#14932)
svader0 Aug 8, 2026
e97be18
docs: Exporting SBOMs and VEX (Pro)
Aug 9, 2026
e2052a6
Merge pull request #15584 from devGregA/docs/sbom-vex-export
devGregA Aug 9, 2026
ed550de
feat(parsers): add 39 file-import parsers (with tests, fixtures, and …
devGregA Aug 10, 2026
4eae248
docs: the sidebar page now covers the whole menu, not just Settings (…
devGregA Aug 10, 2026
3e57fed
docs(universal parser): document editing field mappings from the UI (…
devGregA Aug 10, 2026
6a2d126
docs(connectors): a connector's field mappings can be customised per …
devGregA Aug 10, 2026
682b7c3
perf(locations): clean and stringify each URL location once per impor…
devGregA Aug 10, 2026
775d7be
feat(locations): make the endpoint->location migration resumable and …
devGregA Aug 10, 2026
2080b4d
docs(sensei): add the Sensei Advisor page (#15594)
devGregA Aug 10, 2026
6c5e7a3
refactor(locations): let locations be recorded before the finding is …
devGregA Aug 10, 2026
c9b78d0
Update versions in application files
Aug 10, 2026
3655a66
Merge remote-tracking branch 'origin/dev' into master-into-dev/3.2.10…
rossops Aug 10, 2026
3e6002b
fix(lint): drop duplicate pre_save_logic left by the merge back
rossops Aug 10, 2026
3d6a00e
Merge pull request #15606 from DefectDojo/master-into-dev/3.2.100-3.3…
rossops Aug 10, 2026
4a39f6d
feat(parsers): fifteen new file-import parsers (#15611)
devGregA Aug 10, 2026
e546fc9
refactor(importers): reconcile a finding's child rows before the find…
devGregA Aug 11, 2026
8c41868
docs: organization types + non-exclusive membership (#15619)
devGregA Aug 11, 2026
c07bf26
feat(i18n): full platform internationalization with per-user language…
devGregA Aug 11, 2026
bc3be8d
refactor(reimporter): a seam for deferring the new-finding write (#15…
devGregA Aug 11, 2026
e8c9550
fix(ci): drop duplicated db-snapshot steps that make dev's unit test …
devGregA Aug 11, 2026
71807de
docs: asset versions, BOM snapshots, and per-version SBOM/VEX export …
devGregA Aug 12, 2026
4acf228
chore(deps): update valkey docker tag from 0.25.0 to v0.25.4 (helm/de…
renovate[bot] Aug 12, 2026
6262001
chore(deps): update dependency django-debug-toolbar from 7.0.0 to v7.…
renovate[bot] Aug 12, 2026
6b55911
update Helm documentation
github-actions[bot] Aug 12, 2026
9ad13cd
chore(deps): update release-drafter/release-drafter action from v7.5.…
renovate[bot] Aug 12, 2026
a32ac15
docs: asset kinds and per-source asset aliases (asset model Phase 4) …
devGregA Aug 12, 2026
ba874b0
chore(deps): bump django-htmx from 1.28.0 to 1.29.0
dependabot[bot] Aug 12, 2026
7fbb995
chore(deps): bump setuptools from 83.0.0 to 84.0.0
dependabot[bot] Aug 12, 2026
4ba0419
chore(deps): bump gitpython from 3.1.57 to 3.1.58
dependabot[bot] Aug 12, 2026
218a3ce
chore(deps): bump ruff from 0.16.1 to 0.16.2
dependabot[bot] Aug 12, 2026
f23f138
chore(deps): bump djangorestframework from 3.17.1 to 3.18.0
dependabot[bot] Aug 12, 2026
2517246
Merge pull request #15644 from DefectDojo/dependabot/pip/dev/djangore…
Maffooch Aug 12, 2026
e2e6be8
Merge pull request #15643 from DefectDojo/dependabot/pip/dev/ruff-0.16.2
Maffooch Aug 12, 2026
3860864
Merge pull request #15638 from DefectDojo/dependabot/pip/dev/setuptoo…
Maffooch Aug 12, 2026
e6c045b
Merge pull request #15631 from DefectDojo/renovate/django-debug-toolb…
Maffooch Aug 12, 2026
084565d
Merge pull request #15637 from DefectDojo/dependabot/pip/dev/django-h…
Maffooch Aug 12, 2026
6a9b174
Merge pull request #15630 from DefectDojo/renovate/valkey-0.25.x
Maffooch Aug 12, 2026
aead077
Merge pull request #15635 from DefectDojo/renovate/release-drafter-re…
Maffooch Aug 12, 2026
df18976
Merge pull request #15639 from DefectDojo/dependabot/pip/dev/gitpytho…
Maffooch Aug 12, 2026
90a4e56
fix(dedupe): stop Xeol finding identity depending on the wall clock (…
devGregA Aug 13, 2026
886aed6
fix(dedupe): hash Checkmarx One on the vendor id it already matches o…
devGregA Aug 13, 2026
a0adb71
feat: API v3 (alpha) — parallel /api/v3-alpha/ with slim refs, expand…
valentijnscholten Aug 13, 2026
29d049d
fix(dedupe): give Checkmarx Scan detailed a hash_code field list (#15…
devGregA Aug 13, 2026
825e41e
refactor(importers): extract persist_new_findings as a bulk-write sea…
valentijnscholten Aug 14, 2026
effd88b
refactor(importers): track reimport finding buckets by id, not instan…
valentijnscholten Aug 14, 2026
3013a2c
fix(dedupe): order locations/endpoints inside hash_code, and catch se…
valentijnscholten Aug 14, 2026
40a1129
refactor(dedupe): one definition of the location prefetch (#15520)
valentijnscholten Aug 14, 2026
4b7728d
chore(deps): update python:3.14.7-slim-trixie docker digest from 3.14…
renovate[bot] Aug 14, 2026
56e6b8f
docs: where to find Asset types and aliases in the UI (asset model Ph…
devGregA Aug 14, 2026
dd89a43
chore(deps): update dependency renovatebot/renovate from 44.14.3 to v…
renovate[bot] Aug 14, 2026
4c83531
perf(finding): drop four dojo_finding indexes that no query uses (#15…
devGregA Aug 14, 2026
1043510
chore(deps): update python:3.14.7-alpine3.23 docker digest from 3.14.…
renovate[bot] Aug 14, 2026
52011d5
docs(asset-hierarchy): relationship types, and direct vs indirect vul…
devGregA Aug 15, 2026
0148246
docs(assets): how to reach Asset Versions and per-version claims in t…
devGregA Aug 15, 2026
609a509
Update versions in application files
Aug 17, 2026
4fcbce6
Merge remote-tracking branch 'origin/dev' into master-into-dev/3.2.20…
rossops Aug 17, 2026
2b33be2
fix(locations): route the parsers added on dev through locations_enab…
rossops Aug 17, 2026
94f7bc9
fix(metrics): revert #15601, which left metrics.js unparseable
rossops Aug 17, 2026
43965b9
test(importers): add both sides' reimport query bumps, not one
rossops Aug 17, 2026
dc092b7
Merge pull request #15692 from DefectDojo/master-into-dev/3.2.200-3.3…
rossops Aug 17, 2026
341f0d5
Update versions in application files
Aug 18, 2026
bb8ab28
Merge remote-tracking branch 'origin/dev' into master-into-dev/3.2.20…
Maffooch Aug 18, 2026
6d5f5c6
Merge pull request #15708 from DefectDojo/master-into-dev/3.2.201-3.3…
Maffooch Aug 18, 2026
e96641a
chore(deps): update dependency renovatebot/renovate from 44.30.1 to v…
renovate[bot] Aug 18, 2026
ad64239
docs(psirt): who can use PSIRT, and why the permission beats the role…
devGregA Aug 19, 2026
2fd1467
feat: kev_date logic added to anchore grype (#15730)
Kasyap7 Aug 19, 2026
cf6b6a4
docs(rules-engine-2): document the exploit-evidence, reachability and…
devGregA Aug 19, 2026
77ea8ac
docs(psirt): document Feed Rules, the no-SBOM path, and advisory-to-c…
devGregA Aug 19, 2026
64bf4da
chore(deps): update docker/setup-buildx-action action from v4.2.0 to …
renovate[bot] Aug 19, 2026
2857918
docs(rules-engine-2): document the missing-scan trigger (#15731)
devGregA Aug 19, 2026
f802d92
chore(deps): update postgres docker tag from 18.4 to v18.6 (docker-co…
renovate[bot] Aug 19, 2026
def9b4d
chore(deps): bump django-permissions-policy from 4.32.0 to 4.33.0 (#1…
dependabot[bot] Aug 19, 2026
9f7d80c
chore(deps): bump sqlalchemy from 2.0.51 to 2.0.52 (#15727)
dependabot[bot] Aug 19, 2026
3008513
chore(deps): bump gitpython from 3.1.58 to 3.1.59 (#15726)
dependabot[bot] Aug 19, 2026
f1c5d9f
chore(deps): bump @scalar/api-reference in /components (#15725)
dependabot[bot] Aug 19, 2026
c84a053
chore(deps-dev): bump django-debug-toolbar from 7.1.0 to 7.1.1 (#15724)
dependabot[bot] Aug 19, 2026
4b86125
chore(deps): bump vulners from 4.0.0 to 4.0.1 (#15723)
dependabot[bot] Aug 19, 2026
12f66c0
chore(deps): bump ruff from 0.16.2 to 0.16.3 (#15722)
dependabot[bot] Aug 19, 2026
806b634
docs(federal): document PAIN-keyed VDR remediation deadlines (#15719)
devGregA Aug 19, 2026
82a05c3
chore(deps): update dependency django-debug-toolbar from 7.1.0 to v7.…
renovate[bot] Aug 19, 2026
ea79492
docs(connectors): note that Location is pre-filled for single-host to…
svader0 Aug 19, 2026
029bfb7
docs(organizations): roles scoped to an organization type (#15673)
devGregA Aug 19, 2026
2679903
docs(notifications): document the Connector Health Warning notificati…
svader0 Aug 19, 2026
f713f4b
docs(connectors): document the data-visibility warnings (#15634)
svader0 Aug 19, 2026
979ccd9
docs: notifications follow every organization membership (#15628)
devGregA Aug 19, 2026
73c81e9
chore(deps): update valkey docker tag from 0.25.4 to v0.25.5 (helm/de…
renovate[bot] Aug 19, 2026
369b0cd
docs: asset exposure and deployment context (#15679)
devGregA Aug 19, 2026
6665276
chore(deps): update gcr.io/cloudsql-docker/gce-proxy docker tag from …
renovate[bot] Aug 20, 2026
30b4d08
chore(deps): update nginx/nginx-prometheus-exporter docker tag from 1…
renovate[bot] Aug 20, 2026
e718fc0
docs: Fix typos (#15747)
9alexx3 Aug 20, 2026
c1f3fa9
chore(deps): update valkey/valkey:9.1.1-alpine docker digest from 9.1…
renovate[bot] Aug 20, 2026
b9982b7
docs: restore eight markdown links mangled into NUL bytes (#15756)
devGregA Aug 21, 2026
8f9f527
docs(connectors): document GitHub issue import on the GHAS connector …
devGregA Aug 21, 2026
8c08536
docs(connectors): Wiz imports without Projects via a tenant-level Rec…
svader0 Aug 21, 2026
51fa065
docs: 3.3.x upgrade notes for the three deduplication identity change…
devGregA Aug 22, 2026
edb9c77
fix: correct #toggleBox positioning on login page in Chrome
koushik-hs Aug 23, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
2 changes: 1 addition & 1 deletion .github/workflows/build-docker-images-for-testing.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ jobs:
run: echo "IMAGE_REPOSITORY=$(echo ${{ github.repository }} | tr '[:upper:]' '[:lower:]')" >> $GITHUB_ENV

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # v4.2.0
uses: docker/setup-buildx-action@37fe631027851001ddb9b187196cc803df7f5f0e # v4.3.0

- name: Build
id: docker_build
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 @@ -24,7 +24,7 @@ jobs:
- name: Setup Node
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: '24.18.0' # TODO: Renovate helper might not be needed here - needs to be fully tested
node-version: '24.19.0' # TODO: Renovate helper might not be needed here - needs to be fully tested

- name: Cache dependencies
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release-x-manual-docker-containers.yml
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ jobs:

- name: Set up Docker Buildx
id: buildx
uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # v4.2.0
uses: docker/setup-buildx-action@37fe631027851001ddb9b187196cc803df7f5f0e # v4.3.0

# we cannot set any tags here, those are set on the merged digest in release-x-manual-merge-container-digests.yml
- name: Build and push images
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ jobs:
password: ${{ secrets.DOCKERHUB_TOKEN }}

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # v4.2.0
uses: docker/setup-buildx-action@37fe631027851001ddb9b187196cc803df7f5f0e # v4.3.0

# the alpine and debian images are tagged with the os name
- name: Create OS specific manifest list and push
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release-x-manual-tag-as-latest.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ jobs:
password: ${{ secrets.DOCKERHUB_TOKEN }}

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # v4.2.0
uses: docker/setup-buildx-action@37fe631027851001ddb9b187196cc803df7f5f0e # v4.3.0

- name: Tag with latest tags
run: |
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release_drafter_valentijn.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ jobs:
update_release_draft:
runs-on: ubuntu-latest
steps:
- uses: release-drafter/release-drafter@4d75298e00d9e34c483e5ff8c68d0ea1c1940c1e # v7.5.1
- uses: release-drafter/release-drafter@34d80673e067bdc0c24568d3af899c216adcfaa9 # v7.7.0
with:
version: ${{ github.event.inputs.version }}
filter-by-range: ${{ github.event.inputs['filter-by-range'] }}
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.288.0 # renovate: datasource=github-releases depName=renovatebot/renovate
validator_version: 44.33.2 # 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 @@ -19,7 +19,7 @@ jobs:
- name: Setup Node
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: '24.18.0' # TODO: Renovate helper might not be needed here - needs to be fully tested
node-version: '24.19.0' # TODO: Renovate helper might not be needed here - needs to be fully tested

- name: Cache dependencies
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
Expand Down
504 changes: 504 additions & 0 deletions API_V3_DIVERGENCE_ANALYSIS.md

Large diffs are not rendered by default.

588 changes: 588 additions & 0 deletions API_V3_PLAN.md

Large diffs are not rendered by default.

12 changes: 11 additions & 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.14.6-alpine3.23@sha256:b165067c5afc37fa5608a3c05609cc3d51aafd808a30fbfd822ee594fef55ad4 AS base
FROM python:3.14.7-alpine3.23@sha256:6b8f06d04d5305c1d1288435388df9165ab41e681fae6439d6349d8053cc3f83 AS base
FROM base AS build
WORKDIR /app
RUN \
Expand Down Expand Up @@ -91,6 +91,16 @@ RUN \
mkdir -p dojo/migrations && \
chmod g=u dojo/migrations && \
true

# Compile translation catalogs (.mo) into the image (the .mo files are gitignored
# and generated at build). msgfmt is run directly so no Django settings/env are
# needed; gettext is only required at build time, so install and remove it here.
# BusyBox find has no -execdir, so the destination is derived with shell
# parameter expansion instead of relying on -execdir's working-directory change.
RUN \
apk add --no-cache --virtual .build-i18n gettext && \
find dojo/locale -name 'django.po' -exec sh -c 'msgfmt "$1" -o "${1%.po}.mo"' _ {} \; && \
apk del .build-i18n
USER root
RUN \
addgroup --gid ${gid} ${appuser} && \
Expand Down
15 changes: 14 additions & 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.14.6-slim-trixie@sha256:cea0e6040540fb2b965b6e7fb5ffa00871e632eef63719f0ea54bca189ce14a6 AS base
FROM python:3.14.7-slim-trixie@sha256:ce40764625a4ff50df3548277632e7f96c4e77fe75fa848aae9885476e7df5a4 AS base
FROM base AS build
WORKDIR /app
RUN \
Expand Down Expand Up @@ -105,6 +105,19 @@ RUN \
mkdir -p dojo/migrations && \
chmod g=u dojo/migrations && \
true

# Compile translation catalogs (.mo) into the image (the .mo files are gitignored
# and generated at build). msgfmt is run directly so no Django settings/env are
# needed; gettext is only required at build time, so install and purge it here.
# Kept in the portable -exec form (not -execdir) so this stays identical to the
# alpine Dockerfile, whose BusyBox find has no -execdir.
RUN \
apt-get -y update && \
apt-get -y install --no-install-recommends gettext && \
find dojo/locale -name 'django.po' -exec sh -c 'msgfmt "$1" -o "${1%.po}.mo"' _ {} \; && \
apt-get -y purge --auto-remove gettext && \
apt-get clean && \
rm -rf /var/lib/apt/lists
USER root
RUN \
addgroup --gid ${gid} ${appuser} && \
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.24.0@sha256:5bf3dc75f764c584da8e3344c51b2f3f1e74703461d46a035b5ac1d31515cc88 AS openapitools
# currently only supports x64, no arm yet due to chrome and selenium dependencies
FROM python:3.14.6-slim-trixie@sha256:cea0e6040540fb2b965b6e7fb5ffa00871e632eef63719f0ea54bca189ce14a6 AS build
FROM python:3.14.7-slim-trixie@sha256:ce40764625a4ff50df3548277632e7f96c4e77fe75fa848aae9885476e7df5a4 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.14.6-alpine3.23@sha256:b165067c5afc37fa5608a3c05609cc3d51aafd808a30fbfd822ee594fef55ad4 AS base
FROM python:3.14.7-alpine3.23@sha256:6b8f06d04d5305c1d1288435388df9165ab41e681fae6439d6349d8053cc3f83 AS base
FROM base AS build
WORKDIR /app
RUN \
Expand Down
123 changes: 123 additions & 0 deletions api-v3-alpha-debrief.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
# API v3 Alpha — Combined Implementation Debrief

**Date:** 2026-07-19 · **Branch:** `upstream/feature/api-v3-alpha` · **Status:** all OS phases complete, feature-complete per plan §2
**Final state:** 267 tests green + 2 CI-excluded harnesses · ruff clean · v2 untouched and green · framework: django-ninja 1.6.2 (pydantic 2.13.4 transitive)

How this was built: one Opus subagent per phase working from `API_V3_PLAN.md`; a coordinator reviewed every diff against the §4 contract and §5 invariants and independently re-ran the full suite before each commit. Per-phase detail reports live in `.claude/os*-report.md` (uncommitted).

---

## Commit timeline

| Commit | Phase | Tests after |
|---|---|---:|
| `0102153` | Plan document | — |
| `9ae4abc` | OS1 — kernel + findings read + import | 37 |
| `62c2713` | OS2 — kernel hardening | 65 |
| `31de8e8` | OS3a — product_type/product/user CRUD | 129 |
| `9e27c68` | OS3b — engagement/test CRUD + finding services | 197 |
| `5d9aa8a` | Query sweep harness (N+1 detector) | 198 |
| `65fd508` | OS4 — locations | 229 |
| `36d3c8b` | OS5 — notes/tags/files sub-resources | 252 |
| `9c341420` | OS6 — verification/docs/examples/benchmark | 267 (+2 skipped) |

---

## OS1 — Foundation, findings read path, import, framework gate

**Delivered:** `dojo/api_v3/` kernel (`api.py` mount, `auth.py` TokenAuth reusing the v2 token store + session/CSRF via `django_auth`, `pagination.py` envelope with hybrid exact→planner-estimate counts, `errors.py` RFC 9457 problem+json + DRF boundary adapter, `expand.py` cycle guard + budget + expand-driven `select_related`/`prefetch_related`, `filtering.py` django-filter adapter, `include.py` counts); `dojo/finding/api_v3/` (FindingSlim/Detail, `build_findings_router()`); `dojo/importers/services.py` (`ImportResult` replacing the 7-tuple); `POST /import` with mode auto|import|reimport; conditional mount at `/api/v3/` gated on `V3_FEATURE_LOCATIONS`.

**Gate (all 7 criteria pass → GO on Ninja):** constant query counts (5 slim / 7 expanded at 10 and 100 rows vs v2 91→271 / 222→762); import DB-state equivalence vs v2; RBAC via `get_authorized_findings`; django-filter reuse proven; both auth modes on the same endpoints; OpenAPI renders; subclass-and-remount seam demo (I4/I5).

**Coordinator review caught:** `locations_count` used `Count("locations")` without `distinct=True` — inflated counts when combined with the `tags__in` join. Fixed pre-commit.

**Notable §12 decisions:** CSRF enforced by `SessionAuth` (ninja 1.6 dropped `NinjaAPI(csrf=)`); no trailing slashes (architect confirmed); `LOGIN_EXEMPT_URLS` append so anonymous gets 401 problem+json, not a login redirect.

## OS2 — Kernel hardening

**Delivered:** severity ordering by rank (query-time Case/When mirroring v2's `numerical_severity`, never alphabetical); strict unknown-filter-param → 400 (typo'd filters must not silently return everything); `FilterSpec` registry + vocabulary snapshot test (contract drift fails CI); `expand=locations` swapping `locations_count` for edge rows via a special renderer with declared prefetch paths; dedicated `fields` problem type; 13-test problem+json error-path sweep; OpenAPI schema-generation guard.

**Architect calls:** `api.py` stays in the kernel package as composition root; strict-400 stance kept; fields/expand interplay deferred to OS4.

## OS3a — product_type, product, user CRUD

**Delivered:** three resources with Slim/Detail/Write/Update schemas (`extra="forbid"` → unknown write field 400), router factories, GET/POST/PATCH/DELETE (no PUT in alpha — additive later), FilterSpecs, canonical slim schemas relocated out of the finding module (is-identity asserted). Deletion mirrors v2 exactly: product/product_type use `async_delete()` or synchronous delete inside `Endpoint.allow_endpoint_init()`; user delete is plain + self-delete guard. `UserSerializer.validate()` rules ported (superuser/staff gating, password write-only, no self-delete).

**Coordinator review caught (the big one):** the agent had opened collaborator-scoped user reads — including emails — to every authenticated user, a PII-exposure widening vs v2's `view_user` config-permission gate, with the absurd side effect of 404 on your own user record. Sent back and corrected: v2-parity `view_user` gate, and plain users see exactly themselves (guaranteed self-read).

## OS3b — engagement/test CRUD + finding writes (the D7 flagship)

**Delivered:** engagement + test resources (same pattern; deletion mirrors v2 — notably *without* the `allow_endpoint_init` wrapper, unlike products, faithfully mirroring v2); `dojo/finding/services.py` with `create_finding`/`update_finding`/`delete_finding` extracted by reconciling **both** reference implementations (v2 serializer + UI `edit_finding` flows); `POST/PATCH/DELETE /findings` as thin routes with the 404-then-403 permission ladder.

**Divergence table (17 rows, in `.claude/os3b-report.md`):** serializer semantics canonical for the API — risk-acceptance processing before field updates, synchronous JIRA push with `force_sync=True` raising on failure (mapped to problem+json 400, tested with mocks), `finding_added` notification on create. Deferred as UI-only to CONV2: `last_reviewed` stamping, false-positive-history reactivation, finding-group handling, burp req/resp, github, jira link/unlink. One deliberate deviation: v3 resyncs `Finding_CWE` when scalar `cwe` changes (v2's scalar path doesn't) — consistency chosen, logged.

Also fixed en route: a pydantic forward-ref shadowing bug (`date` field default shadowing the `date` type).

## Query sweep harness (coordinator-built, architect-requested)

`unittests/api_v3/query_report.py` + `test_apiv3_query_report.py`: captures per-request SQL, normalizes literals, flags the N+1 signature (same shape ≥4× in one request) across **every** mounted v3 GET route with fanned-out rows (15+) so per-row queries can't hide. An OpenAPI completeness gate fails the test whenever a new GET endpoint lacks a representative request — OS4/OS5 were forced to extend it, by construction. Writes `/tmp/apiv3_query_report.md` every run. Result across the finished surface: **zero N+1 flags**.

## OS4 — Locations

**Delivered:** `GET /locations` + `/locations/{id}` read-only (superuser gate — verified faithful mirror of v2 `LocationViewSet`'s `IsSuperUser`; rows still drawn via `get_authorized_locations` as the future RBAC seam); `GET /findings/{id}/locations` (edge rows: location ref + status + audit_time + auditor) and `GET /products/{id}/locations` (location ref + status — the model has no audit columns on the product edge); auditor added to `expand=locations` (closing the OS2 deferral); fields/expand interplay resolved kernel-side (`?fields=` allowlist = schema fields ∪ expandable keys); **flag-off test**: in-process URLconf reload proves `V3_FEATURE_LOCATIONS=False` unmounts all of `/api/v3/`.

## OS5 — Notes / tags / files sub-resources

**Delivered:** three generic kernel factories (`dojo/api_v3/subresources.py`), attached only where models have real storage — the plan's "all seven resources" was wrong:

| resource | notes | tags | files |
|---|:---:|:---:|:---:|
| finding / engagement / test | ✓ | ✓ | ✓ |
| product | — | ✓ | — |
| product_type / user / location | — | — | — |

**v2 parity findings:** note privacy is report-exclusion only, never a per-user read filter (verified against v2 code paths); tags go through the tagulous `force_lowercase` + inheritance write path; files validate via `FileUpload.clean()` with streamed downloads. Authorization: parent via authorized queryset (404), then per-method permission (403), values mirroring v2's related-object permission classes.

**Known alpha parity gap (recorded, deliberate):** v3 note creation does not yet fire the v2 finding-note side-effects (JIRA comment, `last_reviewed`, @mentions) — those are resource-specific side-effects that belong in services (D7), landing with the convergence track.

## OS6 — Verification, docs, examples, benchmark

**Delivered:**
- **RBAC expand sweep** (15 tests): no expanded object, included count, denormalized parent ref, or sub-resource row ever drawn from outside the caller's authorized querysets — the v3 port of `test_apiv2_prefetch_rbac`'s intent.
- **Benchmark** (1021 findings, limit=100, N=30, in-process — latency directional, query counts load-bearing):

| Scenario | Queries | Median | p95 |
|---|---:|---:|---:|
| v2 `?prefetch=test` | 636 | 521.2 ms | 720.5 ms |
| v2 (no prefetch) | 229 | 192.7 ms | 424.3 ms |
| **v3 slim** | **5** | **37.9 ms** | 45.2 ms |
| **v3 `?expand=test.engagement`** | **7** | **60.2 ms** | 231.1 ms |

- **`api_v3_examples.md`** (repo root, committed): auto-generated verbatim request/response pairs — findings (detail, expand, filtered+paginated lists, `include=counts`, notes, locations edges, import, PATCH) and products as the simple contrast. Regenerate: `DD_API_V3_EXAMPLES=1` harness (CI-excluded).
- **Docs page**: `docs/content/automation/api/api-v3-alpha-docs.md` (next to the v2 page; plain markdown, no unverified shortcodes) — overview, auth (v2 tokens work unchanged), contract summary, v2→v3 mapping, **Known alpha gaps** section, beta URL-migration notice.
- **Invariants I1–I10: all pass** (verdict table in `.claude/os6-report.md`); v2 regression sample untouched-and-green (`test_rest_framework` 879 OK, `test_apiv2_prefetch_rbac` 10 OK); `manage.py check` clean.
- **Scalar docs-UI: deferred** — alpha keeps ninja's built-in Swagger at `/api/v3/docs`; vendoring a JS bundle into a security product's repo needs its own supply-chain review (swap = one template view + locally vendored asset, sidecar-style).

---

## Post-debrief update (same day, architect review of the gaps)

The architect reviewed the five "gaps" and directed changes; all landed on the branch:

1. **Note side-effects — CLOSED in alpha** (`5686fac`). The notes factory gained an
`on_note_created` callback; `process_note_added` services fire the verified v2 side-effects
per resource (finding: JIRA comment + `last_reviewed` + @mentions; engagement/test:
@mentions only). 8 new tests.
2. **Divergence analysis + fix proposal — delivered** as committed `API_V3_DIVERGENCE_ANALYSIS.md`
(`e8a49ec`): 19 divergences verified in code, proposed canonical behavior per row for v3 AND v2
AND the UI, v2-consumer impact assessment (1 potentially breaking / 4 behavioral / 12 invisible),
sequencing across alpha/CONV1/CONV2. Its one confirmed **v3 regression (D17: delete-time JIRA
sync silently skipped)** was fixed in the same commit as the notes work, with a pinning test.
3. **Locations URL-only** — reclassified in the docs page as a *platform limitation*, not a v3 gap.
4. **Bulk/workflow actions** — recorded as an explicit architect-confirmed **post-alpha OS backlog**
(checkbox TODOs) in plan §6.
5. **Approximate counts** — reclassified in the docs page as a *design decision to be aware of*.

## Open items for the architect

1. **Draft PR to `dev`**.
2. **Scalar swap** — pending supply-chain review; not blocking.
3. **Convergence track** — CONV1 (v2 serializers → services), CONV2 (UI views → services; the OS3b divergence table + OS5 side-effect gap are the worklist), CONV3 (delete dead duplicates).
4. Minor deferred additions logged in §12: PUT (full replace), delete-time `push_to_jira` param, `configuration_permissions` on user writes, a v3 self-profile endpoint, filter vocabularies for the edge sub-resources.
5. **TODO (architect-confirmed): port the v2 endpoint-level test corpora to v3** — priority: the import/reimport scenario corpus (`test_import_reimport.py` mixin, `test_apiv2_scan_import_options.py`, `test_importers_closeold.py`) via a **dual-endpoint adapter** (parametrize the existing mixins with a client shim mapping v2 field names to v3's `asset_name`/`organization_name`/`mode=` form) rather than copying — a copy would fork the corpus and drift; then the JIRA push flows; then a scoped pass over the rest of `test_apiv2_*` (much covers surfaces v3 deliberately lacks in alpha). Recorded in plan §6 post-alpha backlog.
5. Operational notes: the two env-gated harnesses run via `docker compose exec -e ...` (`run-unittest.sh` has no env passthrough); `.claude/` was made world-writable so the containerized harness could write reports there.
Loading