Skip to content

feat: Create rule exclusion directly from a log event #264

Description

@bihius

Problem

When an administrator reviews a WAF event in the log viewer and identifies it as a false positive, creating the corresponding RuleExclusion is a multi-step manual process:

  1. Open the log detail and read the rule ID, request URI, and matched variable.
  2. Switch to the policy tuning page.
  3. Manually fill in the exclusion form: rule ID, target type, target value, scope path.
  4. Remember or copy the exact variable name from the log.

This is slow, error-prone, and requires the admin to mentally translate Coraza audit output into Guard Proxy exclusion fields.

Goal

Add a "Create Exclusion" action directly inside the log detail view. Clicking it should open a pre-filled exclusion form populated from the event's data, so the admin only needs to review and confirm.

Proposed Flow

  1. Admin opens the Log Detail Modal (already exists).
  2. Clicks a new button "Create Exclusion".
  3. A modal or slide-over opens with the RuleExclusionCreate form pre-filled:
    • rule_id — from the log's rule_id field.
    • target_type — parsed from the matched variable in raw_context (e.g. ARGSargs, REQUEST_HEADERSrequest_headers).
    • target_value — parsed from the matched variable (e.g. ARGS:tokentoken).
    • scope_path — extracted from the log's request_uri (path prefix only, no query string).
    • comment — pre-filled with something like "Generated from log #<id>" (editable).
  4. Admin can adjust any field before saving.
  5. On save, a RuleExclusion is created and the admin is prompted to run /config/apply.

Backend Changes

New endpoint

POST /logs/{log_id}/suggest-exclusion

Returns a RuleExclusionCreate-shaped object with fields pre-filled from the log.

Parsing logic

The endpoint must:

  1. Fetch the log row by log_id.
  2. Parse raw_context (the original Coraza audit JSON) to find the exact matched variable.
  3. Map the Coraza variable name to Guard Proxy's TargetType enum.
  4. Extract the variable value (the argument/header name).
  5. Derive scope_path from request_uri by stripping the query string.

The log-shipper already parses Coraza audit JSON (src/log-shipper/app/mapping.py). The same parsing helpers can be reused or adapted in the backend.

Example Coraza audit snippet:

{
  "messages": [{
    "message": "SQL Injection Attack Detected via libinjection",
    "details": {
      "match": "Matched \"...\" at ARGS:token"
    }
  }]
}

From "at ARGS:token" the endpoint derives:

  • target_type: args
  • target_value: "token"

Edge cases to handle

  • Log has no raw_context → return 422 with clear message.
  • Multiple messages in raw_context → use the first one that contains a rule-bearing message.
  • Variable cannot be parsed → return the form with rule_id and scope_path pre-filled, leaving target_type and target_value empty for manual entry.
  • Log belongs to a vhost with no bound policy → return 422 because exclusions need a policy.

Frontend Changes

  • Add a "Create Exclusion" button inside LogDetailModal (visible only for events with rule_id and when the user is an admin).
  • Re-use the existing exclusion creation form or extract it into a shared component.
  • Call POST /logs/{id}/suggest-exclusion to get pre-filled data.
  • After save, show a success toast with a link/button to trigger /config/apply.

Acceptance Criteria

  • Admin sees a "Create Exclusion" button in the log detail modal for events that have a matched rule.
  • Clicking it opens a pre-filled exclusion form with data derived from the log.
  • The admin can edit any field before saving.
  • Saving creates a RuleExclusion under the correct policy.
  • If raw_context cannot be parsed, the form opens with whatever fields could be extracted.
  • Backend endpoint has unit tests for the parsing logic.
  • Integration tests cover the full flow: log exists → suggest → create exclusion.
  • Frontend passes pnpm run type-check and pnpm run lint.

Out of Scope

Related


Estimated effort: ~150–250 lines across backend + frontend. Small, focused PR.

Metadata

Metadata

Assignees

No one assigned

    Labels

    area/backendFastAPI, SQLAlchemy, services, APIarea/frontendReact, Vite, panel UIarea/wafHAProxy, Coraza, CRS, SPOEenhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions