Skip to content

Commit 0586007

Browse files
authored
Merge pull request #53 from corazawaf/copilot/add-swig-support
feat: add SWIG support for generating multi-language bindings
2 parents e7f1777 + 65dd99e commit 0586007

16 files changed

Lines changed: 1703 additions & 6 deletions

.github/workflows/make-install.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,11 @@ jobs:
1818
runs-on: ${{ matrix.os }}
1919
steps:
2020
- name: Install Go
21-
uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6
21+
uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0
2222
with:
2323
go-version: ${{ matrix.go-version }}
2424
- name: Checkout code
25-
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
25+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
2626
with:
2727
lfs: true
2828
fetch-depth: 0 #for better blame info

.github/workflows/swig.yml

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
name: Test SWIG wrappers
2+
3+
on:
4+
push:
5+
branches: [main]
6+
pull_request:
7+
8+
permissions:
9+
contents: read
10+
11+
jobs:
12+
swig-python:
13+
name: Python SWIG wrapper (${{ matrix.go-version }}, ${{ matrix.os }})
14+
strategy:
15+
matrix:
16+
go-version: [1.25.x, 1.26.x]
17+
os: [ubuntu-latest, macos-latest]
18+
runs-on: ${{ matrix.os }}
19+
steps:
20+
- name: Checkout code
21+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
22+
with:
23+
fetch-depth: 0
24+
25+
# Checkout before setup-go so go.mod is present for module cache key.
26+
- name: Install Go
27+
uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0
28+
with:
29+
go-version: ${{ matrix.go-version }}
30+
31+
- name: Install dependencies (Linux)
32+
if: runner.os == 'Linux'
33+
run: |
34+
sudo apt-get update -q
35+
sudo apt-get install -y swig python3-dev
36+
37+
- name: Cache Homebrew packages (macOS)
38+
if: runner.os == 'macOS'
39+
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
40+
with:
41+
path: |
42+
~/Library/Caches/Homebrew/downloads
43+
key: brew-swig-python-${{ runner.arch }}-${{ hashFiles('.github/workflows/swig.yml') }}
44+
restore-keys: brew-swig-python-${{ runner.arch }}-
45+
46+
- name: Install dependencies (macOS)
47+
if: runner.os == 'macOS'
48+
run: |
49+
brew install autoconf automake libtool swig
50+
51+
- name: Build libcoraza
52+
run: |
53+
./build.sh
54+
./configure
55+
make V=1
56+
57+
- name: Check SWIG interface sync
58+
run: make check-swig-sync
59+
60+
- name: Generate and compile Python SWIG wrapper
61+
run: |
62+
make -C examples/python
63+
64+
- name: Run Python example
65+
run: |
66+
make -C examples/python run
67+
68+
swig-java:
69+
name: Java SWIG wrapper (${{ matrix.go-version }}, ${{ matrix.os }})
70+
strategy:
71+
matrix:
72+
go-version: [1.25.x, 1.26.x]
73+
os: [ubuntu-latest, macos-latest]
74+
runs-on: ${{ matrix.os }}
75+
steps:
76+
- name: Checkout code
77+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
78+
with:
79+
fetch-depth: 0
80+
81+
# Checkout before setup-go so go.mod is present for module cache key.
82+
- name: Install Go
83+
uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0
84+
with:
85+
go-version: ${{ matrix.go-version }}
86+
87+
# setup-java handles JDK installation on all platforms via the GitHub
88+
# tool cache, avoiding the large Temurin cask download on macOS.
89+
- name: Set up Java
90+
uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4.7.1
91+
with:
92+
java-version: '21'
93+
distribution: 'temurin'
94+
95+
- name: Install dependencies (Linux)
96+
if: runner.os == 'Linux'
97+
run: |
98+
sudo apt-get update -q
99+
sudo apt-get install -y swig
100+
101+
- name: Cache Homebrew packages (macOS)
102+
if: runner.os == 'macOS'
103+
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
104+
with:
105+
path: |
106+
~/Library/Caches/Homebrew/downloads
107+
key: brew-swig-java-${{ runner.arch }}-${{ hashFiles('.github/workflows/swig.yml') }}
108+
restore-keys: brew-swig-java-${{ runner.arch }}-
109+
110+
- name: Install dependencies (macOS)
111+
if: runner.os == 'macOS'
112+
run: |
113+
brew install autoconf automake libtool swig
114+
115+
- name: Build libcoraza
116+
run: |
117+
./build.sh
118+
./configure
119+
make V=1
120+
121+
- name: Check SWIG interface sync
122+
run: make check-swig-sync
123+
124+
- name: Generate and compile Java SWIG wrapper
125+
run: |
126+
make -C examples/java
127+
128+
- name: Run Java example
129+
run: |
130+
make -C examples/java run

