Skip to content

Commit 03124a3

Browse files
Maffoochclaude
andcommitted
devGregA Merge branch 'dev' into tailwind
Resolve conflicts from dev's removal of Credential Manager (DefectDojo#14836), Stub Findings (DefectDojo#14837), deprecated questionnaire API (DefectDojo#14835), plus Xygeni parser (DefectDojo#14769) and import-time tag batching (DefectDojo#14839). Accepted dev's deletions: cred module, stub findings, deprecated viewsets, and their UI sections in view_eng/view_finding/view_test. Kept tailwind's refactored auth (api_permissions shim, action-string roles, _user_authorized_for) over dev's legacy Permissions-enum code. Trimmed cred/Stub_Finding refs from authorization/{api_permissions, query_registrations,url_permissions}.py and the legacy auth tests. Note: dojo/templates_classic/ still references removed URL names (new_cred_*, promote_to_finding, delete_stub_finding) — follow-up. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2 parents a225c48 + 558a3d2 commit 03124a3

81 files changed

Lines changed: 27002 additions & 5225 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/integration-tests.yml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ jobs:
2222
"tests/check_various_pages.py",
2323
"tests/close_old_findings_dedupe_test.py",
2424
"tests/close_old_findings_test.py",
25-
"tests/credential_test.py",
2625
"tests/dashboard_test.py",
2726
"tests/dedupe_test.py",
2827
"tests/endpoint_extended_test.py",
@@ -46,7 +45,6 @@ jobs:
4645
"tests/notification_webhook_test.py",
4746
"tests/notifications_test.py",
4847
"tests/object_test.py",
49-
"tests/product_credential_test.py",
5048
"tests/product_group_test.py",
5149
"tests/product_member_test.py",
5250
"tests/product_metadata_test.py",

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,3 +152,4 @@ docs/.hugo_build.lock
152152

