Skip to content

Commit be9f4e0

Browse files
authored
Merge pull request #53 from magicsword-io/feat/multi-sigma-versions
split into frontend and backend to support multiple sigma versions in parallel
2 parents 20166da + 2619363 commit be9f4e0

20 files changed

+957
-919
lines changed

.github/workflows/deploy.yml

+17-15
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ on:
44
push:
55
branches:
66
- main
7+
schedule:
8+
- cron: "0 0 * * 0"
79

810
env:
911
SERVICE_NAME: sigconverter
@@ -14,23 +16,23 @@ jobs:
1416
runs-on: ubuntu-latest
1517

1618
steps:
17-
- name: Checkout code
18-
uses: actions/checkout@v3
19+
- name: Checkout code
20+
uses: actions/checkout@v3
1921

20-
- name: Setup Google Cloud SDK
21-
uses: google-github-actions/[email protected]
22-
with:
23-
project_id: ${{ secrets.PROJECT_ID }}
24-
service_account_key: ${{ secrets.GCLOUD_AUTH }}
22+
- name: Setup Google Cloud SDK
23+
uses: google-github-actions/[email protected]
24+
with:
25+
project_id: ${{ secrets.PROJECT_ID }}
26+
service_account_key: ${{ secrets.GCLOUD_AUTH }}
2527

26-
- name: Configure Docker
27-
run: gcloud auth configure-docker
28+
- name: Configure Docker
29+
run: gcloud auth configure-docker
2830

29-
- name: Build Docker image
30-
run: docker build -t ${{ env.IMAGE_NAME }} .
31+
- name: Build Docker image
32+
run: docker build -t ${{ env.IMAGE_NAME }} .
3133

32-
- name: Push Docker image to Google Container Registry
33-
run: gcloud builds submit --tag gcr.io/${{ secrets.PROJECT_ID }}/${{ env.IMAGE_NAME }}
34+
- name: Push Docker image to Google Container Registry
35+
run: gcloud builds submit --tag gcr.io/${{ secrets.PROJECT_ID }}/${{ env.IMAGE_NAME }}
3436

35-
- name: Deploy to Google Cloud Run
36-
run: gcloud run deploy ${{ env.SERVICE_NAME }} --image gcr.io/${{ secrets.PROJECT_ID }}/${{ env.IMAGE_NAME }} --platform managed --region us-central1
37+
- name: Deploy to Google Cloud Run
38+
run: gcloud run deploy ${{ env.SERVICE_NAME }} --image gcr.io/${{ secrets.PROJECT_ID }}/${{ env.IMAGE_NAME }} --platform managed --region us-central1

.github/workflows/packages-test.yml

+17-12
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# This workflow will install Python dependencies, run tests and lint with a single version of Python
22
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions
33

4-
name: Backend Packages Test
4+
name: Frontend Packages Test
55

66
on: # yamllint disable-line rule:truthy
77
push:
@@ -12,18 +12,23 @@ on: # yamllint disable-line rule:truthy
1212
- main
1313

1414
jobs:
15-
test-poetry-package:
15+
pip-package:
1616
runs-on: ubuntu-latest
1717
steps:
18-
- uses: actions/[email protected]
19-
with:
20-
submodules: true
18+
- uses: actions/[email protected]
19+
with:
20+
submodules: true
2121

22-
- name: Set up Python 3.11
23-
uses: actions/[email protected]
24-
with:
25-
python-version: 3.11
22+
- name: Set up Python 3.11
23+
uses: actions/[email protected]
24+
with:
25+
python-version: 3.11
2626

27-
- name: Test poetry package installation
28-
run: |
29-
python -m pip install poetry && poetry install
27+
- name: Install the latest version of uv
28+
uses: astral-sh/setup-uv@v3
29+
with:
30+
version: "latest"
31+
32+
- name: Test uv package installation
33+
run: uv venv && uv pip sync pyproject.toml
34+
working-directory: frontend

Dockerfile

+11-24
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,17 @@
1-
# Use the specified Python version
21
FROM python:3.11.4-slim-buster
32

