fix(ui): correct #toggleBox positioning on login page in Chrome - #15769
Closed
koushik-hs wants to merge 126 commits into
Closed
fix(ui): correct #toggleBox positioning on login page in Chrome#15769koushik-hs wants to merge 126 commits into
koushik-hs wants to merge 126 commits into
Conversation
….2.0-3.3.0-dev Release: Merge back 3.2.0 into dev from: master-into-dev/3.2.0-3.3.0-dev
DefectDojo#15482) * feat(parsers): add Socket and Lacework parsers Both mirror the mapping the corresponding DefectDojo Pro connector already implements, so that a file import and an API sync produce findings that deduplicate against each other rather than two copies of everything. The scan type strings are byte-identical to what each connector reports, and the deduplication settings are copied from the connector's own configuration. Socket: one finding per alert, identity is the Socket alert key. Note Socket grades alerts low/middle/high/critical - "middle", not "medium"; guessing "medium" would silently drop every middle-severity alert to Info. Lacework: two shapes with different mappings. Container/image rows are static findings keyed by image; host rows are dynamic findings keyed by hostname, falling back to the machine id. Container rows report fix_available as an integer and host rows as a string, so testing one shape's type against the other would drop every host mitigation. A row Lacework reports as fixed or resolved is imported but not active. Fixtures are constructed from each vendor's documented schema with generic package scopes, registries and hostnames. * feat(parsers): add CrowdStrike Falcon Spotlight parser Mirrors pkg/tools/crowdstrike/connector/vuln_converter.go field for field, and uses the connector's ScanTypeSpotlight string verbatim, so a file import and an API sync deduplicate against each other instead of producing two copies of every finding. Dedup settings are copied from the connector's own config - including the fact that it lists unique_id_from_tool among the hash fields as well as pairing with unique_id_from_tool_or_hash_code. Deliberately does NOT claim the connector's separate "CrowdStrike:Detections - Connectors Import" scan type, which is a different shape; a test asserts that. Points worth noting, all mirrored rather than corrected: - The connector marks these findings NEITHER static NOR dynamic. Spotlight reads the Falcon agent's software inventory: it neither analyses source nor probes a running service. - Severity comes from the CVE, not the vulnerability. - CrowdStrike returns no discrete version field, so component_version is what remains of product_name_version once the normalized product name is stripped. - firstCWE takes the first entry parseable as CWE-<number>; a list with none parseable leaves cwe at 0, which is the IntegerField default rather than None. The host is recorded via the nmap locations idiom (LocationData under V3_FEATURE_LOCATIONS, Endpoint otherwise) rather than the connector's protocol-relative "//<host>" string, which exists only to survive DefectDojo's URI parsing. Tested through get_unsaved_locations so it passes in both modes. Fixtures are constructed from the documented combined-vulnerabilities schema with generic hostnames, private-range addresses and placeholder CVE ids. * feat(parsers): add FOSSA parser Mirrors pkg/tools/fossa/converter field for field, uses the connector's ScanType string verbatim, and copies its dedup settings, so a file import and an API sync deduplicate against each other. Covers both of FOSSA's issue categories - security vulnerabilities and licensing or quality issues - because the connector does. Three points that needed care: - An issue counts as a vulnerability when its type says so OR when it carries vulnerability-only fields (cve, vulnId, cvssVector). That belt-and-braces check is the converter's: a missing or renamed type must not silently downgrade a CVE to a licensing finding, which would also change its grading. - Severity for a vulnerability is FOSSA's own, but FOSSA reports "unknown" often enough that the CVSS v3 band fallback matters. Licensing and quality issues carry no severity at all, so the connector's type table is mirrored - including BOTH spellings of the risk_* types, since FOSSA's docs hyphenate (risk_empty-package) while fossa-cli's wire format uses underscores. - unique_id_from_tool is "<issue id>:<project locator>". One FOSSA issue can affect several projects and becomes one finding per DefectDojo product; those must not share a tool id. The parser reproduces this from the issue's own projects list, and falls back to the issue id alone when an export carries no project context - documented, since that case cannot reproduce the suffix. FOSSA is SCA, so a finding has no file or line; the dependency coordinates are its only location and are written into the description. References are accepted as either a bare string or a {url, title} object, which is what the connector's Reference.Link() handles. * feat(parsers): add Endor Labs parser Mirrors pkg/tools/endorlabs/connector/converter.go field for field, uses the connector's ScanType string verbatim, and copies its dedup settings, so a file import and an API sync deduplicate against each other. Endor Labs is reachability-aware SCA, and its reachability verdict is the one thing that distinguishes it from any other SCA tool. The connector promotes that verdict to the finding's IMPACT rather than leaving it in tags, and this does the same. The precedence is the connector's: a function-level verdict outranks a dependency-level one, and a definite verdict outranks a "potentially". Endor advisory text arrives as HTML from upstream advisories, and the connector never renders it. Its InertText is reproduced here: script and style content is dropped, block tags become newlines, runs of blank lines collapse, and the result is escaped. Go's html.EscapeString is matched byte for byte rather than using Python's html.escape, which spells the apostrophe entity differently. Other points mirrored rather than corrected: - CVSS prefers the v3 score but falls back to the v4 base score; without that a v4-only advisory would import with no score at all. - Vulnerability IDs are the primary identifier followed by Endor's aliases, deduplicated, so a GHSA and its CVE both land on the finding. - Component name falls back from the resolved dependency name to the package name. - Tags drop any value ending _UNSPECIFIED: those only record that Endor did not determine something. - Identity is the finding UUID, falling back to a vulnerability/component composite when Endor sends none. - Endor also reports secrets and other non-CVE findings with no vulnerability block at all; those import without CVE, score or reachability. Fixtures are constructed from Endor's documented findings schema with generic package names and a generic tenant namespace. * feat(parsers): add GitGuardian parser Mirrors pkg/tools/gitguardian/connector/converter.go field for field, uses the connector's ScanTypeName verbatim, and copies its dedup settings, so a file import and an API sync deduplicate against each other. Note GitGuardian pairs the PLAIN hash_code algorithm with a unique-id-only hash field set, unlike the other connector scan types added here - incident ids are stable, so nothing else is needed. One finding per incident, not per occurrence: an incident is one distinct exposed credential however many times it appears, and the occurrence count goes in the description. No secret value is imported. GitGuardian's incidents endpoint does not return the matched secret, and a test asserts that, so a future change that starts pulling occurrences cannot quietly begin copying credentials into the database. GitGuardian actively checks whether a credential still authenticates, which is the most actionable thing it reports. The connector spells that verdict out rather than leaving a bare enum, and marks the finding verified ONLY for "valid" - an unchecked credential is not evidence either way, and marking it verified would overstate what GitGuardian knows. The three unverified states (no_checker, not_checked, failed_to_check) share one narrative. Fixtures are constructed from the documented secret-incident schema with generic incident names and a generic dashboard host; none contains a credential-shaped value. * feat(parsers): add Codacy parser Mirrors pkg/tools/codacy/converter field for field, uses the connector's ScanType string verbatim, and copies its dedup settings, so a file import and an API sync deduplicate against each other. Codacy surfaces several underlying scanners through one security-items endpoint, so the parser flags a finding static or dynamic from the item's own scanType - only DAST looked at something running. Flagging all of them one way would misreport most. Points that needed care, all mirrored from the converter: - The vulnerable package is the LAST entry of the first non-empty dependency chain. The first entry is the project itself, so taking it would name the application as the vulnerable component on every SCA finding. A leading empty chain is skipped rather than treated as "no package". - Codacy's "cve" is a typed string documented as possibly holding several identifiers, so it is scanned for all of them and deduplicated; vuln_id_from_tool takes the first and falls back to Codacy's itemSourceId. - An item Codacy ignored as a FALSE POSITIVE imports with false_p set, so triaged noise does not go back in front of the team. Any other ignore reason ("acceptable risk") is a real finding somebody accepted and is NOT flagged. The reason comparison strips spaces so "False Positive" matches. - The converter dates a finding today when openedAt will not parse, so a finding always has a date. Mirrored; the test asserts a range so it cannot flake on a date rollover. The scanned application (or, for a container item, its image) is recorded via the locations idiom so it survives under either value of V3_FEATURE_LOCATIONS. * feat(parsers): add DeepSource parser Mirrors pkg/tools/deepsource/converter field for field, uses the connector's ScanType string verbatim, and copies its dedup settings, so a file import and an API sync deduplicate against each other. DeepSource reports two different things and the connector converts them differently, so both shapes are handled: static analysis issue occurrences, and dependency vulnerabilities from advisories. A bare array is classified per entry rather than per file, so a mixed export is not mis-mapped. The severity work is the interesting part. DeepSource grades EVERY issue CRITICAL/MAJOR/MINOR whatever the issue actually is - a missing docstring can be MAJOR - so the category decides which ladder applies. A security issue keeps its grade; a bug-risk, performance, typecheck or anti-pattern issue drops a step, because it describes a defect rather than a weakness. Applying one ladder to both would either inflate every lint finding or bury the real ones. A hit from the secrets analyzer is Critical regardless, since a committed credential is a committed credential. Advisories use a separate ladder: the CVSS v3 band when scored (bottoming out at Low, never Info), otherwise the severity word, accepting GitHub's MODERATE spelling of medium. Identifiers are the advisory id plus aliases, upper-cased and deduplicated. An advisory with no published fix says so explicitly rather than leaving the mitigation blank - "no fix published" is useful triage information. Note the connector grades STYLE, DOCUMENTATION and COVERAGE issues as Info and imports them. That is mirrored here rather than filtered, because parity with the connector is what stops findings duplicating; it is raised in the PR as a follow-up worth discussing against the connector instead. One bug caught by the clean-run fixture: presence of the occurrences or vulnerabilities KEY identifies the shape, not whether it has entries - a clean run legitimately reports both lists empty. * feat(parsers): add Probely parser Mirrors pkg/tools/probely/connector/finding_converter.go field for field and uses the connector's ScanType verbatim. That string is "Probely API Import" - it does NOT follow the "<Vendor> - Connectors Import" pattern the other connector scan types use, so it cannot be derived from the vendor name and a test asserts it. Probely reports severity as an INTEGER with only three values (10/20/30 -> Low/Medium/High, no Critical). Treating the number as a score or an index would misgrade every finding. Findings Probely records as fixed, invalid or accepted are skipped, matching the converter's IsIgnored. "retesting" is deliberately NOT skipped: a re-test means somebody is actively working the issue, so it is assumed still open. Skipping it would drop live findings whenever a re-test was queued. This scan type's dedup config pairs the plain hash_code algorithm with a wide field set that includes ENDPOINTS, so the parser always records the scanned origin - reduced to scheme, host and port as the converter does. An unpopulated endpoint would leave the hash computed over nothing and every rescan would reimport. Recorded via the locations idiom so it works in both V3_FEATURE_LOCATIONS modes, and a test pairs the assertion with the dedup field list so the two cannot drift apart. One parity bug caught by the field-mapping test: Probely names the definition's prose field "desc", not "description", so reading the latter silently produced an empty description. The insertion point is rendered as a readable label with the converter's acronym fixes (URL/JSON/GraphQL); plain title casing gives "Url" and "Json". * feat(parsers): add Detectify parser Mirrors pkg/tools/detectify/connector/finding_converter.go field for field and uses the connector's ScanType verbatim. Like Probely, that string is "Detectify Scan" - it does NOT follow the "<Vendor> - Connectors Import" pattern, so it cannot be derived; a test asserts it. Findings Detectify records as patched or false_positive are skipped, but an ACCEPTED RISK is deliberately kept and flagged risk_accepted rather than discarded - dropping it would lose the record that somebody accepted it. Detectify reports separate CVSS 2.0, 3.0 and 3.1 blocks. The converter prefers 3.1, falls back to 3.0, and ignores 2.0 because cvssv3 is a v3 field. A block counts as present when it carries a score OR a vector, so a vector-only entry is not discarded; the fixture gives all three blocks different values to prove the preference. Detectify has no dedicated CVE field, so identifiers are extracted from the finding title, the definition's title, description and risk text, and every reference name and link, then deduplicated in order. Endpoint preference is the converter's: the request URL, then the host with the location appended ONLY when it starts with "/" (otherwise it is not a path and concatenating would produce a nonsense host), then the location alone. Recorded via the locations idiom so it works in both V3_FEATURE_LOCATIONS modes. Detectify supplies no remediation prose, only reference links, so the mitigation points at them rather than being left empty. CWE arrives as a plain integer, not a CWE-<n> string. * feat(parsers): add HackerOne and YesWeHack parsers Two more bug-bounty platforms, same rule as the rest of this PR: the scan-type string is byte-identical to the connector's and the dedup config is copied from the connector's own block. Both use the plain hash_code algorithm over unique_id_from_tool alone, because report ids are globally unique on each platform. HackerOne's API is JSON:API, and severity, weakness and reporter are RELATIONSHIPS rather than attributes - each nested under relationships.<name>.data.attributes. Reading them off the top level would leave every finding at Info with no CWE and no reporter, silently and with no error, so a test asserts the raw fixture really does not carry them anywhere else. An already-flattened export is accepted too. YesWeHack's workflow state carries real triage information and the converter translates each one rather than importing everything active: accepted -> active+verified, resolved/auto_close -> mitigated, wont_fix -> risk accepted, invalid/rejected -> false positive, duplicate -> duplicate, out_of_scope/informative -> inactive. An unrecognised state stays ACTIVE so a state YesWeHack adds later cannot silently close a finding. Its severity resolves through three sources in order - CVSS criticity, priority name, priority slug - because the criticity is often empty while a priority is set. * feat(parsers): add Intigriti parser Mirrors pkg/tools/intigriti/connector/finding_converter.go field for field, uses the connector's ScanType verbatim, and copies its dedup settings (plain hash_code over unique_id_from_tool alone - submission codes are globally unique). Three things needed care: - Intigriti's API lists submissions and serves each report separately, so the converter builds a finding from TWO objects and prefers the overview wherever both carry a field. An export may nest the detail under "detail" or carry the report on the entry itself; both are recognised, because missing the merged form would lose the CWE, impact, solution and the whole description body. - Intigriti grades its top tier "Exceptional", not "Critical". Mapping only "critical" would silently drop every top-tier submission to Info. - For a closed or archived submission the CLOSE REASON distinguishes a fix from a rejection: accepted risk -> risk accepted, duplicate -> duplicate, out of scope -> out of scope, and the rejection reasons (including Intigriti's terse "no") -> false positive, with anything else treated as fixed. Treating every closed submission the same way would mark rejected and duplicate submissions as mitigated, which reads as work completed. Researcher-submitted prose - proof of concept, impact, solution, asset and the intake question answers - is flattened to escaped plain text rather than rendered, matching the connector's InertText including Go's html.EscapeString entities. The connectors repo duplicates that sanitizer per tool rather than sharing it, so it is reproduced here rather than imported from another parser. * feat(parsers): add Bugcrowd connector-parity parser Mirrors pkg/tools/bugcrowd/connector/converter.go field for field, uses the connector's ScanTypeName verbatim, and copies its dedup settings (plain hash_code over unique_id_from_tool alone). Deliberately a SEPARATE directory from the shipped `bugcrowd` parser, which handles Bugcrowd's CSV export under the scan type "BugCrowd Scan" (capital C). Two formats, two scan types; a test asserts both so neither can start shadowing the other, and the CSV parser is untouched. Behaviour mirrored rather than chosen: - "triaging" is NOT importable. A submission mid-triage has no confirmed verdict, so importing it would put unvetted researcher claims into the queue. - not_applicable overrides the priority as well as closing the finding: a P1 that Bugcrowd then judged not applicable must not sit in the queue as Critical. - informational is imported but inactive, so a courtesy report is recorded without occupying the open queue. - P5 has no mapping and lands at Info, like anything unrecognised. - Titles are researcher-written, so colons and quotes become spaces and "@" becomes "at" - but only when the title needs it; whitespace collapses and an over-long title is cut to DefectDojo's 511-character column with an ellipsis. - States are normalised, so Bugcrowd's hyphenated and underscored spellings both match the importable set instead of being silently dropped. - A schemeless bug_url is prefixed with "//" so the host survives URI parsing. One faithfulness note: with no programme code in the export the tracker link contains a double slash, because the connector concatenates base + code + self link. Reproduced rather than tidied, and the test says why - the connector always has a code, so tidying here would be the only divergence for an export that carries one. * feat(parsers): add Cobalt.io connector-parity parser Mirrors pkg/tools/cobalt/connector/converter.go field for field, uses the connector's ScanTypeName verbatim, and copies its dedup settings (plain hash_code over unique_id_from_tool alone). A SEPARATE directory from the shipped `cobalt` parser, which handles Cobalt's CSV export under "Cobalt.io Scan". Two formats, two scan types; a test asserts both and the CSV parser is untouched. Three details that would each be wrong if guessed: - Cobalt nests each finding under "resource" but puts the human-facing deep link OUTSIDE it at links.ui.url. Reading the entry directly finds no fields; reading only the resource loses the link, which is the only route back to the pentest report. A test asserts the raw fixture really is shaped that way. - The date comes from the "created" entry in the finding's LOG, not created_at. Cobalt can carry a finding over from an earlier pentest, and then created_at is the carry-over date - the fixture has one created in January and carried over in July, so taking created_at would date it six months late. - CVSS takes the first entry whose version starts with 3, because Cobalt reports v2 and v3 side by side and cvssv3 is a v3 field. The fixture lists the v2 entry FIRST so a naive first-match would be caught. State handling is the connector's: only valid_fix, invalid and out_of_scope close a finding, so a DUPLICATE or an ACCEPTED RISK stays active and is merely flagged. new and triaging are the only unverified states. A state Cobalt adds later is not imported at all - every documented state is already in the importable set, so an unknown one means the API changed and skipping beats guessing. Impact and likelihood are numeric and ZERO IS A REAL SCORE, so presence is tested against None and the empty string rather than truthiness; ruff suggested the falsy shortcut here and it would have silently dropped the zero. * feat(parsers): add Harbor connector-parity parser Mirrors pkg/tools/harbor/converter field for field, uses the connector's ScanType verbatim, and copies its dedup settings (plain hash_code over unique_id_from_tool alone - the composed id already carries repository, artifact, vulnerability and package). Own directory, distinct from the shipped `harbor_vulnerability` parser ("Harbor Vulnerability Scan"); a test asserts both scan types differ. Two things a reader would not guess: - Harbor's scan endpoint keys the report by the SCANNER'S MIME TYPE, so a saved export is normally an object whose single key is "application/vnd.security.vulnerability.report; version=1.1". Expecting the bare report would reject every real export, so the envelope is unwrapped. - The artifact's identity is NOT in the report body at all. The connector supplies repository, tag and digest from the artifact it fetched, and they feed both the finding identity and the image context in the description. An export should carry them; without them the finding still imports with empty segments, which is the connector's own behaviour when the fields are blank. The digest is preferred over the tag because a tag can be moved to a different image, which would silently merge findings from two artifacts. Also mirrored: only a CVE id becomes a vulnerability id (Harbor reports GHSA and distro ids too, and a GHSA in the CVE field would have DefectDojo try to resolve it as one), and a missing description becomes "No description found" rather than an empty field that reads as a parser bug. * fix(parsers): drop duplicate-vendor parsers, fix DeepSource input shape Two corrections, both from review. 1. A parser is scoped to ONE uploaded run; a connector syncs an entire account's ongoing state. So a second parser for a vendor that already has one is redundant. Dropped bugcrowd_connectors, cobalt_connectors and harbor_connectors - DefectDojo already ships bugcrowd (CSV), cobalt (CSV) and harbor_vulnerability, and those cover the file-import case. Their settings entries are reverted too. That also removes the export conventions I had invented purely to fake whole-account context a single-run file does not have: Harbor's repository/tag/digest and Bugcrowd's program_code. Quay keeps its parser - DefectDojo has none for Quay - and takes the plain `quay` directory rather than a _connectors suffix. 2. DeepSource has NO REST API; everything goes through POST /graphql/. The envelope the parser accepted ({"run":..., "occurrences": [...]}) mirrored the connector's internal Go model and was NOT something a DeepSource user could produce, so the parser could not do its job. It now reads the real saved GraphQL response - data.repository with each collection behind a GraphQL connection (edges[].node) - and the fixtures are that shape. A test pins the envelope so it cannot regress to the invented one. Only the wrapper was wrong: every field mapping already used DeepSource's camelCase names (cvssV3BaseScore, epssScore, beginLine), and 26 of 27 existing tests passed unchanged against the real envelope. 14 parsers remain, one per vendor, none duplicating a shipped parser. * feat(parsers): add Google Cloud SCC parser Mirrors pkg/tools/googlescc/connector/converter.go field for field, uses the connector's ScanTypeName verbatim, and copies its dedup settings (plain hash_code over unique_id_from_tool alone - the finding's full resource name is globally unique across the organisation). The shape detail worth knowing: SCC's ListFindings pairs each finding with the resource it was found on as SIBLINGS, not nested. Both halves matter - the category and severity are on the finding, while the display name and type that make it readable are on the resource. Reading the result as if it were the finding would import nothing; reading only the finding half would lose the resource context. A test asserts the raw fixture really is shaped that way. Also mirrored: the title falls back from "<category> - <resource>" to the category alone and then to a constant, because SCC does not always set a category and an empty title is useless in the finding list; SEVERITY_UNSPECIFIED falls through to Info; and CVE/CVSS live two objects deep under vulnerability.cve and exist only on vulnerability-class findings, so a misconfiguration or observation finding legitimately has neither. The score is recorded only when above zero. First of the vendors that have no DefectDojo parser of any kind. * feat(parsers): add Fairwinds Insights parser Mirrors pkg/tools/fairwinds/converter field for field, uses the connector's ScanType verbatim, and copies its dedup settings. Two things that would be wrong if guessed: - Fairwinds normalises severity to a 0.0-1.0 FLOAT, not a word and not a CVSS score. The breakpoints are its own (0.9/0.7/0.4/0.1); reading the number as CVSS would put every finding at Info. A malformed value falls to Info rather than erroring. - Fairwinds aggregates Polaris, Trivy, OPA, kube-bench and Goldilocks into ONE action-item stream, so an item may be about a container image or a Kubernetes manifest. The component is the image and tag when there is one, otherwise the resource name - a single rule would mislabel half the findings. The originating tool becomes a tool:<report type> tag. Also mirrored: a Fixed item imports closed rather than active; the resource line is namespace/kind/name with an optional container qualifier, skipping missing segments; CVEs come from the prose since Fairwinds has no CVE field; and the cluster tag is added unconditionally, so an item with no cluster gets a bare "cluster:" tag - reproduced rather than tidied, since tidying would be a divergence between a file import and an API sync. JSON keys are PascalCase (Title, Severity, ResourceKind). * feat(parsers): add AccuKnox parser Mirrors pkg/tools/accuknox/converter field for field, uses the connector's ScanType verbatim, and copies its dedup settings. The defining problem: AccuKnox returns container, IaC, cloud-posture and runtime findings through ONE endpoint with different column names per type, and does not publish that part of its schema. The converter therefore probes a list of candidate keys per field, and so does this parser - assuming one set of names would silently import empty Info findings for every type but one. Every candidate is also tried with AccuKnox's "vulnerability__" column prefix, which some rows use. The fixture deliberately mixes all three conventions so a regression shows up as a failing test rather than blank findings. Status handling: only fixed, accepted-risk and duplicate close a finding; the working states (in progress, waiting for 3rd party, exception requested, waiting for verification) stay OPEN so work in progress is not hidden. A finding is verified unless the status is empty or "potential" - note a blank status counts as verified, the opposite of a plain truthiness check. A row AccuKnox has ignored is marked out of scope rather than dropped, and that flag arrives as a boolean or a string, so both are handled. CVEs come from the CVE column, which may be an array, falling back to the title since AccuKnox often carries the identifier only there. * style(accuknox): clear three ruff errors that were left outstanding I committed the parser with these unfixed because I checked lint with 'tail -1', which showed only the trailing help line and hid 'Found 3 errors'. Check the error count, not the last line. All three are rewrites with no behaviour change; the 24 tests still pass. The verified rule is now spelled 'status not in {"", "potential"}', which reads closer to the connector's intent than the chained comparison did. * feat(parsers): add Halo Security parser Mirrors pkg/tools/halosecurity/converter field for field, uses the connector's ScanType verbatim, and copies its dedup settings. Three things that matter: - Halo splits an issue across TWO calls. The list row carries the issue, target and status; the description, category, CVEs and PCI flag exist ONLY on a per-issue detail. A row-only import would produce findings with no prose at all, so the parser merges a detail supplied as a top-level map keyed by issue id, a top-level array, or nested on the row. - Severity is an INTEGER level with 5 highest - the inverse of a priority number. Reading it as a score, or assuming 1 is worst, would invert the ladder. The row's level wins, falling back to the detail's, because the list response sometimes omits it. - Identity is "<issue id>:<target id>", because Halo reports the same issue once per affected host. Keying on the issue alone would collapse two hosts into one finding - and their statuses often differ, which the fixture covers. This scan type's dedup config includes ENDPOINTS, so the scanned host is always recorded; an unpopulated endpoint would leave the hash computed over nothing. A test pairs that assertion with the dedup field list so the two cannot drift. Also mirrored: only confirmed/fixing/fixed count as verified, since a new or investigating issue has not been confirmed by anyone; Halo's literal "Nobody" assignee is not reported; and the finding is dated today because Halo's list response carries no discovery date. * feat(beagle): add Beagle Security file parser Adds a file parser for Beagle Security DAST reports, matching the scan type "Beagle Security - Connectors Import" so a file import and an API sync deduplicate against each other instead of producing two copies of every finding. Beagle documents the report-level keys and the occurrence block but not the names of the per-finding fields, and their sample report omits the finding array, so the parser reads every field from the same alias set the API path uses and locates the finding array by name or, failing that, by shape. - one finding per occurrence; "Fixed" (the only documented status) imports as mitigated, every other status as open - severity label first, falling back to grading a bare CVSS score against the 9.0 / 7.0 / 4.0 floors; an unrecognised label becomes Info and is kept as a tag - accepts both the API envelope, which carries the report as a JSON string, and the report body itself - records the tested URL, since this scan type's hash includes endpoints 37 tests, four sample reports, docs page, and the two settings entries. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * feat(nightfall): add Nightfall AI file parser Adds a file parser for Nightfall AI DLP violations, matching the scan type "Nightfall AI - Connectors Import" so a file import and an API sync deduplicate against each other instead of producing two copies of every violation. Nightfall needs two calls per violation - the violation, then the detections that make it up - and a detection carries no violation id, so the parser accepts the detections keyed by violation id or nested on each violation. Without them a finding loses its evidence, its credential verdict and its severity. - a credential Nightfall verified as live is Critical whatever the policy risk says; otherwise the risk label is used and an unrecognised one becomes Info - PENDING imports as active but unverified, EXPIRED as out of scope - location, permalink and exposure note are per-integration mappings, mirroring the connector; GitHub violations are the only ones with a code location - only redacted detection text is read, so no secret value is imported 39 tests, three sample exports, docs page, and the two settings entries. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * feat(fleet): add Fleet vulnerabilities and policies file parsers Adds two file parsers for Fleet host exports, matching the scan types "Fleet:Vulnerabilities - Connectors Import" and "Fleet:Policies - Connectors Import" so a file import and an API sync deduplicate against each other instead of producing two copies of everything. They are two parsers because Fleet's API models software CVEs and policy results as different things: the connector imports them under two scan types with different deduplication keys, one hashing the component and the other the policy. Each parser reads the same host export and ignores the other's half. Vulnerabilities: - a finding per host per software per CVE; identity "<host>:<software>:<version>:<CVE>" - an unscored CVE is Medium, not Info - Fleet enriches from the NVD, so no score means "not scored yet"; an explicit zero is Info - CISA KEV membership is flagged and tagged but does not change the severity - scores may arrive as numbers or numeric strings Policies: - only a failing policy is a finding; passing, result-less and unnamed ones are skipped - High when Fleet marks the policy critical, Medium otherwise - Fleet has no other severity signal for policies - the policy query is included so a reviewer can see what was checked 43 tests, six sample exports, two docs pages, and four settings entries. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * feat(elastic-security): add CNVM, posture and detections file parsers Adds three file parsers for Elastic Security exports, matching the scan types "Elastic Security:CNVM - Connectors Import", "Elastic Security:Posture - Connectors Import" and "Elastic Security:Detections - Connectors Import" so a file import and an API sync deduplicate against each other instead of producing two copies of everything. Elastic returns all three kinds of document from the same _search API with the same ECS asset objects, and each is imported under its own scan type with its own deduplication key - the component for CNVM, the rule for the other two. So there are three parsers, each claiming only its own documents: one export can be uploaded three times without a document landing under the wrong key. The shared document walk and asset rendering live on the CNVM module, the way the shipped Invicti parser extends the Netsparker one. CNVM: - Elastic's severity label wins over the CVSS score; an unrecognised label falls back to grading the score, and only a v3 base reaches the cvssv3_score field - identity is the Elasticsearch document id, which is stable across syncs Posture: - only a failing, named benchmark rule is a finding - an unrecognised label is Medium, not Info - there is no score to fall back on - a description that merely repeats the rationale is not printed twice Detections: - alerts are read from kibana.alert or the older top-level signal object - imported as neither static nor dynamic, with a triage instruction rather than a fix: a detection is observed activity, not a defect with a patch - Elastic's 0-100 risk score is reported, not converted into a severity 62 tests, nine sample exports, three docs pages, and six settings entries. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(parsers): guard every endpoint host against DefectDojo's validator Endpoint.clean() accepts a host of letters, digits, dot, hyphen, underscore or plus - or an IP address - and raises ValidationError otherwise. Because it raises, one unusable host fails the WHOLE import rather than the one finding. Codacy hit exactly that in CI: a container item's affectedTargets is an image reference, so "registry.example.com/generic-app" went into the host field and took the import down with it. It is now parsed, giving host + path, which is what DefectDojo does with the endpoint string the connector sends. Two more parsers were hand-splitting a URL in a way that left "host:port" in the host field - Halo Security and YesWeHack - and both now parse instead. Every parser that records a host also checks it first and drops the endpoint rather than raising, because the values are free text: a Fleet display name is often "Someone's MacBook", and an Elastic cloud resource name can be a path. The value still appears in the description, so nothing is lost. Nine parsers touched: beagle, codacy, crowdstrike_spotlight, elastic_security_cnvm (shared by the three Elastic scan types), fleet_policies, fleet_vulnerabilities, halosecurity, probely, yeswehack. Nine new tests pin the behaviour, including IPv6 hosts, which the pattern rejects but the IP check accepts. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(detectify): assert an unset location path as falsey, not None An unset path is "" on the URL location model (CharField(blank=True)) and None on Endpoint, so asserting either one specifically passes under one value of V3_FEATURE_LOCATIONS and fails under the other. This test asserted None and so failed the locations leg in CI while passing the endpoint leg. The local harness had the same coercion, which is why it did not reproduce; the harness now keeps "" as the real model does, and reproduced this failure exactly and no others. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * feat(action1): add Action1 file parser Adds a file parser for Action1 vulnerability exports, matching the scan type "Action1 Scan" so a file import and an API sync deduplicate against each other instead of producing two copies of everything. Note the scan type does not follow the "<Vendor> - Connectors Import" pattern, so it is copied rather than derived, and a test asserts the derived form is not claimed. Action1 describes a finding across two calls - the vulnerability catalogue and the machines each CVE affects - so the parser accepts the affected endpoints keyed by CVE or nested on the vulnerability. A catalogue entry nothing is running produces no finding, mirroring the connector. - identity is "action1-<CVE>-<endpoint id>": one CVE on three machines is three findings, and the endpoint's own copy of the software wins because the installed version differs per machine - Action1's "score" is a word, not a number; it is the fallback when base_severity is absent, and an unrecognised bucket is Info - mitigation lists Action1's available updates and is left empty when it knows of no patch, rather than filled with generic advice - the machine name is recorded as an endpoint only when it can be a host; Action1 names are free text 28 tests, three sample exports, docs page, and the two settings entries. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * feat(datadog): add Datadog Cloud Security file parser Adds a file parser for Datadog Cloud Security findings, matching the scan type "Datadog Cloud Security" so a file import and an API sync deduplicate against each other instead of producing two copies of everything. The scan type does not follow the "<Vendor> - Connectors Import" pattern, so it is copied rather than derived. Datadog returns misconfigurations, library and code vulnerabilities, attack paths, identity risks and API-security findings through one endpoint, told apart only by finding_type - so this parser decides static versus dynamic per row rather than per file. Its attributes are also nested twice, and reading the outer object as the finding yields nothing. - three independent signals mean "already dealt with" and all three are honoured: a muted/resolved/auto_closed status, an explicit workflow mute, and a compliance evaluation that passed - base_severity is deliberately ignored; the adjusted severity is the graded one - dates are unix milliseconds, and CVSS comes from the base block with the adjusted block as fallback, both values taken together - vulnerability ids come from the advisory and from the prose, since Datadog puts them in different places per finding type - tags are deduplicated but not sorted, matching the connector 26 tests, three sample exports, docs page, and the two settings entries. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * feat(escape): add Escape file parser Adds a file parser for Escape API-security scans, matching the scan type "Escape - Connectors Import" so a file import and an API sync deduplicate against each other instead of producing two copies of everything. Escape nests issues under the scan that produced them and the connector reads an application's latest scan, so the parser accepts a scan, an application carrying one, an applications response, or the issue list itself. - this scan type's hash includes endpoints, so the tested URL is always recorded with its scheme, port, path and query - the endpoint line carries the method, because the same URL behaves differently per verb; the method tag is uppercased while the description keeps Escape's casing, matching the connector - CWE is read from "CWE-89" or a bare number, and an unparseable value still appears in the description - mitigation is left unset when Escape has no remediation, rather than filled with generic advice 22 tests, three sample exports, docs page, and the two settings entries. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * feat(insightappsec): add Rapid7 InsightAppSec file parser Adds a file parser for Rapid7 InsightAppSec vulnerabilities, matching the scan type "Rapid7 InsightAppSec - Connectors Import" so a file import and an API sync deduplicate against each other instead of producing two copies of everything. InsightAppSec names a vulnerability only by the id of the attack module that found it, so the readable title, the rule identity and the description prose all come from a separate module-metadata call; the parser accepts it as a list, a map, or the module endpoint's own response. - only UNREVIEWED and VERIFIED rows are imported, so a reimport closes the remediated, duplicate, ignored and false-positive ones - status and severity are matched case-sensitively against Rapid7's own uppercase enums, and the raw label is kept as the severity justification - this scan type hashes the unique id ALONE - no title, no severity - because the vulnerability id is stable and a volatile field would split a regraded finding - evidence is flattened to inert text, matching Go's EscapeString byte for byte, and only the first three entries are printed - CVSS is recorded only when the vector really is v3 28 tests, three sample exports, docs page, and the two settings entries. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * feat(intruder): add Intruder file parser Adds a file parser for Intruder issues, matching the scan type "Intruder API Import" so a file import and an API sync deduplicate against each other instead of producing two copies of everything. The scan type does not follow the "<Vendor> - Connectors Import" pattern, so it is copied rather than derived. Intruder separates an issue from its occurrences - the targets it was found on - and the occurrence is the finding, so an issue with none produces nothing. Its own issue object carries "occurrences" as a URL string rather than a list, so the parser accepts that second call keyed by issue id or nested on the issue, and a test asserts the unexpanded URL is not mistaken for data. - this is the one connector scan type using the PLAIN hash_code algorithm, with the occurrence id inside the hash fields rather than paired with them - snoozing is how Intruder records triage: FALSE_POSITIVE and the two risk-acceptance reasons map to flags, and an unrecognised reason leaves the finding inactive with neither rather than guessing - the occurrence's CVSS score wins over the issue's, since the same weakness scores differently per target - a port of "0" and a target that cannot be a host are both left unrecorded 27 tests, three sample exports, docs page, and the two settings entries. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * feat(nowsecure): add NowSecure file parser Adds a file parser for NowSecure mobile-app assessments, matching the scan type "NowSecure" so a file import and an API sync deduplicate against each other instead of producing two copies of everything. The scan type is the bare vendor name rather than the "<Vendor> - Connectors Import" pattern, so it is copied. NowSecure reports every check it ran, so only rows it marks as affecting the app and not hidden are imported. One assessment runs both a static and a dynamic analysis of the same app, so static-versus-dynamic is decided per finding, and an unrecognised analysis type leaves both flags at their default rather than guessing. - identity is "nowsecure-<check id>[-<vulnerability id>]", falling back to a slug of the title when there is no check id - vulnerability identifiers are SORTED and deduplicated case-insensitively, which is this connector's extractor behaviour and differs from the order-preserving path the others use - the assessment supplies the date and platform, which the finding rows lack - the CVSS score is set unconditionally, so an unscored finding lands as 0.0 22 tests, three sample exports, docs page, and the two settings entries. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * feat(vanta): add Vanta file parser Adds a file parser for Vanta compliance exports, matching the scan type "Vanta Compliance" so a file import and an API sync deduplicate against each other instead of producing two copies of everything. The scan type does not follow the "<Vendor> - Connectors Import" pattern, so it is copied rather than derived. A Vanta finding is a (test, failing entity) pair: the test is the control and the entity is the resource failing it, so the parser accepts the failing entities keyed by test id or nested on the test, in either the paged results.data wrapper or a bare list. A test with no failing entity is the control working and produces nothing. - severity is always Medium: Vanta has no scale, and Info would read as non-actionable when a failing control is actionable by definition - that makes component_name load-bearing - it is the failing entity, and the hash needs it to keep two resources failing one control apart - only FAILING entities are imported; an entity with no status is taken at its word, since the connector only ever receives failing ones - the date is when the entity started failing, falling back to the test's flip date 21 tests, three sample exports, docs page, and the two settings entries. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * feat(wallarm): add Wallarm file parser Adds a file parser for Wallarm API-security vulnerabilities, matching the scan type "Wallarm API Security" so a file import and an API sync deduplicate against each other instead of producing two copies of everything. The scan type does not follow the "<Vendor> - Connectors Import" pattern, so it is copied rather than derived. - the threat level arrives in ONE field as either a number or a word, depending on which API answered, so both ladders are needed; 5 is the most severe, which is the one part of the mapping the vendor does not document and is worth confirming live - closed and false-positive rows are skipped - identity is "wallarm-<id>", falling back to the wid and then the location, which is last because it is the only fallback that is not an id - only an absolute path is appended to the endpoint: Wallarm reuses the path field for a parameter location on some vulnerability types - mitigation is Wallarm's exploit example rather than advice - the only remediation-shaped field it has - and identifiers are sorted and deduplicated case-insensitively, matching the connector's extractor 25 tests, three sample exports, docs page, and the two settings entries. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(nowsecure): an unset analysis type lands as dynamic, not neutral DefectDojo defaults static_finding to False and dynamic_finding to TRUE, so a finding that leaves both alone is recorded as DYNAMIC rather than as neither. The NowSecure parser leaves them alone for an analysis type the connector does not recognise - which is correct, because the connector's own findings behave the same way when they reach DefectDojo - but the test asserted two Falses and the docs page called it "neither flag set". Both now state what actually happens. Forcing two Falses instead would have been the wrong fix: it would make a file import and an API sync disagree about the same finding. The local harness stubbed every boolean as None, which is why this passed locally and failed on all four CI legs. It now mirrors the real defaults - active and dynamic_finding True, the rest False - and reproduced this failure exactly while surfacing no others across all 33 parsers. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * feat(bright): add Bright Security file parser Adds a file parser for Bright Security DAST scans, matching the scan type "Bright - Connectors Import" so a file import and an API sync deduplicate against each other instead of producing two copies of everything. - this scan type's hash includes endpoints, so one is always recorded: the entry point Bright attacked, falling back to EVERY affected resource, because Bright reports one issue against several when the weakness is reachable from more than one URL - the request and response go in fenced code blocks - raw HTTP captured from the target, which must not be read as markup and which a reviewer needs verbatim - CWE reads "CWE-79" or a bare number, and an unparseable value still shows in the description; the CVSS score may arrive as a number or a numeric string - an entry point that cannot be a host is left out of the endpoints rather than failing the whole import 24 tests, three sample exports, docs page, and the two settings entries. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * feat(defender-for-cloud): add Microsoft Defender for Cloud file parser Adds a file parser for Defender for Cloud sub-assessments, matching the scan type "Microsoft Defender for Cloud - Connectors Import" so a file import and an API sync deduplicate against each other instead of producing two copies of everything. This is a different product from Defender for Endpoint, which DefectDojo already parses as ms_defender. Defender returns every sort of sub-assessment through one endpoint, and the same field means different things in each: a container finding puts the vulnerable package under softwareDetails while a server finding flattens it into softwareName. Reading only one shape would leave every finding of the other kind with no component, which is the field a reviewer patches. - only Unhealthy sub-assessments are imported, so a reimport closes resolved ones; SQL baselines and posture checks are excluded by resource type, and an UNFAMILIAR type is decided by whether it carries a CVE - so a new Defender scanner is not dropped silently - the highest CVSS base score wins, and only a v3 base reaches the v3 field; the justification records which version it was - CVE ids are matched anchored, so "supersedes CVE-2000-0009" in a reference title does not contribute another finding's identifier - the TVM cve field is accepted as a list, an object or a bare string - the title appends the package only when the display name is a bare CVE - deduplication is the ARM id alone: it already encodes subscription, resource and finding 25 tests, three sample exports, docs page, and the two settings entries. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * feat(akto): add Akto file parser Adds a file parser for Akto API-security issues, matching the scan type "Akto Scan" so a file import and an API sync deduplicate against each other instead of producing two copies of everything. The scan type does not follow the "<Vendor> - Connectors Import" pattern, so it is copied rather than derived. Akto runs every test against every endpoint it knows, so neither alone identifies a finding: the identity is "akto-<collection>-<method>-<url>-<test sub-category>" and both the endpoint and the test are in this scan type's hash fields. Akto has no package, so component_name is "<METHOD> <url>" - the tested endpoint is what the component slot means here. - IGNORED is how a reviewer marks a false positive; FIXED is inactive but NOT flagged, because "fixed" is not a judgement about whether it was real - a relative apiUrl is not recorded as an endpoint - the connector does not invent a host - but it is still the component and in the description - CVE ids come from Akto's free-text field, sorted and deduplicated case-insensitively 22 tests, three sample exports, docs page, and the two settings entries. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * feat(holm-security): add Holm Security file parser Adds a file parser for Holm Security vulnerabilities, matching the scan type "Holm Security Scan" so a file import and an API sync deduplicate against each other instead of producing two copies of everything. The scan type does not follow the "<Vendor> - Connectors Import" pattern, so it is copied rather than derived. Holm scans two ways and only the web class exercises a running application, but the class is a property of the scan rather than the row - so an export states it as a top-level "class", and an absent one means static, matching the connector's default. - the severity NAME wins and the numeric level is only the fallback, so an unfamiliar name does not become Info while a usable level sits beside it; 4 is the most severe, the inverse of a priority number - the identity carries the asset and port because Holm reports one weakness per host and per listening port; a port of zero is left out rather than recorded as zero - the endpoint is Holm's URL alone - the separately-reported detected_port is in the identity and description instead, so no endpoint is invented - the CVSS base wins over the score, and the date prefers the last detection 25 tests, three sample exports, docs page, and the two settings entries. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * feat(klocwork): add Klocwork file parser Adds a file parser for Klocwork issues, matching the scan type "Klocwork Scan" so a file import and an API sync deduplicate against each other instead of producing two copies of everything. The scan type does not follow the "<Vendor> - Connectors Import" pattern, so it is copied rather than derived. Klocwork's search endpoint answers with NDJSON - one issue per line, not an array - so that is the shape read first; a JSON array, an object with an issues list, and a single issue object are accepted for an export somebody reshaped. The trailing summary line is skipped by testing for its key rather than parsing it, and a search that matched nothing answers with that line ALONE, which is an empty result rather than a malformed file. - the severity CODE is the inverse of a score: 1 is Critical, and codes 5-10 are Klocwork's informational tiers - numbers may arrive quoted; both forms are read, because the connector's decoder silently skips a line it cannot parse and would report a clean empty sync - Ignore, Not a problem and Filter are false positives; the deferred states the connector's query selects stay ACTIVE, since a deferred finding is still a finding - file_path and the checker are both in the hash, so one checker in two files is two findings - dates are unix milliseconds 24 tests, four sample exports (three NDJSON, one reshaped array), docs page, and the two settings entries. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * feat(qwiet): add Qwiet AI file parser Adds a file parser for Qwiet AI findings, matching the scan type "Qwiet Scan" so a file import and an API sync deduplicate against each other instead of producing two copies of everything. The scan type does not follow the "<Vendor> - Connectors Import" pattern, so it is copied rather than derived. Qwiet carries the interesting metadata as a LIST of key/value tag objects rather than as fields - the CVE, package URL, CVSS score, CWE category and reachability verdict all live there - so each is read by key. Looking for fields of those names would find nothing at all. - reachability is recorded as the severity justification rather than changing the grade, so a reviewer can see why two findings of equal severity differ in urgency - a dependency finding with related_findings is reachable even with no reachability tag: those related findings ARE the path Qwiet traced through the application - the package URL is reduced to its last segment, because the namespace before it is the group rather than the artefact a component matches on - only the first file location becomes file_path and line, since a data-flow finding spans several files and DefectDojo has one path; the rest stay in the description, and an unparseable line keeps the path - the hash spans file_path, cwe AND component_name because Qwiet reports both code and dependency findings 23 tests, three sample exports, docs page, and the two settings entries. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(holm_security): stop asserting a port the locations model infers The locations model fills an unset port in from the protocol (URL.clean_port reads DEFAULT_PORTS), so an https URL naming no port is port 443 there while Endpoint leaves it None. The test asserted the port was falsy against a fixture whose detected_port is 443 on an https URL - a number both sources produce - so it passed with V3_FEATURE_LOCATIONS off and failed with it on. Assert against the fixture row whose detected_port is 8443 on a URL naming no port instead: a value neither the URL nor the scheme default can produce, so it distinguishes the two sources in both modes. The scheme-default case keeps its own test, covering the description and identity where the port does belong. The parser is unchanged - the connector puts only the URL in the endpoint. * feat(automox,bigid): add Automox and BigID file parsers Both mirror their connector's finding converter field for field, and both report the connector's own scan type verbatim so a file import and an API sync deduplicate against each other rather than producing two copies of everything. Automox reports a missing patch and the device it is missing on from two different endpoints, so an export carries both lists and they are joined on the package's server_id. A package whose device is absent is still a finding: the connector's device lookup is a map read that can miss, and it converts anyway. A row with no usable id is dropped, because the id is the whole identity and every row without one would collapse onto "automox-0". BigID reads sensitive data at rest, so only the case identity, its policy and data-source context and the COUNT of affected objects are carried. No sample, preview or value of the data itself is read, even when the export contains one - covered by a fixture that includes those fields and a test that asserts none of them reach the finding. * feat(calicocloud,dragos): add Calico Cloud and Dragos file parsers Both mirror their connector's finding converter field for field and report the connector's own scan type verbatim, so a file import and an API sync deduplicate against each other rather than producing two copies of everything. Calico Cloud serves the image list and each image's vulnerabilities from two endpoints, so an export carries both - nested in the image or keyed by image id. An image whose scan result is still "Unknown" is skipped entirely, as the connector does: its results are unfinished, and importing them would record a partial scan as a complete one. Severity comes from CVSS rather than Calico's Pass/Warn/Fail verdict, whose thresholds are per-tenant configuration. Dragos grades on its own 0-5 scale where 5 is the most severe - the inverse of a score - so CVSS wins where both exist and the scale is read as a scale, not as a score. Its OT exploitability intel is recorded as the severity justification rather than moving the severity. pera_level is absent rather than zero when unknown, and Purdue level 0 is a real level (the physical process layer), so the two do not render alike. * feat(finitestate): add Finite State file parser Mirrors the connector's finding converter field for field and reports its scan type verbatim, so a file import and an API sync deduplicate against each other. The VEX status carries the real semantics. NOT_AFFECTED is a product team asserting the vulnerability does not apply to this build, so it lands inactive and out of scope rather than active - leaving it active would put an answered question back in the queue on every import. It is additionally a false positive when the justification says the vulnerable code is not there to be reached; a justification like inline mitigations means the flaw is real but handled, which is out of scope and NOT a false positive. Anything unrecognised stays active, the safe direction to be wrong in. "unknown" is a value the platform actually uses and means Info, so it does not fall through to the CVSS-derived severity - treating it as missing would silently upgrade every unscored finding. EPSS is per-CVE, so the highest is taken with its own percentile rather than mixing one CVE's score with another's percentile. This scan type has no hashcode field list in the connector settings, so none is registered here: it deduplicates with DefectDojo's default algorithm, which is what the connector's own findings already do. * feat(fortytwocrunch): add 42Crunch file parser Mirrors the connector's converter field for field and reports its scan type verbatim, so a file import and an API sync deduplicate against each other. 42Crunch produces two reports for the same API and the connector converts both under one scan type: a Security Audit of the OpenAPI definition (static, one finding per issue occurrence) and a Conformance Scan of the running API (dynamic, one per scan issue). A file is one or the other, so the shape decides. Neither report stores its text inline - an audit occurrence's pointer and a scan issue's description and location are integer indexes into the report's own lookup tables, so each is resolved. An out-of-range index resolves to nothing rather than failing the import, and an audit identity then keeps the raw index, without which two occurrences of one issue at no resolvable location would collapse into a single finding. Scan descriptions are templates filled from a separate parameter list, one substitution per parameter. A scan issue's own id is a per-scan UUID, so the identity uses the operation plus the check index - stable for the same issue across scans. The API id prefix that every connector identity carries is not in a downloaded report, so a wrapper may supply it; the docs explain that findings will not deduplicate against synced ones without it. * test: compare default dates against the UTC date, not the local one Finding.date defaults to get_current_date(), which is timezone.now().date() - the UTC date, since USE_TZ is on and TIME_ZONE is "UTC". Four of the new tests compared it against date.today(), the LOCAL date, which agrees only while both happen to fall on the same day. The other fourteen parser tests on this branch already use the UTC form; this makes these four match. * feat(hiddenlayer): add HiddenLayer model-scan file parser Mirrors the connector's finding converter field for field and reports its scan type verbatim, so a file import and an API sync deduplicate against each other. HiddenLayer reports SARIF and DefectDojo already parses SARIF - but importing through the generic parser records the findings under the "SARIF" scan type, where they would not deduplicate against the connector's. The mapping here is the connector's, which itself mirrors dojo/tools/sarif/parser.py. Three SARIF behaviours carry…
…t only narrow them (DefectDojo#15502) The candidate hook could already only be used to drop candidates the deduplication algorithm had resolved. A plugin that identifies a finding by something this module does not know about had no way to contribute a match, even though the call site already replaces the candidate list wholesale rather than intersecting with it. This states that as the contract and covers it with a test, so a later refactor cannot quietly turn the hook back into a filter. Adding candidates carries obligations this function cannot check without giving up its single-query fetch: stay inside the product, or a false-positive verdict replicates across a boundary the user never crossed; exclude the findings being processed, or a finding can mark itself; and have the read fields loaded, since candidates are fetched with .only(). The first two need the batch's scope, which the hook never saw -- it is handed one finding at a time -- so FalsePositiveCandidateContext now carries the product, the algorithm and the excluded ids. The context is passed only to hooks that accept it, so a hook written against the two-argument form keeps working; the two existing hook tests cover that form. Co-authored-by: devGregA <greg-agent-2@defectdojo.com>
) Port of DefectDojo#15501, which landed on bugfix only. dev is where this has actually bitten: two changes each adding a migration on the same parent go green individually, because the fork exists only in their union, and nothing re-derives the graph after a merge. Until a release merge-back carries DefectDojo#15501 across, the branch that most needs the check is the one without it. Cherry-pick rather than reimplement, so the two lines stay byte-identical and the next merge-back is a no-op. Verified on this branch: the dojo graph is sound (199 migrations, 90 squashed out, single leaf 0288_backfill_vulnerability_id_entities) and all 22 tests pass. Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…rs can reuse it (DefectDojo#15489) * refactor(finding): extract save()'s field derivation so batched writers can reuse it Finding.save() derives the finding's own columns -- title casing/truncation, blank component normalization, the date default, numerical_severity, CVSS v3/v4 parsing, and the same-tool hash -- inline, mixed in with work that needs a primary key. Only the former is meaningful to a caller writing rows in bulk. bulk_create and bulk_update bypass save() and its signals entirely, so a batched writer must either reimplement that derivation or write rows that differ from every other finding: wrong casing, no numerical_severity, an unparsed CVSS vector, no hash. Reimplementing it has already cost us once. A downstream hash re-derived only the title truncation and omitted titlecase(); because titlecase() also collapses whitespace, the pre-save lookup hash and the stored hash diverged for any multi-line title, so reports with an embedded newline pair matched nothing on reimport and were closed and recreated on every run. Adds Finding.persisted_title() as the single source of truth for the title transform (reading max_length rather than hardcoding 511) and Finding.derive_persisted_fields(), which is the existing block moved verbatim and called by save() at the same point. Fields needing a PK stay in save(): a batched writer needs a set-based implementation of those, not a shared one. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * refactor(finding): fold the new-finding static/dynamic derivation into the shared method The flags a new finding gets from file_path plus its parser-attached locations are derived in memory -- no row required -- so they belong with the rest of the derivation a batched writer needs, not in save()'s body where a bulk path would have to duplicate them. The equivalent branch for an existing finding queries self.locations/endpoints and stays in save(). save() passes is_new_finding through, so its behavior is unchanged. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * style(finding): put the new docstring summaries on the second line Upstream ruff enforces D213 (multi-line-summary-second-line); both new docstrings used the summary-on-first-line form. Caught by CI rather than locally: this repo's ruff.toml pins a rule selector my local ruff rejects outright, so the config cannot be loaded here and the whole file lints as unrunnable rather than clean. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Greg Anderson <greg@Gregs-MacBook-Pro.local> Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
dev still runs the CI this repository had a week ago: the 113-job per-file Selenium matrix, serial unit tests that migrate from scratch, no snapshot restore, no grouping, no tiers, no merge_group triggers, no gate context. Every pull request against dev pays roughly 565 runner-minutes and queue-bound wall clock that bugfix stopped paying days ago -- and the gap would persist until a release merge-back happens to carry the workflow files across. This ports the CI stack directly: fifteen files, each checked out verbatim from bugfix and verified blob-identical to it, so the next release merge-back reduces to a no-op for all of them. It is the union of what landed on bugfix as DefectDojo#15505 (parallel unit phase + in-process celery broker + tblib), DefectDojo#15507 (migrated-database snapshot restore + warm workflow), DefectDojo#15521 (merge_group triggers + the Unit Tests Complete gate), DefectDojo#15522 (Docker Hub pull retry), DefectDojo#15524 (grouped Selenium matrix + sequential-group entrypoint + the async-test product cleanup and paged finding purge), DefectDojo#15528 (two-tier matrices), DefectDojo#15529 (push-trigger scoping + the makemigrations heredoc that misdiagnosed forked graphs), and DefectDojo#15545 (the merge-queue documentation comment). Divergence check before copying, per file, against the merge-base: dev had not modified any of the fifteen since the branches diverged, except migration-graph.yml, whose only difference is that dev's copy (from DefectDojo#15504) predates the push-trigger scoping -- bugfix's copy is that file plus the scoping, so the verbatim copy is the correct merge there too. What changes for dev pull requests: the light tier (23 jobs, ~130 runner-minutes) replaces the old full fan-out on every push, and the "Unit Tests Complete" context starts reporting. What does not change: the merge queue is NOT enabled on dev -- ruleset 20466211 targets bugfix only, so merge_group stays inert here exactly as it was on bugfix before the ruleset, and merging on dev works as before. Extending the queue to dev is a one-line ruleset change to make deliberately, after it has soaked on bugfix. After this merges, dispatch "CI: Warm Caches" on the dev ref once so the snapshot exists in dev's cache scope; until then the first pull requests pay one cold migrate-and-save, which is the designed fallback. Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…ectDojo#15548) * refactor(reimporter): hold findings, not ids, until dispatch batch_finding_ids accumulated (finding.id, push_to_jira) per finding, but those ids are only consumed in the flush block below. Holding the finding and reading .id at dispatch defers the only primary-key read in the per-finding loop body to a point where every finding is certainly written. That makes the loop primary-key free, so an importer that buffers inserts and flushes them per batch can do so by overriding process_finding_that_was_not_matched alone, rather than copying the whole of _process_findings_internal -- the same accommodation get_original_findings and get_reimport_match_candidates_for_batch already provide, and which their docstrings describe for exactly this case. No behavior change: the same (id, push_to_jira) pairs reach the same dispatch, in the same order. * docs(reimporter): claim only what the deferral achieves Self-review correction. The prior comment said a buffering importer could get by 'by overriding process_finding_that_was_not_matched alone'. That overstates it: the batch-boundary block persists locations and applies parser/inherited tags -- all of which need written rows -- before the ids are read, and none of that is behind an importer-controllable seam. Deferring the id read removes the only DIRECT primary-key read in the per-finding loop body. That is what this change does; the comment now says exactly that. * fix(reimporter): always dispatch the final post-processing batch The batch-boundary flush lived inside the per-finding loop, guarded by 'len(batch) >= size or is_final'. The matched branch's force_continue -- taken whenever an incoming finding matches an existing false positive / out of scope / risk accepted finding whose statuses agree -- skips the rest of the loop body, including that check. Findings are content-sorted before processing, so any report whose LAST sorted finding took that path never flushed its tail: for reports under IMPORT_REIMPORT_DEDUPE_BATCH_SIZE (1000), the entire report silently skipped post_process_findings_batch (deduplication, rules, issue updater, JIRA) and parser/inherited tag application. Locations and vulnerability ids were only rescued later if close_old_findings ran, which it does not when close-old is off. The same loss applied when the final iteration's finding was falsy (the 'appears to always be true' guard). Fix: extract the flush block into _flush_post_processing_batch and call it from two places -- size-triggered inside the loop, and once unconditionally after the loop as a drain. The drain replaces the is_final machinery (is_final_batch / is_final / the enumerate index existed only to feed that condition), so the final flush no longer depends on how the last iteration ended. Every step is a no-op on empty state, which close_old_findings already relies on by calling persist() unconditionally. The regression test drives the real matching path: an existing false positive, an incoming status-parity match crafted to hash-match it and sort last, and one genuinely new finding. Without the drain, the dispatch mock records no batch at all. * test(reimporter): drop the unused unpacked variable ruff (CI pins 0.16.0) flags the unused 'to_mitigate' from the empty-report drain test's tuple unpack. Renamed to _to_mitigate. Caught late because I had been running ruff --isolated: this repo's ruff.toml selects PLW0717, which only exists from 0.16.0, so a local 0.15.x cannot parse the config at all. Falling back to --isolated silently drops the repo's whole rule set. 'uvx ruff@0.16.0 check .' is the parity command. * perf(locations): skip the empty transaction when nothing is buffered LocationManager.persist() opened transaction.atomic() unconditionally. _persist_locations() and _persist_status_updates() both already short-circuit on empty accumulators, so with nothing buffered the only cost of proceeding was an empty transaction -- and inside an outer atomic block that is a SAVEPOINT/RELEASE pair, two queries to do nothing. persist() is called at every batch boundary and again unconditionally by close_old_findings, so imports that touched no location paid those pairs already. The post-loop drain added in this branch made it measurable: the performance suite moved expected_num_queries4 by exactly +2. The guard is the union of the two inner guards, so it can only skip work that both inner methods would have skipped anyway. * test: drop the V3 reimport baselines by the removed empty transaction LocationManager.persist() no longer opens transaction.atomic() when nothing is buffered, so the SAVEPOINT/RELEASE pair that used to cost two queries on the persist() call with nothing to write is gone. A reimport makes two persist() calls -- the batch boundary and close_old_findings -- and one of them has nothing to write. 94 -> 92 (no change) and 195 -> 193 (with new findings). V2 is unaffected because EndpointManager.persist() opens no transaction, which is why only the V3 constants move. --------- Co-authored-by: Greg Anderson <greg@Gregs-MacBook-Pro.local>
Bumps [pillow](https://github.com/python-pillow/Pillow) from 12.2.0 to 12.3.0. - [Release notes](https://github.com/python-pillow/Pillow/releases) - [Changelog](https://github.com/python-pillow/Pillow/blob/main/CHANGES.rst) - [Commits](python-pillow/Pillow@12.2.0...12.3.0) --- updated-dependencies: - dependency-name: pillow dependency-version: 12.3.0 dependency-type: direct:production ... 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#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>
…Dojo#14932) * API token revocation * Correct TokenAuthentication import and add expiry to classic template * Ruff linter compliance for token expiry feature * Enhance API token management documentation and improve token expiry error message * feat(api-tokens): wire model field, URL registration, and unit tests - 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 * Ruff compliance and docs correction * Fix silent pass in API test * refactor: replace proposed token viewset with simple revoke-by-key endpoint * docs: update token management section after change to revocation and expiry processes * fix(lint): hoist token expiry imports to module scope 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> * fix(tests): keep the admin token fresh, pin the retroactive behaviour, 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> --------- Co-authored-by: Greg Anderson <greg.anderson@owasp.org> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
…#15673) Documents granting a role against an Organization *type* rather than one Organization at a time, so it applies to every Organization of that type and to ones created later. Extends the existing Organizations page rather than adding one: Organization Types and union-of-grants already ship there, so the new section cross-links them instead of restating them. Covers what the grant does and does not do (bounded by the role, follows membership, additive-only, ignores nesting), the deployment flag, and why managing grants is restricted to superusers and global owners. Co-authored-by: devGregA <greg-agent-2@defectdojo.com>
…on (DefectDojo#15646) A connector whose runs fail, or whose tool stops returning records, is silent outside the connectors page. Document the notification that tells you, what each message covers, and how often it arrives.
…15628) Co-authored-by: devGregA <greg-agent-2@defectdojo.com>
…fectdojo/chart.yaml) (DefectDojo#15721) * chore(deps): update valkey docker tag from 0.25.4 to v0.25.5 (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>
* docs: asset exposure and deployment context Documents the Asset Exposure beta feature in DefectDojo Pro: the five exposure verdicts and how conflicting sources resolve, why automated sources may report exposure but never assert isolation, the per-asset override, deployment context from environment mapping and a production branch, how both signals adjust priority, the optional risk floor and ceiling, and the separate opt-in for using exposure in VDR remediation tiers. Also cross-links the existing Reachability page, since the two answer different questions (code reachable inside the app, versus asset reachable from outside) and are easy to confuse. * docs: exposure inherited from where an asset runs An asset can be reachable without anything having scanned it, because of what it is deployed onto. Documents the deploys-to relationship, the effective-exposure badge, and the two limits that will otherwise surprise people: inherited exposure does not move priority, and you only inherit from assets your permissions let you see. Folded into this page rather than a new one because it is the same question the page already answers, and a reader looking up "is this asset reachable" should not have to know that the answer is split across two documents. * docs: universal parser can map asset exposure Adds the Asset exposure row to the mappable-fields table, plus the Reachability row, which has been mappable for a while and was never listed -- a field users can map but cannot discover is not much of a feature. Notes the one thing about Asset exposure that will otherwise confuse people: it is the only output field describing something other than the finding, so every row mapping it asserts the same thing about the same asset. That is expected and costs nothing. * docs: fix broken internal link to asset exposure page The relative link resolved under import_data/ instead of the site root, failing the lychee internal-link check. Use an absolute path matching the docs convention for cross-section links. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: devGregA <greg-agent-2@defectdojo.com> Co-authored-by: Cody Maffucci <46459665+Maffooch@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
…1.38.2 to v1.38.3 (helm/defectdojo/values.yaml) (DefectDojo#15718) * chore(deps): update gcr.io/cloudsql-docker/gce-proxy docker tag from 1.38.2 to v1.38.3 (helm/defectdojo/values.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>
….5.1 to v1.5.3 (helm/defectdojo/values.yaml) (DefectDojo#15720) * chore(deps): update nginx/nginx-prometheus-exporter docker tag from 1.5.1 to v1.5.3 (helm/defectdojo/values.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> Co-authored-by: Cody Maffucci <46459665+Maffooch@users.noreply.github.com>
….1 to 9.1.1-alpine (docker-compose.yml) (DefectDojo#15744) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
…#15756) * docs(connectors): restore three markdown links mangled into NUL bytes `docs/content/connectors/upstream/toolreference.md` carried six literal NUL bytes (0x00) that broke three inline markdown links. Each one is a leftover placeholder of the form `\0<index>\0` sitting where the link destination `](url)` belongs: - Semgrep token page: `[https://semgrep.dev/orgs/\-/settings/tokens\0389\0)` - Snyk REST base: `**[https://api.snyk.io/rest\0394\0**` - Snyk EU REST base: `**[https://api.eu.snyk.io/rest\0395\0**` The NUL bytes also made the whole file register as binary, so `grep` skipped it and `git diff` rendered it as "Binary files differ". The placeholders were introduced in 1cafb09 (DefectDojo#15661); its parent 1e48e93 still has the intact text, and all four translated siblings (`.de`, `.es`, `.fr`, `.ja`) carry the same markup unchanged. Both sources agree, so the destinations are restored verbatim from them - the two repaired lines are now byte-identical to their pre-DefectDojo#15661 counterparts. The file contains no NUL bytes after this change. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * docs: restore five more markdown links mangled into NUL bytes Same defect as the previous commit, same origin (1cafb09 / DefectDojo#15661): a placeholder of the form \0<index>\0 left sitting where a link destination `](url)` belongs. A repo-wide scan found four more affected files carrying ten NUL bytes between them: - get_started/about/about_defectdojo.md (2: demo, pro demo) - get_started/contributing/documentation.md (1: http://localhost:1313) - get_started/contributing/how-to-write-a-parser.md (1: acunetix.md example) - issue_tracking/pro_integration/messaging_connectors.md (1: api.slack.com/apps) Each destination is restored verbatim from the last revision of that file before DefectDojo#15661, located by walking the file history and counting NUL bytes per revision. All five are self-links (display text equals destination), and every repaired line is byte-identical to its pre-DefectDojo#15661 counterpart. No NUL bytes remain anywhere under docs/, and a regex sweep for markdown link openers with no `](` destination now reports zero hits across docs/content. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
…efectDojo#15755) The GitHub Advanced Security connector can now import a repository's issue tracker as a fourth finding type, GitHub:Issues, alongside the code scanning, Dependabot and secret scanning alert families. Documents the two settings that drive it -- Issue Labels as the filter, Issue Severity Labels as the label-to-severity map, and Default Issue Severity for whatever the map does not match -- plus the token permission issues need, which is less than the alert families require and does not depend on Advanced Security being enabled at all. Adds a "what to expect" section covering the parts that surprise people: pull requests are never imported even though GitHub returns them from the issues endpoint, closing an issue closes the finding, the issue body is reproduced verbatim as the description, and these findings carry no CWE, CVE or component so they will not deduplicate against scanner findings for the same problem. English only; the translated tool references are updated by their own pass. Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
…ord (DefectDojo#15752) The Wiz connector used to make a Record only for each Wiz Project, so a tenant with no Projects had nothing to map and imported nothing. It now always reports a tenant-level Record as well, named after the tenant. Rewrite the Wiz section to say what the connector imports and what a Record is, the same way the other tool sections do. Drop the note saying an account without Project visibility has nothing to import, because that is no longer true, and explain when to map the tenant-level Record. Translations updated to match.
DefectDojo#15761) * docs: 3.3.x upgrade notes for the three deduplication identity changes The 3.3 upgrade page said "no special instructions", which this release makes wrong three times over: Xeol Parser, Checkmarx One Scan and Checkmarx Scan detailed all gain HASHCODE_FIELDS_PER_SCANNER registrations, so findings imported before the upgrade carry hashes that no longer match what an import computes after it. Follows the shape the 3.2 notes set: what changed and why per scan type, then two paths. Instances running the identity signature ledger (default from Pro 3.2.300) need no action because drift detection, the scoped rehash and signature matching bridge the change; the one caveat spelled out is that a large never-backfilled instance should run the backfill before upgrading, since a rehash replaces the identities the backfill exists to record. Everyone else gets the three manage.py dedupe commands in the same form the 2.4x and 3.2 notes use. * docs: the drift watch reports, it does not repair The Pro section overpromised twice. It claimed both ledger flags default on in 3.2.300, and matching is deliberately opt-in. It also claimed the definition change is repaired automatically, but identity_drift_watch notifies and stops there; the repair is the operator accepting the change or running the same dedupe commands everyone else uses. Rewritten so the Pro path is honest: recording is on by default, the drift watch will tell you, and the fix is the same three commands or a Tuner acceptance. The backfill-before-upgrade guidance stays, scoped to instances where matching is enabled, because that is where the preserved identities are actually consulted.
Author
|
I just noticed that this issue was already addressed upstream in an earlier PR. I missed that discussion before working on this. Closing this PR to avoid duplicating the existing fix. Sorry for the noise! |
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.
Description
Renamed
#toggleBoxto#loginTogglePasswordacrosslogin.html,index.js, anddojo.css. This resolves a CSS ID collision where the password visibility toggle button inherited global absolute positioning, causing layout misalignment in Google Chrome.Fixes #15134
Test results
docker compose up).Screenshots
Checklist
bugfixordevbranch.