Skip to content
Merged
80 changes: 80 additions & 0 deletions tests/test_api_network_map.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import json

# Updated import path to match the network_auditing folder
from gitgalaxy.tools.network_auditing.full_api_network_map import run_api_audit

def test_shadow_and_ghost_api_detection(tmp_path):
"""
End-to-End test for the API Network Map.
Verifies the multi-language regex traps, Swagger auto-discovery (and test exclusion),
and the strict Shadow/Ghost API set-difference math.
"""
# 1. Construct the Mock Repository Space
repo_dir = tmp_path / "mock_api_repo"
repo_dir.mkdir()

# ==========================================================================
# A. Mock Source Code (The Physical Endpoints)
# ==========================================================================
# Python FastAPI
(repo_dir / "main.py").write_text('@app.get("/api/health")', encoding="utf-8")

# Node Express
(repo_dir / "server.js").write_text('router.post("/api/users")', encoding="utf-8")

# Java Spring Boot
(repo_dir / "UserController.java").write_text('@DeleteMapping("/api/users/{id}")', encoding="utf-8")

# Ruby Rails (THE SHADOW API - Intentionally omitted from Swagger docs)
(repo_dir / "admin.rb").write_text('get "/api/secret_debug"', encoding="utf-8")

# ==========================================================================
# B. Mock Official Documentation (The Approved Endpoints)
# ==========================================================================
official_swagger = {
"openapi": "3.0.0",
"paths": {
"/api/health": {"get": {}},
"/api/users": {"post": {}},
"/api/users/{id}": {"delete": {}},
"/api/legacy_v1_sync": {"put": {}} # THE GHOST API (In docs, but deleted from code)
}
}
(repo_dir / "openapi.json").write_text(json.dumps(official_swagger), encoding="utf-8")

# ==========================================================================
# C. Mock Test Swagger (Must be ignored by auto-discovery!)
# ==========================================================================
# We name this exactly "test" to trigger the programmatic filter inside run_api_audit
test_dir = repo_dir / "test"
test_dir.mkdir()
test_swagger = {"openapi": "3.0.0", "paths": {"/test/mock": {"get": {}}}}
(test_dir / "swagger.json").write_text(json.dumps(test_swagger), encoding="utf-8")

# ==========================================================================
# 2. Execute the Engine
# ==========================================================================
result = run_api_audit(repo_dir)

# ==========================================================================
# 3. The Invariant Assertions
# ==========================================================================
assert result["status"] == "success", f"Failed to audit: status was {result['status']}"

# A) Prove the Multi-Language Regex Traps worked
frameworks = result["frameworks"]
assert "Python (FastAPI/Flask/Django)" in frameworks
assert "Node.js (Express/Fastify/Koa)" in frameworks
assert "Java (Spring Boot)" in frameworks
assert "Ruby (Rails/Sinatra)" in frameworks

# B) Prove Shadow API detection (Physical code without Documentation)
assert result["shadow_count"] == 1
assert "GET /api/secret_debug" in result["shadow_apis"], "Engine failed to flag the undocumented Ruby Shadow API!"

# C) Prove Ghost API detection (Documentation without Physical code)
assert result["ghost_count"] == 1

# D) Prove Auto-Discovery segregation (It ignored the test directory)
# If it read the test swagger, the ghost count would be 2 (because /test/mock isn't in the code).
assert "GET /test/mock" not in result["shadow_apis"]
102 changes: 102 additions & 0 deletions tests/test_binary_anomaly_detector.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import os

Check notice

Code scanning / CodeQL

Unused import Note test

Import of 'os' is not used.
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
from unittest.mock import patch, MagicMock

Check notice

Code scanning / CodeQL

Unused import Note test

Import of 'MagicMock' is not used.

import gitgalaxy.tools.supply_chain_security.binary_anomaly_detector as xray_module

# ==============================================================================
# TEST 1: The Routing Matrix (Denylist vs Allowlist vs Test Folders)
# ==============================================================================
@patch("gitgalaxy.tools.supply_chain_security.binary_anomaly_detector.SecurityLens")
@patch("gitgalaxy.tools.supply_chain_security.binary_anomaly_detector.ApertureFilter")
def test_xray_routing_matrix(mock_aperture_class, mock_security_class, tmp_path, monkeypatch):
monkeypatch.setattr(xray_module, "DENYLIST_PATTERNS", ["*.key", "*.pem", "id_rsa*"])
monkeypatch.setattr(xray_module, "XRAY_BYPASS_EXTENSIONS", [".gz", ".zip"])
monkeypatch.setattr(xray_module, "ALLOWLIST_PATHS", ["approved_keys/"])

mock_aperture = mock_aperture_class.return_value
mock_aperture._check_solar_shield.return_value = True

mock_security = mock_security_class.return_value
mock_security.scan_content.return_value = {"counts": {"entropy": 6.5, "bitwise_hits": 0}}
mock_security.scan_binary.return_value = {}

repo_dir = tmp_path / "routing_repo"
repo_dir.mkdir()

# File A (Anomaly)
(repo_dir / "private.key").write_text("FAKE_PRIVATE_KEY", encoding="utf-8")

# File B (Bypass Allowlist)
approved_dir = repo_dir / "approved_keys"
approved_dir.mkdir()
(approved_dir / "service.pem").write_text("FAKE_CERT", encoding="utf-8")

# File C (Bypass Extension)
(repo_dir / "compressed.zip").write_text("FAKE_ZIP_DATA", encoding="utf-8")

# File D (Bypass Test Folder)
# Nested inside 'src' so the string matching for "/tests/" perfectly aligns
src_dir = repo_dir / "src"
src_dir.mkdir()
test_dir = src_dir / "tests"
test_dir.mkdir()
(test_dir / "mock_payload.dat").write_text("FAKE_HIGH_ENTROPY_DATA", encoding="utf-8")

result = xray_module.run_xray_audit(repo_dir)
assert result["anomalies_found"] == 1, "The routing matrix failed! Check Denylist/Allowlist math."

# ==============================================================================
# TEST 2: The Deep Scan Threat Identification
# ==============================================================================
@patch("gitgalaxy.tools.supply_chain_security.binary_anomaly_detector.SecurityLens")
@patch("gitgalaxy.tools.supply_chain_security.binary_anomaly_detector.ApertureFilter")
def test_xray_deep_scan_threats(mock_aperture_class, mock_security_class, tmp_path):
mock_aperture = mock_aperture_class.return_value
mock_aperture._check_solar_shield.return_value = True

repo_dir = tmp_path / "deep_scan_repo"
repo_dir.mkdir()

clean_file = repo_dir / "clean.txt"
clean_file.write_text("Hello world", encoding="utf-8")

spoofed_file = repo_dir / "hidden_exe.jpg"
spoofed_file.write_text("MZ\x90\x00...", encoding="utf-8")

mock_security = mock_security_class.return_value

def mock_scan_binary(head_bytes, ext):
if ext == ".jpg": return {"threat_snippet": "Magic Byte Mismatch: Expected JPEG, got PE32 Executable"}
return {}

def mock_scan_content(content, limit):
if "MZ" in content: return {"counts": {"entropy": 6.8, "bitwise_hits": 2}}
return {"counts": {"entropy": 1.2, "bitwise_hits": 0}}

mock_security.scan_binary.side_effect = mock_scan_binary
mock_security.scan_content.side_effect = mock_scan_content

result = xray_module.run_xray_audit(repo_dir)
assert result["anomalies_found"] == 1, "Failed to flag magic byte mismatch or high entropy!"

# ==============================================================================
# TEST 3: The Shebang Shield
# ==============================================================================
@patch("gitgalaxy.tools.supply_chain_security.binary_anomaly_detector.SecurityLens")
@patch("gitgalaxy.tools.supply_chain_security.binary_anomaly_detector.ApertureFilter")
def test_xray_shebang_shield(mock_aperture_class, mock_security_class, tmp_path):
mock_aperture = mock_aperture_class.return_value
mock_aperture._check_solar_shield.return_value = True

repo_dir = tmp_path / "shebang_repo"
repo_dir.mkdir()

sh_file = repo_dir / "deploy.sh"
sh_file.write_text("#!/bin/bash\necho 'Deploying...'", encoding="utf-8")

mock_security = mock_security_class.return_value
mock_security.scan_content.return_value = {"counts": {"entropy": 0, "bitwise_hits": 0}}
mock_security.scan_binary.return_value = {"threat_snippet": "Suspicious execution header: #!/bin/bash"}

result = xray_module.run_xray_audit(repo_dir)
assert result["anomalies_found"] == 0, "The Shebang Shield failed!"
101 changes: 101 additions & 0 deletions tests/test_sbom_generator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import pytest

Check notice

Code scanning / CodeQL

Unused import Note test

Import of 'pytest' is not used.
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
import json
import sys
from unittest.mock import patch, MagicMock

Check notice

Code scanning / CodeQL

Unused import Note test

Import of 'MagicMock' is not used.

