Skip to content

Commit 1770855

Browse files
msureshkumar88Suresh Kumar Moharajan
andauthored
fix(encoded_exfil_detection): remove parametric tests, drop Python fallback, bump to 0.2.1 (#64)
* fix(encoded_exfil_detection): remove parametric use_rust testing; make test-unit Rust-optional The plugin tests were running every scenario twice — once with use_rust=False (Python fallback) and once with use_rust=True (Rust backend) — via @pytest.mark.parametrize. Since the Rust extension is the only production implementation, the Python-path variants test internal fallback code that users never hit directly and that is not a supported product surface. Changes: - Remove all @pytest.mark.parametrize("use_rust", ...) decorators; each test now calls _scan_container(payload, cfg) and lets the plugin auto-select the backend (Rust when available, Python fallback otherwise). - Remove TestRustPythonParity class (Rust/Python output parity is only meaningful while two maintained implementations exist). - Strip explicit use_rust=False from non-parametric helpers. - Rename TestNewFeaturesRustParity → TestNewFeatures. - Rename test_max_findings_per_value_cap_python_path → test_max_findings_per_value_cap. - Guard make test-unit to emit a skip message instead of a hard error when cargo is not on PATH, so pytest can still run in environments without Rust tooling installed. Closes #63 Signed-off-by: Suresh Kumar Moharajan <suresh.kumar.m@ibm.com> * refactor(encoded_exfil_detection): remove Python fallback; Rust is the only implementation The Python scanning implementation (shannon_entropy, printable_ratio, decode_candidate, scan_text, scan_container, etc.) was a complete duplicate of the Rust engine kept as a silent ImportError fallback. Since the Rust extension is the sole production path and has full feature parity, the fallback is dead weight. Changes: - encoded_exfil_detection.py: delete all Python detection functions and constants; replace try/except import with a direct hard import of ExfilDetectorEngine and py_scan_container; simplify plugin __init__ to always construct the Rust engine; keep _scan_container and _scan_text as thin Rust-backed wrappers for external callers - __init__.py: remove backward-compat py_scan_container re-export - test_integration.py: remove 12 tests that exercised deleted Python helpers (_shannon_entropy, _normalize_padding, _decode_candidate, _has_egress_context, _printable_ratio, _evaluate_candidate); all remaining 84 tests pass via the Rust engine Closes #63 Signed-off-by: Suresh Kumar Moharajan <suresh.kumar.m@ibm.com> * chore(encoded_exfil_detection): bump version to 0.2.1 Signed-off-by: Suresh Kumar Moharajan <suresh.kumar.m@ibm.com> * fix(encoded_exfil_detection): address PR #64 review feedback Three issues raised by reviewer lucarlig: 1. Stubs diverge from runtime API — remove py_scan_container from __init__.pyi and from the hardcoded top-level stub in stub_gen.rs. The symbol is no longer re-exported by __init__.py so type checkers were accepting an import that fails at runtime. 2. Allowlist regex validation used Python re.compile() semantics, which accepts lookaround and backreferences that Rust regex rejects. Replace the misleading Python check with a non-empty-string guard and wrap ExfilDetectorEngine construction in a try/except that raises a clear ValueError naming allowlist_patterns and the unsupported features, so the engine fails closed with an actionable message. 3. test-unit silently skipped when cargo was absent, letting test-all and check-all go green without testing the only scanner implementation. Make test-unit fail loudly if cargo is missing. Add test-unit-local as an explicit opt-in target that preserves the skip-with-notice behaviour for environments without Rust toolchain. Signed-off-by: Suresh Kumar Moharajan <suresh.kumar.m@ibm.com> * fix(encoded_exfil_detection): address PR #64 second-round review feedback 1. Restore py_scan_container top-level re-export — removing it in a patch release (0.2.0→0.2.1) is a breaking change; lazy re-export added to __init__.py/__init__.pyi/stub_gen.rs so callers continue to work. 2. Guard Rust extension import at module level — replaced hard top-level import with a try/except that captures ImportError; _scan_container and EncodedExfilDetectorPlugin.__init__ now raise an actionable ImportError (rather than failing silently during plugin discovery). 3. Remove **_kwargs compatibility shim — _scan_container and _scan_text no longer accept use_rust= or other stale kwargs; callers using unsupported kwargs now get a clear TypeError instead of silent no-op. 4. Allowlist regex tests and docs — added test_python_valid_rust_invalid_allowlist_regex_rejected_at_init that passes (?<=foo)bar (lookbehind: valid Python, rejected by Rust's regex crate) and asserts ValueError matches "allowlist_patterns"; updated test_invalid_allowlist_regex_rejected_at_init to assert the same. README corrected: regex errors surface at engine initialization time, not at configuration time. Signed-off-by: Suresh Kumar Moharajan <suresh.kumar.m@ibm.com> --------- Signed-off-by: Suresh Kumar Moharajan <suresh.kumar.m@ibm.com> Co-authored-by: Suresh Kumar Moharajan <suresh.kumar.m@ibm.com>
1 parent 5e7d9f6 commit 1770855

8 files changed

Lines changed: 195 additions & 795 deletions

File tree

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

plugins/rust/python-package/encoded_exfil_detection/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "encoded_exfil_detection"
3-
version = "0.2.0"
3+
version = "0.2.1"
44
edition.workspace = true
55
authors.workspace = true
66
license.workspace = true

plugins/rust/python-package/encoded_exfil_detection/Makefile

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,11 @@ clippy:
2828

2929
# help: sync - Install plugin development dependencies
3030
# help: test - Run Rust unit tests and Python integration tests
31-
# help: test-unit - Run Rust unit tests
31+
# help: test-unit - Run Rust unit tests (requires cargo; fails loudly if absent)
32+
# help: test-unit-local - Run Rust unit tests; skip with a notice if cargo is absent
3233
# help: test-integration - Run repo-level integration tests for encoded_exfil_detection
3334
# help: test-all - Alias for test
34-
.PHONY: sync test test-unit test-python test-integration test-all verify-stubs
35+
.PHONY: sync test test-unit test-unit-local test-python test-integration test-all verify-stubs
3536

3637
sync:
3738
uv sync --dev
@@ -40,6 +41,14 @@ test-unit:
4041
@echo "$(GREEN)Running encoded_exfil_detection Rust tests...$(NC)"
4142
$(CARGO) test
4243

44+
test-unit-local:
45+
@echo "$(GREEN)Running encoded_exfil_detection Rust tests...$(NC)"
46+
@if command -v $(CARGO) >/dev/null 2>&1; then \
47+
$(CARGO) test; \
48+
else \
49+
echo "$(YELLOW)cargo not found — skipping Rust unit tests (use test-unit to require cargo)$(NC)"; \
50+
fi
51+
4352
test: test-unit test-integration
4453

4554
test-python:

plugins/rust/python-package/encoded_exfil_detection/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ Blocking responses use the `ENCODED_EXFIL_DETECTED` violation code.
8585

8686
## Security Notes
8787

88-
- Guardrails reject invalid allowlist regexes at configuration time.
88+
- Guardrails reject Rust-incompatible allowlist regexes at engine initialization time (during plugin construction). Features such as lookaround and backreferences are not supported.
8989
- Scan and recursion caps exist to keep detection bounded on large payloads.
9090
- Detailed findings can be reduced or sanitized before metadata emission depending on configuration.
9191

plugins/rust/python-package/encoded_exfil_detection/cpex_encoded_exfil_detection/__init__.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,7 @@ def __getattr__(name: str):
1717
}
1818
return exports[name]
1919
if name == "py_scan_container":
20-
from cpex_encoded_exfil_detection.encoded_exfil_detection_rust import (
21-
py_scan_container,
22-
)
23-
20+
from cpex_encoded_exfil_detection.encoded_exfil_detection_rust import py_scan_container
2421
return py_scan_container
2522
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
2623

0 commit comments

Comments
 (0)