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

on:
push:
branches: [ master ]
paths:
- 'bindings/**'
- 'include/naab/public/**'
- 'src/api/**'
- 'CMakeLists.txt'
pull_request:
branches: [ master ]
paths:
- 'bindings/**'
- 'include/naab/public/**'
- 'src/api/**'
- 'CMakeLists.txt'
workflow_dispatch:

jobs:
build-shared-lib:
name: Build libnaab-governance
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
submodules: recursive

- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install -y \
cmake ninja-build libsqlite3-dev python3-dev \
libssl-dev libffi-dev libcurl4-openssl-dev pkg-config

- name: Build shared library
run: |
cmake -B build -DCMAKE_BUILD_TYPE=Release -G Ninja
ninja -C build naab_governance naab-gov -j$(nproc)
# Verify shared lib was built
ls -la build/libnaab-governance.so*

- name: Prepare artifacts
run: |
mkdir -p artifacts
# Copy the real .so file (not symlinks — artifacts don't preserve them)
cp build/libnaab-governance.so.*.* artifacts/libnaab-governance.so 2>/dev/null || \
cp build/libnaab-governance.so artifacts/libnaab-governance.so
cp build/naab-gov artifacts/
ls -la artifacts/

- name: Upload shared library
uses: actions/upload-artifact@v4
with:
name: libnaab-governance
path: artifacts/
retention-days: 1

test-go:
name: Go Bindings
needs: build-shared-lib
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: actions/setup-go@v5
with:
go-version: '1.21'
cache: false

- name: Download shared library
uses: actions/download-artifact@v4
with:
name: libnaab-governance
path: build

- name: Install shared library
run: |
sudo cp build/libnaab-governance.so /usr/local/lib/
sudo ldconfig

- name: Run Go tests
working-directory: bindings/go
run: |
CGO_LDFLAGS="-L/usr/local/lib" \
go test -v ./naabgov/

test-rust:
name: Rust Bindings
needs: build-shared-lib
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: dtolnay/rust-toolchain@stable

- name: Download shared library
uses: actions/download-artifact@v4
with:
name: libnaab-governance
path: build

- name: Install shared library
run: |
sudo cp build/libnaab-governance.so /usr/local/lib/
sudo ldconfig

- name: Run Rust tests
working-directory: bindings/rust
run: |
NAAB_LIB_DIR=/usr/local/lib cargo test --verbose

test-java:
name: Java Bindings
needs: build-shared-lib
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: '17'

- name: Download shared library
uses: actions/download-artifact@v4
with:
name: libnaab-governance
path: build

- name: Install shared library
run: |
sudo cp build/libnaab-governance.so /usr/local/lib/
sudo ldconfig

- name: Build JNI bridge
working-directory: bindings/java
run: |
# Compile Java source
javac -d classes src/main/java/org/naab/governance/GovernanceEngine.java

# Build JNI shared library
cc -shared -fPIC -o libnaab-governance-jni.so \
src/main/c/naab_gov_jni.c \
-I${JAVA_HOME}/include \
-I${JAVA_HOME}/include/linux \
-I../../include/naab/public \
-L/usr/local/lib \
-lnaab-governance

sudo cp libnaab-governance-jni.so /usr/local/lib/
sudo ldconfig

- name: Compile tests
working-directory: bindings/java
run: |
javac -d classes -cp classes \
src/test/java/org/naab/governance/GovernanceEngineTest.java

- name: Run Java tests
working-directory: bindings/java
run: |
java -cp classes \
-Djava.library.path=/usr/local/lib \
org.naab.governance.GovernanceEngineTest

test-csharp:
name: C# Bindings
needs: build-shared-lib
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: actions/setup-dotnet@v4
with:
dotnet-version: '8.0'

- name: Download shared library
uses: actions/download-artifact@v4
with:
name: libnaab-governance
path: build

- name: Install shared library
run: |
sudo cp build/libnaab-governance.so /usr/local/lib/
sudo ldconfig

- name: Run C# tests
working-directory: bindings/csharp
run: |
LD_LIBRARY_PATH=/usr/local/lib dotnet test NaabGovernance.Tests/ --verbosity normal

test-python:
name: Python Bindings (subprocess)
needs: build-shared-lib
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: actions/setup-python@v5
with:
python-version: '3.12'

- name: Download shared library
uses: actions/download-artifact@v4
with:
name: libnaab-governance
path: build

- name: Install shared library and CLI
run: |
sudo cp build/libnaab-governance.so /usr/local/lib/
sudo ldconfig
chmod +x build/naab-gov

- name: Run Python binding smoke test
run: |
python3 -c "
import sys, os
sys.path.insert(0, 'bindings/python')

