-
-
Notifications
You must be signed in to change notification settings - Fork 312
docs: add AGENTS.md for LLM codebase navigation #1535
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
17aa31c
f76cba1
e6caaf5
6b28e50
629f437
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,295 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Coraza WAF - LLM Navigation Guide | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| This document helps LLMs (GitHub Copilot, Claude, Cursor, etc.) understand the Coraza codebase architecture, navigate the code, and perform common tasks. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ## Project Overview | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Coraza is a Web Application Firewall (WAF) engine written in Go. It implements the SecLang directive language (compatible with ModSecurity v2/v3), is fully compatible with OWASP CRS v4, and is an OWASP Production Project. Coraza also supports TinyGo compilation for environments with constrained runtimes (e.g. WASM). | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ## Repository Structure | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| | Directory | Purpose | | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |---|---| | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| | `types/` | Public API interfaces: `Transaction`, `WAF`, `MatchData`, `Interruption`, `RuleMetadata`, variables | | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| | `collection/` | Public collection interfaces: `Collection`, `Single`, `Keyed`, `Map` | | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| | `debuglog/` | Debug logging interfaces and helpers | | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| | `http/` | HTTP integration helpers and end-to-end tests | | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| | `testing/` | Test utilities, test data, and CRS regression tests | | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| | `examples/` | Usage examples (e.g. `http-server`) | | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| | `experimental/plugins/` | Plugin registration system: `RegisterOperator()`, `RegisterTransformation()`, `RegisterAction()` | | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| | `experimental/plugins/plugintypes/` | Plugin interfaces: `Operator`, `Transformation`, `Action`, `TransactionState`, `RuleMetadata` | | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| | `experimental/plugins/macro/` | Macro expansion for rule messages and log data | | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| | `internal/corazawaf/` | Core WAF and Transaction implementation, RuleGroup evaluation | | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| | `internal/corazarules/` | Rule metadata and match data implementation | | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| | `internal/collections/` | Variable storage implementations: `Map`, `Named`, `Single`, `Sized` | | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| | `internal/operators/` | All operator implementations (~38 non-test files) | | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| | `internal/transformations/` | All transformation implementations (~33 non-test files) | | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| | `internal/actions/` | All action implementations (~33 non-test files) | | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| | `internal/seclang/` | SecLang rule and directive parser | | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| | `internal/auditlog/` | Audit logging: serial, concurrent, syslog, HTTPS writers; JSON and OCSF formatters | | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| | `internal/bodyprocessors/` | Body parsers: JSON, XML, multipart, urlencoded, raw | | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| | `internal/variables/` | Variable type system with generated maps | | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| | `internal/strings/`, `internal/url/`, `internal/cookies/` | Utility packages | | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| | `internal/sync/` | TinyGo-compatible sync primitives (`pool.go`, `pool_std.go`, `pool_tinygo.go`) | | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| | `internal/memoize/` | Memoization utilities for builders | | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| | `internal/environment/` | Build environment detection (FS access, etc.) | | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ## Architecture: Request Processing Pipeline | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Every HTTP request/response flows through 5 phases. Each `Process*` method triggers `WAF.Rules.Eval(phase, tx)` and checks for interruptions. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ### Phase 1 - Request Headers | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ``` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ProcessConnection(clientIP, clientPort, serverIP, serverPort) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| -> ProcessURI(uri, method, httpVersion) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| -> AddRequestHeader(key, value) // repeat per header | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| -> ProcessRequestHeaders() -> *Interruption | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ``` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ### Phase 2 - Request Body | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ``` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| WriteRequestBody([]byte) / ReadRequestBodyFrom(io.Reader) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| -> ProcessRequestBody() -> (*Interruption, error) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ``` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+51
to
+53
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| The body processor type (JSON, XML, multipart, urlencoded) is auto-detected from the `Content-Type` header. The processor parses the body into WAF variables (e.g. `ARGS_POST`, `REQUEST_BODY`, `FILES`). | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ### Phase 3 - Response Headers | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ``` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| AddResponseHeader(key, value) // repeat per header | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| -> ProcessResponseHeaders(statusCode, proto) -> *Interruption | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ``` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ### Phase 4 - Response Body | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ``` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| WriteResponseBody([]byte) / ReadResponseBodyFrom(io.Reader) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| WriteResponseBody([]byte) / ReadResponseBodyFrom(io.Reader) | |
| WriteResponseBody([]byte) -> (*Interruption, int, error) | |
| ReadResponseBodyFrom(io.Reader) -> (*Interruption, int, error) |
Copilot
AI
Mar 28, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
go run mage.go test currently runs go test ./..., then reruns tests with -tags=coraza.no_memoize, plus additional suites (examples/http-server with race, and testing/coreruleset with multiple tags). The comment mentioning "memoize_builders" doesn’t match any build tag or what Mage runs today; consider updating this line to reference the actual tag (coraza.no_memoize) and the additional tagged suites (multiphase, no_regex_multiline, case_sensitive_args_keys).
| go run mage.go test # Run all tests (including memoize_builders, multiphase, CRS) | |
| go run mage.go test # Run all tests (go test ./..., then with -tags=coraza.no_memoize, plus examples/http-server (race) and testing/coreruleset with multiphase/no_regex_multiline/case_sensitive_args_keys) |
Copilot
AI
Mar 28, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The build tags table appears inaccurate/incomplete compared to the repository’s documented/used tags: there is no memoize_builders build tag in the codebase, and coraza.no_memoize (used in Mage/CI and documented in README) is missing. Suggest removing memoize_builders, adding coraza.no_memoize, and aligning the list with README.md’s “Build tags” section to avoid confusing integrators/LLMs.
| | `memoize_builders` | Enable memoization of operator/transformation builders | | |
| | `coraza.no_memoize` | Disable memoization of operator/transformation builders | |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Surround the build-tags table with blank lines.
The table starting at Line 206 should be separated by blank lines to satisfy MD058.
💡 Proposed doc-only fix
### Build tags
+
| Tag | Effect |
|---|---|
| `coraza.disabled_operators.<name>` | Exclude a specific operator from compilation |
| `coraza.rule.multiphase_evaluation` | Evaluate rule variables in phases they become ready |
| `coraza.rule.case_sensitive_args_keys` | Case-sensitive ARGS key matching (RFC 3986) |
| `coraza.rule.no_regex_multiline` | Disable default multiline mode in `@rx` operator |
| `coraza.rule.mandatory_rule_id_check` | Require `id` action for all SecRule/SecAction |
| `tinygo` | TinyGo-compatible build (affects sync primitives, FS access) |
| `memoize_builders` | Enable memoization of operator/transformation builders |
| `no_fs_access` | Disable filesystem access |
+
### Generated code📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| | Tag | Effect | | |
| |---|---| | |
| | `coraza.disabled_operators.<name>` | Exclude a specific operator from compilation | | |
| | `coraza.rule.multiphase_evaluation` | Evaluate rule variables in phases they become ready | | |
| | `coraza.rule.case_sensitive_args_keys` | Case-sensitive ARGS key matching (RFC 3986) | | |
| | `coraza.rule.no_regex_multiline` | Disable default multiline mode in `@rx` operator | | |
| | `coraza.rule.mandatory_rule_id_check` | Require `id` action for all SecRule/SecAction | | |
| | `tinygo` | TinyGo-compatible build (affects sync primitives, FS access) | | |
| | `memoize_builders` | Enable memoization of operator/transformation builders | | |
| | `no_fs_access` | Disable filesystem access | | |
| ### Build tags | |
| | Tag | Effect | | |
| |---|---| | |
| | `coraza.disabled_operators.<name>` | Exclude a specific operator from compilation | | |
| | `coraza.rule.multiphase_evaluation` | Evaluate rule variables in phases they become ready | | |
| | `coraza.rule.case_sensitive_args_keys` | Case-sensitive ARGS key matching (RFC 3986) | | |
| | `coraza.rule.no_regex_multiline` | Disable default multiline mode in `@rx` operator | | |
| | `coraza.rule.mandatory_rule_id_check` | Require `id` action for all SecRule/SecAction | | |
| | `tinygo` | TinyGo-compatible build (affects sync primitives, FS access) | | |
| | `memoize_builders` | Enable memoization of operator/transformation builders | | |
| | `no_fs_access` | Disable filesystem access | | |
| ### Generated code |
🧰 Tools
🪛 markdownlint-cli2 (0.22.0)
[warning] 206-206: Tables should be surrounded by blank lines
(MD058, blanks-around-tables)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@AGENTS.md` around lines 206 - 216, The Markdown table that begins with the
header "| Tag | Effect |" and lists tags like
`coraza.disabled_operators.<name>`, `coraza.rule.multiphase_evaluation`,
`tinygo`, etc., needs blank lines inserted immediately before and after the
table to satisfy MD058; update the AGENTS.md content to add a single empty line
above the table header and a single empty line after the final `| no_fs_access |
Disable filesystem access |` row.
Copilot
AI
Mar 28, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The “Adding a new operator” steps mix two different workflows and the example as written would create an import cycle if followed inside internal/operators/ (because experimental/plugins imports internal/operators). If the intent is adding a built-in operator, the registration should be done via internal/operators.Register(...) from within the operators package; if the intent is an external plugin, show plugins.RegisterOperator(...) from an integrator’s package (not from internal/operators).
| 1. Create `internal/operators/my_operator.go`: | |
| ```go | |
| type myOperator struct { | |
| data string | |
| } | |
| func (o *myOperator) Evaluate(tx plugintypes.TransactionState, value string) bool { | |
| // implementation | |
| } | |
| ``` | |
| 2. Register in `internal/operators/` init or via `experimental/plugins/operators.go`: | |
| ```go | |
| plugins.RegisterOperator("myOperator", func(options plugintypes.OperatorOptions) (plugintypes.Operator, error) { | |
| return &myOperator{data: options.Arguments}, nil | |
| }) | |
| ``` | |
| 3. Add tests in `internal/operators/my_operator_test.go` | |
| There are two ways to add an operator: | |
| - As a **built-in operator** in the core `internal/operators` package. | |
| - As an **external plugin operator** registered via `experimental/plugins` from your own package. | |
| #### Built-in operator (core) | |
| 1. Create `internal/operators/my_operator.go`: | |
| ```go | |
| package operators | |
| import "github.com/corazawaf/coraza/v3/experimental/plugins/plugintypes" | |
| type myOperator struct { | |
| data string | |
| } | |
| func (o *myOperator) Evaluate(tx plugintypes.TransactionState, value string) bool { | |
| // implementation | |
| return false | |
| } | |
| func init() { | |
| Register("myOperator", func(options plugintypes.OperatorOptions) (plugintypes.Operator, error) { | |
| return &myOperator{data: options.Arguments}, nil | |
| }) | |
| } |
- Add tests in
internal/operators/my_operator_test.go.
External operator plugin (integrator)
- In your own package (not in
internal/operators), create e.g.my_operator_plugin.go:package mywaf import ( "github.com/corazawaf/coraza/v3/experimental/plugins" "github.com/corazawaf/coraza/v3/experimental/plugins/plugintypes" ) type myOperator struct { data string } func (o *myOperator) Evaluate(tx plugintypes.TransactionState, value string) bool { // implementation return false } func init() { plugins.RegisterOperator("myOperator", func(options plugintypes.OperatorOptions) (plugintypes.Operator, error) { return &myOperator{data: options.Arguments}, nil }) }
- Add tests in your package to cover the new operator.
Copilot
AI
Mar 28, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The transformation section has the same workflow mix-up as operators: registering via experimental/plugins/transformations.go is something an integrator would do from outside the internal/transformations package. If you’re documenting how to add a built-in transformation under internal/transformations/, the example should use that package’s internal registration mechanism (to avoid an import cycle and match existing built-ins).
Copilot
AI
Mar 28, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same as operators/transformations: the “Adding a new action” section shows plugins.RegisterAction(...) but places it in a workflow that starts by creating internal/actions/my_action.go. Using experimental/plugins from internal/actions would introduce an import cycle; for built-in actions the registration should be done via the internal actions registry, while plugins.RegisterAction should be shown as code that lives in an integrator/plugin package.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add languages to fenced code blocks to satisfy markdown linting.
Several fenced blocks are missing a language hint (e.g., around Line 42 and Line 181). This triggers MD040 and reduces tooling compatibility.
💡 Proposed doc-only fix
Verify each finding against the current code and only fix it if needed.
In
@AGENTS.mdaround lines 42 - 47, Several fenced code blocks in AGENTS.md (forexample the block containing "ProcessConnection(clientIP, clientPort, serverIP,
serverPort) -> ProcessURI(uri, method, httpVersion) -> AddRequestHeader(key,
value) // repeat per header -> ProcessRequestHeaders() -> *Interruption" and
the other unlabeled blocks around the same area) lack a language hint; add a
language label (e.g.,
text) to each unlabeled fenced block to satisfy MD040 and markdown linters, updating the opening fences for the blocks that contain constructs like ProcessConnection(...), ProcessURI(...), AddRequestHeader(...), and similar sequences so they becometext ... ``` (or another appropriatelanguage) while leaving the block contents unchanged.