Skip to content

Release: Merge release into master from: release/merge-dev-into-master-3.5.103 - #16876

Merged
defectdojo-release-tracker[bot] merged 62 commits into
masterfrom
release/merge-dev-into-master-3.5.103
Nov 13, 2026
Merged

defectdojo-release-tracker[bot] merged 62 commits into
masterfrom
release/merge-dev-into-master-3.5.103

Conversation

@github-actions

Copy link
Copy Markdown
Contributor

Release triggered by defectdojo-release-tracker[bot]

DefectDojo release bot and others added 30 commits November 12, 2026 05:01
…ev-3.5.102

Release: Merge back 3.5.102 into dev from: release/merge-master-into-dev-3.5.102
#16802)

* docs(rules-engine): say a rule close mitigates the finding's endpoints

Set Status (false_positive, mitigated), Copy Status from Matched Finding and the
classic Rules Engine's False Positive / Mitigated fields mitigate the closed
Finding's open Endpoints (or Locations), as closing it by hand does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HJqD5GHgqGFiHWpR4JYfWm

* docs(rules-engine): say a rule reopen opens the endpoints again

A rule reopening a Finding sets its Endpoints (or Locations) open again; the
close names the rule owner and the run's time. Folds the classic Rules Engine
bullet into one sentence and adds the sentence to the ticket sync node.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HJqD5GHgqGFiHWpR4JYfWm

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…nding records on upgrade (#16808)

* docs(risk-acceptance): say acceptances from before 3.3.0 get their finding records on upgrade

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GLL1WfyvvMFAmudbcCyaZK

* docs(risk-acceptance): an expiry does not sever an acceptance's membership

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…wn options (#16806)

Claude-Session: https://claude.ai/code/session_01HJqD5HCezZb93pC6XgZHcT

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…es (#16790)

* fix(product-attributes): refuse a comma in a new attribute option value

The findings filters over Asset attribute values (product_lifecycle on
/api/v2/findings/) take a comma-separated list, so an option whose value
contains a comma could never be matched.

Creating a Platform, Lifecycle or Origin option through /api/v2/ or the
classic UI form now refuses a value with a comma. Only a create is checked:
an update ignores the value, and an existing row keeps its own, so a legacy
row stays editable.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HJqD5F8fqQRckGxJ6sobkn

* fix(product-attributes): translate the comma refusal and check it on the model

- The message is gettext_lazy, so /api/v2/ and the classic form show it in the
  user's language.
- The create-only check moves from the classic form to the model's clean(), so
  every ModelForm, Django admin included, refuses a comma in a new value; an
  existing row keeps its own. The API serializers keep their own check.
- The docs say a derived value also collapses repeated spaces.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HJqD5HBSaAhHF6FQrzAbmo

* fix(findings): match a lifecycle value that contains a comma, in quotes

Replaces the comma refusal on Asset attribute option values with a filter
that can match them. An option's value may contain a comma, but
product_lifecycle on /api/v2/findings/ split its parameter on every comma,
so such an option could never be matched.

product_lifecycle now reads a value in double quotes as one value, CSV-style
("beta, ga",production); a list without quotes splits exactly as before. The
create-time refusal, its model check and their test are reverted, and the
attribute options docs say how to quote such a value.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HJqD5F8fqQRckGxJ6sobkn

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* feat(parsers): add Early Light JSON parser

Adds the "Early Light Scan" scan type for `earlylight scan <fs|image|repo|host>
--format json` reports from DefectDojo's emerging-vulnerability scanner.

- vulnerable findings are imported tagged "earlylight"; unknown findings are
  imported tagged "earlylight" and "needs-review" with the reason in the
  description; fixed, inactive and managed findings are skipped
- vuln_id_from_tool is "<detector>/<kind>"; vulnerability IDs are the
  finding's own CVEs only
- severity is the finding's own when set, else the kind's severity from the
  detector catalog, else High (vulnerable) or Medium (unknown)
- unique_id_from_tool is a stable hash of detector, kind, target, path and
  the certificate or image facts; deduplication uses unique_id_from_tool
- sample reports are real Early Light output, trimmed

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(parsers): prefer the kind severities newer Early Light reports carry

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Tag fields are case-insensitive, so tagulous finds each tag it adds with
get_or_create(name__iexact=...), which compiles to UPPER("name"::text) =
UPPER(%s). The unique index on name cannot serve that, so every tag added to
an object read the whole tag table. Tag rows pile up on an instance whose
pipelines create, tag and delete findings, because a tag row is not deleted
when its last finding is. On a 3,000,000-row finding tag table one lookup
took 1.6 s, and a finding PATCH that set 12 tags spent 19 s in SQL.

dojo.0308 adds a text_pattern_ops btree on UPPER(name) to each of the 14 tag
tables, so the lookup is an index scan (0.03 ms) and the istartswith tag
search can use it too. The migration builds CONCURRENTLY outside a
transaction (plainly inside one), drops an INVALID leftover of an
interrupted build first, and ANALYZEs each table. It changes no migration
state: the tag models are generated by tagulous and declare no indexes.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…to build fast (#16823)

0307 built each index on a large event table CONCURRENTLY under the
database's default maintenance_work_mem. On an instance with about 2.9M
findings that held an upgrade for about two hours, almost all of it three
builds on the Finding event table. At the 64 MB default each build's sort
spills to disk, and since Postgres gives a build one parallel worker per
32 MB of maintenance_work_mem, each build also ran with one worker.

Each concurrent build now runs with maintenance_work_mem = 512MB and
max_parallel_maintenance_workers = 4 in its own session, never lowering a
session that already has more, and the session's settings are restored
after the build. Indexes are built smallest table first, so a run that
fails on a large table leaves every small one built. Migration state is
unchanged.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…osed while it waited (#16811)

Claude-Session: https://claude.ai/code/session_01HJqD5Cv3VzGrZWHeaGqoU4

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…t previews count (#16809)

* docs(triage-engine): say which matching passes the import and reimport previews count

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GLL1Wc7eenQgvZJnGkx7Qe

* docs(triage-engine): Global Component is previewed when Locations is off

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GLL1Wc7eenQgvZJnGkx7Qe

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…rows need (#16815)

* docs(page-layouts): say a narrowed simple table keeps the height its rows need

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HJqD5Ez7PB1xE6VT3N39Ci

* docs(page-layouts): say the row rises with a narrowed simple table, and other widgets keep their height

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HJqD5Ez7PB1xE6VT3N39Ci

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
)

* docs(login-settings): describe the warning before a session ends

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018B4x1AbucwijBnH3k7TSpF

* docs(login-settings): say the session expiry warning is the Pro UI's

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018B4x1AbucwijBnH3k7TSpF

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…#16814)

The Insights filter bar narrows every dashboard to findings from chosen
test types or test type categories (SCA, DAST, Container, Secrets ...).


Claude-Session: https://claude.ai/code/session_018B4x1AmvtKPP7BqHYS8Bwu

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…the Pro UI (#16812)

* docs(notifications): say where to set notifications for one asset in the Pro UI

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GLL1WbzJ8Ut9h66RhUqM1d

* docs(notifications): list the Pro events an asset's notification settings offer

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GLL1WbzJ8Ut9h66RhUqM1d

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* docs(triage-engine): document the Merge Findings node

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018B4x19GRapP5jCLo2KZaTk

* docs(triage-engine): say Merge Findings groups the findings that reach it

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018B4x19GRapP5jCLo2KZaTk

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
#16759)

* docs(custom-fields): document computed custom fields and rule formulas

Computed custom fields: what a formula reads (built-in numbers, number
custom fields, finding counts and aggregates), computed fields reading
each other, when values recompute, and the Triage Engine event a moved
value raises. Triage Engine: the Findings Changed asset trigger, the
Write To target on the custom field nodes, and formulas over finding
counts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(custom-fields): scoped recompute, the compute confirmation and the sweep lock

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(custom-fields): empty results, recompute triggers and rule chains

Describe what the review changed: a record the formula has no result
for is emptied, a label-only edit recomputes nothing, the row offers
Insert Number too, and a recompute a rule set off stays in that rule's
chain.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(custom-fields): a computed field with no result is empty, and a new formula announces no Finding updates

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…close pass (#16798)

Reimport loaded every finding of the to-mitigate set as a full instance, with
its JIRA relations, before close_old_findings() looked at it. That set is
every finding of the test the report no longer carries, closed ones included,
and the chunked load appended every chunk to one list. On a test whose
findings are nearly all closed, a reimport that closed nothing held all of
them in memory at once, about 8 KiB each, and ran the worker out of memory on
tests with a few hundred thousand closed findings. Reported via a customer
support ticket.

_hydrate_findings_for_close_old() now loads only the findings close_old_findings()
can close (mitigated unset, or is_mitigated false: the same test it already
applied) and keeps the other ids in already_closed_to_mitigate. The one thing
close_old_findings() still did with an already-closed finding, pushing its
finding group to JIRA, now reads those groups by id. Which findings close,
reopen and match is unchanged.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…16800)

* fix(locations): new importer references follow the finding's status

An import created every new location finding reference as Active, even for a
finding the report already marks mitigated or inactive, and never reactivated
an Asset reference that was Mitigated when a new Active finding reference
joined it. The Locations list then counted closed findings as active on a
location whose Asset reference read Mitigated.

- New finding references take the status the UI add path already uses
  (status_from_finding, now a pure function in dojo.location.status).
- A new Asset reference is Mitigated when every new finding reference to its
  location is closed; a location recorded for the product alone stays Active.
- An existing Mitigated Asset reference becomes Active again when a new Active
  finding reference joins it (one UPDATE, only when that happens; the
  existing-reference query now also reads the status, so no query is added).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(locations): describe how references follow a finding's status

An import now gives a new Finding Reference its finding's status, and a
finding closed or reopened outside an import brings its references and
the Location's Asset Reference along. The Locations overview said every
new reference starts Active.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(locations): keep Active refs for inactive imported findings

The importer gave every new finding reference its finding's full status, so
an inactive or duplicate finding got a Mitigated reference. That changed the
import contract test_import_reimport pins (findings imported with
active=False keep Active references).

A new finding reference is now Mitigated only when the report marks the
finding mitigated (is_mitigated, excluding False Positive and Out Of Scope,
which the save also marks mitigated, and Risk Accepted). Every other finding
keeps the Active reference imports have always created. The Asset reference
rule follows: Mitigated only when every new finding reference to its location
is Mitigated. Location.status_from_finding is back to its dev form, since the
importer no longer uses it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…ound task (#16821)

* fix(celery): give the outer task its cache back after a nested foreground task

A task run in the foreground inside another one (dojo_dispatch_task with
force_sync=True) went through DojoAsyncTask.__call__, which installed a fresh
task cache and then set the thread's cache to None when it ended. The outer
task ran on with no task cache, so every memoized read after that point went
back to the database or the shared cache.

begin_task_cache() now returns the cache it replaced and end_task_cache()
restores it. A top-level task still starts with a fresh cache and leaves
None behind; a nested one still gets a cache of its own.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(celery): say what a nested task does to invalidation; make the nested-task test rerunnable

begin_task_cache and end_task_cache now say that an outer task keeps what it
memoized for the whole task, and that an invalidation inside a nested
foreground task clears only the nested task's cache.

The test registered its two tasks on the global Celery app inside the test
body. Celery hands back an already registered task for a name, so a second
run in one process called the first run's closures. The test now drops both
names before registering them and again when it ends.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
)

* fix(jira): let a Jira project suppress SLA notification comments

A Jira project's "Send SLA notifications as comment?" could only turn SLA
comments on. The daily SLA job comments when the Jira instance's global
setting or the project's setting is on, so a project that unchecked it
still got an overdue comment on every breached issue each day while the
instance setting was on.

Add JIRA_Project.suppress_sla_notification_comments (default False, also
as a database default). When it is set the SLA job posts no Jira comment
for that project; email, in-app and other SLA notifications still go out.
Otherwise the rule is unchanged: comment when the instance or the project
asks for it. product_jira_sla_notification keeps its type and meaning, and
migration 0309 only adds the column, so no row is rewritten and API
clients that never send the new field behave as before.

Both set at once is rejected by the model's clean() and the API
serializer. The classic form shows one select (Inherit from Jira instance
/ Always send / Never send) mapped onto the two booleans. The instance
setting's help text now says how a project overrides it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(jira): describe the three SLA comment choices and the API fields

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(jira): a classic form post with the old SLA comment checkbox still sends

The classic Jira project form replaced the "Send SLA notifications as
comment?" checkbox with a three-choice select. A post that still sends the
old product_jira_sla_notification=on and no choice (a script or a saved
form) was saved as Inherit and the "on" was dropped. It now maps to Always
send, and counts as a change. A post with neither keeps the stored setting.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…eld pickers (#16813)

* docs(ui): describe the grouped CSV export, rules engine and widget field pickers

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018B4x19EcwUC1yHLZXRZrvr

* docs(ui): the rule insert menu, Set Field actions, integrator and connector pickers are grouped too

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018B4x19EcwUC1yHLZXRZrvr

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…connectors, ticket templates and the group API token (#16786)

* docs(groups): group-owned receivers, notification webhooks, connectors, ticket templates and the group API token

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(groups): a group token acts as the group; flag ends its OAuth access tokens

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(groups): receiver moves keep rules listening, group test messages, token issue rule

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(groups): receiver URL tokens for editors only, group webhook private addresses, Service Users for group tokens

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
#16816)

* docs(findings): document the SLA Status filter and the due-soon window

The SLA filter takes one condition, so Less Than or Equal To 7 also lists
every overdue Finding. SLA Status (Breached / Within SLA) combines with it
to list only Findings due within the week.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018B4x1B6U6wWEo2P2ZYHuB8

* docs(findings): make Status Active step 1 of the due-soon recipe and widen Breached

Without Status Active the recipe also lists Mitigated Findings closed up to
7 days before their SLA date, so it is now the first step. Breached covers
every Finding that is not Mitigated (Risk Accepted, False Positive and Out of
Scope included), not only open ones.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018B4x1B6U6wWEo2P2ZYHuB8

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…16831)

The Test page's Finding Groups table now fits its tile: a fixed set of
columns (severity, name, findings, SLA, status, tickets, creator) that
drops by priority as the tile narrows, and a header tag counting the
groups past their SLA.


Claude-Session: https://claude.ai/code/session_018B4x19wWovdgzjtXK877AR

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…by product (#16828)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
The Pro notes feed moves the raw-source toggle off the note text and into
each note's row menu (View Source / View Formatted).


Claude-Session: https://claude.ai/code/session_018B4x18J3XfNxMMqYYkqvkF

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
blakeaowens and others added 24 commits November 13, 2026 04:41
* docs(pro): describe the Findings table's tile on the Test page

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018B4x1Ad36PqVVT34YSs8W8

* docs(pro): say where the tile tables keep the saved page size

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018B4x1Ad36PqVVT34YSs8W8

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…page (#16849)

* docs(pro): describe the Request / Response Pairs rows on the Finding page

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0143hYTCggquV2UhQbY9gSg1

* docs(pro): name the body size and the 100-pair cap of the Request / Response Pairs tab

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0143hYTCggquV2UhQbY9gSg1

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* docs(prioritization): describe custom field priority factors

* docs(prioritization): say which changes recalculate an Organization for a numeric factor

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…s in the Pro UI (#16844)

* docs(questionnaires): link, answer and share engagement questionnaires in the Pro UI

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018B4x1AtjA2T1JZEbHRaTBZ

* docs(questionnaires): say Link to a Different Engagement also needs edit access to both engagements

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018B4x1AtjA2T1JZEbHRaTBZ

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…e a still-reported suppressed finding (#16848)

* fix(reimport): keep false-positive history to its host, and never close a still-reported suppressed finding

False-positive history matched on the hash alone, so for scanners whose hash
leaves the host out, a decision on one host reached every host that reported
the same issue, on import and retroactively. It now also requires a shared
location for hash_code and unique_id_from_tool_or_hash_code, compared on
DEDUPE_ALGO_ENDPOINT_FIELDS as deduplication does, with each finding's
locations loaded once per batch.

Reimport closed a matched false-positive or out-of-scope finding whose
incoming status differed: it was left out of the findings the report still
contains, and close_old_findings mitigated it. Every matched special-status
finding is now unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0143hYTGJPWSyyHzFb8RQv54

* perf(dedupe): compare locations only for pairs that can change false-positive history

Locations were loaded and compared for every same-hash candidate, even when
nothing was a false positive, which made an import with no false positives
several times slower. Only a false-positive candidate, or with retroactive
history an active candidate of a finding that is or becomes a false positive,
can change the outcome; the others are dropped before any location is loaded.
Also fix the docs' match table and the Open Source matching note.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0143hYTGJPWSyyHzFb8RQv54

* test(migrations): release the 0305 in-flight build from inside the wait, not on a timer

test_build_still_running_is_waited_for_and_kept freed the concurrent index
build on a one-second timer started before the operation ran. On a slow
runner the build finished before the operation's first check, so nothing was
waited for and no warning was logged. The snapshot is now released from the
wait loop's first check, after the builder's transaction has ended, so the
test no longer depends on how fast the runner is.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0143hYTGJPWSyyHzFb8RQv54

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…16851)

The Pro View Finding gear menu now offers Add Request/Response Pair, since the
Request/Response Pairs tab stays hidden until a finding has one.


Claude-Session: https://claude.ai/code/session_018B4x1APEsneSo3iBNsDE5y

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* docs(pro): document whole-asset standing risk acceptances

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018B4x1BWN2LmqRTViYFPQL7

* docs(pro): say whole-asset standing acceptances cover new findings only

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0143hYTEaMtiXEXEwDEfTUW2

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…gets (#16854)

* docs(pro): manage Organization members and groups from the page's widgets

The Organization page's Members and Groups widgets add a user or group, change a role
and remove access from their row menu, with the same permission checks as the
Permissions window.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0143hYTE55mFtCHpbAcBTAyb

* docs(pro): bold the Role field like the paragraph's other labels

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0143hYTE55mFtCHpbAcBTAyb

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…#16843)

* fix(importers): run imports and reimports into one test one at a time

A client that times out on a long synchronous reimport and retries it
started a second reimport into the same test while the first was still
running. The two matched and wrote the same finding rows and deadlocked
(SQLSTATE 40P01) or had statements cancelled by statement_timeout (57014).

An import now takes an exclusive PostgreSQL advisory lock on its test
before it matches or writes the first finding, and holds it until the
import ends. A second import into that test waits for the first to finish
and then runs as before. Only a wait longer than
DD_SERIALIZE_TEST_IMPORTS_WAIT_SECONDS (default 600) fails, with
409 Conflict and a message naming the test; 0 fails at once.
DD_SERIALIZE_TEST_IMPORTS=False turns it off behind a pooler in
transaction mode, and a lock whose every holder has been idle for
DD_DUPE_DELETE_ABANDONED_IMPORT_LOCK_MINUTES holds nothing back.

The lock uses its own key (0x44440002), separate from the shared per-test
lock the excess-duplicate delete reads (0x44440001). That lock is shared
by design: an import's deduplication batches take it while the import
waits for them in async_wait mode, so the import cannot hold the same key
exclusively. The delete is unchanged.

The wait polls pg_try_advisory_lock rather than blocking in PostgreSQL,
so a statement timeout cannot cancel it and the transaction stays usable.
Each import issues two more queries (take and release), which the
importer performance counts now include.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(importers): let a caller try for a test's import turn without waiting

A background import that runs in a Celery worker must not hold the worker
while another import into its test finishes: a burst of them would take the
pool, including the deduplication tasks the running import waits on.

lock_test_under_import and wait_for_test_import_turn now take wait_seconds,
which overrides DD_SERIALIZE_TEST_IMPORTS_WAIT_SECONDS for that call; 0 only
tries, and raises TestImportInProgress at once when the test is taken, so
the caller can queue the import again instead. The turn is asked for once
per import_lock_scope: a caller that took it up front is not made to wait
again by process_scan, and neither is one that was let through because the
lock is off or was left behind.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(importers): never leave a test's import turn taken but unrecorded

Review follow-ups for the one-import-at-a-time lock.

- A time limit (SoftTimeLimitExceeded) or any other exception landing after
  pg_try_advisory_lock had taken the test's turn, but before the importer
  had recorded it for release, left a lock nothing released: on a
  persistent worker connection it held the test until the connection
  closed. wait_for_test_import_turn now records the turn through on_taken
  in the same guarded step, and gives the lock back if anything interrupts
  the take before it is recorded. lock_test_under_import records through
  it.
- The tag-inheritance perf baselines pin exact query counts for a ZAP
  import and two reimports. Each now takes and releases its test's turn,
  two queries per path, in both Locations modes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: DefectDojo release bot <dojo-release-bot@users.noreply.github.com>
…its links go (#16863)

* docs(psirt): describe the redesigned PSIRT Exposure widget and its links

The share bar and legend, definitions on hover and focus, where each count,
advisory and row-menu item leads, and that the four PSIRT widgets are offered
only with the PSIRT Advisory Engine license and PSIRT read access.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018B4x19xVrWYZ93rkDVHqJq

* docs(psirt): wrap the widgets paragraph and say when Affected Components is offered

Replaces the em dash left in "Adding the widgets individually", rewraps that
paragraph at the file's width, and states that the row menu offers Affected
Components only where the row names components, listing the version-verified ones.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018B4x19xVrWYZ93rkDVHqJq

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…idgets (#16865)

* docs(dashboards): describe chart order, Sankey and Sunburst colors, the Treemap tint and the KPI / Trend change color

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018B4x1CBpazy1TGCrg2LNX4

* docs(dashboards): say the Top-N Leaderboard ranks by count, and a Sunburst child slice is a muted shade

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018B4x1CBpazy1TGCrg2LNX4

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…s_for_all_findings (#16869)

* perf(reimport): batch the finding group JIRA lookups in process_groups_for_all_findings

With JIRA keep-in-sync on, process_groups_for_all_findings read each
group of the reimport and its JIRA issue one query at a time, then read
the JIRA issue of every reactivated or unchanged group one query at a
time. A reimport touching 200 groups ran 784 statements instead of 241.

The first finding of every group name is now loaded once, with its JIRA
issue, its group and the group's JIRA issue. The reactivated/unchanged
groups are read with their JIRA issue.

Their ids now come from two id-list reads per chunk (open findings, then
memberships) instead of a DISTINCT subquery under the group read. With
planner statistics that saw the group tables as empty, that subquery ran
once per group and probed dojo_finding with the whole id list for every
membership: 94 s for 200 groups on a 10M-finding database.

Which objects are pushed, and in which order, is unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(reimport): pin the group_by JIRA keep-in-sync reimport at request level

TestDojoReimportGroupJiraPerformance reimports through
DefaultReImporter.process_scan with group_by and a keep-in-sync JIRA
instance whose groups have issues, into 2 and into 10 groups with the same
findings, and asserts the statement count does not grow with the groups
(dev: 122 statements with 2 groups, 146 with 10) and that every group is
pushed once per pass.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(reimport): clear the recorded pushes with list.clear()

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* docs(pro): dashboard tile title bar and Section Break behavior

The Dashboards page now draws no refresh button or "updated" time on the
widgets that read nothing that changes (Markdown, Section Break, Quick
Actions, Favorites), lays a small tile's refresh button and menu over the
end of its title until the tile is pointed at or tabbed to, and shows a
Section Break's header once. A newly added list widget is sized to show
every row it is set to show.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0143hYTGfYzneCXvjR6Ni7W1

* docs(pro): say the updated stamp shows on a wide enough tile

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018B4x18QM3cTf69C2wfdf6o

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…nsaction commits (#16822)

* fix(notifications): send "added" notifications after the creating transaction commits

product_post_save, product_type_post_save and engagement_post_save dispatched
async_create_notification straight from post_save. Inside a transaction a
worker could run the task before the COMMIT, find no row by id and return
without notifying anyone, while the task still reported success.

dojo_dispatch_task_on_commit builds the signature at save time (acting user,
pghistory context, foreground/background choice) and sends it from
transaction.on_commit. In autocommit it dispatches at once, as before; a
rolled-back create sends nothing. The three receivers use it.

The new tests commit for real and check, from a second connection, that the
row is visible at the moment the task is handed to the broker.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(notifications): log a failed on-commit dispatch instead of raising

dojo_dispatch_task_on_commit registered a plain on_commit hook. A foreground
task that raised (a user with block_execution and a failing mail or webhook
send) or a broker error then surfaced after the COMMIT: the row was saved but
the caller got an error, and every on_commit hook registered after it in the
same transaction was skipped.

Register the hook with robust=True: Django logs the failure and runs the
remaining hooks. The new tests force the foreground task to raise, inside a
transaction and in autocommit, and check the row is committed, the caller sees
no error, the failure is logged and a later hook still runs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
… check and the import (#16873)

* fix(import): resolve the import target the same way in the permission check and the import

The scan import permission checks read the raw request, while the import
itself runs on the serializer's validated data. Two differences let them
pick different targets: the serializer trims whitespace from the name
fields and the raw read did not, and the import permission check read a
response-only engagement field that the serializer drops. Trim the names
when reading the raw request, and ignore that field in the check, so the
check authorizes the same product and engagement the import writes to.

* fix(import): ignore response-only ids in the reimport and meta import checks

ReImportScanSerializer drops test_id and EndpointMetaImporterSerializer
drops product_id, because both are response-only fields. The permission
checks still read them from the raw request. Ignore them in the checks
too, so each check resolves its target the same way as its serializer.

Adds reimport tests, including one for a name with surrounding spaces,
and a meta import test.
…ar Findings page (#16853)

* docs(dedupe): describe the Duplicates tab's cluster view on the Similar Findings page

The View Finding page's duplicate card now holds a Duplicates tab and a Linked
Findings tab. The Duplicates tab shows the duplicate cluster as a tree, the
similar findings that are not in it with the cluster each one already belongs
to, a Why Is This a Duplicate toggle, and Open in Findings for search, filters,
export and bulk actions. The page and its two screenshots describe that view.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018B4x1BUZdWVtPdaWMZaGrK

* docs(dedupe): match the similar findings rule and the first-100 note to the Duplicates tab

Vulnerability IDs match when a Finding carries at least one of them, not all.
Each part of the Duplicates tab shows up to 100 Findings and says how many it
holds past that; Open in Findings covers the cluster only. An original whose
duplicates cannot be viewed reads Original of another cluster.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0143hYTEqcB7uGZJ5U42zWwJ

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions github-actions Bot added the release-management Automated release-train PR label Nov 13, 2026
@defectdojo-release-tracker
defectdojo-release-tracker Bot merged commit f6cee25 into master Nov 13, 2026
@defectdojo-release-tracker
defectdojo-release-tracker Bot deleted the release/merge-dev-into-master-3.5.103 branch November 13, 2026 21:13
@dryrunsecurity

Copy link
Copy Markdown

DryRun Security

This pull request modifies three files within configured sensitive codepaths, including importers, authorization logic, and templates. All changes were made by 'svader0', who is not on the allowed authors list for these specific paths.

Configured Sensitive Codepath Modified by Non-Allowed Author in dojo/importers/auto_create_context.py (drs_32ee6a13)
Vulnerability Configured Sensitive Codepath Modified by Non-Allowed Author
Description File 'dojo/importers/auto_create_context.py' matches configured sensitive codepath pattern 'dojo/importers/*.py' and was modified by 'svader0' (commit 4844a0b) who is not in the allowed authors list.
Configured Sensitive Codepath Modified by Non-Allowed Author in dojo/authorization/api_permissions.py (drs_4f7fb3c5)
Vulnerability Configured Sensitive Codepath Modified by Non-Allowed Author
Description File 'dojo/authorization/api_permissions.py' matches configured sensitive codepath pattern 'dojo/authorization/*.py' and was modified by 'svader0' (commit 4844a0b) who is not in the allowed authors list.
Configured Sensitive Codepath Modified by Non-Allowed Author in dojo/templates/dojo/calendar.html (drs_95b59323)
Vulnerability Configured Sensitive Codepath Modified by Non-Allowed Author
Description File 'dojo/templates/dojo/calendar.html' matches configured sensitive codepath pattern 'dojo/templates/**/*.html' and was modified by 'svader0' (commit 86453d0) who is not in the allowed authors list.

Comment to provide feedback on these findings.

Report false positive: @dryrunsecurity fp [FINDING ID] [FEEDBACK]
Report low-impact: @dryrunsecurity nit [FINDING ID] [FEEDBACK]

Example: @dryrunsecurity fp drs_90eda195 This code is not user-facing

All finding details can be found in the DryRun Security Dashboard.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

release-management Automated release-train PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants