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
66 changes: 66 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
name: CI

on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
schedule:
# Run weekly on Mondays at midnight UTC to catch upstream changes
- cron: '0 0 * * 1'

jobs:
test:
runs-on: ubuntu-latest
permissions:
contents: read
strategy:
fail-fast: false
matrix:
python-version: ['3.9', '3.10', '3.11', '3.12']

steps:
- uses: actions/checkout@v6

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

- name: Install system dependencies
run: |
sudo apt-get update
sudo apt-get install -y swig gcc python3-dev

- name: Install Python dependencies
run: |
pip install --upgrade pip setuptools pytest

- name: Clone upstream libinjection
run: |
git clone --depth=1 https://github.com/libinjection/libinjection.git upstream

- name: Copy upstream source files
run: |
cp -f upstream/src/libinjection*.h upstream/src/libinjection*.c libinjection/

- name: Create tests symlink for test_driver.py
# test_driver.py resolves test files relative to ../tests from the repo root
run: |
ln -s "$(realpath upstream/tests)" "$(realpath ..)/tests"

- name: Generate words.py from upstream data
run: |
python json2python.py < upstream/src/sqlparse_data.json > words.py

- name: Generate SWIG wrapper
run: |
swig -python -builtin -Wall -Wextra libinjection/libinjection.i

- name: Build C extension in-place
run: |
python setup.py build_ext --inplace

- name: Run tests
run: |
pytest test_driver.py -v
4 changes: 3 additions & 1 deletion json2python.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@ def lookup(state, stype, keyword):
keyword = keyword.decode('latin-1')
keyword = keyword.upper()
if stype == libinjection.LOOKUP_FINGERPRINT:
if keyword in fingerprints and libinjection.sqli_not_whitelist(state):
# sqli_check_fingerprint calls sqli_blacklist (fingerprint membership
# check) and sqli_not_whitelist (false-positive reduction) internally.
if libinjection.sqli_check_fingerprint(state):
return 'F'
else:
return chr(0)
Expand Down
9 changes: 9 additions & 0 deletions libinjection/libinjection.i
Original file line number Diff line number Diff line change
Expand Up @@ -154,5 +154,14 @@ for (i = 0; i < $1_dim0; i++) {
}
%include "libinjection_error.h"
%include "libinjection.h"

// These functions are declared static in the upstream header and therefore
// cannot be exported as symbols from the compiled extension. Ignore them
// so SWIG does not generate wrappers that produce undefined symbol errors
// at import time. Use sqli_check_fingerprint (non-static) instead.
%ignore libinjection_sqli_reset;
%ignore libinjection_sqli_lookup_word;
%ignore libinjection_sqli_blacklist;
%ignore libinjection_sqli_not_whitelist;
%include "libinjection_sqli.h"
%include "libinjection_xss.h"