@ping-identity/dvlint-base-rule-pack
Version:
Collection of base rules used to lint DaVinci flows.
224 lines (208 loc) • 11.6 kB
JavaScript
const { LintRule } = require("@ping-identity/dvlint");
class GlobalVariableRule extends LintRule {
constructor() {
super({
id: "dv-rule-globalVariable-001",
description: "Global variable usage in script",
cleans: false,
reference: "",
});
this.addCode("dv-bp-globalVariable-001", {
description: "Global variable usage in Custom HTML Template script",
message: "Global variable usage in Custom HTML Template script",
type: "best-practice",
recommendation: "Use global variables cautiously in custom code. Avoid storing or processing sensitive information within them.",
});
this.addCode("dv-bp-globalVariable-002", {
description: "Global variable usage in Custom Function",
message: "Global variable usage in Custom Function",
type: "best-practice",
recommendation: "Use global company variables cautiously in the 'Variable Input List' of Custom Function. Avoid storing or processing sensitive information within them.",
});
this.addCode("dv-bp-globalVariable-003", {
description: "Custom JavaScript usage in flow",
message: "Custom JavaScript usage in flow",
type: "best-practice",
recommendation: "Review the script in the node(s): % to ensure it does not introduce security vulnerabilities.",
});
}
getPropertiesValue(value, pattern) {
const strVal = JSON.stringify(value) || '';
const matches = [...strVal.matchAll(pattern)];
const localVarArray = matches.map(match => match[1]);
const uniqueGlobalVariables = [...new Set(localVarArray)];
return uniqueGlobalVariables;
}
runRule() {
try {
const connectorIdsForGlobalVariableCheck = {
transunionConnector: {
otpAuthentication: 'htmlConfig_htmlLogin1',
kba: 'htmlConfig_questionsScreen',
otpVerification: 'htmlConfig_htmlLogin1',
documentVerification: [
'htmlConfig_docVerificationConsentScreen', 'htmlConfig_documentTypeScreen',
'htmlConfig_photosScreen', 'htmlConfig_thankyouScreen'
]
},
userPolicyConnector: {
authenticateUser: ['htmlConfig_htmlLogin1', 'htmlConfig_htmlLogin2']
},
idemiaConnector: {
documentVerification: [
'htmlConfig_docVerificationDocumentTypeSelectScreen',
'htmlConfig_docVerificationConnectivityCheck',
'htmlConfig_docVerificationDocCapture',
'htmlConfig_docVerificationCountrySelectScreen',
'htmlConfig_docVerificationFrontScanTutorial'
],
portraitVerification: [
'htmlConfig_portraitVerificationCheckBrowserSupport',
'htmlConfig_portraitVerificationPortraitCapture'
]
},
jumioConnector: {
register: ['htmlConfig_documentVerify3', 'htmlConfig_documentVerify2'],
documentVerify: ['htmlConfig_documentVerify2', 'htmlConfig_documentVerify3'],
login: 'htmlConfig_login2',
loginFirstFactor: ['htmlConfig_loginFirstFactor1', 'htmlConfig_loginFirstFactor2']
},
nuanceConnector: {
register: ['htmlConfig_htmlRegister1', 'htmlConfig_htmlRegister2'],
login: ['htmlConfig_htmlLogin1', 'htmlConfig_htmlLogin2']
},
pingFederateConnectorV2: {
authenticate: ''
},
mfaContainerConnector: {
login: '',
loginDynamicMethods: '',
register: ''
},
entrustConnector: {
auth: ['htmlConfig0_select', 'htmlConfig1_otp', 'htmlConfig2_token']
},
securIdConnector: {
verifyRSA: ['htmlConfig0_selectScreen', 'htmlConfig1_authenticateTokenCode', 'htmlConfig2_emergencyTokenCode']
},
lexisnexisV2Connector: {
initialize: 'htmlConfig_initializeScreen',
otp: 'htmlConfig_OTPscreen',
iidqa: 'htmlConfig_questionsScreen2'
}
}
const connectorIdWithKeyBeforeCustomScript = [
'transunionConnector', 'userPolicyConnector',
'idemiaConnector', 'jumioConnector',
'nuanceConnector', 'entrustConnector',
'lexisnexisV2Connector', 'securIdConnector'
];
const connectorIdWithoutKeyBeforeCustomScript = ['pingFederateConnectorV2', 'mfaContainerConnector'];
const pattern = /\{\{(.*?)\}\}/g; // regex pattern to match all instances of {{...}}
for (const flow of this.allFlows) {
const { nodes, edges } = flow?.graphData?.elements;
const globalVarNodeIdArr = []
nodes?.forEach((node) => {
const { data } = node;
if (data.nodeType === "CONNECTION" && data.capabilityName === "customHTMLTemplate" || data.capabilityName === "customFunction") {
let property = data.capabilityName === "customFunction" ? "variableInputList" : "customScript";
let value = data.properties[property]?.value;
const uniqueGlobalVariables = this.getPropertiesValue(value, pattern);
const globalVarArr = []
uniqueGlobalVariables?.forEach(v => {
if (v.includes('global.')) {
globalVarArr.push(`{{${v}}}`)
}
});
if (globalVarArr.length > 0) {
let ruleNo = data.capabilityName === "customFunction" ? "dv-bp-globalVariable-002" : "dv-bp-globalVariable-001";
this.addError(ruleNo, {
flowId: this.mainFlow.flowId,
nodeId: node.data.id
});
}
}
if (
data.nodeType === "CONNECTION" &&
(data.connectorId in connectorIdsForGlobalVariableCheck) &&
data.capabilityName in connectorIdsForGlobalVariableCheck[data.connectorId]
) {
let value;
if (connectorIdWithKeyBeforeCustomScript.includes(data.connectorId)) {
const key = connectorIdsForGlobalVariableCheck[data.connectorId][data.capabilityName];
// this case is for capabilities where multiple code snippet sections are available in custom screens
if (Array.isArray(key) && key.length > 0) {
const counterArr = []
key.forEach(k => {
const propValue = data.properties[k]?.properties?.customScript?.value;
if (propValue && counterArr.length === 0) {
globalVarNodeIdArr.push(node.data.id);
// const uniqueGlobalVariables = this.getPropertiesValue(propValue, pattern)
// uniqueGlobalVariables?.forEach(v => {
// if (v.includes('global.')) {
counterArr.push(node.data.id)
// }
// });
}
});
// if (globalVarArr.length > 0) {
// globalVarNodeIdArr.push(node.data.id);
// }
} else {
value = data.properties[key]?.properties?.customScript?.value;
}
} else if (connectorIdWithoutKeyBeforeCustomScript.includes(data.connectorId)) {
value = data.properties?.customScript?.value;
}
const strVal = JSON.stringify(value) || '';
// const uniqueGlobalVariables = this.getPropertiesValue(value, pattern)
// const globalVarArr = []
// uniqueGlobalVariables?.forEach(v => {
// if (v.includes('global.')) {
// globalVarArr.push(`{{${v}}}`)
// }
// });
if (strVal.length > 0) {
globalVarNodeIdArr.push(node.data.id)
}
}
});
if (globalVarNodeIdArr.length > 0) {
const stringWithQuotesCommaSeparated = globalVarNodeIdArr.map(str => `'${str}'`).join(', ');
this.addError("dv-bp-globalVariable-003", {
flowId: this.mainFlow.flowId,
recommendationArgs: [stringWithQuotesCommaSeparated]
});
}
// configuration code check
const connectorIdsForConfigurationCheck = ['codeSnippetConnector'];
connectorIdsForConfigurationCheck.forEach(connectorId => {
const codeSnippetConnectorNodeids = nodes?.filter(node => node.data.connectorId === connectorId).map(node => node.data.id);
flow.connections?.forEach((connection) => {
if (connection?.connectorId === connectorId) {
const value = connection?.properties?.code?.value;
const strVal = JSON.stringify(value) || '';
// const uniqueGlobalVariables = this.getPropertiesValue(value, pattern);
// const globalVarArr = []
// uniqueGlobalVariables?.forEach(v => {
// // Check if the configurations code section has global variable
// if (v.includes('global.')) {
// globalVarArr.push(`{{${v}}}`)
// }
// });
if (strVal.length > 0) {
this.addError("dv-bp-globalVariable-003", {
flowId: this.mainFlow.flowId,
recommendationArgs: [codeSnippetConnectorNodeids.map(str => `'${str}'`).join(', ')],
});
}
}
})
})
}
} catch (err) {
this.addError(undefined, { messageArgs: [`${err}`] });
}
}
}
module.exports = GlobalVariableRule;