Skip to content

IRIS Extension for interactive Webhook execution #801

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 98 commits into
base: develop
Choose a base branch
from

Conversation

yukh1402
Copy link

@yukh1402 yukh1402 commented May 5, 2025

We’ve developed a webhook-based integration feature that enables IRIS to send structured requests to external tools and display responses in a visually intuitive format. More details are outlined in the following document:

IRIS_Documentation_Final.docx

Summary by CodeRabbit

  • New Features

    • Introduced comprehensive webhook management, including UI for adding, editing, uploading, and deleting webhooks, with detailed validation and JSON schema support.
    • Added support for triggers and actions in case templates, enabling automated workflows via webhooks.
    • Implemented execution of triggers and actions with asynchronous HTTP requests and real-time response tracking.
    • Enhanced case and task management with new UI elements for viewing and executing webhook actions and visualizing responses.
    • Added new REST API endpoints for managing case triggers, task actions, and webhook responses.
    • Expanded navigation menus to include direct access to webhooks and triggers.
  • Improvements

    • Improved validation and error messaging for case templates, triggers, actions, and webhooks.
    • Updated templates and UI for better clarity, modularity, and user experience, including new modal dialogs and responsive tables.
    • Refined permission system to include webhook-specific access controls.
  • Bug Fixes

    • Addressed cleanup of related task responses when deleting tasks to prevent orphaned data.
  • Documentation

    • Updated example JSON structures and in-app documentation to reflect new triggers and actions fields.
  • Style

    • Enhanced code and template formatting for consistency and readability.
    • Added new CSS for improved modal presentation.
  • Chores

    • Added new dependencies for debugging and functionality support.

Mahamkeyani and others added 30 commits October 17, 2024 13:24
Mahamkeyani and others added 14 commits January 22, 2025 14:27
# Conflicts:
#	docker-compose.dev.yml
#	docker-compose.yml
#	source/app/blueprints/case/case_routes.py
#	source/app/blueprints/case/case_tasks_routes.py
#	source/app/blueprints/pages/case/case_triggers_routes.py
#	source/app/blueprints/pages/case/templates/case-nav_actions.html
#	source/app/blueprints/pages/case/templates/case.html
#	source/app/blueprints/pages/case/templates/case_triggers.html
#	source/app/blueprints/pages/case/templates/modal_add_case_task.html
#	source/app/blueprints/pages/manage/manage_webhooks_routes.py
#	source/app/blueprints/pages/manage/templates/manage_webhooks.html
#	source/app/blueprints/pages/manage/templates/modal_upload_webhook.html
#	source/app/blueprints/pages/manage/templates/modal_webhook.html
#	source/app/blueprints/rest/api_routes.py
#	source/app/blueprints/rest/manage/manage_case_templates_routes.py
#	source/app/blueprints/rest/manage/manage_cases_routes.py
#	source/app/business/cases.py
#	source/app/datamgmt/case/case_tasks_db.py
#	source/app/datamgmt/manage/manage_case_templates_db.py
#	source/app/datamgmt/manage/manage_cases_db.py
#	source/app/models/cases.py
#	source/app/views.py
#	ui/src/pages/case.taskaction.response.js
#	ui/src/pages/case.tasks.js
#	ui/src/pages/case.triggers.js
#	ui/src/pages/manage.case.templates.js
#	ui/src/pages/manage.webhooks.js
adding case_template_id column in cases table
Remove Trigger Id , Case Id and Created By from Case Triggers listing
Copy link

coderabbitai bot commented May 5, 2025

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

🗂️ Base branches to auto review (1)
  • api_*

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

This update introduces a comprehensive webhook integration framework into the application. It adds new database models, schemas, and RESTful endpoints for managing webhooks, task and case responses, and their interactions with cases and tasks. The case template system is extended to support actions and triggers linked to webhooks, enabling automated execution of webhooks during case and task lifecycles. The UI is enhanced with new pages and modals for managing webhooks, viewing and executing task actions, and visualizing trigger responses. Supporting backend logic includes validation, asynchronous execution, and result persistence for webhook calls. Numerous templates and JavaScript files are updated or added to support these features.

Changes

File(s) Change Summary
docker-compose.base.yml, docker-compose.yml, docker/nginx/entrypoint.sh, docker/nginx/nginx.conf, docker/webApp/Dockerfile Docker configuration updates: environment file for DB, local image builds, ports, CSP header expansion, and script shebang adjustment.
source/app/blueprints/case/case_routes.py, source/app/blueprints/case/case_tasks_routes.py, source/app/blueprints/pages/case/case_triggers_routes.py New Flask blueprints for case management, tasks, and triggers: routes for viewing, editing, and synchronizing case/task data, triggers, activities, and real-time features.
source/app/blueprints/pages/case/templates/case-nav.html, .../case/templates/case-nav_actions.html, .../case/templates/case.html, .../case/templates/case_tasks.html, .../case/templates/case_triggers.html, .../case/templates/modal_add_case_task.html UI templates updated/added for navigation, actions, triggers, task actions, and modals with improved formatting and new functionality for actions and triggers.
source/app/blueprints/pages/manage/manage_case_templates_routes.py, .../manage_webhooks_routes.py Backend routes for managing case templates and webhooks, including modal rendering, CRUD operations, validation, and error handling.
source/app/blueprints/pages/manage/templates/manage_webhooks.html, .../modal_case_template.html, .../modal_upload_case_template.html, .../modal_upload_webhook.html, .../modal_webhook.html New and updated templates for webhook and case template management, including modals for editing, uploading, and viewing JSON schema details.
source/app/blueprints/rest/api_routes.py, .../rest/case/case_tasks_routes.py, .../rest/manage/manage_case_templates_routes.py, .../rest/manage/manage_cases_routes.py REST API endpoints for ping/version, case/task action responses, triggers execution, and improved error messages in template creation.
source/app/business/cases.py, source/app/datamgmt/case/case_tasks_db.py Internal logic for case/task creation/deletion: trigger execution, response persistence, and cleanup for new models.
source/app/datamgmt/manage/manage_case_response_db.py, .../manage_case_templates_db.py, .../manage_cases_db.py, .../manage_task_response_db.py, .../manage_webhooks_db.py New modules and extended logic for managing case/task responses, webhooks, validation, and execution.
source/app/forms.py Added WebhookForm for webhook JSON input.
source/app/models/authorization.py, .../cases.py, .../models.py Models updated: new permissions, new/extended models for webhook, task/case responses, and extended case/case template fields.
source/app/post_init.py Post-initialization now enables pgcrypto extension and logs migrations.
source/app/schema/marshables.py Added Marshmallow schemas for new models and extended existing schemas for webhooks, actions, triggers, and input parameters.
source/app/static/assets/css/webhooks/webhooks.css New CSS for webhook modals and backdrops.
source/app/templates/includes/sidenav.html Sidebar updated with "Webhooks" menu under "Manage" for administrators.
source/app/views.py Registered new blueprints for webhooks and triggers.
source/requirements.txt Added Debugpy package.
ui/src/pages/case.taskaction.response.js, .../case.tasks.js, .../case.triggers.js, .../manage.case.templates.js, .../manage.webhooks.js New and updated JS for managing webhooks, task actions, triggers, DataTables, JSON editors, and UI enhancements.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant UI
    participant FlaskApp
    participant DB
    participant Webhook

    User->>UI: Initiate case creation (select template)
    UI->>FlaskApp: POST /api/cases (with case_template_id)
    FlaskApp->>DB: Create new case
    FlaskApp->>DB: Fetch triggers for case_template_id
    alt Triggers exist
        loop For each trigger
            FlaskApp->>Webhook: POST trigger payload (async)
            Webhook-->>FlaskApp: JSON response
            FlaskApp->>DB: Save CaseResponse
        end
    else No triggers
        FlaskApp-->>User: Error (no triggers)
    end
    FlaskApp-->>UI: Success response (case created)
    UI-->>User: Show case details
Loading
sequenceDiagram
    participant User
    participant UI
    participant FlaskApp
    participant DB
    participant Webhook

    User->>UI: Execute task action (select action)
    UI->>FlaskApp: POST /case/jsoneditor (payload, task_id, action_id)
    FlaskApp->>DB: Fetch webhook for action_id
    FlaskApp->>Webhook: POST action payload
    Webhook-->>FlaskApp: JSON response
    FlaskApp->>DB: Save TaskResponse
    FlaskApp-->>UI: Success response (action result)
    UI-->>User: Display action result (JSON viewer)
Loading

Poem

In burrows deep, where webhooks leap,
The rabbits code, no time for sleep!
Triggers fire and actions run,
JSON flows and tasks are spun.
New menus bloom, the tables dance,
With modals bright, we seize the chance—
To automate, connect, and cheer,
For webhooks’ magic now is here!

((\
( -.-)
o_(")(")


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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@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: 59

🔭 Outside diff range comments (4)
docker-compose.base.yml (1)

29-36: ⚠️ Potential issue

Avoid overriding .env values by dropping the bare “environment” list or converting it to key-value pairs

Compose treats

environment:
  - POSTGRES_USER

as “pass the current shell value (or empty string)”, while env_file loads defaults from .env.
If a variable is missing from the host environment, the empty value silently shadows the value coming from .env, yielding blank credentials at container start-up.

Either:

-    environment:
-      - POSTGRES_USER
-      - POSTGRES_PASSWORD
-      ...
-    env_file:
-      - .env
+    env_file: .env              # keep a single source of truth

or specify explicit fall-backs:

environment:
  POSTGRES_USER: ${POSTGRES_USER:-iris}
  POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-changeme}

Right now the mix of both sections is brittle and may break local setups or CI pipelines.

source/app/blueprints/rest/api_routes.py (1)

36-41: ⚠️ Potential issue

Same duplication for /api/versions – ensure a single decorator

The twin decorators will likewise crash the app with “endpoint api_version already registered”.

-@rest_api_blueprint.route('/api/versions', methods=['GET'])
 @rest_api_blueprint.route('/api/versions', methods=['GET'])

Delete the redundant line.

source/app/blueprints/rest/manage/manage_case_templates_routes.py (1)

300-305: 🛠️ Refactor suggestion

Complete exception handling in the add_case_template route

There are commented-out exception handling blocks, which suggests incomplete implementation.

        case_template = CaseTemplate(**case_template_data)
        db.session.add(case_template)

        # Commit the template to generate an ID
        db.session.commit()

    except Exception as e:
        print("Error adding case template:", e)
        db.session.rollback()
+       # Log the exception
+       import logging
+       logging.error(f"Error adding case template: {e}")
        return response_error("Could not add case template into DB", data=str(e))
source/app/models/models.py (1)

255-277: 💡 Verification agent

🧩 Analysis chain

Changing CaseTemplate table name breaks all FKs & queries

Renaming to case_templatessss without a migration will break existing installations and any code using 'case_templates'.
Confirm this is intentional and, if so, accompany it with:

  1. Alembic migration renaming the table.
  2. Search/replace across the codebase.

Otherwise revert.


I’ll check for references to both the old and new table names and any migration renames.


🏁 Script executed:

#!/bin/bash
echo "=== References to 'case_templates' ==="
rg -n "case_templates" || echo "None found"
echo
echo "=== References to 'case_templatessss' ==="
rg -n "case_templatessss" || echo "None found"
echo
echo "=== 'rename_table' usages (migrations) ==="
rg -n "rename_table" || echo "None found"

Length of output: 8619


Add Alembic migration or revert accidental table rename

The CaseTemplate model’s __tablename__ was changed to 'case_templatessss' (source/app/models/models.py:256), but there are no corresponding Alembic migrations to rename the existing case_templates table. This will break all foreign‐keys and any raw or ORM queries still targeting case_templates.

Please address by either:

  • Writing a new Alembic migration that renames the table from case_templates to case_templatessss, and updating any raw SQL/tests referencing the old name.
  • Or reverting the __tablename__ change back to case_templates if the rename was unintended.
🧹 Nitpick comments (52)
source/app/forms.py (1)

114-116: Consider enhancing the webhook form with additional validation.

The current implementation only validates that JSON is provided but doesn't perform any structure validation. Consider implementing custom validators to ensure the webhook JSON adheres to expected schema before submission.

class WebhookForm(FlaskForm):
-    webhook_json = TextAreaField(u'Webhook JSON', validators=[DataRequired()])
+    webhook_json = TextAreaField(u'Webhook JSON', validators=[
+        DataRequired(),
+        # Add custom validator for JSON structure
+    ])
+    
+    def validate_webhook_json(self, field):
+        """Validate that the JSON has the required fields and structure"""
+        try:
+            data = json.loads(field.data)
+            # Validate required fields
+            required_fields = ['name', 'url', 'method']
+            for req_field in required_fields:
+                if req_field not in data:
+                    raise ValidationError(f"Missing required field: {req_field}")
+        except json.JSONDecodeError:
+            raise ValidationError("Invalid JSON format")
docker/webApp/Dockerfile (1)

86-87: Commented-out chmod Command

This commented-out line appears to be an attempt to consolidate the chmod commands but is not active.

Either remove this commented-out line or uncomment it and remove the individual chmod commands that follow if you intended to use this consolidated approach.

source/app/datamgmt/case/case_tasks_db.py (1)

310-310: Extra Blank Line

There's an extra blank line at line 310 that could be removed.

Consider removing this extra blank line to maintain consistent spacing in the file.

source/app/models/cases.py (2)

23-23: Unused import detected

The Table class is imported but not used in this file.

-# IMPORTS ------------------------------------------------
-from sqlalchemy import BigInteger, Table, CheckConstraint
+# IMPORTS ------------------------------------------------
+from sqlalchemy import BigInteger, CheckConstraint
🧰 Tools
🪛 Ruff (0.8.2)

23-23: sqlalchemy.Table imported but unused

Remove unused import: sqlalchemy.Table

(F401)


46-46: Unused import detected

The Base class is imported but not used in this file.

-from app.models.models import Client, Base
+from app.models.models import Client
🧰 Tools
🪛 Ruff (0.8.2)

46-46: app.models.models.Base imported but unused

Remove unused import: app.models.models.Base

(F401)

source/app/blueprints/pages/manage/templates/modal_upload_webhook.html (1)

51-73: JSON example is helpful but could be improved

The JSON example provides a good structure, but it contains an inconsistency in the payload_schema structure. The "properties" field is defined as an array but should likely be an object according to JSON Schema standards.

                                            "payload_schema": [
                                            {
                                                "type": "object",
-                                                "properties": [
+                                                "properties": {
                                                    {
                                                        "property": "property name",
                                                        "type": "type of data"
                                                    }
-                                                ]
+                                                }
                                            }
                                        ],
docker/nginx/nginx.conf (1)

29-33: Consider adding integrity hashes for external scripts

When loading scripts from external CDNs, consider adding integrity hashes (SRI) in your HTML to ensure the delivered content hasn't been tampered with. While this isn't directly in the nginx.conf, it's an important security consideration for the frontend implementation.

source/app/business/cases.py (2)

25-25: Remove unused imports.

The imports jsonify, current_app, and g from Flask are not used in this file.

-from flask import jsonify, current_app, g
+from flask import current_app
🧰 Tools
🪛 Ruff (0.8.2)

25-25: flask.jsonify imported but unused

Remove unused import

(F401)


25-25: flask.current_app imported but unused

Remove unused import

(F401)


25-25: flask.g imported but unused

Remove unused import

(F401)


122-127: Replace print statement with proper logging.

Using a print statement for debugging is not ideal in production code. Consider using Flask's logger instead.

def execute_trigger_with_context(trigger):
    # Make sure each thread runs within the Flask app context
    with app.app_context():
-        print(f"Trigger executed for case: {trigger}")
+        log.info(f"Executing trigger for case: {case.case_id}")
        execute_and_save_trigger(trigger, case.case_id)
source/app/blueprints/pages/manage/manage_case_templates_routes.py (2)

136-138: Use clearer placeholder values for webhook IDs.

The current placeholder "Webhook Id" might be confused with an actual ID. Consider using a value that clearly indicates it's a placeholder.

{
-    "webhook_id": "Webhook Id",
-    "display_name": "Trigger Name",
+    "webhook_id": "<enter_webhook_id_here>",
+    "display_name": "<enter_trigger_name_here>",
    "input_params": {
    }
}

117-119: Use clearer placeholder values for action webhook IDs.

Similar to the triggers, the placeholder "Webhook Id" in actions might be confused with an actual ID.

{
-    "webhook_id": "Webhook Id",
-    "display_name": "Action Name"
+    "webhook_id": "<enter_webhook_id_here>",
+    "display_name": "<enter_action_name_here>"
}
ui/src/pages/case.taskaction.response.js (3)

12-14: Consider adding user-facing error messages.

Currently, errors are only logged to the console. Consider showing user-friendly error messages in the UI.

.then(data => {
    if (data.status && data.data) {
        const responseData = data.data.body;
        
        const jsonCrackEmbed = document.getElementById("jsoncrackIframe");
        const jsonViewerContainer = $('#jsonViewerContainer');

        jsonViewerContainer.slideDown();
+    } else {
+        // Show user-friendly error message
+        toastr.error("Failed to load response data. Please try again later.");
+        console.error("Failed to fetch action response data:", data);
+    }
-    } else {
-        console.error("Failed to fetch action response data:", data);
    }
})
.catch(error => {
+    toastr.error("Error loading response data. Please try again later.");
    console.error("Error fetching task action response:", error);
});

21-28: Use optional chaining as recommended by static analysis.

The code accesses iframe's contentWindow without checking if it exists first.