4-
# Configure Poetry
5-
ENV POETRY_VERSION=1.6.1
6-
ENV POETRY_HOME=/opt/poetry
7-
ENV POETRY_VENV=/opt/poetry-venv
8-
ENV POETRY_CACHE_DIR=/opt/.cache
3+
# install dependencies
4+
RUN apt-get update
5+
RUN apt-get install -y git curl jq
6+
COPY --from=ghcr.io/astral-sh/uv:latest /uv /bin/uv
97

10-
# Install poetry separated from system interpreter
11-
RUN python3 -m venv $POETRY_VENV \
12-
&& $POETRY_VENV/bin/pip install -U pip setuptools \
13-
&& $POETRY_VENV/bin/pip install poetry==${POETRY_VERSION}
14-
15-
# Add `poetry` to PATH
16-
ENV PATH="${PATH}:${POETRY_VENV}/bin"
17-
18-
# Set the working directory
19-
WORKDIR /app
20-
21-
# Install dependencies
22-
COPY poetry.lock pyproject.toml ./
23-
RUN poetry install
24-
25-
# Copy the flask app to the working directory
8+
# define work directory
9+
WORKDIR /app/
2610
COPY . /app
2711

28-
# Run the application
12+
# install backend
13+
RUN cd backend && ./setup-sigma-versions.sh
14+
15+
# launch front- and backend
2916
EXPOSE 8000
30-
CMD [ "poetry", "run", "python", "./run.py" ]
17+
ENTRYPOINT ["./entrypoint.sh"]

run.py renamed to backend/backend.py

+50-33
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44
import os
55
import yaml
66
import base64
7-
import json
8-
from flask import Flask, jsonify, render_template, request, Response
7+
import importlib.metadata as metadata
8+
from flask import Flask, jsonify, request, Response
99

1010
from sigma.conversion.base import Backend
1111
from sigma.plugins import InstalledSigmaPlugins
@@ -20,43 +20,59 @@
2020
backends = plugins.backends
2121
pipeline_resolver = plugins.get_pipeline_resolver()
2222
pipelines = list(pipeline_resolver.list_pipelines())
23-
pipelines_names = [p[0] for p in pipelines]
2423

25-
26-
@app.route("/")
27-
def home():
28-
formats = []
29-
for backend in backends.keys():
30-
for name, description in plugins.backends[backend].formats.items():
31-
formats.append(
32-
{"name": name, "description": description, "backend": backend}
24+
@app.route("/api/v1/targets", methods=["GET"])
25+
def get_targets():
26+
response = []
27+
for name, backend in backends.items():
28+
response.append(
29+
{"name": name, "description": backend.name}
3330
)
34-
35-
for name, pipeline in pipelines:
36-
if len(pipeline.allowed_backends) > 0:
37-
pipeline.backends = ", ".join(pipeline.allowed_backends)
38-
else:
39-
pipeline.backends = "all"
40-
41-
return render_template(
42-
"index.html", backends=backends, pipelines=pipelines, formats=formats
43-
)
44-
45-
46-
@app.route("/getpipelines", methods=["GET"])
31+
return jsonify(response)
32+
33+
@app.route("/api/v1/formats", methods=["GET"])
34+
def get_formats():
35+
args = request.args
36+
response = []
37+
if len(args) == 0:
38+
for backend in backends.keys():
39+
for name, description in plugins.backends[backend].formats.items():
40+
response.append(
41+
{"name": name, "description": description, "target": backend}
42+
)
43+
elif "target" in args:
44+
target = args.get("target")
45+
for backend in backends.keys():
46+
if backend == target:
47+
for name, description in plugins.backends[backend].formats.items():
48+
response.append(
49+
{"name": name, "description": description}
50+
)
51+
52+
return jsonify(response)
53+
54+
@app.route("/api/v1/pipelines", methods=["GET"])
4755
def get_pipelines():
48-
return jsonify(pipelines_names)
49-
50-
51-
@app.route("/sigma", methods=["POST"])
56+
args = request.args
57+
response = []
58+
if len(args) == 0:
59+
for name, pipeline in pipelines:
60+
response.append({"name": name, "targets": list(pipeline.allowed_backends)})
61+
elif "target" in args:
62+
target = args.get("target")
63+
for name, pipeline in pipelines:
64+
if (len(pipeline.allowed_backends) == 0) or (target in pipeline.allowed_backends):
65+
response.append({"name": name, "targets": list(pipeline.allowed_backends)})
66+
return jsonify(response)
67+
68+
69+
@app.route("/api/v1/convert", methods=["POST"])
5270
def convert():
53-
# get params from request
5471
rule = str(base64.b64decode(request.json["rule"]), "utf-8")
5572
# check if input is valid yaml
5673
try:
5774
yaml.safe_load(rule)
5875
except:
59-
print("error")
6076
return Response(
6177
f"YamlError: Malformed yaml file", status=400, mimetype="text/html"
6278
)
@@ -84,7 +100,7 @@ def convert():
84100
try:
85101
backend_class = backends[target]
86102
except:
87-
return Response(f"Unknown Backend", status=400, mimetype="text/html")
103+
return Response(f"Unknown Target", status=400, mimetype="text/html")
88104

89105
try:
90106
processing_pipeline = pipeline_resolver.resolve(pipeline)
@@ -109,6 +125,7 @@ def convert():
109125

110126
return result
111127

112-
113128
if __name__ == "__main__":
114-
app.run(host="0.0.0.0", port=int(os.environ.get("PORT", 8000)))
129+
current_version = metadata.version("sigma-cli")
130+
port = int(f'8{current_version.replace(".","")}')
131+
app.run(host="0.0.0.0", port=int(os.environ.get("PORT", port)))

backend/launch-backends.sh

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#!/bin/bash
2+
3+
# Specify the directory to search in (or use the current directory)
4+
directory="./"
5+
6+
# Iterate over all subdirectories
7+
for dir in "$directory"/*/; do
8+
if [ -d "$dir" ]; then
9+
version=$(basename $dir)
10+
echo "Launching sigconverter backend for sigma version: $version"
11+
./$version/.venv/bin/python ./backend.py &
12+
fi
13+
done

backend/pyproject.toml

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
[project]
2+
name = "sigconverter-backend"
3+
version = "1.0.0"
4+
description = "backend for the sigconverter projects"
5+
readme = "README.md"
6+
requires-python = ">=3.10"
7+
authors = [{ name = "Magic Sword", email = "[email protected]" }]
8+
dependencies = [
9+
"flask>=3.0.3",
10+
"setuptools>=75.1.0",
11+
]

backend/setup-sigma-versions.sh

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
#!/bin/bash
2+
3+
# fetch 10 latest versions of sigma-cli
4+
SIGMA_VERSIONS=$(curl -s https://pypi.org/pypi/sigma-cli/json | jq -r '.releases | keys | .[-10:] | .[]')
5+
6+
# prepare virtualenv for each version
7+
for VERSION in $SIGMA_VERSIONS; do
8+
# prepare folder to contain a single version
9+
mkdir $VERSION
10+
cp pyproject.toml uv.lock $VERSION
11+
cd $VERSION
12+
uv venv && uv -q pip sync pyproject.toml
13+
14+
# fetch all plugins from plugin directory json and install latest compatible plugins available
15+
uv -q add sigma-cli==$VERSION
16+
curl https://raw.githubusercontent.com/SigmaHQ/pySigma-plugin-directory/refs/heads/main/pySigma-plugins-v1.json | jq '.plugins[].package' | xargs -n 1 uv add -q
17+
18+
# remove if installed because of https://github.com/redsand/pySigma-backend-hawk/issues/1
19+
uv -q remove pySigma-backend-hawk
20+
21+
# TODO: some problems with kusto backend, disable for now
22+
uv -q remove pySigma-backend-kusto
23+
24+
# remove unused pyparsing imports in older version, see https://github.com/SigmaHQ/pySigma/pull/289#issuecomment-2410153076
25+
find ./ -iwholename "*sigma/conversion/base.py" -exec sed -i "/from pyparsing import Set/d" {} +
26+
find ./ -iwholename "*sigma/exceptions.py" -exec sed -i "/from pyparsing import List/d" {} +
27+
cd ..
28+
done

0 commit comments

Comments
 (0)