Skip to content

Commit 01eb33e

Browse files
authored
Merge pull request #35 from coreruleset/refactor/python-directories
refactor(python): split into standard directories
2 parents fdc25e3 + 8406651 commit 01eb33e

22 files changed

Lines changed: 775 additions & 72 deletions

.github/workflows/go.yml

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,20 @@ name: Go
66
on:
77
push:
88
branches: [ "main" ]
9+
paths:
10+
- "**/*.go"
11+
- "**/*_test.go"
12+
- ".github/**"
13+
- "g4/**"
14+
- "magefile.go"
915
pull_request:
1016
branches: [ "main" ]
17+
paths:
18+
- "**/*.go"
19+
- "**/*_test.go"
20+
- ".github/**"
21+
- "g4/**"
22+
- "magefile.go"
1123

1224
jobs:
1325

@@ -19,7 +31,7 @@ jobs:
1931
- name: Set up Go
2032
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5
2133
with:
22-
go-version: '1.25'
34+
go-version: '1.23'
2335

2436
- name: Build
2537
run: |

.github/workflows/python.yml

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
# This workflow will build a golang project
2+
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-go
3+
4+
name: Python
5+
6+
on:
7+
push:
8+
branches: [ "main" ]
9+
paths:
10+
- "src/**"
11+
- "tests/**"
12+
- ".github/**"
13+
- "g4/**"
14+
- "pyproject.toml"
15+
pull_request:
16+
branches: [ "main" ]
17+
paths:
18+
- "src/**"
19+
- "tests/**"
20+
- ".github/**"
21+
- "g4/**"
22+
- "pyproject.toml"
23+
24+
jobs:
25+
python:
26+
runs-on: ubuntu-latest
27+
strategy:
28+
fail-fast: true
29+
matrix:
30+
python-version: ['3.11', '3.12', '3.13']
31+
32+
steps:
33+
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
34+
35+
- name: Set up Python 3
36+
uses: actions/setup-python@v5
37+
with:
38+
python-version: ${{ matrix.python-version }}
39+
40+
- name: Install uv
41+
uses: astral-sh/setup-uv@f0ec1fc3b38f5e7cd731bb6ce540c5af426746bb # v6
42+
with:
43+
enable-cache: true
44+
45+
- name: Set up Python 3
46+
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
47+
with:
48+
python-version: ${{ matrix.python-version }}
49+
50+
- name: "Generate parser files"
51+
run: |
52+
pushd g4
53+
./generate.sh
54+
popd
55+
56+
- name: Install dependencies
57+
run: |
58+
uv sync --all-extras --dev
59+
60+
- name: "Run unit tests"
61+
run: |
62+
uv run pytest -vs
63+

