Token revocation - #2
Open
svader0 wants to merge 245 commits into
Open
Conversation
- Add token_expiry DateTimeField to UserContactInfo (was missing from initial commit despite migration referencing it) - Register ApiTokenViewSet at api-tokens/ in urls.py (was missing from initial commit despite ViewSet existing in views.py) - Add unit tests for list, retrieve, revoke, expiry enforcement, and default-expiry-on-reset behaviours
…x/3.1.200-3.2.0-dev Release: Merge back 3.1.200 into bugfix from: master-into-bugfix/3.1.200-3.2.0-dev
…/workflows/validate_docs_build.yml) (DefectDojo#15526) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
…v44 (.github/workflows/renovate.yaml) (DefectDojo#15473) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
…fectdojo/chart.yaml) (DefectDojo#15532) * chore(deps): update valkey docker tag from 0.24.6 to v0.25.0 (helm/defectdojo/chart.yaml) * update Helm documentation --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Bumps [redis](https://github.com/redis/redis-py) from 8.0.1 to 8.1.0. - [Release notes](https://github.com/redis/redis-py/releases) - [Changelog](https://github.com/redis/redis-py/blob/master/CHANGES) - [Commits](redis/redis-py@v8.0.1...v8.1.0) --- updated-dependencies: - dependency-name: redis dependency-version: 8.1.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…o#15538) Bumps [django-polymorphic](https://github.com/django-commons/django-polymorphic) from 4.11.6 to 4.11.7. - [Release notes](https://github.com/django-commons/django-polymorphic/releases) - [Commits](django-commons/django-polymorphic@v4.11.6...v4.11.7) --- updated-dependencies: - dependency-name: django-polymorphic dependency-version: 4.11.7 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…1.6.0 (requirements-dev.txt) (DefectDojo#15544) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
…ile.nginx-alpine) (DefectDojo#15546) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
…lassified and versioned (DefectDojo#15556) Replaces the create-only lifecycle section. Editing was blocked because a mapping edit can change what a finding's identity is built from -- true of some edits and not others -- so the page now documents the distinction the API makes, the acknowledgement an identity-relevant edit requires, the impact endpoint for checking before committing, the transition window and its two limits, and the revision history. Rolling forward to a new parser stays documented, as the right answer when a report format changes enough to yield a different set of findings. Also flags the Django admin as the one path that bypasses versioning. Co-authored-by: DefectDojo Agent <greg-agent-4@defectdojo.com>
Bumps [markdown](https://github.com/Python-Markdown/markdown) from 3.10.2 to 3.10.3. - [Release notes](https://github.com/Python-Markdown/markdown/releases) - [Changelog](https://github.com/Python-Markdown/markdown/blob/master/docs/changelog.md) - [Commits](Python-Markdown/markdown@3.10.2...3.10.3) --- updated-dependencies: - dependency-name: markdown dependency-version: 3.10.3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [ruff](https://github.com/astral-sh/ruff) from 0.16.0 to 0.16.1. - [Release notes](https://github.com/astral-sh/ruff/releases) - [Changelog](https://github.com/astral-sh/ruff/blob/main/CHANGELOG.md) - [Commits](astral-sh/ruff@0.16.0...0.16.1) --- updated-dependencies: - dependency-name: ruff dependency-version: 0.16.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [pyopenssl](https://github.com/pyca/pyopenssl) from 26.3.0 to 26.4.0. - [Changelog](https://github.com/pyca/pyopenssl/blob/main/CHANGELOG.rst) - [Commits](pyca/pyopenssl@26.3.0...26.4.0) --- updated-dependencies: - dependency-name: pyopenssl dependency-version: 26.4.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…efectDojo#15542) Bumps [drf-spectacular-sidecar](https://github.com/tfranzel/drf-spectacular-sidecar) from 2026.7.1 to 2026.8.1. - [Commits](tfranzel/drf-spectacular-sidecar@2026.7.1...2026.8.1) --- updated-dependencies: - dependency-name: drf-spectacular-sidecar dependency-version: 2026.8.1 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…/workflows/validate_docs_build.yml) (DefectDojo#15527) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Bumps [django-crispy-forms](https://github.com/django-crispy-forms/django-crispy-forms) from 2.6 to 2.7. - [Release notes](https://github.com/django-crispy-forms/django-crispy-forms/releases) - [Changelog](https://github.com/django-crispy-forms/django-crispy-forms/blob/main/CHANGELOG.md) - [Commits](django-crispy-forms/django-crispy-forms@2.6...2.7) --- updated-dependencies: - dependency-name: django-crispy-forms dependency-version: '2.7' dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
… UI (DefectDojo#15567) * docs(notifications): note where notification settings live in the Pro UI The Pro UI now serves notification settings as three pages -- Personal, System and Template -- instead of the classic single page with a Scope drop-down, and the webhook page moved with them. Added as a Pro callout on the two configuration pages rather than a rewrite: the Scope drop-down is still exactly how it works in open source, so the existing instructions stay correct for that audience. Pairs with the DefectDojo Pro change on the same release line. * docs(notifications): use the site's Pro-note style, not an undefined shortcode The Pro callout used `{{% alert %}}`, a Docsy shortcode this site does not define, so `hugo` failed the docs build outright: failed to extract shortcode: template for shortcode "alert" not found Those two were the only `{{% alert %}}` uses in the whole content tree, which was the tell. Replaced with the highlighted-span convention the docs already use for Pro-only notes. Verified with a local `hugo` build rather than another CI round-trip.
…tDojo#15472) An engagement whose `status` or `engagement_type` column holds NULL (or an empty string) could not be saved at all. Both fields are declared `null=True` but neither was given `blank=True`, and Django counts `None` among a field's empty values, so `full_clean()` -- which Engagement runs on every save -- rejected the row with `{'status': ['This field cannot be blank.']}`: the model refusing a value its own column permits. Every import and reimport writes its engagement back at the end of the run (`save_without_resurrecting(self.test.engagement)`), so one such row turned every subsequent scan ingest into that engagement into a hard failure. Through the API the Django ValidationError is translated to HTTP 400, so callers got a rejected import rather than any findings. Neither column offers an empty choice and both declare a default, so an empty value carries no meaning the rest of the codebase can read -- filters, reports and the UI all assume one of the listed choices. `Engagement.pre_save_logic` now fills an empty value in from the field's own default, which keeps a value outside the choice list from becoming valid (as widening to `blank=True` would) and lets each affected row heal the next time anything saves it. No data migration: `pre_save_logic` runs before `full_clean`, so the very save that used to fail is the one that repairs the row, and affected engagements recover on their next (re)import rather than needing a backfill on deploy. Tests cover both fields on the model save path, the import and reimport write-backs, and that a populated value is never overwritten. Claude-Session: https://claude.ai/code/session_01HzJ7XLk2RsabJbYyJJgxvg Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Bumps [cryptography](https://github.com/pyca/cryptography) from 49.0.0 to 50.0.0. - [Changelog](https://github.com/pyca/cryptography/blob/main/CHANGELOG.rst) - [Commits](pyca/cryptography@49.0.0...50.0.0) --- updated-dependencies: - dependency-name: cryptography dependency-version: 50.0.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…lk (DefectDojo#15571) Documents the DISA STIG Checklist scan type: the two STIG Viewer formats it reads, how the four checklist statuses become finding states, how DISA categories and assessor severity overrides map to severity, what each finding carries, and how findings are identified. Two points get more than a passing mention because they change how someone organizes their imports. Findings are identified by V-number *on the assessed asset*, so the same rule failing on two hosts stays two findings and a STIG release upgrade keeps history. And closing on re-import is driven by an item's absence from the report, so a test holding several assets' checklists needs Close Old Findings turned off. On the compliance side, STIG rules cite CCIs rather than naming controls, so control coverage gains a section on the CCI crosswalk, the precedence between mapping sources, and the fact that the backfill command now runs both passes. The compliance profile's configuration-test-types note gains the STIG case, including why it is not switched on for you. Co-authored-by: devGregA <greg-agent-2@defectdojo.com>
* refactor(ui): remove the classic Bootstrap UI
The deprecation banner announced that the classic UI is retired and the
redesigned UI becomes the default in 3.3.0. This removes the classic tree
and the machinery that chose between the two.
Removed:
- dojo/templates_classic/ (the Bootstrap 3 / SB Admin 2 tree)
- dojo/template_loaders.py: UIPreferenceLoader picked a tree per request
- UserContactInfo.ui_use_tailwind and the opt-in banner it gated
- dojo/static/dojo/{css,js}/classic/ and the vendored Bootswatch build
- 16 npm dependencies that only the classic UI used
TEMPLATES is now a plain filesystem + app-dirs chain over dojo/templates,
wrapped in the cached loader outside debug mode. The per-tree caching that
UIPreferenceLoader did internally is preserved that way.
Three templates were shadowed rather than superseded: UIPreferenceLoader
searched both trees ahead of the filesystem loader, so the classic copy won
for every user and the consolidated app-dir copies underneath had gone
stale. Deleting the tree naively would have regressed them, so the live
versions are ported across:
- notifications/mail/scan_added.tpl and webhooks/scan_added.tpl kept the
duplicate-findings sections added in DefectDojo#15007
- dojo/action_history.html kept has_active_filters from DefectDojo#15082
Two exceptions keep the app-dir copy deliberately: alert/other.tpl, whose
|safe removal (344c913) the classic copy never received, so escaping in
alert notifications is restored; and the GitHub form templates, which the
Tailwind rebuild edited on purpose.
DataTables no longer loads the Bootstrap styling integrations; markup is
styled against DataTables' own dt-* classes, which datatables-dd.css was
already written for. Two visual regressions this surfaced are fixed here:
the control row is inlined again (the integration supplied form-inline),
and report_base.html states its heading scale and opts out of the app's
uppercase h6 label styling, both of which the vendored Bootstrap build
used to provide.
Verified against a running instance, before and after, plus 163 unit tests
covering report rendering, search, notifications, and audit log.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CEcRPkdyRP6wtEnqVkSB9a
* chore(deps): drop three unused npm packages and fix report icons
Follow-up audit of the Dockerfiles and package.json.
The Dockerfiles referenced none of the removed paths, so they needed no
change. collectstatic copies all of components/node_modules into the nginx
image, so an unused dependency is shipped, not just installed:
- alpinejs and htmx.org: the app loads committed vendored copies from
dojo/static/dojo/js/vendor/, and nothing references the npm paths
- font-awesome: only report_base.html loaded it, and it was the v4 build
Dropping font-awesome means reports use the v6 build that the rest of the
app already uses, which ships with the fontawesomefree pip package rather
than npm. That also explains a comment repeated across seven report
templates -- "for some reason the font-awesome icons don't work with the
report template". Two causes, both fixed here: the templates use v6 class
names (fa-solid) that the v4 build does not define, and report_base.html
sets font-family on `*` with !important, which overrode the icon font and
rendered every icon as a missing glyph.
Verified in the browser: icons resolve to Font Awesome 6 Free and render
at full width instead of a fallback box, with body font and heading scale
unchanged. Dependencies are down to 19, from 37 before this branch.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CEcRPkdyRP6wtEnqVkSB9a
* fix(ui): restore the handles the UI suite navigates by, and the jQuery dropdown API
With the classic tree gone the admin user renders the redesigned templates, and
the Selenium suite lost three things it had been navigating by. None of the
failures were assertion failures -- every one was a NoSuchElementException.
The sidebar repeats every product tab label. "Engagements", "Findings",
"Endpoints", "Components" and "Metrics" all appear in the sidebar, which renders
~60 anchors before the product tab bar, so By.LINK_TEXT / By.PARTIAL_LINK_TEXT
resolved to the sidebar entry. Those entries are bound to Alpine with
@click.prevent, so the click never navigated -- it expanded a submenu, left the
tab's dropdown closed, and the failure surfaced one step later on a menu item
that was in the DOM but not rendered, so link text could not match it. The tab
bar now carries data-testid handles and tests go through a new
BaseTestCase.open_product_tab() helper that documents the trap.
The classic tree carried ids the redesigned tree never picked up:
simple_search_submit on the search button, product_component_view and
id_user_menu and menu_configuration in the sidebar. They are restored on the
equivalent elements, under the same permission gates. The Configuration section
expands on click rather than hover, so the one test that hovered it now clicks.
$.fn.dropdown was missing. index.js replaces bootstrap.min.js by delegating
clicks, but never registered the jQuery plugin, so the inline onclick handlers in
view_test, findings_list_snippet, finding_related_actions and view_objects died
with "$(...).dropdown is not a function". It is now backed by the same open/close
logic, resolving the toggle, the .dropdown container and the .dropdown-menu alike.
This one was user-visible, not just a test problem.
The eight reported failures were only the first failure in each of the nine CI
groups: the runner uses failfast=True and the entrypoint stops a group at its
first failing file, so most of the suite never ran. Fixing only those eight would
have surfaced the next one a CI cycle later, so the same breakage is fixed at all
~30 affected call sites, and all 41 files in the UI matrix were run locally.
Also adds data-testid="report-link" to the engagement, test and organization
report menu items, which By.PARTIAL_LINK_TEXT "Report" could no longer reach past
the sidebar's "Reports" section.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rpv46XWjWXCWTQSkawKSHp
* test(ui): stop the footer eating submit clicks on the long forms
The engagement and add-test forms run long enough that their button row sits
next to the page footer, and the footer wins the click. Selenium sometimes says
so (ElementClickInterceptedException naming #footer-wrapper) and sometimes the
click simply lands on the footer: the form is never submitted, the page never
changes, and the test fails much later asserting a success banner that was never
going to appear. A page-source dump taken at one such failure shows the browser
still sitting on a pristine "New Interactive Engagement" form -- no success
alert, no error alert, no errorlist.
click_submit() centres the button in the viewport before clicking it, which
takes it out from under the footer. Measured on threat_model_test.py, the file
that flaked most readily: 4 failures in 12 runs before, 0 in 12 after.
is_success_message_present() now waits for the banner instead of sampling once.
It only renders after the redirect that follows a POST, so the assertion was
riding on the 1s implicit wait -- fine locally, not always enough on a loaded
runner. Every caller asserts the banner is present and nothing asserts its
absence, so the wait costs nothing on the happy path and only delays a genuine
failure.
Both are applied to the two forms where interception was actually observed
rather than to every submit in the suite.
Two things deliberately NOT done. Targeting the "Done" button instead of the
first input.btn.btn-primary looks like the obvious tightening -- new_eng.html
renders "Import Scan Results", "Add Tests" then "Done", so the bare selector
submits the first one while the comment says Done -- but "Done" is the
bottom-right button, nearest the footer, and switching to it made
threat_model_test and calendar_test fail outright. That ordering is also
byte-identical in the template the classic UI used, so it is long-standing and
not a regression. And the footer overlap itself is left alone: #footer-wrapper
is position:static in normal flow and does not overlap the buttons at rest, so
this is a layout shift during load, not a stylesheet bug with an obvious fix.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rpv46XWjWXCWTQSkawKSHp
* test(ui): route the settings-form saves through click_submit too
The previous commit hardened the engagement and add-test forms against the
footer swallowing a submit click, and CI then failed in the one place it had
not covered:
enable_false_positive_history -> change_system_setting
AssertionError: False is not true # assertTrue(is_enabled) after the save
Same mechanism, on the system settings form -- also long enough to put its save
button next to the footer. This one matters more than most: set_suite_settings()
calls change_system_setting() at the head of nearly every file in the suite, so
a click that lands on the footer there takes the whole file down before its
first real test. set_block_execution() and set_deduplication_execution_mode()
submit the same way on the profile form and get the same treatment.
The notification helpers submit that form too and are deliberately left alone.
notifications_test.py fails roughly one run in three locally with or without the
change -- on a different test each time -- so there is no signal to act on, and
it has passed every CI run so far. Patching it would have been guesswork.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rpv46XWjWXCWTQSkawKSHp
* test(ui): stop toggling false positive history while deduplication is on
The settings view refuses this combination outright:
"Settings cannot be saved: Deduplicate findings and False positive history
can not be set at the same time."
test_toggle_deduplication runs immediately before test_toggle_false_positive_history
and deliberately ends with deduplication ON, so the very next test asks for a
save the view will not perform, and change_system_setting()'s assertion fails.
This has been latent, not new: the save was already being refused, but the click
that submitted it was landing on the footer often enough that deduplication
never actually persisted, so the next test found it off and passed. Fixing the
swallowed click made the earlier test's effect stick, which is what surfaced
this. Confirmed against a running instance -- setting deduplication on and then
enabling false positive history through the UI produces exactly that warning and
no save.
So turn deduplication off around the false-positive-history toggle and restore
it afterwards, which keeps both contracts: the file still leaves deduplication
enabled for dedupe_test.py, and false positive history disabled as its default.
Verified with deduplication pre-set to on, the state CI reaches: all six files
in this group pass, and the settings land at deduplication on / false positive
history off.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rpv46XWjWXCWTQSkawKSHp
* test(ui): centre the checkbox clicks too, not just the submits
A lost click on a checkbox is worse than a lost click on a submit button, and
until now only the submits were protected. When the footer eats a submit, the
form is simply never sent. When it eats a checkbox, the submit still happens and
the form saves with the checkbox in its old state, so the setting silently does
not change and the failure lands somewhere that looks unrelated.
That is what CI was showing. Once the submits stopped being lost, the failures
moved onto the checkboxes further down the same long form:
enable_false_positive_history -> assertTrue(is_enabled) (earlier)
enable_retroactive_false_positive_history -> assertTrue(is_enabled)
The settings view re-renders the bound form after a POST, so the checkbox in the
response reflects what was submitted, not what was saved. change_system_setting()
asserting False can therefore only mean the checkbox click never registered --
the save itself would have re-rendered it checked either way.
click_submit() is now a thin wrapper over a general click_centered(), used for
the setting toggles in change_system_setting() and set_block_execution() as well.
notifications_test.py gets the same treatment, and this reverses an earlier call.
I had left it alone after a single run suggested the change made things worse,
but that run was against a database dirtied by dozens of earlier files. On a
clean database -- what CI actually gives each group -- the unpatched file fails
at test_enable_personal_notification with the mail setting simply not applied,
which is the lost-click signature exactly. Patched, it passes 22/22.
Verified on a fresh database: all of groups 04 and 05 pass, plus product_test,
dedupe_test, close_old_findings_test and threat_model_test as the heaviest users
of the changed helper -- 290 tests.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rpv46XWjWXCWTQSkawKSHp
* test(ui): route every form submit through click_submit()
Fixing these one at a time has cost a CI cycle per site. Each run surfaced the
next lost click a little further along -- the engagement submit, then the
settings save, then the false positive history toggle, then the retroactive
toggle, then the CI/CD engagement's Done button -- always the same cause, the
footer taking a click on a long form, only ever a different button.
So convert the whole class at once: all 89 form-submit clicks across 29 files.
The change is mechanical and semantically identical -- same selector, same
element, centred in the viewport before the click. The one time centring made
things worse was when it was paired with changing WHICH button was clicked
(targeting Done instead of the first primary submit, which moved the click
nearer the footer rather than away from it); that is not what this does.
The last CI run left group 02 failing on exactly this, in engagement_test.py,
where the submit is input[value='Done'] -- the bottom-right button and so the
most footer-exposed of the three.
Verified over the first 34 files of the UI matrix on a clean database, 0
failures, covering every group that has failed at any point (01, 02, 04, 05)
including engagement_test.py itself. The remaining files were still running when
this went out; they are all the same transformation.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rpv46XWjWXCWTQSkawKSHp
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
* docs(psirt): new PSIRT section — advisory feeds, terms acceptance, clearance states Documents the platform-native PSIRT advisory feeds shipping in Pro (beta, behind the PSIRT feature flag + license entitlement): the customer-self-fetch posture, the 20-source shipped catalog with its cleared / pending-clearance states, the recorded terms-acceptance transaction (append-only ledger, encrypted credentials), custom feeds, feed health, and the attribution rendered with advisory content. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs(psirt): Import SBOM and Feed Findings pages Documents the two surfaces that answer "am I vulnerable to this advisory?" — the global SBOM upload that gets inventory in without visiting each product, and the triage queue that gives each advisory its answer. The Feed Findings page spends its length on the part users will otherwise get wrong: the answer is three-valued, and "not affected" and "unknown" are different claims. Reporting a component with no recorded version as "not affected" would tell someone they are safe on the strength of missing data, so the docs say what each state means and what the reader can do about an unknown. Same reasoning for verified vs unverified matches, and for why a confirmed match is never retracted automatically. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs(psirt): Matching Rules and Cases/SLA pages Two surfaces that were shipping without documentation, and both need it more than the pages already here — each has a behaviour a user will otherwise read as a bug. **Matching Rules** explains why a rule can be refused. A rule graded weak cannot be enabled, and someone who hits that without knowing the rule exists will conclude the feature is broken rather than that their rule matches every advisory in the store. The page leads with the grading, states the two-sided requirement with a worked example, and covers group scope, opt-out precedence, templates, and why deleting a group switches its rules off. **Cases and SLA** documents the grouping suggestions and the clock. The parts that need saying out loud: * a breach never clears — work finished late stops as breached, not met; * the triage clock cannot be paused at all, and why; * a paused clock's deadline moves, so paused time is not charged to the team; * the tier is fixed when the clock starts and can only ever move up; * mitigation priority is not the SLA tier, and downgrading one does not move a deadline; * closing a case does not stop a clock, because the obligation belongs to the advisories inside it; * you do not need a case at all to get a finding into DefectDojo. Also reordered the section so the sidebar matches the order the entries appear in the app menu — they disagreed, which would have had a reader following two different sequences for the same feature — and added a "how it fits together" list to the section index, including that the workflow pieces are optional. * docs(psirt): advisory publishing and the PSIRT dashboard Two pages for the halves of PSIRT that were undocumented, plus the index entries that place them. `advisories.md` covers the direction nothing else in PSIRT does: publishing your own advisories rather than consuming other people's. The parts a reader needs before they use it, not after — that editing approved content costs you the approvals (and why that is not a setting), that a rejection needs a reason, that preflight reports un-built checks as pending rather than passing, that exclusions beat both derived and added recipients, and that `skipped_not_configured` and `not_implemented` are neither success nor failure. `dashboard.md` leads with the four-way exposure split and says plainly that "not affected" and "no signal" are not the same claim, because that is the misreading the whole design exists to prevent. Also why feed health and triage SLA sit side by side — an SLA that looks healthy while a feed has silently stopped polling is the worst combination, and only visible together — and why feed health declines to say "all clear". The index now reads as seven steps with an explicit note that steps 1-5 are about what others published and step 6 is the other direction. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * docs(psirt): components, SLA policies, settings, and the relevance surfaces Three new pages for surfaces that previously had no documentation, and which until now could only be configured through the Django admin: * Components — the SBOM inventory as PSIRT reads it, one row per (component, asset), and the per-pair judgement you add: a PCRSS rating, an authoritative CPE (the strongest correlation axis, which a purl-keyed SBOM row cannot carry), and tags. Says plainly that a versionless row can only ever be answered "unknown". * SLA Policies — tuning the tier ladder. Separates the clock definition (the obligation, decided in code) from the tiers (the calibration you own), and explains why editing a tier never moves a running clock and why deleting one that live clocks use is refused. * PSIRT Settings — case-worthiness weights and bands, the "new" item window, and the material-change policy, including why KEV-add is on by default and KEV-remove is not. Matching Rules gains the four capabilities from the relevance pipeline: building rules from an inventory, previewing a rule or an unsaved condition set, per-rule effectiveness with the dead vs insufficient-data distinction, and per-asset coverage with the inventory tempering that stops "covered" being earned by one precise rule beside a thousand unwatched components. Index updated and the chapter renumbered so the reading order matches the workflow. --------- Co-authored-by: Greg Anderson <greg@Gregs-MacBook-Pro.local> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
The branch was ~607 commits behind and its files had been reorganized out from under it (user API/UI/model code moved into dojo/user/*), so the feature is re-applied against the current structure rather than merged line by line. Relocations: - RevokeApiTokenView moves to dojo/user/api/views.py, with its input modelled by a real RevokeApiTokenSerializer instead of reading request.data by hand. - token_expiry moves to the UserContactInfo definition in dojo/user/models.py. - Migration renumbered 0269 -> 0290 behind 0289_remove_usercontactinfo_ui_use_tailwind. - The classic api_v2_key template was deleted upstream; that copy is dropped. Two defects in the original approach are fixed: 1. The instance-wide default was bypassable. It stamped an expiry onto the user during reset_token_for_user, but tokens are also minted by the lazy create on the UI key page and by DRF's api-token-auth endpoint. A token obtained either of those ways got no expiry at all, so the control could be sidestepped by choosing a different path. Expiry is now derived at authentication time from the token's own created timestamp, which covers every path including any added later. An explicit per-user token_expiry still overrides it. 2. token_expiry would have been user-editable. UserContactInfoForm is the self-service profile form and its Meta only excludes user/slack_user_id, so a new model field arrives on it as an editable input; a user could have cleared or extended their own expiry. It is now disabled there, alongside the existing timestamp fields, leaving superusers the only writers via user_contact_infos. Also: revoking clears the stale per-user override so the owner's next token is not dead on arrival, notifies the owner that their token was revoked and logs the actor, and the key page now shows the effective expiry instead of always claiming the token is permanent. Tests cover both regressions above, expiry precedence, the api-token-auth path, revoke authz (anonymous/non-superuser/superuser), and input validation. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
Conflicts have been resolved. A maintainer will review the pull request shortly. |
The serializer imported Token and token_expires_at inside get_token_expiry to sidestep a circular import that does not exist: dojo.user.authentication reaches only authorization, models and notifications.helper, none of which import back into dojo.user.api.serializer, and dojo/user/api/views.py already imports both modules at module scope. Resolves PLC0415 (import-outside-top-level), which was gating the whole unit-test suite. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…, drop a Docsy shortcode
The six failures were all in the new test file; the rest of the suite (6491 tests)
was unaffected.
Five of them failed in setup with 403 "API token has expired." rather than at
their assertion: dojo_testdata's admin token has an old created timestamp, so any
test switching on a default lifetime expired the very credential used to build
its fixtures. The admin token is now stamped fresh in setUp.
That collision is worth stating rather than papering over, so
test_enabling_the_default_expires_pre_existing_tokens now pins it directly: a
200-day-old token works, and the moment a 90-day default is switched on the same
token is rejected. Evaluating expiry at use is what makes the control impossible
to sidestep, and retroactivity is the price. The docs now carry that warning,
along with the recovery path (the UI uses session auth and can issue a new token).
The sixth was a real test defect: a disabled form field falls back to its initial
value through a round trip that drops microseconds, so comparing exact datetimes
failed on precision while the property under test held. It now asserts the posted
value was ignored, to the second.
Also replaces the {{% alert %}} block added with the earlier docs edit. That is a
Docsy shortcode; this site runs Thulite Doks, where {{< highlight >}} is the only
shortcode in use across docs/content, so it would not have rendered.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
We don't want to waste your time, so if you're unsure whether your hypothetical enhancement meets the criteria for approval, please file an issue to get pre-approval before beginning work on a PR.
Learn more here: https://github.com/DefectDojo/django-DefectDojo/blob/master/readme-docs/CONTRIBUTING.md#submission-pre-approval
Description
Describe the feature / bug fix implemented by this PR.
If this is a new parser, the parser guide may be worth (re)reading.
Test results
Ideally you extend the test suite in
tests/anddojo/unitteststo cover the changed in this PR.Alternatively, describe what you have and haven't tested.
Documentation
Please update any documentation when needed in the documentation folder)
Checklist
This checklist is for your information.
dev.dev.bugfixbranch.Extra information
Please clear everything below when submitting your pull request, it's here purely for your information.
Moderators: Labels currently accepted for PRs:
Contributors: Git Tips
Rebase on dev branch
If the dev branch has changed since you started working on it, please rebase your work after the current dev.
On your working branch
mybranch:In case of conflict:
When everything's fine on your local branch, force push to your
myOriginremote:To cancel everything:
Squashing commits
pickbyfixupon the commits you want squashed outpickbyrewordon the first commit if you want to change the commit messageForce push to your
myOriginremote: