#2062

ENG-536: one Guardrail form and one Data Masking form for agents and sidecars

OpenEmanuelJrwants to merge intofrom

📝 Description

One Guardrail form and one Data Masking form for agents and sidecars. Built on the sidecar vocabulary (the richer one), each form writes both halves of a rule in one save: the agent fields (input/output rules, or entity types, custom entity types and threshold) and the sidecar block (sidecar_spec), plus the resource roles, attributes and sidecar listeners it binds to. An option one engine does not run carries an inline Sidecar only / Agent only tag; a rule bound to resource roles that uses a sidecar-only option stores an empty agent half, and the form says so. The two forms per feature (Gateway*Form / Sidecar*Form) and the dead-end for a rule carrying both halves are gone.

No backend behaviour change. The list endpoints add sidecar_targets per rule so the list page can tell which traffic a rule protects.

📣 User-facing impact

Admins write one rule and it protects a database whether the traffic goes through an agent or a sidecar. Every option says which engine runs it.

🔗 Related Issue

ENG-536

🚀 Type of Change

  • ✨ New feature (non-breaking change which adds functionality)
  • 🎨 Style/UI update

📋 Changes Made

  • webapp_v2/src/pages/Guardrails/Create/{GuardrailForm,GuardrailFields,useGuardrailEditor}: one form; each rule has Checks: Statements | Results (Results is agent-only, so it is disabled for a type only the sidecar runs); sidecar_spec is written from the statement rules, input/output from the two directions; a results rule beside a sidecar-only rule, or a results rule on a listener-only binding, is refused with the reason
  • webapp_v2/src/pages/Features/DataMasking/Create/{DataMaskingForm,DataMaskingFields,useDataMaskingEditor}: one form; presets became a quick-fill of a rule's entity list; the agent-only threshold and custom entity types live beside the rule list; supported_entity_types is written from the entity rules, emptied when a rule matches by column
  • webapp_v2/src/utils/ruleSupport.js, components/RuleSupportTag: what each engine runs, and the inline marker
  • webapp_v2/src/utils/ruleTraffic.js: a rule's traffic is where it is bound (connection_ids/attributes, sidecar_targets), not which fields it has; <ByRuleTraffic> and NewRuleButton stay for the analyzer only
  • webapp_v2/src/components/ConnectionsMultiSelect: disabled
  • Deleted: Gateway{Guardrail,DataMasking}Form, both RulesTables, the guardrail presets and the row/payload helpers
  • gateway/api/guardrails, gateway/api/datamasking, gateway/api/sidecarbind.LoadAll: list responses carry sidecar_targets

🧪 Testing

Result: an admin writes one Guardrail or Data Masking rule, binds it to resource roles and sidecar listeners in the same form, every option says which engine runs it, and the saved rule carries both the agent half and the sidecar half.

Same seed on origin/main 4eb14ab7 and on 1c947acd (resource role pg-prod, sidecar qa536 with listener appdb), same steps.

1. One guardrail for agents and sidecars

Full size: 1-one-guardrail-form.mp4

  • Main: "Create new rule" asks "For agent resources" or "For sidecar listeners"; the sidecar form has Listeners only. Branch: one form with Resource Roles, Attributes and Listeners.
  • Branch, rule 1: the Type dropdown tags Operation, Table, PII as Sidecar only; Checks offers Statements and Results · Agent only; the operations scope is Sidecar only. Rule 2 is a Pattern match results rule.
  • Last frame: main lists the rule as Sidecar, the branch as Agent and sidecar.

2. One masking rule for agents and sidecars

Full size: 2-one-masking-form.mp4

  • Branch: Analyzer confidence threshold · Agent only; a preset fills the entity list; Strategy · Sidecar only; Custom entity types · Agent only with ORDER_ID.

3. The saved rule carries both halves

GET /:id after the flows. Main stores the sidecar half only (connection_ids: [], input.rules: [], supported_entity_types: []). The branch stores both from one save.

What it solves: a customer with agents and sidecars keeps one rule per policy and one form to edit it, and sees on each option which engine enforces it.

Also checked by hand on the final backend: a legacy agent-only rule opens and an unchanged save adds the sidecar half; a results rule beside a sidecar-only rule and a custom entity beside a column rule are refused by the form; a results-only rule bound to a listener gets the gateway's 422; the Sidecars page dialog creates and edits with the same fields; select pg_sleep(1) through a listener is denied by a rule saved from the form.

Recording caught and fixed: the Checks select hidden on a new rule, the Agent only tag ellipsized in its dropdown, the operations list surviving a type switch as a scope.

Re-run:

cd webapp_v2 && npm run lint && npm run build
cd gateway && go build ./... && go test -count=1 ./api/sidecarbind/... ./api/datamasking ./api/sidecar/... ./services ./models

Test Configuration:

  • Browser(s): Brave (Chromium 156), recorded over CDP
  • OS: macOS arm64

Tests performed:

  • Unit tests pass
  • Integration tests pass (CI)
  • Manual testing completed

✅ Checklist

  • My code follows the style guidelines of this project
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • My changes generate no new warnings
  • New and existing unit tests pass locally with my changes
  • I have checked my code and corrected any misspellings

📄 Additional Notes

  • The branch first translated agent rules into sidecar rules at handshake (services/sidecargatewayrules.go); that was removed in favour of the form writing the sidecar half itself. A rule without a sidecar_spec bound to a listener is still refused by the gateway, as on main; an old agent-only rule reaches a sidecar after one save in the form.
  • The evidence stacks had no live DLP provider, so agent-side enforcement of the saved rules was not exercised; the agent half keeps the shape the old agent form sent.
  • Gateway↔agent and gateway↔sidecar contracts: unchanged. No flag, migration, env var or analytics event.
  • Sidecar deny_words_list is case-insensitive and the agent's is case-sensitive; the Words field says so.
  • Pre-existing, not fixed here: the Contact Information preset carries PERSON, which no shipped sidecar loads, so that preset bound to a listener is refused with the daemon's message.

Activity

33
github-actions[bot]5d ago

📋 API Changelog

API Changelog unknown vs. unknown

No changes detected

sandromello5d ago

✅ Build Completed with Success, Version=2062.0.0-g278a6fa

EmanuelJrreviewed2d ago
EmanuelJrreviewed2d ago
EmanuelJrreviewed2d ago
EmanuelJrreviewed2d ago
EmanuelJrreviewed2d ago
EmanuelJrreviewed2d ago
sandromello2d ago

✅ Build Completed with Success, Version=2062.0.0-gd3ad9b0

luanlorenzoapproved changes2d ago
rogefm2d ago

topzera

rogefmapproved changes2d ago