from datetime import datetime, timedelta, timezone import hashlib import os from pathlib import Path import sys import tempfile import unittest from unittest import mock ROOT = Path(__file__).resolve().parents[1] APP_DIR = ROOT / 'app' sys.path.insert(0, str(APP_DIR)) import dashboard class DashboardDatabaseTests(unittest.TestCase): def test_postgres_connection_uses_bounded_timeouts(self): connection = mock.Mock() with mock.patch.object(dashboard, 'connect_postgres', return_value=connection) as connect: result = dashboard.connect_db(None, 'postgresql://truf:secret@127.0.0.1:5432/truf') self.assertIs(result, connection) self.assertEqual(connect.call_args.kwargs['connect_timeout_sec'], 3) self.assertEqual(connect.call_args.kwargs['statement_timeout_ms'], 10000) self.assertEqual(connect.call_args.kwargs['lock_timeout_ms'], 2000) connection.commit.assert_called_once() def test_failed_query_rolls_back_before_rendering_degraded_result(self): connection = mock.Mock(is_postgres=True) connection.execute.side_effect = RuntimeError('database unavailable') with mock.patch.object(dashboard.st, 'error'): result = dashboard.query_df(connection, 'SELECT 1') self.assertTrue(result.empty) connection.rollback.assert_called_once() def test_default_frames_and_queries_do_not_include_raw_secret(self): known_secret = 'known-super-secret-value' frame = dashboard.pd.DataFrame([{ 'service': 'example', 'raw_secret': known_secret, 'redacted_secret': 'know...alue', }]) with mock.patch.object(dashboard.st, 'dataframe') as render: dashboard.display_df(frame) rendered = render.call_args.args[0] self.assertNotIn('raw_secret', rendered.columns) self.assertNotIn(known_secret, rendered.to_string()) queries = [] def capture(_conn, sql, params=None): queries.append(sql) return dashboard.pd.DataFrame() with mock.patch.object(dashboard, 'query_df', side_effect=capture): result = dashboard.finding_metadata_search(mock.Mock(), known_secret) self.assertTrue(result.empty) self.assertEqual(queries, []) def test_hash_metadata_search_never_selects_raw_columns(self): queries = [] with mock.patch.object( dashboard, 'query_df', side_effect=lambda _conn, sql, params=None: queries.append(sql) or dashboard.pd.DataFrame(), ): dashboard.finding_metadata_search(mock.Mock(), 'a' * 64) self.assertTrue(queries) self.assertTrue(all('raw_secret' not in sql for sql in queries)) def test_no_available_dashboard_source_path_queries_or_reveals_raw_payloads(self): source = (APP_DIR / 'dashboard.py').read_text(encoding='utf-8') forbidden = ( 'f.raw_secret', 'fh.raw_secret', 'SELECT raw_secret', 'Reveal raw credentials', 'Show raw secrets', 'config_json FROM', 'Download shown log text', ) self.assertTrue(all(value not in source for value in forbidden)) launcher = (ROOT / 'start_runtime.ps1').read_text(encoding='utf-8') self.assertIn('--no-dashboard', launcher) self.assertNotIn('--background --dashboard', launcher) def test_query_guard_refuses_sensitive_payload_columns_before_database_access(self): connection = mock.Mock() with mock.patch.object(dashboard.st, 'error'): result = dashboard.query_df(connection, 'SELECT raw_secret FROM findings') self.assertTrue(result.empty) connection.execute.assert_not_called() def test_reporting_presets_produce_exact_utc_window(self): now = datetime(2026, 8, 2, 12, 30, tzinfo=timezone.utc) start, end = dashboard.reporting_window('24 hours', now=now) self.assertEqual(end, now) self.assertEqual(end - start, timedelta(hours=24)) self.assertEqual(start.tzinfo, timezone.utc) def test_invalid_custom_reporting_window_is_refused(self): now = datetime(2026, 8, 2, 12, 30, tzinfo=timezone.utc) with self.assertRaisesRegex(ValueError, 'later'): dashboard.reporting_window('Custom', custom_start=now, custom_end=now) def test_source_activity_applies_time_bounds_before_limit(self): captured = {} def capture(_conn, sql, params=None): captured['sql'] = sql captured['params'] = params return dashboard.pd.DataFrame() start = datetime(2026, 8, 1, tzinfo=timezone.utc) end = datetime(2026, 8, 2, tzinfo=timezone.utc) with mock.patch.object(dashboard, 'query_df', side_effect=capture): dashboard.source_activity_df(mock.Mock(), start, end) sql = captured['sql'].lower() self.assertLess(sql.index('where'), sql.index('limit')) self.assertIn('ended_at::timestamptz >=', sql) self.assertIn('sum(sc.queued_new_count)', sql) self.assertIn('sum(sc.queued_updated_count)', sql) self.assertEqual(captured['params'], [start.isoformat(timespec='seconds'), end.isoformat(timespec='seconds')]) def test_period_summary_counts_first_alive_and_first_usable_results(self): captured = {} def capture(_conn, sql, params=None): captured['sql'] = sql captured['params'] = params return dashboard.pd.DataFrame() start = datetime(2026, 8, 1, tzinfo=timezone.utc) end = datetime(2026, 8, 2, tzinfo=timezone.utc) with mock.patch.object(dashboard, 'query_df', side_effect=capture): dashboard.period_summary_df(mock.Mock(), start, end) sql = captured['sql'].lower() self.assertEqual(sql.count('partition by r.credential_id'), 2) self.assertIn("r.status_group = 'alive'", sql) self.assertIn('r.alive_rank = 1', sql) self.assertIn('r.usable_rank = 1', sql) self.assertIn('pd.queued_new', sql) self.assertIn('pd.queued_updated', sql) self.assertNotIn('c.created_at', sql) self.assertEqual(captured['params'], [start.isoformat(timespec='seconds'), end.isoformat(timespec='seconds')]) def test_new_alive_breakdown_uses_first_alive_result_time(self): captured = {} def capture(_conn, sql, params=None): captured['sql'] = sql captured['params'] = params return dashboard.pd.DataFrame() start = datetime(2026, 8, 1, tzinfo=timezone.utc) end = datetime(2026, 8, 2, tzinfo=timezone.utc) with mock.patch.object(dashboard, 'query_df', side_effect=capture): dashboard.new_alive_breakdown_df(mock.Mock(), start, end) sql = captured['sql'].lower() self.assertIn('partition by kr.credential_id', sql) self.assertIn("kr.status_group = 'alive'", sql) self.assertIn('ra.alive_rank = 1', sql) self.assertNotIn('keycheck_current_state', sql) self.assertNotIn('c.created_at', sql) self.assertEqual(captured['params'], [start.isoformat(timespec='seconds'), end.isoformat(timespec='seconds')]) def test_raw_lookup_is_replaced_by_sha256_identity(self): raw = 'sk-proj-dashboard-test-not-a-real-key' request = dashboard.normalize_lookup(raw) self.assertEqual(request, { 'kind': 'digest', 'digest': hashlib.sha256(raw.encode('utf-8')).hexdigest(), }) self.assertNotIn(raw, request.values()) def test_pasted_finding_json_extracts_and_hashes_raw_value(self): raw = 'ghp_dashboard_test_not_a_real_token' request = dashboard.normalize_lookup('{"Raw": "' + raw + '", "DetectorName": "GitHub"}') self.assertEqual(request['kind'], 'digest') self.assertEqual(request['digest'], hashlib.sha256(raw.encode('utf-8')).hexdigest()) self.assertNotIn(raw, request.values()) def test_digest_lookup_uses_exact_identity_without_raw_columns(self): digest = 'a' * 64 captured = {} def capture(_conn, sql, params=None): captured['sql'] = sql captured['params'] = params return dashboard.pd.DataFrame() with mock.patch.object(dashboard, 'query_df', side_effect=capture): dashboard.lookup_findings_df(mock.Mock(), {'kind': 'digest', 'digest': digest}) sql = captured['sql'].lower() self.assertIn('f.secret_hash = ?', sql) self.assertIn('f.finding_uid = ?', sql) self.assertNotIn('raw_secret', sql) self.assertTrue(all(value == digest or isinstance(value, int) for value in captured['params'])) def test_metadata_wildcards_are_escaped(self): self.assertEqual(dashboard.escaped_like('repo_100%!'), '%repo!_100!%!!%') class DashboardConfigTests(unittest.TestCase): def test_direct_main_without_authority_never_parses_args_or_opens_database(self): with mock.patch.object( dashboard, 'require_active_supervisor_child', side_effect=dashboard.LifecycleAuthorityError('direct dashboard refused'), ), mock.patch.object(dashboard, 'parse_args') as parse_args, \ mock.patch.object(dashboard, 'connect_db') as connect_db, \ mock.patch.dict(os.environ, {}, clear=True): with self.assertRaisesRegex(SystemExit, 'direct dashboard refused'): dashboard.main() parse_args.assert_not_called() connect_db.assert_not_called() def test_authenticated_dashboard_still_rejects_non_loopback_launch_marker_before_args(self): with mock.patch.object(dashboard, 'require_active_supervisor_child', return_value={}), \ mock.patch.object(dashboard, 'parse_args') as parse_args, \ mock.patch.dict(os.environ, { 'TRUF_DASHBOARD_CANONICAL_LAUNCH': '1', 'TRUF_DASHBOARD_HOST': '0.0.0.0', }, clear=True): with self.assertRaisesRegex(SystemExit, 'loopback'): dashboard.main() parse_args.assert_not_called() def test_managed_config_parse_failure_is_fatal(self): with tempfile.TemporaryDirectory() as temp_dir: config = os.path.join(temp_dir, 'config.yaml') Path(config).write_text('[invalid', encoding='ascii') with mock.patch.object(sys, 'argv', ['dashboard.py', '--config', config]), \ mock.patch.dict(os.environ, {'TRUF_SUPERVISOR_CHILD_KIND': 'dashboard'}, clear=False): with self.assertRaisesRegex(SystemExit, 'failed closed'): dashboard.parse_args() def test_legacy_mutation_ui_contains_no_scanner_controls(self): source = (APP_DIR / 'app.py').read_text(encoding='utf-8') self.assertNotIn('from scanner import', source) self.assertNotIn('start_scan(', source) self.assertIn('controls are retired', source) def test_main_routes_only_to_single_page(self): source = (APP_DIR / 'dashboard.py').read_text(encoding='utf-8') main_source = source[source.index('def main():'):] self.assertIn('page_simple_dashboard(', main_source) self.assertNotIn('st.sidebar', main_source) self.assertNotIn('current_queue_counts(', main_source) def test_streamlit_usage_telemetry_is_disabled(self): config = (APP_DIR / '.streamlit' / 'config.toml').read_text(encoding='utf-8') self.assertIn('[browser]', config) self.assertIn('gatherUsageStats = false', config) def test_log_tail_never_materializes_more_than_bounded_bytes(self): with tempfile.TemporaryDirectory() as temp_dir: path = os.path.join(temp_dir, 'large.log') Path(path).write_bytes(b'x' * (dashboard.MAX_LOG_TAIL_BYTES * 3)) lines = dashboard.read_tail(path, 5000) self.assertLessEqual( sum(len(line.encode('utf-8')) for line in lines), dashboard.MAX_LOG_TAIL_BYTES, ) class SupervisorStatusParsingTests(unittest.TestCase): def test_current_status_columns_are_mapped_by_header(self): with tempfile.TemporaryDirectory() as temp_dir: path = os.path.join(temp_dir, 'supervisor.status.txt') Path(path).write_text( 'PostgreSQL: READY failures=0 detail=ready\n\n' 'source | status | desired | pid | mode | up | exit | next | rs | auth | last_log\n' '-------+--------+---------+-----+------+----+------+------|----|------|---------\n' 'github | running | running | 42 | loop | 5s | - | - | 1/2 | token-1 | healthy\n', encoding='utf-8', ) rows, found_path = dashboard.parse_supervisor_status(temp_dir) self.assertEqual(found_path, path) self.assertEqual(len(rows), 1) row = rows.iloc[0] self.assertEqual(row['runtime_status'], 'running') self.assertEqual(row['desired'], 'running') self.assertEqual(row['pid'], '42') self.assertEqual(row['restarts'], '1/2') self.assertEqual(row['auth'], 'token-1') self.assertNotIn('last_log', row.index) def test_blocked_paused_backoff_and_done_statuses_remain_distinct(self): with tempfile.TemporaryDirectory() as temp_dir: path = os.path.join(temp_dir, 'supervisor.status.txt') rows = '\n'.join( f'{name} | {status} | running | - | loop | - | - | - | 0/0 | - | detail' for name, status in ( ('one', 'blocked'), ('two', 'paused'), ('three', 'backoff'), ('four', 'done'), ) ) Path(path).write_text( 'source | status | desired | pid | mode | up | exit | next | rs | auth | last_log\n' + rows + '\n', encoding='utf-8', ) with mock.patch.object(dashboard, 'load_per_source_states', return_value=dashboard.pd.DataFrame()), \ mock.patch.object(dashboard, 'latest_cycle_df', return_value=dashboard.pd.DataFrame()): health, _ = dashboard.runtime_source_health(mock.Mock(), temp_dir) mapped = dict(zip(health['source'], health['health'])) self.assertEqual(mapped['one'], 'blocked') self.assertEqual(mapped['two'], 'paused') self.assertEqual(mapped['three'], 'backoff') self.assertEqual(mapped['four'], 'done') if __name__ == '__main__': unittest.main()