153153
# claude etc
154154
MEMORY.md
155+
.claude/
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
---
2+
title: "Xygeni"
3+
toc_hide: true
4+
---
5+
### About Xygeni
6+
[Xygeni](https://xygeni.io) is a Software Supply Chain Security platform whose
7+
scanners produce JSON reports for code vulnerabilities (SAST), open-source
8+
dependency vulnerabilities (SCA), hard-coded secrets, IaC flaws, web-application
9+
vulnerabilities (DAST), CI/CD and SCM misconfigurations, and malicious or
10+
suspect components.
11+
12+
This parser handles three Xygeni scan kinds in phase 1: **SAST**, **SCA**, and
13+
**Secrets**. All three share a common `metadata` envelope; the parser
14+
dispatches on `metadata.scanType`.
15+
16+
### Scan Types
17+
| Scan type | `metadata.scanType` | Xygeni CLI command (typical) |
18+
| ------------------------ | ------------------- | ---------------------------- |
19+
| `Xygeni SAST Scan` | `sast` | `xygeni scan --scan-type=sast --format=json` |
20+
| `Xygeni SCA Scan` | `deps` | `xygeni scan --scan-type=deps --format=json` |
21+
| `Xygeni Secrets Scan` | `secrets` | `xygeni scan --scan-type=secrets --format=json` |
22+
23+
See the Xygeni documentation at <https://docs.xygeni.io> for installation and
24+
the full set of CLI options.
25+
26+
### Acceptable JSON Format
27+
All three scan types share the same envelope:
28+
29+
~~~
30+
{
31+
"metadata": {
32+
"uuid": "...",
33+
"timestamp": "2026-04-26T07:08:29Z",
34+
"projectName": "...",
35+
"scanType": "sast" | "deps" | "secrets",
36+
"format": "<scanType>-xygeni",
37+
"reportProperties": {
38+
"tool.name": "Xygeni",
39+
"tool.version": "..."
40+
}
41+
},
42+
...
43+
}
44+
~~~
45+
46+
The kind-specific payload then follows:
47+
48+
- **SAST**`vulnerabilities[]` — each entry carries `detector` (the rule id),
49+
`severity`, `location.{filepath, beginLine, endLine, code}`, `cwe` /
50+
`cwes[]`, `tags[]`, `explanation`, `uniqueHash`, `issueId`, and an optional
51+
`codeFlows[]` block describing source / sink frames and the data path.
52+
- **SCA**`dependencies[]` — each dependency has `name`, `version`,
53+
`ecosystem`, and a nested `vulnerabilities[]` of CVE/GHSA advisories with
54+
`cve`, `cwes`, `fixedVersion`, `aliases`, `overallCvssScore`, `references`,
55+
`description`, `uniqueHash`, `issueId`.
56+
- **Secrets**`secrets[]` — each entry has `type` (e.g.
57+
`aws_access_key`), `detector`, `severity`, `location` (same shape as SAST),
58+
`description`, `tags`, `uniqueHash`, `issueId`. The `secret` value and
59+
`location.code` are already redacted by the Xygeni CLI before serialisation.
60+
61+
### Sample Scan Data
62+
Sample Xygeni JSON reports can be found
63+
[here](https://github.com/DefectDojo/django-DefectDojo/tree/master/unittests/scans/xygeni).
64+
65+
### Deduplication
66+
67+
Every finding carries `unique_id_from_tool` (set from Xygeni's vendor-stable
68+
`uniqueHash`) and `vuln_id_from_tool` (set from `issueId`). The deduplication
69+
algorithm is configured per scan type:
70+
71+
| Scan type | Algorithm | Hash-code fields (fallback) |
72+
| -------------------- | ---------------------------------- | -------------------------------------------------------------- |
73+
| Xygeni SAST Scan | `unique_id_from_tool` | n/a |
74+
| Xygeni SCA Scan | `unique_id_from_tool_or_hash_code` | `vulnerability_ids`, `component_name`, `component_version` |
75+
| Xygeni Secrets Scan | `unique_id_from_tool` | n/a |
76+
77+
For SCA the hash-code fallback enables cross-tool deduplication: the same
78+
CVE on the same package@version reported by Xygeni and another SCA scanner
79+
(Snyk, Trivy, etc.) collapse into a single Finding.

dojo/api_v2/mixins.py

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
from rest_framework.decorators import action
99

1010
from dojo.api_v2 import serializers
11-
from dojo.models import Answer, Question
1211

1312

1413
class DeletePreviewModelMixin:
@@ -46,13 +45,3 @@ def flatten(elem):
4645

4746
serializer = serializers.DeletePreviewSerializer(page, many=True)
4847
return self.get_paginated_response(serializer.data)
49-
50-
51-
class QuestionSubClassFieldsMixin:
52-
def get_queryset(self):
53-
return Question.objects.select_subclasses()
54-
55-
56-
class AnswerSubClassFieldsMixin:
57-
def get_queryset(self):
58-
return Answer.objects.select_subclasses()

dojo/api_v2/serializers.py

Lines changed: 3 additions & 156 deletions
Original file line numberDiff line numberDiff line change
@@ -46,15 +46,9 @@
4646
SEVERITY_CHOICES,
4747
STATS_FIELDS,
4848
Announcement,
49-
Answer,
50-
Answered_Survey,
5149
App_Analysis,
5250
BurpRawRequestResponse,
5351
Check_List,
54-
ChoiceAnswer,
55-
ChoiceQuestion,
56-
Cred_Mapping,
57-
Cred_User,
5852
Development_Environment,
5953
Dojo_User,
6054
DojoMeta,
@@ -63,12 +57,12 @@
6357
Endpoint_Status,
6458
Engagement,
6559
Engagement_Presets,
66-
Engagement_Survey,
6760
FileUpload,
6861
Finding,
6962
Finding_Group,
7063
Finding_Template,
7164
General_Survey,
65+
Global_Role,
7266
Language_Type,
7367
Languages,
7468
Network_Locations,
@@ -78,20 +72,19 @@
7872
Product,
7973
Product_API_Scan_Configuration,
8074
Product_Type,
75+
Product_Type_Group,
76+
Product_Type_Member,
8177
Question,
8278
Regulation,
8379
Risk_Acceptance,
8480
SLA_Configuration,
8581
Sonarqube_Issue,
8682
Sonarqube_Issue_Transition,
87-
Stub_Finding,
8883
System_Settings,
8984
Test,
9085
Test_Import,
9186
Test_Import_Finding_Action,
9287
Test_Type,
93-
TextAnswer,
94-
TextQuestion,
9588
Tool_Configuration,
9689
Tool_Product_Settings,
9790
Tool_Type,
@@ -1788,47 +1781,6 @@ def update(self, instance, validated_data):
17881781
return super().update(instance, validated_data)
17891782

17901783

1791-
class CredentialSerializer(serializers.ModelSerializer):
1792-
class Meta:
1793-
model = Cred_User
1794-
exclude = ("password",)
1795-
1796-
1797-
class CredentialMappingSerializer(serializers.ModelSerializer):
1798-
class Meta:
1799-
model = Cred_Mapping
1800-
fields = "__all__"
1801-
1802-
1803-
class StubFindingSerializer(serializers.ModelSerializer):
1804-
class Meta:
1805-
model = Stub_Finding
1806-
fields = "__all__"
1807-
1808-
def validate_severity(self, value: str) -> str:
1809-
if value not in SEVERITIES:
1810-
msg = f"Severity must be one of the following: {SEVERITIES}"
1811-
raise serializers.ValidationError(msg)
1812-
return value
1813-
1814-
1815-
class StubFindingCreateSerializer(serializers.ModelSerializer):
1816-
test = serializers.PrimaryKeyRelatedField(queryset=Test.objects.all())
1817-
1818-
class Meta:
1819-
model = Stub_Finding
1820-
fields = "__all__"
1821-
extra_kwargs = {
1822-
"reporter": {"default": serializers.CurrentUserDefault()},
1823-
}
1824-
1825-
def validate_severity(self, value: str) -> str:
1826-
if value not in SEVERITIES:
1827-
msg = f"Severity must be one of the following: {SEVERITIES}"
1828-
raise serializers.ValidationError(msg)
1829-
return value
1830-
1831-
18321784
class ProductSerializer(serializers.ModelSerializer):
18331785
findings_count = serializers.SerializerMethodField()
18341786
findings_list = serializers.SerializerMethodField()
@@ -2735,111 +2687,6 @@ class Meta:
27352687
exclude = ("content_type",)
27362688

27372689

2738-
class QuestionnaireQuestionSerializer(serializers.ModelSerializer):
2739-
def to_representation(self, instance):
2740-
if isinstance(instance, TextQuestion):
2741-
return TextQuestionSerializer(instance=instance).data
2742-
if isinstance(instance, ChoiceQuestion):
2743-
return ChoiceQuestionSerializer(instance=instance).data
2744-
return QuestionSerializer(instance=instance).data
2745-
2746-
class Meta:
2747-
model = Question
2748-
exclude = ("polymorphic_ctype",)
2749-
2750-
2751-
class QuestionSerializer(serializers.ModelSerializer):
2752-
class Meta:
2753-
model = Question
2754-
exclude = ("polymorphic_ctype",)
2755-
2756-
2757-
class TextQuestionSerializer(serializers.ModelSerializer):
2758-
class Meta:
2759-
model = TextQuestion
2760-
exclude = ("polymorphic_ctype",)
2761-
2762-
2763-
class ChoiceQuestionSerializer(serializers.ModelSerializer):
2764-
choices = serializers.StringRelatedField(many=True)
2765-
2766-
class Meta:
2767-
model = ChoiceQuestion
2768-
exclude = ("polymorphic_ctype",)
2769-
2770-
2771-
class QuestionnaireAnsweredSurveySerializer(serializers.ModelSerializer):
2772-
class Meta:
2773-
model = Answered_Survey
2774-
fields = "__all__"
2775-
2776-
2777-
class QuestionnaireAnswerSerializer(serializers.ModelSerializer):
2778-
def to_representation(self, instance):
2779-
if isinstance(instance, TextAnswer):
2780-
return TextAnswerSerializer(instance=instance).data
2781-
if isinstance(instance, ChoiceAnswer):
2782-
return ChoiceAnswerSerializer(instance=instance).data
2783-
return AnswerSerializer(instance=instance).data
2784-
2785-
class Meta:
2786-
model = Answer
2787-
exclude = ("polymorphic_ctype",)
2788-
2789-
2790-
class AnswerSerializer(serializers.ModelSerializer):
2791-
question = serializers.StringRelatedField()
2792-
answered_survey = QuestionnaireAnsweredSurveySerializer()
2793-
2794-
class Meta:
2795-
model = Answer
2796-
exclude = ("polymorphic_ctype",)
2797-
2798-
2799-
class TextAnswerSerializer(serializers.ModelSerializer):
2800-
question = serializers.StringRelatedField()
2801-
answered_survey = QuestionnaireAnsweredSurveySerializer()
2802-
2803-
class Meta:
2804-
model = TextAnswer
2805-
exclude = ("polymorphic_ctype",)
2806-
2807-
2808-
class ChoiceAnswerSerializer(serializers.ModelSerializer):
2809-
answer = serializers.StringRelatedField(many=True)
2810-
question = serializers.StringRelatedField()
2811-
answered_survey = QuestionnaireAnsweredSurveySerializer()
2812-
2813-
class Meta:
2814-
model = ChoiceAnswer
2815-
exclude = ("polymorphic_ctype",)
2816-
2817-
2818-
class QuestionnaireEngagementSurveySerializer(serializers.ModelSerializer):
2819-
questions = serializers.SerializerMethodField()
2820-
2821-
@extend_schema_field(serializers.ListField(child=serializers.CharField()))
2822-
def get_questions(self, obj):
2823-
questions = obj.questions.all()
2824-
formated_questions = []
2825-
for question in questions:
2826-
formated_question = f"Order #{question.order} - {question.text}{' (Optional)' if question.optional else ''}"
2827-
formated_questions.append(formated_question)
2828-
return formated_questions
2829-
2830-
class Meta:
2831-
model = Engagement_Survey
2832-
fields = "__all__"
2833-
2834-
2835-
class QuestionnaireGeneralSurveySerializer(serializers.ModelSerializer):
2836-
survey = QuestionnaireEngagementSurveySerializer()
2837-
2838-
class Meta:
2839-
model = General_Survey
2840-
fields = "__all__"
2841-
2842-
28432690
class AnnouncementSerializer(serializers.ModelSerializer):
28442691

28452692
class Meta:

0 commit comments

Comments
 (0)