sync #2651

Merged
mfreeman451 merged 4 commits from refs/pull/2651/head into fix/discovery_sources_empty 2026-01-11 18:45:38 +00:00
mfreeman451 commented 2026-01-11 18:41:39 +00:00 (Migrated from github.com)
Owner

Imported from GitHub pull request.

Original GitHub pull request: #2250
Original author: @mfreeman451
Original URL: https://github.com/carverauto/serviceradar/pull/2250
Original created: 2026-01-11T18:41:39Z
Original updated: 2026-01-11T18:45:39Z
Original head: carverauto/serviceradar:staging
Original base: fix/discovery_sources_empty
Original merged: 2026-01-11T18:45:38Z by @mfreeman451

User description

IMPORTANT: Please sign the Developer Certificate of Origin

Thank you for your contribution to ServiceRadar. Please note, when contributing, the developer must include
a DCO sign-off statement indicating the DCO acceptance in one commit message. Here
is an example DCO Signed-off-by line in a commit message:

Signed-off-by: J. Doe <j.doe@domain.com>

Describe your changes

Code checklist before requesting a review

  • I have signed the DCO?
  • The build completes without errors?
  • All tests are passing when running make test?

PR Type

Enhancement, Tests


Description

  • Major infrastructure overhaul: Comprehensive multi-tenant database schema with 30+ tables for user management, agents, devices, gateways, and monitoring systems

  • Stateful alert engine: New GenServer implementing bucketed time-window alert evaluation with rule state persistence and lifecycle management (firing, recovery, cooldown, renotification)

  • Gateway process infrastructure: New GenServer for agent coordination, check dispatch, and health monitoring with metrics tracking and load balancing

  • Enhanced LiveView modules: New cluster status monitoring, infrastructure monitoring with role-based access, integration sources management, and improved agent/gateway/job management UIs

  • Form modernization: Migrated edge package creation from Ecto changesets to AshPhoenix.Form with automatic TLS certificate generation

  • Job scheduler redesign: Unified job catalog view supporting both Cron and AshOban jobs with search, filtering, and triggering capabilities

  • Live agent registry integration: Dynamic agent lookups via Horde with real-time status tracking and node system metrics retrieval

  • Comprehensive test coverage: New test suites for TenantRegistry multi-tenant infrastructure validation

  • Legacy code removal: Removed Go-based poller implementation and legacy Elixir user/device models in favor of new Ash-based architecture


Diagram Walkthrough

flowchart LR
  DB["Database Schema<br/>30+ Tables"]
  TenantReg["TenantRegistry<br/>Multi-tenant Mgmt"]
  GatewayProc["GatewayProcess<br/>Check Dispatch"]
  AlertEngine["StatefulAlertEngine<br/>Alert Evaluation"]
  LiveViews["Enhanced LiveViews<br/>Cluster/Infrastructure/Jobs"]
  Forms["AshPhoenix Forms<br/>Auto TLS Certs"]
  
  DB -- "Tenant Isolation" --> TenantReg
  TenantReg -- "Agent Coordination" --> GatewayProc
  GatewayProc -- "Check Results" --> AlertEngine
  AlertEngine -- "Alert Events" --> LiveViews
  Forms -- "Edge Packages" --> GatewayProc
  TenantReg -- "Real-time Updates" --> LiveViews

File Walkthrough

Relevant files
Database migration
1 files
20260107043446_initial_schema.exs
Initial tenant schema migration with core infrastructure tables

elixir/serviceradar_core/priv/repo/tenant_migrations/20260107043446_initial_schema.exs

  • Comprehensive database migration creating 30+ tables for multi-tenant
    infrastructure
  • Defines schema for user management, agents, devices, gateways, and
    monitoring systems
  • Establishes relationships between core entities (devices, agents,
    gateways, partitions)
  • Includes encrypted fields for sensitive data (credentials,
    certificates, keys)
+1416/-0
Feature
1 files
index.ex
Integration sources management LiveView with modal forms 

web-ng/lib/serviceradar_web_ng_web/live/admin/integration_live/index.ex

  • LiveView module for managing integration sources (Armis, SNMP, Syslog,
    Nmap, Custom)
  • Implements CRUD operations with modal-based UI for create, edit, and
    view operations
  • Handles dynamic query management and network blacklist configuration
  • Includes filtering, status tracking, and agent/partition assignment
    features
+1471/-0
Tests
2 files
tenant_registry_test.exs
TenantRegistry unit tests for multi-tenant infrastructure

elixir/serviceradar_core/test/serviceradar/cluster/tenant_registry_test.exs

  • Comprehensive test suite for TenantRegistry module with 15+ test cases
  • Tests registry creation, tenant isolation, gateway/agent registration,
    and lifecycle management
  • Validates process counting, heartbeat updates, and infrastructure
    cleanup
  • Ensures proper separation of concerns across multiple tenants
+310/-0 
test_helper.exs
ExUnit test framework initialization                                         

web-ng/serviceradar/test/test_helper.exs

  • Minimal test helper file that starts ExUnit test framework
+1/-0     
Enhancement
7 files
index.ex
New cluster status monitoring LiveView for settings           

web-ng/lib/serviceradar_web_ng_web/live/settings/cluster_live/index.ex

  • New LiveView module for real-time cluster monitoring from settings
    area
  • Displays ERTS cluster topology, Horde-managed gateways/agents, and
    Oban job queue status
  • Implements PubSub subscriptions for cluster events and periodic
    refresh scheduling
  • Provides health metrics cards, cluster nodes table, gateways/agents
    tables, and recent events log
+1086/-0
index.ex
Refactor edge package creation with Ash forms and auto-cert generation

web-ng/lib/serviceradar_web_ng_web/live/admin/edge_package_live/index.ex

  • Migrated from Ecto changesets to AshPhoenix.Form for form handling
  • Added automatic TLS certificate generation via create_with_tenant_cert
    function
  • Changed component type from "poller" to "gateway" as primary type
  • Enhanced UI with loading states, improved success modal with install
    commands, and advanced options
  • Added support for "sync" component type alongside gateway, agent, and
    checker
+546/-224
index.ex
New infrastructure monitoring LiveView with role-based access

web-ng/lib/serviceradar_web_ng_web/live/infrastructure_live/index.ex

  • New LiveView for infrastructure monitoring showing cluster nodes and
    agent gateways
  • Implements role-based visibility (platform admins see all tabs,
    regular users see only agents)
  • Caches gateways and agents locally with PubSub synchronization across
    cluster nodes
  • Provides tabbed interface with overview, nodes, gateways, and
    connected agents sections
+1029/-0
show.ex
Live agent registry integration with node system metrics 

web-ng/lib/serviceradar_web_ng_web/live/agent_live/show.ex

  • Replaced hardcoded agent type mappings with dynamic lookups via
    ServiceRadar.Infrastructure.Agent module
  • Added live agent registry lookup and Horde-based agent discovery with
    real-time status tracking
  • Implemented gateway node system information retrieval (memory,
    processes, schedulers, uptime) via RPC calls
  • Added service checks loading and display with check type badges and
    status indicators
  • Enhanced UI with live status banners, registration timeline, and
    comprehensive node metrics visualization
+501/-159
index.ex
Job scheduler UI redesign with catalog integration             

web-ng/lib/serviceradar_web_ng_web/live/admin/job_live/index.ex

  • Refactored from manual schedule management to unified job catalog view
    supporting both Cron and AshOban jobs
  • Added search, filtering by source, sorting, and pagination
    capabilities with configurable auto-refresh
  • Implemented job triggering functionality with permission checks for
    admin users
  • Replaced form-based schedule editing with read-only job display and
    navigation to detail views
  • Added leader node detection and conditional UI elements based on user
    permissions and tenant type
+610/-212
stateful_alert_engine.ex
Stateful alert engine with bucketed time-window evaluation

elixir/serviceradar_core/lib/serviceradar/observability/stateful_alert_engine.ex

  • New GenServer implementing bucketed stateful alert evaluation for logs
    and events with time-windowed thresholds
  • Supports rule matching on log/event attributes, severity, service
    names, and custom predicates
  • Manages alert lifecycle including firing, recovery, cooldown periods,
    and renotification scheduling
  • Persists rule state snapshots to database and maintains in-memory ETS
    cache for performance
  • Integrates with AlertGenerator and WebhookNotifier for alert creation
    and notification dispatch
+960/-0 
gateway_process.ex
Gateway process for agent coordination and check dispatch

elixir/serviceradar_core/lib/serviceradar/edge/gateway_process.ex

  • New GenServer representing an agent gateway in the ERTS cluster for
    check execution and agent coordination
  • Implements job execution with agent discovery by partition or domain,
    with load balancing via random selection
  • Provides health check and result retrieval APIs for external health
    monitoring services
  • Maintains metrics tracking (jobs executed, checks executed, execution
    times) and registry heartbeats
  • Handles both synchronous and asynchronous job execution with PubSub
    result broadcasting
+466/-0 
Database schema
1 files
20260110054954_add_stateful_alert_rules.exs
Database migration for stateful alert rules system             

elixir/serviceradar_core/priv/repo/tenant_migrations/20260110054954_add_stateful_alert_rules.exs

  • Creates stateful_alert_rules table for storing alert rule
    configurations with thresholds and windows
  • Creates stateful_alert_rule_states table for tracking per-group rule
    state and firing history
  • Adds unique indexes on tenant_id+name for rules and
    tenant_id+rule_id+group_key for states
  • Includes fields for cooldown, renotification, and alert metadata
+85/-0   
Additional files
101 files
.bazelignore +4/-0     
.bazelrc +5/-0     
.env-sample +33/-0   
.env.example +38/-0   
main.yml +18/-0   
sbom-images.yml +1/-3     
web-lint.yml +0/-60   
AGENTS.md +177/-11
INSTALL.md +19/-15 
MODULE.bazel +22/-2   
Makefile +58/-56 
README-Docker.md +17/-2   
README.md +3/-4     
ROADMAP.md +1/-1     
BUILD.bazel +11/-38 
BUILD.bazel +12/-0   
mix_release.bzl +141/-49
BUILD.bazel +1/-0     
README.md +4/-4     
config.json +5/-6     
main.go +174/-74
build.rs +0/-1     
monitoring.proto +3/-26   
server.rs +2/-0     
BUILD.bazel +1/-1     
main.go +1/-1     
README.md +2/-2     
monitoring.proto +2/-26   
server.rs +6/-6     
main.go +16/-2   
Cargo.toml +0/-3     
README.md +8/-8     
config.rs +85/-28 
grpc_server.rs +2/-2     
message_processor.rs +2/-16   
nats.rs +4/-0     
zen-consumer-with-otel.json +14/-11 
zen-consumer.json +14/-11 
.ko.yaml +0/-15   
BUILD.bazel +0/-17   
BUILD.bazel +0/-24   
app.go +0/-206 
config.json +0/-165 
config.json +0/-165 
main.go +0/-86   
BUILD.bazel +1/-0     
main.go +68/-0   
README.md +3/-3     
README.md +9/-12   
flowgger.toml +2/-1     
nats_output.rs +14/-0   
otel.toml +3/-1     
otel.toml.example +5/-2     
config.rs +21/-3   
nats_output.rs +22/-5   
setup.rs +1/-0     
BUILD.bazel +0/-25   
config.json +0/-111 
main.go +0/-138 
BUILD.bazel +0/-25   
config.json +0/-77   
main.go +0/-123 
main.go +1/-1     
README.md +3/-3     
config.rs +22/-1   
main.rs +23/-3   
docker-compose.dev.yml +23/-34 
docker-compose.elx.yml +117/-0 
docker-compose.spiffe.yml +39/-192
docker-compose.yml +316/-269
README.md +6/-5     
Dockerfile.agent-gateway +94/-0   
Dockerfile.core +0/-93   
Dockerfile.core-elx +108/-0 
Dockerfile.poller +0/-70   
Dockerfile.sync +0/-95   
Dockerfile.tools +1/-2     
Dockerfile.web +0/-110 
Dockerfile.web-ng +6/-0     
agent-minimal.docker.json +6/-6     
agent.docker.json +5/-20   
agent.mtls.json +7/-10   
bootstrap-nested-spire.sh +0/-80   
.gitkeep +1/-0     
datasvc.docker.json +3/-2     
datasvc.mtls.json +14/-1   
db-event-writer.docker.json +15/-11 
db-event-writer.mtls.json +10/-8   
FRICTION_POINTS.md +0/-355 
README.md +0/-207 
SETUP_GUIDE.md +0/-307 
docker-compose.edge-e2e.yml +0/-27   
manage-packages.sh +0/-211 
setup-edge-e2e.sh +0/-198 
edge-poller-restart.sh +0/-178 
downstream-agent.conf +0/-32   
env +0/-4     
server.conf +0/-51   
upstream-agent.conf +0/-32   
entrypoint-certs.sh +13/-9   
Additional files not shown

Imported from GitHub pull request. Original GitHub pull request: #2250 Original author: @mfreeman451 Original URL: https://github.com/carverauto/serviceradar/pull/2250 Original created: 2026-01-11T18:41:39Z Original updated: 2026-01-11T18:45:39Z Original head: carverauto/serviceradar:staging Original base: fix/discovery_sources_empty Original merged: 2026-01-11T18:45:38Z by @mfreeman451 --- ### **User description** ## IMPORTANT: Please sign the Developer Certificate of Origin Thank you for your contribution to ServiceRadar. Please note, when contributing, the developer must include a [DCO sign-off statement]( https://developercertificate.org/) indicating the DCO acceptance in one commit message. Here is an example DCO Signed-off-by line in a commit message: ``` Signed-off-by: J. Doe <j.doe@domain.com> ``` ## Describe your changes ## Issue ticket number and link ## Code checklist before requesting a review - [ ] I have signed the DCO? - [ ] The build completes without errors? - [ ] All tests are passing when running make test? ___ ### **PR Type** Enhancement, Tests ___ ### **Description** - **Major infrastructure overhaul**: Comprehensive multi-tenant database schema with 30+ tables for user management, agents, devices, gateways, and monitoring systems - **Stateful alert engine**: New GenServer implementing bucketed time-window alert evaluation with rule state persistence and lifecycle management (firing, recovery, cooldown, renotification) - **Gateway process infrastructure**: New GenServer for agent coordination, check dispatch, and health monitoring with metrics tracking and load balancing - **Enhanced LiveView modules**: New cluster status monitoring, infrastructure monitoring with role-based access, integration sources management, and improved agent/gateway/job management UIs - **Form modernization**: Migrated edge package creation from Ecto changesets to `AshPhoenix.Form` with automatic TLS certificate generation - **Job scheduler redesign**: Unified job catalog view supporting both Cron and AshOban jobs with search, filtering, and triggering capabilities - **Live agent registry integration**: Dynamic agent lookups via Horde with real-time status tracking and node system metrics retrieval - **Comprehensive test coverage**: New test suites for TenantRegistry multi-tenant infrastructure validation - **Legacy code removal**: Removed Go-based poller implementation and legacy Elixir user/device models in favor of new Ash-based architecture ___ ### Diagram Walkthrough ```mermaid flowchart LR DB["Database Schema<br/>30+ Tables"] TenantReg["TenantRegistry<br/>Multi-tenant Mgmt"] GatewayProc["GatewayProcess<br/>Check Dispatch"] AlertEngine["StatefulAlertEngine<br/>Alert Evaluation"] LiveViews["Enhanced LiveViews<br/>Cluster/Infrastructure/Jobs"] Forms["AshPhoenix Forms<br/>Auto TLS Certs"] DB -- "Tenant Isolation" --> TenantReg TenantReg -- "Agent Coordination" --> GatewayProc GatewayProc -- "Check Results" --> AlertEngine AlertEngine -- "Alert Events" --> LiveViews Forms -- "Edge Packages" --> GatewayProc TenantReg -- "Real-time Updates" --> LiveViews ``` <details><summary><h3>File Walkthrough</h3></summary> <table><thead><tr><th></th><th align="left">Relevant files</th></tr></thead><tbody><tr><td><strong>Database migration</strong></td><td><details><summary>1 files</summary><table> <tr> <td> <details> <summary><strong>20260107043446_initial_schema.exs</strong><dd><code>Initial tenant schema migration with core infrastructure tables</code></dd></summary> <hr> elixir/serviceradar_core/priv/repo/tenant_migrations/20260107043446_initial_schema.exs <ul><li>Comprehensive database migration creating 30+ tables for multi-tenant <br>infrastructure<br> <li> Defines schema for user management, agents, devices, gateways, and <br>monitoring systems<br> <li> Establishes relationships between core entities (devices, agents, <br>gateways, partitions)<br> <li> Includes encrypted fields for sensitive data (credentials, <br>certificates, keys)</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2250/files#diff-0d217dc9822fab0d3390e8ec21040f98e67106e5c9126e043a9b701efcbfb576">+1416/-0</a></td> </tr> </table></details></td></tr><tr><td><strong>Feature</strong></td><td><details><summary>1 files</summary><table> <tr> <td> <details> <summary><strong>index.ex</strong><dd><code>Integration sources management LiveView with modal forms</code>&nbsp; </dd></summary> <hr> web-ng/lib/serviceradar_web_ng_web/live/admin/integration_live/index.ex <ul><li>LiveView module for managing integration sources (Armis, SNMP, Syslog, <br>Nmap, Custom)<br> <li> Implements CRUD operations with modal-based UI for create, edit, and <br>view operations<br> <li> Handles dynamic query management and network blacklist configuration<br> <li> Includes filtering, status tracking, and agent/partition assignment <br>features</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2250/files#diff-61d0262af13a42905ebbd793e83537e61bfc09493df1664a82eb2536980ee1cd">+1471/-0</a></td> </tr> </table></details></td></tr><tr><td><strong>Tests</strong></td><td><details><summary>2 files</summary><table> <tr> <td> <details> <summary><strong>tenant_registry_test.exs</strong><dd><code>TenantRegistry unit tests for multi-tenant infrastructure</code></dd></summary> <hr> elixir/serviceradar_core/test/serviceradar/cluster/tenant_registry_test.exs <ul><li>Comprehensive test suite for <code>TenantRegistry</code> module with 15+ test cases<br> <li> Tests registry creation, tenant isolation, gateway/agent registration, <br>and lifecycle management<br> <li> Validates process counting, heartbeat updates, and infrastructure <br>cleanup<br> <li> Ensures proper separation of concerns across multiple tenants</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2250/files#diff-ff7e8a23791514dee76b77ef78fa2f8bc548f42a4d09897ae375e2ca7734fca5">+310/-0</a>&nbsp; </td> </tr> <tr> <td> <details> <summary><strong>test_helper.exs</strong><dd><code>ExUnit test framework initialization</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> web-ng/serviceradar/test/test_helper.exs - Minimal test helper file that starts ExUnit test framework </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2250/files#diff-2f4b2939497dc679e70f8bb20e57263bed9c409a489f400f3308bbdda723f905">+1/-0</a>&nbsp; &nbsp; &nbsp; </td> </tr> </table></details></td></tr><tr><td><strong>Enhancement</strong></td><td><details><summary>7 files</summary><table> <tr> <td> <details> <summary><strong>index.ex</strong><dd><code>New cluster status monitoring LiveView for settings</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> web-ng/lib/serviceradar_web_ng_web/live/settings/cluster_live/index.ex <ul><li>New LiveView module for real-time cluster monitoring from settings <br>area<br> <li> Displays ERTS cluster topology, Horde-managed gateways/agents, and <br>Oban job queue status<br> <li> Implements PubSub subscriptions for cluster events and periodic <br>refresh scheduling<br> <li> Provides health metrics cards, cluster nodes table, gateways/agents <br>tables, and recent events log</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2250/files#diff-644e64ac49eaa128f9b429c8507d6a8a9ad820e2796721dd665caba8a154d24e">+1086/-0</a></td> </tr> <tr> <td> <details> <summary><strong>index.ex</strong><dd><code>Refactor edge package creation with Ash forms and auto-cert generation</code></dd></summary> <hr> web-ng/lib/serviceradar_web_ng_web/live/admin/edge_package_live/index.ex <ul><li>Migrated from Ecto changesets to <code>AshPhoenix.Form</code> for form handling<br> <li> Added automatic TLS certificate generation via <code>create_with_tenant_cert</code> <br>function<br> <li> Changed component type from "poller" to "gateway" as primary type<br> <li> Enhanced UI with loading states, improved success modal with install <br>commands, and advanced options<br> <li> Added support for "sync" component type alongside gateway, agent, and <br>checker</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2250/files#diff-d0d22a987aa942ef25088696daea862e36bd8ca083ce29e4670f63e62947c62f">+546/-224</a></td> </tr> <tr> <td> <details> <summary><strong>index.ex</strong><dd><code>New infrastructure monitoring LiveView with role-based access</code></dd></summary> <hr> web-ng/lib/serviceradar_web_ng_web/live/infrastructure_live/index.ex <ul><li>New LiveView for infrastructure monitoring showing cluster nodes and <br>agent gateways<br> <li> Implements role-based visibility (platform admins see all tabs, <br>regular users see only agents)<br> <li> Caches gateways and agents locally with PubSub synchronization across <br>cluster nodes<br> <li> Provides tabbed interface with overview, nodes, gateways, and <br>connected agents sections</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2250/files#diff-f1af30a84da554ef3d43b226a9303174b33dd5d27e23e9b702031483074e5f54">+1029/-0</a></td> </tr> <tr> <td> <details> <summary><strong>show.ex</strong><dd><code>Live agent registry integration with node system metrics</code>&nbsp; </dd></summary> <hr> web-ng/lib/serviceradar_web_ng_web/live/agent_live/show.ex <ul><li>Replaced hardcoded agent type mappings with dynamic lookups via <br><code>ServiceRadar.Infrastructure.Agent</code> module<br> <li> Added live agent registry lookup and Horde-based agent discovery with <br>real-time status tracking<br> <li> Implemented gateway node system information retrieval (memory, <br>processes, schedulers, uptime) via RPC calls<br> <li> Added service checks loading and display with check type badges and <br>status indicators<br> <li> Enhanced UI with live status banners, registration timeline, and <br>comprehensive node metrics visualization</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2250/files#diff-5e622205d1abddd8ad7dcf7a8ca1be583804d622d3d38b75140e9b909cf0534a">+501/-159</a></td> </tr> <tr> <td> <details> <summary><strong>index.ex</strong><dd><code>Job scheduler UI redesign with catalog integration</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> web-ng/lib/serviceradar_web_ng_web/live/admin/job_live/index.ex <ul><li>Refactored from manual schedule management to unified job catalog view <br>supporting both Cron and AshOban jobs<br> <li> Added search, filtering by source, sorting, and pagination <br>capabilities with configurable auto-refresh<br> <li> Implemented job triggering functionality with permission checks for <br>admin users<br> <li> Replaced form-based schedule editing with read-only job display and <br>navigation to detail views<br> <li> Added leader node detection and conditional UI elements based on user <br>permissions and tenant type</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2250/files#diff-b275777f111009bbb4976d57623231aae2262452ef320a9f91ecbf202144115a">+610/-212</a></td> </tr> <tr> <td> <details> <summary><strong>stateful_alert_engine.ex</strong><dd><code>Stateful alert engine with bucketed time-window evaluation</code></dd></summary> <hr> elixir/serviceradar_core/lib/serviceradar/observability/stateful_alert_engine.ex <ul><li>New GenServer implementing bucketed stateful alert evaluation for logs <br>and events with time-windowed thresholds<br> <li> Supports rule matching on log/event attributes, severity, service <br>names, and custom predicates<br> <li> Manages alert lifecycle including firing, recovery, cooldown periods, <br>and renotification scheduling<br> <li> Persists rule state snapshots to database and maintains in-memory ETS <br>cache for performance<br> <li> Integrates with AlertGenerator and WebhookNotifier for alert creation <br>and notification dispatch</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2250/files#diff-bae3a52db882de8c947e62f219a95dff8db4e155e37d9a361dbe14ec25fcd3bd">+960/-0</a>&nbsp; </td> </tr> <tr> <td> <details> <summary><strong>gateway_process.ex</strong><dd><code>Gateway process for agent coordination and check dispatch</code></dd></summary> <hr> elixir/serviceradar_core/lib/serviceradar/edge/gateway_process.ex <ul><li>New GenServer representing an agent gateway in the ERTS cluster for <br>check execution and agent coordination<br> <li> Implements job execution with agent discovery by partition or domain, <br>with load balancing via random selection<br> <li> Provides health check and result retrieval APIs for external health <br>monitoring services<br> <li> Maintains metrics tracking (jobs executed, checks executed, execution <br>times) and registry heartbeats<br> <li> Handles both synchronous and asynchronous job execution with PubSub <br>result broadcasting</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2250/files#diff-b0b102ecc88366a0e9eea09de860b80929729880cd47f1aa96d6cfcb0ccc9f8f">+466/-0</a>&nbsp; </td> </tr> </table></details></td></tr><tr><td><strong>Database schema</strong></td><td><details><summary>1 files</summary><table> <tr> <td> <details> <summary><strong>20260110054954_add_stateful_alert_rules.exs</strong><dd><code>Database migration for stateful alert rules system</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> elixir/serviceradar_core/priv/repo/tenant_migrations/20260110054954_add_stateful_alert_rules.exs <ul><li>Creates <code>stateful_alert_rules</code> table for storing alert rule <br>configurations with thresholds and windows<br> <li> Creates <code>stateful_alert_rule_states</code> table for tracking per-group rule <br>state and firing history<br> <li> Adds unique indexes on tenant_id+name for rules and <br>tenant_id+rule_id+group_key for states<br> <li> Includes fields for cooldown, renotification, and alert metadata</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2250/files#diff-0f4b254a0546c5b2926a95e946ac30080b8fc11db180d64d6342dad1b97c66db">+85/-0</a>&nbsp; &nbsp; </td> </tr> </table></details></td></tr><tr><td><strong>Additional files</strong></td><td><details><summary>101 files</summary><table> <tr> <td><strong>.bazelignore</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2250/files#diff-a5641cd37d6ad98b32cdfce1980836cc68312277bc6a7052f55da02ada5bc6cf">+4/-0</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td><strong>.bazelrc</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2250/files#diff-544556920c45b42cbfe40159b082ce8af6bd929e492d076769226265f215832f">+5/-0</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td><strong>.env-sample</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2250/files#diff-c4368a972a7fa60d9c4e333cebf68cdb9a67acb810451125c02e3b7eb2594e3d">+33/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>.env.example</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2250/files#diff-a3046da0d15a27e89f2afe639b25748a7ad4d9290af3e7b1b6c1a5533c8f0a8c">+38/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>main.yml</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2250/files#diff-7829468e86c1cc5d5133195b5cb48e1ff6c75e3e9203777f6b2e379d9e4882b3">+18/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>sbom-images.yml</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2250/files#diff-306f4aa8e8e286f727246a7517eecd45f3535fd99a644f60d635b9fa39875f54">+1/-3</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td><strong>web-lint.yml</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2250/files#diff-9d090859e31fc574efb47cacf534d619a3e83d55e59da3be2484999b9055b1b2">+0/-60</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>AGENTS.md</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2250/files#diff-a54ff182c7e8acf56acfd6e4b9c3ff41e2c41a31c9b211b2deb9df75d9a478f9">+177/-11</a></td> </tr> <tr> <td><strong>INSTALL.md</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2250/files#diff-09b140a43ebfdd8dbec31ce72cafffd15164d2860fd390692a030bcb932b54a0">+19/-15</a>&nbsp; </td> </tr> <tr> <td><strong>MODULE.bazel</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2250/files#diff-6136fc12446089c3db7360e923203dd114b6a1466252e71667c6791c20fe6bdc">+22/-2</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>Makefile</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2250/files#diff-76ed074a9305c04054cdebb9e9aad2d818052b07091de1f20cad0bbac34ffb52">+58/-56</a>&nbsp; </td> </tr> <tr> <td><strong>README-Docker.md</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2250/files#diff-9fd61d24482efe68c22d8d41e2a1dcc440f39195aa56e7a050f2abe598179efd">+17/-2</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>README.md</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2250/files#diff-b335630551682c19a781afebcf4d07bf978fb1f8ac04c6bf87428ed5106870f5">+3/-4</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td><strong>ROADMAP.md</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2250/files#diff-683343bdf93f55ed3cada86151abb8051282e1936e58d4e0a04beca95dff6e51">+1/-1</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td><strong>BUILD.bazel</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2250/files#diff-884fa9353a5226345e44fbabea3300efc7a87dfbcde0b6a42521ca51823f1b68">+11/-38</a>&nbsp; </td> </tr> <tr> <td><strong>BUILD.bazel</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2250/files#diff-0e80ea46aeb61a873324685edb96eae864c7a2004fbb7ee404b4ec951190ba10">+12/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>mix_release.bzl</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2250/files#diff-86ec281f99363b6b6eb1f49e21d83b7eeca93a35b552b9f305fffc6855e38ccd">+141/-49</a></td> </tr> <tr> <td><strong>BUILD.bazel</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2250/files#diff-143f8d1549d52f28906f19ce28e5568a5be474470ff103c2c1e63c3e6b08d670">+1/-0</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td><strong>README.md</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2250/files#diff-bfd308915d0cf522e7fc76600dee687617dc69165ab22502a1d219850c0c0860">+4/-4</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td><strong>config.json</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2250/files#diff-5b1bc8fe77422534739bdd3a38dc20d2634a86c171265c34e1b5d0c5a61b6bab">+5/-6</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td><strong>main.go</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2250/files#diff-61358711e980ccf505246fd3915f97cbd3a380e9b66f6fa5aad46749968c5ca3">+174/-74</a></td> </tr> <tr> <td><strong>build.rs</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2250/files#diff-251e7a923f45f8f903e510d10f183366bda06d281c8ecc3669e1858256e2186d">+0/-1</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td><strong>monitoring.proto</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2250/files#diff-b56f709f4a0a3db694f2124353908318631f23e20b7846bc4b8ee869e2e0632a">+3/-26</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>server.rs</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2250/files#diff-bce0f4ca6548712f224b73816825d28e831acbbff7dbed3c98671ed50f65d028">+2/-0</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td><strong>BUILD.bazel</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2250/files#diff-7da152990199fd73c1eecb40f9c49e0d4e6453a8ec1acb111e445c55d1ca0af0">+1/-1</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td><strong>main.go</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2250/files#diff-f25402eade63525184cb5e7437accff93c7b9338eebe81add6dc5f2a9eb12550">+1/-1</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td><strong>README.md</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2250/files#diff-2e9751b437fa61442aac074c7a4a912d0ac50ac3ea156ac8aedd8478d21c6bdb">+2/-2</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td><strong>monitoring.proto</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2250/files#diff-9faf6025eb0d3d38383f5b7ad2b733abeb38454d5e4de3e83994e94b12d87a50">+2/-26</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>server.rs</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2250/files#diff-2c4395fee16396339c3eea518ad9bec739174c67c9cedf62e6848c17136dd33e">+6/-6</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td><strong>main.go</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2250/files#diff-ed4d81d29a7267f93fd77e17993fd3491b9ef6ded18490b4514d10ed1d803bc2">+16/-2</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>Cargo.toml</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2250/files#diff-fcf0c672917b64a5b953a914af013f16dddd6a1d813810236364e32f1ae70382">+0/-3</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td><strong>README.md</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2250/files#diff-643d2c3959322902c5bc9a22666b1e9ef71fa0bb87c9451b0e4147a4d5b51987">+8/-8</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td><strong>config.rs</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2250/files#diff-05038f3867985e757de9027609950e682bad6d1992dac6acd7c28962a3c65dc4">+85/-28</a>&nbsp; </td> </tr> <tr> <td><strong>grpc_server.rs</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2250/files#diff-e4564a93f6cf84ff91cd3d8141fc9272ec9b4ec19defd107afa42be01fcfed5b">+2/-2</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td><strong>message_processor.rs</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2250/files#diff-9fcbc5358a9009e60a8cd22d21e5a9ea652787c727732d0b869e0865495114c3">+2/-16</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>nats.rs</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2250/files#diff-97f7335def0ad5d644b594a1076ae2d7080b11259cbb8de22c7946cc8e4b39f8">+4/-0</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td><strong>zen-consumer-with-otel.json</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2250/files#diff-68375f1f7847e1fbdf75664f6be65b1ad94ae6ce86ed73fc5964d65054668acb">+14/-11</a>&nbsp; </td> </tr> <tr> <td><strong>zen-consumer.json</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2250/files#diff-4d308af9802a93a0f656e8c02a3b5fcd8991407bb18360f087470db74e1f9524">+14/-11</a>&nbsp; </td> </tr> <tr> <td><strong>.ko.yaml</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2250/files#diff-c403e088cfbc9e150c604da6a056a188e7a5f5585c45cdb515460f000008b441">+0/-15</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>BUILD.bazel</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2250/files#diff-cf437f055db002c5a1dba0ac4a4949d0ecc4b095e8e760bf5aec0f5d4c08f572">+0/-17</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>BUILD.bazel</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2250/files#diff-5570e460b26d54e1c23df4652073efa22cfd37353237956b5ffc4f355bbb3346">+0/-24</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>app.go</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2250/files#diff-4ad8a289575edf3b163088617b7a40ae1305c29ced0c7d59b3751c57d6938072">+0/-206</a>&nbsp; </td> </tr> <tr> <td><strong>config.json</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2250/files#diff-2423ef78d36e905ae993b69ff59f5df6b2e1b9492fb0fa8c6d0aad7c76d2d229">+0/-165</a>&nbsp; </td> </tr> <tr> <td><strong>config.json</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2250/files#diff-ef778d85ac6f9652c25cb0d631f0fe8dfb3edac4dde5d719a4fc2926fb5c3216">+0/-165</a>&nbsp; </td> </tr> <tr> <td><strong>main.go</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2250/files#diff-4ab3fd1d4debc53dd2499d94a0f60c648fdae4235dd1e3678095a975f5bb434a">+0/-86</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>BUILD.bazel</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2250/files#diff-c62c0139ebdb337369f4067567cd2c52b8e7decb3ddfabc77f9f67b2f6e5789c">+1/-0</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td><strong>main.go</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2250/files#diff-5e7731adfb877918cd65d9d5531621312496450fd550fea2682efca4ca8fe816">+68/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>README.md</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2250/files#diff-0b0725713b87dca1de57200214a4fe04633f0d856c39aa8032280227bf8e8141">+3/-3</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td><strong>README.md</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2250/files#diff-f425b4378f84e0ba0c6f532facff17ff5d55b4dc6033d8bf35130a159cd2ba32">+9/-12</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>flowgger.toml</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2250/files#diff-af9f49f931e282dca53d1f0521b036d222fe671f77e61a876a84cf4c6d7cca4d">+2/-1</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td><strong>nats_output.rs</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2250/files#diff-a82e2e4d413539bf0b414b5629665b19648447523994cba639c4d1238aa5a0c1">+14/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>otel.toml</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2250/files#diff-c64b9ace832b8ea57a2be62f84166e03bb1904882635d444ec76a880cdf14cc0">+3/-1</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td><strong>otel.toml.example</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2250/files#diff-c1889866f35f98cdba9cd229fc119273c5fa5fca501451db23813b575f6fec66">+5/-2</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td><strong>config.rs</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2250/files#diff-abbaec651da3d6af96b482e0f77bb909b65dbe0cabd78b5803769cc9dab0a1b0">+21/-3</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>nats_output.rs</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2250/files#diff-6b585ea3564a481174e04da1270e2e13edd4e2b980d02a2652d6d21e6d82a498">+22/-5</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>setup.rs</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2250/files#diff-3891f667deb20fd26e296d3e2742c57378d3764fe1743118e612465ae360391f">+1/-0</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td><strong>BUILD.bazel</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2250/files#diff-e1f7c698e0e3a4e6afa971c1140e71cbf22593fbb19c81cb26b02c15c5dc46ec">+0/-25</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>config.json</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2250/files#diff-9edc2486fff55fc399e0ac96dba5137948a7ea7285f5ef7846835355684b7ab5">+0/-111</a>&nbsp; </td> </tr> <tr> <td><strong>main.go</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2250/files#diff-4b8ec845da50cd58d011e69f9d1c30530ee1968df26616b8768bb1fc03433bbe">+0/-138</a>&nbsp; </td> </tr> <tr> <td><strong>BUILD.bazel</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2250/files#diff-4f5d2ea4260d490a0d6f28adde0b35eca8af77d22f3ee366a783946c53687619">+0/-25</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>config.json</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2250/files#diff-bcac20d6b3cb81f0059e766839ba1ee59a885009249501b0ba1182ebb1daea25">+0/-77</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>main.go</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2250/files#diff-78dc6bc53f1c760c66f43ff5f486bfe78a65bee8b2e0d4862293ec0892da2b29">+0/-123</a>&nbsp; </td> </tr> <tr> <td><strong>main.go</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2250/files#diff-bc6eeb1b05bcb9179525e32fac1de9926b5823ec3504be546ab10c5c9740f544">+1/-1</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td><strong>README.md</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2250/files#diff-9c32ee8446458b6fd2ae7fee52016f4b707a59978b67888cd5bee2804d934528">+3/-3</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td><strong>config.rs</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2250/files#diff-c89b88ba4d2bf0a054d0ba69a672a92c30140b8d19503d67b980a218ffe3106d">+22/-1</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>main.rs</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2250/files#diff-33b655d8730ae3e9c844ee280787d11f1b0d5343119188273f89558805f814ba">+23/-3</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>docker-compose.dev.yml</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2250/files#diff-9542f82d64bbeebd91f6236324bfe199e9657e2cb1fd9779d5d6dcdcf9cd4de1">+23/-34</a>&nbsp; </td> </tr> <tr> <td><strong>docker-compose.elx.yml</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2250/files#diff-9562070d7ad4a3e9b2d06567008cf35de1d96448d914b3b45bf6c36d97cdd914">+117/-0</a>&nbsp; </td> </tr> <tr> <td><strong>docker-compose.spiffe.yml</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2250/files#diff-603fd9e7d40841d174f26b95d0cb0c9537430bf3f7a5da3ccbba4ea3d8ac66c9">+39/-192</a></td> </tr> <tr> <td><strong>docker-compose.yml</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2250/files#diff-e45e45baeda1c1e73482975a664062aa56f20c03dd9d64a827aba57775bed0d3">+316/-269</a></td> </tr> <tr> <td><strong>README.md</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2250/files#diff-da8fcbe728a9172b578e5d754f8e2df214c658c4321f610e63dd68bea828ab49">+6/-5</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td><strong>Dockerfile.agent-gateway</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2250/files#diff-332bc81a932ae08efa711a71b60fe0954d99bf17ebdab00a3baaa177a44de8b0">+94/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>Dockerfile.core</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2250/files#diff-34849cba9f0b40185bfcba10b1a076a87f4ab2d63e08d5a88fc932c60956df66">+0/-93</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>Dockerfile.core-elx</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2250/files#diff-5ec7a971285669999af442a0c7f141c34f7fd9180257307f5c4ed12f789a2182">+108/-0</a>&nbsp; </td> </tr> <tr> <td><strong>Dockerfile.poller</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2250/files#diff-d3ba129830fb366bfe23b00db4ef6218b10fc981d3c04842b1b3b3b367a8982f">+0/-70</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>Dockerfile.sync</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2250/files#diff-0227933b9961fd553af1d229e89d71a0271fdc475081bbcef49b587941af1eda">+0/-95</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>Dockerfile.tools</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2250/files#diff-0258db71e4070e342198965f1d046f3097640850b037df8a2287a7e239630add">+1/-2</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td><strong>Dockerfile.web</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2250/files#diff-89393e743136e5cf7e1d67b378bc56da802452c481f2d63399bf72aac3d21e67">+0/-110</a>&nbsp; </td> </tr> <tr> <td><strong>Dockerfile.web-ng</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2250/files#diff-92d43af1965575d56c3380ecc8a81024aac2ff36f039ec2d3839e9fc7852bc10">+6/-0</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td><strong>agent-minimal.docker.json</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2250/files#diff-1f09fad94636c90373af8e270f6ba0332ae4f4d1df50a4909729280a3a9691e6">+6/-6</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td><strong>agent.docker.json</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2250/files#diff-5d33fe703515d03076d31261ecf946e9c6fc668cf5bf65099d49b670739e455e">+5/-20</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>agent.mtls.json</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2250/files#diff-008f2216f159a9bd5db9cc90baaf6f1e64487df7af05b56ab3b9d6c4946aa95f">+7/-10</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>bootstrap-nested-spire.sh</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2250/files#diff-ab4746a08fb1e0b307a1e47660cd22182e283a087cba87dcbff0fdfe750f44f1">+0/-80</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>.gitkeep</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2250/files#diff-d72c41aab2d6f2c230a4340dfefe7917cdd12bed942c825aa0d4c9875a637bac">+1/-0</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td><strong>datasvc.docker.json</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2250/files#diff-3f2719d3dbfe042e8383739e3c78e74e5f851a44e5e46bea8e79c4b79fdcc34f">+3/-2</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td><strong>datasvc.mtls.json</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2250/files#diff-3a45619e57f1e6e9a31486ec7fffb33ef246e271f82bac272ee0a946b88da70a">+14/-1</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>db-event-writer.docker.json</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2250/files#diff-9fc51271f7ef5bb460160013e24e44e829b730656891d26fc49d5fe72fbb3147">+15/-11</a>&nbsp; </td> </tr> <tr> <td><strong>db-event-writer.mtls.json</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2250/files#diff-7a33f95f7545499abf0ed9fc91b58499ab209639e4885019579c959583fc7496">+10/-8</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>FRICTION_POINTS.md</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2250/files#diff-b0653c58880f810ba832c0500733d63de309db98b43009fe73a1862494cf41bd">+0/-355</a>&nbsp; </td> </tr> <tr> <td><strong>README.md</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2250/files#diff-31849f033cfc932acee35f549c069abb1f36101c352e553dd6bff8713b29f98c">+0/-207</a>&nbsp; </td> </tr> <tr> <td><strong>SETUP_GUIDE.md</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2250/files#diff-b4914f8640a78038e45f51235a624535672680dc902de5f107fc051f4f281913">+0/-307</a>&nbsp; </td> </tr> <tr> <td><strong>docker-compose.edge-e2e.yml</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2250/files#diff-575d19ea771bdf8102cb9729db43a1bfd6afc2527160e54105beeac2e314f362">+0/-27</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>manage-packages.sh</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2250/files#diff-3c2ff6febbddb956c71557894adaf7d0a39a1f20dda120fe126364946bc47280">+0/-211</a>&nbsp; </td> </tr> <tr> <td><strong>setup-edge-e2e.sh</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2250/files#diff-2714e2c7e111f69ea9e9f5ddd7f6a70fa5ea96e3a53b851cb13b8b8b7cd12917">+0/-198</a>&nbsp; </td> </tr> <tr> <td><strong>edge-poller-restart.sh</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2250/files#diff-96a8fe52c38fd0d7c14895127df34a27be311cac89c53d28ee178661b629bd22">+0/-178</a>&nbsp; </td> </tr> <tr> <td><strong>downstream-agent.conf</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2250/files#diff-747de0375ced42af978ca7dac239862bdabb7f6bd0bd634f134b485517a7b4ee">+0/-32</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>env</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2250/files#diff-686f1a954c542f2ec9bf14c3170648b65190ad242c7f3a95a0f872ae41b8b1c6">+0/-4</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td><strong>server.conf</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2250/files#diff-025f5b5ab79526cf549ca1fdb90dd659ba76b438f05a7f77d916d18728c4b572">+0/-51</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>upstream-agent.conf</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2250/files#diff-e8a869ddf4affa31536a8d4e4e6f09c40072a7026da2c609d93c6ecf04138902">+0/-32</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>entrypoint-certs.sh</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2250/files#diff-83d6800b184a5233c66c69766286b0a60fece1bc64addb112d9f8dc019437f05">+13/-9</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>Additional files not shown</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2250/files#diff-2f328e4cd8dbe3ad193e49d92bcf045f47a6b72b1e9487d366f6b8288589b4ca"></a></td> </tr> </table></details></td></tr></tbody></table> </details> ___
qodo-code-review[bot] commented 2026-01-11 18:43:30 +00:00 (Migrated from github.com)
Author
Owner

Imported GitHub PR comment.

Original author: @qodo-code-review[bot]
Original URL: https://github.com/carverauto/serviceradar/pull/2250#issuecomment-3735392523
Original created: 2026-01-11T18:43:30Z

PR Compliance Guide 🔍

Below is a summary of compliance checks for this PR:

Security Compliance
Authorization bypass

Description: The new load_tenant/1 performs Ash.get(Tenant, tenant_id, authorize?: false), which can
bypass authorization checks and may enable tenant enumeration or cross-tenant data access
if an attacker can influence tenant_id (e.g., via a forged/compromised current_scope).
index.ex [981-999]

Referred Code
defp get_tenant(socket) do
  case socket.assigns[:current_scope] do
    %{active_tenant: %Tenant{} = tenant} ->
      tenant

    %{user: %{tenant_id: tenant_id}} when not is_nil(tenant_id) ->
      load_tenant(tenant_id)

    _ ->
      nil
  end
end

defp load_tenant(tenant_id) do
  case Ash.get(Tenant, tenant_id, authorize?: false) do
    {:ok, %Tenant{} = tenant} -> tenant
    _ -> nil
  end
end

DoS via atom conversion

Description: build_create_form/2 converts user-controlled "component_type" via
String.to_existing_atom/1, which is safe from atom exhaustion but can raise on unexpected
values and potentially be abused to crash the LiveView process (DoS) if invalid
component_type values reach this path.
index.ex [947-977]

