Skip to content

feat: permission improvements, correlation alerts, and audit logging#72

Merged
TerrifiedBug merged 5 commits intomainfrom
feat/permission-and-bug-fixes
Jan 30, 2026
Merged

feat: permission improvements, correlation alerts, and audit logging#72
TerrifiedBug merged 5 commits intomainfrom
feat/permission-and-bug-fixes

Conversation

@TerrifiedBug
Copy link
Owner

@TerrifiedBug TerrifiedBug commented Jan 30, 2026

Summary

  • Permission granularity: Separate manage_index_config from manage_settings for better access control
  • Correlation alert improvements: Add MITRE tags extraction from linked rules, consolidate UI
  • Audit logging: Add comprehensive audit trails for alert comments and ownership
  • CodeQL optimization: Exclude alembic migrations to reduce false positives

Changes

Permissions

  • Add manage_index_config permission for index pattern management
  • Update frontend route guards and navigation menu permissions
  • Restrict Health page to manage_settings, Field Mappings to manage_index_config

Correlation Alerts

  • Extract MITRE ATT&CK tags from linked sigma rules' YAML content
  • Add links to source sigma rules and alerts in correlation alert details
  • Consolidate correlation rule link into the Correlation Alert Details box
  • Hide separate "Rule" card for correlation alerts (redundant)

Audit Logging

  • alert.comment_add - Track comment additions
  • alert.comment_edit - Track comment edits
  • alert.comment_delete - Track comment deletions
  • alert.assign - Track alert ownership assignments
  • alert.unassign - Track alert ownership releases

Bug Fixes

  • Fix "Assigned to me" pagination returning only 25 alerts (now fetches up to 1000)
  • Fix correlation alert entity_field and tags not populated (requires new alerts)

