fix(doctor): honest module count, temp-path audit warning, connection line
Some checks are pending
Security / Security check (push) Waiting to run
Some checks are pending
Security / Security check (push) Waiting to run
Three doctor-page findings from the usertest panel: - Module tile no longer counts the hub's built-in 'system' pseudo-module — a fresh hub shows 0 modules, matching the welcome checklist's definition of an install. Counting is a top-level function with unit tests. - When the audit DB lives in an OS-cleanable temp directory (/var/folders, /tmp, Windows Temp), the daemon-files panel says so instead of presenting the state as healthy. The classifier is a top-level function with unit tests. - The daemon card states who-talks-to-whom-how in one line: endpoint, transport security (TLS / unencrypted-local / unencrypted), and whether a bearer token is attached (length only) — the auditor's baseline the green dot cannot answer. Also syncs pubspec.yaml (0.70.0 -> 0.72.0) with kStudioVersion, which had drifted to 0.71.0 while pubspec stayed behind. Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
This commit is contained in:
parent
afa3fa7eae
commit
703d961cec
10 changed files with 303 additions and 3 deletions
|
|
@ -4,6 +4,7 @@ import 'package:flutter/services.dart';
|
|||
import '../data/error_presentation.dart';
|
||||
import '../data/chain_log.dart';
|
||||
import '../data/hub.dart';
|
||||
import '../data/hub_auth_token.dart';
|
||||
import '../data/system_actions.dart';
|
||||
import '../l10n/app_localizations.dart';
|
||||
import '../theme/theme.dart';
|
||||
|
|
@ -334,6 +335,24 @@ class _EventLogPanel extends StatelessWidget {
|
|||
}
|
||||
}
|
||||
|
||||
/// True when [path] points into an OS-managed temporary
|
||||
/// directory (macOS `/var/folders`, Unix `/tmp`, the Windows
|
||||
/// user/system temp folders) — locations the OS may clean up
|
||||
/// at any time. The doctor page warns when the audit DB lives
|
||||
/// in one: an evidence log the OS can silently delete is not
|
||||
/// evidence. Top-level so the unit test can exercise the
|
||||
/// classification directly.
|
||||
bool isVolatilePath(String path) {
|
||||
if (path.isEmpty) return false;
|
||||
final p = path.toLowerCase().replaceAll('\\', '/');
|
||||
return p.startsWith('/tmp/') ||
|
||||
p.startsWith('/private/tmp/') ||
|
||||
p.startsWith('/var/folders/') ||
|
||||
p.startsWith('/private/var/folders/') ||
|
||||
p.contains('/appdata/local/temp/') ||
|
||||
p.contains('/windows/temp/');
|
||||
}
|
||||
|
||||
/// Lists the daemon's filesystem paths with one "Open" button
|
||||
/// per row. Windows operators who never touch a shell still
|
||||
/// need a way to inspect the audit DB or edit the operator
|
||||
|
|
@ -374,10 +393,33 @@ class _DaemonPathsPanel extends StatelessWidget {
|
|||
);
|
||||
}
|
||||
|
||||
final theme = Theme.of(context);
|
||||
return ChainCard(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if (isVolatilePath(paths.dbPath)) ...[
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Icon(
|
||||
Icons.warning_amber_outlined,
|
||||
size: 16,
|
||||
color: ChainColors.warning,
|
||||
),
|
||||
const SizedBox(width: ChainSpace.sm),
|
||||
Expanded(
|
||||
child: Text(
|
||||
l.doctorDbVolatileWarning,
|
||||
style: theme.textTheme.bodySmall?.copyWith(
|
||||
color: theme.colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: ChainSpace.sm),
|
||||
],
|
||||
for (final e in entries)
|
||||
_PathRow(label: e.$1, path: e.$2, icon: e.$3, isDirectory: e.$4),
|
||||
],
|
||||
|
|
@ -512,10 +554,20 @@ class _DaemonActionsCardState extends State<_DaemonActionsCard> {
|
|||
bool _binaryMissing = false;
|
||||
ChannelStatusSnapshot? _channels;
|
||||
|
||||
/// Trimmed length of `~/.chain/hub-auth-token`, or null when
|
||||
/// Studio talks to the hub anonymously. Length only — the
|
||||
/// secret itself never reaches the UI.
|
||||
int? _tokenChars;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_refreshStatus();
|
||||
HubAuthToken.charCount()
|
||||
.then((n) {
|
||||
if (mounted) setState(() => _tokenChars = n);
|
||||
})
|
||||
.catchError((_) {});
|
||||
}
|
||||
|
||||
Future<void> _refreshStatus() async {
|
||||
|
|
@ -528,6 +580,21 @@ class _DaemonActionsCardState extends State<_DaemonActionsCard> {
|
|||
}
|
||||
}
|
||||
|
||||
String _connectionLine(AppLocalizations l) {
|
||||
final ep = HubService.instance.currentEndpoint;
|
||||
final isLocal =
|
||||
ep.host == '127.0.0.1' || ep.host == 'localhost' || ep.host == '::1';
|
||||
final transport = ep.secure
|
||||
? l.doctorConnTls
|
||||
: isLocal
|
||||
? l.doctorConnPlainLocal
|
||||
: l.doctorConnPlainRemote;
|
||||
final auth = _tokenChars == null
|
||||
? l.doctorConnAnonymous
|
||||
: l.doctorConnToken(_tokenChars!);
|
||||
return l.doctorConnLine(ep.toString(), transport, auth);
|
||||
}
|
||||
|
||||
Future<void> _run(
|
||||
String label,
|
||||
Future<({bool ok, String stdout, String stderr})> Function() action,
|
||||
|
|
@ -626,6 +693,17 @@ class _DaemonActionsCardState extends State<_DaemonActionsCard> {
|
|||
color: theme.colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 2),
|
||||
// Who-talks-to-whom-how in one line: endpoint,
|
||||
// transport security, and whether a bearer token is
|
||||
// attached — the auditor's baseline questions the
|
||||
// green "connected" dot alone cannot answer.
|
||||
SelectableText(
|
||||
_connectionLine(l),
|
||||
style: theme.textTheme.bodySmall?.copyWith(
|
||||
color: theme.colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: ChainSpace.md),
|
||||
Wrap(
|
||||
spacing: ChainSpace.sm,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue