@flow-scanner/lightning-flow-scanner-core 6.6.4 → 6.6.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (231) hide show
  1. package/CONTRIBUTING.md +31 -0
  2. package/README.md +426 -0
  3. package/SECURITY.md +54 -0
  4. package/package.json +7 -57
  5. package/.husky/pre-commit +0 -1
  6. package/.husky/pre-push +0 -1
  7. package/.prettierignore +0 -5
  8. package/.swcrc +0 -26
  9. package/eslint.config.mjs +0 -36
  10. package/jest.config.cjs +0 -32
  11. package/jest.env-setup.js +0 -101
  12. package/lint-staged.config.mjs +0 -8
  13. package/out/assets/media/banner.png +0 -0
  14. package/prettier.config.mjs +0 -5
  15. package/src/index.ts +0 -44
  16. package/src/main/interfaces/IExceptions.ts +0 -6
  17. package/src/main/interfaces/IRuleConfig.ts +0 -3
  18. package/src/main/interfaces/IRuleDefinition.ts +0 -12
  19. package/src/main/interfaces/IRuleOptions.ts +0 -5
  20. package/src/main/interfaces/IRulesConfig.ts +0 -15
  21. package/src/main/internals/internals.ts +0 -35
  22. package/src/main/libs/BuildFlow.ts +0 -11
  23. package/src/main/libs/Compiler.ts +0 -69
  24. package/src/main/libs/ConvertFlowNodes.ts +0 -4
  25. package/src/main/libs/DynamicRule.ts +0 -11
  26. package/src/main/libs/FixFlows.ts +0 -61
  27. package/src/main/libs/GetRuleDefinitions.ts +0 -65
  28. package/src/main/libs/ParseFlows.ts +0 -34
  29. package/src/main/libs/ScanFlows.ts +0 -112
  30. package/src/main/libs/exportAsDetails.ts +0 -26
  31. package/src/main/libs/exportAsSarif.ts +0 -88
  32. package/src/main/models/FlatViolation.ts +0 -8
  33. package/src/main/models/Flow.ts +0 -214
  34. package/src/main/models/FlowAttribute.ts +0 -12
  35. package/src/main/models/FlowElement.ts +0 -15
  36. package/src/main/models/FlowElementConnector.ts +0 -28
  37. package/src/main/models/FlowMetadata.ts +0 -7
  38. package/src/main/models/FlowNode.ts +0 -171
  39. package/src/main/models/FlowResource.ts +0 -10
  40. package/src/main/models/FlowType.ts +0 -52
  41. package/src/main/models/FlowVariable.ts +0 -12
  42. package/src/main/models/LoopRuleCommon.ts +0 -55
  43. package/src/main/models/ParsedFlow.ts +0 -15
  44. package/src/main/models/RuleCommon.ts +0 -68
  45. package/src/main/models/RuleInfo.ts +0 -43
  46. package/src/main/models/RuleResult.ts +0 -25
  47. package/src/main/models/ScanResult.ts +0 -12
  48. package/src/main/models/Violation.ts +0 -78
  49. package/src/main/rules/APIVersion.ts +0 -59
  50. package/src/main/rules/ActionCallsInLoop.ts +0 -24
  51. package/src/main/rules/AutoLayout.ts +0 -44
  52. package/src/main/rules/CopyAPIName.ts +0 -29
  53. package/src/main/rules/CyclomaticComplexity.ts +0 -67
  54. package/src/main/rules/DMLStatementInLoop.ts +0 -24
  55. package/src/main/rules/DuplicateDMLOperation.ts +0 -114
  56. package/src/main/rules/FlowDescription.ts +0 -32
  57. package/src/main/rules/FlowName.ts +0 -40
  58. package/src/main/rules/GetRecordAllFields.ts +0 -59
  59. package/src/main/rules/HardcodedId.ts +0 -37
  60. package/src/main/rules/HardcodedUrl.ts +0 -42
  61. package/src/main/rules/InactiveFlow.ts +0 -31
  62. package/src/main/rules/MissingFaultPath.ts +0 -89
  63. package/src/main/rules/MissingMetadataDescription.ts +0 -39
  64. package/src/main/rules/MissingNullHandler.ts +0 -95
  65. package/src/main/rules/ProcessBuilder.ts +0 -33
  66. package/src/main/rules/RecursiveAfterUpdate.ts +0 -88
  67. package/src/main/rules/SOQLQueryInLoop.ts +0 -24
  68. package/src/main/rules/SameRecordFieldUpdates.ts +0 -64
  69. package/src/main/rules/TriggerOrder.ts +0 -44
  70. package/src/main/rules/UnconnectedElement.ts +0 -48
  71. package/src/main/rules/UnsafeRunningContext.ts +0 -44
  72. package/src/main/rules/UnusedVariable.ts +0 -64
  73. package/src/main/store/DefaultRuleStore.ts +0 -54
  74. package/stryker.config.mjs +0 -23
  75. package/tests/APIVersion.test.ts +0 -83
  76. package/tests/AutoLayout.test.ts +0 -39
  77. package/tests/Config.test.ts +0 -119
  78. package/tests/ConfigBetaMode.test.ts +0 -26
  79. package/tests/CopyAPIName.test.ts +0 -43
  80. package/tests/CyclomaticComplexity.test.ts +0 -123
  81. package/tests/DMLStatementInLoop.test.ts +0 -31
  82. package/tests/DuplicateDMLOperation.test.ts +0 -41
  83. package/tests/Exceptions.test.ts +0 -813
  84. package/tests/ExportSarif.test.ts +0 -61
  85. package/tests/FlowDescription.test.ts +0 -42
  86. package/tests/FlowName.test.ts +0 -62
  87. package/tests/GetRecordElementAllFields.test.ts +0 -180
  88. package/tests/HardcodedId.test.ts +0 -16
  89. package/tests/HardcodedUrl.test.ts +0 -252
  90. package/tests/InactiveFlow.test.ts +0 -99
  91. package/tests/MissingFaultPath.test.ts +0 -50
  92. package/tests/MissingMetadataDescription.test.ts +0 -24
  93. package/tests/MissingNullHandler.test.ts +0 -43
  94. package/tests/No_Missing_Null_Handler.test.ts +0 -30
  95. package/tests/RecursiveAfterUpdate.test.ts +0 -160
  96. package/tests/SOQLQueryInLoop.test.ts +0 -32
  97. package/tests/SameRecordFieldUpdates.test.ts +0 -241
  98. package/tests/SanityTest.test.ts +0 -15
  99. package/tests/TriggerOrder.test.ts +0 -92
  100. package/tests/UnconnectedElement.test.ts +0 -74
  101. package/tests/UnsafeRunningContext.test.ts +0 -46
  102. package/tests/UnusedVariable.test.ts +0 -56
  103. package/tests/jsonfiles/MissingFaultPath_BeforeSave_Bypass.json +0 -128
  104. package/tests/jsonfiles/MissingFaultPath_WaitConditions.json +0 -102
  105. package/tests/jsonfiles/MissingFaultPath_WaitDate.json +0 -88
  106. package/tests/jsonfiles/MissingFaultPath_WaitDuration.json +0 -90
  107. package/tests/models/Flow.test.ts +0 -107
  108. package/tests/models/LoopRuleCommon.test.ts +0 -253
  109. package/tests/models/RuleCommon.test.ts +0 -47
  110. package/tsconfig.json +0 -19
  111. package/tsconfig.tsbuildinfo +0 -1
  112. package/tsconfig.types.json +0 -25
  113. package/vite.config.ts +0 -28
  114. /package/{out/index.d.ts → index.d.ts} +0 -0
  115. /package/{out/index.js → index.js} +0 -0
  116. /package/{out/main → main}/interfaces/IExceptions.d.ts +0 -0
  117. /package/{out/main → main}/interfaces/IExceptions.js +0 -0
  118. /package/{out/main → main}/interfaces/IRuleConfig.d.ts +0 -0
  119. /package/{out/main → main}/interfaces/IRuleConfig.js +0 -0
  120. /package/{out/main → main}/interfaces/IRuleDefinition.d.ts +0 -0
  121. /package/{out/main → main}/interfaces/IRuleDefinition.js +0 -0
  122. /package/{out/main → main}/interfaces/IRuleOptions.d.ts +0 -0
  123. /package/{out/main → main}/interfaces/IRuleOptions.js +0 -0
  124. /package/{out/main → main}/interfaces/IRulesConfig.d.ts +0 -0
  125. /package/{out/main → main}/interfaces/IRulesConfig.js +0 -0
  126. /package/{out/main → main}/internals/internals.d.ts +0 -0
  127. /package/{out/main → main}/internals/internals.js +0 -0
  128. /package/{out/main → main}/libs/BuildFlow.d.ts +0 -0
  129. /package/{out/main → main}/libs/BuildFlow.js +0 -0
  130. /package/{out/main → main}/libs/Compiler.d.ts +0 -0
  131. /package/{out/main → main}/libs/Compiler.js +0 -0
  132. /package/{out/main → main}/libs/ConvertFlowNodes.d.ts +0 -0
  133. /package/{out/main → main}/libs/ConvertFlowNodes.js +0 -0
  134. /package/{out/main → main}/libs/DynamicRule.d.ts +0 -0
  135. /package/{out/main → main}/libs/DynamicRule.js +0 -0
  136. /package/{out/main → main}/libs/FixFlows.d.ts +0 -0
  137. /package/{out/main → main}/libs/FixFlows.js +0 -0
  138. /package/{out/main → main}/libs/GetRuleDefinitions.d.ts +0 -0
  139. /package/{out/main → main}/libs/GetRuleDefinitions.js +0 -0
  140. /package/{out/main → main}/libs/ParseFlows.d.ts +0 -0
  141. /package/{out/main → main}/libs/ParseFlows.js +0 -0
  142. /package/{out/main → main}/libs/ScanFlows.d.ts +0 -0
  143. /package/{out/main → main}/libs/ScanFlows.js +0 -0
  144. /package/{out/main → main}/libs/exportAsDetails.d.ts +0 -0
  145. /package/{out/main → main}/libs/exportAsDetails.js +0 -0
  146. /package/{out/main → main}/libs/exportAsSarif.d.ts +0 -0
  147. /package/{out/main → main}/libs/exportAsSarif.js +0 -0
  148. /package/{out/main → main}/models/FlatViolation.d.ts +0 -0
  149. /package/{out/main → main}/models/FlatViolation.js +0 -0
  150. /package/{out/main → main}/models/Flow.d.ts +0 -0
  151. /package/{out/main → main}/models/Flow.js +0 -0
  152. /package/{out/main → main}/models/FlowAttribute.d.ts +0 -0
  153. /package/{out/main → main}/models/FlowAttribute.js +0 -0
  154. /package/{out/main → main}/models/FlowElement.d.ts +0 -0
  155. /package/{out/main → main}/models/FlowElement.js +0 -0
  156. /package/{out/main → main}/models/FlowElementConnector.d.ts +0 -0
  157. /package/{out/main → main}/models/FlowElementConnector.js +0 -0
  158. /package/{out/main → main}/models/FlowMetadata.d.ts +0 -0
  159. /package/{out/main → main}/models/FlowMetadata.js +0 -0
  160. /package/{out/main → main}/models/FlowNode.d.ts +0 -0
  161. /package/{out/main → main}/models/FlowNode.js +0 -0
  162. /package/{out/main → main}/models/FlowResource.d.ts +0 -0
  163. /package/{out/main → main}/models/FlowResource.js +0 -0
  164. /package/{out/main → main}/models/FlowType.d.ts +0 -0
  165. /package/{out/main → main}/models/FlowType.js +0 -0
  166. /package/{out/main → main}/models/FlowVariable.d.ts +0 -0
  167. /package/{out/main → main}/models/FlowVariable.js +0 -0
  168. /package/{out/main → main}/models/LoopRuleCommon.d.ts +0 -0
  169. /package/{out/main → main}/models/LoopRuleCommon.js +0 -0
  170. /package/{out/main → main}/models/ParsedFlow.d.ts +0 -0
  171. /package/{out/main → main}/models/ParsedFlow.js +0 -0
  172. /package/{out/main → main}/models/RuleCommon.d.ts +0 -0
  173. /package/{out/main → main}/models/RuleCommon.js +0 -0
  174. /package/{out/main → main}/models/RuleInfo.d.ts +0 -0
  175. /package/{out/main → main}/models/RuleInfo.js +0 -0
  176. /package/{out/main → main}/models/RuleResult.d.ts +0 -0
  177. /package/{out/main → main}/models/RuleResult.js +0 -0
  178. /package/{out/main → main}/models/ScanResult.d.ts +0 -0
  179. /package/{out/main → main}/models/ScanResult.js +0 -0
  180. /package/{out/main → main}/models/Violation.d.ts +0 -0
  181. /package/{out/main → main}/models/Violation.js +0 -0
  182. /package/{out/main → main}/rules/APIVersion.d.ts +0 -0
  183. /package/{out/main → main}/rules/APIVersion.js +0 -0
  184. /package/{out/main → main}/rules/ActionCallsInLoop.d.ts +0 -0
  185. /package/{out/main → main}/rules/ActionCallsInLoop.js +0 -0
  186. /package/{out/main → main}/rules/AutoLayout.d.ts +0 -0
  187. /package/{out/main → main}/rules/AutoLayout.js +0 -0
  188. /package/{out/main → main}/rules/CopyAPIName.d.ts +0 -0
  189. /package/{out/main → main}/rules/CopyAPIName.js +0 -0
  190. /package/{out/main → main}/rules/CyclomaticComplexity.d.ts +0 -0
  191. /package/{out/main → main}/rules/CyclomaticComplexity.js +0 -0
  192. /package/{out/main → main}/rules/DMLStatementInLoop.d.ts +0 -0
  193. /package/{out/main → main}/rules/DMLStatementInLoop.js +0 -0
  194. /package/{out/main → main}/rules/DuplicateDMLOperation.d.ts +0 -0
  195. /package/{out/main → main}/rules/DuplicateDMLOperation.js +0 -0
  196. /package/{out/main → main}/rules/FlowDescription.d.ts +0 -0
  197. /package/{out/main → main}/rules/FlowDescription.js +0 -0
  198. /package/{out/main → main}/rules/FlowName.d.ts +0 -0
  199. /package/{out/main → main}/rules/FlowName.js +0 -0
  200. /package/{out/main → main}/rules/GetRecordAllFields.d.ts +0 -0
  201. /package/{out/main → main}/rules/GetRecordAllFields.js +0 -0
  202. /package/{out/main → main}/rules/HardcodedId.d.ts +0 -0
  203. /package/{out/main → main}/rules/HardcodedId.js +0 -0
  204. /package/{out/main → main}/rules/HardcodedUrl.d.ts +0 -0
  205. /package/{out/main → main}/rules/HardcodedUrl.js +0 -0
  206. /package/{out/main → main}/rules/InactiveFlow.d.ts +0 -0
  207. /package/{out/main → main}/rules/InactiveFlow.js +0 -0
  208. /package/{out/main → main}/rules/MissingFaultPath.d.ts +0 -0
  209. /package/{out/main → main}/rules/MissingFaultPath.js +0 -0
  210. /package/{out/main → main}/rules/MissingMetadataDescription.d.ts +0 -0
  211. /package/{out/main → main}/rules/MissingMetadataDescription.js +0 -0
  212. /package/{out/main → main}/rules/MissingNullHandler.d.ts +0 -0
  213. /package/{out/main → main}/rules/MissingNullHandler.js +0 -0
  214. /package/{out/main → main}/rules/ProcessBuilder.d.ts +0 -0
  215. /package/{out/main → main}/rules/ProcessBuilder.js +0 -0
  216. /package/{out/main → main}/rules/RecursiveAfterUpdate.d.ts +0 -0
  217. /package/{out/main → main}/rules/RecursiveAfterUpdate.js +0 -0
  218. /package/{out/main → main}/rules/SOQLQueryInLoop.d.ts +0 -0
  219. /package/{out/main → main}/rules/SOQLQueryInLoop.js +0 -0
  220. /package/{out/main → main}/rules/SameRecordFieldUpdates.d.ts +0 -0
  221. /package/{out/main → main}/rules/SameRecordFieldUpdates.js +0 -0
  222. /package/{out/main → main}/rules/TriggerOrder.d.ts +0 -0
  223. /package/{out/main → main}/rules/TriggerOrder.js +0 -0
  224. /package/{out/main → main}/rules/UnconnectedElement.d.ts +0 -0
  225. /package/{out/main → main}/rules/UnconnectedElement.js +0 -0
  226. /package/{out/main → main}/rules/UnsafeRunningContext.d.ts +0 -0
  227. /package/{out/main → main}/rules/UnsafeRunningContext.js +0 -0
  228. /package/{out/main → main}/rules/UnusedVariable.d.ts +0 -0
  229. /package/{out/main → main}/rules/UnusedVariable.js +0 -0
  230. /package/{out/main → main}/store/DefaultRuleStore.d.ts +0 -0
  231. /package/{out/main → main}/store/DefaultRuleStore.js +0 -0
