Feature flags
/feature-flags/ (Superadmin-only, feature_flags.manage) is a single
instance-wide settings row. There’s exactly one FeatureFlags object per
instance, a singleton model fetched with get_solo() (a plain
get_or_create(pk=1) under the hood, so every fetch is a real read from
Postgres, not a cached value). It’s not a per-user or per-engagement
setting.
| Flag | Controls | Default |
|---|---|---|
retest_workflow |
Retest and remediation tracking on findings (Fixed, Not Fixed, Partially Fixed, Risk Accepted, plus history) | On |
notifications |
Reviewer and QA assignment notifications, both in-app and email. Per-user email opt-out (see Notifications & profile) still applies on top of this | On |
recurrence_and_trends |
Recurring-vulnerability detection on the finding page, and the cross-engagement Trends page | On |
cvss_calculator |
The CVSS v3.1/v4.0 calculator widget on the finding and catalogue create/edit forms | On |
global_search |
Global search across every engagement and finding the user has access to | On |
scan_import |
Scanner import (Nmap, Burp Suite, Nuclei) from the findings list | On |
mfa_required |
Requires TOTP MFA for every local account, not just Superadmin, which always requires it regardless of this flag | Off |
client_portal_enabled |
Allows client-role accounts to log in and use the Client Portal. Managing client companies and accounts stays available to Superadmin and Team Lead either way | Off |
What actually happens when a flag is off
Section titled “What actually happens when a flag is off”Every one of these is enforced in two places at once, not just hidden in the UI: the relevant nav link or button disappears from the template, and the underlying view independently checks the flag itself and refuses to serve the page even if someone requests the URL directly. Turning a flag off is a real access control, not just a cosmetic hide.
scan_importoff: the Import link disappears from the findings list, andPOSTing to/engagements/<id>/findings/import/directly raises aPermissionDeniedwith the message “Scan import is currently disabled,” which Django renders as a 403 page.global_searchoff: the search box disappears from the sidebar and dashboard, and/search/itself raises the same kind ofPermissionDenied(“Global search is currently disabled”), a 403 either way you’d try to reach it.retest_workflowoff:can_log_retest_result()returnsFalseunconditionally, so the retest logging UI never renders on a finding’s page, andlog_retest_result()independently raisesPermissionDenied(“Retest tracking is currently disabled”) if something tries to log one anyway. Existing retest history already on a finding is unaffected either way, this only blocks logging new results.recurrence_and_trendsoff: the recurring-vulnerability panel disappears from the finding page, the Trends link disappears from the sidebar and dashboard (gated together with thereports.trendspermission, both have to be true), and/trends/redirects away rather than rendering.notificationsoff: no in-app or email notifications are generated for review and QA assignment at all. This is a stronger switch than a user’s own per-account notification preferences, which only ever narrow what one person receives, never re-enable something this flag has turned off instance-wide.cvss_calculatoroff: the CVSS calculator’s supporting JavaScript is simply never included on the finding or catalogue create and edit pages. The CVSS score and vector fields themselves stay on the form either way, since they’re plain text fields underneath, you’d just be typing the vector by hand instead of using the calculator widget.mfa_requiredoff: only changes whether non-Superadmin roles are forced into MFA enrollment. It never disables MFA for accounts that already enrolled voluntarily or that belong to a role with its ownrequires_mfaset (see Roles & permissions), and it never touches the Superadmin role, which requires MFA unconditionally regardless of this flag.client_portal_enabledoff: enforced at two separate points. A client account with completely correct credentials is rejected at login with the same generic invalid-login message a wrong password would produce, not a distinct “portal disabled” message, so the flag being off isn’t distinguishable from a bad password by someone probing the login form. If the flag is turned off while a client is already logged in, the very next request they make is caught byClientPortalAccessMiddleware, which logs them out immediately and redirects to the login page with an explicit “The client portal is currently unavailable” message. Either way, managing client companies and accounts from the staff side is unaffected, this flag only gates the client-facing login and portal itself.
Changes take effect immediately
Section titled “Changes take effect immediately”Because get_solo() reads straight from Postgres on every call rather than
from any in-process or shared cache, and the request middleware fetches
request.feature_flags fresh at the start of every request, toggling any
of these takes effect on the very next request, instance wide, with no
restart, no cache to clear, and no per-user override to worry about.