Skip to content

Commit c7e3f3c

Browse files
author
r0BIT
committed
refactor: remove dead code, deprecated functions, and consolidate duplicates
1 parent c8b946a commit c7e3f3c

File tree

8 files changed

+33
-266
lines changed

8 files changed

+33
-266
lines changed

taskhound/cli.py

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
)
2222
from .opengraph import generate_opengraph_files
2323
from .output.bloodhound import upload_opengraph_to_bloodhound
24-
from .output.printer import print_results
2524
from .output.summary import print_decrypted_credentials, print_summary_table
2625
from .output.writer import write_csv, write_json, write_rich_plain
2726
from .parsers.highvalue import HighValueLoader
@@ -175,7 +174,6 @@ def main():
175174
dpapi_key=args.dpapi_key,
176175
concise=not args.verbose,
177176
)
178-
print_results(lines)
179177
else:
180178
# Online mode: process targets via SMB
181179
# Build targets list
@@ -246,13 +244,6 @@ def main():
246244
# Aggregate results
247245
all_rows, laps_failures, laps_successes = aggregate_results(results)
248246

249-
# Print results (already captured in results)
250-
for result in results:
251-
if result.lines:
252-
print_results(result.lines)
253-
254-
# Summary already printed by async engine's Rich progress bar
255-
256247
else:
257248
# Sequential mode (default, --threads 1)
258249
for tgt in targets:
@@ -267,7 +258,6 @@ def main():
267258
laps_successes += 1
268259
elif isinstance(laps_result, LAPSFailure):
269260
laps_failures.append(laps_result)
270-
print_results(lines)
271261

272262
# Exports
273263
# Auto-generate JSON if OpenGraph is enabled and no explicit JSON output was specified

taskhound/output/__init__.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,17 @@
11
"""Output module for TaskHound results."""
2+
3+
# Shared color scheme for task output
4+
COLORS = {
5+
"tier0_header": "bold red",
6+
"tier0_border": "red",
7+
"priv_header": "bold yellow",
8+
"priv_border": "yellow",
9+
"task_header": "bold green",
10+
"task_border": "green",
11+
"label": "dim",
12+
"value": "white",
13+
"password": "bold green",
14+
"warning": "yellow",
15+
"error": "red",
16+
"success": "green",
17+
}

taskhound/output/printer.py

Lines changed: 1 addition & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,14 @@
1-
import re
21
from typing import Any, Dict, List, Optional
32

43
from rich.table import Table
54

5+
from . import COLORS
66
from ..parsers.highvalue import HighValueLoader
77
from ..utils import logging as log_utils
88
from ..utils.console import console
99
from ..utils.date_parser import parse_iso_date
1010
from ..utils.sid_resolver import format_runas_with_sid_resolution
1111

12-
# Color scheme for task output
13-
COLORS = {
14-
"tier0_header": "bold red",
15-
"tier0_border": "red",
16-
"priv_header": "bold yellow",
17-
"priv_border": "yellow",
18-
"task_header": "bold green",
19-
"task_border": "green",
20-
"label": "dim",
21-
"value": "white",
22-
"password": "bold green",
23-
"warning": "yellow",
24-
"error": "red",
25-
"success": "green",
26-
}
27-
28-
29-
def print_results(lines: List[str]):
30-
"""
31-
Legacy print function - no longer prints to console.
32-
33-
Tables are now printed directly by format_block() via print_task_table().
34-
This function exists for backward compatibility but does nothing since
35-
the Rich tables are already printed when format_block is called.
36-
37-
The text lines are still used for file output (--plain flag).
38-
"""
39-
# Tables are already printed by print_task_table() in format_block()
40-
# This function is kept for API compatibility but no longer prints
41-
pass
42-
4312

4413
def print_task_table(
4514
kind: str,

taskhound/output/writer.py

Lines changed: 1 addition & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -8,21 +8,9 @@
88
from rich.console import Console
99
from rich.table import Table
1010

11+
from . import COLORS
1112
from ..utils.logging import good
1213

13-
# Colors for task types (matching printer.py)
14-
COLORS = {
15-
"tier0_header": "bold red",
16-
"tier0_border": "red",
17-
"priv_header": "bold yellow",
18-
"priv_border": "yellow",
19-
"task_header": "bold green",
20-
"task_border": "green",
21-
"label": "cyan",
22-
"value": "white",
23-
"password": "bold green",
24-
}
25-
2614

2715
def _rows_to_dicts(rows: List[Any]) -> List[Dict]:
2816
"""Convert TaskRow objects to dicts for serialization."""
@@ -123,21 +111,6 @@ def _format_task_table(row_dict: Dict[str, Any]) -> Table:
123111
return table
124112

125113

126-
def write_plain(outdir: str, host: str, lines: List[str]):
127-
"""
128-
Legacy plain text writer - writes raw text lines.
129-
130-
.. deprecated::
131-
Use write_rich_plain() instead for Rich-formatted output with tables.
132-
"""
133-
os.makedirs(outdir, exist_ok=True)
134-
safe = host.replace(":", "_")
135-
path = os.path.join(outdir, f"{safe}.txt")
136-
with open(path, "w", encoding="utf-8") as f:
137-
f.write("\n".join(lines) + ("\n" if lines else ""))
138-
good(f"Wrote results to {path}")
139-
140-
141114
def write_rich_plain(outdir: str, all_rows: List[Any], force_color: bool = True):
142115
"""
143116
Write Rich-formatted output files grouped by host.

taskhound/utils/console.py

Lines changed: 0 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -198,13 +198,6 @@ def update(item: str, success: bool = True, error_msg: Optional[str] = None):
198198
)
199199

200200

201-
def update_progress_status(status_text: str):
202-
"""Update the progress bar status text (for external use)."""
203-
global _progress, _progress_task_id
204-
if _progress and _progress_task_id is not None:
205-
_progress.update(_progress_task_id, status=status_text)
206-
207-
208201
@contextmanager
209202
def spinner(description: str = "Processing"):
210203
"""
@@ -238,36 +231,6 @@ def spinner(description: str = "Processing"):
238231
pass # Spinner removed automatically (transient=True)
239232

240233

241-
# =============================================================================
242-
# Collecting Status (for per-target output during scanning)
243-
# =============================================================================
244-
245-
def collecting_start(target: str):
246-
"""Show that we're starting to collect from a target."""
247-
console.print(f"[dim][Collecting][/] {target} [dim]...[/]")
248-
249-
250-
def collecting_done(target: str, task_count: int, priv_count: int):
251-
"""Show successful collection from a target."""
252-
console.print(
253-
f"[dim][Collecting][/] {target} [green][+][/] "
254-
f"[dim]({task_count} tasks, {priv_count} privileged)[/]"
255-
)
256-
257-
258-
def collecting_skip(target: str, reason: str):
259-
"""Show that a target was skipped."""
260-
console.print(f"[dim][Collecting][/] {target} [yellow][SKIP][/] [dim]({reason})[/]")
261-
262-
263-
def collecting_fail(target: str, error_msg: str):
264-
"""Show that collection from a target failed."""
265-
# Truncate long error messages
266-
if len(error_msg) > 60:
267-
error_msg = error_msg[:57] + "..."
268-
console.print(f"[dim][Collecting][/] {target} [red][-][/] [dim]({error_msg})[/]")
269-
270-
271234
# =============================================================================
272235
# Summary Table
273236
# =============================================================================

tests/test_console_utils.py

Lines changed: 0 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -295,57 +295,3 @@ def test_tier0_takes_precedence_over_priv(self):
295295
assert "[PRIV]" not in result
296296

297297

298-
# ============================================================================
299-
# Test: collecting_* functions
300-
# ============================================================================
301-
302-
class TestCollectingFunctions:
303-
"""Tests for collecting status functions."""
304-
305-
@patch.object(console, 'console')
306-
def test_collecting_done(self, mock_console):
307-
"""Should print success message with counts."""
308-
from taskhound.utils.console import collecting_done
309-
310-
collecting_done("DC01", task_count=10, priv_count=3)
311-
mock_console.print.assert_called_once()
312-
call_arg = mock_console.print.call_args[0][0]
313-
assert "DC01" in call_arg
314-
assert "10 tasks" in call_arg
315-
assert "3 privileged" in call_arg
316-
317-
@patch.object(console, 'console')
318-
def test_collecting_skip(self, mock_console):
319-
"""Should print skip message with reason."""
320-
from taskhound.utils.console import collecting_skip
321-
322-
collecting_skip("DC02", "Access denied")
323-
mock_console.print.assert_called_once()
324-
call_arg = mock_console.print.call_args[0][0]
325-
assert "DC02" in call_arg
326-
assert "SKIP" in call_arg
327-
assert "Access denied" in call_arg
328-
329-
@patch.object(console, 'console')
330-
def test_collecting_fail(self, mock_console):
331-
"""Should print failure message."""
332-
from taskhound.utils.console import collecting_fail
333-
334-
collecting_fail("DC03", "Connection timeout")
335-
mock_console.print.assert_called_once()
336-
call_arg = mock_console.print.call_args[0][0]
337-
assert "DC03" in call_arg
338-
assert "Connection timeout" in call_arg
339-
340-
@patch.object(console, 'console')
341-
def test_collecting_fail_truncates_long_error(self, mock_console):
342-
"""Should truncate error messages over 60 characters."""
343-
from taskhound.utils.console import collecting_fail
344-
345-
long_error = "This is a very long error message that exceeds the sixty character limit significantly"
346-
collecting_fail("DC04", long_error)
347-
mock_console.print.assert_called_once()
348-
call_arg = mock_console.print.call_args[0][0]
349-
assert "..." in call_arg
350-
assert long_error not in call_arg
351-

tests/test_output_writer.py

Lines changed: 2 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@
33
44
Tests cover:
55
- _rows_to_dicts helper function
6-
- write_plain function
76
- write_json function
87
- write_csv function
8+
- write_rich_plain function
99
"""
1010

1111
import csv
@@ -14,7 +14,7 @@
1414
import pytest
1515
from unittest.mock import MagicMock, patch
1616

17-
from taskhound.output.writer import _rows_to_dicts, write_plain, write_json, write_csv
17+
from taskhound.output.writer import _rows_to_dicts, write_json, write_csv
1818

1919

2020
# ============================================================================
@@ -91,65 +91,6 @@ def test_mixed_objects_and_dicts(self, sample_task_dict, sample_task_object):
9191
assert result[1] == {"host": "WS01.example.com", "path": "\\MaintTask", "type": "TASK"}
9292

9393

94-
# ============================================================================
95-
# Unit Tests: write_plain
96-
# ============================================================================
97-
98-
99-
class TestWritePlain:
100-
"""Tests for write_plain function"""
101-
102-
@patch('taskhound.output.writer.good')
103-
def test_creates_output_directory(self, mock_good, temp_output_dir):
104-
"""Should create output directory if it doesn't exist"""
105-
subdir = temp_output_dir / "subdir"
106-
lines = ["Line 1", "Line 2"]
107-
108-
write_plain(str(subdir), "host1", lines)
109-
110-
assert subdir.exists()
111-
112-
@patch('taskhound.output.writer.good')
113-
def test_writes_lines_to_file(self, mock_good, temp_output_dir):
114-
"""Should write lines to file with newlines"""
115-
lines = ["Line 1", "Line 2", "Line 3"]
116-
117-
write_plain(str(temp_output_dir), "host1.example.com", lines)
118-
119-
output_file = temp_output_dir / "host1.example.com.txt"
120-
assert output_file.exists()
121-
122-
content = output_file.read_text()
123-
assert content == "Line 1\nLine 2\nLine 3\n"
124-
125-
@patch('taskhound.output.writer.good')
126-
def test_empty_lines_creates_file(self, mock_good, temp_output_dir):
127-
"""Should create file even with empty lines"""
128-
write_plain(str(temp_output_dir), "host1", [])
129-
130-
output_file = temp_output_dir / "host1.txt"
131-
assert output_file.exists()
132-
content = output_file.read_text()
133-
assert content == ""
134-
135-
@patch('taskhound.output.writer.good')
136-
def test_colon_replaced_in_filename(self, mock_good, temp_output_dir):
137-
"""Should replace colons with underscores in filename"""
138-
write_plain(str(temp_output_dir), "host:port", ["test"])
139-
140-
output_file = temp_output_dir / "host_port.txt"
141-
assert output_file.exists()
142-
143-
@patch('taskhound.output.writer.good')
144-
def test_logs_success_message(self, mock_good, temp_output_dir):
145-
"""Should log success message"""
146-
write_plain(str(temp_output_dir), "host1", ["test"])
147-
148-
mock_good.assert_called_once()
149-
call_arg = mock_good.call_args[0][0]
150-
assert "Wrote results to" in call_arg
151-
152-
15394
# ============================================================================
15495
# Unit Tests: write_json
15596
# ============================================================================

0 commit comments

Comments
 (0)