From a51dd25ac41ccf78d4a0b29910886095d825618a Mon Sep 17 00:00:00 2001 From: ek0ms savi0r Date: Sun, 21 Jun 2026 17:44:33 +0000 Subject: [PATCH] Upload files to "tests" --- tests/test_native_modules.py | 55 ++++++++++++++++++++++++++++++++++ tests/test_surface.py | 18 ++++++++++++ tests/test_surface_graph.py | 45 ++++++++++++++++++++++++++++ tests/test_ui.py | 57 ++++++++++++++++++++++++++++++++++++ 4 files changed, 175 insertions(+) create mode 100644 tests/test_native_modules.py create mode 100644 tests/test_surface.py create mode 100644 tests/test_surface_graph.py create mode 100644 tests/test_ui.py diff --git a/tests/test_native_modules.py b/tests/test_native_modules.py new file mode 100644 index 0000000..ec058c9 --- /dev/null +++ b/tests/test_native_modules.py @@ -0,0 +1,55 @@ +import json +import tempfile +import unittest +from pathlib import Path +from unittest.mock import patch + +from redflare.core.models import Target +from redflare.modules.base import ModuleContext +from redflare.modules.browser import NativeBrowserRuntimeModule +from redflare.modules.http import HTTPResponse +from redflare.modules.noauth import NativeNoAuthModule +from redflare.modules.repository import normalize_repository, run_repository_intelligence + + +class NativeModuleTests(unittest.TestCase): + def test_repository_slug_normalization(self): + self.assertEqual(normalize_repository("owner/repository"), "owner/repository") + with self.assertRaises(ValueError): normalize_repository("repository") + + @patch("redflare.modules.browser.NativeBrowserRuntimeModule._capture", side_effect=RuntimeError("fixture")) + @patch("redflare.modules.browser.request") + def test_browser_runtime_has_native_fallback(self, get, _capture): + get.return_value = HTTPResponse("https://example.test", 200, {}, b"fixture") + with tempfile.TemporaryDirectory() as directory: + context = ModuleContext("run", Path(directory)) + result = NativeBrowserRuntimeModule().run(Target("https://example.test", "example.test", "https", 443), context) + self.assertEqual(result.status, "completed") + self.assertEqual(result.observations["engine"], "native-http-fallback") + + @patch("redflare.modules.noauth.time.sleep") + @patch("redflare.modules.noauth.request") + def test_noauth_module_emits_native_finding(self, get, _sleep): + get.return_value = HTTPResponse("https://example.test/admin", 200, {"content-type": "text/html"}, b"admin console") + with tempfile.TemporaryDirectory() as directory: + context = ModuleContext("run", Path(directory), rate=0) + result = NativeNoAuthModule().run(Target("https://example.test", "example.test", "https", 443), context) + self.assertGreater(len(result.findings), 0) + self.assertEqual(result.observations["engine"], "native-redflare") + + @patch("redflare.modules.repository._github") + @patch.dict("os.environ", {"GITHUB_TOKEN": "fixture-token"}) + def test_repository_intelligence_masks_findings(self, github): + github.side_effect = [ + {"default_branch": "main"}, + {"tree": [{"type": "blob", "path": "config.js", "sha": "abc", "size": 100}]}, + {"encoding": "base64", "content": "YXBpX2tleSA9ICJhYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5eiI="}, + ] + with tempfile.TemporaryDirectory() as directory: + result = run_repository_intelligence(["owner/repo"], Path(directory)) + data = json.loads(Path(result["output"]).read_text()) + self.assertEqual(result["engine"], "native-redflare") + self.assertNotIn("abcdefghijklmnopqrstuvwxyz", json.dumps(data)) + + +if __name__ == "__main__": unittest.main() diff --git a/tests/test_surface.py b/tests/test_surface.py new file mode 100644 index 0000000..3246a85 --- /dev/null +++ b/tests/test_surface.py @@ -0,0 +1,18 @@ +import unittest + +from redflare.modules.surface import SurfaceParser, decode_targets + + +class SurfaceTests(unittest.TestCase): + def test_extracts_forms_and_redirects(self): + parser = SurfaceParser("https://example.test/start") + parser.feed( + '' + '
' + ) + self.assertEqual(parser.forms[0]["action"], "https://example.test/login") + self.assertEqual(parser.meta_refresh, ["https://example.test/next"]) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/test_surface_graph.py b/tests/test_surface_graph.py new file mode 100644 index 0000000..d83417c --- /dev/null +++ b/tests/test_surface_graph.py @@ -0,0 +1,45 @@ +import unittest + +from redflare.core.surface_graph import AttackSurfaceGraph + + +class SurfaceGraphTests(unittest.TestCase): + def test_merges_sources_methods_and_query_parameters(self): + graph = AttackSurfaceGraph() + graph.add_endpoint( + "https://example.test", + "https://example.test/api/users?id=7", + method="GET", + source="html-link", + ) + graph.add_endpoint( + "https://example.test", + "https://example.test/api/users?id=9", + method="POST", + source="openapi", + parameters=[{"name": "name", "location": "body", "required": True, "data_type": "string"}], + ) + snapshot = graph.snapshot() + endpoints = snapshot["targets"]["https://example.test"]["endpoints"] + self.assertEqual(len(endpoints), 1) + self.assertEqual(endpoints[0]["methods"], ["GET", "POST"]) + self.assertEqual({item["name"] for item in endpoints[0]["parameters"]}, {"id", "name"}) + self.assertEqual( + graph.request_urls("https://example.test"), + ["https://example.test/api/users?id=7"], + ) + + def test_redacts_sensitive_query_values_in_snapshot(self): + graph = AttackSurfaceGraph() + graph.add_endpoint( + "https://example.test", + "https://example.test/callback?id=7&access_token=super-secret", + source="browser-traffic", + ) + serialized = str(graph.snapshot()) + self.assertNotIn("super-secret", serialized) + self.assertIn("access_token=%3Credacted%3E", serialized) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/test_ui.py b/tests/test_ui.py new file mode 100644 index 0000000..bdf64f4 --- /dev/null +++ b/tests/test_ui.py @@ -0,0 +1,57 @@ +from __future__ import annotations + +from io import StringIO +from pathlib import Path +import unittest + +from redflare.core.models import Finding, ModuleResult +from redflare.ui import LiveConsole + + +class FinalReportTests(unittest.TestCase): + def test_full_module_assessment_includes_observations_findings_and_artifacts(self): + stream = StringIO() + finding = Finding( + run_id="run_test", + target="https://example.test", + module="path_discovery", + category="discovered-path", + title="Accessible path discovered: /admin", + severity="info", + confidence=0.8, + description="The path returned HTTP 200.", + evidence={"status": 200, "url": "https://example.test/admin"}, + ) + result = ModuleResult( + module="path_discovery", + target="https://example.test", + findings=[finding], + observations={ + "paths_tested": 2, + "hits": [{"path": "/admin", "status": 200, "bytes": 42}], + }, + artifacts=["runs/run_test/artifacts/path.log"], + duration_seconds=1.25, + ) + summary = { + "run_id": "run_test", + "completed": 1, + "errors": 0, + "findings": 1, + "by_severity": {"info": 1}, + } + + LiveConsole(stream).final_report(summary, [result], Path("runs/run_test")) + output = stream.getvalue() + + self.assertIn("PATH DISCOVERY ASSESSMENT — https://example.test", output) + self.assertIn("Paths tested: 2", output) + self.assertIn("Path: /admin", output) + self.assertIn("Status: 200", output) + self.assertIn("Module findings:", output) + self.assertIn("runs/run_test/artifacts/path.log", output) + self.assertIn("CONSOLIDATED FINDINGS", output) + + +if __name__ == "__main__": + unittest.main()