π‘οΈ CRSLang
Transform OWASP CRS rules into a modern, abstract representation π
The code in this repository aims to generate a new representation for the OWASP CRS rules, making them more accessible and maintainable.
π€ Why CRSLang?
The goal is to abstract OWASP CRS from the specifics of the Seclang language in which it is currently written. This abstraction provides:
- π Better maintainability - Easier to understand and modify rules
- π― Language independence - Not tied to Seclang syntax
- π Enhanced tooling - Better support for analysis and transformation
- π Improved readability - Cleaner representation of security rules
β¨ Features
- π§ Extended ANTLR Listener - Extends the listener generated by the ANTLR Seclang parser
- π Complete Rule Loading - Loads Seclang rule information stored in the CRS files, including comments
- π¨ New Representation - Defines a new representation for the rules and translates them into it
- π Bidirectional Conversion - Convert between Seclang and CRSLang formats
π Quick Start
π₯ Installation
Initialize the repo:
git clone https://github.com/coreruleset/crslang.git
cd crslang
π¨ Build
Generate the ANTLR parser code and build the project:
go build
π» Usage
π Convert Seclang to CRSLang
Load and translate the Seclang OWASP CRS files to the new representation:
./crslang -o crs seclang_parser/testdata/crs
π Convert CRSLang back to Seclang
Load and translate the CRSLang back to Seclang:
./crslang -s crs.yaml
π§ͺ Testing
Run the tests to ensure everything works correctly:
go test -v
π WebAssembly (WASM)
CRSLang can be compiled to WebAssembly so the translation logic runs directly in
the browser without any server-side component.
π¨ Build the WASM module
Running make wasm produces two files inside the wasm/ directory:
| File |
Description |
wasm/crslang.wasm |
Compiled WebAssembly module |
wasm/wasm_exec.js |
Go-provided JS glue required to load the module |
make wasm
π₯οΈ Serve the demo
The generated files must be served over HTTP (browsers block file:// WASM
loads). Any static file server works, for example:
cd wasm
python3 -m http.server 8080
# Open http://localhost:8080/demo.html
π¦ JavaScript API
After loading wasm_exec.js and instantiating crslang.wasm, two functions
are available on the global window object:
seclangToCRSLang(seclangContent)
Translates a SecLang configuration string into a CRSLang YAML string.
const result = seclangToCRSLang(
'SecRule REQUEST_HEADERS:Host "@rx ^$" "id:920280,phase:1,block,msg:\'Request Missing a Host Header\'"'
);
if (result.error) {
console.error("Translation failed:", result.error);
} else {
console.log(result.yaml); // CRSLang YAML string
}
crslangToSeclang(crslangYaml)
Translates a CRSLang YAML string back into SecLang format.
const result = crslangToSeclang(crslangYaml);
if (result.error) {
console.error("Translation failed:", result.error);
} else {
console.log(result.seclang); // SecLang string
}
π Minimal HTML integration example
<!DOCTYPE html>
<html>
<head><meta charset="UTF-8"></head>
<body>
<script src="wasm_exec.js"></script>
<script>
const go = new Go();
WebAssembly.instantiateStreaming(fetch("crslang.wasm"), go.importObject)
.then(result => {
go.run(result.instance);
const out = seclangToCRSLang(
'SecRule REQUEST_HEADERS:Host "@rx ^$" ' +
'"id:920280,phase:1,block,msg:\'Request Missing a Host Header\'"'
);
console.log(out.yaml);
});
</script>
</body>
</html>
A ready-to-use playground is available at wasm/index.html.
π Documentation
For detailed information about the project structure and API, check out the source code and test files.
Rule examples
- kind: rule
metadata:
comment: ...
phase: "1"
id: 920300
message: Request Missing an Accept Header
severity: NOTICE
tags:
- application-multi
- language-multi
- platform-multi
- attack-protocol
- paranoia-level/3
- OWASP_CRS
- capec/1000/210/272
- PCI/6.5.10
version: OWASP_CRS/4.0.1-dev
conditions:
- collections:
- name: REQUEST_HEADERS
arguments:
- Accept
count: true
operator:
eq: "0"
transformations:
- none
- variables:
- REQUEST_METHOD
operator:
negate: true
rx: ^(?:OPTIONS|CONNECT)$
- collections:
- name: REQUEST_HEADERS
arguments:
- User-Agent
operator:
negate: true
pm: AppleWebKit Android
transformations:
- none
actions:
disruptive: pass
non-disruptive:
- setvar:
collection: TX
operation: =+
assignments:
- inbound_anomaly_score_pl3: "%{tx.notice_anomaly_score}"
flow:
- chain
- kind: rule
metadata:
comment: ...
phase: "1"
id: 920170
message: GET or HEAD Request with Body Content
severity: CRITICAL
tags:
- application-multi
- language-multi
- platform-multi
- attack-protocol
- paranoia-level/1
- OWASP_CRS
- capec/1000/210/272
version: OWASP_CRS/4.0.1-dev
conditions:
- variables:
- REQUEST_METHOD
operator:
rx: ^(?:GET|HEAD)$
transformations:
- none
actions:
disruptive: block
non-disruptive:
- logdata: "%{MATCHED_VAR}"
flow:
- chain
chained-rule:
kind: rule
conditions:
- collections:
- name: REQUEST_HEADERS
arguments:
- Content-Length
operator:
negate: true
rx: ^0?$
transformations:
- none
actions:
non-disruptive:
- setvar:
collection: TX
operation: =+
assignments:
- inbound_anomaly_score_pl1: "%{tx.critical_anomaly_score}"
- kind: rule
metadata:
comment: ...
phase: "1"
id: 901200
tags:
- OWASP_CRS
version: OWASP_CRS/4.0.1-dev
conditions:
- alwaysMatch: true
transformations:
- none
actions:
disruptive: pass
non-disruptive:
- nolog
- setvar:
- blocking_inbound_anomaly_score: "0"
- detection_inbound_anomaly_score: "0"
- inbound_anomaly_score_pl1: "0"
- inbound_anomaly_score_pl2: "0"
- inbound_anomaly_score_pl3: "0"
- inbound_anomaly_score_pl4: "0"
- sql_injection_score: "0"
- xss_score: "0"
- rfi_score: "0"
- lfi_score: "0"
- rce_score: "0"
- php_injection_score: "0"
- http_violation_score: "0"
- session_fixation_score: "0"
- blocking_outbound_anomaly_score: "0"
- detection_outbound_anomaly_score: "0"
- outbound_anomaly_score_pl1: "0"
- outbound_anomaly_score_pl2: "0"
- outbound_anomaly_score_pl3: "0"
- outbound_anomaly_score_pl4: "0"
- anomaly_score: "0"
π€ Contributing
We welcome contributions! Please feel free to submit issues and pull requests.
π License
This project is licensed under the Apache License 2.0 - see the LICENSE file for details.