Vydané zdroje
Stáhnout zdroje ZIP
CH-J Server Manager
Procházejte adresáře a soubory konkrétního vydání aplikace.
Zdroje jsou zveřejněny pod CH-J Proprietary Software License 1.14. Jejich dostupnost nemění licenční podmínky ani neposkytuje další oprávnění.
1
"use strict";3
const test = require("node:test");4
const assert = require("node:assert/strict");5
const fs = require("node:fs");6
const os = require("node:os");7
const path = require("node:path");8
const zlib = require("node:zlib");9
const AdmZip = require("adm-zip");10
const { MAX_TEXT_BYTES, RemoteFileService, normalizeRemotePath, safeEntryName } = require("../src/main/files/remoteFileService");12
function attrs({ directory = false, size = 0, mtime = 0 } = {}) {13
return { size, mtime, mode: directory ? 0o40755 : 0o100644, isDirectory: () => directory, isSymbolicLink: () => false };14
}16
function 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
};34
}36
test("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
});43
test("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
});53
test("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 archivu: 5ac91caf4fa32a6fdb114f2430deed486fbe7489d5eea343d1f034169fafb5e0