# IMPORTANT: Adjust this import path depending on where sbom_generator.py lives
from gitgalaxy.tools.compliance.sbom_generator import UniversalManifestSlicer, main

# ==============================================================================
# TEST 1: The Multi-Ecosystem Slicer Guard
# ==============================================================================
def test_universal_manifest_slicer(tmp_path):
"""
Proves that the regex and JSON parsing can perfectly extract dependencies
across diverse language ecosystems without dropping packages.
"""
slicer = UniversalManifestSlicer()

# 1. Test NPM (JSON Parsing)
pkg_json = tmp_path / "package.json"
pkg_json.write_text('{"dependencies": {"express": "^4.17.1"}, "devDependencies": {"jest": "27.0.0"}}', encoding='utf-8')
eco1, deps1 = slicer.slice_manifest(pkg_json)

assert eco1 == "npm"
assert deps1["express"] == "^4.17.1"
assert deps1["jest"] == "27.0.0"

# 2. Test PyPI (Text / Operator Regex)
req_txt = tmp_path / "requirements.txt"
req_txt.write_text("requests==2.26.0\n# comment ignored\nurllib3>=1.26.0", encoding='utf-8')
eco2, deps2 = slicer.slice_manifest(req_txt)

assert eco2 == "pypi"
assert deps2["requests"] == "2.26.0"
assert deps2["urllib3"] == "latest" # The slicer defaults to 'latest' for non '==' operators

# 3. Test Rust / Cargo (Toml Block Regex)
cargo_toml = tmp_path / "Cargo.toml"
cargo_toml.write_text("[package]\nname=\"test\"\n\n[dependencies]\ntokio = \"1.0\"\nserde = \"1.0\"", encoding='utf-8')
eco3, deps3 = slicer.slice_manifest(cargo_toml)

assert eco3 == "cargo"
assert deps3["tokio"] == "latest"
assert deps3["serde"] == "latest"

# ==============================================================================
# TEST 2: The Zero-Trust CycloneDX Matrix
# ==============================================================================
@patch("gitgalaxy.tools.compliance.sbom_generator.SecurityLens")
@patch("gitgalaxy.tools.compliance.sbom_generator.LanguageDetector")
def test_zero_trust_sbom_generation(mock_detector_class, mock_security_class, tmp_path):
"""
Proves that the physical audit matrix correctly translates missing packages
and spoofed files into strict CycloneDX JSON properties.
"""
# 1. Setup Mock Workspace
project_dir = tmp_path / "target_project"
project_dir.mkdir()

# Create a package.json with two dependencies
pkg_json = project_dir / "package.json"
pkg_json.write_text('{"dependencies": {"safe-lib": "1.0", "ghost-lib": "2.0"}}', encoding='utf-8')

# Simulate "safe-lib" existing physically on disk, but "ghost-lib" is missing
safe_lib_dir = project_dir / "node_modules" / "safe-lib"
safe_lib_dir.mkdir(parents=True)
(safe_lib_dir / "index.js").write_text("console.log('hello');", encoding='utf-8')

# 2. Configure the Mocks to simulate a Spoof Detection
mock_sec_instance = mock_security_class.return_value
mock_det_instance = mock_detector_class.return_value

# We will pretend the SecurityLens found high entropy malware in safe-lib!
mock_sec_instance.scan_content.return_value = {"counts": {"entropy": 5.2}}
mock_det_instance.inspect.return_value = {"anomaly_flags": ["Disguised Executable"]}

# 3. Execute the Generator
test_args = ["sbom_generator.py", str(project_dir), "--out", "test_bom.json"]
with patch.object(sys, 'argv', test_args):
main()

# 4. Verify the CycloneDX Output
bom_file = tmp_path / "target_project_test_bom.json"
assert bom_file.exists(), "SBOM generator failed to create the output JSON."

bom_data = json.loads(bom_file.read_text(encoding='utf-8'))

assert bom_data["bomFormat"] == "CycloneDX"
assert len(bom_data["components"]) == 2

# Extract components by name for easy assertion
components = {c["name"]: c for c in bom_data["components"]}

# Assert Ghost-Lib (Missing on Disk)
ghost_props = {p["name"]: p["value"] for p in components["ghost-lib"]["properties"]}
assert ghost_props["gitgalaxy:trust_status"] == "UNVERIFIED_MISSING_ON_DISK"

# Assert Safe-Lib (Malware Spoof Detected)
safe_props = {p["name"]: p["value"] for p in components["safe-lib"]["properties"]}
assert safe_props["gitgalaxy:trust_status"] == "SPOOF_DETECTED"
assert "High Entropy" in safe_props["gitgalaxy:anomaly_notes"]
93 changes: 93 additions & 0 deletions tests/test_supply_chain_firewall.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import pytest
import sys
from unittest.mock import patch

import gitgalaxy.tools.supply_chain_security.supply_chain_firewall as firewall_module

# ==============================================================================
# TEST 1: Zero-Trust Import Slicer (Regex & Bins)
# ==============================================================================
def test_zero_trust_import_slicer(tmp_path, monkeypatch):
monkeypatch.setattr(firewall_module, "APPROVED_IMPORTS", ["react", "express"])
monkeypatch.setattr(firewall_module, "BLACKLISTED_IMPORTS", ["event-stream-malware"])

repo_dir = tmp_path / "imports_repo"
repo_dir.mkdir()
(repo_dir / "app.js").write_text("import 'react'; require('event-stream-malware');", encoding="utf-8")
(repo_dir / "main.py").write_text("from 'django' import models", encoding="utf-8")

result = firewall_module.run_firewall_audit(repo_dir)
assert result["imports_blacklisted"] == 1, "Failed to identify blacklisted package!"
assert result["imports_unknown"] == 1, "Failed to identify unknown package!"

# ==============================================================================
# TEST 2: Strict Mode Enforcement
# ==============================================================================
@patch("gitgalaxy.tools.supply_chain_security.supply_chain_firewall.SecurityLens")
@patch("gitgalaxy.tools.supply_chain_security.supply_chain_firewall.ApertureFilter")
def test_strict_mode_enforcement(mock_aperture_class, mock_security_class, tmp_path, monkeypatch):
monkeypatch.setattr(firewall_module, "APPROVED_IMPORTS", ["react"])
monkeypatch.setattr(firewall_module, "BLACKLISTED_IMPORTS", [])
monkeypatch.setattr(firewall_module, "STRICT_IMPORT_MODE", True)

mock_aperture = mock_aperture_class.return_value
mock_aperture._check_solar_shield.return_value = True
mock_aperture.evaluate_path_integrity.return_value = (True, 100, "OK")

mock_security = mock_security_class.return_value
mock_security.scan_content.return_value = {"counts": {}, "snippets": {}}
mock_security.evaluate_risk.return_value = {}

repo_dir = tmp_path / "strict_repo"
repo_dir.mkdir()
(repo_dir / "server.js").write_text("import 'shadow-library';", encoding="utf-8")

test_args = ["supply_chain_firewall.py", str(repo_dir)]
with patch.object(sys, 'argv', test_args):
with pytest.raises(SystemExit) as exc:
firewall_module.main()
assert exc.value.code == 1, "STRICT_IMPORT_MODE failed!"

# ==============================================================================
# TEST 3: The Inert Data Shield (Minified File Bypass)
# ==============================================================================
@patch("gitgalaxy.tools.supply_chain_security.supply_chain_firewall.SecurityLens")
@patch("gitgalaxy.tools.supply_chain_security.supply_chain_firewall.ApertureFilter")
def test_inert_data_shield_minified_bypass(mock_aperture_class, mock_security_class, tmp_path, monkeypatch):
monkeypatch.setattr(firewall_module, "STRICT_IMPORT_MODE", False)
monkeypatch.setattr(firewall_module, "BLACKLISTED_IMPORTS", [])

mock_aperture = mock_aperture_class.return_value
mock_aperture._check_solar_shield.return_value = True
mock_aperture.evaluate_path_integrity.return_value = (True, 100, "OK")

mock_security = mock_security_class.return_value
mock_security.scan_content.return_value = {"counts": {"homoglyphs": 500, "danger": 50}, "snippets": {}}

def mock_eval_risk(counts, loc):
if counts.get("homoglyphs", 0) > 10: return {"Hidden Malware Risk": 0.99}
return {}

mock_security.evaluate_risk.side_effect = mock_eval_risk

repo_dir = tmp_path / "inert_repo"
repo_dir.mkdir()
test_args = ["supply_chain_firewall.py", str(repo_dir)]

normal_file = repo_dir / "logic.js"
normal_file.write_text("var a = 'fake malware';", encoding="utf-8")

with patch.object(sys, 'argv', test_args):
with pytest.raises(SystemExit) as exc:
firewall_module.main()
assert exc.value.code == 1

normal_file.unlink()
min_file = repo_dir / "logic.min.js"
min_file.write_text("var a = 'fake malware';", encoding="utf-8")

try:
with patch.object(sys, 'argv', test_args):
firewall_module.main()
except SystemExit:
pytest.fail("The Inert Data Shield failed!")
Loading
Loading