Skip to content

Conversation

@tchoumi313
Copy link
Contributor

@tchoumi313 tchoumi313 commented Dec 29, 2025

  • Add X-Rays tests for domain change inconsistencies
  • forms update of make domains changeable

Summary by CodeRabbit

  • New Features

    • Cross-domain branch warnings added for assessments, assets, evidences, controls and acceptances.
  • Bug Fixes

    • Folder updates now apply atomically and cascade to related records; folder-change permission checks enforce immutability where required; notifications deferred until commit.
  • Refactor

    • Folder/perimeter inputs now visible or disabled by default across many forms; improved folder autocomplete behavior; deduplicated domain-mismatch warnings.
  • Tests

    • Added tests validating cross-domain branch warning behavior.
  • Localization

    • Added EN/FR messages for branch/domain mismatch warnings.

✏️ Tip: You can customize this high-level summary in your review settings.

@tchoumi313 tchoumi313 self-assigned this Dec 29, 2025
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 29, 2025

📝 Walkthrough

Walkthrough

Adds domain-branch mismatch checks to assessment quality checks; wraps many serializer updates in transactions that cascade folder changes and enqueue post-commit notifications; adds translation keys; and makes folder/perimeter fields visible/selectable across many frontend forms.

Changes

Cohort / File(s) Summary
Domain mismatch validation
backend/core/models.py
Adds is_domain_mismatch() helper, computes root/assessment folder context, injects domain-mismatch warnings/infos into RiskAssessment and ComplianceAssessment quality_check, and deduplicates warnings via domain_checked_controls.
Tests for domain checks
backend/core/tests/test_models.py
Adds TestAssessmentQualityCheckDomainMismatch verifying new cross-domain warning msgids (e.g., appliedControlDifferentBranch, riskAcceptanceDifferentBranch, assetDifferentBranch, evidenceDifferentBranch, requirementAssessmentDifferentBranch).
Core serializers: atomic updates & cascades
backend/core/serializers.py
Introduces transaction.atomic() in many update() flows, records prior state, cascades folder changes to related models (Evidence→EvidenceRevision, ComplianceAssessment→RequirementAssessment, Incident→TimelineEntry, TaskTemplate→TaskNode, ValidationFlow→FlowEvent, etc.), and schedules assignment/notification actions via transaction.on_commit.
Other backend serializers with cascades
backend/crq/serializers.py, backend/ebios_rm/serializers.py, backend/privacy/serializers.py
Add transactional update() implementations that detect folder_id changes and propagate folder updates to child models (QuantitativeRiskStudy/Scenario → scenarios/hypotheses, EbiosRMStudy → multiple children, Processing → Purpose/PersonalData/DataSubject/DataRecipient/DataContractor/DataTransfer).
Frontend translation keys
frontend/messages/en.json, frontend/messages/fr.json
Adds branch-difference message keys and libraryFilteringLabels entries in English and French.
Frontend: remove conditional hidden props (many forms)
frontend/src/lib/components/Forms/ModelForm/... (e.g., AccreditationForm.svelte, AssetForm.svelte, CampaignForm.svelte, ContractForm.svelte, CustomMetricSampleForm.svelte, Dashboard*Form.svelte, DataBreachForm.svelte, EbiosRmForm.svelte, IncidentForm.svelte, MetricDefinitionForm.svelte, OrganisationIssueForm.svelte, OrganisationObjectiveForm.svelte, ProcessingForm.svelte, QuantitativeRiskStudyForm.svelte, ReferenceControlForm.svelte, RiskAcceptanceForm.svelte, RiskAssessmentForm.svelte, ValidationFlowForm.svelte, VulnerabilitiesForm.svelte, BusinessImpactAnalysisForm.svelte, ComplianceAssessmentForm.svelte, EntityAssessmentForm.svelte, FindingsAssessmentForm.svelte, ThreatForm.svelte, TimelineEntryForm.svelte, ...)
Removes hidden={initialData.folder} and/or hidden={initialData.perimeter} so folder/perimeter AutocompleteSelects are no longer conditionally hidden.
HiddenInput → AutocompleteSelect replacements
frontend/src/lib/components/Forms/ModelForm/EvidenceRevisionForm.svelte, .../RequirementAssessmentForm.svelte, .../TimelineEntryForm.svelte
Replace HiddenInput folder fields with AutocompleteSelect configured with folders endpoint, content_type filters, pathField, formDataCache, and cacheLock to enable explicit folder selection.
Disable vs hidden UI tweak
frontend/src/lib/components/Forms/ModelForm/EntityForm.svelte, frontend/src/lib/components/Forms/ModelForm/PerimeterForm.svelte
Switch some folder/perimeter controls from hidden to disabled (e.g., disabled={object.builtin} or disabled={initialData.folder}), preserving visibility while restricting interaction.
Model table actions
frontend/src/lib/components/ModelTable/ModelTable.svelte
Expands delete prevention to rows with builtin or urn (except terminologies) and allows editing for URLModel === 'entities' in edit-url logic.
Perimeter API tests & permission
backend/app_tests/api/test_api_perimeters.py
Adjusts authenticated update tests and adds test_update_perimeters_folder_forbidden to assert 403 when attempting to change a Perimeter's folder; ensures folder immutability enforced.

Sequence Diagram(s)

sequenceDiagram
  participant Client as Client (UI/API)
  participant API as API View
  participant Serializer as WriteSerializer.update
  participant DB as Database
  participant Related as Related Models
  participant Notif as Notification/Worker

  Note over Client,API: Update request (PATCH/PUT)
  Client->>API: send update payload
  API->>Serializer: call .update(instance, validated_data)
  Serializer->>DB: BEGIN transaction (transaction.atomic)
  Serializer->>DB: update instance record
  alt folder_id changed
    Serializer->>Related: bulk update related objects' folder (cascade)
    Related-->>DB: apply bulk updates
  end
  Serializer->>DB: COMMIT transaction
  DB-->>Serializer: transaction.on_commit callbacks registered
  Note over Serializer,Notif: post-commit actions
  Serializer->>Notif: schedule assignment/notifications via transaction.on_commit
  Notif->>Notif: dispatch notifications/tasks (async worker)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

🐇
I hopped through folders, sniffed each branch and tree,
Spotted mismatched homes and left a gentle plea.
Hidden doors now open, cascades commit as one,
Warnings whisper softly — the tidy work is done.

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 22.22% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the main change: making domain fields changeable across all object forms. It accurately reflects the primary objective and encompasses the core feature modification evident in the changeset.
✨ Finishing touches
  • 📝 Generate docstrings

📜 Recent review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 691fc7a and 5f201fe.

📒 Files selected for processing (1)
  • backend/app_tests/api/test_api_perimeters.py
🧰 Additional context used
🧬 Code graph analysis (1)
backend/app_tests/api/test_api_perimeters.py (2)
backend/app_tests/api/test_utils.py (1)
  • get_endpoint_url (19-24)
backend/app_tests/api/test_api_personal_access_tokens.py (1)
  • authenticated_client (27-29)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (9)
  • GitHub Check: build_enterprise_frontend
  • GitHub Check: build_community_frontend
  • GitHub Check: test (3.12)
  • GitHub Check: enterprise-startup-functional-test (3.12)
  • GitHub Check: startup-docker-compose-test
  • GitHub Check: startup-functional-test (3.12)
  • GitHub Check: enterprise-startup-docker-compose-test
  • GitHub Check: build (3.12)
  • GitHub Check: Analyze (python)
🔇 Additional comments (4)
backend/app_tests/api/test_api_perimeters.py (4)

2-2: LGTM!

The import additions support the new test's functionality correctly.

Also applies to: 7-7


150-150: LGTM!

Using str(test.folder) is more consistent with the object's string representation.


