Skip to content

Commit

Permalink
Merge pull request #142 from zowe/typoLoop_Fix
Browse files Browse the repository at this point in the history
fix to disallow invalid characters in filenames
  • Loading branch information
zFernand0 authored Oct 30, 2023
2 parents 86bebc5 + bb38206 commit 575e6e1
Show file tree
Hide file tree
Showing 4 changed files with 108 additions and 20 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

All notable changes to the z/OS FTP Plug-in for Zowe CLI will be documented in this file.

## Recent Changes
- BugFix: Provide new utility function that checks file names for valid characters [143](https://github.com/zowe/zowe-cli-ftp-plugin/issues/143).

## `2.1.3`

- Update example of `upload file-to-data-set` command.
Expand Down
36 changes: 35 additions & 1 deletion __tests__/__unit__/cli/download/data-set/DataSet.Handler.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,45 @@
*
*/

import { ZosFilesMessages } from "@zowe/cli";
import DownloadDataSetHandler from "../../../../../src/cli/download/data-set/DataSet.Handler";
import TestUtils from "../../TestUtils";
import { ImperativeError } from "@zowe/imperative";
import { Utilities } from "../../../../../src/cli/Utilities";

describe("Download data set handler", () => {

it ("should throw ImperativeError for invalid file name", async () => {
const handler = new DownloadDataSetHandler();
const files: any[] = [
{
Used: 100
}
];

const mockResponse = TestUtils.getMockResponse();
const mockParams: any = {
arguments: {
dataSet: "ds1",
file: "invalid.txt☻"
},
connection: {
listDataset: jest.fn().mockReturnValue(Promise.resolve(files)),
getDataset: jest.fn().mockReturnValue(Promise.resolve(TestUtils.getSingleLineStream()))
},
response: mockResponse
};

jest.spyOn(Utilities, "isValidFileName").mockReturnValueOnce(false);

try {
await handler.processFTP(mockParams);
} catch(err) {
expect(err instanceof ImperativeError).toEqual(true);
expect(err.message).toContain(ZosFilesMessages.invalidFileName.message);
}
});

it("should throw error if no data set is found.", async () => {
const handler = new DownloadDataSetHandler();
const files: any[] = [];
Expand Down Expand Up @@ -55,7 +89,7 @@ describe("Download data set handler", () => {
response: mockResponse
};
await handler.processFTP(mockParams);
expect(mockResponse.console.log.mock.calls[0][0]).toBe("Data set downloaded successfully.");
expect(mockResponse.console.log.mock.calls[0][0]).toBe(ZosFilesMessages.datasetDownloadedSuccessfully.message);
expect(mockResponse.console.log.mock.calls[0][1]).toBe("ds1");
expect(mockParams.connection.getDataset.mock.calls[0][1]).toBe("binary_rdw");
});
Expand Down
36 changes: 36 additions & 0 deletions src/cli/Utilities.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* This program and the accompanying materials are made available under the terms of the
* Eclipse Public License v2.0 which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v20.html
*
* SPDX-License-Identifier: EPL-2.0
*
* Copyright Contributors to the Zowe Project.
*
*/

export class Utilities{
/**
* Check to ensure filename only contains valid characters
* @param {fileName} string - filename to be evaluated
* @returns {Promise<boolean>} - promise that resolves to true if filename contains valid characters
* @memberof Utilities
*/
public static isValidFileName(fileName: string): boolean {
// Define valid character ranges for ISO/IEC 8859-1 https://en.wikipedia.org/wiki/ISO/IEC_8859-1
const validRanges = [
{ start: 32, end: 127 }, // First chunk of valid characters
{ start: 160, end: 255 } // Second chunk of valid characters
];

// Check if each character in the filename is within a valid range
for (const char of fileName) {
const charCode = char.charCodeAt(0);
if (!validRanges.some(range => charCode >= range.start && charCode <= range.end)) {
return false;
}
}

return true;
}
}
53 changes: 34 additions & 19 deletions src/cli/download/data-set/DataSet.Handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,33 +14,48 @@ import { FTPBaseHandler } from "../../../FTPBase.Handler";
import { IFTPHandlerParams } from "../../../IFTPHandlerParams";
import { FTPProgressHandler } from "../../../FTPProgressHandler";
import { DataSetUtils, TRANSFER_TYPE_ASCII, TRANSFER_TYPE_ASCII_RDW, TRANSFER_TYPE_BINARY, TRANSFER_TYPE_BINARY_RDW } from "../../../api";
import { ImperativeError } from "@zowe/imperative";
import { Utilities } from "../../Utilities";

export default class DownloadDataSetHandler extends FTPBaseHandler {
public async processFTP(params: IFTPHandlerParams): Promise<void> {

const file = params.arguments.file == null ?
ZosFilesUtils.getDirsFromDataSet(params.arguments.dataSet) :
params.arguments.file;
try {
// Validate the destination file name before proceeding
if (!(Utilities.isValidFileName(file))) {
throw new ImperativeError({ msg: ZosFilesMessages.invalidFileName.message });
}

let progress;
if (params.response && params.response.progress) {
progress = new FTPProgressHandler(params.response.progress, true);
}
let transferType = params.arguments.binary ? TRANSFER_TYPE_BINARY : TRANSFER_TYPE_ASCII;
if (params.arguments.rdw) {
transferType = params.arguments.binary ? TRANSFER_TYPE_BINARY_RDW : TRANSFER_TYPE_ASCII_RDW;
}
const options = {
localFile: file,
response: params.response,
transferType,
progress,
encoding: params.arguments.encoding
};
await DataSetUtils.downloadDataSet(params.connection, params.arguments.dataSet, options);

let progress;
if (params.response && params.response.progress) {
progress = new FTPProgressHandler(params.response.progress, true);
const successMsg = params.response.console.log(ZosFilesMessages.datasetDownloadedSuccessfully.message, file);
this.log.info(successMsg);
params.response.data.setMessage(successMsg);
}
let transferType = params.arguments.binary ? TRANSFER_TYPE_BINARY : TRANSFER_TYPE_ASCII;
if (params.arguments.rdw) {
transferType = params.arguments.binary ? TRANSFER_TYPE_BINARY_RDW : TRANSFER_TYPE_ASCII_RDW;
catch (e) {
if (e instanceof ImperativeError){
throw e;
}
throw new ImperativeError({
msg: `An error was encountered while trying to download your file '${file}'.\nError details: ${e.message}`
});
}
const options = {
localFile: file,
response: params.response,
transferType,
progress,
encoding: params.arguments.encoding
};
await DataSetUtils.downloadDataSet(params.connection, params.arguments.dataSet, options);

const successMsg = params.response.console.log(ZosFilesMessages.datasetDownloadedSuccessfully.message, file);
this.log.info(successMsg);
params.response.data.setMessage(successMsg);
}
}

0 comments on commit 575e6e1

Please sign in to comment.