Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion .github/workflows/go.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,20 @@ name: Go
on:
push:
branches: [ "main" ]
paths:
- "**/*.go"
- "**/*_test.go"
- ".github/**"
- "g4/**"
- "magefile.go"
pull_request:
branches: [ "main" ]
paths:
- "**/*.go"
- "**/*_test.go"
- ".github/**"
- "g4/**"
- "magefile.go"

jobs:

Expand All @@ -19,7 +31,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5
with:
go-version: '1.25'
go-version: '1.23'

- name: Build
run: |
Expand Down
63 changes: 63 additions & 0 deletions .github/workflows/python.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# This workflow will build a golang project
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-go

name: Python

on:
push:
branches: [ "main" ]
paths:
- "src/**"
- "tests/**"
- ".github/**"
- "g4/**"
- "pyproject.toml"
pull_request:
branches: [ "main" ]
paths:
- "src/**"
- "tests/**"
- ".github/**"
- "g4/**"
- "pyproject.toml"

jobs:
python:
runs-on: ubuntu-latest
strategy:
fail-fast: true
matrix:
python-version: ['3.11', '3.12', '3.13']

steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4

- name: Set up Python 3
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}

- name: Install uv
uses: astral-sh/setup-uv@f0ec1fc3b38f5e7cd731bb6ce540c5af426746bb # v6
with:
enable-cache: true

- name: Set up Python 3
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
with:
python-version: ${{ matrix.python-version }}

- name: "Generate parser files"
run: |
pushd g4
./generate.sh
popd

- name: Install dependencies
run: |
uv sync --all-extras --dev

- name: "Run unit tests"
run: |
uv run pytest -vs

6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,14 @@ go test ./...
```

Or for python:

- Get [uv](https://github.com/astral-sh/uv) first
```bash
cd parser
./generate.sh
cd ..
poetry install
poetry run python ./base_test.py
uv sync --all-extras --dev
uv run pytest -vs
```

## Authors
Expand Down
42 changes: 0 additions & 42 deletions base_test.py

This file was deleted.

17 changes: 0 additions & 17 deletions poetry.lock

This file was deleted.

57 changes: 47 additions & 10 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,17 +1,54 @@
[tool.poetry]
[project]
name = "seclang_parser"
version = "0.1.0"
dynamic = [ "version" ]
description = "SecLang Parser using ANTLR"
authors = ["Felipe Zipitria <felipe.zipitria@owasp.org>"]
authors = [
{name = "Felipe Zipitria", email = "felipe.zipitria@owasp.org"}
]
requires-python = ">=3.11"
license = "Apache-2.0"
readme = "README.md"
keywords = ["OWASP", "CRS", "SecLang", "ModSecurity", "Coraza"]

[tool.poetry.dependencies]
python = "^3.9"
antlr4-python3-runtime = "^4.13.2"
classifiers = [
"Programming Language :: Python :: 3",
"License :: OSI Approved :: Apache Software License",
"Operating System :: OS Independent",
]

dependencies = [
"antlr4-python3-runtime >=4.13.2",
]

[project.scripts]
seclang_parser = 'seclang_parser.cli:run'

[project.urls]
issues = "https://github.com/coreruleset/seclang_parser/issues"
homepage = "https://github.com/coreruleset/seclang_parser"
repository = "https://github.com/coreruleset/seclang_parser.git"

[dependency-groups]
dev = [
"pytest >=8.1.1,<9"
]

[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
requires = ["hatchling", "hatch-vcs"]
build-backend = "hatchling.build"

[tool.hatch.version]
source = "vcs"

[tool.hatch.version.raw-options]
version_scheme = "no-guess-dev"

[[tool.uv.index]]
name = "pypi"
url = "https://pypi.org/simple/"
publish-url = "https://pypi.org/legacy/"

[tool.poetry.scripts]
parser = 'base_test:main'
[[tool.uv.index]]
name = "testpypi"
url = "https://test.pypi.org/simple/"
publish-url = "https://test.pypi.org/legacy/"
Empty file added src/seclang_parser/__init__.py
Empty file.
84 changes: 84 additions & 0 deletions src/seclang_parser/actions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
class Action:
def __init__(self, action, param=None):
self.action = action
self.param = param

def set_action(self, action, param):
self.action = action
self.param = param

def to_string(self):
if self.param:
return f"{self.action}:{self.param}"
return self.action

def get_key(self):
return self.action

def __str__(self):
return self.to_string()


class SeclangActions:
def __init__(self):
self.disruptive_action = None
self.non_disruptive_actions = []
self.flow_actions = []
self.data_actions = []

def to_string(self):
results = []
if self.disruptive_action and self.disruptive_action.action:
results.append(self.disruptive_action.to_string())
results.extend(action.to_string() for action in self.non_disruptive_actions)
results.extend(action.to_string() for action in self.flow_actions)
results.extend(action.to_string() for action in self.data_actions)
return ", ".join(results)

def __str__(self):
return f"Disruptive: {self.disruptive_action}, NonDisruptive: {self.non_disruptive_actions}, Flow: {self.flow_actions}, Data: {self.data_actions}"

def set_disruptive_action_with_param(self, action, value):
self.disruptive_action = Action(action, value)

def set_disruptive_action_only(self, action):
self.disruptive_action = Action(action)

def add_non_disruptive_action_with_param(self, action, param):
self.non_disruptive_actions.append(Action(action, param))

def add_non_disruptive_action_only(self, action):
self.non_disruptive_actions.append(Action(action))

def add_flow_action_with_param(self, action, param):
self.flow_actions.append(Action(action, param))

def add_flow_action_only(self, action):
self.flow_actions.append(Action(action))

def add_data_action_with_params(self, action, param):
self.data_actions.append(Action(action, param))

def get_action_keys(self):
keys = [action.get_key() for action in self.non_disruptive_actions]
keys.extend(action.get_key() for action in self.flow_actions)
keys.extend(action.get_key() for action in self.data_actions)
return keys

def get_action_by_key(self, key):
for action in self.non_disruptive_actions:
if action.get_key() == key:
return action
for action in self.flow_actions:
if action.get_key() == key:
return action
for action in self.data_actions:
if action.get_key() == key:
return action
return None

def get_actions_by_key(self, key):
actions = [action for action in self.non_disruptive_actions if action.get_key() == key]
actions.extend(action for action in self.flow_actions if action.get_key() == key)
actions.extend(action for action in self.data_actions if action.get_key() == key)
return actions
37 changes: 37 additions & 0 deletions src/seclang_parser/cli.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
"""Command-line interface for the CRS parser."""

import argparse
from .parser import parse_file


def parse_args():
"""Parse our command line arguments"""
cmdline = argparse.ArgumentParser(description="CRS parser")
cmdline.add_argument(
"-f",
"--files",
metavar="FILE",
required=True,
nargs="*",
help="files to read, if empty, stdin is used",
)
return cmdline.parse_args()

def cli(args: list[str] = None):
""" Receives a list of files to parse """
for file in args.files:
with open(file, "r", encoding="utf-8") as f:
parse_file(f.read())

return True


def run():
"""Runs the example parser"""
args = parse_args()
return cli(args)


if __name__ == "__main__":
run()

11 changes: 11 additions & 0 deletions src/seclang_parser/configuration.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
class Configuration:
def __init__(self, marker=None, directives=None):
self.marker = marker
self.directives = directives or []

class ConfigurationList:
def __init__(self):
self.configurations = []

def add_configuration(self, configuration):
self.configurations.append(configuration)
Loading