Skip to content

coreruleset/crslang

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

299 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

πŸ›‘οΈ 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.

About

The next generation CRS language

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Contributors 4

  •  
  •  
  •  
  •