Referred Code
# Build AshPhoenix.Form for creating OnboardingPackage
defp build_create_form(tenant, security_mode) do
  AshPhoenix.Form.for_create(OnboardingPackage, :create,
    domain: ServiceRadar.Edge,
    tenant: tenant,
    transform_params: fn _form, params, _action ->
      # Convert component_type string to atom if needed
      params =
        case params["component_type"] do
          type when is_binary(type) and type != "" ->
            Map.put(params, "component_type", String.to_existing_atom(type))

          _ ->
            params
        end

      # Set security mode from environment config
      params = Map.put(params, "security_mode", security_mode)

      # Parse checker_config_json if present
      case params["checker_config_json"] do



 ... (clipped 10 lines)
Ticket Compliance
🎫 No ticket provided
  • Create ticket/issue
Codebase Duplication Compliance
Codebase context is not defined

Follow the guide to enable codebase context checks.

Custom Compliance
🟢
Generic: Meaningful Naming and Self-Documenting Code

Objective: Ensure all identifiers clearly express their purpose and intent, making code
self-documenting

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

🔴
Generic: Robust Error Handling and Edge Case Management

Objective: Ensure comprehensive error handling that provides meaningful context and graceful
degradation

Status:
Crashable atom cast: The code converts user-controlled component_type with String.to_existing_atom/1 without a
whitelist/guard which can raise and crash the LiveView instead of handling invalid input
gracefully.

Referred Code
# Convert component_type string to atom if needed
params =
  case params["component_type"] do
    type when is_binary(type) and type != "" ->
      Map.put(params, "component_type", String.to_existing_atom(type))

    _ ->
      params
  end

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Security-First Input Validation and Data Handling

Objective: Ensure all data inputs are validated, sanitized, and handled securely to prevent
vulnerabilities

Status:
Missing input hardening: The component_type parameter is not strictly validated before being used (including
conversion to an atom), allowing crafted input to trigger exceptions and potentially
impact availability.

Referred Code
transform_params: fn _form, params, _action ->
  # Convert component_type string to atom if needed
  params =
    case params["component_type"] do
      type when is_binary(type) and type != "" ->
        Map.put(params, "component_type", String.to_existing_atom(type))

      _ ->
        params
    end

  # Set security mode from environment config
  params = Map.put(params, "security_mode", security_mode)

  # Parse checker_config_json if present
  case params["checker_config_json"] do
    json when is_binary(json) and json != "" ->
      case Jason.decode(json) do
        {:ok, config} -> Map.put(params, "checker_config_json", config)
        {:error, _} -> params
      end



 ... (clipped 6 lines)

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Comprehensive Audit Trails

Objective: To create a detailed and reliable record of critical system actions for security analysis
and compliance.

Status:
Audit coverage unclear: The PR adds create/revoke/delete flows for onboarding packages but the diff does not show
whether these critical actions are consistently recorded in an immutable audit trail with
actor, timestamp, action, and outcome.

Referred Code
def handle_event("create_package", %{"form" => params}, socket) do
  form = AshPhoenix.Form.validate(socket.assigns.create_form, params)

  if form.source.valid? do
    # Show loading state while creating package and generating certificates
    socket = assign(socket, :creating, true)

    # Extract validated form data
    actor = get_actor(socket)
    tenant = get_tenant(socket)
    attrs = build_package_attrs_from_form(params, socket.assigns.security_mode)

    # Use create_with_tenant_cert for automatic certificate generation
    # This will auto-generate the tenant CA if it doesn't exist
    result =
      OnboardingPackages.create_with_tenant_cert(attrs,
        actor: actor,
        tenant: tenant
      )

    case result do



 ... (clipped 86 lines)

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Error Handling

Objective: To prevent the leakage of sensitive system information through error messages while
providing sufficient detail for internal debugging.

Status:
User error details: The UI flashes "Failed to create package: #{error_msg}" where error_msg may
include internal Ash/DB details depending on error content, and the diff does not show
sanitization guarantees.

Referred Code
  {:error, error} ->
    error_msg = format_error(error)

    {:noreply,
     socket
     |> assign(:creating, false)
     |> put_flash(:error, "Failed to create package: #{error_msg}")}
end

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Logging Practices

Objective: To ensure logs are useful for debugging and auditing without exposing sensitive
information like PII, PHI, or cardholder data.

Status:
Logging not shown: The diff does not provide enough visibility into application logging to verify structured
logging and confirm no sensitive data (tokens/certs/PII) is logged during the new
onboarding flows.

Referred Code
import Config

# Production configuration is typically set via runtime.exs
# This file is for compile-time production settings only

config :logger, level: :info

Learn more about managing compliance generic rules or creating your own custom rules

Compliance status legend 🟢 - Fully Compliant
🟡 - Partial Compliant
🔴 - Not Compliant
- Requires Further Human Verification
🏷️ - Compliance label
Imported GitHub PR comment. Original author: @qodo-code-review[bot] Original URL: https://github.com/carverauto/serviceradar/pull/2250#issuecomment-3735392523 Original created: 2026-01-11T18:43:30Z --- ## PR Compliance Guide 🔍 <!-- https://github.com/carverauto/serviceradar/commit/84e3228f77227d5915d622e6b237048b29808408 --> Below is a summary of compliance checks for this PR:<br> <table><tbody><tr><td colspan='2'><strong>Security Compliance</strong></td></tr> <tr><td rowspan=2>⚪</td> <td><details><summary><strong>Authorization bypass </strong></summary><br> <b>Description:</b> The new <code>load_tenant/1</code> performs <code>Ash.get(Tenant, tenant_id, authorize?: false)</code>, which can <br>bypass authorization checks and may enable tenant enumeration or cross-tenant data access <br>if an attacker can influence <code>tenant_id</code> (e.g., via a forged/compromised <code>current_scope</code>).<br> <strong><a href='https://github.com/carverauto/serviceradar/pull/2250/files#diff-d0d22a987aa942ef25088696daea862e36bd8ca083ce29e4670f63e62947c62fR981-R999'>index.ex [981-999]</a></strong><br> <details open><summary>Referred Code</summary> ```elixir defp get_tenant(socket) do case socket.assigns[:current_scope] do %{active_tenant: %Tenant{} = tenant} -> tenant %{user: %{tenant_id: tenant_id}} when not is_nil(tenant_id) -> load_tenant(tenant_id) _ -> nil end end defp load_tenant(tenant_id) do case Ash.get(Tenant, tenant_id, authorize?: false) do {:ok, %Tenant{} = tenant} -> tenant _ -> nil end end ``` </details></details></td></tr> <tr><td><details><summary><strong>DoS via atom conversion </strong></summary><br> <b>Description:</b> <code>build_create_form/2</code> converts user-controlled <code>"component_type"</code> via <br><code>String.to_existing_atom/1</code>, which is safe from atom exhaustion but can raise on unexpected <br>values and potentially be abused to crash the LiveView process (DoS) if invalid <br><code>component_type</code> values reach this path.<br> <strong><a href='https://github.com/carverauto/serviceradar/pull/2250/files#diff-d0d22a987aa942ef25088696daea862e36bd8ca083ce29e4670f63e62947c62fR947-R977'>index.ex [947-977]</a></strong><br> <details open><summary>Referred Code</summary> ```elixir # Build AshPhoenix.Form for creating OnboardingPackage defp build_create_form(tenant, security_mode) do AshPhoenix.Form.for_create(OnboardingPackage, :create, domain: ServiceRadar.Edge, tenant: tenant, transform_params: fn _form, params, _action -> # Convert component_type string to atom if needed params = case params["component_type"] do type when is_binary(type) and type != "" -> Map.put(params, "component_type", String.to_existing_atom(type)) _ -> params end # Set security mode from environment config params = Map.put(params, "security_mode", security_mode) # Parse checker_config_json if present case params["checker_config_json"] do ... (clipped 10 lines) ``` </details></details></td></tr> <tr><td colspan='2'><strong>Ticket Compliance</strong></td></tr> <tr><td>⚪</td><td><details><summary>🎫 <strong>No ticket provided </strong></summary> - [ ] Create ticket/issue <!-- /create_ticket --create_ticket=true --> </details></td></tr> <tr><td colspan='2'><strong>Codebase Duplication Compliance</strong></td></tr> <tr><td>⚪</td><td><details><summary><strong>Codebase context is not defined </strong></summary> Follow the <a href='https://qodo-merge-docs.qodo.ai/core-abilities/rag_context_enrichment/'>guide</a> to enable codebase context checks. </details></td></tr> <tr><td colspan='2'><strong>Custom Compliance</strong></td></tr> <tr><td rowspan=1>🟢</td><td> <details><summary><strong>Generic: Meaningful Naming and Self-Documenting Code</strong></summary><br> **Objective:** Ensure all identifiers clearly express their purpose and intent, making code <br>self-documenting<br> **Status:** Passed<br> > Learn more about managing compliance <a href='https://qodo-merge-docs.qodo.ai/tools/compliance/#configuration-options'>generic rules</a> or creating your own <a href='https://qodo-merge-docs.qodo.ai/tools/compliance/#custom-compliance'>custom rules</a> </details></td></tr> <tr><td rowspan=2>🔴</td> <td><details> <summary><strong>Generic: Robust Error Handling and Edge Case Management</strong></summary><br> **Objective:** Ensure comprehensive error handling that provides meaningful context and graceful <br>degradation<br> **Status:** <br><a href='https://github.com/carverauto/serviceradar/pull/2250/files#diff-d0d22a987aa942ef25088696daea862e36bd8ca083ce29e4670f63e62947c62fR953-R961'><strong>Crashable atom cast</strong></a>: The code converts user-controlled <code>component_type</code> with <code>String.to_existing_atom/1</code> without a <br>whitelist/guard which can raise and crash the LiveView instead of handling invalid input <br>gracefully.<br> <details open><summary>Referred Code</summary> ```elixir # Convert component_type string to atom if needed params = case params["component_type"] do type when is_binary(type) and type != "" -> Map.put(params, "component_type", String.to_existing_atom(type)) _ -> params end ``` </details> > Learn more about managing compliance <a href='https://qodo-merge-docs.qodo.ai/tools/compliance/#configuration-options'>generic rules</a> or creating your own <a href='https://qodo-merge-docs.qodo.ai/tools/compliance/#custom-compliance'>custom rules</a> </details></td></tr> <tr><td><details> <summary><strong>Generic: Security-First Input Validation and Data Handling</strong></summary><br> **Objective:** Ensure all data inputs are validated, sanitized, and handled securely to prevent <br>vulnerabilities<br> **Status:** <br><a href='https://github.com/carverauto/serviceradar/pull/2250/files#diff-d0d22a987aa942ef25088696daea862e36bd8ca083ce29e4670f63e62947c62fR952-R978'><strong>Missing input hardening</strong></a>: The <code>component_type</code> parameter is not strictly validated before being used (including <br>conversion to an atom), allowing crafted input to trigger exceptions and potentially <br>impact availability.<br> <details open><summary>Referred Code</summary> ```elixir transform_params: fn _form, params, _action -> # Convert component_type string to atom if needed params = case params["component_type"] do type when is_binary(type) and type != "" -> Map.put(params, "component_type", String.to_existing_atom(type)) _ -> params end # Set security mode from environment config params = Map.put(params, "security_mode", security_mode) # Parse checker_config_json if present case params["checker_config_json"] do json when is_binary(json) and json != "" -> case Jason.decode(json) do {:ok, config} -> Map.put(params, "checker_config_json", config) {:error, _} -> params end ... (clipped 6 lines) ``` </details> > Learn more about managing compliance <a href='https://qodo-merge-docs.qodo.ai/tools/compliance/#configuration-options'>generic rules</a> or creating your own <a href='https://qodo-merge-docs.qodo.ai/tools/compliance/#custom-compliance'>custom rules</a> </details></td></tr> <tr><td rowspan=3>⚪</td> <td><details> <summary><strong>Generic: Comprehensive Audit Trails</strong></summary><br> **Objective:** To create a detailed and reliable record of critical system actions for security analysis <br>and compliance.<br> **Status:** <br><a href='https://github.com/carverauto/serviceradar/pull/2250/files#diff-d0d22a987aa942ef25088696daea862e36bd8ca083ce29e4670f63e62947c62fR121-R227'><strong>Audit coverage unclear</strong></a>: The PR adds create/revoke/delete flows for onboarding packages but the diff does not show <br>whether these critical actions are consistently recorded in an immutable audit trail with <br>actor, timestamp, action, and outcome.<br> <details open><summary>Referred Code</summary> ```elixir def handle_event("create_package", %{"form" => params}, socket) do form = AshPhoenix.Form.validate(socket.assigns.create_form, params) if form.source.valid? do # Show loading state while creating package and generating certificates socket = assign(socket, :creating, true) # Extract validated form data actor = get_actor(socket) tenant = get_tenant(socket) attrs = build_package_attrs_from_form(params, socket.assigns.security_mode) # Use create_with_tenant_cert for automatic certificate generation # This will auto-generate the tenant CA if it doesn't exist result = OnboardingPackages.create_with_tenant_cert(attrs, actor: actor, tenant: tenant ) case result do ... (clipped 86 lines) ``` </details> > Learn more about managing compliance <a href='https://qodo-merge-docs.qodo.ai/tools/compliance/#configuration-options'>generic rules</a> or creating your own <a href='https://qodo-merge-docs.qodo.ai/tools/compliance/#custom-compliance'>custom rules</a> </details></td></tr> <tr><td><details> <summary><strong>Generic: Secure Error Handling</strong></summary><br> **Objective:** To prevent the leakage of sensitive system information through error messages while <br>providing sufficient detail for internal debugging.<br> **Status:** <br><a href='https://github.com/carverauto/serviceradar/pull/2250/files#diff-d0d22a987aa942ef25088696daea862e36bd8ca083ce29e4670f63e62947c62fR171-R178'><strong>User error details</strong></a>: The UI flashes <code>&quot;Failed to create package: #{error_msg}&quot;</code> where <code>error_msg</code> may <br>include internal Ash/DB details depending on error content, and the diff does not show <br>sanitization guarantees.<br> <details open><summary>Referred Code</summary> ```elixir {:error, error} -> error_msg = format_error(error) {:noreply, socket |> assign(:creating, false) |> put_flash(:error, "Failed to create package: #{error_msg}")} end ``` </details> > Learn more about managing compliance <a href='https://qodo-merge-docs.qodo.ai/tools/compliance/#configuration-options'>generic rules</a> or creating your own <a href='https://qodo-merge-docs.qodo.ai/tools/compliance/#custom-compliance'>custom rules</a> </details></td></tr> <tr><td><details> <summary><strong>Generic: Secure Logging Practices</strong></summary><br> **Objective:** To ensure logs are useful for debugging and auditing without exposing sensitive <br>information like PII, PHI, or cardholder data.<br> **Status:** <br><a href='https://github.com/carverauto/serviceradar/pull/2250/files#diff-4fe837307f8710c783ecb9ae7595bb7bb9ec37d8522248bd081fa256803c3e92R1-R6'><strong>Logging not shown</strong></a>: The diff does not provide enough visibility into application logging to verify structured <br>logging and confirm no sensitive data (tokens/certs/PII) is logged during the new <br>onboarding flows.<br> <details open><summary>Referred Code</summary> ```elixir import Config # Production configuration is typically set via runtime.exs # This file is for compile-time production settings only config :logger, level: :info ``` </details> > Learn more about managing compliance <a href='https://qodo-merge-docs.qodo.ai/tools/compliance/#configuration-options'>generic rules</a> or creating your own <a href='https://qodo-merge-docs.qodo.ai/tools/compliance/#custom-compliance'>custom rules</a> </details></td></tr> <tr><td align="center" colspan="2"> <!-- placeholder --> <!-- /compliance --update_compliance=true --> </td></tr></tbody></table> <details><summary>Compliance status legend</summary> 🟢 - Fully Compliant<br> 🟡 - Partial Compliant<br> 🔴 - Not Compliant<br> ⚪ - Requires Further Human Verification<br> 🏷️ - Compliance label<br> </details>
qodo-code-review[bot] commented 2026-01-11 18:45:11 +00:00 (Migrated from github.com)
Author
Owner

Imported GitHub PR comment.

Original author: @qodo-code-review[bot]
Original URL: https://github.com/carverauto/serviceradar/pull/2250#issuecomment-3735398070
Original created: 2026-01-11T18:45:11Z

PR Code Suggestions

Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
General
Add tenant to edit form

In build_edit_form/2, provide the tenant schema to AshPhoenix.Form.for_update to
ensure the form operates within the correct tenant context.

web-ng/lib/serviceradar_web_ng_web/live/admin/integration_live/index.ex [1336-1341]

 defp build_edit_form(source, actor) do
   source
   |> AshPhoenix.Form.for_update(:update,
+    tenant: TenantSchemas.schema_for_id(source.tenant_id),
     domain: Integrations,
     actor: actor,
     transform_params: fn _form, params, _action ->

[To ensure code accuracy, apply this suggestion manually]

Suggestion importance[1-10]: 9

__

Why: This suggestion fixes a critical bug where the edit form was created without a tenant context, which would cause updates to fail in a multi-tenant environment.

High
Log RPC failures for cluster debugging

Add a case clause to handle and log {:badrpc, reason} errors from :rpc.call to
avoid silently ignoring RPC failures.

web-ng/lib/serviceradar_web_ng_web/live/infrastructure_live/index.ex [736-741]

 defp fetch_gateways_from_node(node) do
   case :rpc.call(node, ServiceRadar.GatewayTracker, :list_gateways, [], 1_000) do
-    gateways when is_list(gateways) -> gateways
-    _ -> []
+    gateways when is_list(gateways) -> 
+      gateways
+    {:badrpc, reason} ->
+      Logger.warning("Failed to fetch gateways from #{node}: #{inspect(reason)}")
+      []
+    _ -> 
+      []
   end
 end

[To ensure code accuracy, apply this suggestion manually]

Suggestion importance[1-10]: 7

__

Why: The suggestion correctly points out that RPC failures are being silently ignored. Adding logging for {:badrpc, reason} significantly improves the observability and debuggability of the distributed system.

Medium
Remove redundant unique indexes on primary keys

Remove the redundant unique indexes on the uid columns for the ocsf_agents and
ocsf_devices tables, as these columns are already primary keys.

elixir/serviceradar_core/priv/repo/tenant_migrations/20260107043446_initial_schema.exs [591-797]

-    create unique_index(:ocsf_agents, [:uid], name: "ocsf_agents_unique_uid_index")
-
     alter table(:service_checks, prefix: prefix()) do
 ...
-    create unique_index(:ocsf_devices, [:uid], name: "ocsf_devices_unique_uid_index")
-
     alter table(:service_checks, prefix: prefix()) do

[To ensure code accuracy, apply this suggestion manually]

Suggestion importance[1-10]: 6

__

Why: The suggestion correctly identifies redundant unique indexes on columns already defined as primary keys, which improves database performance and simplifies the migration script.

Low
Remove raw JSON params

In parse_json_field/3, remove the original raw JSON string parameter from the
params map after it has been successfully decoded or if decoding fails.

web-ng/lib/serviceradar_web_ng_web/live/admin/integration_live/index.ex [1368-1379]

 defp parse_json_field(params, json_key, target_key) do
   case Map.get(params, json_key) do
     json when is_binary(json) and json != "" ->
       case Jason.decode(json) do
-        {:ok, decoded} -> Map.put(params, target_key, decoded)
-        {:error, _} -> params
+        {:ok, decoded} -> params |> Map.put(target_key, decoded) |> Map.delete(json_key)
+        {:error, _} -> Map.delete(params, json_key)
       end
     _ ->
       params
   end
 end

[To ensure code accuracy, apply this suggestion manually]

Suggestion importance[1-10]: 4

__

Why: The suggestion improves code hygiene by removing the original JSON string parameter after it has been parsed, preventing it from being unintentionally passed to Ash actions.

Low
Possible issue
Include tenant in update calls

In the toggle_enabled event handler, pass the tenant schema to the Ash.update
call to ensure the operation is performed within the correct tenant context.

web-ng/lib/serviceradar_web_ng_web/live/admin/integration_live/index.ex [350]

-case Ash.update(source, action, actor: actor) do
+case Ash.update(source, action, tenant: tenant_schema(tenant_id), actor: actor) do

[To ensure code accuracy, apply this suggestion manually]

Suggestion importance[1-10]: 9

__

Why: This suggestion fixes a critical bug where Ash.update was called without a tenant context, which would cause it to fail in a multi-tenant environment.

High
Safely convert component type to atom

Replace the unsafe String.to_existing_atom/1 call with a pattern that validates
the input against a list of known component types before converting it to an
atom.

web-ng/lib/serviceradar_web_ng_web/live/admin/edge_package_live/index.ex [954-961]

 params =
   case params["component_type"] do
-    type when is_binary(type) and type != "" ->
-      Map.put(params, "component_type", String.to_existing_atom(type))
+    type when type in ["gateway", "agent", "checker", "sync"] ->
+      Map.put(params, "component_type", String.to_atom(type))
 
     _ ->
       params
   end

[To ensure code accuracy, apply this suggestion manually]

Suggestion importance[1-10]: 9

__

Why: This is a critical suggestion that prevents a potential server crash. Using String.to_existing_atom/1 with user-provided data is unsafe; the proposed change correctly validates the input against a safelist before conversion.

High
Consolidate multiple RPC calls into one

Refactor fetch_node_info/1 to use a single RPC call instead of multiple
sequential calls. This will prevent the LiveView process from blocking for a
long time if the remote node is unresponsive.

web-ng/lib/serviceradar_web_ng_web/live/agent_live/show.ex [164-189]

 defp fetch_node_info(node) when is_atom(node) do
+  # It's assumed a function like `ServiceRadar.Gateway.System.info/0` is defined
+  # on the remote node, which collects all the necessary metrics in one go.
   try do
-    memory = :rpc.call(node, :erlang, :memory, [], 5000)
-    {uptime_ms, _} = :rpc.call(node, :erlang, :statistics, [:wall_clock], 5000)
-
-    %{
-      process_count: :rpc.call(node, :erlang, :system_info, [:process_count], 5000),
-      port_count: :rpc.call(node, :erlang, :system_info, [:port_count], 5000),
-      otp_release: to_string(:rpc.call(node, :erlang, :system_info, [:otp_release], 5000)),
-      schedulers: :rpc.call(node, :erlang, :system_info, [:schedulers], 5000),
-      schedulers_online: :rpc.call(node, :erlang, :system_info, [:schedulers_online], 5000),
-      uptime_ms: uptime_ms,
-      memory_total: memory[:total],
-      memory_processes: memory[:processes],
-      memory_system: memory[:system],
-      memory_atom: memory[:atom],
-      memory_binary: memory[:binary],
-      memory_code: memory[:code],
-      memory_ets: memory[:ets]
-    }
+    :rpc.call(node, ServiceRadar.Gateway.System, :info, [], 5000)
   rescue
     _ -> nil
   catch
     :exit, _ -> nil
   end
 end

[To ensure code accuracy, apply this suggestion manually]

Suggestion importance[1-10]: 8

__

Why: The suggestion correctly identifies a significant performance and reliability issue where multiple sequential RPC calls can block the LiveView process for an extended period, leading to timeouts. Consolidating these into a single RPC call is a critical improvement for the robustness of this feature.

Medium
Remove redundant column removal before dropping tables

In the down/0 function, remove the redundant alter table ... remove blocks for
tables that are subsequently dropped, as dropping a table automatically removes
all its columns.

elixir/serviceradar_core/priv/repo/tenant_migrations/20260107043446_initial_schema.exs [1086-1125]

-    alter table(:alerts, prefix: prefix()) do
-      remove :updated_at
-      remove :created_at
-      remove :tenant_id
-      remove :tags
-      remove :metadata
-      remove :suppressed_until
-      remove :last_notification_at
-      remove :notification_count
-      remove :escalation_reason
-      remove :escalation_level
-      remove :escalated_at
-      remove :resolution_note
-      remove :resolved_by
-      remove :resolved_at
-      remove :acknowledged_by
-      remove :acknowledged_at
-      remove :triggered_at
-      remove :comparison
-      remove :threshold_value
-      remove :metric_value
-      remove :metric_name
-      remove :agent_uid
-      remove :device_uid
-      remove :service_check_id
-      remove :source_id
-      remove :source_type
-      remove :status
-      remove :severity
-      remove :description
-      remove :title
-    end
-
     drop constraint(:monitoring_events, "monitoring_events_alert_id_fkey")
 
     alter table(:monitoring_events, prefix: prefix()) do
       modify :alert_id, :uuid
     end
 
     drop table(:alerts, prefix: prefix())

[To ensure code accuracy, apply this suggestion manually]

Suggestion importance[1-10]: 6

__

Why: The suggestion correctly identifies that removing columns individually before dropping the entire table is redundant. Applying this change simplifies the migration's down function and improves clarity.

Low
Handle unexpected errors during package creation

Wrap the package creation logic in a try/rescue block to ensure the :creating
flag is reset if an unexpected error occurs.

web-ng/lib/serviceradar_web_ng_web/live/admin/edge_package_live/index.ex [121-126]

 def handle_event("create_package", %{"form" => params}, socket) do
   form = AshPhoenix.Form.validate(socket.assigns.create_form, params)
 
   if form.source.valid? do
-    # Show loading state while creating package and generating certificates
     socket = assign(socket, :creating, true)
+    
+    try do
+      # Extract validated form data and create package
+      ...
+    rescue
+      error ->
+        {:noreply,
+         socket
+         |> assign(:creating, false)
+         |> put_flash(:error, "Unexpected error: #{inspect(error)}")}
+    end
+  else
     ...

[To ensure code accuracy, apply this suggestion manually]

Suggestion importance[1-10]: 6

__

Why: The suggestion correctly identifies that an unhandled exception during package creation would leave the UI in a permanent loading state, and proposes a try/rescue block to improve robustness and user experience.

Low
Security
Prevent map injection by validating input

Prevent a map injection vulnerability in the update_query event handler by
validating the field parameter against an allowlist of permitted fields before
updating the query map.

web-ng/lib/serviceradar_web_ng_web/live/admin/integration_live/index.ex [232-241]

     def handle_event("update_query", %{"id" => id, "field" => field, "value" => value}, socket) do
       id = String.to_integer(id)
+      allowed_fields = ["label", "query"]
 
       queries =
-        Enum.map(socket.assigns.form_queries, fn q ->
-          if q["id"] == id, do: Map.put(q, field, value), else: q
-        end)
+        if field in allowed_fields do
+          Enum.map(socket.assigns.form_queries, fn q ->
+            if q["id"] == id, do: Map.put(q, field, value), else: q
+          end)
+        else
+          socket.assigns.form_queries
+        end
 
       {:noreply, assign(socket, :form_queries, queries)}
     end

[To ensure code accuracy, apply this suggestion manually]

Suggestion importance[1-10]: 8

__

Why: The suggestion correctly identifies a map injection vulnerability by allowing a client-provided field to update a map. Implementing an allowlist for the field parameter is a critical security fix.

Medium
Remove authorization bypass for tenant loading

Remove the authorize?: false option when calling Ash.get to ensure tenant data
access is properly authorized by the framework's policies.

web-ng/lib/serviceradar_web_ng_web/live/settings/cluster_live/index.ex [994-998]

 defp load_tenant(tenant_id) do
-  case Ash.get(Tenant, tenant_id, authorize?: false) do
+  case Ash.get(Tenant, tenant_id) do
     {:ok, %Tenant{} = tenant} -> tenant
     _ -> nil
   end
 end

[To ensure code accuracy, apply this suggestion manually]

Suggestion importance[1-10]: 8

__

Why: This is a valid security recommendation to avoid bypassing the authorization system. Relying on Ash policies by removing authorize?: false is a best practice that strengthens security.

Medium
  • More
Imported GitHub PR comment. Original author: @qodo-code-review[bot] Original URL: https://github.com/carverauto/serviceradar/pull/2250#issuecomment-3735398070 Original created: 2026-01-11T18:45:11Z --- ## PR Code Suggestions ✨ <!-- 84e3228 --> Explore these optional code suggestions: <table><thead><tr><td><strong>Category</strong></td><td align=left><strong>Suggestion&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </strong></td><td align=center><strong>Impact</strong></td></tr><tbody><tr><td rowspan=4>General</td> <td> <details><summary>Add tenant to edit form</summary> ___ **In <code>build_edit_form/2</code>, provide the <code>tenant</code> schema to <code>AshPhoenix.Form.for_update</code> to <br>ensure the form operates within the correct tenant context.** [web-ng/lib/serviceradar_web_ng_web/live/admin/integration_live/index.ex [1336-1341]](https://github.com/carverauto/serviceradar/pull/2250/files#diff-61d0262af13a42905ebbd793e83537e61bfc09493df1664a82eb2536980ee1cdR1336-R1341) ```diff defp build_edit_form(source, actor) do source |> AshPhoenix.Form.for_update(:update, + tenant: TenantSchemas.schema_for_id(source.tenant_id), domain: Integrations, actor: actor, transform_params: fn _form, params, _action -> ``` `[To ensure code accuracy, apply this suggestion manually]` <details><summary>Suggestion importance[1-10]: 9</summary> __ Why: This suggestion fixes a critical bug where the edit form was created without a tenant context, which would cause updates to fail in a multi-tenant environment. </details></details></td><td align=center>High </td></tr><tr><td> <details><summary>Log RPC failures for cluster debugging</summary> ___ **Add a case clause to handle and log <code>{:badrpc, reason}</code> errors from <code>:rpc.call</code> to <br>avoid silently ignoring RPC failures.** [web-ng/lib/serviceradar_web_ng_web/live/infrastructure_live/index.ex [736-741]](https://github.com/carverauto/serviceradar/pull/2250/files#diff-f1af30a84da554ef3d43b226a9303174b33dd5d27e23e9b702031483074e5f54R736-R741) ```diff defp fetch_gateways_from_node(node) do case :rpc.call(node, ServiceRadar.GatewayTracker, :list_gateways, [], 1_000) do - gateways when is_list(gateways) -> gateways - _ -> [] + gateways when is_list(gateways) -> + gateways + {:badrpc, reason} -> + Logger.warning("Failed to fetch gateways from #{node}: #{inspect(reason)}") + [] + _ -> + [] end end ``` `[To ensure code accuracy, apply this suggestion manually]` <details><summary>Suggestion importance[1-10]: 7</summary> __ Why: The suggestion correctly points out that RPC failures are being silently ignored. Adding logging for `{:badrpc, reason}` significantly improves the observability and debuggability of the distributed system. </details></details></td><td align=center>Medium </td></tr><tr><td> <details><summary>Remove redundant unique indexes on primary keys</summary> ___ **Remove the redundant unique indexes on the <code>uid</code> columns for the <code>ocsf_agents</code> and <br><code>ocsf_devices</code> tables, as these columns are already primary keys.** [elixir/serviceradar_core/priv/repo/tenant_migrations/20260107043446_initial_schema.exs [591-797]](https://github.com/carverauto/serviceradar/pull/2250/files#diff-0d217dc9822fab0d3390e8ec21040f98e67106e5c9126e043a9b701efcbfb576R591-R797) ```diff - create unique_index(:ocsf_agents, [:uid], name: "ocsf_agents_unique_uid_index") - alter table(:service_checks, prefix: prefix()) do ... - create unique_index(:ocsf_devices, [:uid], name: "ocsf_devices_unique_uid_index") - alter table(:service_checks, prefix: prefix()) do ``` `[To ensure code accuracy, apply this suggestion manually]` <details><summary>Suggestion importance[1-10]: 6</summary> __ Why: The suggestion correctly identifies redundant unique indexes on columns already defined as primary keys, which improves database performance and simplifies the migration script. </details></details></td><td align=center>Low </td></tr><tr><td> <details><summary>Remove raw JSON params</summary> ___ **In <code>parse_json_field/3</code>, remove the original raw JSON string parameter from the <br><code>params</code> map after it has been successfully decoded or if decoding fails.** [web-ng/lib/serviceradar_web_ng_web/live/admin/integration_live/index.ex [1368-1379]](https://github.com/carverauto/serviceradar/pull/2250/files#diff-61d0262af13a42905ebbd793e83537e61bfc09493df1664a82eb2536980ee1cdR1368-R1379) ```diff defp parse_json_field(params, json_key, target_key) do case Map.get(params, json_key) do json when is_binary(json) and json != "" -> case Jason.decode(json) do - {:ok, decoded} -> Map.put(params, target_key, decoded) - {:error, _} -> params + {:ok, decoded} -> params |> Map.put(target_key, decoded) |> Map.delete(json_key) + {:error, _} -> Map.delete(params, json_key) end _ -> params end end ``` `[To ensure code accuracy, apply this suggestion manually]` <details><summary>Suggestion importance[1-10]: 4</summary> __ Why: The suggestion improves code hygiene by removing the original JSON string parameter after it has been parsed, preventing it from being unintentionally passed to Ash actions. </details></details></td><td align=center>Low </td></tr><tr><td rowspan=5>Possible issue</td> <td> <details><summary>Include tenant in update calls</summary> ___ **In the <code>toggle_enabled</code> event handler, pass the <code>tenant</code> schema to the <code>Ash.update</code> <br>call to ensure the operation is performed within the correct tenant context.** [web-ng/lib/serviceradar_web_ng_web/live/admin/integration_live/index.ex [350]](https://github.com/carverauto/serviceradar/pull/2250/files#diff-61d0262af13a42905ebbd793e83537e61bfc09493df1664a82eb2536980ee1cdR350-R350) ```diff -case Ash.update(source, action, actor: actor) do +case Ash.update(source, action, tenant: tenant_schema(tenant_id), actor: actor) do ``` `[To ensure code accuracy, apply this suggestion manually]` <details><summary>Suggestion importance[1-10]: 9</summary> __ Why: This suggestion fixes a critical bug where `Ash.update` was called without a tenant context, which would cause it to fail in a multi-tenant environment. </details></details></td><td align=center>High </td></tr><tr><td> <details><summary>Safely convert component type to atom</summary> ___ **Replace the unsafe <code>String.to_existing_atom/1</code> call with a pattern that validates <br>the input against a list of known component types before converting it to an <br>atom.** [web-ng/lib/serviceradar_web_ng_web/live/admin/edge_package_live/index.ex [954-961]](https://github.com/carverauto/serviceradar/pull/2250/files#diff-d0d22a987aa942ef25088696daea862e36bd8ca083ce29e4670f63e62947c62fR954-R961) ```diff params = case params["component_type"] do - type when is_binary(type) and type != "" -> - Map.put(params, "component_type", String.to_existing_atom(type)) + type when type in ["gateway", "agent", "checker", "sync"] -> + Map.put(params, "component_type", String.to_atom(type)) _ -> params end ``` `[To ensure code accuracy, apply this suggestion manually]` <details><summary>Suggestion importance[1-10]: 9</summary> __ Why: This is a critical suggestion that prevents a potential server crash. Using `String.to_existing_atom/1` with user-provided data is unsafe; the proposed change correctly validates the input against a safelist before conversion. </details></details></td><td align=center>High </td></tr><tr><td> <details><summary>Consolidate multiple RPC calls into one</summary> ___ **Refactor <code>fetch_node_info/1</code> to use a single RPC call instead of multiple <br>sequential calls. This will prevent the LiveView process from blocking for a <br>long time if the remote node is unresponsive.** [web-ng/lib/serviceradar_web_ng_web/live/agent_live/show.ex [164-189]](https://github.com/carverauto/serviceradar/pull/2250/files#diff-5e622205d1abddd8ad7dcf7a8ca1be583804d622d3d38b75140e9b909cf0534aR164-R189) ```diff defp fetch_node_info(node) when is_atom(node) do + # It's assumed a function like `ServiceRadar.Gateway.System.info/0` is defined + # on the remote node, which collects all the necessary metrics in one go. try do - memory = :rpc.call(node, :erlang, :memory, [], 5000) - {uptime_ms, _} = :rpc.call(node, :erlang, :statistics, [:wall_clock], 5000) - - %{ - process_count: :rpc.call(node, :erlang, :system_info, [:process_count], 5000), - port_count: :rpc.call(node, :erlang, :system_info, [:port_count], 5000), - otp_release: to_string(:rpc.call(node, :erlang, :system_info, [:otp_release], 5000)), - schedulers: :rpc.call(node, :erlang, :system_info, [:schedulers], 5000), - schedulers_online: :rpc.call(node, :erlang, :system_info, [:schedulers_online], 5000), - uptime_ms: uptime_ms, - memory_total: memory[:total], - memory_processes: memory[:processes], - memory_system: memory[:system], - memory_atom: memory[:atom], - memory_binary: memory[:binary], - memory_code: memory[:code], - memory_ets: memory[:ets] - } + :rpc.call(node, ServiceRadar.Gateway.System, :info, [], 5000) rescue _ -> nil catch :exit, _ -> nil end end ``` `[To ensure code accuracy, apply this suggestion manually]` <details><summary>Suggestion importance[1-10]: 8</summary> __ Why: The suggestion correctly identifies a significant performance and reliability issue where multiple sequential RPC calls can block the LiveView process for an extended period, leading to timeouts. Consolidating these into a single RPC call is a critical improvement for the robustness of this feature. </details></details></td><td align=center>Medium </td></tr><tr><td> <details><summary>Remove redundant column removal before dropping tables</summary> ___ **In the <code>down/0</code> function, remove the redundant <code>alter table ... remove</code> blocks for <br>tables that are subsequently dropped, as dropping a table automatically removes <br>all its columns.** [elixir/serviceradar_core/priv/repo/tenant_migrations/20260107043446_initial_schema.exs [1086-1125]](https://github.com/carverauto/serviceradar/pull/2250/files#diff-0d217dc9822fab0d3390e8ec21040f98e67106e5c9126e043a9b701efcbfb576R1086-R1125) ```diff - alter table(:alerts, prefix: prefix()) do - remove :updated_at - remove :created_at - remove :tenant_id - remove :tags - remove :metadata - remove :suppressed_until - remove :last_notification_at - remove :notification_count - remove :escalation_reason - remove :escalation_level - remove :escalated_at - remove :resolution_note - remove :resolved_by - remove :resolved_at - remove :acknowledged_by - remove :acknowledged_at - remove :triggered_at - remove :comparison - remove :threshold_value - remove :metric_value - remove :metric_name - remove :agent_uid - remove :device_uid - remove :service_check_id - remove :source_id - remove :source_type - remove :status - remove :severity - remove :description - remove :title - end - drop constraint(:monitoring_events, "monitoring_events_alert_id_fkey") alter table(:monitoring_events, prefix: prefix()) do modify :alert_id, :uuid end drop table(:alerts, prefix: prefix()) ``` `[To ensure code accuracy, apply this suggestion manually]` <details><summary>Suggestion importance[1-10]: 6</summary> __ Why: The suggestion correctly identifies that removing columns individually before dropping the entire table is redundant. Applying this change simplifies the migration's `down` function and improves clarity. </details></details></td><td align=center>Low </td></tr><tr><td> <details><summary>Handle unexpected errors during package creation</summary> ___ **Wrap the package creation logic in a <code>try/rescue</code> block to ensure the <code>:creating</code> <br>flag is reset if an unexpected error occurs.** [web-ng/lib/serviceradar_web_ng_web/live/admin/edge_package_live/index.ex [121-126]](https://github.com/carverauto/serviceradar/pull/2250/files#diff-d0d22a987aa942ef25088696daea862e36bd8ca083ce29e4670f63e62947c62fR121-R126) ```diff def handle_event("create_package", %{"form" => params}, socket) do form = AshPhoenix.Form.validate(socket.assigns.create_form, params) if form.source.valid? do - # Show loading state while creating package and generating certificates socket = assign(socket, :creating, true) + + try do + # Extract validated form data and create package + ... + rescue + error -> + {:noreply, + socket + |> assign(:creating, false) + |> put_flash(:error, "Unexpected error: #{inspect(error)}")} + end + else ... ``` `[To ensure code accuracy, apply this suggestion manually]` <details><summary>Suggestion importance[1-10]: 6</summary> __ Why: The suggestion correctly identifies that an unhandled exception during package creation would leave the UI in a permanent loading state, and proposes a `try/rescue` block to improve robustness and user experience. </details></details></td><td align=center>Low </td></tr><tr><td rowspan=2>Security</td> <td> <details><summary>Prevent map injection by validating input</summary> ___ **Prevent a map injection vulnerability in the <code>update_query</code> event handler by <br>validating the <code>field</code> parameter against an allowlist of permitted fields before <br>updating the query map.** [web-ng/lib/serviceradar_web_ng_web/live/admin/integration_live/index.ex [232-241]](https://github.com/carverauto/serviceradar/pull/2250/files#diff-61d0262af13a42905ebbd793e83537e61bfc09493df1664a82eb2536980ee1cdR232-R241) ```diff def handle_event("update_query", %{"id" => id, "field" => field, "value" => value}, socket) do id = String.to_integer(id) + allowed_fields = ["label", "query"] queries = - Enum.map(socket.assigns.form_queries, fn q -> - if q["id"] == id, do: Map.put(q, field, value), else: q - end) + if field in allowed_fields do + Enum.map(socket.assigns.form_queries, fn q -> + if q["id"] == id, do: Map.put(q, field, value), else: q + end) + else + socket.assigns.form_queries + end {:noreply, assign(socket, :form_queries, queries)} end ``` `[To ensure code accuracy, apply this suggestion manually]` <details><summary>Suggestion importance[1-10]: 8</summary> __ Why: The suggestion correctly identifies a map injection vulnerability by allowing a client-provided `field` to update a map. Implementing an allowlist for the `field` parameter is a critical security fix. </details></details></td><td align=center>Medium </td></tr><tr><td> <details><summary>Remove authorization bypass for tenant loading</summary> ___ **Remove the <code>authorize?: false</code> option when calling <code>Ash.get</code> to ensure tenant data <br>access is properly authorized by the framework's policies.** [web-ng/lib/serviceradar_web_ng_web/live/settings/cluster_live/index.ex [994-998]](https://github.com/carverauto/serviceradar/pull/2250/files#diff-644e64ac49eaa128f9b429c8507d6a8a9ad820e2796721dd665caba8a154d24eR994-R998) ```diff defp load_tenant(tenant_id) do - case Ash.get(Tenant, tenant_id, authorize?: false) do + case Ash.get(Tenant, tenant_id) do {:ok, %Tenant{} = tenant} -> tenant _ -> nil end end ``` `[To ensure code accuracy, apply this suggestion manually]` <details><summary>Suggestion importance[1-10]: 8</summary> __ Why: This is a valid security recommendation to avoid bypassing the authorization system. Relying on `Ash` policies by removing `authorize?: false` is a best practice that strengthens security. </details></details></td><td align=center>Medium </td></tr> <tr><td align="center" colspan="2"> - [ ] More <!-- /improve --more_suggestions=true --> </td><td></td></tr></tbody></table>
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
carverauto/serviceradar!2651
No description provided.