Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 79 additions & 0 deletions src/RokuDeploy.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -828,6 +828,41 @@ describe('RokuDeploy', () => {

expect(copyPaths[0].dest).to.equal(s`${stagingDir}`);
});

it('handles dest with leading slash without escaping staging dir', async () => {
const copyPaths = [] as Array<{ src: string; dest: string }>;
sinon.stub(rokuDeploy.fsExtra, 'ensureDir').returns(Promise.resolve() as any);
sinon.stub(rokuDeploy.fsExtra as any, 'copy').callsFake((src, dest) => {
copyPaths.push({ src: src as string, dest: dest as string });
return Promise.resolve();
});
sinon.stub(rokuDeploy, 'getFilePaths').returns(
Promise.resolve([{
src: s`${rootDir}/source/main.brs`,
dest: '/source/main.brs'
}])
);

await rokuDeploy['copyToStaging']([], stagingDir, rootDir);

expect(copyPaths[0].dest).to.equal(s`${stagingDir}/source/main.brs`);
});

it('full pipeline with leading-slash dest copies to staging correctly', async () => {
writeFiles(rootDir, ['source/main.brs', 'components/comp.xml']);

await rokuDeploy['copyToStaging'](
[
{ src: 'source/**/*', dest: '/source' },
{ src: 'components/**/*', dest: '/components' }
],
stagingDir,
rootDir
);

expectPathExists(s`${stagingDir}/source/main.brs`);
expectPathExists(s`${stagingDir}/components/comp.xml`);
});
});

describe('zipPackage', () => {
Expand Down Expand Up @@ -3458,6 +3493,50 @@ describe('RokuDeploy', () => {
const zipPaths = Object.keys(zip.files);
expect(zipPaths.every(p => !path.isAbsolute(p))).to.be.true;
});

it('handles dest with leading slash producing relative zip entries', async () => {
writeFiles(stagingDir, ['source/main.brs']);
const outputZipPath = s`${tempDir}/output.zip`;
await rokuDeploy.zipFolder(stagingDir, outputZipPath, null, [{
src: s`${stagingDir}/source/main.brs`,
dest: '/source/main.brs'
}]);
const zip = await JSZip.loadAsync(fsExtra.readFileSync(outputZipPath) as any);
expect(zip.file('source/main.brs')).to.exist;
const zipPaths = Object.keys(zip.files);
expect(zipPaths.every(p => !path.isAbsolute(p))).to.be.true;
});

it('makes absolute dest under srcFolder relative in zip entry', async () => {
writeFiles(stagingDir, ['source/main.brs']);
const outputZipPath = s`${tempDir}/output.zip`;
const absoluteDest = s`${stagingDir}/source/main.brs`;
sinon.stub(rokuDeploy, 'getFilePaths').returns(
Promise.resolve([{
src: s`${stagingDir}/source/main.brs`,
dest: absoluteDest
}])
);
await rokuDeploy.zipFolder(stagingDir, outputZipPath);
const zip = await JSZip.loadAsync(fsExtra.readFileSync(outputZipPath) as any);
expect(zip.file('source/main.brs')).to.exist;
});

it('strips leading slash from absolute dest not under srcFolder in zip entry', async () => {
writeFiles(stagingDir, ['source/main.brs']);
const outputZipPath = s`${tempDir}/output.zip`;
sinon.stub(rokuDeploy, 'getFilePaths').returns(
Promise.resolve([{
src: s`${stagingDir}/source/main.brs`,
dest: '/source/main.brs'
}])
);
await rokuDeploy.zipFolder(stagingDir, outputZipPath);
const zip = await JSZip.loadAsync(fsExtra.readFileSync(outputZipPath) as any);
expect(zip.file('source/main.brs')).to.exist;
const zipPaths = Object.keys(zip.files);
expect(zipPaths.every(p => !path.isAbsolute(p))).to.be.true;
});
});

describe('getDestPath', () => {
Expand Down
27 changes: 23 additions & 4 deletions src/RokuDeploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -357,9 +357,16 @@ export class RokuDeploy {
let fileObjects = await this.getFilePaths(files, rootDir);
//copy all of the files
await Promise.all(fileObjects.map(async (fileObject) => {
let destFilePath = util.standardizePath(
path.resolve(stagingPath, fileObject.dest ?? '')
);
let dest = fileObject.dest ?? '';
let destFilePath: string;
if (path.isAbsolute(dest) && util.isParentOfPath(stagingPath, dest)) {
//dest is already an absolute path under stagingPath — use as-is
destFilePath = util.standardizePath(dest);
} else {
//strip leading slashes so path.join doesn't treat dest as absolute
dest = dest.replace(/^[\/\\]+/, '');
destFilePath = util.standardizePath(path.join(stagingPath, dest));
}

//make sure the containing folder exists
await this.fsExtra.ensureDir(path.dirname(destFilePath));
Expand Down Expand Up @@ -1356,7 +1363,19 @@ export class RokuDeploy {
if (ext === '.jpg' || ext === '.png' || ext === '.jpeg') {
compression = 'STORE';
}
zip.file(path.relative(srcFolder, path.resolve(srcFolder, file.dest)).replace(/[\\/]/g, '/'), data as any, {
let destForZip: string;
if (path.isAbsolute(file.dest)) {
if (util.isParentOfPath(srcFolder, file.dest)) {
//absolute path under srcFolder — make it relative
destForZip = path.relative(srcFolder, file.dest);
} else {
//absolute path not under srcFolder — strip leading slashes
destForZip = file.dest.replace(/^[\/\\]+/, '');
}
} else {
destForZip = file.dest;
}
zip.file(destForZip.replace(/[\\/]/g, '/'), data as any, {
compression: compression
});
});
Expand Down