fix: harden downloader against path traversal, URL escape, oversized bodies
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -70,12 +70,25 @@ class ContentDownloader {
|
||||
}
|
||||
|
||||
Future<bool> _downloadPack(ManifestSeason season) async {
|
||||
// seasonId becomes a filesystem path segment; only accept safe slugs so a
|
||||
// hostile manifest can never write outside the cache dir.
|
||||
if (!RegExp(r'^[a-zA-Z0-9_-]+$').hasMatch(season.seasonId)) return false;
|
||||
|
||||
if (season.packUrl.contains('..') ||
|
||||
season.packUrl.startsWith('/') ||
|
||||
season.packUrl.contains('://')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
final res = await _client
|
||||
.get(Uri.parse('$baseUrl/${season.packUrl}'))
|
||||
.timeout(const Duration(seconds: 30));
|
||||
if (res.statusCode != 200) return false;
|
||||
|
||||
// Packs are tens of KB; anything huge is a server error or an attack.
|
||||
if (res.bodyBytes.length > 5 * 1024 * 1024) return false;
|
||||
|
||||
// Verify in memory before any file is touched, so a bad pack can
|
||||
// never leave artifacts on disk.
|
||||
final digest = sha256.convert(res.bodyBytes).toString();
|
||||
|
||||
Reference in New Issue
Block a user