@theia/filesystem 1.74.0-next.9 → 1.74.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/node/disk-file-system-provider.spec.js +5 -1
- package/lib/node/disk-file-system-provider.spec.js.map +1 -1
- package/lib/node/download/file-download-endpoint.d.ts +2 -0
- package/lib/node/download/file-download-endpoint.d.ts.map +1 -1
- package/lib/node/download/file-download-endpoint.js +10 -4
- package/lib/node/download/file-download-endpoint.js.map +1 -1
- package/lib/node/upload/node-file-upload-service.d.ts +2 -1
- package/lib/node/upload/node-file-upload-service.d.ts.map +1 -1
- package/lib/node/upload/node-file-upload-service.js +8 -2
- package/lib/node/upload/node-file-upload-service.js.map +1 -1
- package/lib/node/upload/node-file-upload-service.spec.d.ts +2 -0
- package/lib/node/upload/node-file-upload-service.spec.d.ts.map +1 -0
- package/lib/node/upload/node-file-upload-service.spec.js +107 -0
- package/lib/node/upload/node-file-upload-service.spec.js.map +1 -0
- package/package.json +6 -6
- package/src/node/disk-file-system-provider.spec.ts +5 -1
- package/src/node/download/file-download-endpoint.ts +10 -5
- package/src/node/upload/node-file-upload-service.spec.ts +126 -0
- package/src/node/upload/node-file-upload-service.ts +9 -3
|
@@ -24,6 +24,7 @@ const ipc_connection_provider_1 = require("@theia/core/lib/node/messaging/ipc-co
|
|
|
24
24
|
const inversify_1 = require("@theia/core/shared/inversify");
|
|
25
25
|
const assert_1 = require("assert");
|
|
26
26
|
const fs_1 = require("fs");
|
|
27
|
+
const os = require("os");
|
|
27
28
|
const path_1 = require("path");
|
|
28
29
|
const temp = require("temp");
|
|
29
30
|
const uuid_1 = require("@theia/core/lib/common/uuid");
|
|
@@ -96,7 +97,10 @@ describe('disk-file-system-provider', () => {
|
|
|
96
97
|
});
|
|
97
98
|
it('delete is able to delete file', async function () {
|
|
98
99
|
this.timeout(10000);
|
|
99
|
-
|
|
100
|
+
// Place the temp dir under $HOME so the XDG trash dir (~/.local/share/Trash) is on the same
|
|
101
|
+
// filesystem. Otherwise the trash spec falls back to creating `.Trash-$UID` at the mount's
|
|
102
|
+
// topdir, which fails when /tmp is on a filesystem whose root is not user-writable.
|
|
103
|
+
const tempDirPath = tracked.mkdirSync({ dir: os.homedir(), prefix: 'theia-fs-test-' });
|
|
100
104
|
const testFile = (0, path_1.join)(tempDirPath, 'test.file');
|
|
101
105
|
const testFileUri = file_uri_1.FileUri.create(testFile);
|
|
102
106
|
for (const recursive of [true, false]) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"disk-file-system-provider.spec.js","sourceRoot":"","sources":["../../src/node/disk-file-system-provider.spec.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"disk-file-system-provider.spec.js","sourceRoot":"","sources":["../../src/node/disk-file-system-provider.spec.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { Application } from '@theia/core/shared/express';
|
|
2
2
|
import { BackendApplicationContribution } from '@theia/core/lib/node/backend-application';
|
|
3
|
+
import { HttpConnectionValidator } from '@theia/core/lib/node';
|
|
3
4
|
import { FileDownloadHandler } from './file-download-handler';
|
|
4
5
|
export declare class FileDownloadEndpoint implements BackendApplicationContribution {
|
|
5
6
|
protected static PATH: string;
|
|
6
7
|
protected readonly singleFileDownloadHandler: FileDownloadHandler;
|
|
7
8
|
protected readonly multiFileDownloadHandler: FileDownloadHandler;
|
|
8
9
|
protected readonly downloadLinkHandler: FileDownloadHandler;
|
|
10
|
+
protected readonly connectionValidator: HttpConnectionValidator;
|
|
9
11
|
configure(app: Application): void;
|
|
10
12
|
}
|
|
11
13
|
//# sourceMappingURL=file-download-endpoint.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"file-download-endpoint.d.ts","sourceRoot":"","sources":["../../../src/node/download/file-download-endpoint.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"file-download-endpoint.d.ts","sourceRoot":"","sources":["../../../src/node/download/file-download-endpoint.ts"],"names":[],"mappings":"AAoBA,OAAO,EAAE,WAAW,EAA0B,MAAM,4BAA4B,CAAC;AACjF,OAAO,EAAE,8BAA8B,EAAE,MAAM,0CAA0C,CAAC;AAC1F,OAAO,EAAE,uBAAuB,EAAE,MAAM,sBAAsB,CAAC;AAE/D,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAE9D,qBACa,oBAAqB,YAAW,8BAA8B;IAEvE,SAAS,CAAC,MAAM,CAAC,IAAI,SAAY;IAIjC,SAAS,CAAC,QAAQ,CAAC,yBAAyB,EAAE,mBAAmB,CAAC;IAIlE,SAAS,CAAC,QAAQ,CAAC,wBAAwB,EAAE,mBAAmB,CAAC;IAIjE,SAAS,CAAC,QAAQ,CAAC,mBAAmB,EAAE,mBAAmB,CAAC;IAG5D,SAAS,CAAC,QAAQ,CAAC,mBAAmB,EAAE,uBAAuB,CAAC;IAEhE,SAAS,CAAC,GAAG,EAAE,WAAW,GAAG,IAAI;CAqBpC"}
|
|
@@ -19,25 +19,27 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
19
19
|
exports.FileDownloadEndpoint = void 0;
|
|
20
20
|
const tslib_1 = require("tslib");
|
|
21
21
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
22
|
-
const url = require("url");
|
|
23
22
|
const inversify_1 = require("@theia/core/shared/inversify");
|
|
24
23
|
const body_parser_1 = require("body-parser");
|
|
25
24
|
const express_1 = require("@theia/core/shared/express");
|
|
25
|
+
const node_1 = require("@theia/core/lib/node");
|
|
26
26
|
const file_uri_1 = require("@theia/core/lib/common/file-uri");
|
|
27
27
|
const file_download_handler_1 = require("./file-download-handler");
|
|
28
28
|
let FileDownloadEndpoint = class FileDownloadEndpoint {
|
|
29
29
|
static { FileDownloadEndpoint_1 = this; }
|
|
30
30
|
static { this.PATH = '/files'; }
|
|
31
31
|
configure(app) {
|
|
32
|
+
// Reject unauthenticated/cross-origin requests to the file read endpoints.
|
|
33
|
+
const guard = (request, response, next) => this.connectionValidator.validateRequest(request, response, next);
|
|
32
34
|
const router = (0, express_1.Router)();
|
|
33
35
|
router.get('/download', (request, response) => this.downloadLinkHandler.handle(request, response));
|
|
34
36
|
router.get('/', (request, response) => this.singleFileDownloadHandler.handle(request, response));
|
|
35
37
|
router.put('/', (request, response) => this.multiFileDownloadHandler.handle(request, response));
|
|
36
38
|
// Content-Type: application/json
|
|
37
39
|
app.use((0, body_parser_1.json)());
|
|
38
|
-
app.use(FileDownloadEndpoint_1.PATH, router);
|
|
39
|
-
app.get('/file', (request, response) => {
|
|
40
|
-
const uri =
|
|
40
|
+
app.use(FileDownloadEndpoint_1.PATH, guard, router);
|
|
41
|
+
app.get('/file', guard, (request, response) => {
|
|
42
|
+
const uri = new URL(request.url, 'http://localhost').search.slice(1);
|
|
41
43
|
if (!uri) {
|
|
42
44
|
response.status(400).send('invalid uri');
|
|
43
45
|
return;
|
|
@@ -63,6 +65,10 @@ tslib_1.__decorate([
|
|
|
63
65
|
(0, inversify_1.named)(file_download_handler_1.FileDownloadHandler.DOWNLOAD_LINK),
|
|
64
66
|
tslib_1.__metadata("design:type", file_download_handler_1.FileDownloadHandler)
|
|
65
67
|
], FileDownloadEndpoint.prototype, "downloadLinkHandler", void 0);
|
|
68
|
+
tslib_1.__decorate([
|
|
69
|
+
(0, inversify_1.inject)(node_1.HttpConnectionValidator),
|
|
70
|
+
tslib_1.__metadata("design:type", Object)
|
|
71
|
+
], FileDownloadEndpoint.prototype, "connectionValidator", void 0);
|
|
66
72
|
exports.FileDownloadEndpoint = FileDownloadEndpoint = FileDownloadEndpoint_1 = tslib_1.__decorate([
|
|
67
73
|
(0, inversify_1.injectable)()
|
|
68
74
|
], FileDownloadEndpoint);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"file-download-endpoint.js","sourceRoot":"","sources":["../../../src/node/download/file-download-endpoint.ts"],"names":[],"mappings":";AAAA,gFAAgF;AAChF,yCAAyC;AACzC,EAAE;AACF,2EAA2E;AAC3E,mEAAmE;AACnE,wCAAwC;AACxC,EAAE;AACF,4EAA4E;AAC5E,8EAA8E;AAC9E,6EAA6E;AAC7E,yDAAyD;AACzD,uDAAuD;AACvD,EAAE;AACF,gFAAgF;AAChF,gFAAgF;;;;;AAEhF,uDAAuD;AAEvD,
|
|
1
|
+
{"version":3,"file":"file-download-endpoint.js","sourceRoot":"","sources":["../../../src/node/download/file-download-endpoint.ts"],"names":[],"mappings":";AAAA,gFAAgF;AAChF,yCAAyC;AACzC,EAAE;AACF,2EAA2E;AAC3E,mEAAmE;AACnE,wCAAwC;AACxC,EAAE;AACF,4EAA4E;AAC5E,8EAA8E;AAC9E,6EAA6E;AAC7E,yDAAyD;AACzD,uDAAuD;AACvD,EAAE;AACF,gFAAgF;AAChF,gFAAgF;;;;;AAEhF,uDAAuD;AAEvD,4DAAyE;AACzE,6CAAmC;AACnC,wDAAiF;AAEjF,+CAA+D;AAC/D,8DAA0D;AAC1D,mEAA8D;AAGvD,IAAM,oBAAoB,GAA1B,MAAM,oBAAoB;;aAEZ,SAAI,GAAG,QAAQ,AAAX,CAAY;IAiBjC,SAAS,CAAC,GAAgB;QACtB,2EAA2E;QAC3E,MAAM,KAAK,GAAmB,CAAC,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,mBAAmB,CAAC,eAAe,CAAC,OAAO,EAAE,QAAQ,EAAE,IAAI,CAAC,CAAC;QAC7H,MAAM,MAAM,GAAG,IAAA,gBAAM,GAAE,CAAC;QACxB,MAAM,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC,OAAO,EAAE,QAAQ,EAAE,EAAE,CAAC,IAAI,CAAC,mBAAmB,CAAC,MAAM,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC;QACnG,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,OAAO,EAAE,QAAQ,EAAE,EAAE,CAAC,IAAI,CAAC,yBAAyB,CAAC,MAAM,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC;QACjG,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,OAAO,EAAE,QAAQ,EAAE,EAAE,CAAC,IAAI,CAAC,wBAAwB,CAAC,MAAM,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC;QAChG,iCAAiC;QACjC,GAAG,CAAC,GAAG,CAAC,IAAA,kBAAI,GAAE,CAAC,CAAC;QAChB,GAAG,CAAC,GAAG,CAAC,sBAAoB,CAAC,IAAI,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC;QAClD,GAAG,CAAC,GAAG,CAAC,OAAO,EAAE,KAAK,EAAE,CAAC,OAAO,EAAE,QAAQ,EAAE,EAAE;YAC1C,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,EAAE,kBAAkB,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;YACrE,IAAI,CAAC,GAAG,EAAE,CAAC;gBACP,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;gBACzC,OAAO;YACX,CAAC;YACD,MAAM,MAAM,GAAG,kBAAO,CAAC,MAAM,CAAC,kBAAkB,CAAC,GAAG,CAAC,CAAC,CAAC;YACvD,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QAC9B,CAAC,CAAC,CAAC;IACP,CAAC;;AAtCQ,oDAAoB;AAMV;IAFlB,IAAA,kBAAM,EAAC,2CAAmB,CAAC;IAC3B,IAAA,iBAAK,EAAC,2CAAmB,CAAC,MAAM,CAAC;sCACY,2CAAmB;uEAAC;AAI/C;IAFlB,IAAA,kBAAM,EAAC,2CAAmB,CAAC;IAC3B,IAAA,iBAAK,EAAC,2CAAmB,CAAC,KAAK,CAAC;sCACY,2CAAmB;sEAAC;AAI9C;IAFlB,IAAA,kBAAM,EAAC,2CAAmB,CAAC;IAC3B,IAAA,iBAAK,EAAC,2CAAmB,CAAC,aAAa,CAAC;sCACD,2CAAmB;iEAAC;AAGzC;IADlB,IAAA,kBAAM,EAAC,8BAAuB,CAAC;;iEACgC;+BAjBvD,oBAAoB;IADhC,IAAA,sBAAU,GAAE;GACA,oBAAoB,CAwChC"}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import express = require('@theia/core/shared/express');
|
|
2
|
-
import { BackendApplicationContribution } from '@theia/core/lib/node';
|
|
2
|
+
import { BackendApplicationContribution, HttpConnectionValidator } from '@theia/core/lib/node';
|
|
3
3
|
import { ILogger } from '@theia/core';
|
|
4
4
|
export declare class NodeFileUploadService implements BackendApplicationContribution {
|
|
5
5
|
protected readonly logger: ILogger;
|
|
6
|
+
protected readonly connectionValidator: HttpConnectionValidator;
|
|
6
7
|
private static readonly UPLOAD_DIR;
|
|
7
8
|
configure(app: express.Application): Promise<void>;
|
|
8
9
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"node-file-upload-service.d.ts","sourceRoot":"","sources":["../../../src/node/upload/node-file-upload-service.ts"],"names":[],"mappings":"AAmBA,OAAO,OAAO,GAAG,QAAQ,4BAA4B,CAAC,CAAC;AAEvD,OAAO,EAAE,8BAA8B,EAAW,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"node-file-upload-service.d.ts","sourceRoot":"","sources":["../../../src/node/upload/node-file-upload-service.ts"],"names":[],"mappings":"AAmBA,OAAO,OAAO,GAAG,QAAQ,4BAA4B,CAAC,CAAC;AAEvD,OAAO,EAAE,8BAA8B,EAAW,uBAAuB,EAAE,MAAM,sBAAsB,CAAC;AAGxG,OAAO,EAAE,OAAO,EAAO,MAAM,aAAa,CAAC;AAE3C,qBACa,qBAAsB,YAAW,8BAA8B;IAGxE,SAAS,CAAC,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IAGnC,SAAS,CAAC,QAAQ,CAAC,mBAAmB,EAAE,uBAAuB,CAAC;IAEhE,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAkB;IAE9C,SAAS,CAAC,GAAG,EAAE,OAAO,CAAC,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAkBxD;;OAEG;cACa,qBAAqB,IAAI,OAAO,CAAC,MAAM,CAAC;IAIxD;;OAEG;cACa,sBAAsB,IAAI,OAAO,CAAC,MAAM,CAAC;cAIzC,gBAAgB,CAAC,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,QAAQ,EAAE,OAAO,CAAC,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;CA2BxG"}
|
|
@@ -36,7 +36,9 @@ let NodeFileUploadService = class NodeFileUploadService {
|
|
|
36
36
|
]);
|
|
37
37
|
this.logger.debug(`HTTP file upload URL path: ${http_path}`);
|
|
38
38
|
this.logger.debug(`Backend file upload cache path: ${dest}`);
|
|
39
|
-
|
|
39
|
+
// Reject unauthenticated/cross-origin requests before `multer` writes any temp file.
|
|
40
|
+
const guard = (request, response, next) => this.connectionValidator.validateRequest(request, response, next);
|
|
41
|
+
app.post(http_path, guard,
|
|
40
42
|
// `multer` handles `multipart/form-data` containing our file to upload.
|
|
41
43
|
multer({ dest }).single('file'), (request, response, next) => this.handleFileUpload(request, response));
|
|
42
44
|
}
|
|
@@ -54,7 +56,7 @@ let NodeFileUploadService = class NodeFileUploadService {
|
|
|
54
56
|
}
|
|
55
57
|
async handleFileUpload(request, response) {
|
|
56
58
|
const fields = request.body;
|
|
57
|
-
if (!request.file || typeof fields !== 'object' || typeof fields.uri !== 'string') {
|
|
59
|
+
if (!request.file || typeof fields !== 'object' || typeof fields.uri !== 'string' || new core_1.URI(fields.uri).scheme !== 'file') {
|
|
58
60
|
response.sendStatus(400); // bad request
|
|
59
61
|
return;
|
|
60
62
|
}
|
|
@@ -88,6 +90,10 @@ tslib_1.__decorate([
|
|
|
88
90
|
(0, inversify_1.named)('filesystem:NodeFileUploadService'),
|
|
89
91
|
tslib_1.__metadata("design:type", Object)
|
|
90
92
|
], NodeFileUploadService.prototype, "logger", void 0);
|
|
93
|
+
tslib_1.__decorate([
|
|
94
|
+
(0, inversify_1.inject)(node_1.HttpConnectionValidator),
|
|
95
|
+
tslib_1.__metadata("design:type", Object)
|
|
96
|
+
], NodeFileUploadService.prototype, "connectionValidator", void 0);
|
|
91
97
|
exports.NodeFileUploadService = NodeFileUploadService = NodeFileUploadService_1 = tslib_1.__decorate([
|
|
92
98
|
(0, inversify_1.injectable)()
|
|
93
99
|
], NodeFileUploadService);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"node-file-upload-service.js","sourceRoot":"","sources":["../../../src/node/upload/node-file-upload-service.ts"],"names":[],"mappings":";AAAA,gFAAgF;AAChF,yCAAyC;AACzC,EAAE;AACF,2EAA2E;AAC3E,mEAAmE;AACnE,wCAAwC;AACxC,EAAE;AACF,4EAA4E;AAC5E,8EAA8E;AAC9E,6EAA6E;AAC7E,yDAAyD;AACzD,uDAAuD;AACvD,EAAE;AACF,gFAAgF;AAChF,gFAAgF;;;;;AAEhF,iCAAkC;AAClC,6BAA8B;AAC9B,yBAA0B;AAE1B,kDAAmD;AACnD,+
|
|
1
|
+
{"version":3,"file":"node-file-upload-service.js","sourceRoot":"","sources":["../../../src/node/upload/node-file-upload-service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"node-file-upload-service.spec.d.ts","sourceRoot":"","sources":["../../../src/node/upload/node-file-upload-service.spec.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// *****************************************************************************
|
|
3
|
+
// Copyright (C) 2026 EclipseSource GmbH and others.
|
|
4
|
+
//
|
|
5
|
+
// This program and the accompanying materials are made available under the
|
|
6
|
+
// terms of the Eclipse Public License v. 2.0 which is available at
|
|
7
|
+
// http://www.eclipse.org/legal/epl-2.0.
|
|
8
|
+
//
|
|
9
|
+
// This Source Code may also be made available under the following Secondary
|
|
10
|
+
// Licenses when the conditions for such availability set forth in the Eclipse
|
|
11
|
+
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
|
|
12
|
+
// with the GNU Classpath Exception which is available at
|
|
13
|
+
// https://www.gnu.org/software/classpath/license.html.
|
|
14
|
+
//
|
|
15
|
+
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
|
|
16
|
+
// *****************************************************************************
|
|
17
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
|
+
const chai_1 = require("chai");
|
|
19
|
+
const os = require("os");
|
|
20
|
+
const path = require("path");
|
|
21
|
+
const fs = require("@theia/core/shared/fs-extra");
|
|
22
|
+
const file_uri_1 = require("@theia/core/lib/common/file-uri");
|
|
23
|
+
const node_file_upload_service_1 = require("./node-file-upload-service");
|
|
24
|
+
describe('NodeFileUploadService', () => {
|
|
25
|
+
let tempDir;
|
|
26
|
+
function createService() {
|
|
27
|
+
const service = new node_file_upload_service_1.NodeFileUploadService();
|
|
28
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
29
|
+
service['logger'] = { error: () => { } };
|
|
30
|
+
return service;
|
|
31
|
+
}
|
|
32
|
+
function createResponse() {
|
|
33
|
+
const captured = {};
|
|
34
|
+
const res = {
|
|
35
|
+
status(code) {
|
|
36
|
+
captured.statusCode = code;
|
|
37
|
+
return res;
|
|
38
|
+
},
|
|
39
|
+
send(body) {
|
|
40
|
+
captured.body = body;
|
|
41
|
+
},
|
|
42
|
+
sendStatus(code) {
|
|
43
|
+
captured.sentStatus = code;
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
return { res, captured };
|
|
47
|
+
}
|
|
48
|
+
async function handleUpload(filePath, body) {
|
|
49
|
+
const service = createService();
|
|
50
|
+
const { res, captured } = createResponse();
|
|
51
|
+
const request = { file: filePath === undefined ? undefined : { path: filePath }, body };
|
|
52
|
+
// handleFileUpload is protected; invoke it directly to exercise the input validation.
|
|
53
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
54
|
+
await service.handleFileUpload(request, res);
|
|
55
|
+
return captured;
|
|
56
|
+
}
|
|
57
|
+
beforeEach(async () => {
|
|
58
|
+
tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'theia-upload-spec-'));
|
|
59
|
+
});
|
|
60
|
+
afterEach(async () => {
|
|
61
|
+
await fs.remove(tempDir);
|
|
62
|
+
});
|
|
63
|
+
it('should reject a non-file: URI with 400', async () => {
|
|
64
|
+
const source = path.join(tempDir, 'source.txt');
|
|
65
|
+
await fs.writeFile(source, 'payload');
|
|
66
|
+
const captured = await handleUpload(source, { uri: 'http://evil.example/usr/bin/bash' });
|
|
67
|
+
(0, chai_1.expect)(captured.sentStatus).to.equal(400);
|
|
68
|
+
// The scoped-write guard must reject before any file operation runs.
|
|
69
|
+
(0, chai_1.expect)(await fs.pathExists(source)).to.be.true;
|
|
70
|
+
});
|
|
71
|
+
it('should reject a remote-scheme URI with 400', async () => {
|
|
72
|
+
const source = path.join(tempDir, 'source.txt');
|
|
73
|
+
await fs.writeFile(source, 'payload');
|
|
74
|
+
const captured = await handleUpload(source, { uri: 'vscode-remote://host/usr/bin/bash' });
|
|
75
|
+
(0, chai_1.expect)(captured.sentStatus).to.equal(400);
|
|
76
|
+
(0, chai_1.expect)(await fs.pathExists(source)).to.be.true;
|
|
77
|
+
});
|
|
78
|
+
it('should reject a missing uri field with 400', async () => {
|
|
79
|
+
const source = path.join(tempDir, 'source.txt');
|
|
80
|
+
await fs.writeFile(source, 'payload');
|
|
81
|
+
const captured = await handleUpload(source, {});
|
|
82
|
+
(0, chai_1.expect)(captured.sentStatus).to.equal(400);
|
|
83
|
+
});
|
|
84
|
+
it('should reject a missing file with 400', async () => {
|
|
85
|
+
const target = file_uri_1.FileUri.create(path.join(tempDir, 'target.txt')).toString();
|
|
86
|
+
const captured = await handleUpload(undefined, { uri: target });
|
|
87
|
+
(0, chai_1.expect)(captured.sentStatus).to.equal(400);
|
|
88
|
+
});
|
|
89
|
+
it('should accept a file: URI and move the upload to the target', async () => {
|
|
90
|
+
const source = path.join(tempDir, 'source.txt');
|
|
91
|
+
await fs.writeFile(source, 'payload');
|
|
92
|
+
const targetPath = path.join(tempDir, 'target.txt');
|
|
93
|
+
const target = file_uri_1.FileUri.create(targetPath).toString();
|
|
94
|
+
const captured = await handleUpload(source, { uri: target });
|
|
95
|
+
(0, chai_1.expect)(captured.sentStatus).to.be.undefined;
|
|
96
|
+
(0, chai_1.expect)(captured.statusCode).to.equal(200);
|
|
97
|
+
// Compare against the canonical fs-path of the URI: on Windows the create/fsPath round-trip
|
|
98
|
+
// normalizes the drive letter to lower-case (e.g. `C:` -> `c:`), which would otherwise fail
|
|
99
|
+
// an exact string match against `targetPath` derived from `os.tmpdir()`.
|
|
100
|
+
(0, chai_1.expect)(captured.body).to.equal(file_uri_1.FileUri.fsPath(target));
|
|
101
|
+
(0, chai_1.expect)(await fs.pathExists(targetPath)).to.be.true;
|
|
102
|
+
(0, chai_1.expect)(await fs.readFile(targetPath, 'utf8')).to.equal('payload');
|
|
103
|
+
// The temp source file was moved, not copied.
|
|
104
|
+
(0, chai_1.expect)(await fs.pathExists(source)).to.be.false;
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
//# sourceMappingURL=node-file-upload-service.spec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"node-file-upload-service.spec.js","sourceRoot":"","sources":["../../../src/node/upload/node-file-upload-service.spec.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@theia/filesystem",
|
|
3
|
-
"version": "1.74.0
|
|
3
|
+
"version": "1.74.0",
|
|
4
4
|
"description": "Theia - FileSystem Extension",
|
|
5
5
|
"dependencies": {
|
|
6
|
-
"@theia/core": "1.74.0
|
|
6
|
+
"@theia/core": "1.74.0",
|
|
7
7
|
"@types/body-parser": "^1.19.6",
|
|
8
8
|
"@types/multer": "^1.4.13",
|
|
9
9
|
"@types/tar-fs": "^1.16.3",
|
|
@@ -13,11 +13,11 @@
|
|
|
13
13
|
"http-status-codes": "^1.4.0",
|
|
14
14
|
"ignore": "^6.0.2",
|
|
15
15
|
"minimatch": "^10.2.5",
|
|
16
|
-
"multer": "^2.
|
|
16
|
+
"multer": "^2.2.0",
|
|
17
17
|
"opfs-worker": "1.3.1",
|
|
18
18
|
"rimraf": "^5.0.10",
|
|
19
19
|
"stat-mode": "^1.0.0",
|
|
20
|
-
"tar-fs": "^3.1.
|
|
20
|
+
"tar-fs": "^3.1.3",
|
|
21
21
|
"tar-stream": "^3.2.0",
|
|
22
22
|
"trash": "^7.2.0",
|
|
23
23
|
"tslib": "^2.8.1",
|
|
@@ -77,10 +77,10 @@
|
|
|
77
77
|
"watch": "theiaext watch"
|
|
78
78
|
},
|
|
79
79
|
"devDependencies": {
|
|
80
|
-
"@theia/ext-scripts": "1.
|
|
80
|
+
"@theia/ext-scripts": "1.74.0"
|
|
81
81
|
},
|
|
82
82
|
"nyc": {
|
|
83
83
|
"extends": "../../configs/nyc.json"
|
|
84
84
|
},
|
|
85
|
-
"gitHead": "
|
|
85
|
+
"gitHead": "7b7376225f0825b4f13d296d686cc13db95fdb69"
|
|
86
86
|
}
|
|
@@ -23,6 +23,7 @@ import { IPCConnectionProvider } from '@theia/core/lib/node/messaging/ipc-connec
|
|
|
23
23
|
import { Container, ContainerModule } from '@theia/core/shared/inversify';
|
|
24
24
|
import { equal, fail } from 'assert';
|
|
25
25
|
import { promises as fs } from 'fs';
|
|
26
|
+
import * as os from 'os';
|
|
26
27
|
import { join } from 'path';
|
|
27
28
|
import * as temp from 'temp';
|
|
28
29
|
import { generateUuid } from '@theia/core/lib/common/uuid';
|
|
@@ -115,7 +116,10 @@ describe('disk-file-system-provider', () => {
|
|
|
115
116
|
|
|
116
117
|
it('delete is able to delete file', async function (): Promise<void> {
|
|
117
118
|
this.timeout(10000);
|
|
118
|
-
|
|
119
|
+
// Place the temp dir under $HOME so the XDG trash dir (~/.local/share/Trash) is on the same
|
|
120
|
+
// filesystem. Otherwise the trash spec falls back to creating `.Trash-$UID` at the mount's
|
|
121
|
+
// topdir, which fails when /tmp is on a filesystem whose root is not user-writable.
|
|
122
|
+
const tempDirPath = tracked.mkdirSync({ dir: os.homedir(), prefix: 'theia-fs-test-' });
|
|
119
123
|
const testFile = join(tempDirPath, 'test.file');
|
|
120
124
|
const testFileUri = FileUri.create(testFile);
|
|
121
125
|
for (const recursive of [true, false]) {
|
|
@@ -16,11 +16,11 @@
|
|
|
16
16
|
|
|
17
17
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
18
18
|
|
|
19
|
-
import * as url from 'url';
|
|
20
19
|
import { injectable, inject, named } from '@theia/core/shared/inversify';
|
|
21
20
|
import { json } from 'body-parser';
|
|
22
|
-
import { Application, Router } from '@theia/core/shared/express';
|
|
21
|
+
import { Application, Router, RequestHandler } from '@theia/core/shared/express';
|
|
23
22
|
import { BackendApplicationContribution } from '@theia/core/lib/node/backend-application';
|
|
23
|
+
import { HttpConnectionValidator } from '@theia/core/lib/node';
|
|
24
24
|
import { FileUri } from '@theia/core/lib/common/file-uri';
|
|
25
25
|
import { FileDownloadHandler } from './file-download-handler';
|
|
26
26
|
|
|
@@ -41,16 +41,21 @@ export class FileDownloadEndpoint implements BackendApplicationContribution {
|
|
|
41
41
|
@named(FileDownloadHandler.DOWNLOAD_LINK)
|
|
42
42
|
protected readonly downloadLinkHandler: FileDownloadHandler;
|
|
43
43
|
|
|
44
|
+
@inject(HttpConnectionValidator)
|
|
45
|
+
protected readonly connectionValidator: HttpConnectionValidator;
|
|
46
|
+
|
|
44
47
|
configure(app: Application): void {
|
|
48
|
+
// Reject unauthenticated/cross-origin requests to the file read endpoints.
|
|
49
|
+
const guard: RequestHandler = (request, response, next) => this.connectionValidator.validateRequest(request, response, next);
|
|
45
50
|
const router = Router();
|
|
46
51
|
router.get('/download', (request, response) => this.downloadLinkHandler.handle(request, response));
|
|
47
52
|
router.get('/', (request, response) => this.singleFileDownloadHandler.handle(request, response));
|
|
48
53
|
router.put('/', (request, response) => this.multiFileDownloadHandler.handle(request, response));
|
|
49
54
|
// Content-Type: application/json
|
|
50
55
|
app.use(json());
|
|
51
|
-
app.use(FileDownloadEndpoint.PATH, router);
|
|
52
|
-
app.get('/file', (request, response) => {
|
|
53
|
-
const uri =
|
|
56
|
+
app.use(FileDownloadEndpoint.PATH, guard, router);
|
|
57
|
+
app.get('/file', guard, (request, response) => {
|
|
58
|
+
const uri = new URL(request.url, 'http://localhost').search.slice(1);
|
|
54
59
|
if (!uri) {
|
|
55
60
|
response.status(400).send('invalid uri');
|
|
56
61
|
return;
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
// *****************************************************************************
|
|
2
|
+
// Copyright (C) 2026 EclipseSource GmbH and others.
|
|
3
|
+
//
|
|
4
|
+
// This program and the accompanying materials are made available under the
|
|
5
|
+
// terms of the Eclipse Public License v. 2.0 which is available at
|
|
6
|
+
// http://www.eclipse.org/legal/epl-2.0.
|
|
7
|
+
//
|
|
8
|
+
// This Source Code may also be made available under the following Secondary
|
|
9
|
+
// Licenses when the conditions for such availability set forth in the Eclipse
|
|
10
|
+
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
|
|
11
|
+
// with the GNU Classpath Exception which is available at
|
|
12
|
+
// https://www.gnu.org/software/classpath/license.html.
|
|
13
|
+
//
|
|
14
|
+
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
|
|
15
|
+
// *****************************************************************************
|
|
16
|
+
|
|
17
|
+
import { expect } from 'chai';
|
|
18
|
+
import * as os from 'os';
|
|
19
|
+
import * as path from 'path';
|
|
20
|
+
import * as fs from '@theia/core/shared/fs-extra';
|
|
21
|
+
import { FileUri } from '@theia/core/lib/common/file-uri';
|
|
22
|
+
import { NodeFileUploadService } from './node-file-upload-service';
|
|
23
|
+
|
|
24
|
+
/** Captures what the upload handler wrote to the Express response. */
|
|
25
|
+
interface CapturedResponse {
|
|
26
|
+
statusCode?: number;
|
|
27
|
+
sentStatus?: number;
|
|
28
|
+
body?: unknown;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
describe('NodeFileUploadService', () => {
|
|
32
|
+
|
|
33
|
+
let tempDir: string;
|
|
34
|
+
|
|
35
|
+
function createService(): NodeFileUploadService {
|
|
36
|
+
const service = new NodeFileUploadService();
|
|
37
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
38
|
+
(service as any)['logger'] = { error: () => { } };
|
|
39
|
+
return service;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function createResponse(): { res: unknown, captured: CapturedResponse } {
|
|
43
|
+
const captured: CapturedResponse = {};
|
|
44
|
+
const res = {
|
|
45
|
+
status(code: number): unknown {
|
|
46
|
+
captured.statusCode = code;
|
|
47
|
+
return res;
|
|
48
|
+
},
|
|
49
|
+
send(body: unknown): void {
|
|
50
|
+
captured.body = body;
|
|
51
|
+
},
|
|
52
|
+
sendStatus(code: number): void {
|
|
53
|
+
captured.sentStatus = code;
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
return { res, captured };
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
async function handleUpload(filePath: string | undefined, body: unknown): Promise<CapturedResponse> {
|
|
60
|
+
const service = createService();
|
|
61
|
+
const { res, captured } = createResponse();
|
|
62
|
+
const request = { file: filePath === undefined ? undefined : { path: filePath }, body };
|
|
63
|
+
// handleFileUpload is protected; invoke it directly to exercise the input validation.
|
|
64
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
65
|
+
await (service as any).handleFileUpload(request, res);
|
|
66
|
+
return captured;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
beforeEach(async () => {
|
|
70
|
+
tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'theia-upload-spec-'));
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
afterEach(async () => {
|
|
74
|
+
await fs.remove(tempDir);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('should reject a non-file: URI with 400', async () => {
|
|
78
|
+
const source = path.join(tempDir, 'source.txt');
|
|
79
|
+
await fs.writeFile(source, 'payload');
|
|
80
|
+
const captured = await handleUpload(source, { uri: 'http://evil.example/usr/bin/bash' });
|
|
81
|
+
expect(captured.sentStatus).to.equal(400);
|
|
82
|
+
// The scoped-write guard must reject before any file operation runs.
|
|
83
|
+
expect(await fs.pathExists(source)).to.be.true;
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it('should reject a remote-scheme URI with 400', async () => {
|
|
87
|
+
const source = path.join(tempDir, 'source.txt');
|
|
88
|
+
await fs.writeFile(source, 'payload');
|
|
89
|
+
const captured = await handleUpload(source, { uri: 'vscode-remote://host/usr/bin/bash' });
|
|
90
|
+
expect(captured.sentStatus).to.equal(400);
|
|
91
|
+
expect(await fs.pathExists(source)).to.be.true;
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it('should reject a missing uri field with 400', async () => {
|
|
95
|
+
const source = path.join(tempDir, 'source.txt');
|
|
96
|
+
await fs.writeFile(source, 'payload');
|
|
97
|
+
const captured = await handleUpload(source, {});
|
|
98
|
+
expect(captured.sentStatus).to.equal(400);
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it('should reject a missing file with 400', async () => {
|
|
102
|
+
const target = FileUri.create(path.join(tempDir, 'target.txt')).toString();
|
|
103
|
+
const captured = await handleUpload(undefined, { uri: target });
|
|
104
|
+
expect(captured.sentStatus).to.equal(400);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it('should accept a file: URI and move the upload to the target', async () => {
|
|
108
|
+
const source = path.join(tempDir, 'source.txt');
|
|
109
|
+
await fs.writeFile(source, 'payload');
|
|
110
|
+
const targetPath = path.join(tempDir, 'target.txt');
|
|
111
|
+
const target = FileUri.create(targetPath).toString();
|
|
112
|
+
|
|
113
|
+
const captured = await handleUpload(source, { uri: target });
|
|
114
|
+
|
|
115
|
+
expect(captured.sentStatus).to.be.undefined;
|
|
116
|
+
expect(captured.statusCode).to.equal(200);
|
|
117
|
+
// Compare against the canonical fs-path of the URI: on Windows the create/fsPath round-trip
|
|
118
|
+
// normalizes the drive letter to lower-case (e.g. `C:` -> `c:`), which would otherwise fail
|
|
119
|
+
// an exact string match against `targetPath` derived from `os.tmpdir()`.
|
|
120
|
+
expect(captured.body).to.equal(FileUri.fsPath(target));
|
|
121
|
+
expect(await fs.pathExists(targetPath)).to.be.true;
|
|
122
|
+
expect(await fs.readFile(targetPath, 'utf8')).to.equal('payload');
|
|
123
|
+
// The temp source file was moved, not copied.
|
|
124
|
+
expect(await fs.pathExists(source)).to.be.false;
|
|
125
|
+
});
|
|
126
|
+
});
|
|
@@ -19,10 +19,10 @@ import path = require('path');
|
|
|
19
19
|
import os = require('os');
|
|
20
20
|
import express = require('@theia/core/shared/express');
|
|
21
21
|
import fs = require('@theia/core/shared/fs-extra');
|
|
22
|
-
import { BackendApplicationContribution, FileUri } from '@theia/core/lib/node';
|
|
22
|
+
import { BackendApplicationContribution, FileUri, HttpConnectionValidator } from '@theia/core/lib/node';
|
|
23
23
|
import { injectable, inject, named } from '@theia/core/shared/inversify';
|
|
24
24
|
import { HTTP_FILE_UPLOAD_PATH } from '../../common/file-upload';
|
|
25
|
-
import { ILogger } from '@theia/core';
|
|
25
|
+
import { ILogger, URI } from '@theia/core';
|
|
26
26
|
|
|
27
27
|
@injectable()
|
|
28
28
|
export class NodeFileUploadService implements BackendApplicationContribution {
|
|
@@ -30,6 +30,9 @@ export class NodeFileUploadService implements BackendApplicationContribution {
|
|
|
30
30
|
@inject(ILogger) @named('filesystem:NodeFileUploadService')
|
|
31
31
|
protected readonly logger: ILogger;
|
|
32
32
|
|
|
33
|
+
@inject(HttpConnectionValidator)
|
|
34
|
+
protected readonly connectionValidator: HttpConnectionValidator;
|
|
35
|
+
|
|
33
36
|
private static readonly UPLOAD_DIR = 'theia_upload';
|
|
34
37
|
|
|
35
38
|
async configure(app: express.Application): Promise<void> {
|
|
@@ -39,8 +42,11 @@ export class NodeFileUploadService implements BackendApplicationContribution {
|
|
|
39
42
|
]);
|
|
40
43
|
this.logger.debug(`HTTP file upload URL path: ${http_path}`);
|
|
41
44
|
this.logger.debug(`Backend file upload cache path: ${dest}`);
|
|
45
|
+
// Reject unauthenticated/cross-origin requests before `multer` writes any temp file.
|
|
46
|
+
const guard: express.RequestHandler = (request, response, next) => this.connectionValidator.validateRequest(request, response, next);
|
|
42
47
|
app.post(
|
|
43
48
|
http_path,
|
|
49
|
+
guard,
|
|
44
50
|
// `multer` handles `multipart/form-data` containing our file to upload.
|
|
45
51
|
multer({ dest }).single('file'),
|
|
46
52
|
(request, response, next) => this.handleFileUpload(request, response)
|
|
@@ -63,7 +69,7 @@ export class NodeFileUploadService implements BackendApplicationContribution {
|
|
|
63
69
|
|
|
64
70
|
protected async handleFileUpload(request: express.Request, response: express.Response): Promise<void> {
|
|
65
71
|
const fields = request.body;
|
|
66
|
-
if (!request.file || typeof fields !== 'object' || typeof fields.uri !== 'string') {
|
|
72
|
+
if (!request.file || typeof fields !== 'object' || typeof fields.uri !== 'string' || new URI(fields.uri).scheme !== 'file') {
|
|
67
73
|
response.sendStatus(400); // bad request
|
|
68
74
|
return;
|
|
69
75
|
}
|