feat(store): trust gate before every module install
Some checks failed
Security / Security check (push) Failing after 2s

One-click installs showed no trust signal at all (top security
finding of the usertest panel). Every install path — store card,
detail sheet, and the flow list's quick fix — now routes through
one confirmation dialog showing what the hub actually knows
before download: origin store, version, license, maturity, and
required services/capabilities, plus the sandbox model and an
honest note that per-entry signature status is not in the store
index yet (verification happens hub-side at install). Widget
tests cover content and confirm/cancel semantics.

Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
This commit is contained in:
flemming-it 2026-07-17 23:44:14 +02:00
parent c253f5d23e
commit 039f11b6bc
10 changed files with 477 additions and 0 deletions

View file

@ -0,0 +1,84 @@
// Install trust gate every install path routes through this
// dialog, so it must show provenance (source, version, license,
// maturity) and the honest signature note, and only proceed on
// an explicit confirmation.
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:chain_studio/data/hub.dart';
import 'package:chain_studio/l10n/app_localizations.dart';
import 'package:chain_studio/widgets/chain_install_confirm.dart';
const _item = StoreItem(
name: 'text.extract',
taglineEn: '',
taglineDe: '',
descriptionEn: '',
descriptionDe: '',
category: 'text',
tags: [],
requiresCapabilities: [],
requiresServices: ['judge-ner'],
license: 'Apache-2.0',
repository: '',
bestVersion: '0.1.0',
status: 'alpha',
installed: false,
featured: false,
iconUrl: '',
screenshotUrls: [],
docsUrl: '',
kind: 'native',
provider: 'fai',
source: 'bundled',
);
Widget _host({required void Function(bool) onResult}) => MaterialApp(
localizationsDelegates: AppLocalizations.localizationsDelegates,
supportedLocales: AppLocalizations.supportedLocales,
locale: const Locale('de'),
home: Builder(
builder: (context) => Center(
child: ElevatedButton(
onPressed: () async {
onResult(await ChainInstallConfirmDialog.show(context, _item));
},
child: const Text('go'),
),
),
),
);
void main() {
testWidgets('shows provenance, maturity, and the signature note',
(tester) async {
await tester.pumpWidget(_host(onResult: (_) {}));
await tester.tap(find.text('go'));
await tester.pumpAndSettle();
expect(find.text('„text.extract" installieren?'), findsOneWidget);
expect(find.text('v0.1.0'), findsOneWidget);
expect(find.text('Mitgelieferter Store-Index'), findsOneWidget);
expect(find.text('Apache-2.0'), findsOneWidget);
expect(find.text('experimentell'), findsOneWidget);
expect(find.text('judge-ner'), findsOneWidget);
expect(find.textContaining('Signaturen prüft der Hub'), findsOneWidget);
});
testWidgets('cancel returns false, install returns true', (tester) async {
bool? result;
await tester.pumpWidget(_host(onResult: (r) => result = r));
await tester.tap(find.text('go'));
await tester.pumpAndSettle();
await tester.tap(find.text('Abbrechen'));
await tester.pumpAndSettle();
expect(result, isFalse);
await tester.tap(find.text('go'));
await tester.pumpAndSettle();
await tester.tap(find.text('Installieren'));
await tester.pumpAndSettle();
expect(result, isTrue);
});
}