Skip to content

feat(auditlog): add libmodsecurity v3 JSON formatter. - #1638

Open
slg95 wants to merge 1 commit into
corazawaf:mainfrom
slg95:fix/issue-856-auditlog-parity
Open

feat(auditlog): add libmodsecurity v3 JSON formatter.#1638
slg95 wants to merge 1 commit into
corazawaf:mainfrom
slg95:fix/issue-856-auditlog-parity

Conversation

@slg95

@slg95 slg95 commented Jul 15, 2026

Copy link
Copy Markdown

Closes: #856

I added a new JSONV3 audit log formatter that emits the libmodsecurity v3 JSON schema and kept the existing JSON and JSONLEGACY formatters unchanged. The SecAuditLogFomrat JSONV3 is registered and H-only audit logs retain structured match-rule data so messages can be emitted in the v3 format. Best-effort match details are populated for rule messages.

I am new to this codebase, so let me know what I can improve. Hope this helps!

  • My code includes positive and negative tests.
  • I have an appropriate description with correct grammar.
  • I have read the Contribution guidelines and Code of Conduct.
  • My code is properly linted and passes pre-commit tests.

Summary by CodeRabbit

  • New Features
    • Added support for the JSONV3 (libModSecurity v3) audit log format, registered as modsecurityv3.
    • Produces a v3-compatible JSON structure with HTTP version normalization, producer metadata, and structured per-message match/reference when enabled.
  • Bug Fixes
    • Improved robustness for optional message data and structured match/reference fields.
    • Refined audit-trailer generation, including correct handling of chained-rule attribution and audit-part gating.
  • Documentation
    • Updated SecAuditLogFormat directive docs to include JSONV3.
  • Tests
    • Added JSONV3 formatter and audit-part coverage, plus unit tests validating trailer structured messages and chain attribution.

@slg95
slg95 requested a review from a team as a code owner July 15, 2026 20:31
@coderabbitai

coderabbitai Bot commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds structured match and reference data to audit messages, introduces a libmodsecurity-compatible JSONV3 formatter, integrates it with audit-log generation, registers it across builds, and documents and tests the new format.

Changes

Audit-log JSONV3

Layer / File(s) Summary
Structured message contracts
internal/auditlog/auditlog.go, internal/corazarules/rule_match.go
Audit message and rule match data expose match and reference values, with nil-safe accessors and Message.Data() handling.
Audit message generation
internal/corazawaf/rule.go, internal/corazawaf/transaction.go, internal/corazawaf/transaction_test.go
Matched rules populate audit match text, transaction metadata is expanded, and audit messages include structured rule details and trailer errors.
JSONV3 formatter implementation
internal/auditlog/formats_json_v3.go, internal/auditlog/formats_json_test.go
Adds libmodsecurity-compatible JSON serialization for transaction, request, response, producer, and message data, including audit-part filtering and normalization helpers.
Formatter registration and documentation
internal/auditlog/init*.go, internal/auditlog/formats.go, internal/seclang/directives.go
Registers modsecurityv3 across build variants and documents JSONV3 as a supported audit-log format.

Estimated code review effort: 4 (Complex) | ~45 minutes

Possibly related PRs

  • corazawaf/coraza#1587: Changes related audit-log message inclusion logic in internal/corazawaf/transaction.go.

Suggested reviewers: m4tteop

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly states the main change: adding the libmodsecurity v3 JSON formatter.
Linked Issues check ✅ Passed The PR adds a JSONV3 formatter with richer libmodsecurity3-compatible fields and registers it, matching issue #856's parity goals.
Out of Scope Changes check ✅ Passed The changes stay within audit-log formatting, registration, and tests, with no clear unrelated feature additions.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

}