CodeQL

  • Exclude backend/alembic/versions/** from analysis (reduces FP noise)

Test plan

  • Verify permission separation works (index config vs settings)
  • Create new correlation alert and verify tags appear
  • Add/edit/delete comment on alert, check audit log
  • Assign/unassign alert, check audit log
  • Verify "Assigned to me" shows all assigned alerts

Backend changes:
- Fix correlation alert entity_field bug in logs.py (was using sigma_field)
- Add permission checks for viewer restrictions (comments, ownership)
- Add PATCH endpoint for users to edit their own comments
- Add logger import to alerts.py (was missing)
- Grant analysts manage_settings permission for Index Patterns, Field Mappings, Health
- Add updated_at field to alert comments model and schema
- Create migration for alert_comments.updated_at column

Frontend changes:
- Add permission checks to bulk action buttons in Alerts page
- Add edit/delete functionality for comments in AlertDetail
- Hide add comment section for viewers (no manage_alerts permission)
- Disable Take Ownership button for viewers
- Show edit button only for own comments, delete only for admins
- Add useAuth hook to Alerts page for permission checking
- Add MITRE tags from linked sigma rules to correlation alerts
- Add links to source sigma rules in correlation alert UI
- Fix "Assigned to me" pagination to show all assigned alerts
- Create manage_index_config permission separate from manage_settings
- Allow viewers to see Health page (read-only)
- Restrict analyst Settings page access while keeping Index Patterns/Field Mappings
- Fix admin delete comment button using isAdmin hook
- Suppress ssl_context warning from opensearch-py
The Rule model doesn't have a tags attribute - tags are stored
in the YAML content. Parse the yaml_content to extract tags
from both linked sigma rules for correlation alerts.
if parsed and isinstance(parsed, dict):
tags = parsed.get("tags", []) or []
correlation_tags.extend(tags)
except (ValueError, yaml.YAMLError, Exception):

Check notice

Code scanning / CodeQL

Empty except Note

'except' clause does nothing but pass and there is no explanatory comment.

Copilot Autofix

AI about 1 month ago

In general, empty except blocks should be replaced with handlers that either (1) perform some recovery/compensation, (2) re-raise or wrap the exception, or at minimum (3) log the exception with enough context to diagnose issues later. When failure should not abort the operation (as here, where tags are additive metadata), the best fix is to log the error and continue.

For this specific block, the ideal minimal-impact fix is:

  • Keep the try/except structure and continue to swallow the error so alert creation is never blocked by a bad rule’s YAML.
  • Replace pass with a log statement that records:
    • Which rule failed (rule_a_id / rule_b_id).
    • That tags could not be loaded from YAML.
    • The exception object.
  • Use the existing logger already defined a few lines above in the if triggered_correlations: block, so no new imports or global loggers are required.
  • Apply the same change consistently to both the rule_a_id and rule_b_id except blocks.

Concretely, within backend/app/api/logs.py, in the shown region:

  • For the except (ValueError, yaml.YAMLError, Exception): at line 417, replace pass with a logger.warning(...) call mentioning rule_a_id and including exc_info=True so stack traces are available if needed.
  • For the analogous except at line 431, replace pass with a similar logger.warning(...) mentioning rule_b_id.

No new imports or helper methods are required; the logging import and logger variable are already in scope in this part of the function.

Suggested changeset 1
backend/app/api/logs.py

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/backend/app/api/logs.py b/backend/app/api/logs.py
--- a/backend/app/api/logs.py
+++ b/backend/app/api/logs.py
@@ -414,8 +414,13 @@
                                         if parsed and isinstance(parsed, dict):
                                             tags = parsed.get("tags", []) or []
                                             correlation_tags.extend(tags)
-                                except (ValueError, yaml.YAMLError, Exception):
-                                    pass
+                                except (ValueError, yaml.YAMLError, Exception) as e:
+                                    logger.warning(
+                                        "Failed to load tags from YAML for correlated rule_a_id %s: %s",
+                                        rule_a_id,
+                                        e,
+                                        exc_info=True,
+                                    )
 
                             if rule_b_id:
                                 try:
@@ -428,8 +433,13 @@
                                         if parsed and isinstance(parsed, dict):
                                             tags = parsed.get("tags", []) or []
                                             correlation_tags.extend(tags)
-                                except (ValueError, yaml.YAMLError, Exception):
-                                    pass
+                                except (ValueError, yaml.YAMLError, Exception) as e:
+                                    logger.warning(
+                                        "Failed to load tags from YAML for correlated rule_b_id %s: %s",
+                                        rule_b_id,
+                                        e,
+                                        exc_info=True,
+                                    )
 
                             # Deduplicate tags while preserving order
                             seen = set()
EOF
@@ -414,8 +414,13 @@
if parsed and isinstance(parsed, dict):
tags = parsed.get("tags", []) or []
correlation_tags.extend(tags)
except (ValueError, yaml.YAMLError, Exception):
pass
except (ValueError, yaml.YAMLError, Exception) as e:
logger.warning(
"Failed to load tags from YAML for correlated rule_a_id %s: %s",
rule_a_id,
e,
exc_info=True,
)

if rule_b_id:
try:
@@ -428,8 +433,13 @@
if parsed and isinstance(parsed, dict):
tags = parsed.get("tags", []) or []
correlation_tags.extend(tags)
except (ValueError, yaml.YAMLError, Exception):
pass
except (ValueError, yaml.YAMLError, Exception) as e:
logger.warning(
"Failed to load tags from YAML for correlated rule_b_id %s: %s",
rule_b_id,
e,
exc_info=True,
)

# Deduplicate tags while preserving order
seen = set()
Copilot is powered by AI and may make mistakes. Always verify output.
if parsed and isinstance(parsed, dict):
tags = parsed.get("tags", []) or []
correlation_tags.extend(tags)
except (ValueError, yaml.YAMLError, Exception):

Check notice

Code scanning / CodeQL

Empty except Note

'except' clause does nothing but pass and there is no explanatory comment.

Copilot Autofix

AI about 1 month ago

General fix: empty except blocks should either (a) handle the error (e.g., log it, adjust control flow, or provide a fallback) or (b) be documented with a clear comment why ignoring is safe. Here, the appropriate fix is to treat YAML/DB issues as non-fatal but log them so operators can debug malformed rules.

Best minimal fix: keep the same behavior (alert creation continues even if tag parsing fails) but add logging in the except blocks for both rule_a and rule_b. The logging should include the rule ID and the exception details. We will not re‑raise; we’ll just record the problem. This matches the later pattern where WebSocket broadcast failures are logged but do not stop processing.

Concretely:

  • In backend/app/api/logs.py, within the shown code region:
    • Replace the except (ValueError, yaml.YAMLError, Exception): pass for rule_a with an except Exception as e: (or keep the tuple if desired) that logs a warning or error, then continues.
    • Apply the same change to the rule_b block at line 431.
  • To do this we need a logger available in this module. The snippet already shows logger.error(...) later, so a logger is presumably defined earlier in the file; we will rely on that existing definition and just call logger.warning(...) in the new code.

This adds observability without altering functional behavior: alerts are still created; tags enrichment remains best-effort, but failures are now visible.

Suggested changeset 1
backend/app/api/logs.py

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/backend/app/api/logs.py b/backend/app/api/logs.py
--- a/backend/app/api/logs.py
+++ b/backend/app/api/logs.py
@@ -414,8 +414,13 @@
                                         if parsed and isinstance(parsed, dict):
                                             tags = parsed.get("tags", []) or []
                                             correlation_tags.extend(tags)
-                                except (ValueError, yaml.YAMLError, Exception):
-                                    pass
+                                except (ValueError, yaml.YAMLError, Exception) as e:
+                                    # Best-effort enrichment: failure to parse tags should not block alert creation
+                                    logger.warning(
+                                        "Failed to load tags from correlation rule A '%s': %s",
+                                        rule_a_id,
+                                        e,
+                                    )
 
                             if rule_b_id:
                                 try:
@@ -428,8 +433,13 @@
                                         if parsed and isinstance(parsed, dict):
                                             tags = parsed.get("tags", []) or []
                                             correlation_tags.extend(tags)
-                                except (ValueError, yaml.YAMLError, Exception):
-                                    pass
+                                except (ValueError, yaml.YAMLError, Exception) as e:
+                                    # Best-effort enrichment: failure to parse tags should not block alert creation
+                                    logger.warning(
+                                        "Failed to load tags from correlation rule B '%s': %s",
+                                        rule_b_id,
+                                        e,
+                                    )
 
                             # Deduplicate tags while preserving order
                             seen = set()
EOF
@@ -414,8 +414,13 @@
if parsed and isinstance(parsed, dict):
tags = parsed.get("tags", []) or []
correlation_tags.extend(tags)
except (ValueError, yaml.YAMLError, Exception):
pass
except (ValueError, yaml.YAMLError, Exception) as e:
# Best-effort enrichment: failure to parse tags should not block alert creation
logger.warning(
"Failed to load tags from correlation rule A '%s': %s",
rule_a_id,
e,
)

if rule_b_id:
try:
@@ -428,8 +433,13 @@
if parsed and isinstance(parsed, dict):
tags = parsed.get("tags", []) or []
correlation_tags.extend(tags)
except (ValueError, yaml.YAMLError, Exception):
pass
except (ValueError, yaml.YAMLError, Exception) as e:
# Best-effort enrichment: failure to parse tags should not block alert creation
logger.warning(
"Failed to load tags from correlation rule B '%s': %s",
rule_b_id,
e,
)

# Deduplicate tags while preserving order
seen = set()
Copilot is powered by AI and may make mistakes. Always verify output.
- Add correlation rule link to CorrelationAlertDetails component
- Hide separate Rule card for correlation alerts
- Shows correlation rule name if available, otherwise 'View Rule'
- Add audit logging for comment add/edit/delete operations
- Add audit logging for alert assign/unassign operations
- Exclude alembic/versions from CodeQL analysis (reduces FPs)

New audit actions:
- alert.comment_add
- alert.comment_edit
- alert.comment_delete
- alert.assign
- alert.unassign
@TerrifiedBug TerrifiedBug changed the title feat: permission improvements and bug fixes feat: permission improvements, correlation alerts, and audit logging Jan 30, 2026
@TerrifiedBug TerrifiedBug merged commit 321dafd into main Jan 30, 2026
13 checks passed
@TerrifiedBug TerrifiedBug deleted the feat/permission-and-bug-fixes branch January 30, 2026 13:38
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.

1 participant