153-155: The concern is based on a misunderstanding of how test_params is used. The test_params dict is not sent in the PATCH request—it is only used to validate the response after the update. The PATCH request sends only update_params, which intentionally excludes the folder field, ensuring it cannot be changed. The test_params with the folder id is then used in assertions to confirm the folder remained unchanged, which aligns with the folder immutability enforced by the next test.

Likely an incorrect or invalid review comment.


159-179: This test is correctly implemented and the concerns are unfounded.

The test properly validates that the Perimeter folder field is immutable. The PerimeterWriteSerializer.update() method explicitly forbids folder changes by raising PermissionDenied with the message "Perimeter domain cannot be changed". Using authenticated_client (a superuser) is appropriate here—it demonstrates that even administrative users cannot change the folder domain, which is a business rule enforced at the serializer level, not a permission issue.

The test follows the correct pattern for this specific validation: it directly tests the business rule rather than role-based access control, which is why it doesn't use the parametrized test fixture. The error context is already provided by the serializer's validation logic.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (6)
frontend/messages/en.json (1)

3027-3031: Consider more idiomatic phrasing for domain mismatch messages.

The new translation keys are clear and consistent, which is good. However, the phrasing "belongs to another domain than" could be slightly more idiomatic in English. Consider using "belongs to a different domain from" or "is in a different domain than" for more natural-sounding messages.

Example:

  • Current: "Requirement assessment belongs to another domain than the audit"
  • Alternative: "Requirement assessment belongs to a different domain from the audit"

This is purely stylistic—the current phrasing is grammatically acceptable and will be understood by users.

🔎 Proposed refinement
-	"requirementAssessmentDifferentDomain": "Requirement assessment belongs to another domain than the audit",
-	"appliedControlDifferentDomain": "Applied control belongs to another domain than the assessment",
-	"riskAcceptanceDifferentDomain": "Risk acceptance belongs to another domain than the risk assessment",
-	"evidenceDifferentDomain": "Evidence belongs to another domain than the assessment",
-	"assetDifferentDomain": "Asset belongs to another domain than the assessment"
+	"requirementAssessmentDifferentDomain": "Requirement assessment belongs to a different domain from the audit",
+	"appliedControlDifferentDomain": "Applied control belongs to a different domain from the assessment",
+	"riskAcceptanceDifferentDomain": "Risk acceptance belongs to a different domain from the risk assessment",
+	"evidenceDifferentDomain": "Evidence belongs to a different domain from the assessment",
+	"assetDifferentDomain": "Asset belongs to a different domain from the assessment"
backend/core/models.py (2)

4919-4935: Shared is_domain_mismatch helper is correct but duplicated and mildly type-unsafe at the root helper boundary

The domain comparison logic (null guard, root-folder bypass, then folder-id inequality) looks consistent and matches the intended “root is global, others must match the assessment folder” semantics. However, the same closure is duplicated in both RiskAssessment.quality_check and ComplianceAssessment.quality_check, and both depend on Folder.get_root_folder_id() which (per iam.models.get_root_folder_id) can theoretically return either an id or a Folder instance on its fallback path, relying on str(...) coercion for comparison.

Extracting a small shared utility (e.g. a module-level def is_domain_mismatch(folder_id, assessment_folder_id)) that internally normalizes the root folder strictly to an id would:

  • Remove the duplication between the two quality checks.
  • Guarantee consistent typing regardless of how get_root_folder_id is implemented or evolves.

Also applies to: 6334-6351


5139-5156: Applied-control domain dedupe works; consider upstream distinct() if you ever want to dedupe other warnings too

Using domain_checked_controls = set() to avoid emitting multiple domain-mismatch warnings for the same AppliedControl id is a good way to keep findings readable, given that the serialized lists can contain duplicates when a control is linked to multiple scenarios/requirements.

If, in the future, you also want to avoid duplicated other warnings for such controls (appliedControlNoETA, appliedControlNoEffort, etc.), you might additionally apply .distinct() on the AppliedControl querysets before serialization. That would centralize deduplication at the queryset level instead of per-check, without changing current behavior for these newly-added domain warnings.

Also applies to: 6447-6467

backend/core/tests/test_models.py (2)

395-404: Consider using a pytest fixture for root folder setup.

The _get_root_folder method creates side effects by potentially creating a root folder during tests. Consider using a pytest fixture (e.g., similar to other fixtures in the file) to ensure consistent test isolation and clearer dependency management.

Alternative approach using a fixture
@pytest.fixture
def root_folder():
    """Ensure a root folder exists for domain mismatch tests."""
    root_folder = Folder.get_root_folder()
    if not root_folder:
        root_folder = Folder.objects.create(
            name="root",
            description="root folder",
            content_type=Folder.ContentType.ROOT,
            parent_folder=None,
        )
    return root_folder

Then use it as a parameter in test methods:

def test_risk_assessment_quality_check_warns_on_cross_domain_links(self, root_folder):
    folder_a = Folder.objects.create(
        name="domain-a",
        description="domain a",
        parent_folder=root_folder,
    )
    # ... rest of test

406-467: Consider adding a complementary positive test case.

The test correctly validates that cross-domain entity links trigger warnings. Consider adding a test that verifies entities within the same domain do NOT produce these warnings, ensuring the domain mismatch logic works bidirectionally.

Example complementary test
def test_risk_assessment_quality_check_no_warnings_for_same_domain(self):
    root_folder = self._get_root_folder()
    folder_a = Folder.objects.create(
        name="domain-a",
        description="domain a",
        parent_folder=root_folder,
    )
    risk_matrix = RiskMatrix.objects.create(
        name="risk matrix",
        description="risk matrix description",
        json_definition=RISK_MATRIX_JSON_DEFINITION,
        folder=folder_a,
    )
    perimeter = Perimeter.objects.create(name="perimeter", folder=folder_a)
    risk_assessment = RiskAssessment.objects.create(
        name="risk assessment",
        description="risk assessment description",
        perimeter=perimeter,
        risk_matrix=risk_matrix,
        folder=folder_a,
    )
    scenario = RiskScenario.objects.create(
        name="scenario",
        description="scenario description",
        risk_assessment=risk_assessment,
    )
    # All entities in folder_a (same domain)
    applied_control = AppliedControl.objects.create(
        name="applied control",
        description="applied control description",
        folder=folder_a,
    )
    scenario.applied_controls.add(applied_control)
    
    findings = risk_assessment.quality_check()
    warning_ids = {item["msgid"] for item in findings["warnings"]}
    
    # No domain mismatch warnings should be present
    assert "appliedControlDifferentDomain" not in warning_ids
    assert "riskAcceptanceDifferentDomain" not in warning_ids
    assert "assetDifferentDomain" not in warning_ids
    assert "evidenceDifferentDomain" not in warning_ids
frontend/src/lib/components/Forms/ModelForm/EvidenceRevisionForm.svelte (1)

77-92: Consider removing commented-out code.

The commented block shows the previous conditional logic for the folder field. Since the new implementation is complete and aligned with PR objectives, this commented code can be removed to improve maintainability and reduce clutter.