README.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,14 @@ go test ./...
2727
```
2828

2929
Or for python:
30+
31+
- Get [uv](https://github.com/astral-sh/uv) first
3032
```bash
3133
cd parser
3234
./generate.sh
3335
cd ..
34-
poetry install
35-
poetry run python ./base_test.py
36+
uv sync --all-extras --dev
37+
uv run pytest -vs
3638
```
3739

3840
## Authors

base_test.py

Lines changed: 0 additions & 42 deletions
This file was deleted.

poetry.lock

Lines changed: 0 additions & 17 deletions
This file was deleted.

pyproject.toml

Lines changed: 47 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,54 @@
1-
[tool.poetry]
1+
[project]
22
name = "seclang_parser"
3-
version = "0.1.0"
3+
dynamic = [ "version" ]
44
description = "SecLang Parser using ANTLR"
5-
authors = ["Felipe Zipitria <felipe.zipitria@owasp.org>"]
5+
authors = [
6+
{name = "Felipe Zipitria", email = "felipe.zipitria@owasp.org"}
7+
]
8+
requires-python = ">=3.11"
9+
license = "Apache-2.0"
610
readme = "README.md"
11+
keywords = ["OWASP", "CRS", "SecLang", "ModSecurity", "Coraza"]
712

8-
[tool.poetry.dependencies]
9-
python = "^3.9"
10-
antlr4-python3-runtime = "^4.13.2"
13+
classifiers = [
14+
"Programming Language :: Python :: 3",
15+
"License :: OSI Approved :: Apache Software License",
16+
"Operating System :: OS Independent",
17+
]
18+
19+
dependencies = [
20+
"antlr4-python3-runtime >=4.13.2",
21+
]
22+
23+
[project.scripts]
24+
seclang_parser = 'seclang_parser.cli:run'
25+
26+
[project.urls]
27+
issues = "https://github.com/coreruleset/seclang_parser/issues"
28+
homepage = "https://github.com/coreruleset/seclang_parser"
29+
repository = "https://github.com/coreruleset/seclang_parser.git"
30+
31+
[dependency-groups]
32+
dev = [
33+
"pytest >=8.1.1,<9"
34+
]
1135

1236
[build-system]
13-
requires = ["poetry-core"]
14-
build-backend = "poetry.core.masonry.api"
37+
requires = ["hatchling", "hatch-vcs"]
38+
build-backend = "hatchling.build"
39+
40+
[tool.hatch.version]
41+
source = "vcs"
42+
43+
[tool.hatch.version.raw-options]
44+
version_scheme = "no-guess-dev"
45+
46+
[[tool.uv.index]]
47+
name = "pypi"
48+
url = "https://pypi.org/simple/"
49+
publish-url = "https://pypi.org/legacy/"
1550

16-
[tool.poetry.scripts]
17-
parser = 'base_test:main'
51+
[[tool.uv.index]]
52+
name = "testpypi"
53+
url = "https://test.pypi.org/simple/"
54+
publish-url = "https://test.pypi.org/legacy/"

src/seclang_parser/__init__.py

Whitespace-only changes.

src/seclang_parser/actions.py

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
class Action:
2+
def __init__(self, action, param=None):
3+
self.action = action
4+
self.param = param
5+
6+
def set_action(self, action, param):
7+
self.action = action
8+
self.param = param
9+
10+
def to_string(self):
11+
if self.param:
12+
return f"{self.action}:{self.param}"
13+
return self.action
14+
15+
def get_key(self):
16+
return self.action
17+
18+
def __str__(self):
19+
return self.to_string()
20+
21+
22+
class SeclangActions:
23+
def __init__(self):
24+
self.disruptive_action = None
25+
self.non_disruptive_actions = []
26+
self.flow_actions = []
27+
self.data_actions = []
28+
29+
def to_string(self):
30+
results = []
31+
if self.disruptive_action and self.disruptive_action.action:
32+
results.append(self.disruptive_action.to_string())
33+
results.extend(action.to_string() for action in self.non_disruptive_actions)
34+
results.extend(action.to_string() for action in self.flow_actions)
35+
results.extend(action.to_string() for action in self.data_actions)
36+
return ", ".join(results)
37+
38+
def __str__(self):
39+
return f"Disruptive: {self.disruptive_action}, NonDisruptive: {self.non_disruptive_actions}, Flow: {self.flow_actions}, Data: {self.data_actions}"
40+
41+
def set_disruptive_action_with_param(self, action, value):
42+
self.disruptive_action = Action(action, value)
43+
44+
def set_disruptive_action_only(self, action):
45+
self.disruptive_action = Action(action)
46+
47+
def add_non_disruptive_action_with_param(self, action, param):
48+
self.non_disruptive_actions.append(Action(action, param))
49+
50+
def add_non_disruptive_action_only(self, action):
51+
self.non_disruptive_actions.append(Action(action))
52+
53+
def add_flow_action_with_param(self, action, param):
54+
self.flow_actions.append(Action(action, param))
55+
56+
def add_flow_action_only(self, action):
57+
self.flow_actions.append(Action(action))
58+
59+
def add_data_action_with_params(self, action, param):
60+
self.data_actions.append(Action(action, param))
61+
62+
def get_action_keys(self):
63+
keys = [action.get_key() for action in self.non_disruptive_actions]
64+
keys.extend(action.get_key() for action in self.flow_actions)
65+
keys.extend(action.get_key() for action in self.data_actions)
66+
return keys
67+
68+
def get_action_by_key(self, key):
69+
for action in self.non_disruptive_actions:
70+
if action.get_key() == key:
71+
return action
72+
for action in self.flow_actions:
73+
if action.get_key() == key:
74+
return action
75+
for action in self.data_actions:
76+
if action.get_key() == key:
77+
return action
78+
return None
79+
80+
def get_actions_by_key(self, key):
81+
actions = [action for action in self.non_disruptive_actions if action.get_key() == key]
82+
actions.extend(action for action in self.flow_actions if action.get_key() == key)
83+
actions.extend(action for action in self.data_actions if action.get_key() == key)
84+
return actions

src/seclang_parser/cli.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
"""Command-line interface for the CRS parser."""
2+
3+
import argparse
4+
from .parser import parse_file
5+
6+
7+
def parse_args():
8+
"""Parse our command line arguments"""
9+
cmdline = argparse.ArgumentParser(description="CRS parser")
10+
cmdline.add_argument(
11+
"-f",
12+
"--files",
13+
metavar="FILE",
14+
required=True,
15+
nargs="*",
16+
help="files to read, if empty, stdin is used",
17+
)
18+
return cmdline.parse_args()
19+
20+
def cli(args: list[str] = None):
21+
""" Receives a list of files to parse """
22+
for file in args.files:
23+
with open(file, "r", encoding="utf-8") as f:
24+
parse_file(f.read())
25+
26+
return True
27+
28+
29+
def run():
30+
"""Runs the example parser"""
31+
args = parse_args()
32+
return cli(args)
33+
34+
35+
if __name__ == "__main__":
36+
run()
37+
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
class Configuration:
2+
def __init__(self, marker=None, directives=None):
3+
self.marker = marker
4+
self.directives = directives or []
5+
6+
class ConfigurationList:
7+
def __init__(self):
8+
self.configurations = []
9+
10+
def add_configuration(self, configuration):
11+
self.configurations.append(configuration)

0 commit comments

Comments
 (0)