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
31 changes: 30 additions & 1 deletion .github/workflows/smoke-test-ffi.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,36 @@ jobs:
run: iex "& { $(iwr 'https://github.com/AikidoSec/safe-chain/releases/latest/download/install-safe-chain.ps1' -UseBasicParsing) } -ci"

- name: Install dependencies
run: make install && cp -r .cache/binaries/* aikido_zen/libs
run: make install

- name: Run tests
run: poetry run pytest aikido_zen/vulnerabilities/sql_injection/init_test.py

smoke-test-musl:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-24.04, ubuntu-22.04-arm]
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14"]

steps:
- name: Checkout code
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false

- name: Download binaries
run: make binaries && cp -r .cache/binaries/* aikido_zen/libs

# pip runs inside the container, so safe-chain is set up there and not on the runner.
# build-base compiles pytricia, which has no prebuilt wheel.
- name: Run tests on python:${{ matrix.python-version }}-alpine
run: |
docker run --rm -v "$(pwd):/app" -w /app python:${{ matrix.python-version }}-alpine sh -eo pipefail -c "
apk add --no-cache build-base curl
curl -fsSL https://github.com/AikidoSec/safe-chain/releases/latest/download/install-safe-chain.sh | sh -s -- --ci
export PATH=\"\$HOME/.safe-chain/shims:\$HOME/.safe-chain/bin:\$PATH\"
pip install --root-user-action=ignore . pytest
python -m pytest aikido_zen/helpers/zen_internals_test.py
"
Comment on lines +76 to +83

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Binary, code or archive is pulled from a remote source without integrity verification - medium severity
A GitHub Actions Workflow was built using an artifact from a remote source without any integrity verification. If the remote artifact were silently replaced with a malicious version (for example, through a supply chain attack), the integrity and confidentiality of the environment in which the container is deployed could be compromised.

Show fix

Remediation: Validate the artifact against a trusted SHA-512 checksum in the CI/CD pipeline using sha512sum in check mode. Store the expected checksum in a file (e.g., artifact.sha512), then verify it with: sha512sum -c artifact.sha512. Enable strict error handling (for example, set -e in shell scripts) so the pipeline fails if verification fails or outputs errors.

Reply @AikidoSec ignore: [REASON] to ignore this issue.
More info

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the same safe-chain install line used in the other 8 workflows here and in firewall-node, and none of them pin a version.

21 changes: 14 additions & 7 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@ BUILD_DIR := dist/
.PHONY: build
build: check_binaries
@echo "Building using poetry, creates $(BUILD_DIR) folder."
@echo "Copying binaries from $(CACHE_DIR) folder : "
cp -r .cache/binaries/* aikido_zen/libs
poetry build
.PHONY: clean
clean:
Expand Down Expand Up @@ -46,35 +44,44 @@ benchmark:


# Binaries cache :
BASE_URL = https://github.com/AikidoSec/zen-internals/releases/download/v0.1.60
BASE_URL = https://github.com/AikidoSec/zen-internals/releases/download/v0.1.74
FILES = \
libzen_internals_aarch64-apple-darwin.dylib \
libzen_internals_aarch64-apple-darwin.dylib.sha256sum \
libzen_internals_aarch64-pc-windows-msvc.dll \
libzen_internals_aarch64-pc-windows-msvc.dll.sha256sum \
libzen_internals_aarch64-unknown-linux-gnu.so \
libzen_internals_aarch64-unknown-linux-gnu.so.sha256sum \
libzen_internals_aarch64-unknown-linux-musl.so \
libzen_internals_aarch64-unknown-linux-musl.so.sha256sum \
libzen_internals_x86_64-apple-darwin.dylib \
libzen_internals_x86_64-apple-darwin.dylib.sha256sum \
libzen_internals_x86_64-pc-windows-gnu.dll \
libzen_internals_x86_64-pc-windows-gnu.dll.sha256sum \
libzen_internals_x86_64-unknown-linux-gnu.so \
libzen_internals_x86_64-unknown-linux-gnu.so.sha256sum
libzen_internals_x86_64-unknown-linux-gnu.so.sha256sum \
libzen_internals_x86_64-unknown-linux-musl.so \
libzen_internals_x86_64-unknown-linux-musl.so.sha256sum

binaries: binaries_make_dir $(addprefix .cache/binaries/, $(FILES))
echo "$(BASE_URL)" > $(CACHE_DIR)/.version
binaries_make_dir:
rm -rf .cache/binaries
mkdir -p .cache/binaries/
.cache/binaries/%:
@echo "Downloading $*..."
curl -L -o $@ $(BASE_URL)/$*

# Binaries from another release may not match the Python bindings, so download them again.
.PHONY: check_binaries
check_binaries:
@if [ -d "$(CACHE_DIR)" ]; then \
echo "Directory $(CACHE_DIR) exists."; \
@if [ "$$(cat $(CACHE_DIR)/.version 2>/dev/null)" = "$(BASE_URL)" ]; then \
echo "Directory $(CACHE_DIR) has the binaries from $(BASE_URL)."; \
else \
echo "Directory $(CACHE_DIR) is empty. Running 'make binaries'..."; \
echo "Directory $(CACHE_DIR) is missing or outdated. Running 'make binaries'..."; \
$(MAKE) binaries; \
fi
cp -r $(CACHE_DIR)/* aikido_zen/libs


# Replace version number automatically on publish :
Expand Down
36 changes: 0 additions & 36 deletions aikido_zen/helpers/get_lib_path.py

This file was deleted.

72 changes: 60 additions & 12 deletions aikido_zen/helpers/zen_internals.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,30 +6,74 @@
import ctypes
import os
import platform
import sysconfig

from aikido_zen.helpers.get_lib_path import get_binary_path
from aikido_zen.helpers.logging import logger


_TARGET = f"{platform.system().lower()}-{platform.machine().lower()}"


def _binary_path():
os_name = platform.system().lower()
machine = platform.machine().lower()
is_arm = "arm64" in machine or "aarch64" in machine
file_name = "libzen_internals_"

# On macOS, platform.machine() returns "arm64" for Apple Silicon
# On Linux, platform.machine() returns "aarch64" for ARM64
if is_arm:
file_name += "aarch64-"
# On macOS, platform.machine() returns "x86_64" for Intel
# On Linux, platform.machine() returns "x86_64" for AMD64
elif "x86_64" in machine or "amd64" in machine:
file_name += "x86_64-"

if os_name == "windows":
file_name += "pc-windows-msvc.dll" if is_arm else "pc-windows-gnu.dll"
elif os_name == "darwin":
file_name += "apple-darwin.dylib"
elif os_name in ["linux", "linux2"]:
# The library must use the same libc as this Python, which records it at build
# time (for example "x86_64-pc-linux-musl"). platform.libc_ver() and files under
# /lib describe the system, which can have both glibc and musl installed.
host = sysconfig.get_config_var("HOST_GNU_TYPE") or ""
libc = "musl" if "musl" in host else "gnu"
file_name += f"unknown-linux-{libc}.so"

return os.path.join(
os.path.dirname(os.path.abspath(__file__)), "../libs", file_name
)


def _load():
binary_path = get_binary_path()
target = f"{platform.system().lower()}-{platform.machine().lower()}"
binary_path = _binary_path()
if not os.path.exists(binary_path):
logger.warning(
"Zen will NOT block SQL injection attacks. Cannot find the zen-internals library for %s. "
"Cannot find the zen-internals library for %s. "
"Request support: https://github.com/AikidoSec/firewall-python/issues",
target,
_TARGET,
)
return None
try:
return ctypes.CDLL(binary_path)
except OSError as e:
logger.warning(
"Zen will NOT block SQL injection attacks. Failed to load the zen-internals library for %s: %s",
target,
e,
)
return None
error = e
# A musl Python built without HOST_GNU_TYPE picks the glibc build, which musl

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we have CI tests using alpine linux? If not, let's add?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, adding now

# cannot load.
musl_path = os.path.join(
os.path.dirname(binary_path),
os.path.basename(binary_path).replace("-gnu.so", "-musl.so"),
)
Comment on lines +64 to +67

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Building musl_path from the directory and basename needlessly decomposes and reassembles binary_path before replacing the suffix.

Show fix
Suggested change
musl_path = os.path.join(
os.path.dirname(binary_path),
os.path.basename(binary_path).replace("-gnu.so", "-musl.so"),
)
musl_path = binary_path.replace("-gnu.so", "-musl.so")
Details

✨ AI Reasoning
​The retry path is derived from binary_path. Splitting it into directory and basename and joining those parts again adds an unnecessary step without changing the resulting path.

Reply on this comment to give feedback or ignore the issue.
More info

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would bring back an earlier bug: replace changes every -gnu.so in the path, including folder names, so a library in a folder like app-gnu.so/ would never get the musl retry. test_load_falls_back_to_the_musl_build_when_the_glibc_build_fails covers this and fails with the suggested line.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⛔ You don't have permission to ignore issues.

if musl_path != binary_path and os.path.exists(musl_path):
try:
return ctypes.CDLL(musl_path)
except OSError:
pass
logger.warning(
"Failed to load the zen-internals library for %s: %s", _TARGET, error
)
return None


def _bind(internals_lib, name, argtypes, restype):
Expand All @@ -39,7 +83,9 @@ def _bind(internals_lib, name, argtypes, restype):
try:
function = getattr(internals_lib, name)
except AttributeError:
logger.debug("The zen-internals library has no function %s", name)
logger.warning(
"The zen-internals library for %s has no %s function.", _TARGET, name
)
return None
function.argtypes = argtypes
function.restype = restype
Expand All @@ -60,6 +106,8 @@ def _bind(internals_lib, name, argtypes, restype):
],
ctypes.c_int,
)
if _detect_sql_injection is None:
logger.warning("Zen will NOT block SQL injection attacks.")


def detect_sql_injection(query: bytes, user_input: bytes, dialect: int) -> int:
Expand Down
Loading
Loading