🔎 Suggested cleanup
-<!-- {#if !(initialData.applied_controls || initialData.requirement_assessments || initialData.evidence)}
-	<AutocompleteSelect
-		{form}
-		optionsEndpoint="folders?content_type=DO&content_type=GL"
-		field="folder"
-		pathField="path"
-		cacheLock={cacheLocks['folder']}
-		bind:cachedValue={formDataCache['folder']}
-		label={m.domain()}
-		hidden={initialData.applied_controls ||
-			initialData.requirement_assessments ||
-			initialData.folder}
-	/>
-{:else}
-	<HiddenInput {form} field="folder" />
-{/if} -->
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 503c07e and 1cae98a.

📒 Files selected for processing (34)
  • backend/core/models.py
  • backend/core/tests/test_models.py
  • frontend/messages/en.json
  • frontend/messages/fr.json
  • frontend/src/lib/components/Forms/ModelForm/AccreditationForm.svelte
  • frontend/src/lib/components/Forms/ModelForm/AssetForm.svelte
  • frontend/src/lib/components/Forms/ModelForm/CampaignForm.svelte
  • frontend/src/lib/components/Forms/ModelForm/ContractForm.svelte
  • frontend/src/lib/components/Forms/ModelForm/CustomMetricSampleForm.svelte
  • frontend/src/lib/components/Forms/ModelForm/DashboardBuiltinWidgetForm.svelte
  • frontend/src/lib/components/Forms/ModelForm/DashboardTextWidgetForm.svelte
  • frontend/src/lib/components/Forms/ModelForm/DashboardWidgetForm.svelte
  • frontend/src/lib/components/Forms/ModelForm/DataBreachForm.svelte
  • frontend/src/lib/components/Forms/ModelForm/EbiosRmForm.svelte
  • frontend/src/lib/components/Forms/ModelForm/ElementaryActionForm.svelte
  • frontend/src/lib/components/Forms/ModelForm/EntityForm.svelte
  • frontend/src/lib/components/Forms/ModelForm/EvidenceRevisionForm.svelte
  • frontend/src/lib/components/Forms/ModelForm/GenericCollectionForm.svelte
  • frontend/src/lib/components/Forms/ModelForm/IncidentForm.svelte
  • frontend/src/lib/components/Forms/ModelForm/MetricDefinitionForm.svelte
  • frontend/src/lib/components/Forms/ModelForm/OrganisationIssueForm.svelte
  • frontend/src/lib/components/Forms/ModelForm/OrganisationObjectiveForm.svelte
  • frontend/src/lib/components/Forms/ModelForm/PerimeterForm.svelte
  • frontend/src/lib/components/Forms/ModelForm/ProcessingForm.svelte
  • frontend/src/lib/components/Forms/ModelForm/QuantitativeRiskStudyForm.svelte
  • frontend/src/lib/components/Forms/ModelForm/ReferenceControlForm.svelte
  • frontend/src/lib/components/Forms/ModelForm/RequirementAssessmentForm.svelte
  • frontend/src/lib/components/Forms/ModelForm/RightRequestForm.svelte
  • frontend/src/lib/components/Forms/ModelForm/RiskAcceptanceForm.svelte
  • frontend/src/lib/components/Forms/ModelForm/SecurityExceptionForm.svelte
  • frontend/src/lib/components/Forms/ModelForm/ThreatForm.svelte
  • frontend/src/lib/components/Forms/ModelForm/TimelineEntryForm.svelte
  • frontend/src/lib/components/Forms/ModelForm/ValidationFlowForm.svelte
  • frontend/src/lib/components/Forms/ModelForm/VulnerabilitiesForm.svelte
💤 Files with no reviewable changes (27)
  • frontend/src/lib/components/Forms/ModelForm/DataBreachForm.svelte
  • frontend/src/lib/components/Forms/ModelForm/RightRequestForm.svelte
  • frontend/src/lib/components/Forms/ModelForm/EntityForm.svelte
  • frontend/src/lib/components/Forms/ModelForm/ReferenceControlForm.svelte
  • frontend/src/lib/components/Forms/ModelForm/OrganisationObjectiveForm.svelte
  • frontend/src/lib/components/Forms/ModelForm/SecurityExceptionForm.svelte
  • frontend/src/lib/components/Forms/ModelForm/AccreditationForm.svelte
  • frontend/src/lib/components/Forms/ModelForm/RiskAcceptanceForm.svelte
  • frontend/src/lib/components/Forms/ModelForm/GenericCollectionForm.svelte
  • frontend/src/lib/components/Forms/ModelForm/EbiosRmForm.svelte
  • frontend/src/lib/components/Forms/ModelForm/PerimeterForm.svelte
  • frontend/src/lib/components/Forms/ModelForm/QuantitativeRiskStudyForm.svelte
  • frontend/src/lib/components/Forms/ModelForm/ValidationFlowForm.svelte
  • frontend/src/lib/components/Forms/ModelForm/VulnerabilitiesForm.svelte
  • frontend/src/lib/components/Forms/ModelForm/ElementaryActionForm.svelte
  • frontend/src/lib/components/Forms/ModelForm/OrganisationIssueForm.svelte
  • frontend/src/lib/components/Forms/ModelForm/CustomMetricSampleForm.svelte
  • frontend/src/lib/components/Forms/ModelForm/ContractForm.svelte
  • frontend/src/lib/components/Forms/ModelForm/ThreatForm.svelte
  • frontend/src/lib/components/Forms/ModelForm/ProcessingForm.svelte
  • frontend/src/lib/components/Forms/ModelForm/IncidentForm.svelte
  • frontend/src/lib/components/Forms/ModelForm/AssetForm.svelte
  • frontend/src/lib/components/Forms/ModelForm/DashboardTextWidgetForm.svelte
  • frontend/src/lib/components/Forms/ModelForm/DashboardWidgetForm.svelte
  • frontend/src/lib/components/Forms/ModelForm/MetricDefinitionForm.svelte
  • frontend/src/lib/components/Forms/ModelForm/CampaignForm.svelte
  • frontend/src/lib/components/Forms/ModelForm/DashboardBuiltinWidgetForm.svelte
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-09-19T09:08:52.306Z
Learnt from: Mohamed-Hacene
Repo: intuitem/ciso-assistant-community PR: 2550
File: backend/core/migrations/0098_evidence_lifecycle_data_migration.py:21-28
Timestamp: 2025-09-19T09:08:52.306Z
Learning: Evidence model inherits from FolderMixin making folder a mandatory field, and the lifecycle management handles folder propagation to revisions automatically.

Applied to files:

  • frontend/src/lib/components/Forms/ModelForm/EvidenceRevisionForm.svelte
🧬 Code graph analysis (2)
backend/core/tests/test_models.py (1)
backend/core/models.py (12)
  • create (4626-4628)
  • RiskAssessment (4782-5320)
  • AppliedControl (4056-4496)
  • applied_controls (7536-7537)
  • RiskAcceptance (7200-7288)
  • risk_scenarios (4333-4334)
  • Asset (2417-3334)
  • assets (7532-7533)
  • Evidence (3727-3814)
  • quality_check (4912-5317)
  • quality_check (6325-6558)
  • RequirementAssessment (6708-7000)
backend/core/models.py (1)
backend/iam/models.py (1)
  • get_root_folder_id (92-97)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (9)
  • GitHub Check: startup-docker-compose-test
  • GitHub Check: enterprise-startup-docker-compose-test
  • GitHub Check: startup-functional-test (3.12)
  • GitHub Check: enterprise-startup-functional-test (3.12)
  • GitHub Check: build_enterprise_frontend
  • GitHub Check: build_community_frontend
  • GitHub Check: build (3.12)
  • GitHub Check: test (3.12)
  • GitHub Check: Analyze (python)
🔇 Additional comments (6)
frontend/src/lib/components/Forms/ModelForm/TimelineEntryForm.svelte (1)

34-42: All verification concerns confirmed as correct and intentionally designed.

  1. Content type parameters: The content_type=DO&content_type=GL query parameters are correct. Migration 0069 confirms these values (DO for Domain, GL for Global), and this pattern is used consistently across 30+ forms in the codebase for multi-domain objects.

  2. Context-independent editability: The folder field being always visible (not conditionally rendered based on context) is an intentional design pattern. All examined forms consistently show the folder field as always editable, unlike the incident field which has explicit context checks. This pattern is used for forms where domain/folder should be changeable.

  3. Backend domain validation: The backend enforces domain consistency automatically. TimelineEntry's save() method includes self.folder = self.incident.folder (line 4043), which synchronizes the folder to always match its parent incident's folder. This prevents cross-domain relationship inconsistencies at the model level.

The implementation correctly aligns with the PR objective to make domains changeable on forms, and all concerns are properly addressed by the existing design and backend constraints.

frontend/messages/fr.json (1)

3026-3030: These translation keys are defined but not used in the codebase.

The English and French translations exist in both message files and are grammatically correct. However, a search for references to these keys across TypeScript, JavaScript, and Vue components returned no results, indicating these translations are not currently utilized in the UI.

Either:

  • The implementation to use these keys in domain-mismatch validation warnings is missing, or
  • These translations are orphaned and should be removed

Verify whether the backend validation logic that should emit these warnings has been implemented, and update the component code to reference these keys accordingly.

frontend/src/lib/components/Forms/ModelForm/RequirementAssessmentForm.svelte (1)

74-82: The folders endpoint correctly supports content_type filtering through FolderFilter.content_type, and the backend properly handles DO and GL parameters. Domain change validation is implemented via ComplianceAssessment.is_domain_mismatch(), which generates warnings (not blocks) for cross-domain relationships and uses the translation keys correctly. The AutocompleteSelect implementation is correct and follows established patterns. No issues identified.

backend/core/models.py (1)

5231-5242: Domain checks for risk acceptances, assets and evidences look consistent and efficiently scoped

The new quality checks for:

  • RiskAcceptance vs RiskAssessment folder (riskAcceptanceDifferentDomain),
  • assets linked to risk scenarios (assetDifferentDomain),
  • evidences linked via applied controls in risk assessments (evidenceDifferentDomain),
  • evidences linked either via applied controls or directly to a compliance assessment, and
  • assets linked to a compliance assessment

all consistently reuse the same is_domain_mismatch logic and limit queries to only id, name, and folder_id with appropriate .distinct() where needed. This keeps the additional checks cheap while broadening coverage of cross-domain inconsistencies exactly in the places the PR description calls out.

Also applies to: 5269-5310, 6484-6485, 6513-6551

backend/core/tests/test_models.py (1)

469-531: LGTM! Comprehensive compliance assessment domain mismatch test.

The test thoroughly validates domain mismatch warnings for compliance assessments, covering requirement assessments, applied controls, evidence, and assets. The setup correctly places entities across different domains and verifies the expected warnings are emitted.

frontend/src/lib/components/Forms/ModelForm/EvidenceRevisionForm.svelte (1)

4-4: LGTM!

The AutocompleteSelect import is necessary for the new folder field implementation.

@tchoumi313 tchoumi313 requested review from nas-tabchiche and removed request for nas-tabchiche December 30, 2025 14:44
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (4)
backend/core/models.py (3)

4919-4936: Domain-mismatch helper and applied-control deduplication are sound, but duplicated

The is_domain_mismatch helper correctly:

  • Treats missing folders and root-folder participation as “no mismatch”.
  • Compares folder IDs via string to be robust to different PK types.
    And the domain_checked_controls set avoids spamming duplicate findings when the same AppliedControl is linked to multiple scenarios.

You now have essentially identical is_domain_mismatch implementations (and very similar control‑dedup logic) in both RiskAssessment.quality_check and ComplianceAssessment.quality_check. Consider extracting a small shared utility (e.g. a staticmethod on Assessment or a helper in this module) that:

  • Builds the assessment “domain context” (root vs assessment folder).
  • Performs the mismatch test and optionally handles deduplication on an ID set.

This would reduce drift if the domain rules evolve again.

Also applies to: 5139-5156


6334-6351: ComplianceAssessment domain helper and requirement-assessment checks align with RiskAssessment

The ComplianceAssessment.quality_check domain helper mirrors the RiskAssessment one and is used immediately to flag RequirementAssessments whose folder_id differs from the assessment (excluding the root case). Using ra.folder_id (model field, not serialized) keeps this cheap, and serializing to ra_dict only once per RA is fine.

Given this logic is duplicated with the risk side, it would benefit from the same shared helper abstraction mentioned earlier, but functionally it looks correct.

Also applies to: 6386-6397


6437-6439: Applied-control domain checks in compliance quality_check behave as intended

The introduction of applied_control_qs and domain_checked_controls:

  • Prevents repeated “different branch” findings when the same AppliedControl is linked to multiple requirement assessments.
  • Reuses the same folder-based mismatch criteria as elsewhere.
  • Leaves the existing appliedControlNoReferenceControl check unchanged.

If you ever run into performance issues with very large assessments, a small further optimization would be to short‑circuit on id in domain_checked_controls before calling is_domain_mismatch, but that’s purely micro‑level.

Also applies to: 6442-6442, 6447-6467

backend/core/tests/test_models.py (1)

395-404: Consider relying on existing fixtures for root folder.

The _get_root_folder() helper creates a root folder if it doesn't exist, but fixtures used in other tests (e.g., domain_perimeter_fixture at line 1312) likely already handle root folder creation. Creating it conditionally here could mask fixture issues or create inconsistent test state.

Suggested simplification
 def _get_root_folder(self):
-    root_folder = Folder.get_root_folder()
-    if not root_folder:
-        root_folder = Folder.objects.create(
-            name="root",
-            description="root folder",
-            content_type=Folder.ContentType.ROOT,
-            parent_folder=None,
-        )
-    return root_folder
+    return Folder.get_root_folder()

If Folder.get_root_folder() can return None, consider adding a fixture or using @pytest.mark.usefixtures to ensure the root folder exists for all tests.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1cae98a and 7195921.

📒 Files selected for processing (10)
  • backend/core/models.py
  • backend/core/serializers.py
  • backend/core/tests/test_models.py
  • frontend/messages/en.json
  • frontend/messages/fr.json
  • frontend/src/lib/components/Forms/ModelForm/BusinessImpactAnalysisForm.svelte
  • frontend/src/lib/components/Forms/ModelForm/ComplianceAssessmentForm.svelte
  • frontend/src/lib/components/Forms/ModelForm/EntityAssessmentForm.svelte
  • frontend/src/lib/components/Forms/ModelForm/FindingsAssessmentForm.svelte
  • frontend/src/lib/components/Forms/ModelForm/RiskAssessmentForm.svelte
💤 Files with no reviewable changes (5)
  • frontend/src/lib/components/Forms/ModelForm/ComplianceAssessmentForm.svelte
  • frontend/src/lib/components/Forms/ModelForm/FindingsAssessmentForm.svelte
  • frontend/src/lib/components/Forms/ModelForm/EntityAssessmentForm.svelte
  • frontend/src/lib/components/Forms/ModelForm/BusinessImpactAnalysisForm.svelte
  • frontend/src/lib/components/Forms/ModelForm/RiskAssessmentForm.svelte
🚧 Files skipped from review as they are similar to previous changes (2)
  • frontend/messages/fr.json
  • frontend/messages/en.json
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Analyze (python)
🔇 Additional comments (5)
backend/core/models.py (2)

5231-5242: RiskAcceptance / Asset / Evidence domain checks are consistent and efficient

The new checks for:

  • RiskAcceptance linked to scenarios,
  • Asset linked via risk_scenarios,
  • Evidence linked via applied_controls,

all:

  • Use relational filters (distinct() + only('id', 'name', 'folder_id')) to avoid N+1 queries.
  • Reuse the same is_domain_mismatch semantics and emit clearly scoped info findings with stable msgids.

This looks correct and fits the intended “cross‑branch” detection behavior.

Also applies to: 5269-5288, 5289-5309


6513-6535: Evidence and asset domain checks for compliance assessments look correct

The new blocks that:

  • Build domain_evidence_objects from both applied_control_qs and self.evidences, and
  • Iterate over self.assets,

use .distinct().only('id', 'name', 'folder_id') and the shared is_domain_mismatch helper, yielding a single info finding per evidence/asset that is outside the assessment’s folder (except when either side is root).

This should cover both direct and control‑linked evidences and assets without duplicates, and the message payloads (msgid, link, obj_type, object) are consistent with the risk‑assessment side.

Also applies to: 6537-6551

backend/core/tests/test_models.py (2)

406-467: Comprehensive test coverage for RiskAssessment domain-mismatch detection.

This test properly validates that the quality_check method detects cross-domain references for:

  • Applied controls in different domains
  • Risk acceptances in different domains
  • Assets in different domains
  • Evidence in different domains (via applied control)

The test correctly sets up a scenario where folder_a contains the assessment while folder_b contains all related objects, ensuring domain-mismatch warnings are triggered.


469-531: Good test coverage for ComplianceAssessment domain-mismatch detection.

This test validates that quality_check detects cross-domain references for requirement assessments, applied controls, evidence, and assets. The test correctly creates objects in folder_b while the compliance assessment is in folder_a, ensuring domain-mismatch warnings are triggered.

Note: The test bypasses the serializer's folder cascade logic (lines 1870-1874 in serializers.py) by creating objects directly via ORM. This is correct—quality_check should detect inconsistencies from any source (manual creation, imports, legacy data).

backend/core/serializers.py (1)

1848-1848: Folder cascade logic is correct and requires no additional cascading.

The cascade from ComplianceAssessment to RequirementAssessment correctly mirrors the established pattern used for Evidence→EvidenceRevision (lines 1574-1578) and TaskTemplate→TaskNode (lines 2610-2614).

RequirementAssessment's relationships to Evidence and AppliedControl do not require folder cascading. Unlike EvidenceRevision and TaskNode (which are child objects owned by their parents), Evidence and AppliedControl are independent domain objects with their own folder associations. The many-to-many relationships are simple association links, not ownership relationships, and Evidence/AppliedControl maintain their folder context independently across multiple assessments.

Comment on lines +494 to +500
requirement = RequirementNode.objects.create(
name="requirement",
description="requirement description",
framework=framework,
assessable=True,
folder=folder_b,
)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Search for RequirementNode model definition and folder field
rg -n -A 10 'class RequirementNode' backend/core/models.py | head -50

Repository: intuitem/ciso-assistant-community

Length of output: 210


🏁 Script executed:

#!/bin/bash
# Search for quality_check method and requirementNodeDifferentBranch references
rg -n 'requirementNodeDifferentBranch|quality_check' backend/core/models.py | head -30

Repository: intuitem/ciso-assistant-community

Length of output: 160


🏁 Script executed:

#!/bin/bash
# Search for framework folder relationship in RequirementNode
rg -n -B 3 -A 3 'framework.*folder|RequirementNode.*folder' backend/core/models.py | head -100

Repository: intuitem/ciso-assistant-community

Length of output: 59


🏁 Script executed:

#!/bin/bash
# Check serializers for framework folder cascade logic
rg -n -B 5 -A 5 'requirement.*folder\|framework.*folder' backend/core/serializers.py | head -100

Repository: intuitem/ciso-assistant-community

Length of output: 59


🏁 Script executed:

#!/bin/bash
# Find RequirementNode model definition
rg -n 'class RequirementNode' backend/core/models.py -A 20 | head -60

Repository: intuitem/ciso-assistant-community

Length of output: 889


🏁 Script executed:

#!/bin/bash
# Look at quality_check method around line 4912
sed -n '4900,4950p' backend/core/models.py

Repository: intuitem/ciso-assistant-community

Length of output: 2025


🏁 Script executed:

#!/bin/bash
# Search for folder field in RequirementNode and related validations
rg -n 'folder.*=' backend/core/models.py | grep -i 'requirement\|framework' | head -20

Repository: intuitem/ciso-assistant-community

Length of output: 59


🏁 Script executed:

#!/bin/bash
# Check the test file to understand what's being tested
sed -n '469,531p' backend/core/tests/test_models.py

Repository: intuitem/ciso-assistant-community

Length of output: 2545


🏁 Script executed:

#!/bin/bash
# Find ComplianceAssessment quality_check method (likely around line 6325)
sed -n '6320,6450p' backend/core/models.py

Repository: intuitem/ciso-assistant-community

Length of output: 5533


🏁 Script executed:

#!/bin/bash
# Search for any requirementNodeDifferentBranch or similar checks
rg -n 'requirementNodeDifferentBranch|requirement.*DifferentBranch' backend/core/models.py

Repository: intuitem/ciso-assistant-community

Length of output: 154


🏁 Script executed:

#!/bin/bash
# Look at RequirementNode model more completely to see folder field definition
sed -n '2080,2150p' backend/core/models.py

Repository: intuitem/ciso-assistant-community

Length of output: 2597


🏁 Script executed:

#!/bin/bash
# Check if there are any folder validation constraints on RequirementNode
rg -n -B 5 -A 5 'class Meta' backend/core/models.py | grep -A 10 'RequirementNode' | head -30

Repository: intuitem/ciso-assistant-community

Length of output: 400


Add assertion for requirementNodeDifferentBranch or update quality_check to warn about framework-requirement folder mismatch.

The test creates a RequirementNode in folder_b (line 499) while its parent framework is in folder_a (line 484). The quality_check() method already detects and warns about similar cross-domain mismatches for RequirementAssessment, AppliedControl, Evidence, and Asset, but does not check if a RequirementNode's folder differs from its Framework's folder. Either add an assertion confirming this scenario is detected, or update quality_check() to include the missing check for consistency.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7195921 and e0af5a6.

📒 Files selected for processing (1)
  • backend/core/serializers.py
🧰 Additional context used
🧬 Code graph analysis (1)
backend/core/serializers.py (1)
backend/core/models.py (2)
  • update (1209-1221)
  • RequirementAssessment (6708-7000)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Analyze (python)
🔇 Additional comments (2)
backend/core/serializers.py (2)

1860-1864: LGTM — folder cascade logic is correct.

The cascade update to RequirementAssessment objects is appropriately guarded by the folder change check, uses an efficient bulk update, and is consistent with similar patterns elsewhere in this file (e.g., EvidenceWriteSerializer, TaskTemplateWriteSerializer).


1871-1876: Good pattern: deferring notifications to post-commit.

Using transaction.on_commit() ensures notifications are only dispatched after the database changes are successfully committed, preventing notifications for failed updates. This is a good improvement over the immediate notification pattern used in other serializers like AppliedControlWriteSerializer.

Copy link
Collaborator

@eric-intuitem eric-intuitem left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • I've added transactions and missing cascading effects
  • allow domain change for ebios-rm study
  • the main entity should be stuck to Global domain (the domain should be read-only for that)
  • For perimeter, the domain should be visible in read-only mode (better than invisible)
  • We need to do the other changes mentioned in CA-1456:
    1/ suppress the filtering mechanism based on SCOPE_FOLDER_ID
    2/ remove fieldsrelatedfields everywhere
    3/ fix all broken pages by using dynamic fetch of related fields
    4/ check with non-regression tests that we have fixed all pages

@tchoumi313
Copy link
Contributor Author

  • I've added transactions and missing cascading effects
  • allow domain change for ebios-rm study
  • the main entity should be stuck to Global domain (the domain should be read-only for that)
  • For perimeter, the domain should be visible in read-only mode (better than invisible)
  • We need to do the other changes mentioned in CA-1456:
    1/ suppress the filtering mechanism based on SCOPE_FOLDER_ID
    2/ remove fieldsrelatedfields everywhere
    3/ fix all broken pages by using dynamic fetch of related fields
    4/ check with non-regression tests that we have fixed all pages

the other changes of CA-1456 where done here #3101 (still working on fixing the tests).

Okay, i am going to adjust main entity and perimeter folder view

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (3)
backend/core/models.py (2)

4925-4942: Validate folder‑equality logic for “domain branch” and confirm it matches business semantics

The new is_domain_mismatch helper plus the checks for applied controls, risk acceptances, assets, and evidences are coherent and the root-folder special‑case is sensible (treating the global folder as compatible with everything). The deduplication via domain_checked_controls also avoids spamming multiple findings for the same AppliedControl.

However, the current implementation assumes that “same domain/branch” ⇔ folder_id equality (excluding root):

  • Any object whose folder_id differs from self.folder_id is treated as a different branch, even if both folders might live under the same top‑level “domain” node.
  • If domains in your data model are actually “folder subtrees” (e.g., domain root folder with nested children), this will flag mismatches between siblings/descendants in the same domain.

If your intention is strict “same folder” constraints, this is correct. If “domain branch” really means “same top‑level domain folder (ancestor)”, you may want to compare on a normalized domain identifier (e.g., top‑level ancestor or folder.domain_id) instead of raw folder_id.

I’d recommend double‑checking the domain semantics and, if needed, centralizing the logic in a reusable helper on Assessment/FolderMixin (e.g., _is_domain_mismatch(other_folder_id)) to keep RiskAssessment and ComplianceAssessment in sync.

Also applies to: 5145-5162, 5237-5315


6381-6398: DRY up domain‑mismatch helper and confirm evidence/asset coverage is what you expect

The ComplianceAssessment quality check mirrors the RiskAssessment changes:

  • Same is_domain_mismatch logic (root folder special‑casing, simple folder_id inequality).
  • Domain warnings for requirement assessments, applied controls (with domain_checked_controls to avoid duplicates), evidences (via both applied controls and direct compliance_assessments link), and assets.

Functionally this looks consistent and the reuse of applied_control_qs both for serialization and evidence queries is a nice improvement.

Two follow‑ups worth considering:

  1. Shared helper instead of duplication

    The is_domain_mismatch implementation is now duplicated between RiskAssessment and ComplianceAssessment. To avoid drift later, you could extract it into:

    • a protected method on Assessment (e.g., _is_domain_mismatch(self, folder_id)), or
    • a small utility on Folder/FolderMixin that takes both folder IDs.
  2. Confirm strict folder‑equality vs. “same branch” semantics

    As with RiskAssessment, this logic treats any non‑root folder_id different from self.folder_id as a different branch. If “domain” is actually modeled as a subtree under a domain root folder, you may want to compare using a normalized domain anchor rather than direct folder_id.

If strict folder‑level matching is indeed the intended contract for all these relations, then the current implementation is fine; otherwise, a shared, domain‑aware helper will make it easier to adjust the behavior in one place later.

Also applies to: 6433-6444, 6484-6486, 6494-6515, 6531-6532, 6560-6585

backend/core/serializers.py (1)

2657-2676: Consider using transaction.on_commit() for notification consistency.

The transaction handling and folder cascade are correct. However, for consistency with ComplianceAssessmentWriteSerializer, consider wrapping the notification call in transaction.on_commit(). This is more robust in nested transaction scenarios.

🔎 Suggested change for consistency
     with transaction.atomic():
         instance = super().update(instance, validated_data)
         now_recurrent = instance.is_recurrent
         self._sync_task_node(instance, tasknode_data, was_recurrent, now_recurrent)

         # Update all TaskNodes' folder if the TaskTemplate's folder changed
         if old_folder_id != instance.folder_id:
             TaskNode.objects.filter(task_template=instance).update(
                 folder=instance.folder
             )

-    # Get new assigned users after update
-    new_assigned_ids = set(instance.assigned_to.values_list("id", flat=True))
-
-    # Send notifications only to newly assigned users
-    newly_assigned_ids = new_assigned_ids - old_assigned_ids
-    if newly_assigned_ids:
-        self._send_assignment_notifications(instance, list(newly_assigned_ids))
+        # Get new assigned users after update
+        new_assigned_ids = set(instance.assigned_to.values_list("id", flat=True))
+        newly_assigned_ids = new_assigned_ids - old_assigned_ids
+
+        # Schedule notifications to run after transaction commits
+        if newly_assigned_ids:
+            transaction.on_commit(
+                lambda: self._send_assignment_notifications(
+                    instance, list(newly_assigned_ids)
+                )
+            )

     return instance
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 20afbad and 8cce162.

📒 Files selected for processing (10)
  • backend/core/models.py
  • backend/core/serializers.py
  • frontend/messages/en.json
  • frontend/messages/fr.json
  • frontend/src/lib/components/Forms/ModelForm/ComplianceAssessmentForm.svelte
  • frontend/src/lib/components/Forms/ModelForm/MetricDefinitionForm.svelte
  • frontend/src/lib/components/Forms/ModelForm/OrganisationObjectiveForm.svelte
  • frontend/src/lib/components/Forms/ModelForm/RequirementAssessmentForm.svelte
  • frontend/src/lib/components/Forms/ModelForm/SecurityExceptionForm.svelte
  • frontend/src/lib/components/Forms/ModelForm/ValidationFlowForm.svelte
💤 Files with no reviewable changes (5)
  • frontend/src/lib/components/Forms/ModelForm/SecurityExceptionForm.svelte
  • frontend/src/lib/components/Forms/ModelForm/MetricDefinitionForm.svelte
  • frontend/src/lib/components/Forms/ModelForm/ComplianceAssessmentForm.svelte
  • frontend/src/lib/components/Forms/ModelForm/ValidationFlowForm.svelte
  • frontend/src/lib/components/Forms/ModelForm/OrganisationObjectiveForm.svelte
🚧 Files skipped from review as they are similar to previous changes (2)
  • frontend/messages/fr.json
  • frontend/src/lib/components/Forms/ModelForm/RequirementAssessmentForm.svelte
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-09-19T08:46:34.495Z
Learnt from: Mohamed-Hacene
Repo: intuitem/ciso-assistant-community PR: 2550
File: backend/core/serializers.py:1277-1292
Timestamp: 2025-09-19T08:46:34.495Z
Learning: In EvidenceWriteSerializer, the link and attachment fields are read-only, so updates to these fields should be done through EvidenceRevisionWriteSerializer to maintain proper versioning.

Applied to files:

  • backend/core/serializers.py
📚 Learning: 2025-09-19T09:08:52.306Z
Learnt from: Mohamed-Hacene
Repo: intuitem/ciso-assistant-community PR: 2550
File: backend/core/migrations/0098_evidence_lifecycle_data_migration.py:21-28
Timestamp: 2025-09-19T09:08:52.306Z
Learning: EvidenceRevision.save() method automatically sets is_published from the parent Evidence object and handles folder assignment from the evidence's folder with proper safety checks.

Applied to files:

  • backend/core/serializers.py
🧬 Code graph analysis (1)
backend/core/models.py (1)
backend/iam/models.py (1)
  • get_root_folder_id (92-97)
🪛 Biome (2.1.2)
frontend/messages/en.json

[error] 3057-3058: The key libraryFilteringLabels was already declared.

This where a duplicated key was declared again.

If a key is defined multiple times, only the last definition takes effect. Previous definitions are ignored.

(lint/suspicious/noDuplicateObjectKeys)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (9)
  • GitHub Check: build_enterprise_frontend
  • GitHub Check: build_community_frontend
  • GitHub Check: startup-docker-compose-test
  • GitHub Check: enterprise-startup-docker-compose-test
  • GitHub Check: enterprise-startup-functional-test (3.12)
  • GitHub Check: test (3.12)
  • GitHub Check: startup-functional-test (3.12)
  • GitHub Check: build (3.12)
  • GitHub Check: Analyze (python)
🔇 Additional comments (5)
backend/core/serializers.py (5)

5-5: LGTM! Import fix addresses the critical issue.

The transaction import is now correctly added to support the transaction.atomic() and transaction.on_commit() calls used throughout the serializers.


1564-1581: LGTM! Transaction handling and folder cascade are correctly implemented.

The transaction.atomic() block ensures atomicity between the Evidence update and the cascading folder update to related EvidenceRevision records. This aligns with the learned behavior that EvidenceRevision handles folder assignment from its parent Evidence.


1847-1880: Well-structured transaction handling with proper notification deferral.

Good implementation:

  • transaction.atomic() ensures the folder cascade to RequirementAssessment is atomic with the parent update
  • transaction.on_commit() correctly defers email notifications until after successful commit, preventing notifications for rolled-back updates
  • Auto-lock on status deprecation is preserved from before

2531-2540: LGTM! Clean implementation following the established pattern.

The new update method correctly cascades folder changes to related TimelineEntry records within a transaction, ensuring data consistency.


2864-2956: LGTM! Both update paths handle transactions and folder cascades correctly.

Good implementation details:

  • Both branches (status change vs non-status change) properly wrap updates in transaction.atomic()
  • Folder changes are cascaded to FlowEvent records in both paths
  • The FlowEvent creation in the status-change branch already uses updated_instance.folder, so the subsequent bulk update is idempotent (harmless redundancy)
  • Permission checks are correctly performed before entering the transaction block

Comment on lines +3057 to +3062
"libraryFilteringLabels": "Library labels",
"requirementAssessmentDifferentBranch": "Requirement assessment belongs to another branch than the audit",
"appliedControlDifferentBranch": "Applied control belongs to another branch than the assessment",
"riskAcceptanceDifferentBranch": "Risk acceptance belongs to another branch than the risk assessment",
"evidenceDifferentBranch": "Evidence belongs to another branch than the assessment",
"assetDifferentBranch": "Asset belongs to another branch than the assessment",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Critical: Duplicate JSON key libraryFilteringLabels detected.

The key libraryFilteringLabels is defined at both line 3057 and line 3073. In JSON, only the last definition is used, causing line 3057's value to be ignored at runtime. This duplicate should be removed.

🔎 Proposed fix
-	"libraryFilteringLabels": "Library labels",
 	"requirementAssessmentDifferentBranch": "Requirement assessment belongs to another branch than the audit",
 	"appliedControlDifferentBranch": "Applied control belongs to another branch than the assessment",
 	"riskAcceptanceDifferentBranch": "Risk acceptance belongs to another branch than the risk assessment",
 	"evidenceDifferentBranch": "Evidence belongs to another branch than the assessment",
 	"assetDifferentBranch": "Asset belongs to another branch than the assessment",
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"libraryFilteringLabels": "Library labels",
"requirementAssessmentDifferentBranch": "Requirement assessment belongs to another branch than the audit",
"appliedControlDifferentBranch": "Applied control belongs to another branch than the assessment",
"riskAcceptanceDifferentBranch": "Risk acceptance belongs to another branch than the risk assessment",
"evidenceDifferentBranch": "Evidence belongs to another branch than the assessment",
"assetDifferentBranch": "Asset belongs to another branch than the assessment",
"requirementAssessmentDifferentBranch": "Requirement assessment belongs to another branch than the audit",
"appliedControlDifferentBranch": "Applied control belongs to another branch than the assessment",
"riskAcceptanceDifferentBranch": "Risk acceptance belongs to another branch than the risk assessment",
"evidenceDifferentBranch": "Evidence belongs to another branch than the assessment",
"assetDifferentBranch": "Asset belongs to another branch than the assessment",
🧰 Tools
🪛 Biome (2.1.2)

[error] 3057-3058: The key libraryFilteringLabels was already declared.

This where a duplicated key was declared again.

If a key is defined multiple times, only the last definition takes effect. Previous definitions are ignored.

(lint/suspicious/noDuplicateObjectKeys)

🤖 Prompt for AI Agents
In frontend/messages/en.json around lines 3057 to 3062 (duplicate key also
appears at ~3073), the JSON key "libraryFilteringLabels" is defined twice which
causes the earlier value (line 3057) to be ignored at runtime; remove the
duplicate entry so the key is only defined once (keep the intended/accurate
value — typically the later one at ~3073) or merge/update the values if needed,
then validate the JSON to ensure no other duplicate keys remain.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
backend/core/serializers.py (1)

2887-2979: Transaction wrapping and folder cascade are correctly implemented.

The two transaction blocks handle different scenarios appropriately:

  1. Status changes require additional logic (FlowEvent creation, lock management)
  2. Non-status changes only need folder cascade

Both paths maintain atomicity and properly cascade folder changes to FlowEvent records.

💡 Optional: Consider extracting the folder cascade logic to reduce duplication

The folder cascade logic appears in both transaction blocks (lines 2964-2967 and 2974-2977). While this is minor, you could extract it to a helper method:

def _cascade_folder_to_events(self, validation_flow, old_folder_id):
    """Cascade folder changes to related FlowEvent records"""
    if old_folder_id != validation_flow.folder_id:
        FlowEvent.objects.filter(validation_flow=validation_flow).update(
            folder=validation_flow.folder
        )

Then call it in both places:

self._cascade_folder_to_events(updated_instance, old_folder_id)

This is entirely optional and not blocking.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8cce162 and 691fc7a.

📒 Files selected for processing (4)
  • backend/core/serializers.py
  • frontend/src/lib/components/Forms/ModelForm/EntityForm.svelte
  • frontend/src/lib/components/Forms/ModelForm/PerimeterForm.svelte
  • frontend/src/lib/components/ModelTable/ModelTable.svelte
🚧 Files skipped from review as they are similar to previous changes (1)
  • frontend/src/lib/components/Forms/ModelForm/EntityForm.svelte
🧰 Additional context used
🧠 Learnings (3)
📚 Learning: 2025-12-29T15:52:09.213Z
Learnt from: melinoix
Repo: intuitem/ciso-assistant-community PR: 3107
File: frontend/src/routes/(app)/(internal)/recap/+page.svelte:49-51
Timestamp: 2025-12-29T15:52:09.213Z
Learning: In the recap page (frontend/src/routes/(app)/(internal)/recap/+page.svelte), perimeter.folder is guaranteed to be present when perimeter.compliance_assessments exist, so null safety checks are not necessary for perimeter.folder.str in that context.

Applied to files:

  • frontend/src/lib/components/Forms/ModelForm/PerimeterForm.svelte
📚 Learning: 2025-11-17T11:09:18.488Z
Learnt from: monsieurswag
Repo: intuitem/ciso-assistant-community PR: 2860
File: frontend/src/routes/(app)/(internal)/[model=urlmodel]/[id=uuid]/+page.server.ts:0-0
Timestamp: 2025-11-17T11:09:18.488Z
Learning: In frontend/src/routes/(app)/(internal)/[model=urlmodel]/[id=uuid]/+page.server.ts, the select action performs batch PATCH operations in Promise.all without transaction semantics. Partial failures (e.g., one object fails to update due to race conditions) are acceptable and should not trigger a fail() return, as most objects will still be updated successfully. Console warnings are sufficient for logging failures.

Applied to files:

  • frontend/src/lib/components/ModelTable/ModelTable.svelte
📚 Learning: 2025-09-19T09:08:52.306Z
Learnt from: Mohamed-Hacene
Repo: intuitem/ciso-assistant-community PR: 2550
File: backend/core/migrations/0098_evidence_lifecycle_data_migration.py:21-28
Timestamp: 2025-09-19T09:08:52.306Z
Learning: EvidenceRevision.save() method automatically sets is_published from the parent Evidence object and handles folder assignment from the evidence's folder with proper safety checks.

Applied to files:

  • backend/core/serializers.py
🧬 Code graph analysis (1)
backend/core/serializers.py (2)
backend/privacy/serializers.py (1)
  • update (123-140)
backend/core/models.py (4)
  • update (1209-1221)
  • EvidenceRevision (3817-3886)
  • TimelineEntry (3985-4044)
  • TaskNode (7553-7617)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (9)
  • GitHub Check: test (3.12)
  • GitHub Check: build_community_frontend
  • GitHub Check: build_enterprise_frontend
  • GitHub Check: enterprise-startup-functional-test (3.12)
  • GitHub Check: startup-docker-compose-test
  • GitHub Check: startup-functional-test (3.12)
  • GitHub Check: enterprise-startup-docker-compose-test
  • GitHub Check: build (3.12)
  • GitHub Check: Analyze (python)
🔇 Additional comments (10)
frontend/src/lib/components/Forms/ModelForm/PerimeterForm.svelte (1)

26-35: LGTM! Domain field now visible in read-only mode for existing perimeters.

The change from hidden to disabled correctly implements the PR requirement that perimeter objects should have the domain field visible in read-only mode rather than hidden. When editing an existing perimeter (initialData.folder is truthy), the field is now disabled (read-only), and when creating a new perimeter, it remains enabled.

This improves UX by always showing the domain context while preventing unintended modifications to existing perimeters.

frontend/src/lib/components/ModelTable/ModelTable.svelte (2)

271-271: LGTM!

The updated preventDelete logic correctly prevents deletion of builtin/urn items except for terminologies, which aligns with the domain-change functionality introduced in this PR.


724-726: Confirm intentionality of edit/delete permission asymmetry for entities.

Entities with builtin or urn metadata can be edited (lines 724-726: editURL is enabled when URLModel === 'entities' overrides the builtin/urn check), but cannot be deleted (line 271: preventDelete blocks deletion for builtin || urn when URLModel !== 'terminologies'). Since URLModel === 'entities' satisfies this condition, deletion is prevented. This asymmetry appears intentional, but please confirm it aligns with the domain-change requirements in the PR objectives.

backend/core/serializers.py (7)

5-5: Transaction import successfully added! ✅

This addresses the critical issue from the previous review where transaction.atomic() and transaction.on_commit() calls would have raised NameError.


76-88: Good defensive check for builtin objects.

This validation correctly prevents builtin objects from changing folders, which maintains referential integrity for system-provided objects.


272-280: Perimeter folder-change prevention is correctly implemented.

This enforces the constraint that a Perimeter's domain (folder) should not be changed after creation, which aligns with the PR objectives stating that perimeter domain should be read-only.


1587-1604: Transaction wrapping and folder cascade are correctly implemented.

The atomic transaction ensures that Evidence updates and EvidenceRevision folder propagation happen together or not at all. The folder cascade logic correctly maintains consistency between Evidence and its revisions.


1870-1903: Excellent use of transactional updates with deferred notifications! ✨

This implementation correctly:

  1. Wraps the update and folder cascade in an atomic transaction
  2. Uses transaction.on_commit() to defer notifications until after the transaction successfully commits
  3. Prevents notification spam if the transaction rolls back

The pattern of deferring side effects (like email notifications) until after transaction commit is a best practice for maintaining data consistency and avoiding misleading notifications.


2554-2562: Transaction wrapping and folder cascade are correctly implemented.

The atomic transaction ensures that Incident updates and TimelineEntry folder propagation happen atomically, maintaining consistency between the incident and its timeline entries.


2670-2689: Transaction wrapping and folder cascade are correctly implemented.

The atomic transaction ensures that TaskTemplate updates, TaskNode synchronization, and folder propagation to TaskNode records happen atomically. Notifications are correctly sent after the transaction commits.

Comment on lines +724 to +726
editURL={!(row.meta.builtin || row.meta.urn) ||
URLModel === 'terminologies' ||
URLModel === 'entities'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Add optional chaining to prevent potential runtime errors.

The editURL calculation accesses row.meta.builtin and row.meta.urn without optional chaining, but Line 271 uses row?.meta?.builtin. If row.meta is undefined, this could cause a runtime exception.

🔎 Proposed fix
-editURL={!(row.meta.builtin || row.meta.urn) ||
+editURL={!(row.meta?.builtin || row.meta?.urn) ||
 URLModel === 'terminologies' ||
 URLModel === 'entities'
🤖 Prompt for AI Agents
In frontend/src/lib/components/ModelTable/ModelTable.svelte around lines 724 to
726, the expression accesses row.meta.builtin and row.meta.urn directly which
can throw if row.meta is undefined; update the expression to use optional
chaining (e.g. row?.meta?.builtin and row?.meta?.urn) so it mirrors the safe
check used elsewhere, ensuring the logical conditions and parentheses remain the
same.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Search for entity form definitions and domain field configurations
find frontend/src/lib/components -name "*.svelte" | xargs grep -l "entities.*edit\|entity.*form\|domain.*field" 2>/dev/null | head -20

Repository: intuitem/ciso-assistant-community

Length of output: 217


🏁 Script executed:

#!/bin/bash
# Search for domain field configurations in forms, particularly for entities
rg -n "domain" frontend/src/lib/components -t svelte -B3 -A3 | grep -i "entity\|entities\|edit" | head -40

Repository: intuitem/ciso-assistant-community

Length of output: 107


🏁 Script executed:

#!/bin/bash
# Find any edit form components for entities or models
fd -e svelte "edit|form" frontend/src/lib/components --type f | head -20

Repository: intuitem/ciso-assistant-community

Length of output: 1450


🏁 Script executed:

#!/bin/bash
# Look for model/entity form or editor components
fd -e svelte . frontend/src/lib/components --type f | xargs grep -l "readOnly.*domain\|domain.*readOnly\|disabled.*domain\|domain.*disabled" 2>/dev/null

Repository: intuitem/ciso-assistant-community

Length of output: 61


🏁 Script executed:

#!/bin/bash
# Find EntityForm component
fd -e svelte EntityForm frontend/src/lib/components --type f

Repository: intuitem/ciso-assistant-community

Length of output: 138


🏁 Script executed:

#!/bin/bash
# Search for domain field in form components - use grep instead of rg type
grep -r "domain" frontend/src/lib/components/Forms --include="*.svelte" | grep -i "entity\|readOnly\|disabled" | head -30

Repository: intuitem/ciso-assistant-community

Length of output: 158


🏁 Script executed:

#!/bin/bash
# Examine EntityAssessmentForm to see if it has domain field handling
cat frontend/src/lib/components/Forms/ModelForm/EntityAssessmentForm.svelte | head -100

Repository: intuitem/ciso-assistant-community

Length of output: 2881


🏁 Script executed:

#!/bin/bash
# Search for any domain field definitions in forms
grep -r "domain" frontend/src/lib/components/Forms/ModelForm --include="*.svelte" -B2 -A2 | head -60

Repository: intuitem/ciso-assistant-community

Length of output: 4651


🏁 Script executed:

#!/bin/bash
# View the complete EntityForm.svelte to inspect domain field configuration
cat frontend/src/lib/components/Forms/ModelForm/EntityForm.svelte

Repository: intuitem/ciso-assistant-community

Length of output: 8199


🏁 Script executed:

#!/bin/bash
# Search for domain field with context in EntityForm
grep -n "domain" frontend/src/lib/components/Forms/ModelForm/EntityForm.svelte -B5 -A5

Repository: intuitem/ciso-assistant-community

Length of output: 382


Add urn check to domain field restriction in EntityForm.svelte.

The domain field (folder) in EntityForm.svelte is only disabled for builtin entities (disabled={object.builtin}), but the PR allows editing of both builtin and urn entities. To ensure the domain remains read-only for main entities as stated in the PR objectives, the domain field must also be disabled for urn entities. Update line 76 to: disabled={object.builtin || object.urn}

🤖 Prompt for AI Agents
In frontend/src/lib/components/EntityForm.svelte around line 76, the domain
(folder) field is currently only disabled for builtin entities; update the
disabled prop to also check object.urn so the field is read-only for both
builtin and urn entities (i.e., change the condition to include object.urn).
Ensure the expression uses logical OR between object.builtin and object.urn and
that tests/consumers still treat urn entities as non-editable for the domain
field.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants