CH-J Server ManagerSerververwaltung über SSH
Menü
Veröffentlichte Quellen

CH-J Server Manager

Durchsuchen Sie Verzeichnisse und Dateien einer bestimmten Anwendungsausgabe.

Quellen als ZIP herunterladen
CH-J Proprietary Software License 1.14

Die Quellen werden unter der CH-J Proprietary Software License 1.14 bereitgestellt. Ihre Verfügbarkeit ändert die Lizenzbedingungen nicht und gewährt keine zusätzlichen Rechte.

4,8 KB · 76 ZeilenDatei herunterladen
1"use strict";
3const test = require("node:test");
4const assert = require("node:assert/strict");
5const fs = require("node:fs");
6const os = require("node:os");
7const path = require("node:path");
8const zlib = require("node:zlib");
9const AdmZip = require("adm-zip");
10const { MAX_TEXT_BYTES, RemoteFileService, normalizeRemotePath, safeEntryName } = require("../src/main/files/remoteFileService");
12function attrs({ directory = false, size = 0, mtime = 0 } = {}) {
13 return { size, mtime, mode: directory ? 0o40755 : 0o100644, isDirectory: () => directory, isSymbolicLink: () => false };
16function fakeSftp() {
17 const files = new Map([["/home/test/a.txt", Buffer.from("ahoj")]]);
18 const calls = [];
19 return {
20 calls, files,
21 readdir(_path, callback) { callback(null, [{ filename: "a.txt", attrs: attrs({ size: 4, mtime: 100 }) }, { filename: "folder", attrs: attrs({ directory: true }) }]); },
22 stat(target, callback) { callback(null, attrs({ size: files.get(target)?.length || 0, mtime: 100 })); },
23 lstat(target, callback) { callback(null, attrs({ directory: target.endsWith("folder") })); },
24 readFile(target, callback) { callback(null, files.get(target)); },
25 writeFile(target, data, options, callback) { calls.push(["writeFile", target, options.mode]); files.set(target, Buffer.from(data)); callback(null); },
26 rename(from, to, callback) { calls.push(["rename", from, to]); files.set(to, files.get(from)); files.delete(from); callback(null); },
27 unlink(target, callback) { calls.push(["unlink", target]); files.delete(target); callback(null); },
28 rmdir(target, callback) { calls.push(["rmdir", target]); callback(null); },
29 mkdir(target, options, callback) { calls.push(["mkdir", target, options.mode]); callback(null); },
30 fastPut(localPath, target, callback) { calls.push(["fastPut", localPath, target]); files.set(target, fs.readFileSync(localPath)); callback(null); },
31 fastGet(target, localPath, callback) { calls.push(["fastGet", target, localPath]); fs.writeFileSync(localPath, files.get(target)); callback(null); },
32 end() { calls.push(["end"]); }
33 };
36test("remote file service lists remote files", async () => {
37 const sftp = fakeSftp();
38 const service = new RemoteFileService({ sessionManager: { openSftp: async () => sftp } });
39 const entries = await service.list("session-1", "/home/test");
40 assert.deepEqual(entries.map((entry) => [entry.name, entry.type]), [["folder", "directory"], ["a.txt", "file"]]);
41});
43test("remote file service rejects unsafe paths, root removal and oversized editor content", async () => {
44 const sftp = fakeSftp();
45 const service = new RemoteFileService({ sessionManager: { openSftp: async () => sftp } });
46 assert.equal(normalizeRemotePath("/home/test/../test/a.txt"), "/home/test/a.txt");
47 assert.throws(() => normalizeRemotePath("relative/file"), /absolute/);
48 assert.throws(() => safeEntryName("folder/file"), /safely/);
49 await assert.rejects(() => service.remove("session-1", "/"), /root/);
50 await assert.rejects(() => service.writeText("session-1", "/home/test/a.txt", "x".repeat(MAX_TEXT_BYTES + 1)), /limit/);
51});
53test("remote file service uploads batches atomically and exports zip, tar and tar.gz archives", async (t) => {
54 const root = fs.mkdtempSync(path.join(os.tmpdir(), "chj-remote-files-"));
55 t.after(() => fs.rmSync(root, { recursive: true, force: true }));
56 const localUpload = path.join(root, "upload.txt"); fs.writeFileSync(localUpload, "upload-data");
57 const archivePaths = { zip: path.join(root, "download.zip"), tar: path.join(root, "download.tar"), "tar.gz": path.join(root, "download.tar.gz") }; const sftp = fakeSftp();
58 const service = new RemoteFileService({
59 sessionManager: { openSftp: async () => sftp },
60 selectUploadPath: async () => ({ canceled: false, paths: [localUpload] }),
61 selectArchivePath: async (_suggestedName, format) => ({ canceled: false, path: archivePaths[format] })
62 });
64 const upload = await service.upload("session-1", "/home/test");
65 assert.equal(upload.entries, 1); assert.equal(upload.size, 11);
66 assert.equal(sftp.files.get("/home/test/upload.txt").toString(), "upload-data");
67 assert.ok(sftp.calls.some((entry) => entry[0] === "fastPut" && entry[2].includes(".chj-upload-")));
69 const zip = await service.downloadArchive("session-1", ["/home/test/a.txt"], "zip");
70 assert.equal(zip.format, "zip"); assert.ok(zip.size > 0);
71 assert.equal(new AdmZip(archivePaths.zip).readAsText("a.txt"), "ahoj");
72 const tar = await service.downloadArchive("session-1", ["/home/test/a.txt"], "tar");
73 assert.equal(tar.format, "tar"); assert.equal(fs.readFileSync(archivePaths.tar).subarray(257, 262).toString(), "ustar");
74 const tarGz = await service.downloadArchive("session-1", ["/home/test/a.txt"], "tar.gz");
75 assert.equal(tarGz.format, "tar.gz"); assert.equal(zlib.gunzipSync(fs.readFileSync(archivePaths["tar.gz"])).subarray(257, 262).toString(), "ustar");
76});

SHA-256: 0d11e431b333cfdfc9d937750f56523d3dea6ed5f8eda1a5bfd47c647bfdea36

SHA-256 des Archivs: 5ac91caf4fa32a6fdb114f2430deed486fbe7489d5eea343d1f034169fafb5e0