Skip to content

Commit 976c0c8

Browse files
authored
Update to version v3.3.1 (#216)
1 parent 8a3d6ca commit 976c0c8

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+2069
-1002
lines changed

CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,16 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [3.3.1] - 2024-10-02
9+
10+
### Security
11+
12+
- `rollup` to mitigate [CVE-2024-4067](https://github.com/advisories/GHSA-952p-6rrq-rcjv)
13+
14+
### Fixed
15+
16+
- Fixed API response to 404 NOT_FOUND from 400 BAD_REQUEST for when retrieving/deleting an invalid test
17+
818
## [3.3.0] - 2024-09-16
919

1020
### Added

NOTICE

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,22 +17,33 @@ This software includes third party software subject to the following copyrights:
1717
@aws-amplify/ui-react under the Apache License 2.0
1818
@aws-cdk/assert under the Apache License 2.0
1919
@aws-solutions-constructs/aws-cloudfront-s3 under the Apache License 2.0
20+
@aws-solutions-constructs/aws-eventbridge-lambda under the Apache License 2.0
21+
@aws-solutions-constructs/aws-lambda-sqs-lambda under the Apache License 2.0
22+
@aws-sdk/client-s3 under the Apache License 2.0
23+
@aws-sdk/client-cloudwatch under the Apache License 2.0
24+
@aws-sdk/client-cloudwatch-logs under the Apache License 2.0
25+
@aws-sdk/client-sqs under the Apache License 2.0
2026
@types/jest under the Massachusetts Institute of Technology (MIT) License
2127
@types/node under the Massachusetts Institute of Technology (MIT) License
28+
@types/aws-lambda under the Massachusetts Institute of Technology (MIT) License
2229
@typescript-eslint/eslint-plugin under the Massachusetts Institute of Technology (MIT) license
2330
@typescript-eslint/parser under the BSD 2-Clause license
2431
aws-amplify under the Apache License Version 2.0
2532
aws-cdk under the Apache License Version 2.0
2633
aws-cdk-lib under the Apache License 2.0
2734
aws-sdk under the Apache License Version 2.0
35+
aws4-axios under the Massachusetts Institute of Technology (MIT) license
2836
axios under the Massachusetts Institute of Technology (MIT) license
2937
axios-mock-adapter under the Massachusetts Institute of Technology (MIT) license
38+
ajv under the Massachusetts Institute of Technology (MIT) license
3039
bootstrap under the Massachusetts Institute of Technology (MIT) license
3140
bootstrap-icons under the Massachusetts Institute of Technology (MIT) license
3241
brace under the Massachusetts Institute of Technology (MIT) license
3342
chart.js under the Massachusetts Institute of Technology (MIT) license
3443
chartjs-adapter-date-fns under the Massachusetts Institute of Technology (MIT) license
3544
constructs under the Apache License 2.0
45+
cypress under the Massachusetts Institute of Technology (MIT) license
46+
esbuild under the Massachusetts Institute of Technology (MIT) license
3647
eslint under the Massachusetts Institute of Technology (MIT) license
3748
eslint-config-prettier under the Massachusetts Institute of Technology (MIT) license
3849
eslint-config-standard under the Massachusetts Institute of Technology (MIT) license

VERSION.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
3.3.0
1+
3.3.1

source/api-services/index.js

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,11 @@ class APIHandler {
1111
constructor(resource, method) {
1212
this.resource = resource;
1313
this.method = method;
14-
this.errorMsg = `Method: ${method} not supported for this resource: ${resource} `;
14+
this.errorMsg = new scenarios.ErrorException(
15+
"METHOD_NOT_ALLOWED",
16+
`Method: ${method} not supported for this resource: ${resource}`,
17+
scenarios.StatusCodes.NOT_ALLOWED
18+
);
1519
}
1620
async getRegions() {
1721
let data = { regions: await scenarios.getAllRegionConfigs() };
@@ -21,7 +25,7 @@ class APIHandler {
2125
// Handle the /regions endpoint
2226
async handleRegions() {
2327
if (this.method === "GET") return this.getRegions();
24-
throw new Error(this.errorMsg);
28+
throw this.errorMsg;
2529
}
2630

2731
// Handle the /scenarios endpoint
@@ -63,7 +67,7 @@ class APIHandler {
6367
}
6468
return data;
6569
default:
66-
throw new Error(this.errorMsg);
70+
throw this.errorMsg;
6771
}
6872
}
6973

@@ -77,22 +81,23 @@ class APIHandler {
7781
case "DELETE":
7882
return scenarios.deleteTest(testId, functionName);
7983
default:
80-
throw new Error(this.errorMsg);
84+
throw this.errorMsg;
8185
}
8286
}
8387

8488
// Handle the /tasks endpoint
8589
async handleTasks() {
8690
if (this.method === "GET") return scenarios.listTasks();
87-
throw new Error(this.errorMsg);
91+
throw this.errorMsg;
8892
}
8993

9094
// Handle the /vCPUDetails endpoint
9195
async handleVCPUDetails() {
9296
if (this.method === "GET") return scenarios.getAccountFargatevCPUDetails();
93-
throw new Error(this.errMsg);
97+
throw this.errorMsg;
9498
}
9599
}
100+
96101
// Helper function to handle API response
97102
const createResponse = (data, statusCode) => ({
98103
headers: {
@@ -138,13 +143,13 @@ exports.handler = async (event, context) => {
138143
data = await apiHandler.handleVCPUDetails();
139144
break;
140145
default:
141-
throw new Error(apiHandler.errorMsg);
146+
throw apiHandler.errorMsg;
142147
}
143148

144149
response = createResponse(data, 200);
145150
} catch (err) {
146151
console.error(err);
147-
response = createResponse(err.toString(), 400);
152+
response = createResponse(err.toString(), err.statusCode || scenarios.StatusCodes.BAD_REQUEST);
148153
}
149154

150155
console.log(response);

source/api-services/lib/scenarios/index.js

Lines changed: 51 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,28 @@ const stepFunctions = new AWS.StepFunctions(options);
1616
const cloudwatchevents = new AWS.CloudWatchEvents(options);
1717
const cloudformation = new AWS.CloudFormation(options);
1818

19+
const StatusCodes = {
20+
OK: 200,
21+
BAD_REQUEST: 400,
22+
FORBIDDEN: 403,
23+
NOT_FOUND: 404,
24+
NOT_ALLOWED: 405,
25+
REQUEST_TOO_LONG: 413,
26+
INTERNAL_SERVER_ERROR: 500,
27+
TIMEOUT: 503,
28+
};
29+
1930
/**
2031
* Class to throw errors
2132
* @param {string} code
2233
* @param {string} errMsg
2334
*/
2435
class ErrorException extends Error {
25-
constructor(code, errMsg) {
26-
super(errMsg);
36+
constructor(code, errMsg, statusCode = StatusCodes.BAD_REQUEST) {
37+
super(statusCode, code, errMsg);
2738
this.code = code;
2839
this.message = errMsg;
40+
this.statusCode = statusCode;
2941
}
3042
toString() {
3143
return `${this.code}: ${this.message}`;
@@ -143,6 +155,7 @@ const getRegionInfraConfigs = async (testRegion) => {
143155
const getTestAndRegionConfigs = async (testId) => {
144156
try {
145157
const testEntry = await getTestEntry(testId);
158+
if (!testEntry) throw new ErrorException("TEST_NOT_FOUND", `testId '${testId}' not found`, StatusCodes.NOT_FOUND);
146159
if (testEntry.testTaskConfigs) {
147160
for (let testRegionSettings of testEntry.testTaskConfigs) {
148161
const regionInfraConfig = await getRegionInfraConfigs(testRegionSettings.region);
@@ -272,6 +285,8 @@ const convertLinuxCronToAwsCron = (linuxCron, cronExpiryDate) => {
272285
const checkEnoughIntervalDiff = (cronValue, cronExpiryDate, holdFor, rampUp, testTaskConfigs) => {
273286
if (!holdFor || !rampUp) return "";
274287
let cronExpiry = new Date(cronExpiryDate);
288+
const parts = cronValue.trim().split(" ");
289+
if (parts.length !== 5) throw new ErrorException("Invalid Linux cron expression", "Expected format: 0 * * * *");
275290

276291
let cronInterval;
277292
try {
@@ -396,6 +411,26 @@ const removeRules = async (testId, functionName, recurrence) => {
396411
}
397412
};
398413

414+
const isValidTimeString = (timeString) => {
415+
const timeRegex = /^([01]\d|2[0-3]):([0-5]\d)$/;
416+
if (!timeRegex.test(timeString))
417+
throw new ErrorException("InvalidParameter", "Invalid time format. Expected format: HH:MM");
418+
};
419+
420+
const isValidDateString = (dateString) => {
421+
// Check if the dateString is in the format YYYY-MM-DD
422+
const dateRegex = /^(\d{4})-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$/;
423+
424+
if (!dateRegex.test(dateString))
425+
throw new ErrorException("InvalidParameter", "Invalid date format. Expected format: YYYY-MM-DD");
426+
};
427+
428+
const isValidDate = (date) => {
429+
const today = new Date();
430+
today.setHours(0, 0, 0, 0);
431+
432+
if (date < today) throw new ErrorException("InvalidParameter", "Date cannot be in the past");
433+
};
399434
/**
400435
* Schedules test and returns a consolidated list of test scenarios
401436
* @param {object} event test event information
@@ -459,7 +494,10 @@ const scheduleTest = async (event, context) => {
459494
config.scheduleTime = scheduleTime;
460495
config.scheduleDate = scheduleDate;
461496
} else {
497+
isValidTimeString(scheduleTime);
498+
isValidDateString(scheduleDate);
462499
createRun = new Date(year, parseInt(month, 10) - 1, day, hour, minute);
500+
isValidDate(createRun);
463501
} // Schedule for 1 min prior to account for time it takes to create rule
464502
// getMonth() returns Jan with index Zero that is why months need a +1
465503
// refrence https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getMonth
@@ -1377,6 +1415,15 @@ const cancelTest = async (testId) => {
13771415

13781416
try {
13791417
// Get test and regional infrastructure configuration
1418+
const listTestsRes = await listTests();
1419+
const allTests = listTestsRes.Items;
1420+
1421+
// Check if the testId exists in the list of tests
1422+
const testExists = allTests.some((test) => test.testId === testId);
1423+
if (!testExists) {
1424+
throw new ErrorException("TEST_NOT_FOUND", `testId '${testId}' not found`, StatusCodes.NOT_FOUND);
1425+
}
1426+
13801427
const testAndRegionalInfraConfigs = await getTestAndRegionConfigs(testId);
13811428
if (testAndRegionalInfraConfigs.testTaskConfigs) {
13821429
for (const regionalConfig of testAndRegionalInfraConfigs.testTaskConfigs) {
@@ -1689,4 +1736,6 @@ module.exports = {
16891736
getCFUrl: getCFUrl,
16901737
getAccountFargatevCPUDetails: getAccountFargatevCPUDetails,
16911738
getTestDurationSeconds: getTestDurationSeconds,
1739+
ErrorException: ErrorException,
1740+
StatusCodes: StatusCodes,
16921741
};

source/api-services/lib/scenarios/index.spec.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2175,6 +2175,12 @@ describe("#SCENARIOS API:: ", () => {
21752175
});
21762176

21772177
it('should return SUCCESS when "CANCELTEST" finds running tasks and returns success', async () => {
2178+
mockDynamoDB.mockImplementationOnce(() => ({
2179+
promise() {
2180+
// scan
2181+
return Promise.resolve(listData);
2182+
},
2183+
}));
21782184
mockDynamoDB.mockImplementationOnce(() => ({
21792185
promise() {
21802186
// get

source/api-services/package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

source/api-services/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "api-services",
3-
"version": "3.3.0",
3+
"version": "3.3.1",
44
"description": "REST API micro services",
55
"repository": {
66
"type": "git",

source/console/package-lock.json

Lines changed: 5 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

source/console/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "distributed-load-testing-on-aws-ui",
3-
"version": "3.3.0",
3+
"version": "3.3.1",
44
"private": true,
55
"license": "Apache-2.0",
66
"author": {

source/console/src/Components/Create/Create.js

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,6 @@ import {
3131
NavLink,
3232
TabContent,
3333
TabPane,
34-
Modal,
35-
ModalBody,
36-
ModalFooter,
3734
} from "reactstrap";
3835
import "brace/theme/github";
3936
import { generateUniqueId } from "solution-utils";
@@ -335,7 +332,7 @@ class Create extends React.Component {
335332
}
336333
try {
337334
this.setState({ isUploading: true });
338-
await uploadData({ key: `test-scenarios/${testType}/${filename}`, data: file }).result;
335+
await uploadData({ path: `public/test-scenarios/${testType}/${filename}`, data: file }).result;
339336
console.log("Script uploaded successfully");
340337
} catch (error) {
341338
console.error("Error", error);
@@ -766,9 +763,7 @@ class Create extends React.Component {
766763

767764
const nextSixRuns = this.nextSixRuns();
768765
let fields = JSON.parse(JSON.stringify(interval.fields));
769-
if (fields.minute.length !== 1 && nextSixRuns && nextSixRuns.length > 1) return true;
770-
771-
return false;
766+
return fields.minute.length !== 1 && nextSixRuns && nextSixRuns.length > 1;
772767
};
773768

774769
checkEnoughIntervalDiff = () => {

source/console/src/Components/Create/Create.spec.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ describe("Functions Testing", () => {
150150
let createInstance = new Create(commonProps);
151151
createInstance.state = { file: { type: initialFileType === "zip" ? "application/zip" : "text/plain" } };
152152
await createInstance.uploadFileToScenarioBucket("test");
153-
expect(uploadData).toHaveBeenCalledTimes(1);
153+
expect(uploadData).toHaveBeenCalledTimes(1); // NOSONAR
154154
});
155155
});
156156
});

source/console/src/Components/Details/Details.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ class Details extends React.Component {
145145
const extension = scriptExtensions[testType];
146146

147147
let filename = this.state.data.fileType === "zip" ? `${testId}.zip` : `${testId}.${extension}`;
148-
const url = await getUrl({ key: `test-scenarios/${testType}/${filename}` });
148+
const url = await getUrl({ path: `public/test-scenarios/${testType}/${filename}` });
149149
window.open(url.url, "_blank");
150150
} catch (error) {
151151
console.error("Error", error);

source/console/src/Components/Results/Results.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ class Results extends React.Component {
101101
*/
102102
retrieveImage = async (metricS3ImageLocation) => {
103103
try {
104-
const { body } = await downloadData({ key: metricS3ImageLocation }).result;
104+
const { body } = await downloadData({ path: `public/${metricS3ImageLocation}` }).result;
105105
const imageBodyText = await body.text();
106106
this.setState({ metricImage: imageBodyText });
107107
} catch (error) {

source/custom-resource/package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

source/custom-resource/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "custom-resource",
3-
"version": "3.3.0",
3+
"version": "3.3.1",
44
"description": "cfn custom resources for distributed load testing on AWS workflow",
55
"repository": {
66
"type": "git",

0 commit comments

Comments
 (0)