func (m Message) Data() plugintypes.AuditLogMessageData {
if m.Data_ == nil {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Do we need this?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Based on my understanding of Go, it's needed because returning m.Data_ as an interface produces a non-nil typed-nil value, so the JSONV3 formatter's data == nil guard would not work and it could panic when calling methods on it

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (1)
internal/corazawaf/transaction.go (1)

1647-1667: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low value

Hoist the repeated strings.Join out of the per-message loop.

strings.Join(tx.WAF.ComponentNames, " ") doesn't vary across messages or rules but is recomputed for every matchData iteration.

♻️ Proposed fix
 func (tx *Transaction) auditLogMessages(includeErrorMessage bool) []plugintypes.AuditLogMessage {
 	var messages []plugintypes.AuditLogMessage
+	actionset := strings.Join(tx.WAF.ComponentNames, " ")
 
 	for _, mr := range tx.matchedRules {
 		...
 			messages = append(messages, auditlog.Message{
-				Actionset_:    strings.Join(tx.WAF.ComponentNames, " "),
+				Actionset_:    actionset,
 				Message_:      matchData.Message(),
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/corazawaf/transaction.go` around lines 1647 - 1667, Compute
strings.Join(tx.WAF.ComponentNames, " ") once before the per-message matchData
loop, store the result in a local variable, and use that variable for
Message.Actionset_ within the auditlog.Message construction.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@internal/auditlog/formats_json_test.go`:
- Around line 202-272: Add an assertion in the formatted message checks for
(*transaction.Messages)[0].Details.Match, verifying it equals the Match_ value
configured in the test message data. Place it alongside the existing RuleID and
Reference assertions.

In `@internal/corazawaf/transaction.go`:
- Around line 567-572: The Match_ fallback in the transaction audit-log loop
uses the parent rule for chained child matches. Update MatchData creation or its
record structure so each MatchData retains its originating rule/operator, then
use that source when formatting Match_ instead of r.auditLogMatch(md); preserve
existing behavior for matches already populated.

---

Nitpick comments:
In `@internal/corazawaf/transaction.go`:
- Around line 1647-1667: Compute strings.Join(tx.WAF.ComponentNames, " ") once
before the per-message matchData loop, store the result in a local variable, and
use that variable for Message.Actionset_ within the auditlog.Message
construction.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: d856f040-4e76-4d1e-96c2-0bfa2aca1941

📥 Commits

Reviewing files that changed from the base of the PR and between fd673ca and f9c0934.

📒 Files selected for processing (12)
  • internal/auditlog/auditlog.go
  • internal/auditlog/formats.go
  • internal/auditlog/formats_json_test.go
  • internal/auditlog/formats_json_v3.go
  • internal/auditlog/init.go
  • internal/auditlog/init_tinygo.go
  • internal/auditlog/init_windows.go
  • internal/corazarules/rule_match.go
  • internal/corazawaf/rule.go
  • internal/corazawaf/transaction.go
  • internal/corazawaf/transaction_test.go
  • internal/seclang/directives.go

Comment thread internal/auditlog/formats_json_test.go
Comment thread internal/corazawaf/transaction.go
Comment thread internal/corazawaf/rule.go Outdated

variable := md.Variable().Name()
if key := md.Key(); key != "" {
variable += ":" + key

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

use strings.Builder to improve performance

@jcchavezs

Copy link
Copy Markdown
Member

Shall we name it modsecurityv3 instead?

@codecov

codecov Bot commented Jul 15, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 86.81818% with 29 lines in your changes missing coverage. Please review.
✅ Project coverage is 87.59%. Comparing base (fd673ca) to head (f9c0934).

Files with missing lines Patch % Lines
internal/auditlog/formats_json_v3.go 84.03% 14 Missing and 5 partials ⚠️
internal/auditlog/auditlog.go 40.00% 3 Missing and 3 partials ⚠️
internal/corazawaf/rule.go 87.50% 1 Missing and 1 partial ⚠️
internal/corazawaf/transaction.go 97.10% 1 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1638      +/-   ##
==========================================
- Coverage   87.68%   87.59%   -0.09%     
==========================================
  Files         178      179       +1     
  Lines        9148     9322     +174     
==========================================
+ Hits         8021     8166     +145     
- Misses        858      877      +19     
- Partials      269      279      +10     
Flag Coverage Δ
coraza.no_memoize 87.68% <86.75%> (-0.09%) ⬇️
coraza.rule.case_sensitive_args_keys 87.56% <86.81%> (-0.09%) ⬇️
coraza.rule.mandatory_rule_id_check 87.58% <86.81%> (-0.09%) ⬇️
coraza.rule.multiphase_evaluation 87.36% <86.81%> (-0.08%) ⬇️
coraza.rule.no_regex_multiline 87.55% <86.81%> (-0.09%) ⬇️
coraza.rule.rx_prefilter 87.59% <86.81%> (-0.09%) ⬇️
default 87.59% <86.81%> (-0.09%) ⬇️
examples+ 16.09% <6.39%> (-0.17%) ⬇️
examples+coraza.no_memoize 85.59% <86.75%> (-0.05%) ⬇️
examples+coraza.rule.case_sensitive_args_keys 85.57% <86.81%> (-0.05%) ⬇️
examples+coraza.rule.mandatory_rule_id_check 85.68% <86.81%> (-0.05%) ⬇️
examples+coraza.rule.multiphase_evaluation 87.36% <86.81%> (-0.08%) ⬇️
examples+coraza.rule.no_regex_multiline 85.50% <86.81%> (-0.05%) ⬇️
examples+coraza.rule.rx_prefilter 85.85% <86.81%> (-0.05%) ⬇️
examples+no_fs_access 84.93% <86.81%> (-0.04%) ⬇️
ftw 87.59% <86.81%> (-0.09%) ⬇️
no_fs_access 86.95% <86.81%> (-0.07%) ⬇️
tinygo 87.59% <86.81%> (-0.09%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@slg95

slg95 commented Jul 15, 2026

Copy link
Copy Markdown
Author

Shall we name it modsecurityv3 instead?

I agree that is a bit clearer. Thanks for the suggestion!

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
internal/corazawaf/transaction_test.go (1)

942-944: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Use defer to ensure transaction cleanup.

If t.Fatalf is called earlier in the test, this cleanup code will be skipped, potentially leaking the transaction in the WAF's transaction pool. As per coding guidelines, you should always clean up resources after transaction completion. Consider moving this cleanup logic to a defer block immediately after the transaction is instantiated.

🛠️ Proposed refactor

Apply this change near line 915:

 	tx := NewWAF().NewTransaction()
+	defer func() {
+		if err := tx.Close(); err != nil {
+			t.Errorf("Failed to close transaction: %s", err.Error())
+		}
+	}()
 	tx.AuditLogParts = types.AuditLogParts("H")

And remove these lines at the end of the test:

-	if err := tx.Close(); err != nil {
-		t.Fatalf("Failed to close transaction: %s", err.Error())
-	}
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/corazawaf/transaction_test.go` around lines 942 - 944, Move the
tx.Close cleanup from the test’s final assertions into a defer immediately after
the transaction is instantiated, preserving fatal error handling in the deferred
cleanup so the transaction is always released even when earlier t.Fatalf calls
exit the test.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@internal/corazawaf/transaction_test.go`:
- Around line 942-944: Move the tx.Close cleanup from the test’s final
assertions into a defer immediately after the transaction is instantiated,
preserving fatal error handling in the deferred cleanup so the transaction is
always released even when earlier t.Fatalf calls exit the test.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: c078efbc-0bfc-4011-b521-582d5c6c14e9

📥 Commits

Reviewing files that changed from the base of the PR and between f9c0934 and 1a51a3b.

📒 Files selected for processing (9)
  • internal/auditlog/formats.go
  • internal/auditlog/formats_json_test.go
  • internal/auditlog/init.go
  • internal/auditlog/init_tinygo.go
  • internal/auditlog/init_windows.go
  • internal/corazawaf/rule.go
  • internal/corazawaf/transaction.go
  • internal/corazawaf/transaction_test.go
  • internal/seclang/directives.go
🚧 Files skipped from review as they are similar to previous changes (4)
  • internal/auditlog/init_windows.go
  • internal/seclang/directives.go
  • internal/corazawaf/rule.go
  • internal/corazawaf/transaction.go

Add a new JSONV3 audit formatter that emits the libmodsecurity v3 JSON
schema without changing the existing JSON and JSONLEGACY formats.

Also make H-only audit logs retain structured rule message data so the
new formatter can include producer and message details expected by
libmodsecurity-compatible consumers.
@slg95
slg95 force-pushed the fix/issue-856-auditlog-parity branch from 9228113 to 3b6ef69 Compare July 24, 2026 16:07
@slg95

slg95 commented Jul 24, 2026

Copy link
Copy Markdown
Author

I squashed the commit down, let me know if further changes are needed!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

AuditLog parity with libmodsecurity3

2 participants