-if (jsonCrackEmbed && jsonCrackEmbed.contentWindow) {
+if (jsonCrackEmbed?.contentWindow) {
    const options = {
        theme: "light", 
        direction: "DOWN", 
    };

    jsonCrackEmbed.contentWindow.postMessage({ json: JSON.stringify(responseData), options }, "*");
🧰 Tools
🪛 Biome (1.9.4)

[error] 21-21: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


1-43: Add a loading indicator during fetch operations.

The code doesn't show a loading state while fetching data, which might confuse users if the operation takes time.

function toggleJsonViewer(button) {
    const responseId = button.getAttribute('data-task');
    let id = parseInt(responseId);
    if (isNaN(id)) {
        console.error("Invalid task ID:", responseId);
        return;  
    }

+    // Show loading indicator
+    const jsonViewerContainer = $('#jsonViewerContainer');
+    jsonViewerContainer.html('<div class="d-flex justify-content-center mt-3"><div class="spinner-border" role="status"><span class="sr-only">Loading...</span></div></div>');
+    jsonViewerContainer.slideDown();

    fetch(`/case/tasks/action_response/${id}`)
        .then(response => response.json())
        .then(data => {
            if (data.status && data.data) {
                const responseData = data.data.body;

                const jsonCrackEmbed = document.getElementById("jsoncrackIframe");
-               const jsonViewerContainer = $('#jsonViewerContainer');

-               jsonViewerContainer.slideDown();
+               // Replace loading indicator with content
+               jsonViewerContainer.html('<div class="json-viewer-content"></div><button class="btn btn-sm btn-secondary" data-dismiss="collapse-frame">Close</button>');

                if (jsonCrackEmbed?.contentWindow) {
                    const options = {
                        theme: "light", 
                        direction: "DOWN", 
                    };

                    jsonCrackEmbed.contentWindow.postMessage({ json: JSON.stringify(responseData), options }, "*");
                } else {
                    console.error("jsonCrackEmbed iframe is not available or not loaded.");
+                   jsonViewerContainer.html('<div class="alert alert-danger">Failed to load JSON viewer. Please refresh the page and try again.</div>');
                }
            } else {
                console.error("Failed to fetch action response data:", data);
+               jsonViewerContainer.html('<div class="alert alert-danger">Failed to load response data. Please try again later.</div>');
            }
        })
        .catch(error => {
            console.error("Error fetching task action response:", error);
+           jsonViewerContainer.html('<div class="alert alert-danger">Error loading response data. Please try again later.</div>');
        });

    // Attach event handler for the close button
    $('#jsonViewerContainer .btn[data-dismiss="collapse-frame"]').off('click').on('click', function () {
        $('#jsonViewerContainer').slideUp(); // Hide the container
    });
}
🧰 Tools
🪛 Biome (1.9.4)

[error] 21-21: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

source/app/blueprints/rest/manage/manage_cases_routes.py (1)

289-292: Consider managing thread lifecycle.

Starting threads without tracking them can lead to resource issues. Consider using a ThreadPoolExecutor to manage threads or keeping track of threads for proper cleanup.

+# Use a thread pool to manage concurrent execution with a reasonable maximum
+thread_pool = ThreadPoolExecutor(max_workers=5)

# Start a new thread for each trigger
for trigger in triggers:
-    thread = Thread(target=execute_trigger_with_context, args=(trigger,))
-    thread.start()
+    thread_pool.submit(execute_trigger_with_context, trigger)

This approach would require adding back the ThreadPoolExecutor import that was removed earlier.

source/app/blueprints/pages/manage/templates/manage_webhooks.html (3)

27-32: Decide on the Upload Webhook feature.

The Upload Webhook button is commented out. Either remove it completely if the feature isn't needed, or uncomment it if the feature is implemented.

If the upload feature is implemented and should be available:

-              <!-- <button
-                class="btn btn-sm btn-dark float-right ml-2"
-                onclick="fire_upload_webhook();"
-              >
-                Upload Webhook
-              </button> -->
+              <button
+                class="btn btn-sm btn-dark float-right ml-2"
+                onclick="fire_upload_webhook();"
+              >
+                Upload Webhook
+              </button>

Or if it's not needed, remove it completely.


46-61: Consider adding more columns to the webhooks table.

The current table has minimal information (ID, Name, URL). Consider adding columns for description, status, last used, or action buttons for easier management.

<table
  aria-describedby="Webhook list"
  class="table display table table-striped table-hover"
  style="width: 100%; border-spacing: 0"
  id="webhooks_table"
>
  <thead>
    <tr>
      <th scope="col">#ID</th>
      <th scope="col">Name</th>
      <th scope="col">URL</th>
+      <th scope="col">Description</th>
+      <th scope="col">Last Used</th>
+      <th scope="col">Actions</th>
    </tr>
  </thead>
  <tfoot>
    <tr>
      <th scope="col">#ID</th>
      <th scope="col">Name</th>
      <th scope="col">URL</th>
+      <th scope="col">Description</th>
+      <th scope="col">Last Used</th>
+      <th scope="col">Actions</th>
    </tr>
  </tfoot>
</table>

1-3: Add title to body element.

The <title> element is empty in the template. Add a descriptive title.

{% extends "layouts/default.html" %} {% block title %} Manage Webhooks {%
-endblock title %} {% block stylesheets %} {% endblock stylesheets %} {% block
+endblock title %} 
+{% block stylesheets %}
+<link rel="stylesheet" href="/static/assets/css/webhooks/webhooks.css">
+{% endblock stylesheets %} {% block
ui/src/pages/case.triggers.js (1)

65-82: Remove commented code and improve data rendering.

Clean up commented code and enhance the data table with improved handling of response data.

columns: [
    { data: "id" },
-    // { data: "trigger" },
-    // { data: "case" },
    {
        data: "body",
        render: function (data, type, row) {
            const task = row.id;
+            // For non-display types, return raw data
+            if (type !== 'display') return data;
+            
+            // Check if data is valid JSON for display
+            try {
+                // Try to parse it to validate
+                JSON.parse(data);
+            } catch (e) {
+                return '<span class="text-danger">Invalid response data</span>';
+            }
+            
            return `
                <button type="button" class="btn btn-primary btn-sm view-response"
                    data-task='${task}'
                    data-response='${data}' >
                    View Response
                </button>
            `;
        },
    },
source/app/blueprints/pages/manage/templates/modal_upload_case_template.html (1)

109-114: Add validation guidance for webhook identifiers.

It would be helpful to include guidance on how to specify the webhook_id in triggers.

    "triggers": [
        {
            "webhook_id": "Webhook Id",
            "display_name": "Trigger Name",
-        }
+        }
+    ],
+    "_comment": "webhook_id should reference the ID of an existing webhook in the system"
    ]
}
source/app/blueprints/pages/case/templates/case_triggers.html (2)

37-41: Commented-out table columns indicate unfinished implementation

There are several commented-out table columns (Trigger Id, Case Id, Created By) which might indicate incomplete implementation or debugging code that was left in the final version.

Either remove these commented sections entirely or implement the columns if they're needed for the complete functionality.


86-87: Bootstrap library loaded from external CDN

Loading the Bootstrap bundle from an external CDN (stackpath.bootstrapcdn.com) creates an external dependency that could cause issues if the CDN is unavailable.

Consider hosting the Bootstrap bundle locally to improve reliability and eliminate external dependencies:

-<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.bundle.min.js"></script>
+<script src="/static/assets/js/plugin/bootstrap/bootstrap.bundle.min.js"></script>
source/app/blueprints/pages/case/templates/case.html (1)

430-435: JavaScript dependencies for new webhook and trigger functionality

The addition of case.triggers.js and loading of Bootstrap 4.5.2 from an external CDN supports the new webhook-based functionality.

Consider hosting the Bootstrap bundle locally rather than from an external CDN to improve reliability and reduce external dependencies:

-<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.bundle.min.js"></script>
+<script src="/static/assets/js/plugin/bootstrap/bootstrap.bundle.min.js"></script>
source/app/blueprints/pages/manage/templates/modal_webhook.html (1)

101-153: JSON schema example needs application-specific guidance

The JSON example provided is generic and doesn't offer specific guidance about how webhooks should be structured in this application.

Enhance the example with:

  1. Real-world examples specific to this application
  2. Common webhook patterns for the intended integrations
  3. Best practices for header authentication and payload structure

Additionally, consider adding inline validation and a schema validator to help users create valid webhook configurations more easily.

source/app/datamgmt/manage/manage_case_response_db.py (1)

83-101: Code style improvements for validation function

The validation function has nested if statements that could be simplified, as flagged by static analysis.

Simplify the conditionals for better readability:

    try:
        if not update:
-            # Check required fields for creation
-            if "case" not in data:
+            # Check required fields for creation
+            if "case" not in data:
                return "Case ID is required."

        # Check if trigger is provided and valid
        if "trigger" in data and not isinstance(data["trigger"], int):
            return "Trigger must be an integer."

        # Validate the body field if provided
-        if "body" in data:
-            if not isinstance(data["body"], dict):
+        if "body" in data and not isinstance(data["body"], dict):
                return "Body must be a dictionary."
        
        # If all validations pass, return None
        return None
    except Exception as e:
        return str(e)
🧰 Tools
🪛 Ruff (0.8.2)

83-85: Use a single if statement instead of nested if statements

(SIM102)


93-94: Use a single if statement instead of nested if statements

Combine if statements using and

(SIM102)

source/app/blueprints/pages/case/case_triggers_routes.py (2)

18-19: Fix redirect parameter inconsistency

The original request uses url_redir but the redirect URL uses redirect=True, which could cause redirect issues.

    if url_redir:
-        return redirect(url_for('case_triggers.case_triggers', cid=caseid, redirect=True))
+        return redirect(url_for('case_triggers.case_triggers', cid=caseid, url_redir=False))

52-55: Use a proper logger instead of print statement

For better debugging and log management, replace the print statement with a proper logger.

    except Exception as e:
        # Log the exception for debugging (optional: use a logger instead of print)
-        print(f"Error processing case triggers: {e}")
+        import logging
+        logging.error(f"Error processing case triggers: {e}")
        return jsonify({"success": False, "error": str(e)}), 500
source/app/datamgmt/manage/manage_task_response_db.py (3)

18-24: Remove unused imports

Several imports are not used in this module:

  • datetime from datetime
  • Union from typing
  • db from app
  • TaskResponseSchema from app.schema.marshables
from datetime import datetime
-from typing import List, Optional, Union
+from typing import List, Optional

-from app import db
from app.models.models import TaskResponse
from app.models.authorization import User
-from app.schema.marshables import TaskResponseSchema
🧰 Tools
🪛 Ruff (0.8.2)

18-18: datetime.datetime imported but unused

Remove unused import: datetime.datetime

(F401)


19-19: typing.Union imported but unused

Remove unused import: typing.Union

(F401)


21-21: app.db imported but unused

Remove unused import: app.db

(F401)


24-24: app.schema.marshables.TaskResponseSchema imported but unused

Remove unused import: app.schema.marshables.TaskResponseSchema

(F401)


45-56: Remove debugging print statement

There's a debugging print("in here") statement that should be removed in production code.

def get_task_response_by_id(response_id: int) -> TaskResponse:
    """Get a specific task response by ID.

    Args:
        response_id (int): Task response ID.

    Returns:
        TaskResponse: Task response object.
    """
    task_response = TaskResponse.query.filter_by(id=response_id).first()
-    print("in here")
    return task_response

78-92: Simplify nested if statements

These nested if statements can be simplified using logical operators.

    try:
        if not update:
            # Check required fields for creation
-            if "task" not in data:
-                return "Task ID is required."
+            if "task" not in data:
+                return "Task ID is required."

        # Check if action is provided and valid
        if "action" in data and not isinstance(data["action"], int):
            return "Action must be an integer."

        # Validate the body field if provided
-        if "body" in data:
-            if not isinstance(data["body"], dict):
-                return "Body must be a dictionary."
+        if "body" in data and not isinstance(data["body"], dict):
+            return "Body must be a dictionary."
🧰 Tools
🪛 Ruff (0.8.2)

79-81: Use a single if statement instead of nested if statements

(SIM102)


89-90: Use a single if statement instead of nested if statements

Combine if statements using and

(SIM102)

source/app/blueprints/rest/manage/manage_case_templates_routes.py (1)

33-33: Remove unused imports

The imports AddAssetForm and WebhookForm are not used in this file.

-from app.forms import AddAssetForm, WebhookForm, CaseTemplateForm
+from app.forms import CaseTemplateForm
🧰 Tools
🪛 Ruff (0.8.2)

33-33: app.forms.AddAssetForm imported but unused

Remove unused import

(F401)


33-33: app.forms.WebhookForm imported but unused

Remove unused import

(F401)

source/app/blueprints/pages/case/templates/modal_add_case_task.html (1)

229-234: External CDN dependencies may affect reliability.

The implementation relies on several external CDN dependencies (jsoneditor, tailwindcss). Consider downloading and serving these libraries locally to improve reliability and offline functionality.

-<script src="https://cdn.jsdelivr.net/npm/@json-editor/json-editor@latest/dist/jsoneditor.min.js"></script>
+<script src="/static/assets/js/plugin/jsoneditor/jsoneditor.min.js"></script>
-<link href="https://cdn.jsdelivr.net/npm/tailwindcss@latest/dist/tailwind.min.css" rel="stylesheet">
+<link href="/static/assets/css/tailwind.min.css" rel="stylesheet">
source/app/datamgmt/manage/manage_cases_db.py (3)

22-24: Remove unused imports to keep the module clean

desc and asc are imported but never used, and Ruff rightfully flags them.
Leaving dead imports hinders readability and may hide genuine circular-import problems later.

-from sqlalchemy import and_, desc, asc
+from sqlalchemy import and_
🧰 Tools
🪛 Ruff (0.8.2)

23-23: sqlalchemy.desc imported but unused

Remove unused import

(F401)


23-23: sqlalchemy.asc imported but unused

Remove unused import

(F401)


585-599: Docstring & column names don’t match – could mis-lead maintainers

The docstring speaks of case_template_id, yet the function parameter is case_id; the CaseResponse columns are called case and trigger, which are vague and collide with reserved words.

Recommend:

  1. Fix docstring.
  2. Rename columns to case_id, trigger_id (requires alembic migration).

611-659: Timeouts and JSON-content check are good – add TLS-verify & logging like the trigger helper

Same remarks as for execute_and_save_trigger() apply here.
Additionally, if results is large, serialising straight into a single TaskResponse row may hit Postgres’ 1 GB JSONB limit or make querying painful. Consider splitting or truncating.

source/app/models/models.py (1)

250-253: update_from_dict is too permissive

Blindly doing setattr(self, field, value) invites mass-assignment bugs (e.g., someone passes created_by_user_id).
Whitelist allowed fields or use pydantic / marshmallow for safe loading.

ui/src/pages/case.tasks.js (2)

334-336: Remove stray debugger; statement

Leaving a debugger freezes browsers in dev-tools mode and leaks into production builds.

-    debugger;
🧰 Tools
🪛 Biome (1.9.4)

[error] 334-334: This is an unexpected use of the debugger statement.

Unsafe fix: Remove debugger statement

(lint/suspicious/noDebugger)


184-239: Handle fetch/network errors consistently & cancel dropdown rebuild

If fetch(webHooksurl) fails the dropdown is replaced with a disabled item, but the promise chain ignores JSON parse errors (e.g., 204 No Content). Wrap response.json() in return response.text().then(t=>JSON.parse(t||'{}')) or add another catch.

Minor, yet improves UX.

source/app/blueprints/pages/manage/manage_webhooks_routes.py (3)

1-10: Remove unused imports to keep module lean and silence Ruff warnings

abort, jsonify, and ac_requires are imported but never used. Removing them tightens the module’s footprint and resolves Ruff F401.

-from flask import Blueprint, abort, jsonify
+from flask import Blueprint
...
-from app.blueprints.access_controls import ac_api_requires, ac_requires, ac_requires_case_identifier
+from app.blueprints.access_controls import ac_api_requires, ac_requires_case_identifier
🧰 Tools
🪛 Ruff (0.8.2)

3-3: flask.abort imported but unused

Remove unused import

(F401)


3-3: flask.jsonify imported but unused

Remove unused import

(F401)


226-266: Use HTTP-verb semantics & track activity when updating webhooks

update_webhook is registered as POST, yet it semantically updates an entity. A PUT (full replacement) or PATCH (partial) is clearer to API consumers. Additionally, unlike add_webhook & delete_webhook, no activity is tracked.

-@manage_webhooks_blueprint.route('/manage/webhooks/update/<int:cur_id>', methods=['POST'])
+@manage_webhooks_blueprint.route('/manage/webhooks/<int:cur_id>', methods=['PUT'])
...
 db.session.commit()
+# Track update in activity log
+track_activity(f"webhook '{webhook.name}' updated", ctx_less=True)

268-283: Prefer DELETE verb for deletions

The route currently accepts POST. Switching to DELETE better matches REST conventions and makes front-end intent explicit.

-@manage_webhooks_blueprint.route('/manage/webhooks/delete/<int:webhook_id>', methods=['POST'])
+@manage_webhooks_blueprint.route('/manage/webhooks/<int:webhook_id>', methods=['DELETE'])
ui/src/pages/manage.webhooks.js (2)

416-465: fileData leaked to global scope

fileData is assigned without let/const, creating an implicit global variable and risking collisions.

- fileData = e.target.result;
+ const fileData = e.target.result;

224-268: Duplicate DataTable initialisation may clash

The file initialises both #case_webhooks_table (via initializeDataTable) and #webhooks_table directly. If both tables are not present simultaneously, that’s fine; otherwise, IDs collide and memory leaks occur. Consider consolidating or using distinct IDs.

source/app/datamgmt/manage/manage_case_templates_db.py (2)

18-21: Remove unused imports to keep the module lean

requests and jsonschema are never referenced in this file (Ruff F401).
Deleting them avoids unnecessary dependencies.

-import requests
-import jsonschema
🧰 Tools
🪛 Ruff (0.8.2)

18-18: requests imported but unused

Remove unused import: requests

(F401)


19-19: jsonschema imported but unused

Remove unused import: jsonschema

(F401)


383-399: Return type annotation misleading

get_triggers_by_case_template_id returns a list, not a CaseTemplate object.
Adjusting the signature improves IDE support:

-def get_triggers_by_case_template_id(case_template_id) -> CaseTemplate:
+from typing import List, Dict
+
+def get_triggers_by_case_template_id(case_template_id) -> List[Dict]:
source/app/blueprints/case/case_routes.py (3)

60-63: Remove unused import to silence lint and shorten load-time

ac_fast_check_current_user_has_case_access is imported but never used.

-from app.iris_engine.access_control.utils import ac_get_all_access_level, ac_fast_check_current_user_has_case_access, \
-    ac_fast_check_user_has_case_access
+from app.iris_engine.access_control.utils import ac_get_all_access_level, \
+    ac_fast_check_user_has_case_access
🧰 Tools
🪛 Ruff (0.8.2)

60-60: app.iris_engine.access_control.utils.ac_fast_check_current_user_has_case_access imported but unused

Remove unused import: app.iris_engine.access_control.utils.ac_fast_check_current_user_has_case_access

(F401)


228-233: Boolean comparison to True reduces readability

Ruff E712: use the value directly.

-        UserActivity.display_in_ui == True
+        UserActivity.display_in_ui
🧰 Tools
🪛 Ruff (0.8.2)

230-230: Avoid equality comparisons to True; use if UserActivity.display_in_ui: for truth checks

Replace with UserActivity.display_in_ui

(E712)


104-118: setattr for constants offers no safety and hides typos

Direct assignment is simpler and clearer:

-    setattr(case, 'case_tags', get_case_tags(caseid))
+    case.case_tags = get_case_tags(caseid)
 ...
-    setattr(case, 'status_name', CaseStatus(case.status_id).name.replace('_', ' ').title())
+    case.status_name = CaseStatus(case.status_id).name.replace('_', ' ').title()
🧰 Tools
🪛 Ruff (0.8.2)

104-104: Do not call setattr with a constant attribute value. It is not any safer than normal property access.

Replace setattr with assignment

(B010)


117-117: Do not call setattr with a constant attribute value. It is not any safer than normal property access.

Replace setattr with assignment

(B010)

source/app/blueprints/case/case_tasks_routes.py (4)

55-61: Prune unused imports to avoid lint noise and wasted memory
Permissions and ac_api_requires are never referenced. They trigger Ruff F401 and slightly slow module import.

-from app.models.authorization import CaseAccessLevel, Permissions
+from app.models.authorization import CaseAccessLevel
...
-from app.util import ac_api_case_requires, ac_api_requires
+from app.util import ac_api_case_requires
🧰 Tools
🪛 Ruff (0.8.2)

55-55: app.models.authorization.Permissions imported but unused

Remove unused import: app.models.authorization.Permissions

(F401)


60-60: app.util.ac_api_requires imported but unused

Remove unused import: app.util.ac_api_requires

(F401)


110-119: Micro-cleanup: compress guard clause
Very minor readability tweak – replace the if/else block with a ternary assignment as Ruff suggests.

-    if not ct:
-        output = []
-    else:
-        output = ct
+    output = [] if not ct else ct
🧰 Tools
🪛 Ruff (0.8.2)

115-118: Use ternary operator output = [] if not ct else ct instead of if-else-block

Replace if-else-block with output = [] if not ct else ct

(SIM108)


240-257: Remove unused variable and simplify body-serialization
e is assigned but never used (Ruff F841). Also, double json.dumps will wrap strings in quotes.

-                try:
-                    taskActionResponse['body'] = json.dumps(taskActionResponse['body'])
-                except (TypeError, ValueError) as e:
-                    taskActionResponse['body'] = str(taskActionResponse['body'])
+                try:
+                    taskActionResponse['body'] = json.dumps(
+                        taskActionResponse['body'],
+                        ensure_ascii=False,
+                        indent=2
+                    )
+                except (TypeError, ValueError):
+                    taskActionResponse['body'] = str(taskActionResponse['body'])
🧰 Tools
🪛 Ruff (0.8.2)

255-255: Local variable e is assigned to but never used

Remove assignment to unused variable e

(F841)


138-155: Race-condition potential when multiple clients update the same task status
update_task_status() is called without checking the task’s task_last_update. Two simultaneous requests could overwrite each other silently. Consider optimistic locking (compare-and-swap on task_last_update) or database-level constraints.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a4bfeda and 018dfbc.

📒 Files selected for processing (47)
  • docker-compose.base.yml (1 hunks)
  • docker-compose.yml (1 hunks)
  • docker/nginx/entrypoint.sh (1 hunks)
  • docker/nginx/nginx.conf (1 hunks)
  • docker/webApp/Dockerfile (1 hunks)
  • source/app/blueprints/case/case_routes.py (1 hunks)
  • source/app/blueprints/case/case_tasks_routes.py (1 hunks)
  • source/app/blueprints/pages/case/case_triggers_routes.py (1 hunks)
  • source/app/blueprints/pages/case/templates/case-nav.html (1 hunks)
  • source/app/blueprints/pages/case/templates/case-nav_actions.html (1 hunks)
  • source/app/blueprints/pages/case/templates/case.html (6 hunks)
  • source/app/blueprints/pages/case/templates/case_tasks.html (2 hunks)
  • source/app/blueprints/pages/case/templates/case_triggers.html (1 hunks)
  • source/app/blueprints/pages/case/templates/modal_add_case_task.html (3 hunks)
  • source/app/blueprints/pages/manage/manage_case_templates_routes.py (2 hunks)
  • source/app/blueprints/pages/manage/manage_webhooks_routes.py (1 hunks)
  • source/app/blueprints/pages/manage/templates/manage_webhooks.html (1 hunks)
  • source/app/blueprints/pages/manage/templates/modal_case_template.html (1 hunks)
  • source/app/blueprints/pages/manage/templates/modal_upload_case_template.html (3 hunks)
  • source/app/blueprints/pages/manage/templates/modal_upload_webhook.html (1 hunks)
  • source/app/blueprints/pages/manage/templates/modal_webhook.html (1 hunks)
  • source/app/blueprints/rest/api_routes.py (1 hunks)
  • source/app/blueprints/rest/case/case_tasks_routes.py (3 hunks)
  • source/app/blueprints/rest/manage/manage_case_templates_routes.py (3 hunks)
  • source/app/blueprints/rest/manage/manage_cases_routes.py (3 hunks)
  • source/app/business/cases.py (4 hunks)
  • source/app/datamgmt/case/case_tasks_db.py (2 hunks)
  • source/app/datamgmt/manage/manage_case_response_db.py (1 hunks)
  • source/app/datamgmt/manage/manage_case_templates_db.py (4 hunks)
  • source/app/datamgmt/manage/manage_cases_db.py (3 hunks)
  • source/app/datamgmt/manage/manage_task_response_db.py (1 hunks)
  • source/app/datamgmt/manage/manage_webhooks_db.py (1 hunks)
  • source/app/forms.py (1 hunks)
  • source/app/models/authorization.py (1 hunks)
  • source/app/models/cases.py (6 hunks)
  • source/app/models/models.py (2 hunks)
  • source/app/post_init.py (2 hunks)
  • source/app/schema/marshables.py (3 hunks)
  • source/app/static/assets/css/webhooks/webhooks.css (1 hunks)
  • source/app/templates/includes/sidenav.html (1 hunks)
  • source/app/views.py (4 hunks)
  • source/requirements.txt (1 hunks)
  • ui/src/pages/case.taskaction.response.js (1 hunks)
  • ui/src/pages/case.tasks.js (5 hunks)
  • ui/src/pages/case.triggers.js (1 hunks)
  • ui/src/pages/manage.case.templates.js (1 hunks)
  • ui/src/pages/manage.webhooks.js (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (7)
source/app/blueprints/rest/api_routes.py (2)
source/app/blueprints/access_controls.py (1)
  • ac_api_requires (319-337)
source/app/blueprints/responses.py (1)
  • response_success (57-63)
source/app/models/cases.py (1)
source/app/models/models.py (1)
  • Client (128-140)
source/app/business/cases.py (3)
source/app/datamgmt/manage/manage_case_templates_db.py (1)
  • get_triggers_by_case_template_id (383-398)
source/app/datamgmt/manage/manage_cases_db.py (1)
  • execute_and_save_trigger (548-582)
source/app/business/errors.py (1)
  • BusinessProcessingError (21-31)
source/app/post_init.py (1)
source/app/configuration.py (1)
  • Config (266-491)
source/app/datamgmt/manage/manage_case_response_db.py (2)
source/app/models/models.py (1)
  • CaseResponse (311-333)
source/app/schema/marshables.py (1)
  • CaseResponseSchema (818-834)
source/app/datamgmt/case/case_tasks_db.py (1)
source/app/models/models.py (5)
  • TaskResponse (287-309)
  • TaskAssignee (768-778)
  • TaskComments (868-876)
  • Comments (839-854)
  • CaseTasks (712-735)
source/app/datamgmt/manage/manage_task_response_db.py (2)
source/app/models/models.py (1)
  • TaskResponse (287-309)
source/app/schema/marshables.py (1)
  • TaskResponseSchema (800-816)
🪛 Ruff (0.8.2)
source/app/models/cases.py

23-23: sqlalchemy.Table imported but unused

Remove unused import: sqlalchemy.Table

(F401)


46-46: app.models.models.Base imported but unused

Remove unused import: app.models.models.Base

(F401)

source/app/blueprints/rest/manage/manage_cases_routes.py

17-17: typing.Union imported but unused

Remove unused import: typing.Union

(F401)


22-22: concurrent.futures.ThreadPoolExecutor imported but unused

Remove unused import: concurrent.futures.ThreadPoolExecutor

(F401)


23-23: flask.jsonify imported but unused

Remove unused import

(F401)


23-23: flask.g imported but unused

Remove unused import

(F401)


27-27: flask.redirect imported but unused

Remove unused import: flask.redirect

(F401)


28-28: flask.render_template imported but unused

Remove unused import: flask.render_template

(F401)


30-30: flask.url_for imported but unused

Remove unused import: flask.url_for

(F401)


32-32: flask_wtf.FlaskForm imported but unused

Remove unused import: flask_wtf.FlaskForm

(F401)


41-41: app.datamgmt.client.client_db.get_client_list imported but unused

Remove unused import: app.datamgmt.client.client_db.get_client_list

(F401)


44-44: app.datamgmt.manage.manage_attribute_db.get_default_custom_attributes imported but unused

Remove unused import: app.datamgmt.manage.manage_attribute_db.get_default_custom_attributes

(F401)


45-45: app.datamgmt.manage.manage_case_classifications_db.get_case_classifications_list imported but unused

Remove unused import: app.datamgmt.manage.manage_case_classifications_db.get_case_classifications_list

(F401)


46-46: app.datamgmt.manage.manage_case_state_db.get_case_states_list imported but unused

Remove unused import: app.datamgmt.manage.manage_case_state_db.get_case_states_list

(F401)


47-47: app.datamgmt.manage.manage_case_templates_db.get_case_templates_list imported but unused

Remove unused import: app.datamgmt.manage.manage_case_templates_db.get_case_templates_list

(F401)


51-51: app.datamgmt.manage.manage_cases_db.get_case_protagonists imported but unused

Remove unused import: app.datamgmt.manage.manage_cases_db.get_case_protagonists

(F401)


54-54: app.datamgmt.manage.manage_common.get_severities_list imported but unused

Remove unused import: app.datamgmt.manage.manage_common.get_severities_list

(F401)


55-55: app.forms.AddCaseForm imported but unused

Remove unused import: app.forms.AddCaseForm

(F401)


57-57: app.iris_engine.access_control.utils.ac_current_user_has_permission imported but unused

Remove unused import: app.iris_engine.access_control.utils.ac_current_user_has_permission

(F401)

source/app/business/cases.py

25-25: flask.jsonify imported but unused

Remove unused import

(F401)


25-25: flask.current_app imported but unused

Remove unused import

(F401)


25-25: flask.g imported but unused

Remove unused import

(F401)

source/app/datamgmt/manage/manage_case_response_db.py

18-18: datetime.datetime imported but unused

Remove unused import: datetime.datetime

(F401)


19-19: typing.Union imported but unused

Remove unused import: typing.Union

(F401)


21-21: app.db imported but unused

Remove unused import: app.db

(F401)


24-24: app.schema.marshables.CaseResponseSchema imported but unused

Remove unused import: app.schema.marshables.CaseResponseSchema

(F401)


83-85: Use a single if statement instead of nested if statements

(SIM102)


93-94: Use a single if statement instead of nested if statements

Combine if statements using and

(SIM102)

source/app/datamgmt/manage/manage_task_response_db.py

18-18: datetime.datetime imported but unused

Remove unused import: datetime.datetime

(F401)


19-19: typing.Union imported but unused

Remove unused import: typing.Union

(F401)


21-21: app.db imported but unused

Remove unused import: app.db

(F401)


24-24: app.schema.marshables.TaskResponseSchema imported but unused

Remove unused import: app.schema.marshables.TaskResponseSchema

(F401)


79-81: Use a single if statement instead of nested if statements

(SIM102)


89-90: Use a single if statement instead of nested if statements

Combine if statements using and

(SIM102)

source/app/blueprints/rest/manage/manage_case_templates_routes.py

33-33: app.forms.AddAssetForm imported but unused

Remove unused import

(F401)


33-33: app.forms.WebhookForm imported but unused

Remove unused import

(F401)

source/app/blueprints/pages/manage/manage_webhooks_routes.py

3-3: flask.abort imported but unused

Remove unused import

(F401)


3-3: flask.jsonify imported but unused

Remove unused import

(F401)


12-12: app.blueprints.access_controls.ac_requires imported but unused

Remove unused import: app.blueprints.access_controls.ac_requires

(F401)


53-53: Local variable case_id is assigned to but never used

Remove assignment to unused variable case_id

(F841)

source/app/datamgmt/manage/manage_case_templates_db.py

18-18: requests imported but unused

Remove unused import: requests

(F401)


19-19: jsonschema imported but unused

Remove unused import: jsonschema

(F401)


84-85: Use a single if statement instead of nested if statements

(SIM102)


418-419: Use a single if statement instead of nested if statements

Combine if statements using and

(SIM102)

source/app/datamgmt/manage/manage_cases_db.py

23-23: sqlalchemy.desc imported but unused

Remove unused import

(F401)


23-23: sqlalchemy.asc imported but unused

Remove unused import

(F401)

source/app/blueprints/case/case_routes.py

60-60: app.iris_engine.access_control.utils.ac_fast_check_current_user_has_case_access imported but unused

Remove unused import: app.iris_engine.access_control.utils.ac_fast_check_current_user_has_case_access

(F401)


104-104: Do not call setattr with a constant attribute value. It is not any safer than normal property access.

Replace setattr with assignment

(B010)


117-117: Do not call setattr with a constant attribute value. It is not any safer than normal property access.

Replace setattr with assignment

(B010)


173-173: Redefinition of unused socket_summary_onchange from line 157

(F811)


230-230: Avoid equality comparisons to True; use if UserActivity.display_in_ui: for truth checks

Replace with UserActivity.display_in_ui

(E712)

source/app/datamgmt/manage/manage_webhooks_db.py

80-80: Redefinition of unused jsonschema from line 18

Remove definition: jsonschema

(F811)


84-84: Redefinition of unused jsonschema from line 80

Remove definition: jsonschema

(F811)


85-85: Redefinition of unused Optional from line 81

Remove definition: Optional

(F811)


90-91: Use a single if statement instead of nested if statements

(SIM102)

source/app/blueprints/case/case_tasks_routes.py

55-55: app.models.authorization.Permissions imported but unused

Remove unused import: app.models.authorization.Permissions

(F401)


60-60: app.util.ac_api_requires imported but unused

Remove unused import: app.util.ac_api_requires

(F401)


115-118: Use ternary operator output = [] if not ct else ct instead of if-else-block

Replace if-else-block with output = [] if not ct else ct

(SIM108)


207-207: Undefined name get_task_with_assignees

(F821)


224-224: Undefined name get_task_with_assignees

(F821)


255-255: Local variable e is assigned to but never used

Remove assignment to unused variable e

(F841)


280-280: Undefined name get_task_with_assignees

(F821)


323-323: Undefined name get_task_with_assignees

(F821)

🪛 Biome (1.9.4)
ui/src/pages/case.taskaction.response.js

[error] 21-21: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

ui/src/pages/case.triggers.js

[error] 12-12: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


[error] 29-29: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

ui/src/pages/case.tasks.js

[error] 334-334: This is an unexpected use of the debugger statement.

Unsafe fix: Remove debugger statement

(lint/suspicious/noDebugger)

🔇 Additional comments (50)
docker/nginx/entrypoint.sh (1)

1-1:

✅ Verification successful

Consider the portability implications of changing the shebang line.

Changing from #!/usr/bin/env bash to #!/bin/bash reduces portability as it assumes bash is installed at a specific location. While this likely works fine in your Docker container, #!/usr/bin/env bash is generally more portable as it uses the environment to locate bash.


🏁 Script executed:

#!/bin/bash
# Verify if bash exists at the specified path in the container
if [ -f /bin/bash ]; then
  echo "✅ /bin/bash exists in the container"
else
  echo "❌ /bin/bash doesn't exist in the container - this might cause issues"
fi

Length of output: 103


No portability issue detected – /bin/bash exists in this container

The verification script confirms that /bin/bash is present, so the script will execute as expected in your Docker environment. If this script is ever run outside of this controlled image or on a different base, consider switching back to:

#!/usr/bin/env bash

for maximum portability.

source/app/blueprints/pages/case/templates/case-nav.html (1)

43-47: LGTM! The Triggers navigation item aligns with new webhook functionality.

The new navigation item provides a clear access point to the webhook trigger functionality from the case view.

source/app/templates/includes/sidenav.html (1)

136-143: LGTM! Appropriate permission check for webhook management.

The new UI entry point for webhook management is correctly secured with the server_administrator permission, ensuring only administrators can access this functionality.

source/app/views.py (4)

51-51: New Webhooks Management Blueprint Imported

The code imports a new blueprint for managing webhooks, which aligns with the PR objective of adding webhook-based integration features.


99-99: Case Triggers Blueprint Imported

The code imports a new blueprint for case triggers functionality, which is part of the webhook integration system being added.


154-154: Webhook Management Blueprint Registration

The code registers the webhooks management blueprint with the Flask application, enabling access to the webhook management routes.


181-181: Case Triggers Blueprint Registration

The code registers the case triggers blueprint with the Flask application, enabling access to case trigger functionality.

source/app/static/assets/css/webhooks/webhooks.css (3)

1-6: Modal Dialog Positioning

The CSS properly centers modal dialogs using flexbox, providing a consistent UI experience for the new webhook modals.


8-11: Modal Width Limitation

The CSS sets an appropriate maximum width for modal dialogs, preventing them from stretching too wide on large screens.


13-21: Modal Backdrop Configuration

The CSS properly configures the modal backdrop to cover the entire viewport with a semi-transparent background, creating visual separation between the modal and page content.

docker/webApp/Dockerfile (1)

90-90: Commented-out ENTRYPOINT

The ENTRYPOINT directive remains commented out, suggesting it's being preserved for reference but not currently used.

source/app/datamgmt/case/case_tasks_db.py (4)

36-36: TaskResponse Model Import

The import of the TaskResponse model is correctly added to support the new webhook integration functionality.


281-282: Improved Deletion Process Documentation

The added comments clearly document the deletion process sequence, enhancing code maintainability.

Also applies to: 286-287, 295-296, 298-299, 301-305


301-303: TaskResponse Cleanup Implementation

The code properly implements deletion of TaskResponse records associated with tasks being deleted, maintaining database referential integrity.


302-302:

✅ Verification successful

Verify TaskResponse Reference Field Name

Ensure the TaskResponse model's field name 'task' matches the actual field in the model definition for accurate filtering.


🏁 Script executed:

#!/bin/bash
# Confirm the TaskResponse model field name for task reference
grep -A 25 "class TaskResponse" source/app/models/models.py | grep "task ="

Length of output: 136


TaskResponse Field Name Confirmed

The TaskResponse model defines

task = Column(Integer, db.ForeignKey('case_tasks.id'))

so using TaskResponse.task == task_id for filtering is accurate. No changes required.

source/app/post_init.py (2)

140-141: Adding pgcrypto extension supports webhook functionality

The addition of the pgcrypto PostgreSQL extension is a good enhancement as it provides cryptographic functions that are likely needed for secure webhook implementation, such as generating secure tokens or encrypting sensitive data in webhooks.


154-158: Minor logging improvements

The updated log messages provide slightly better clarity about the database migration process, which is helpful for troubleshooting.

source/app/models/cases.py (2)

89-89: Case template integration for webhook feature

The addition of case_template_id is a good design choice. This allows cases to be linked to templates that can define webhook triggers and actions, supporting the new webhook-based integration feature.


101-102: Constructor properly updated with new parameter

The case template ID parameter has been correctly added to the constructor signature and properly assigned to the instance variable.

Also applies to: 120-120

source/app/blueprints/pages/manage/templates/modal_upload_webhook.html (1)

1-120: Well-structured modal for webhook upload

The modal provides a clean interface for uploading webhook definitions with appropriate guidance:

  • Clear explanations about webhook purpose and structure
  • Helpful example JSON format that demonstrates the expected structure
  • Proper form validation and file type restriction
  • Error handling with detailed logs display capability

This is a good UI implementation that supports the webhook integration feature.

docker/nginx/nginx.conf (1)

29-33:

❓ Verification inconclusive

CSP header expansion for external resources

The Content-Security-Policy header has been expanded to allow external resources from trusted CDNs and services, which is necessary for the webhook visualization features. However, I recommend adding a verification step to ensure these domains are reliable and secure.


🏁 Script executed:

#!/bin/bash
# Verify the reputation and security of the newly added domains

echo "Checking reputation of added domains in CSP:"

# Check SSL Labs rating for cdn.jsdelivr.net
echo "Checking SSL Labs rating for cdn.jsdelivr.net:"
curl -s "https://api.ssllabs.com/api/v3/analyze?host=cdn.jsdelivr.net&publish=off&all=done" | grep "grade"

# Check SSL Labs rating for jsoncrack.com
echo "Checking SSL Labs rating for jsoncrack.com:"
curl -s "https://api.ssllabs.com/api/v3/analyze?host=jsoncrack.com&publish=off&all=done" | grep "grade"

# Check SSL Labs rating for stackpath.bootstrapcdn.com
echo "Checking SSL Labs rating for stackpath.bootstrapcdn.com:"
curl -s "https://api.ssllabs.com/api/v3/analyze?host=stackpath.bootstrapcdn.com&publish=off&all=done" | grep "grade"

Length of output: 749


Verify SSL grades for CSP domains

The SSL Labs API calls did not return any grades (analysis may still be pending). Please either poll the API until each report’s status is “READY” or manually confirm that the following domains achieve at least an A-grade rating before finalizing the CSP update:

  • File: docker/nginx/nginx.conf
    Lines: 29–33

Domains to verify:
• cdn.jsdelivr.net
• jsoncrack.com
• stackpath.bootstrapcdn.com

source/app/business/cases.py (4)

22-22: Good addition of threading support for asynchronous operations.

The addition of the Thread module is necessary for executing triggers asynchronously.


45-45: Good addition of trigger management import.

Adding the import for get_triggers_by_case_template_id supports the new trigger functionality.


49-49: Correct import for webhook execution.

The addition of execute_and_save_trigger import supports the webhook execution functionality.


83-88: Good refactoring to extract case template ID early.

Extracting the template ID earlier and assigning it directly to the case object improves code clarity.

source/app/blueprints/pages/manage/manage_case_templates_routes.py (4)

103-106: Improved tags formatting.

The new format for tags with each tag on its own line improves readability.


111-114: Consistent formatting for task tags.

The task tags now follow the same formatting pattern as the case tags, which maintains consistency.


115-120: Good addition of webhook actions for tasks.

Adding the actions array with webhook_id and display_name enables task-specific webhook actions.


134-141: Good implementation of triggers at the case level.

The addition of triggers with webhook_id, display_name, and input_params provides a clear structure for case-level webhook triggers.

ui/src/pages/case.taskaction.response.js (3)

1-7: Input validation correctly implemented.

Good implementation of input validation to check if the task ID is a valid number.


9-11: API endpoint fetch looks good.

The API endpoint fetch is properly implemented with error handling.


39-42: Good implementation of clean-up for event handler.

The event handler for the close button is properly implemented with off('click') to prevent duplicate handlers.

source/app/blueprints/pages/case/templates/case-nav_actions.html (1)

1-64: Secondary navigation header is well structured.

The secondary navigation header with action buttons is well organized with proper spacing and layout.

source/app/blueprints/pages/case/templates/case_triggers.html (1)

14-14:

✅ Verification successful

Form reference without clear context

The template includes {{ form.hidden_tag() }} but there's no clear form definition or explanation of what form is being referenced here.

Verify that the form object is being properly passed to the template and that it has the expected fields.


🏁 Script executed:

#!/bin/bash
# Check if the form object is being passed to the template
grep -r "render_template.*case_triggers\.html.*form" source/app/

Length of output: 194


Form context confirmed
The form object is correctly passed into the template from case_triggers_routes.py via

return render_template("case_triggers.html", case=case, form=form)

No further action is required here.

source/app/blueprints/pages/case/templates/case.html (2)

61-62: Improved visual representation of case status information

The reorganization of case status display (severity, customer, SOC ID) improves the UI by grouping related information together.

The layout changes to the case header information provide better organization and visual hierarchy.


238-344: Modal dialogs for report template selection

The modals for selecting report templates have been reformatted for better readability and consistent indentation.

The improved formatting makes the template code more maintainable while preserving the same functionality.

docker-compose.yml (3)

28-32: Consider security implications of binding ports to all interfaces.

The database service is now exposing port 5432 to all network interfaces (0.0.0.0). This configuration makes your PostgreSQL database accessible from any IP address that can route to your host, which could be a security risk in production environments.

For development environments, consider using 127.0.0.1:5432:5432 instead to limit connections to localhost only:

    build:
      context: docker/db
    image: iriswebapp_db:v2.4.7
    ports:
-      - "0.0.0.0:5432:5432"
+      - "127.0.0.1:5432:5432"

38-43: Consider security implications of binding webapp to all interfaces.

Similar to the database service, the web application is now exposed on port 8000 to all network interfaces. This could represent a security concern in production environments.

    build:
      context: .
      dockerfile: docker/webApp/Dockerfile
    image: iriswebapp_app:v2.4.7
    ports:
-      - "0.0.0.0:8000:8000"
+      - "127.0.0.1:8000:8000"

28-63:

❌ Incorrect review comment

Verify image version downgrade.

All services are now using version v2.4.7 instead of what appears to be a newer version (based on the AI summary). Ensure this change is intentional and that all services are compatible with this version.

Run this script to check the git history for the version change:


🏁 Script executed:

#!/bin/bash
# Description: Check the git history for changes to the version numbers in docker-compose.yml

# Grep for version number changes in previous commits
git log -p -- docker-compose.yml | grep -E "(v2.4.[0-9]+)"

Length of output: 0


Let’s run a more targeted diff to catch any downgrade in the latest commit:


🏁 Script executed:

#!/bin/bash
# Check the most recent commit for version changes in docker-compose.yml
git diff HEAD~1 HEAD -- docker-compose.yml | grep -E "^[+-].*image:.*:iriswebapp_.*:v2\.4\.[0-9]+"

Length of output: 100


docker-compose.yml: No image version changes detected

I reviewed the diff for docker-compose.yml and didn’t find any modifications to the image tags—services remain pinned at v2.4.7, with no downgrade introduced. This review comment can be safely ignored.

Likely an incorrect or invalid review comment.

ui/src/pages/manage.case.templates.js (3)

60-67: New actions and triggers autocomplete entries added.

The editor now includes autocomplete entries for webhook-related fields (action, triggers, input_params), properly supporting the new webhook integration feature.


82-93: Improved loading message during trigger execution.

The loading message now provides more context with "Executing triggers, please wait..." which improves user experience by setting proper expectations during webhook execution.


105-110: Enhanced error handling for data validation failures.

The error handling now provides more detailed feedback when validation fails, displaying specific error details in a list. This improves the debugging experience for template authors.

source/app/blueprints/pages/manage/templates/modal_case_template.html (5)

1-2: Improved template structure with proper blocks.

The template now properly uses Jinja block structure, which improves organization and maintainability.


81-85: Documentation for new webhook integration features.

The template documentation has been extended to include information about actions and triggers, which is essential for users to understand the new webhook functionality.


119-145: Example JSON updated with webhook integration.

The example JSON template has been updated to include the new webhook-related fields (actions, triggers, webhook_id, display_name, input_params), providing a clear reference for users.


151-190: Added webhooks list for better user visibility.

A new section displaying available webhooks has been added, making it easier for users to find and reference webhook IDs when creating templates.


233-239: Added required JavaScript dependencies for webhook functionality.

The template now includes necessary JavaScript dependencies for ACE editor, DataTables grouping, and webhook management, properly supporting the new features.

source/app/blueprints/pages/case/templates/modal_add_case_task.html (2)

108-177: New webhook action UI for task execution.

A comprehensive webhook action interface has been added to task modals, enabling users to select, configure, and execute webhook actions. The interface includes a dropdown for action selection, a JSON editor for payload configuration, and a table for response history.


169-175: Security concern with external iframe for JSON visualization.

The implementation uses an iframe to jsoncrack.com for visualizing JSON responses. This could potentially expose sensitive data to an external service.

Consider implementing a self-hosted solution or validating that no sensitive information is sent to the external service:

-                                <iframe id="jsoncrackIframe" src="https://jsoncrack.com/widget" frameborder="0" style="height: 50vh; width: 100%;"></iframe>
+                                <!-- Consider a self-hosted alternative or internal JSON viewer component -->
+                                <div id="localJsonViewer" style="height: 50vh; width: 100%;"></div>
source/app/datamgmt/manage/manage_webhooks_db.py (1)

144-156: Possible schema/DB mismatch for header-auth tasks

CaseTaskSchema typically expects fields such as task_title; supplying only header_auth_key and task_status_id may raise a validation error that is silently swallowed.
Consider adding required fields or verifying the target schema.

# unfortunately we are relying on a beta version here. I hope a definitive version gets released soon
graphql-server[flask]==3.0.0b7
graphene-sqlalchemy==3.0.0rc1
Debugpy
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Remove duplicate debugpy entry.
The second occurrence of Debugpy is redundant and may lead to confusion. Please remove this duplicate line.

- Debugpy
📝 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
Debugpy

@@ -37,9 +37,11 @@ graphene==3.3
qrcode[pil]==7.4.2
dictdiffer==0.2.0
oic==1.7.0
Debugpy
Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Pin debugpy with an explicit version for reproducibility.
Requirements files should lock down exact versions to prevent unexpected upgrades. Replace the unpinned entry and normalize the package name to lowercase. To discover the latest stable version, you can run:

Suggested diff (substitute <latest-version> with the value returned):

- Debugpy
+ debugpy==<latest-version>

🏁 Script executed:

#!/bin/bash
# Fetch latest debugpy version from PyPI
curl -s https://pypi.org/pypi/debugpy/json | python3 -c "import sys, json; print(json.load(sys.stdin)['info']['version'])"

Length of output: 143


Pin debugpy to v1.8.14 for reproducible installs

To prevent unexpected upgrades, lock debugpy to the exact, current stable release and use the lowercase package name.

• File: source/requirements.txt
• Line: 40

- Debugpy
+ debugpy==1.8.14
📝 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
Debugpy
debugpy==1.8.14

Comment on lines 100 to 107

{% block javascripts %}
{% include 'includes/footer_case.html' %}
<script>

const caseTemplateId = {{ case.case_template_id }}
console.log(caseTemplateId); // Check the value
</script>
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Escape server values when injecting into JavaScript

{{ case.case_template_id }} is rendered verbatim.
If this variable ever becomes non-numeric (e.g., UUID) or contains quotes, it will break the script or open the door to XSS.

Safer pattern:

-    const caseTemplateId = {{ case.case_template_id }}
+    const caseTemplateId = {{ case.case_template_id | tojson }};

The tojson filter serialises and escapes correctly for inline JS contexts.

📝 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
{% block javascripts %}
{% include 'includes/footer_case.html' %}
<script>
const caseTemplateId = {{ case.case_template_id }}
console.log(caseTemplateId); // Check the value
</script>
{% block javascripts %}
{% include 'includes/footer_case.html' %}
<script>
const caseTemplateId = {{ case.case_template_id | tojson }};
console.log(caseTemplateId); // Check the value
</script>
{% endblock %}

Comment on lines 28 to 33
@rest_api_blueprint.route('/api/ping', methods=['GET'])
# CONTENT ------------------------------------------------
@rest_api_blueprint.route('/api/ping', methods=['GET'])
@ac_api_requires()
def api_ping():
return response_success("pong")
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Duplicate decorator registers the same endpoint twice – Flask will raise AssertionError

@rest_api_blueprint.route('/api/ping') appears twice for api_ping.
During blueprint registration Flask detects the endpoint name api_ping already in use and aborts the startup.

Fix:

-@rest_api_blueprint.route('/api/ping', methods=['GET'])
 # CONTENT ------------------------------------------------
 @rest_api_blueprint.route('/api/ping', methods=['GET'])

Remove the first (or second) decorator; keep exactly one per route.

📝 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
@rest_api_blueprint.route('/api/ping', methods=['GET'])
# CONTENT ------------------------------------------------
@rest_api_blueprint.route('/api/ping', methods=['GET'])
@ac_api_requires()
def api_ping():
return response_success("pong")
# CONTENT ------------------------------------------------
@rest_api_blueprint.route('/api/ping', methods=['GET'])
@ac_api_requires()
def api_ping():
return response_success("pong")

Comment on lines 61 to 66
case_templates_read = 0x100
case_templates_write = 0x200

webhooks_read = 0x100
webhooks_write = 0x200

Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Permission bit masks collide with existing values

webhooks_read = 0x100 and webhooks_write = 0x200 reuse the exact bits of case_templates_read / case_templates_write.
Any role granted “case template” rights will automatically gain “webhook” rights (and vice-versa), defeating fine-grained access control.

Allocate the next free powers-of-two (after activities_write if it exists) e.g.:

-    webhooks_read = 0x100
-    webhooks_write = 0x200
+    webhooks_read = 0x1000
+    webhooks_write = 0x2000

Remember to run migrations and update any hard-coded masks in fixtures or tests.

📝 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
case_templates_read = 0x100
case_templates_write = 0x200
webhooks_read = 0x100
webhooks_write = 0x200
case_templates_read = 0x100
case_templates_write = 0x200
webhooks_read = 0x1000
webhooks_write = 0x2000

Comment on lines 400 to 424
def get_action_by_case_template_id_and_task_id(case_template_id, task_id, caseid) -> list:
"""
Retrieves the actions array for a given case_template_id and task_id.

:param case_template_id: The ID of the case template to look up.
:param task_id: The ID of the task to look up within the case template.
:return: A list of actions or an empty list if none are found.
"""

case_template = CaseTemplate.query.filter_by(id=case_template_id).first()
case_task = get_task(task_id=task_id)
if not case_template:
return []

actions = []

for task in case_template.tasks:

if case_task.task_title == task["title"]:
if 'actions' in task:

for action in task['actions']:
actions.append(dict(action))

return actions
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Indentation and nested if can cause a runtime IndentationError and extra complexity

Current snippet mixes 3-space and 4-space indents and nests two ifs that can be collapsed:

-        if case_task.task_title == task["title"]:
-           if 'actions' in task:
-
-            for action in task['actions']:
-                actions.append(dict(action))
+        if case_task.task_title == task["title"] and "actions" in task:
+            for action in task["actions"]:
+                actions.append(dict(action))

This fixes the indentation and satisfies Ruff SIM102.

📝 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
def get_action_by_case_template_id_and_task_id(case_template_id, task_id, caseid) -> list:
"""
Retrieves the actions array for a given case_template_id and task_id.
:param case_template_id: The ID of the case template to look up.
:param task_id: The ID of the task to look up within the case template.
:return: A list of actions or an empty list if none are found.
"""
case_template = CaseTemplate.query.filter_by(id=case_template_id).first()
case_task = get_task(task_id=task_id)
if not case_template:
return []
actions = []
for task in case_template.tasks:
if case_task.task_title == task["title"]:
if 'actions' in task:
for action in task['actions']:
actions.append(dict(action))
return actions
def get_action_by_case_template_id_and_task_id(case_template_id, task_id, caseid) -> list:
"""
Retrieves the actions array for a given case_template_id and task_id.
:param case_template_id: The ID of the case template to look up.
:param task_id: The ID of the task to look up within the case template.
:return: A list of actions or an empty list if none are found.
"""
case_template = CaseTemplate.query.filter_by(id=case_template_id).first()
case_task = get_task(task_id=task_id)
if not case_template:
return []
actions = []
for task in case_template.tasks:
if case_task.task_title == task["title"] and "actions" in task:
for action in task["actions"]:
actions.append(dict(action))
return actions
🧰 Tools
🪛 Ruff (0.8.2)

418-419: Use a single if statement instead of nested if statements

Combine if statements using and

(SIM102)

Comment on lines 171 to 178
@socket_io.on('clear_buffer')
@ac_socket_requires(CaseAccessLevel.full_access)
def socket_summary_onchange(message):

emit('clear_buffer', message)


@socket_io.on('join')
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Function name is re-defined – breaks “change” event listener

socket_summary_onchange is declared at line 157 and again at 173 for the clear_buffer event.
The second definition overwrites the first, leaving the “change” handler unregistered.

-@socket_io.on('clear_buffer')
-@ac_socket_requires(CaseAccessLevel.full_access)
-def socket_summary_onchange(message):
+@socket_io.on('clear_buffer')
+@ac_socket_requires(CaseAccessLevel.full_access)
+def socket_summary_clear_buffer(message):
📝 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
@socket_io.on('clear_buffer')
@ac_socket_requires(CaseAccessLevel.full_access)
def socket_summary_onchange(message):
emit('clear_buffer', message)
@socket_io.on('join')
@socket_io.on('clear_buffer')
@ac_socket_requires(CaseAccessLevel.full_access)
def socket_summary_clear_buffer(message):
emit('clear_buffer', message)
@socket_io.on('join')
🧰 Tools
🪛 Ruff (0.8.2)

173-173: Redefinition of unused socket_summary_onchange from line 157

(F811)

Comment on lines 320 to 335
@ac_api_case_requires(CaseAccessLevel.full_access)
def case_delete_task(cur_id, caseid):
call_modules_hook('on_preload_task_delete', data=cur_id, caseid=caseid)
task = get_task_with_assignees(task_id=cur_id, case_id=caseid)
if not task:
return response_error("Invalid task ID for this case")

delete_task(task.id)

update_tasks_state(caseid=caseid)

call_modules_hook('on_postload_task_delete', data=cur_id, caseid=caseid)

track_activity(f"deleted task \"{task.task_title}\"")

return response_success("Task deleted")
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Missing DB rollback on early returns
If get_task_with_assignees() returns None, the function returns immediately but any previous uncommitted work in the flask request context is kept open, risking a PendingRollbackError later. Add db.session.rollback() before returning on error paths throughout the file.

🧰 Tools
🪛 Ruff (0.8.2)

323-323: Undefined name get_task_with_assignees

(F821)

Comment on lines 44 to 48
# from app.datamgmt.case.case_tasks_db import get_task_with_assignees
from app.datamgmt.case.case_tasks_db import get_tasks_status
from app.datamgmt.case.case_tasks_db import get_tasks_with_assignees
from app.datamgmt.case.case_tasks_db import update_task_assignees
from app.datamgmt.case.case_tasks_db import update_task_status
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Missing import causes NameError for every task-level view/update/delete
get_task_with_assignees() is referenced four times in this file (lines 207, 224, 280, 323) but the import is commented out, causing a runtime failure the first time any of those routes is hit.

-# from app.datamgmt.case.case_tasks_db import get_task_with_assignees
+from app.datamgmt.case.case_tasks_db import get_task_with_assignees

Without this fix, every call path that needs full task information will 500.

📝 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
# from app.datamgmt.case.case_tasks_db import get_task_with_assignees
from app.datamgmt.case.case_tasks_db import get_tasks_status
from app.datamgmt.case.case_tasks_db import get_tasks_with_assignees
from app.datamgmt.case.case_tasks_db import update_task_assignees
from app.datamgmt.case.case_tasks_db import update_task_status
from app.datamgmt.case.case_tasks_db import get_task_with_assignees
from app.datamgmt.case.case_tasks_db import get_tasks_status
from app.datamgmt.case.case_tasks_db import get_tasks_with_assignees
from app.datamgmt.case.case_tasks_db import update_task_assignees
from app.datamgmt.case.case_tasks_db import update_task_status

Comment on lines +83 to +108
@case_tasks_blueprint.route('/case/jsoneditor', methods=['POST'])
@ac_case_requires(CaseAccessLevel.full_access)
def save_data(caseid, url_redir):
try:
data = request.get_json()

if not isinstance(data, dict):
raise ValueError("Request payload is not a valid JSON object")

payload = data.get('payload')
task_id = data.get('task_id')
action_id = data.get('action_id')

if not payload or not task_id or not action_id:
raise KeyError("Missing one or more required keys: 'payload', 'task_id', 'action_id'")

action_response = execute_and_save_action(payload, task_id, action_id)

return jsonify({"status": "success", "message": "Data saved successfully!", "data": action_response})
except KeyError as e:
return jsonify({"status": "error", "message": f"Missing key: {e}"}), 400
except ValueError as e:
return jsonify({"status": "error", "message": str(e)}), 400
except Exception as e:
return jsonify({"status": "error", "message": str(e)})

Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

⚠️ Potential issue

/case/jsoneditor – strengthen validation & HTTP semantics

  1. payload may legitimately be an empty dict, but if not payload … rejects it.
  2. execute_and_save_action() is called without caseid; double-check its signature.
  3. Generic except Exception returns HTTP 200; clients cannot distinguish success from failure.
  4. No logging → silent failures.
-        if not payload or not task_id or not action_id:
+        if payload is None or task_id is None or action_id is None:
             raise KeyError("Missing one or more required keys: 'payload', 'task_id', 'action_id'")
...
-    except Exception as e:
-        return jsonify({"status": "error", "message": str(e)})
+    except Exception as e:
+        current_app.logger.exception("Unhandled error in save_data")
+        return jsonify({"status": "error", "message": str(e)}), 500

Consider passing caseid explicitly if execute_and_save_action requires context.

📝 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
@case_tasks_blueprint.route('/case/jsoneditor', methods=['POST'])
@ac_case_requires(CaseAccessLevel.full_access)
def save_data(caseid, url_redir):
try:
data = request.get_json()
if not isinstance(data, dict):
raise ValueError("Request payload is not a valid JSON object")
payload = data.get('payload')
task_id = data.get('task_id')
action_id = data.get('action_id')
if not payload or not task_id or not action_id:
raise KeyError("Missing one or more required keys: 'payload', 'task_id', 'action_id'")
action_response = execute_and_save_action(payload, task_id, action_id)
return jsonify({"status": "success", "message": "Data saved successfully!", "data": action_response})
except KeyError as e:
return jsonify({"status": "error", "message": f"Missing key: {e}"}), 400
except ValueError as e:
return jsonify({"status": "error", "message": str(e)}), 400
except Exception as e:
return jsonify({"status": "error", "message": str(e)})
@case_tasks_blueprint.route('/case/jsoneditor', methods=['POST'])
@ac_case_requires(CaseAccessLevel.full_access)
def save_data(caseid, url_redir):
try:
data = request.get_json()
if not isinstance(data, dict):
raise ValueError("Request payload is not a valid JSON object")
payload = data.get('payload')
task_id = data.get('task_id')
action_id = data.get('action_id')
if payload is None or task_id is None or action_id is None:
raise KeyError("Missing one or more required keys: 'payload', 'task_id', 'action_id'")
action_response = execute_and_save_action(payload, task_id, action_id)
return jsonify({"status": "success", "message": "Data saved successfully!", "data": action_response})
except KeyError as e:
return jsonify({"status": "error", "message": f"Missing key: {e}"}), 400
except ValueError as e:
return jsonify({"status": "error", "message": str(e)}), 400
except Exception as e:
current_app.logger.exception("Unhandled error in save_data")
return jsonify({"status": "error", "message": str(e)}), 500

@whikernel whikernel changed the base branch from master to develop May 7, 2025 11:43
@@ -19,8 +19,8 @@

from datetime import datetime
from flask_login import current_user
from sqlalchemy import BigInteger
from sqlalchemy import CheckConstraint
# IMPORTS ------------------------------------------------
Copy link
Contributor

Choose a reason for hiding this comment

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

We prefer to avoid unnecessary comments such as this one.

from sqlalchemy import BigInteger
from sqlalchemy import CheckConstraint
# IMPORTS ------------------------------------------------
from sqlalchemy import BigInteger, CheckConstraint
Copy link
Contributor

Choose a reason for hiding this comment

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

We try to have one import per line (so two lines instead of one here)

@@ -44,7 +43,7 @@
from app.datamgmt.states import update_notes_state
from app.datamgmt.states import update_tasks_state
from app.datamgmt.states import update_timeline_state
from app.models.models import Client
from app.models.models import Client, Base
Copy link
Contributor

Choose a reason for hiding this comment

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

Base is an unused import?

@@ -228,8 +228,32 @@ class EvidenceTypes(db.Model):
created_by = relationship('User')


class Webhook(db.Model):
__tablename__ = 'webhooksss'
Copy link
Contributor

@c8y3 c8y3 May 14, 2025

Choose a reason for hiding this comment

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

Why so many s in the table name (webhookss)?

@@ -87,6 +86,8 @@ class Cases(db.Model):

review_status = relationship('ReviewStatus')

case_template_id = Column(String(256), nullable=True)
Copy link
Contributor

Choose a reason for hiding this comment

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

Since there is a modification in the model, shouldn't there be some migration scripts, so that users with a previous version of DFIR-IRIS are able to migrate?

@@ -82,13 +80,12 @@ def cases_exists(identifier):
def cases_create(request_data):
# TODO remove caseid doesn't seems to be useful for call_modules_hook => remove argument
request_data = call_modules_hook('on_preload_case_create', request_data, None)

case_template_id = request_data.pop('case_template_id', None)
Copy link
Contributor

Choose a reason for hiding this comment

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

This should be done in the _load, with marshmallow.

def __init__(self, **kwargs):
super().__init__(**kwargs)

def update_from_dict(self, data: dict):
Copy link
Contributor

Choose a reason for hiding this comment

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

Would this work with a load and argument instance, rather than adding method update_from_dict?

def __init__(self, **kwargs):
super().__init__(**kwargs)

def update_from_dict(self, data: dict):
Copy link
Contributor

Choose a reason for hiding this comment

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

Would this work with a load and argument instance, rather than adding method update_from_dict?

def __init__(self, **kwargs):
super().__init__(**kwargs)

def update_from_dict(self, data: dict):
Copy link
Contributor

@c8y3 c8y3 May 14, 2025

Choose a reason for hiding this comment

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

Does not seem to be called. Method can be removed. The same remark applies down there for the CaseResponse object

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.

6 participants