# Test 1: ctypes mode (shared lib available)
os.environ['NAAB_GOV_LIB'] = os.path.abspath('build/libnaab-governance.so')
from naab_governance import GovernanceEngine

eng = GovernanceEngine()
assert not eng._subprocess_mode, 'should be in ctypes mode'
eng.load_config_string('{\"version\":\"5.0\",\"mode\":\"enforce\"}')
result = eng.scan('python', 'x = 42')
assert not result.get('blocked'), 'safe code should not be blocked'
del eng
print('Python ctypes binding: OK')

# Test 2: subprocess mode (force by pointing to nonexistent lib)
os.environ['NAAB_GOV_LIB'] = '/nonexistent'
os.environ['NAAB_GOV_BIN'] = os.path.abspath('build/naab-gov')
# Re-import to get fresh instance
import importlib, naab_governance
importlib.reload(naab_governance)
eng2 = naab_governance.GovernanceEngine(lib_path='/nonexistent')
assert eng2._subprocess_mode, 'should be in subprocess mode'
eng2.load_config_dict({'version': '5.0', 'mode': 'enforce'})
result2 = eng2.scan('python', 'x = 42')
assert 'blocked' in result2, 'result should have blocked field'
print('Python subprocess binding: OK')
"
8 changes: 5 additions & 3 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -91,9 +91,11 @@ if(ENABLE_HARDENING AND NOT MSVC)
# Stack protection
add_compile_options(-fstack-protector-strong)

# Position-independent code (PIE) for ASLR
add_compile_options(-fPIE)
add_link_options(-pie)
# Position-independent code for ASLR
# CMAKE_POSITION_INDEPENDENT_CODE=ON (line 33) already adds -fPIC globally,
# which is a superset of -fPIE. We only need -pie for the linker on executables
# (-pie conflicts with -shared, so we scope it to CMAKE_EXE_LINKER_FLAGS).
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -pie")

# Fortify source (buffer overflow detection) — requires -O1+, inactive in Debug
if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
Expand Down
77 changes: 77 additions & 0 deletions bindings/csharp/NaabGovernance.Tests/GovernanceEngineTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
using Xunit;
using NaabGovernance;

namespace NaabGovernance.Tests;

public class GovernanceEngineTests
{
private const string TestConfig =
@"{""version"":""3.0"",""mode"":""enforce"",
""restrictions"":{""dangerous_calls"":{""level"":""hard""},
""code_injection"":{""level"":""hard""}},
""code_quality"":{""no_secrets"":{""level"":""hard""}}}";

[Fact]
public void Version_ReturnsNonEmpty()
{
var v = GovernanceEngine.Version;
Assert.False(string.IsNullOrEmpty(v));
}

[Fact]
public void Lifecycle_CreateAndDispose()
{
using var engine = new GovernanceEngine();
Assert.NotNull(engine);
}

[Fact]
public void Lifecycle_DoubleDispose()
{
var engine = new GovernanceEngine();
engine.Dispose();
engine.Dispose(); // should not throw
}

[Fact]
public void ScanSafeCode_NotBlocked()
{
using var engine = new GovernanceEngine();
engine.LoadConfigString(TestConfig);
Assert.True(engine.IsActive);

var result = engine.Scan("python", "x = 42\nprint(x)", "test.py", 1);
Assert.NotNull(result);
Assert.False(engine.WasBlocked);
}

[Fact]
public void ScanDangerousCode_Blocked()
{
using var engine = new GovernanceEngine();
engine.LoadConfigString(TestConfig);

engine.Scan("python", "import os; os.system('rm -rf /')", "test.py", 1);
Assert.True(engine.WasBlocked);
}

[Fact]
public void Reset_ClearsResults()
{
using var engine = new GovernanceEngine();
engine.LoadConfigString(TestConfig);

engine.Scan("python", "eval(input())", "test.py", 1);
Assert.True(engine.ResultCount > 0);

engine.Reset();
Assert.Equal(0, engine.ResultCount);
}

[Fact]
public void IsActive_FalseBeforeConfig()
{
using var engine = new GovernanceEngine();
Assert.False(engine.IsActive);
}
}
15 changes: 15 additions & 0 deletions bindings/csharp/NaabGovernance.Tests/NaabGovernance.Tests.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<IsPackable>false</IsPackable>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />
<PackageReference Include="xunit" Version="2.7.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.7" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="../NaabGovernance/NaabGovernance.csproj" />
</ItemGroup>
</Project>
1 change: 1 addition & 0 deletions bindings/java/src/main/c/naab_gov_jni.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
*/

#include <jni.h>
#include <stdint.h>
#include <stdlib.h>
#include "naab_governance.h"

Expand Down
Loading
Loading