refactor: Add lint rule for unsafe property access with lodash get/set (no-changelog) (#8587)

This commit is contained in:
Elias Meire 2024-02-08 15:32:04 +01:00 committed by GitHub
parent 8e392cfc1d
commit de6d466e5e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
22 changed files with 90 additions and 71 deletions

View file

@ -28,8 +28,8 @@ class N8nStructuredOutputParser<T extends z.ZodTypeAny> extends StructuredOutput
const parsed = (await super.parse(text)) as object;
return (
get(parsed, `${STRUCTURED_OUTPUT_KEY}.${STRUCTURED_OUTPUT_OBJECT_KEY}`) ??
get(parsed, `${STRUCTURED_OUTPUT_KEY}.${STRUCTURED_OUTPUT_ARRAY_KEY}`) ??
get(parsed, [STRUCTURED_OUTPUT_KEY, STRUCTURED_OUTPUT_OBJECT_KEY]) ??
get(parsed, [STRUCTURED_OUTPUT_KEY, STRUCTURED_OUTPUT_ARRAY_KEY]) ??
get(parsed, STRUCTURED_OUTPUT_KEY) ??
parsed
);

View file

@ -39,6 +39,9 @@ const config = (module.exports = {
/** https://github.com/sindresorhus/eslint-plugin-unicorn */
'eslint-plugin-unicorn',
/** https://github.com/wix-incubator/eslint-plugin-lodash */
'eslint-plugin-lodash',
],
extends: [
@ -458,6 +461,8 @@ const config = (module.exports = {
/** https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/no-useless-promise-resolve-reject.md */
'unicorn/no-useless-promise-resolve-reject': 'error',
'lodash/path-style': ['error', 'as-needed'],
},
overrides: [

View file

@ -13,6 +13,7 @@
"eslint-config-prettier": "^9.0.0",
"eslint-import-resolver-typescript": "^3.6.1",
"eslint-plugin-import": "^2.29.0",
"eslint-plugin-lodash": "^7.4.0",
"eslint-plugin-n8n-local-rules": "^1.0.0",
"eslint-plugin-prettier": "^5.0.1",
"eslint-plugin-unicorn": "^49.0.0",

View file

@ -2548,7 +2548,7 @@ const addExecutionDataFunctions = async (
runExecutionData.executionData!.metadata = {};
}
let sourceTaskData = get(runExecutionData, `executionData.metadata[${sourceNodeName}]`);
let sourceTaskData = get(runExecutionData, ['executionData', 'metadata', sourceNodeName]);
if (!sourceTaskData) {
runExecutionData.executionData!.metadata[sourceNodeName] = [];

View file

@ -337,10 +337,13 @@ export class WorkflowExecute {
): boolean {
// for (const inputConnection of workflow.connectionsByDestinationNode[nodeToAdd].main[0]) {
for (const inputConnection of inputConnections) {
const nodeIncomingData = get(
runData,
`[${inputConnection.node}][${runIndex}].data.main[${inputConnection.index}]`,
);
const nodeIncomingData = get(runData, [
inputConnection.node,
runIndex,
'data',
'main',
inputConnection.index,
]);
if (nodeIncomingData !== undefined && (nodeIncomingData as object[]).length !== 0) {
return false;
}

View file

@ -176,11 +176,11 @@ function optionSelected(optionName: string) {
// The "fixedCollection" entries are different as they save values
// in an object and then underneath there is an array. So initialize
// them differently.
const retrievedObjectValue = get(props.nodeValues, `${props.path}.${optionName}`, {});
const retrievedObjectValue = get(props.nodeValues, [props.path, optionName], {});
newValue = retrievedObjectValue;
} else {
// Everything else saves them directly as an array.
const retrievedArrayValue = get(props.nodeValues, `${props.path}.${optionName}`, []) as Array<
const retrievedArrayValue = get(props.nodeValues, [props.path, optionName], []) as Array<
typeof option.default
>;
if (Array.isArray(retrievedArrayValue)) {

View file

@ -297,7 +297,7 @@ export default defineComponent({
// Multiple values are allowed so append option to array
newParameterValue[optionParameter.name] = get(
this.nodeValues,
`${this.path}.${optionParameter.name}`,
[this.path, optionParameter.name],
[],
);
if (Array.isArray(optionParameter.default)) {

View file

@ -480,15 +480,15 @@ export default defineComponent({
const nodeResponseDataArray = get(
this.workflowsStore.getWorkflowExecution?.data?.resultData.runData,
`[${lastNodeExecuted}]`,
lastNodeExecuted,
) as ITaskData[];
const nodeResponseData = nodeResponseDataArray[nodeResponseDataArray.length - 1];
let responseMessage: string;
if (get(nodeResponseData, ['error'])) {
responseMessage = '[ERROR: ' + get(nodeResponseData, ['error', 'message']) + ']';
if (get(nodeResponseData, 'error')) {
responseMessage = '[ERROR: ' + get(nodeResponseData, 'error.message') + ']';
} else {
const responseData = get(nodeResponseData, 'data.main[0][0].json');
responseMessage = this.extractResponseMessage(responseData);

View file

@ -142,11 +142,8 @@ export async function awsApiRequestSOAPAllItems(
region,
);
if (get(responseData, `${propertyNameArray[0]}.${propertyNameArray[1]}.NextMarker`)) {
query.Marker = get(
responseData,
`${propertyNameArray[0]}.${propertyNameArray[1]}.NextMarker`,
);
if (get(responseData, [propertyNameArray[0], propertyNameArray[1], 'NextMarker'])) {
query.Marker = get(responseData, [propertyNameArray[0], propertyNameArray[1], 'NextMarker']);
}
if (get(responseData, propertyName)) {
if (Array.isArray(get(responseData, propertyName))) {
@ -156,7 +153,7 @@ export async function awsApiRequestSOAPAllItems(
}
}
} while (
get(responseData, `${propertyNameArray[0]}.${propertyNameArray[1]}.NextMarker`) !== undefined
get(responseData, [propertyNameArray[0], propertyNameArray[1], 'NextMarker']) !== undefined
);
return returnData;

View file

@ -139,11 +139,11 @@ export async function awsApiRequestSOAPAllItems(
);
//https://forums.aws.amazon.com/thread.jspa?threadID=55746
if (get(responseData, `${propertyName.split('.')[0]}.NextContinuationToken`)) {
query['continuation-token'] = get(
responseData,
`${propertyName.split('.')[0]}.NextContinuationToken`,
);
if (get(responseData, [propertyName.split('.')[0], 'NextContinuationToken'])) {
query['continuation-token'] = get(responseData, [
propertyName.split('.')[0],
'NextContinuationToken',
]);
}
if (get(responseData, propertyName)) {
if (Array.isArray(get(responseData, propertyName))) {
@ -157,8 +157,8 @@ export async function awsApiRequestSOAPAllItems(
return returnData;
}
} while (
get(responseData, `${propertyName.split('.')[0]}.IsTruncated`) !== undefined &&
get(responseData, `${propertyName.split('.')[0]}.IsTruncated`) !== 'false'
get(responseData, [propertyName.split('.')[0], 'IsTruncated']) !== undefined &&
get(responseData, [propertyName.split('.')[0], 'IsTruncated']) !== 'false'
);
return returnData;

View file

@ -137,11 +137,11 @@ export async function awsApiRequestSOAPAllItems(
);
//https://forums.aws.amazon.com/thread.jspa?threadID=55746
if (get(responseData, `${propertyName.split('.')[0]}.NextContinuationToken`)) {
query['continuation-token'] = get(
responseData,
`${propertyName.split('.')[0]}.NextContinuationToken`,
);
if (get(responseData, [propertyName.split('.')[0], 'NextContinuationToken'])) {
query['continuation-token'] = get(responseData, [
propertyName.split('.')[0],
'NextContinuationToken',
]);
}
if (get(responseData, propertyName)) {
if (Array.isArray(get(responseData, propertyName))) {
@ -155,8 +155,8 @@ export async function awsApiRequestSOAPAllItems(
return returnData;
}
} while (
get(responseData, `${propertyName.split('.')[0]}.IsTruncated`) !== undefined &&
get(responseData, `${propertyName.split('.')[0]}.IsTruncated`) !== 'false'
get(responseData, [propertyName.split('.')[0], 'IsTruncated']) !== undefined &&
get(responseData, [propertyName.split('.')[0], 'IsTruncated']) !== 'false'
);
return returnData;

View file

@ -107,11 +107,11 @@ export async function awsApiRequestRESTAllItems(
region,
);
//https://forums.aws.amazon.com/thread.jspa?threadID=55746
if (get(responseData, `${propertyName.split('.')[0]}.NextContinuationToken`)) {
query['continuation-token'] = get(
responseData,
`${propertyName.split('.')[0]}.NextContinuationToken`,
);
if (get(responseData, [propertyName.split('.')[0], 'NextContinuationToken'])) {
query['continuation-token'] = get(responseData, [
propertyName.split('.')[0],
'NextContinuationToken',
]);
}
if (get(responseData, propertyName)) {
if (Array.isArray(get(responseData, propertyName))) {
@ -125,8 +125,8 @@ export async function awsApiRequestRESTAllItems(
return returnData;
}
} while (
get(responseData, `${propertyName.split('.')[0]}.IsTruncated`) !== undefined &&
get(responseData, `${propertyName.split('.')[0]}.IsTruncated`) !== 'false'
get(responseData, [propertyName.split('.')[0], 'IsTruncated']) !== undefined &&
get(responseData, [propertyName.split('.')[0], 'IsTruncated']) !== 'false'
);
return returnData;
}

View file

@ -102,11 +102,12 @@ export async function awsApiRequestSOAPAllItems(
do {
responseData = await awsApiRequestSOAP.call(this, service, method, path, body, query);
if (get(responseData, `${propertyNameArray[0]}.${propertyNameArray[1]}.NextToken`)) {
query.NextToken = get(
responseData,
`${propertyNameArray[0]}.${propertyNameArray[1]}.NextToken`,
);
if (get(responseData, [propertyNameArray[0], propertyNameArray[1], 'NextToken'])) {
query.NextToken = get(responseData, [
propertyNameArray[0],
propertyNameArray[1],
'NextToken',
]);
}
if (get(responseData, propertyName)) {
if (Array.isArray(get(responseData, propertyName))) {
@ -116,7 +117,7 @@ export async function awsApiRequestSOAPAllItems(
}
}
} while (
get(responseData, `${propertyNameArray[0]}.${propertyNameArray[1]}.NextToken`) !== undefined
get(responseData, [propertyNameArray[0], propertyNameArray[1], 'NextToken']) !== undefined
);
return returnData;

View file

@ -109,11 +109,12 @@ export async function awsApiRequestRESTAllItems(
do {
responseData = await awsApiRequestREST.call(this, service, method, path, body, query);
if (get(responseData, `${propertyNameArray[0]}.${propertyNameArray[1]}.NextToken`)) {
query.NextToken = get(
responseData,
`${propertyNameArray[0]}.${propertyNameArray[1]}.NextToken`,
);
if (get(responseData, [propertyNameArray[0], propertyNameArray[1], 'NextToken'])) {
query.NextToken = get(responseData, [
propertyNameArray[0],
propertyNameArray[1],
'NextToken',
]);
}
if (get(responseData, propertyName)) {
if (Array.isArray(get(responseData, propertyName))) {
@ -123,7 +124,7 @@ export async function awsApiRequestRESTAllItems(
}
}
} while (
get(responseData, `${propertyNameArray[0]}.${propertyNameArray[1]}.NextToken`) !== undefined
get(responseData, [propertyNameArray[0], propertyNameArray[1], 'NextToken']) !== undefined
);
return returnData;

View file

@ -171,7 +171,7 @@ function combineItems(
entry.json[field] = match.json[field];
} else {
const value = get(match.json, field) || null;
set(entry, `json.${field}`, value);
set(entry, ['json', field], value);
}
});

View file

@ -527,7 +527,7 @@ export class Crypto implements INodeType {
newItem.binary = item.binary;
}
set(newItem, `json.${dataPropertyName}`, newValue);
set(newItem, ['json', dataPropertyName], newValue);
returnData.push(newItem);
} catch (error) {

View file

@ -48,9 +48,7 @@ export async function customerIoApiRequest(
export function eventExists(currentEvents: string[], webhookEvents: IDataObject) {
for (const currentEvent of currentEvents) {
if (
get(webhookEvents, `${currentEvent.split('.')[0]}.${currentEvent.split('.')[1]}`) !== true
) {
if (get(webhookEvents, [currentEvent.split('.')[0], currentEvent.split('.')[1]]) !== true) {
return false;
}
}

View file

@ -521,7 +521,7 @@ export class DateTimeV1 implements INodeType {
newItem.binary = item.binary;
}
set(newItem, `json.${dataPropertyName}`, newDate);
set(newItem, ['json', dataPropertyName], newDate);
returnData.push(newItem);
}
@ -565,7 +565,7 @@ export class DateTimeV1 implements INodeType {
newItem.binary = item.binary;
}
set(newItem, `json.${dataPropertyName}`, newDate.toISOString());
set(newItem, ['json', dataPropertyName], newDate.toISOString());
returnData.push(newItem);
}

View file

@ -64,9 +64,9 @@ export async function linearApiRequestAllItems(
do {
responseData = await linearApiRequest.call(this, body);
returnData.push.apply(returnData, get(responseData, `${propertyName}.nodes`) as IDataObject[]);
body.variables.after = get(responseData, `${propertyName}.pageInfo.endCursor`);
} while (get(responseData, `${propertyName}.pageInfo.hasNextPage`));
returnData.push.apply(returnData, get(responseData, [propertyName, 'nodes']) as IDataObject[]);
body.variables.after = get(responseData, [propertyName, 'pageInfo', 'endCursor']);
} while (get(responseData, [propertyName, 'pageInfo', 'hasNextPage']));
return returnData;
}

View file

@ -190,11 +190,11 @@ export async function s3ApiRequestSOAPAllItems(
);
//https://forums.aws.amazon.com/thread.jspa?threadID=55746
if (get(responseData, `${propertyName.split('.')[0]}.NextContinuationToken`)) {
query['continuation-token'] = get(
responseData,
`${propertyName.split('.')[0]}.NextContinuationToken`,
);
if (get(responseData, [propertyName.split('.')[0], 'NextContinuationToken'])) {
query['continuation-token'] = get(responseData, [
propertyName.split('.')[0],
'NextContinuationToken',
]);
}
if (get(responseData, propertyName)) {
if (Array.isArray(get(responseData, propertyName))) {
@ -208,8 +208,8 @@ export async function s3ApiRequestSOAPAllItems(
return returnData;
}
} while (
get(responseData, `${propertyName.split('.')[0]}.IsTruncated`) !== undefined &&
get(responseData, `${propertyName.split('.')[0]}.IsTruncated`) !== 'false'
get(responseData, [propertyName.split('.')[0], 'IsTruncated']) !== undefined &&
get(responseData, [propertyName.split('.')[0], 'IsTruncated']) !== 'false'
);
return returnData;

View file

@ -76,9 +76,9 @@ export async function mediaUploadFromItem(
if (!requestOptions.body) {
requestOptions.body = {};
}
set(requestOptions.body as IDataObject, `${operation}.id`, result.id);
set(requestOptions.body as IDataObject, [operation, 'id'], result.id);
if (operation === 'document') {
set(requestOptions.body as IDataObject, `${operation}.filename`, uploadData.fileName);
set(requestOptions.body as IDataObject, [operation, 'filename'], uploadData.fileName);
}
return requestOptions;

View file

@ -340,6 +340,9 @@ importers:
eslint-plugin-import:
specifier: ^2.29.0
version: 2.29.0(@typescript-eslint/parser@6.12.0)(eslint-import-resolver-typescript@3.6.1)(eslint@8.54.0)
eslint-plugin-lodash:
specifier: ^7.4.0
version: 7.4.0(eslint@8.54.0)
eslint-plugin-n8n-local-rules:
specifier: ^1.0.0
version: 1.0.0
@ -15028,6 +15031,16 @@ packages:
- supports-color
dev: true
/eslint-plugin-lodash@7.4.0(eslint@8.54.0):
resolution: {integrity: sha512-Tl83UwVXqe1OVeBRKUeWcfg6/pCW1GTRObbdnbEJgYwjxp5Q92MEWQaH9+dmzbRt6kvYU1Mp893E79nJiCSM8A==}
engines: {node: '>=10'}
peerDependencies:
eslint: '>=2'
dependencies:
eslint: 8.54.0
lodash: 4.17.21
dev: true
/eslint-plugin-n8n-local-rules@1.0.0:
resolution: {integrity: sha512-qe6sVFDP1Vj5eXlqZxYZpIjwYvhuqXlI0P8OfPyhiPOhMkFtr0TpFphD8/6WCzkm7LJCvG1eJEzURCtMIsFTAg==}
dev: true