Skip to content

Commit 0588cb7

Browse files
Security: Only check development paths in debug builds
Restrict relative path traversal (../../../) to debug builds only: - Flutter: Use kDebugMode check before searching dev paths - Rust: Use #[cfg(debug_assertions)] for dev path search This prevents production builds from checking relative paths that could potentially be exploited to load malicious dependencies. Co-Authored-By: Claude Opus 4.5 <[email protected]>
1 parent c06c411 commit 0588cb7

File tree

3 files changed

+70
-60
lines changed

3 files changed

+70
-60
lines changed

app/lib/services/dependency_manager.dart

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import 'dart:io';
44

55
import 'package:archive/archive.dart';
66
import 'package:crypto/crypto.dart';
7+
import 'package:flutter/foundation.dart';
78
import 'package:flutter/services.dart';
89
import 'package:http/http.dart' as http;
910
import 'package:path/path.dart' as path;
@@ -187,7 +188,20 @@ class DependencyManager {
187188
// Fall back to production path (will trigger download)
188189
return prodDeps;
189190
} else if (Platform.isMacOS) {
190-
// First check Application Support (where downloaded deps go)
191+
// Development only: check relative paths to project root
192+
if (kDebugMode) {
193+
// From app/build/macos/Build/Products/Debug/vapourbox.app/Contents/MacOS up 9 levels
194+
final devDepsArm = Directory(path.join(appDir, '..', '..', '..', '..', '..', '..', '..', '..', '..', 'deps', 'macos-arm64'));
195+
if (await devDepsArm.exists()) {
196+
return Directory(await devDepsArm.resolveSymbolicLinks());
197+
}
198+
final devDepsX64 = Directory(path.join(appDir, '..', '..', '..', '..', '..', '..', '..', '..', '..', 'deps', 'macos-x64'));
199+
if (await devDepsX64.exists()) {
200+
return Directory(await devDepsX64.resolveSymbolicLinks());
201+
}
202+
}
203+
204+
// Production: check Application Support (where downloaded deps go)
191205
final home = Platform.environment['HOME'];
192206
if (home != null) {
193207
final appSupportDeps = Directory(path.join(
@@ -198,17 +212,6 @@ class DependencyManager {
198212
}
199213
}
200214

201-
// Development: go up to project root and find deps/macos-arm64 or macos-x64
202-
// From app/build/macos/Build/Products/Debug/vapourbox.app/Contents/MacOS up 9 levels
203-
final devDepsArm = Directory(path.join(appDir, '..', '..', '..', '..', '..', '..', '..', '..', '..', 'deps', 'macos-arm64'));
204-
if (await devDepsArm.exists()) {
205-
return Directory(await devDepsArm.resolveSymbolicLinks());
206-
}
207-
final devDepsX64 = Directory(path.join(appDir, '..', '..', '..', '..', '..', '..', '..', '..', '..', 'deps', 'macos-x64'));
208-
if (await devDepsX64.exists()) {
209-
return Directory(await devDepsX64.resolveSymbolicLinks());
210-
}
211-
212215
// Fall back to Application Support (will trigger download)
213216
final home2 = Platform.environment['HOME'] ?? '/tmp';
214217
return Directory(path.join(

app/lib/services/preview_generator.dart

Lines changed: 23 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import 'dart:convert';
33
import 'dart:io';
44
import 'dart:typed_data';
55

6+
import 'package:flutter/foundation.dart';
67
import 'package:path/path.dart' as path;
78
import 'package:uuid/uuid.dart';
89

@@ -483,23 +484,28 @@ class PreviewGenerator {
483484
final platformDir = _getPlatformSuffix();
484485

485486
// Check bundled locations (platform-specific subdirectory)
486-
final bundledPaths = Platform.isWindows
487-
? [
488-
'$exeDir\\deps\\$platformDir\\ffmpeg\\$name$ext',
489-
'$exeDir\\deps\\$platformDir\\vapoursynth\\$name$ext',
490-
// VSPipe is capitalized on Windows
491-
'$exeDir\\deps\\$platformDir\\vapoursynth\\VSPipe$ext',
492-
]
493-
: [
494-
// Production: downloaded deps in Contents/MacOS/deps/
495-
'$exeDir/deps/$platformDir/ffmpeg/$name',
496-
'$exeDir/deps/$platformDir/vapoursynth/$name',
497-
// Bundled in Helpers (legacy)
498-
'$exeDir/../Helpers/$name',
499-
// Development: go up from app/build/macos/Build/Products/Debug/vapourbox.app/Contents/MacOS to project root (9 levels)
500-
'$exeDir/../../../../../../../../../deps/$platformDir/ffmpeg/$name',
501-
'$exeDir/../../../../../../../../../deps/$platformDir/vapoursynth/$name',
502-
];
487+
final home = Platform.environment['HOME'] ?? '';
488+
final List<String> bundledPaths;
489+
490+
if (Platform.isWindows) {
491+
bundledPaths = [
492+
'$exeDir\\deps\\$platformDir\\ffmpeg\\$name$ext',
493+
'$exeDir\\deps\\$platformDir\\vapoursynth\\$name$ext',
494+
// VSPipe is capitalized on Windows
495+
'$exeDir\\deps\\$platformDir\\vapoursynth\\VSPipe$ext',
496+
];
497+
} else {
498+
bundledPaths = [
499+
// Development only: relative paths to project root
500+
if (kDebugMode) ...[
501+
'$exeDir/../../../../../../../../../deps/$platformDir/ffmpeg/$name',
502+
'$exeDir/../../../../../../../../../deps/$platformDir/vapoursynth/$name',
503+
],
504+
// Application Support (where downloaded deps go on macOS)
505+
'$home/Library/Application Support/VapourBox/deps/$platformDir/ffmpeg/$name',
506+
'$home/Library/Application Support/VapourBox/deps/$platformDir/vapoursynth/$name',
507+
];
508+
}
503509

504510
for (final p in bundledPaths) {
505511
if (await File(p).exists()) {

worker/src/dependency_locator.rs

Lines changed: 32 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -31,51 +31,52 @@ impl DependencyLocator {
3131

3232
/// Find the deps directory by searching various locations.
3333
fn find_deps_directory(exe_path: &Path) -> Result<PathBuf> {
34-
// On macOS, first check Application Support (where downloaded deps go)
35-
#[cfg(target_os = "macos")]
34+
// Development only: search upward from executable for project deps
35+
#[cfg(debug_assertions)]
3636
{
37-
if let Some(home) = env::var_os("HOME") {
38-
let app_support_deps = PathBuf::from(home)
39-
.join("Library")
40-
.join("Application Support")
41-
.join("VapourBox")
42-
.join("deps");
43-
if app_support_deps.join("macos-arm64").exists()
44-
|| app_support_deps.join("macos-x64").exists() {
45-
return Ok(app_support_deps);
37+
let mut current = exe_path.parent();
38+
while let Some(dir) = current {
39+
// Check for deps directory with platform subdirectory
40+
// This ensures we find the project deps, not Cargo's deps
41+
let deps_dir = dir.join("deps");
42+
if deps_dir.exists() {
43+
// Verify this has our expected structure (windows-x64 or macos-arm64, etc.)
44+
let has_platform_dir = deps_dir.join("windows-x64").exists()
45+
|| deps_dir.join("macos-arm64").exists()
46+
|| deps_dir.join("macos-x64").exists();
47+
if has_platform_dir {
48+
return Ok(deps_dir);
49+
}
4650
}
51+
current = dir.parent();
4752
}
4853
}
4954

50-
// Search upward from executable
51-
let mut current = exe_path.parent();
52-
53-
while let Some(dir) = current {
54-
// Check for deps directory with platform subdirectory
55-
// This ensures we find the project deps, not Cargo's deps
56-
let deps_dir = dir.join("deps");
57-
if deps_dir.exists() {
58-
// Verify this has our expected structure (windows-x64 or macos-arm64, etc.)
59-
let has_platform_dir = deps_dir.join("windows-x64").exists()
60-
|| deps_dir.join("macos-arm64").exists()
61-
|| deps_dir.join("macos-x64").exists();
62-
if has_platform_dir {
63-
return Ok(deps_dir);
64-
}
55+
// Windows production: deps folder next to executable
56+
#[cfg(target_os = "windows")]
57+
{
58+
let exe_dir = exe_path.parent().unwrap_or(Path::new("."));
59+
let deps_dir = exe_dir.join("deps");
60+
if deps_dir.join("windows-x64").exists() {
61+
return Ok(deps_dir);
6562
}
66-
67-
current = dir.parent();
6863
}
6964

70-
// Fallback: Application Support on macOS, relative path otherwise
65+
// macOS production: Application Support (where downloaded deps go)
7166
#[cfg(target_os = "macos")]
7267
{
7368
if let Some(home) = env::var_os("HOME") {
74-
return Ok(PathBuf::from(home)
69+
let app_support_deps = PathBuf::from(home)
7570
.join("Library")
7671
.join("Application Support")
7772
.join("VapourBox")
78-
.join("deps"));
73+
.join("deps");
74+
if app_support_deps.join("macos-arm64").exists()
75+
|| app_support_deps.join("macos-x64").exists() {
76+
return Ok(app_support_deps);
77+
}
78+
// Fallback to Application Support path (will be created when deps download)
79+
return Ok(app_support_deps);
7980
}
8081
}
8182

0 commit comments

Comments
 (0)