diff --git a/.config/simple-scan/state b/.config/simple-scan/state new file mode 100644 index 0000000..0df6a24 --- /dev/null +++ b/.config/simple-scan/state @@ -0,0 +1,8 @@ +[window] +width=600 +height=480 +is-maximized=false +is-fullscreen=false + +[scanner] +scan-type=batch diff --git a/tests/test_check_secrets.py b/tests/test_check_secrets.py index 7eb12d3..bcb8737 100644 --- a/tests/test_check_secrets.py +++ b/tests/test_check_secrets.py @@ -189,6 +189,134 @@ def test_main_cli_invalid_target(self): main() self.assertEqual(cm.exception.code, 1) + def test_main_cli_clean_without_quiet(self): + with tempfile.TemporaryDirectory() as tmp_dir: + tmp_path = Path(tmp_dir) + (tmp_path / "clean.txt").write_text("Hello world\n") + + test_args = ["check_secrets.py", "-t", str(tmp_path)] + with patch("sys.argv", test_args): + captured = io.StringIO() + with patch("sys.stdout", captured): + main() + self.assertIn("[OK] Secret Scanner: No SSH private keys or secrets found", captured.getvalue()) + + def test_main_cli_clean_with_quiet(self): + with tempfile.TemporaryDirectory() as tmp_dir: + tmp_path = Path(tmp_dir) + (tmp_path / "clean.txt").write_text("Hello world\n") + + test_args = ["check_secrets.py", "-t", str(tmp_path), "-q"] + with patch("sys.argv", test_args): + captured = io.StringIO() + with patch("sys.stdout", captured): + main() + self.assertEqual(captured.getvalue(), "") + + def test_main_cli_with_secrets_and_json(self): + with tempfile.TemporaryDirectory() as tmp_dir: + tmp_path = Path(tmp_dir) + (tmp_path / "secret.env").write_text("OPENAI_API_KEY=sk-proj-1234567890abcdefghijklmnopqrstuvwxyz123456\n") + + test_args = ["check_secrets.py", "-t", str(tmp_path), "--json"] + with patch("sys.argv", test_args): + captured = io.StringIO() + with patch("sys.stdout", captured): + main() + data = json.loads(captured.getvalue()) + self.assertFalse(data["clean"]) + self.assertEqual(data["total_secrets_found"], 1) + self.assertIn("secret.env", data["results"]) + + def test_main_cli_with_secrets_plain_output(self): + with tempfile.TemporaryDirectory() as tmp_dir: + tmp_path = Path(tmp_dir) + (tmp_path / "secret.env").write_text("OPENAI_API_KEY=sk-proj-1234567890abcdefghijklmnopqrstuvwxyz123456\n") + + test_args = ["check_secrets.py", "-t", str(tmp_path)] + with patch("sys.argv", test_args): + captured = io.StringIO() + with patch("sys.stdout", captured): + main() + output = captured.getvalue() + self.assertIn("[WARNING] Secret Scanner detected 1 potential secret(s)", output) + self.assertIn("File: secret.env", output) + self.assertIn("OpenAI / Anthropic API Key", output) + + def test_ssh_key_file_read_error(self): + with tempfile.TemporaryDirectory() as tmp_dir: + key_file = Path(tmp_dir) / "id_rsa" + key_file.write_text("dummy") + + # Mock read_text to raise an exception + with patch.object(Path, "read_text", side_effect=PermissionError("Permission denied")): + findings = scan_file_for_secrets(key_file) + self.assertEqual(len(findings), 1) + self.assertEqual(findings[0]["type"], "SSH Private Key File") + self.assertIn("unread: Permission denied", findings[0]["detail"]) + + def test_ssh_key_empty_file(self): + with tempfile.TemporaryDirectory() as tmp_dir: + key_file = Path(tmp_dir) / "id_ecdsa" + key_file.write_text("") + findings = scan_file_for_secrets(key_file) + self.assertEqual(len(findings), 1) + self.assertEqual(findings[0]["snippet"], "") + + def test_file_read_errors_handled_gracefully(self): + with tempfile.TemporaryDirectory() as tmp_dir: + sample_file = Path(tmp_dir) / "test.txt" + sample_file.write_text("some content") + + # Mock open to fail + with patch("builtins.open", side_effect=PermissionError("Access denied")): + findings = scan_file_for_secrets(sample_file) + self.assertEqual(findings, []) + + def test_scan_path_with_ignored_subdirectories(self): + with tempfile.TemporaryDirectory() as tmp_dir: + root = Path(tmp_dir) + git_dir = root / ".git" + git_dir.mkdir() + (git_dir / "secret_in_git.txt").write_text("OPENAI_API_KEY=sk-proj-1234567890abcdefghijklmnopqrstuvwxyz123456\n") + + cache_dir = root / "Cache" + cache_dir.mkdir() + (cache_dir / "secret_in_cache.txt").write_text("OPENAI_API_KEY=sk-proj-1234567890abcdefghijklmnopqrstuvwxyz123456\n") + + normal_file = root / "script.sh" + normal_file.write_text("echo 'hello'\n") + + results = scan_path(root) + self.assertEqual(len(results), 0) + + def test_scan_path_single_file_in_ignores(self): + with tempfile.TemporaryDirectory() as tmp_dir: + readme = Path(tmp_dir) / "README.md" + readme.write_text("OPENAI_API_KEY=sk-proj-1234567890abcdefghijklmnopqrstuvwxyz123456\n") + + results = scan_path(readme) + self.assertEqual(len(results), 0) + + def test_additional_placeholder_cases(self): + self.assertTrue(is_placeholder("your_password_here")) + self.assertTrue(is_placeholder("YOUR_AWS_KEY")) + self.assertTrue(is_placeholder("changeme")) + self.assertTrue(is_placeholder("undefined")) + self.assertTrue(is_placeholder("null")) + self.assertTrue(is_placeholder("none")) + self.assertTrue(is_placeholder("foo")) + self.assertTrue(is_placeholder("bar")) + self.assertTrue(is_placeholder("123456")) + self.assertTrue(is_placeholder("000000000")) + + def test_line_comment_and_blank_lines_skipped(self): + with tempfile.TemporaryDirectory() as tmp_dir: + f = Path(tmp_dir) / "test.conf" + f.write_text("\n\n# AKIAIOSFODNN7EXAMPLE this is a comment\n \n") + findings = scan_file_for_secrets(f) + self.assertEqual(len(findings), 0) + if __name__ == "__main__": unittest.main() diff --git a/tests/test_jf_file_compare.py b/tests/test_jf_file_compare.py index bb84330..2d2e5ca 100644 --- a/tests/test_jf_file_compare.py +++ b/tests/test_jf_file_compare.py @@ -378,6 +378,149 @@ def test_sync_directories_invalid_compare_mode(self): compare_mode="unsupported_mode", ) + def test_normalize_extensions_and_patterns(self): + from jfFileCompare import _normalize_extensions, _normalize_patterns + self.assertEqual(_normalize_extensions(None), ()) + self.assertEqual(_normalize_extensions([]), ()) + self.assertEqual(_normalize_extensions(["mp4", ".MKV", "JPG"]), (".jpg", ".mkv", ".mp4")) + + self.assertEqual(_normalize_patterns(None), ()) + self.assertEqual(_normalize_patterns([]), ()) + self.assertEqual(_normalize_patterns([" antigravity ", "TEST", ""]), ("antigravity", "test")) + + def test_hash_candidates_parallel_empty_and_large_file(self): + from jfFileCompare import hash_candidates_parallel + with tempfile.TemporaryDirectory() as tmp_dir: + root = Path(tmp_dir) + self.assertEqual(hash_candidates_parallel(root, []), {}) + + # Create large file > 3MB to exercise head/middle/tail sample hashing + large_file = root / "large.bin" + large_file.write_bytes(b"A" * (4 * 1024 * 1024)) + + hashes_sample = hash_candidates_parallel(root, ["large.bin"], mode="sample_hash") + self.assertIn("large.bin", hashes_sample) + self.assertEqual(len(hashes_sample["large.bin"]), 64) + + hashes_full = hash_candidates_parallel(root, ["large.bin"], mode="full_hash") + self.assertIn("large.bin", hashes_full) + self.assertEqual(len(hashes_full["large.bin"]), 64) + + def test_probe_metadata_empty_and_nonexistent_parent(self): + with tempfile.TemporaryDirectory() as tmp_dir: + root = Path(tmp_dir) + self.assertEqual(probe_metadata(root, []), {}) + + # Parent exists but is not a directory (it's a file) + fake_parent = root / "fake_file" + fake_parent.write_text("not a dir") + meta = probe_metadata(root, ["fake_file/child.txt"]) + self.assertEqual(meta, {}) + + def test_print_cli_command_mirror_and_size_only(self): + captured_output = io.StringIO() + sys.stdout = captured_output + try: + print_cli_command( + Path("/tmp/src"), + Path("/tmp/dst"), + mirror=True, + compare_mode="size_only", + exclude_extensions=[".tmp", ".bak"], + exclude_patterns=["temp"], + ) + finally: + sys.stdout = sys.__stdout__ + + output = captured_output.getvalue() + self.assertIn("--delete", output) + self.assertIn(" --size-only", output) + self.assertIn("/PURGE", output) + self.assertIn('--exclude="*temp*"', output) + + def test_sync_directories_nested_directory_creation(self): + with tempfile.TemporaryDirectory() as src_dir, tempfile.TemporaryDirectory() as dst_dir: + src = Path(src_dir) + dst = Path(dst_dir) + + nested_file = src / "deep" / "nested" / "folder" / "video.mp4" + nested_file.parent.mkdir(parents=True) + nested_file.write_text("deep video") + + captured_output = io.StringIO() + sys.stdout = captured_output + try: + sync_directories( + src_dir=str(src), + dst_dir=str(dst), + direction="push", + compare_mode="mtime_size", + include_extensions=[".mp4"], + sync=True, + dry_run=False, + ) + finally: + sys.stdout = sys.__stdout__ + + output = captured_output.getvalue() + self.assertIn("[EXECUTING] CREATE: deep/nested/folder/video.mp4", output) + self.assertTrue((dst / "deep" / "nested" / "folder" / "video.mp4").exists()) + + def test_workers_directly_in_process(self): + from jfFileCompare import _sample_hash_worker, _full_hash_worker + with tempfile.TemporaryDirectory() as tmp_dir: + root = Path(tmp_dir) + small_file = root / "small.txt" + small_file.write_bytes(b"hello world") + + rel, h_small = _sample_hash_worker(str(small_file), str(root)) + self.assertEqual(rel, "small.txt") + self.assertEqual(len(h_small), 64) + + rel_full, h_full = _full_hash_worker(str(small_file), str(root)) + self.assertEqual(rel_full, "small.txt") + self.assertEqual(len(h_full), 64) + + # Test large file with > 3MB to exercise head, middle, tail branches + large_file = root / "large.bin" + large_file.write_bytes(b"X" * (4 * 1024 * 1024)) + rel_large, h_large = _sample_hash_worker(str(large_file), str(root)) + self.assertEqual(rel_large, "large.bin") + self.assertEqual(len(h_large), 64) + + def test_sync_directories_size_mismatch_and_orphan_deletion(self): + with tempfile.TemporaryDirectory() as src_dir, tempfile.TemporaryDirectory() as dst_dir: + src = Path(src_dir) + dst = Path(dst_dir) + + # Size mismatch + (src / "diff_size.txt").write_text("1234567890") + (dst / "diff_size.txt").write_text("123") + + # Orphan file + (dst / "orphan.txt").write_text("orphan") + + captured_output = io.StringIO() + sys.stdout = captured_output + try: + sync_directories( + src_dir=str(src), + dst_dir=str(dst), + direction="push", + compare_mode="mtime_size", + sync=True, + dry_run=False, + delete_orphan_dst=True, + ) + finally: + sys.stdout = sys.__stdout__ + + output = captured_output.getvalue() + self.assertIn("[EXECUTING] UPDATE: diff_size.txt", output) + self.assertIn("[EXECUTING] DELETE: orphan.txt", output) + self.assertFalse((dst / "orphan.txt").exists()) + self.assertEqual((dst / "diff_size.txt").read_text(), "1234567890") + if __name__ == "__main__": unittest.main()