mirror of
https://github.com/localsend/localsend.git
synced 2026-08-07 07:14:52 +00:00
fix: create the destination directory when it is missing (#3223)
This commit is contained in:
@@ -57,6 +57,12 @@ jobs:
|
|||||||
- name: Test (app)
|
- name: Test (app)
|
||||||
working-directory: app
|
working-directory: app
|
||||||
run: flutter test
|
run: flutter test
|
||||||
|
- name: Dependencies (localsend_isolates)
|
||||||
|
working-directory: packages/localsend_isolates
|
||||||
|
run: flutter pub get
|
||||||
|
- name: Test (localsend_isolates)
|
||||||
|
working-directory: packages/localsend_isolates
|
||||||
|
run: flutter test
|
||||||
|
|
||||||
packaging:
|
packaging:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|||||||
@@ -215,14 +215,14 @@ Future<(String, String?, String)> digestFilePathAndPrepareDirectory({
|
|||||||
if (!p.isWithin(parentDirectory, dir)) {
|
if (!p.isWithin(parentDirectory, dir)) {
|
||||||
throw 'Path traversal detected';
|
throw 'Path traversal detected';
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
|
||||||
Directory(dir).createSync(recursive: true);
|
|
||||||
} catch (e) {
|
|
||||||
_logger.warning('Could not create directory', e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The destination directory may not exist anymore, e.g. because it was deleted
|
||||||
|
// or is on a drive that is no longer mounted. This also creates the
|
||||||
|
// sub-directories of a folder transfer. Errors are propagated so that the
|
||||||
|
// caller fails the upload instead of writing to a path that cannot be opened.
|
||||||
|
Directory(dir).createSync(recursive: true);
|
||||||
|
|
||||||
String destinationPath;
|
String destinationPath;
|
||||||
int counter = 1;
|
int counter = 1;
|
||||||
do {
|
do {
|
||||||
|
|||||||
@@ -41,6 +41,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.13.1"
|
version: "2.13.1"
|
||||||
|
boolean_selector:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: boolean_selector
|
||||||
|
sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.2"
|
||||||
build:
|
build:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -113,6 +121,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.4"
|
version: "2.0.4"
|
||||||
|
clock:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: clock
|
||||||
|
sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.1.2"
|
||||||
collection:
|
collection:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -161,6 +177,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.1.7"
|
version: "3.1.7"
|
||||||
|
fake_async:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: fake_async
|
||||||
|
sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.3.3"
|
||||||
ffi:
|
ffi:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -214,6 +238,11 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.12.0"
|
version: "2.12.0"
|
||||||
|
flutter_test:
|
||||||
|
dependency: "direct dev"
|
||||||
|
description: flutter
|
||||||
|
source: sdk
|
||||||
|
version: "0.0.0"
|
||||||
flutter_web_plugins:
|
flutter_web_plugins:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description: flutter
|
description: flutter
|
||||||
@@ -291,6 +320,30 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.12.0"
|
version: "4.12.0"
|
||||||
|
leak_tracker:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: leak_tracker
|
||||||
|
sha256: "33e2e26bdd85a0112ec15400c8cbffea70d0f9c3407491f672a2fad47915e2de"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "11.0.2"
|
||||||
|
leak_tracker_flutter_testing:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: leak_tracker_flutter_testing
|
||||||
|
sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.0.10"
|
||||||
|
leak_tracker_testing:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: leak_tracker_testing
|
||||||
|
sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.0.2"
|
||||||
legalize:
|
legalize:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -315,6 +368,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.3.0"
|
version: "1.3.0"
|
||||||
|
matcher:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: matcher
|
||||||
|
sha256: dc0b7dc7651697ea4ff3e69ef44b0407ea32c487a39fff6a4004fa585e901861
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.12.19"
|
||||||
material_color_utilities:
|
material_color_utilities:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -575,6 +636,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.2.2"
|
version: "1.2.2"
|
||||||
|
test_api:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: test_api
|
||||||
|
sha256: "949a932224383300f01be9221c39180316445ecb8e7547f70a41a35bf421fb9e"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.7.11"
|
||||||
type_plus:
|
type_plus:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -614,6 +683,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.2.0"
|
version: "2.2.0"
|
||||||
|
vm_service:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: vm_service
|
||||||
|
sha256: "0016aef94fc66495ac78af5859181e3f3bf2026bd8eecc72b9565601e19ab360"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "15.2.0"
|
||||||
watcher:
|
watcher:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|||||||
@@ -33,4 +33,6 @@ dev_dependencies:
|
|||||||
build_runner: 2.15.1
|
build_runner: 2.15.1
|
||||||
dart_mappable_builder: 4.8.0
|
dart_mappable_builder: 4.8.0
|
||||||
flutter_lints: 6.0.0
|
flutter_lints: 6.0.0
|
||||||
|
flutter_test:
|
||||||
|
sdk: flutter
|
||||||
freezed: 3.2.5
|
freezed: 3.2.5
|
||||||
|
|||||||
@@ -0,0 +1,59 @@
|
|||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:localsend_isolates/src/task/server/file_saver.dart';
|
||||||
|
import 'package:path/path.dart' as p;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
late Directory tempDir;
|
||||||
|
|
||||||
|
setUp(() {
|
||||||
|
tempDir = Directory.systemTemp.createTempSync('file_saver_test');
|
||||||
|
});
|
||||||
|
|
||||||
|
tearDown(() {
|
||||||
|
if (tempDir.existsSync()) {
|
||||||
|
tempDir.deleteSync(recursive: true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Future<String> digest(String parentDirectory, String fileName) async {
|
||||||
|
final (path, _, _) = await digestFilePathAndPrepareDirectory(
|
||||||
|
parentDirectory: parentDirectory,
|
||||||
|
fileName: fileName,
|
||||||
|
createdDirectories: {},
|
||||||
|
);
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
test('creates the destination directory when it does not exist', () async {
|
||||||
|
final destination = p.join(tempDir.path, 'gone');
|
||||||
|
final path = await digest(destination, 'file.txt');
|
||||||
|
|
||||||
|
expect(Directory(destination).existsSync(), isTrue);
|
||||||
|
expect(path, p.join(destination, 'file.txt'));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('creates the sub-directories of a folder transfer', () async {
|
||||||
|
final path = await digest(tempDir.path, p.join('outer', 'inner', 'file.txt'));
|
||||||
|
|
||||||
|
expect(Directory(p.join(tempDir.path, 'outer', 'inner')).existsSync(), isTrue);
|
||||||
|
expect(path, p.join(tempDir.path, 'outer', 'inner', 'file.txt'));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('keeps an existing directory and its content', () async {
|
||||||
|
File(p.join(tempDir.path, 'file.txt')).writeAsStringSync('hello');
|
||||||
|
|
||||||
|
final path = await digest(tempDir.path, 'file.txt');
|
||||||
|
|
||||||
|
expect(path, p.join(tempDir.path, 'file (2).txt'));
|
||||||
|
expect(File(p.join(tempDir.path, 'file.txt')).readAsStringSync(), 'hello');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('still rejects path traversal', () async {
|
||||||
|
await expectLater(
|
||||||
|
digest(tempDir.path, p.join('..', 'escaped', 'file.txt')),
|
||||||
|
throwsA('Path traversal detected'),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user