@@ -3,6 +3,7 @@ import 'dart:convert';
33import 'dart:io' ;
44import 'package:archive/archive.dart' ;
55import 'package:crypto/crypto.dart' ;
6+ import 'package:flutter/foundation.dart' ;
67import 'package:http/http.dart' as http;
78import 'package:path/path.dart' as p;
89import 'package:querya_desktop/core/extensions/extension_support.dart' ;
@@ -12,6 +13,7 @@ import 'package:querya_desktop/core/extensions/local_extension_registry.dart';
1213import 'package:querya_desktop/core/extensions/models/extension_manifest.dart' ;
1314import 'package:querya_desktop/core/extensions/models/extension_type.dart' ;
1415import 'package:querya_desktop/core/security/archive_path_guard.dart' ;
16+ import 'marketplace_download_policy.dart' ;
1517import 'marketplace_repository.dart' ;
1618
1719/// HTTP implementation of [MarketplaceRepository] connecting to MarketApi backend.
@@ -22,13 +24,46 @@ class HttpMarketplaceRepository implements MarketplaceRepository {
2224 HttpMarketplaceRepository ({
2325 this .baseUrl = 'http://localhost:8000/api/v1' ,
2426 http.Client ? client,
25- }) : _client = client ?? http.Client ();
27+ Iterable <String > extraTrustedDownloadHosts = const [],
28+ bool allowLocalhostInDebug = kDebugMode,
29+ }) : _client = client ?? http.Client (),
30+ _allowLocalhostInDebug = allowLocalhostInDebug,
31+ _trustedDownloadHosts = MarketplaceDownloadPolicy .trustedHostsFor (
32+ apiBaseUrl: baseUrl,
33+ extraTrustedHosts: extraTrustedDownloadHosts,
34+ ) {
35+ _validateApiBaseUrl ();
36+ }
2637
2738 final String baseUrl;
2839 final http.Client _client;
40+ final bool _allowLocalhostInDebug;
41+ final Set <String > _trustedDownloadHosts;
42+
43+ void _validateApiBaseUrl () {
44+ if (! MarketplaceDownloadPolicy .isAllowedApiBaseUrl (
45+ baseUrl,
46+ allowLocalhostInDebug: _allowLocalhostInDebug,
47+ )) {
48+ throw MarketplaceException (
49+ 'Marketplace API base URL is not allowed: $baseUrl ' ,
50+ );
51+ }
52+ }
53+
54+ void _validateDownloadUrl (Uri uri) {
55+ if (! MarketplaceDownloadPolicy .isAllowedDownloadUrl (
56+ uri,
57+ trustedHosts: _trustedDownloadHosts,
58+ allowLocalhostInDebug: _allowLocalhostInDebug,
59+ )) {
60+ throw MarketplaceException ('Download URL is not allowed: $uri ' );
61+ }
62+ }
2963
3064 @override
3165 Future <List <ExtensionManifest >> getTrending ({ExtensionType ? type}) async {
66+ _validateApiBaseUrl ();
3267 final uri = Uri .parse ('$baseUrl /extensions/trending' ).replace (
3368 queryParameters: type != null ? {'type' : type.value} : null ,
3469 );
@@ -42,6 +77,7 @@ class HttpMarketplaceRepository implements MarketplaceRepository {
4277
4378 @override
4479 Future <List <ExtensionManifest >> search (String query, {ExtensionType ? type}) async {
80+ _validateApiBaseUrl ();
4581 final uri = Uri .parse ('$baseUrl /extensions/search' ).replace (
4682 queryParameters: {
4783 'q' : query.trim (),
@@ -62,6 +98,7 @@ class HttpMarketplaceRepository implements MarketplaceRepository {
6298 if (uri == null ) {
6399 throw MarketplaceException ('Invalid download URL: $url ' );
64100 }
101+ _validateDownloadUrl (uri);
65102
66103 final request = http.Request ('GET' , uri);
67104 final response = await _client.send (request).timeout (const Duration (seconds: 30 ));
0 commit comments