.gitignore

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ docs/
1010
.deps/
1111
Makefile
1212
Makefile.in
13+
# Track the example Makefiles explicitly
14+
!examples/python/Makefile
15+
!examples/java/Makefile
1316
autom4te.cache/
1417
aclocal.m4
1518
ar-lib
@@ -34,4 +37,15 @@ _obj/
3437
.libs/
3538
Doxyfile
3639
.dirstamp
40+
coraza_wrap.c
41+
coraza_wrap_python.c
42+
coraza_wrap_java.c
43+
coraza.py
44+
examples/python/_coraza.so
45+
examples/python/_coraza_wrap.c
46+
examples/python/coraza.py
47+
examples/python/__pycache__/
48+
examples/java/gen/
49+
examples/java/coraza_wrap.c
50+
examples/java/libcoraza_jni.so
3751
vendor/

Makefile.am

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,26 @@ install-data-local: all check
3030
@INSTALL@ libcoraza.$(SHARED_EXT) $(DESTDIR)@prefix@/lib/
3131
@INSTALL@ coraza/coraza.h $(DESTDIR)@prefix@/include/coraza/coraza.h
3232

33-
.PHONY: docs
33+
.PHONY: docs swig check-swig-sync
3434
docs:
3535
# build C header doxygen
3636
@doxygen ./Doxyfile
3737

38+
check-swig-sync:
39+
@echo "Checking coraza.i is in sync with Go exports..."
40+
@missing=0; \
41+
for fn in $$(grep -h '//export ' $(srcdir)/libcoraza/coraza.go | sed 's|.*//export ||'); do \
42+
if ! grep -qE "(extern|%ignore).*$$fn" $(srcdir)/coraza.i; then \
43+
echo " MISSING: $$fn exported from Go but not declared in coraza.i"; \
44+
missing=1; \
45+
fi; \
46+
done; \
47+
if [ $$missing -eq 0 ]; then \
48+
echo " OK: all Go exports are accounted for in coraza.i"; \
49+
else \
50+
exit 1; \
51+
fi
52+
3853
check_PROGRAMS = tests/simple_get
3954

4055
if OSX
@@ -51,3 +66,14 @@ tests_simple_get_LDADD = ./libcoraza.a
5166
check: tests/simple_get
5267
go test -race ./...
5368
cd tests && ./check_result.sh simple_get
69+
70+
if HAVE_SWIG
71+
# Default target language for SWIG bindings (override with SWIG_LANG=<lang>)
72+
SWIG_LANG ?= python
73+
# Output directory for generated language files (override with SWIG_OUTDIR=<dir>)
74+
SWIG_OUTDIR ?= .
75+
76+
swig: coraza/coraza.h
77+
@mkdir -p $(SWIG_OUTDIR)
78+
$(SWIG) -$(SWIG_LANG) -outdir $(SWIG_OUTDIR) -o coraza_wrap.c $(srcdir)/coraza.i
79+
endif

README.md

Lines changed: 73 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ Welcome to libcoraza, the C library for OWASP Coraza Web Application Firewall. B
77
* A C compiler:
88
* gcc or
99
* clang
10-
* Go compiler v1.24+
10+
* Go compiler v1.25+
1111
* libtool
1212
* autotools
1313
* make
@@ -55,8 +55,79 @@ go mod tidy
5555
make
5656
```
5757

58-
If you didn't install the built library (skipped the `sudo make install` step), you should set the LD_LIBRARY_PATH:
58+
If you didn't install the built library (skipped the `sudo make install` step), set the library path before running your application:
5959

6060
```
61+
# Linux
6162
export LD_LIBRARY_PATH=../:$LD_LIBRARY_PATH
63+
64+
# macOS
65+
export DYLD_LIBRARY_PATH=../:$DYLD_LIBRARY_PATH
66+
```
67+
68+
## SWIG language bindings
69+
70+
libcoraza ships a [SWIG](https://www.swig.org) interface file (`coraza.i`) that allows
71+
generating bindings for a wide range of languages including Python, Ruby, Java, PHP,
72+
Perl, and many others.
73+
74+
### Prerequisites
75+
76+
* SWIG 4.0 or later
77+
78+
Install on Debian/Ubuntu:
79+
80+
```
81+
sudo apt install swig
82+
```
83+
84+
Install on macOS (Homebrew):
85+
86+
```
87+
brew install swig
88+
```
89+
90+
### Ready-made examples
91+
92+
The `examples/` directory contains fully working examples with their own Makefiles:
93+
6294
```
95+
# Python
96+
make -C examples/python # build
97+
make -C examples/python run # build and run
98+
99+
# Java (requires JAVA_HOME to be set)
100+
make -C examples/java # build
101+
make -C examples/java run # build and run
102+
```
103+
104+
Each example exercises the full API including error and debug log callbacks.
105+
106+
### Building bindings for other languages
107+
108+
First build the library:
109+
110+
```
111+
./build.sh
112+
./configure
113+
make
114+
```
115+
116+
Then invoke SWIG directly against `coraza.i`:
117+
118+
```
119+
# Ruby example
120+
swig -ruby -o coraza_wrap.c coraza.i
121+
gcc -shared -fPIC coraza_wrap.c $(ruby -rrbconfig -e 'puts RbConfig::CONFIG["CFLAGS"]') \
122+
-L. -lcoraza -o coraza.so
123+
```
124+
125+
### Notes
126+
127+
* **Callbacks**`coraza_set_error_callback` and `coraza_set_debug_log_callback` are
128+
provided as language-specific trampolines for Python and Java (see `coraza.i` and the
129+
example directories). For other languages, refer to the SWIG documentation on
130+
`%callback` or director classes.
131+
* `coraza_matched_rule_get_error_log` returns a string owned by the caller.
132+
The generated wrapper takes ownership automatically so the target language
133+
runtime frees it when the object is garbage collected.

configure.ac

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
AC_PREREQ([2.60])
22

33
AC_INIT([libcoraza],
4-
m4_esyscmd([build-aux/git-version-gen .tarball-version]),
4+
m4_esyscmd([tr -d '[:space:]' < version.txt]),
55
[https://github.com/corazawaf/libcoraza/issues],
66
[libcoraza],
77
[https://github.com/corazawaf/libcoraza])
@@ -32,6 +32,10 @@ else
3232
fi
3333
PKG_PROG_PKG_CONFIG
3434

35+
# Check for SWIG (optional, needed for language bindings)
36+
AC_PATH_PROG([SWIG], [swig])
37+
AM_CONDITIONAL([HAVE_SWIG], [test -n "$SWIG"])
38+
3539
# check host os
3640
AC_CANONICAL_HOST
3741

@@ -78,5 +82,6 @@ echo \
7882
Preprocessor ${CPP} ${CPPFLAGS}
7983
C Compiler ${CC} ${CFLAGS}
8084
Go Version ${GOVERSION}
85+
SWIG ${SWIG:-not found (language bindings disabled)}
8186

8287
-----------------------------------------------------------------------"

0 commit comments

Comments
 (0)