@@ -1,114 +0,0 @@
1
- import * as core from "../internals/internals";
2
- import { RuleCommon } from "../models/RuleCommon";
3
- import { IRuleDefinition } from "../interfaces/IRuleDefinition";
4
- export class DuplicateDMLOperation extends RuleCommon implements IRuleDefinition {
5
- constructor() {
6
- super({
7
- name: "DuplicateDMLOperation",
8
- label: "Duplicate DML Operation",
9
- description:
10
- "When the flow executes database changes or actions between two screens, it's important to prevent users from navigating back between screens. Failure to do so may result in duplicate database operations being performed within the flow.",
11
- supportedTypes: core.FlowType.visualTypes,
12
- docRefs: [],
13
- });
14
- }
15
- protected check(
16
- flow: core.Flow,
17
- _options: object | undefined,
18
- suppressions: Set<string>
19
- ): core.Violation[] {
20
- const flowElements: core.FlowNode[] = flow.elements.filter(
21
- (node) => node instanceof core.FlowNode
22
- ) as core.FlowNode[];
23
- const processedElementIndexes: number[] = [];
24
- const unconnectedElementIndexes: number[] = [];
25
- const DuplicateDMLOperations: core.FlowNode[] = [];
26
- const startingNode = this.findStart(flow);
27
- if (startingNode === -1) {
28
- return [];
29
- }
30
- let dmlFlag = false;
31
- let indexesToProcess = [startingNode];
32
- do {
33
- indexesToProcess = indexesToProcess.filter(
34
- (index) => !processedElementIndexes.includes(index)
35
- );
36
- if (indexesToProcess.length > 0) {
37
- for (const [index, element] of flowElements.entries()) {
38
- if (indexesToProcess.includes(index)) {
39
- const references: string[] = [];
40
- if (element.connectors && element.connectors.length > 0) {
41
- for (const connector of element.connectors) {
42
- if (connector.reference) {
43
- references.push(connector.reference);
44
- }
45
- }
46
- }
47
- dmlFlag = this.flagDML(element, dmlFlag);
48
- if (references.length > 0) {
49
- const elementsByReferences = flowElements.filter((el) =>
50
- references.includes(el.name)
51
- );
52
- for (const nextElement of elementsByReferences) {
53
- const nextIndex = flowElements.findIndex(
54
- (el) => nextElement.name === el.name
55
- );
56
- if (nextElement.subtype === "screens") {
57
- if (
58
- dmlFlag &&
59
- nextElement.element["allowBack"] === "true" &&
60
- nextElement.element["showFooter"] === "true"
61
- ) {
62
- if (!suppressions.has(nextElement.name)) {
63
- DuplicateDMLOperations.push(nextElement);
64
- }
65
- }
66
- }
67
- if (!processedElementIndexes.includes(nextIndex)) {
68
- indexesToProcess.push(nextIndex);
69
- }
70
- }
71
- }
72
- processedElementIndexes.push(index);
73
- }
74
- }
75
- } else {
76
- for (const index of flowElements.keys()) {
77
- if (!processedElementIndexes.includes(index)) {
78
- unconnectedElementIndexes.push(index);
79
- }
80
- }
81
- }
82
- } while (
83
- processedElementIndexes.length + unconnectedElementIndexes.length <
84
- flowElements.length
85
- );
86
- return DuplicateDMLOperations.map(
87
- (det) => new core.Violation(det)
88
- );
89
- }
90
- private findStart(flow: core.Flow): number {
91
- const flowElements: core.FlowNode[] = flow.elements.filter(
92
- (node) => node instanceof core.FlowNode
93
- ) as core.FlowNode[];
94
- if (flow.startElementReference) {
95
- return flowElements.findIndex((n) => n.name === flow.startElementReference);
96
- } else {
97
- return flowElements.findIndex((n) => n.subtype === "start");
98
- }
99
- }
100
- private flagDML(element: core.FlowNode, dmlFlag: boolean): boolean {
101
- const dmlStatementTypes = ["recordDeletes", "recordUpdates", "recordCreates"];
102
- if (dmlStatementTypes.includes(element.subtype)) {
103
- return true;
104
- } else if (
105
- dmlFlag === true &&
106
- element.subtype === "screens" &&
107
- element.element["allowBack"] === "true"
108
- ) {
109
- return false;
110
- } else {
111
- return dmlFlag;
112
- }
113
- }
114
- }
@@ -1,32 +0,0 @@
1
- import * as core from "../internals/internals";
2
- import { RuleCommon } from "../models/RuleCommon";
3
- import { IRuleDefinition } from "../interfaces/IRuleDefinition";
4
-
5
- export class FlowDescription extends RuleCommon implements IRuleDefinition {
6
- constructor() {
7
- super({
8
- description:
9
- "Descriptions play a vital role in documentation. We highly recommend including details about where they are used and their intended purpose.",
10
- docRefs: [],
11
- label: "Missing Flow Description",
12
- name: "FlowDescription",
13
- supportedTypes: [...core.FlowType.backEndTypes, ...core.FlowType.visualTypes],
14
- });
15
- }
16
-
17
- protected check(
18
- flow: core.Flow,
19
- _options: object | undefined,
20
- _suppressions: Set<string>
21
- ): core.Violation[] {
22
- if (flow.xmldata?.description) {
23
- return [];
24
- }
25
-
26
- return [
27
- new core.Violation(
28
- new core.FlowAttribute("undefined", "description", "!==null")
29
- )
30
- ];
31
- }
32
- }
@@ -1,40 +0,0 @@
1
- import * as core from "../internals/internals";
2
- import { RuleCommon } from "../models/RuleCommon";
3
- import { IRuleDefinition } from "../interfaces/IRuleDefinition";
4
-
5
- export class FlowName extends RuleCommon implements IRuleDefinition {
6
- constructor() {
7
- super({
8
- description:
9
- "The readability of a flow is of utmost importance. Establishing a naming convention for the Flow Name significantly enhances findability, searchability, and maintains overall consistency. It is advisable to include at least a domain and a brief description of the actions carried out in the flow, for instance, 'Service_OrderFulfillment'.",
10
- docRefs: [
11
- {
12
- label: "Naming your Flows is more critical than ever. By Stephen Church",
13
- path: "https://www.linkedin.com/posts/stephen-n-church_naming-your-flows-this-is-more-critical-activity-7099733198175158274-1sPx",
14
- },
15
- ],
16
- label: "Flow Naming Convention",
17
- name: "FlowName",
18
- supportedTypes: core.FlowType.allTypes(),
19
- });
20
- }
21
-
22
- protected check(
23
- flow: core.Flow,
24
- options: { expression?: string } | undefined,
25
- _suppressions: Set<string>
26
- ): core.Violation[] {
27
- const rawRegexp = options?.expression ?? "[A-Za-z0-9]+_[A-Za-z0-9]+";
28
- const flowName = flow.name ?? "";
29
-
30
- if (new RegExp(rawRegexp).test(flowName)) {
31
- return [];
32
- }
33
-
34
- return [
35
- new core.Violation(
36
- new core.FlowAttribute(flowName, "name", rawRegexp)
37
- )
38
- ];
39
- }
40
- }
@@ -1,59 +0,0 @@
1
- import * as core from "../internals/internals";
2
- import { RuleCommon } from "../models/RuleCommon";
3
- import { IRuleDefinition } from "../interfaces/IRuleDefinition";
4
-
5
- export class GetRecordAllFields extends RuleCommon implements IRuleDefinition {
6
- constructor() {
7
- super(
8
- {
9
- description:
10
- "Following the principle of least privilege (PoLP), avoid using Get Records with 'Automatically store all fields' unless necessary.",
11
- docRefs: [
12
- {
13
- label: "SOQL and SOSL | Best Practices for Deployments with Large Data Volumes",
14
- path: "https://developer.salesforce.com/docs/atlas.en-us.salesforce_large_data_volumes_bp.meta/salesforce_large_data_volumes_bp/ldv_deployments_best_practices_soql_and_sosl.htm",
15
- },
16
- {
17
- label: "Indexes | Best Practices",
18
- path: "https://developer.salesforce.com/docs/atlas.en-us.salesforce_large_data_volumes_bp.meta/salesforce_large_data_volumes_bp/ldv_deployments_infrastructure_indexes.htm",
19
- },
20
- ],
21
- label: "Get Record All Fields",
22
- name: "GetRecordAllFields",
23
- supportedTypes: core.FlowType.allTypes(),
24
- },
25
- { severity: "warning" }
26
- );
27
- }
28
-
29
- protected check(
30
- flow: core.Flow,
31
- _options: object | undefined,
32
- _suppressions: Set<string>
33
- ): core.Violation[] {
34
- const lookupNodes = flow.elements?.filter(
35
- (e) => e.subtype === "recordLookups"
36
- ) ?? [];
37
-
38
- const violations = lookupNodes
39
- .filter((node) => {
40
- const el = (node as core.FlowNode).element as core.FlowElement;
41
-
42
- const storeAllFields =
43
- typeof el === "object" &&
44
- "storeOutputAutomatically" in el &&
45
- el.storeOutputAutomatically;
46
-
47
- const hasQueriedFields =
48
- typeof el === "object" &&
49
- Array.isArray((el as any).queriedFields) &&
50
- (el as any).queriedFields.length > 0;
51
-
52
- return storeAllFields && !hasQueriedFields;
53
- })
54
- .map((node) => new core.Violation(node));
55
-
56
- return violations;
57
- }
58
-
59
- }
@@ -1,37 +0,0 @@
1
- import * as core from "../internals/internals";
2
- import { RuleCommon } from "../models/RuleCommon";
3
- import { IRuleDefinition } from "../interfaces/IRuleDefinition";
4
-
5
- export class HardcodedId extends RuleCommon implements IRuleDefinition {
6
- constructor() {
7
- super({
8
- name: "HardcodedId",
9
- label: "Hardcoded Id",
10
- description:
11
- "Avoid hard-coding IDs as they are org-specific. Instead, pass them into variables at the start of the flow. You can achieve this by utilizing merge fields in URL parameters or employing a Get Records element.",
12
- supportedTypes: core.FlowType.allTypes(),
13
- docRefs: [
14
- {
15
- label: "Flow Best Practices",
16
- path: "https://help.salesforce.com/s/articleView?id=sf.flow_prep_bestpractices.htm&type=5",
17
- },
18
- {
19
- label: "Don't hard code Record Type IDs in Flow. By Stephen Church.",
20
- path: "https://www.linkedin.com/feed/update/urn:li:activity:6947530300012826624/",
21
- },
22
- ],
23
- });
24
- }
25
-
26
- protected check(
27
- flow: core.Flow,
28
- _options: object | undefined,
29
- _suppressions: Set<string>
30
- ): core.Violation[] {
31
- const salesforceIdRegex = /\b[a-zA-Z0-9]{5}0[a-zA-Z0-9]{9}(?:[a-zA-Z0-9]{3})?\b/g;
32
-
33
- return flow.elements
34
- .filter((node) => salesforceIdRegex.test(JSON.stringify(node)))
35
- .map((node) => new core.Violation(node));
36
- }
37
- }
@@ -1,42 +0,0 @@
1
- import { Flow, FlowType, Violation } from "../internals/internals";
2
- import { RuleCommon } from "../models/RuleCommon";
3
- import { IRuleDefinition } from "../interfaces/IRuleDefinition";
4
-
5
- export class HardcodedUrl extends RuleCommon implements IRuleDefinition {
6
- constructor() {
7
- super(
8
- {
9
- description:
10
- "Avoid hard-coding URLs as they are org-specific. Instead, use a $API formula (preferred) or you can use an environment-specific such as custom labels, custom metadata, or custom settings.",
11
- docRefs: [
12
- {
13
- label: "The Ultimate Guide to Salesforce Flow Best Practices",
14
- path: "https://admin.salesforce.com/blog/2021/the-ultimate-guide-to-flow-best-practices-and-standards",
15
- },
16
- {
17
- label: "Why You Should Avoid Hard Coding and Three Alternative Solutions",
18
- path: "https://admin.salesforce.com/blog/2021/why-you-should-avoid-hard-coding-and-three-alternative-solutions",
19
- },
20
- ],
21
- label: "Hardcoded Url",
22
- name: "HardcodedUrl",
23
- supportedTypes: FlowType.allTypes(),
24
- },
25
- { severity: "warning" }
26
- );
27
- }
28
-
29
- protected check(
30
- flow: Flow,
31
- _options: object | undefined,
32
- _suppressions: Set<string>
33
- ): Violation[] {
34
- if (!flow.elements || flow.elements.length === 0) return [];
35
-
36
- const urlRegex = /https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}force\.com/g;
37
-
38
- return flow.elements
39
- .filter((element) => urlRegex.test(JSON.stringify(element)))
40
- .map((element) => new Violation(element));
41
- }
42
- }
@@ -1,31 +0,0 @@
1
- import * as core from "../internals/internals";
2
- import { RuleCommon } from "../models/RuleCommon";
3
- import { IRuleDefinition } from "../interfaces/IRuleDefinition";
4
-
5
- export class InactiveFlow extends RuleCommon implements IRuleDefinition {
6
- constructor() {
7
- super({
8
- name: "InactiveFlow",
9
- label: "Inactive Flow",
10
- description:
11
- "Like cleaning out your closet: deleting unused flows is essential. Inactive flows can still cause trouble, like accidentally deleting records during testing, or being activated as subflows within parent flows.",
12
- supportedTypes: core.FlowType.allTypes(),
13
- docRefs: [],
14
- });
15
- }
16
-
17
- protected check(
18
- flow: core.Flow,
19
- _options: object | undefined,
20
- _suppressions: Set<string>
21
- ): core.Violation[] {
22
- if (flow.status !== "Active") {
23
- return [
24
- new core.Violation(
25
- new core.FlowAttribute(flow.status, "status", "!= Active")
26
- ),
27
- ];
28
- }
29
- return [];
30
- }
31
- }
@@ -1,89 +0,0 @@
1
- import * as core from "../internals/internals";
2
- import { RuleCommon } from "../models/RuleCommon";
3
- import { IRuleDefinition } from "../interfaces/IRuleDefinition";
4
- export class MissingFaultPath extends RuleCommon implements IRuleDefinition {
5
- protected applicableElements: string[] = [
6
- "recordLookups",
7
- "recordDeletes",
8
- "recordUpdates",
9
- "recordCreates",
10
- "waits",
11
- "actionCalls",
12
- ];
13
- constructor() {
14
- super({
15
- description:
16
- "At times, a flow may fail to execute a configured operation as intended. By default, the flow displays an error message to the user and notifies the admin who created the flow via email. However, you can customize this behavior by incorporating a Fault Path.",
17
- docRefs: [
18
- {
19
- label: "Flow Best Practices",
20
- path: "https://help.salesforce.com/s/articleView?id=sf.flow_prep_bestpractices.htm&type=5",
21
- },
22
- ],
23
- label: "Missing Fault Path",
24
- name: "MissingFaultPath",
25
- supportedTypes: [...core.FlowType.backEndTypes, ...core.FlowType.visualTypes],
26
- });
27
- }
28
- private isValidSubtype(proxyNode: core.FlowNode): boolean {
29
- if (!this.applicableElements.includes(proxyNode.subtype)) {
30
- return false;
31
- }
32
- if (proxyNode.subtype === "waits") {
33
- const elementSubtype: string = (proxyNode.element as Record<string, unknown>)?.["elementSubtype"] as string;
34
- const excludedSubtypes: string[] = ["WaitDuration", "WaitDate"];
35
- return !excludedSubtypes.includes(elementSubtype);
36
- }
37
- return true;
38
- }
39
- protected check(
40
- flow: core.Flow,
41
- _options: object | undefined,
42
- suppressions: Set<string>
43
- ): core.Violation[] {
44
- const compiler = new core.Compiler();
45
- const results: core.Violation[] = [];
46
- const elementsWhereFaultPathIsApplicable = (
47
- flow.elements?.filter((node) => {
48
- const proxyNode = node as unknown as core.FlowNode;
49
- return this.isValidSubtype(proxyNode);
50
- }) as core.FlowNode[]
51
- ).map((e) => e.name);
52
- const isRecordBeforeSave = flow.start.triggerType === "RecordBeforeSave";
53
- const visitCallback = (element: core.FlowNode) => {
54
- if (
55
- !element?.connectors?.find((connector) => connector.type === "faultConnector") &&
56
- elementsWhereFaultPathIsApplicable.includes(element.name)
57
- ) {
58
- if (isRecordBeforeSave && element.subtype === "recordUpdates") {
59
- return;
60
- }
61
- if (!this.isPartOfFaultHandlingFlow(element, flow)) {
62
- if (!suppressions.has(element.name)) {
63
- results.push(new core.Violation(element));
64
- }
65
- }
66
- }
67
- };
68
- compiler.traverseFlow(flow, flow.startReference, visitCallback);
69
- return results;
70
- }
71
- private isPartOfFaultHandlingFlow(element: core.FlowNode, flow: core.Flow): boolean {
72
- const flowelements = flow.elements?.filter(
73
- (el) => el instanceof core.FlowNode
74
- ) as core.FlowNode[];
75
- for (const otherElement of flowelements) {
76
- if (otherElement !== element) {
77
- if (
78
- otherElement.connectors?.find(
79
- (connector) =>
80
- connector.type === "faultConnector" && connector.reference === element.name
81
- )
82
- ) {
83
- return true;
84
- }
85
- }
86
- }
87
- return false;
88
- }
89
- }
@@ -1,39 +0,0 @@
1
- import { IRuleDefinition } from "../interfaces/IRuleDefinition";
2
- import * as core from "../internals/internals";
3
- import { RuleCommon } from "../models/RuleCommon";
4
-
5
- export class MissingMetadataDescription extends RuleCommon implements IRuleDefinition {
6
- constructor() {
7
- super({
8
- description: "Every element must have a meaningful description",
9
- docRefs: [],
10
- label: "Missing Metadata Description",
11
- name: "MissingMetadataDescription",
12
- supportedTypes: core.FlowType.allTypes(),
13
- });
14
- }
15
-
16
- protected check(
17
- flow: core.Flow,
18
- _options: object | undefined,
19
- _suppression: Set<string>
20
- ): core.Violation[] {
21
- const violations: core.Violation[] = [];
22
-
23
- flow.elements
24
- .filter((elem) => {
25
- if (
26
- elem.metaType !== "metadata" &&
27
- !elem.element["description"] &&
28
- elem.subtype !== "start"
29
- ) {
30
- return elem;
31
- }
32
- })
33
- .forEach((elem) => {
34
- return violations.push(new core.Violation(elem));
35
- });
36
-
37
- return violations;
38
- }
39
- }
@@ -1,95 +0,0 @@
1
- import * as core from "../internals/internals";
2
- import { RuleCommon } from "../models/RuleCommon";
3
- import { IRuleDefinition } from "../interfaces/IRuleDefinition";
4
- export class MissingNullHandler extends RuleCommon implements IRuleDefinition {
5
- constructor() {
6
- super({
7
- description:
8
- "When a Get Records operation doesn't find any data, it returns null. To ensure data validation, utilize a decision element on the operation result variable to check for a non-null result.",
9
- docRefs: [],
10
- label: "Missing Null Handler",
11
- name: "MissingNullHandler",
12
- supportedTypes: [...core.FlowType.backEndTypes, ...core.FlowType.visualTypes],
13
- });
14
- }
15
- protected check(
16
- flow: core.Flow,
17
- _options: object | undefined,
18
- suppressions: Set<string>
19
- ): core.Violation[] {
20
- const getOperations = ["recordLookups"];
21
- const getOperationElements: core.FlowNode[] = flow.elements.filter(
22
- (node) => node.metaType === "node" && getOperations.includes(node.subtype)
23
- ) as core.FlowNode[];
24
- const decisionElements: core.FlowNode[] = flow.elements.filter(
25
- (node) => node.metaType === "node" && node.subtype === "decisions"
26
- ) as core.FlowNode[];
27
- const violations: core.FlowNode[] = [];
28
- for (const getElement of getOperationElements) {
29
- if (suppressions.has(getElement.name)) continue;
30
- const elementName = getElement.name;
31
- const assignNulls = String(getElement.element["assignNullValuesIfNoRecordsFound"]).toLowerCase() === "true";
32
- if (!assignNulls) continue;
33
- const hasFaultConnector =
34
- !!getElement.element["faultConnector"] ||
35
- getElement.connectors?.some((c) => c.type === "faultConnector");
36
- if (hasFaultConnector) continue;
37
- const resultReferences: string[] = [];
38
- if (getElement.element["storeOutputAutomatically"]) {
39
- resultReferences.push(elementName);
40
- } else if (getElement.element["outputReference"]) {
41
- resultReferences.push(getElement.element["outputReference"] as string);
42
- } else if (getElement.element["outputAssignments"]) {
43
- const assignments = getElement.element["outputAssignments"] as any[];
44
- for (const a of assignments) {
45
- resultReferences.push(a.assignToReference);
46
- }
47
- }
48
- const resultIsUsed = flow.elements.some((el) => {
49
- if (el.name === getElement.name) return false;
50
- const json = JSON.stringify(el.element);
51
- return resultReferences.some(
52
- (ref) => json.includes(`"${ref}"`) || json.includes(`"${ref}.`)
53
- );
54
- });
55
- if (!resultIsUsed) continue;
56
- let nullCheckFound = false;
57
- for (const decision of decisionElements) {
58
- let rules = decision.element["rules"];
59
- if (!Array.isArray(rules)) rules = [rules];
60
- for (const rule of rules) {
61
- let conditions = rule.conditions;
62
- if (!Array.isArray(conditions)) conditions = [conditions];
63
- for (const condition of conditions) {
64
- let referenceFound = false;
65
- let isNullOperator = false;
66
- let checksFalse = false;
67
- if (condition.leftValueReference) {
68
- const ref = condition.leftValueReference as string;
69
- if (resultReferences.some((r) => ref.startsWith(r))) {
70
- referenceFound = true;
71
- }
72
- }
73
- if (condition.operator === "IsNull") {
74
- isNullOperator = true;
75
- }
76
- const rightBool = condition.rightValue?.booleanValue;
77
- if (rightBool != null && String(rightBool).toLowerCase() === "false") {
78
- checksFalse = true;
79
- }
80
- if (referenceFound && isNullOperator && checksFalse) {
81
- nullCheckFound = true;
82
- break;
83
- }
84
- }
85
- if (nullCheckFound) break;
86
- }
87
- if (nullCheckFound) break;
88
- }
89
- if (!nullCheckFound) {
90
- violations.push(getElement);
91
- }
92
- }
93
- return violations.map((det) => new core.Violation(det));
94
- }
95
- }
@@ -1,33 +0,0 @@
1
- import * as core from "../internals/internals";
2
- import { RuleCommon } from "../models/RuleCommon";
3
- import { IRuleDefinition } from "../interfaces/IRuleDefinition";
4
-
5
- export class ProcessBuilder extends RuleCommon implements IRuleDefinition {
6
- constructor() {
7
- super({
8
- name: "ProcessBuilder",
9
- label: "No Process Builder",
10
- description:
11
- "Salesforce is transitioning away from Workflow Rules and Process Builder in favor of Flow. Ensure you're prepared for this transition by migrating your organization's automation to Flow. Refer to official documentation for more information on the transition process and tools available.",
12
- supportedTypes: core.FlowType.processBuilder,
13
- docRefs: [
14
- {
15
- label: "Process Builder Retirement",
16
- path: "https://help.salesforce.com/s/articleView?id=000389396&type=1",
17
- },
18
- ],
19
- });
20
- }
21
-
22
- protected check(
23
- flow: core.Flow,
24
- _options: object | undefined,
25
- _suppressions: Set<string>
26
- ): core.Violation[] {
27
- return [
28
- new core.Violation(
29
- new core.FlowAttribute("Workflow", "processType", "== Workflow")
30
- ),
31
- ];
32
- }
33
- }