@flow-scanner/lightning-flow-scanner-core 6.0.3
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.
- package/CONTRIBUTING.md +30 -0
- package/LICENSE.md +21 -0
- package/README.md +314 -0
- package/SECURITY.md +26 -0
- package/assets/media/bannerslim.png +0 -0
- package/index.d.ts +21 -0
- package/index.js +83 -0
- package/main/interfaces/AdvancedRuleConfig.d.ts +11 -0
- package/main/interfaces/AdvancedRuleConfig.js +4 -0
- package/main/interfaces/AdvancedRuleDefintion.d.ts +5 -0
- package/main/interfaces/AdvancedRuleDefintion.js +4 -0
- package/main/interfaces/AdvancedSuppression.d.ts +21 -0
- package/main/interfaces/AdvancedSuppression.js +4 -0
- package/main/interfaces/AutoFixable.d.ts +10 -0
- package/main/interfaces/AutoFixable.js +4 -0
- package/main/interfaces/IExceptions.d.ts +5 -0
- package/main/interfaces/IExceptions.js +4 -0
- package/main/interfaces/IRuleConfig.d.ts +3 -0
- package/main/interfaces/IRuleConfig.js +4 -0
- package/main/interfaces/IRuleDefinition.d.ts +17 -0
- package/main/interfaces/IRuleDefinition.js +4 -0
- package/main/interfaces/IRuleOptions.d.ts +4 -0
- package/main/interfaces/IRuleOptions.js +4 -0
- package/main/interfaces/IRulesConfig.d.ts +8 -0
- package/main/interfaces/IRulesConfig.js +4 -0
- package/main/internals/internals.d.ts +17 -0
- package/main/internals/internals.js +64 -0
- package/main/libs/BuildFlow.d.ts +1 -0
- package/main/libs/BuildFlow.js +20 -0
- package/main/libs/Compiler.d.ts +8 -0
- package/main/libs/Compiler.js +70 -0
- package/main/libs/ConvertFlowNodes.d.ts +1 -0
- package/main/libs/ConvertFlowNodes.js +14 -0
- package/main/libs/DynamicRule.d.ts +5 -0
- package/main/libs/DynamicRule.js +19 -0
- package/main/libs/FixFlows.d.ts +3 -0
- package/main/libs/FixFlows.js +110 -0
- package/main/libs/GetRuleDefinitions.d.ts +5 -0
- package/main/libs/GetRuleDefinitions.js +81 -0
- package/main/libs/ParseFlows.d.ts +2 -0
- package/main/libs/ParseFlows.js +111 -0
- package/main/libs/Scan2.d.ts +3 -0
- package/main/libs/Scan2.js +124 -0
- package/main/libs/ScanFlows.d.ts +4 -0
- package/main/libs/ScanFlows.js +103 -0
- package/main/models/AdvancedRule.d.ts +44 -0
- package/main/models/AdvancedRule.js +84 -0
- package/main/models/Flow.d.ts +33 -0
- package/main/models/Flow.js +277 -0
- package/main/models/FlowAttribute.d.ts +7 -0
- package/main/models/FlowAttribute.js +34 -0
- package/main/models/FlowElement.d.ts +10 -0
- package/main/models/FlowElement.js +37 -0
- package/main/models/FlowElementConnector.d.ts +15 -0
- package/main/models/FlowElementConnector.js +50 -0
- package/main/models/FlowMetadata.d.ts +4 -0
- package/main/models/FlowMetadata.js +16 -0
- package/main/models/FlowNode.d.ts +10 -0
- package/main/models/FlowNode.js +169 -0
- package/main/models/FlowResource.d.ts +5 -0
- package/main/models/FlowResource.js +30 -0
- package/main/models/FlowType.d.ts +23 -0
- package/main/models/FlowType.js +80 -0
- package/main/models/FlowVariable.d.ts +6 -0
- package/main/models/FlowVariable.js +31 -0
- package/main/models/LoopRuleCommon.d.ts +9 -0
- package/main/models/LoopRuleCommon.js +48 -0
- package/main/models/ParsedFlow.d.ts +7 -0
- package/main/models/ParsedFlow.js +35 -0
- package/main/models/ResultDetails.d.ts +10 -0
- package/main/models/ResultDetails.js +57 -0
- package/main/models/RuleCommon.d.ts +19 -0
- package/main/models/RuleCommon.js +48 -0
- package/main/models/RuleInfo.d.ts +55 -0
- package/main/models/RuleInfo.js +61 -0
- package/main/models/RuleResult.d.ts +11 -0
- package/main/models/RuleResult.js +44 -0
- package/main/models/ScanResult.d.ts +7 -0
- package/main/models/ScanResult.js +31 -0
- package/main/rules/APIVersion.d.ts +8 -0
- package/main/rules/APIVersion.js +86 -0
- package/main/rules/ActionCallsInLoop.d.ts +6 -0
- package/main/rules/ActionCallsInLoop.js +38 -0
- package/main/rules/AutoLayout.d.ts +8 -0
- package/main/rules/AutoLayout.js +78 -0
- package/main/rules/CopyAPIName.d.ts +6 -0
- package/main/rules/CopyAPIName.js +82 -0
- package/main/rules/CyclomaticComplexity.d.ts +10 -0
- package/main/rules/CyclomaticComplexity.js +111 -0
- package/main/rules/DMLStatementInLoop.d.ts +6 -0
- package/main/rules/DMLStatementInLoop.js +37 -0
- package/main/rules/DuplicateDMLOperation.d.ts +8 -0
- package/main/rules/DuplicateDMLOperation.js +153 -0
- package/main/rules/FlowDescription.d.ts +6 -0
- package/main/rules/FlowDescription.js +76 -0
- package/main/rules/FlowName.d.ts +7 -0
- package/main/rules/FlowName.js +80 -0
- package/main/rules/GetRecordAllFields.d.ts +6 -0
- package/main/rules/GetRecordAllFields.js +101 -0
- package/main/rules/HardcodedId.d.ts +6 -0
- package/main/rules/HardcodedId.js +87 -0
- package/main/rules/HardcodedUrl.d.ts +6 -0
- package/main/rules/HardcodedUrl.js +50 -0
- package/main/rules/InactiveFlow.d.ts +6 -0
- package/main/rules/InactiveFlow.js +73 -0
- package/main/rules/MissingFaultPath.d.ts +12 -0
- package/main/rules/MissingFaultPath.js +161 -0
- package/main/rules/MissingNullHandler.d.ts +6 -0
- package/main/rules/MissingNullHandler.js +152 -0
- package/main/rules/ProcessBuilder.d.ts +8 -0
- package/main/rules/ProcessBuilder.js +77 -0
- package/main/rules/RecursiveAfterUpdate.d.ts +7 -0
- package/main/rules/RecursiveAfterUpdate.js +124 -0
- package/main/rules/SOQLQueryInLoop.d.ts +6 -0
- package/main/rules/SOQLQueryInLoop.js +35 -0
- package/main/rules/SameRecordFieldUpdates.d.ts +7 -0
- package/main/rules/SameRecordFieldUpdates.js +111 -0
- package/main/rules/TriggerOrder.d.ts +7 -0
- package/main/rules/TriggerOrder.js +101 -0
- package/main/rules/UnconnectedElement.d.ts +7 -0
- package/main/rules/UnconnectedElement.js +93 -0
- package/main/rules/UnsafeRunningContext.d.ts +6 -0
- package/main/rules/UnsafeRunningContext.js +86 -0
- package/main/rules/UnusedVariable.d.ts +6 -0
- package/main/rules/UnusedVariable.js +100 -0
- package/main/store/DefaultRuleStore.d.ts +2 -0
- package/main/store/DefaultRuleStore.js +68 -0
- package/package.json +88 -0
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", {
|
|
3
|
+
value: true
|
|
4
|
+
});
|
|
5
|
+
Object.defineProperty(exports, "scan2", {
|
|
6
|
+
enumerable: true,
|
|
7
|
+
get: function() {
|
|
8
|
+
return scan2;
|
|
9
|
+
}
|
|
10
|
+
});
|
|
11
|
+
const _internals = require("../internals/internals");
|
|
12
|
+
const _DefaultRuleStore = require("../store/DefaultRuleStore");
|
|
13
|
+
const _DynamicRule = require("./DynamicRule");
|
|
14
|
+
function _define_property(obj, key, value) {
|
|
15
|
+
if (key in obj) {
|
|
16
|
+
Object.defineProperty(obj, key, {
|
|
17
|
+
value: value,
|
|
18
|
+
enumerable: true,
|
|
19
|
+
configurable: true,
|
|
20
|
+
writable: true
|
|
21
|
+
});
|
|
22
|
+
} else {
|
|
23
|
+
obj[key] = value;
|
|
24
|
+
}
|
|
25
|
+
return obj;
|
|
26
|
+
}
|
|
27
|
+
function _object_spread(target) {
|
|
28
|
+
for(var i = 1; i < arguments.length; i++){
|
|
29
|
+
var source = arguments[i] != null ? arguments[i] : {};
|
|
30
|
+
var ownKeys = Object.keys(source);
|
|
31
|
+
if (typeof Object.getOwnPropertySymbols === "function") {
|
|
32
|
+
ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function(sym) {
|
|
33
|
+
return Object.getOwnPropertyDescriptor(source, sym).enumerable;
|
|
34
|
+
}));
|
|
35
|
+
}
|
|
36
|
+
ownKeys.forEach(function(key) {
|
|
37
|
+
_define_property(target, key, source[key]);
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
return target;
|
|
41
|
+
}
|
|
42
|
+
function ownKeys(object, enumerableOnly) {
|
|
43
|
+
var keys = Object.keys(object);
|
|
44
|
+
if (Object.getOwnPropertySymbols) {
|
|
45
|
+
var symbols = Object.getOwnPropertySymbols(object);
|
|
46
|
+
if (enumerableOnly) {
|
|
47
|
+
symbols = symbols.filter(function(sym) {
|
|
48
|
+
return Object.getOwnPropertyDescriptor(object, sym).enumerable;
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
keys.push.apply(keys, symbols);
|
|
52
|
+
}
|
|
53
|
+
return keys;
|
|
54
|
+
}
|
|
55
|
+
function _object_spread_props(target, source) {
|
|
56
|
+
source = source != null ? source : {};
|
|
57
|
+
if (Object.getOwnPropertyDescriptors) {
|
|
58
|
+
Object.defineProperties(target, Object.getOwnPropertyDescriptors(source));
|
|
59
|
+
} else {
|
|
60
|
+
ownKeys(Object(source)).forEach(function(key) {
|
|
61
|
+
Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
return target;
|
|
65
|
+
}
|
|
66
|
+
function scan2(parsedFlows, ruleOptions) {
|
|
67
|
+
const flows = parsedFlows.map((parsedFlow)=>parsedFlow.flow);
|
|
68
|
+
const scanResults = [];
|
|
69
|
+
for (const flow of flows){
|
|
70
|
+
scanResults.push(scanFlowWithConfig(flow, ruleOptions));
|
|
71
|
+
}
|
|
72
|
+
return scanResults;
|
|
73
|
+
}
|
|
74
|
+
function ruleAndConfig(ruleOptions) {
|
|
75
|
+
// for unit tests, use a small set of rules
|
|
76
|
+
const ruleConfiguration = unifiedRuleConfig(ruleOptions);
|
|
77
|
+
let allRules = _object_spread({}, _DefaultRuleStore.DefaultRuleStore, _DefaultRuleStore.BetaRuleStore);
|
|
78
|
+
if (// overrideConfig === "true" &&
|
|
79
|
+
(ruleOptions === null || ruleOptions === void 0 ? void 0 : ruleOptions.rules) && Object.keys(ruleOptions.rules).length > 0) {
|
|
80
|
+
allRules = Object.entries(allRules).reduce((accumulator, [ruleName, rule])=>{
|
|
81
|
+
var _ruleOptions_rules;
|
|
82
|
+
if (ruleOptions === null || ruleOptions === void 0 ? void 0 : (_ruleOptions_rules = ruleOptions.rules) === null || _ruleOptions_rules === void 0 ? void 0 : _ruleOptions_rules[ruleName]) {
|
|
83
|
+
accumulator[ruleName] = rule;
|
|
84
|
+
}
|
|
85
|
+
return accumulator;
|
|
86
|
+
}, {});
|
|
87
|
+
}
|
|
88
|
+
return [
|
|
89
|
+
allRules,
|
|
90
|
+
ruleConfiguration
|
|
91
|
+
];
|
|
92
|
+
}
|
|
93
|
+
function scanFlowWithConfig(flow, ruleOptions) {
|
|
94
|
+
const [allRules, ruleConfiguration] = ruleAndConfig(ruleOptions);
|
|
95
|
+
const ruleResults = [];
|
|
96
|
+
for (const [ruleName] of Object.entries(allRules)){
|
|
97
|
+
var _ruleConfiguration_ruleName, _ruleConfiguration_ruleName1, _ruleOptions_exceptions_flowName, _ruleOptions_exceptions;
|
|
98
|
+
const rule = new _DynamicRule.DynamicRule(ruleName);
|
|
99
|
+
if (!rule.supportedTypes.includes(flow.type) || (ruleConfiguration === null || ruleConfiguration === void 0 ? void 0 : (_ruleConfiguration_ruleName = ruleConfiguration[ruleName]) === null || _ruleConfiguration_ruleName === void 0 ? void 0 : _ruleConfiguration_ruleName.disabled) === true) {
|
|
100
|
+
ruleResults.push(new _internals.RuleResult(rule, []));
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
if (ruleConfiguration === null || ruleConfiguration === void 0 ? void 0 : (_ruleConfiguration_ruleName1 = ruleConfiguration[ruleName]) === null || _ruleConfiguration_ruleName1 === void 0 ? void 0 : _ruleConfiguration_ruleName1.severity) {
|
|
104
|
+
rule.severity = ruleConfiguration[ruleName].severity;
|
|
105
|
+
}
|
|
106
|
+
const flowName = flow.name;
|
|
107
|
+
var _ruleConfiguration_ruleName2;
|
|
108
|
+
const userRuleConfiguration = (_ruleConfiguration_ruleName2 = ruleConfiguration[ruleName]) !== null && _ruleConfiguration_ruleName2 !== void 0 ? _ruleConfiguration_ruleName2 : {};
|
|
109
|
+
var _ruleOptions_exceptions_flowName_ruleName;
|
|
110
|
+
const userFlowSuppressions = (_ruleOptions_exceptions_flowName_ruleName = ruleOptions === null || ruleOptions === void 0 ? void 0 : (_ruleOptions_exceptions = ruleOptions.exceptions) === null || _ruleOptions_exceptions === void 0 ? void 0 : (_ruleOptions_exceptions_flowName = _ruleOptions_exceptions[flowName]) === null || _ruleOptions_exceptions_flowName === void 0 ? void 0 : _ruleOptions_exceptions_flowName[ruleName]) !== null && _ruleOptions_exceptions_flowName_ruleName !== void 0 ? _ruleOptions_exceptions_flowName_ruleName : [];
|
|
111
|
+
ruleResults.push(rule.execute2(flow, userRuleConfiguration, userFlowSuppressions));
|
|
112
|
+
}
|
|
113
|
+
return new _internals.ScanResult(flow, ruleResults);
|
|
114
|
+
}
|
|
115
|
+
function unifiedRuleConfig(ruleOptions) {
|
|
116
|
+
var _ruleOptions_rules;
|
|
117
|
+
const configuredRules = (_ruleOptions_rules = ruleOptions === null || ruleOptions === void 0 ? void 0 : ruleOptions.rules) !== null && _ruleOptions_rules !== void 0 ? _ruleOptions_rules : {};
|
|
118
|
+
const activeConfiguredRules = Object.entries(configuredRules).reduce((accumulator, [ruleName, config])=>{
|
|
119
|
+
return _object_spread_props(_object_spread({}, accumulator), {
|
|
120
|
+
[ruleName]: config
|
|
121
|
+
});
|
|
122
|
+
}, {});
|
|
123
|
+
return activeConfiguredRules;
|
|
124
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { Flow, IRulesConfig, ScanResult } from "../../main/internals/internals";
|
|
2
|
+
import { ParsedFlow } from "../models/ParsedFlow";
|
|
3
|
+
export declare function scan(parsedFlows: ParsedFlow[], ruleOptions?: IRulesConfig): ScanResult[];
|
|
4
|
+
export declare function ScanFlows(flows: Flow[], ruleOptions?: IRulesConfig): ScanResult[];
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", {
|
|
3
|
+
value: true
|
|
4
|
+
});
|
|
5
|
+
function _export(target, all) {
|
|
6
|
+
for(var name in all)Object.defineProperty(target, name, {
|
|
7
|
+
enumerable: true,
|
|
8
|
+
get: Object.getOwnPropertyDescriptor(all, name).get
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
_export(exports, {
|
|
12
|
+
get ScanFlows () {
|
|
13
|
+
return ScanFlows;
|
|
14
|
+
},
|
|
15
|
+
get scan () {
|
|
16
|
+
return scan;
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
const _internals = require("../../main/internals/internals");
|
|
20
|
+
const _GetRuleDefinitions = require("./GetRuleDefinitions");
|
|
21
|
+
const _Scan2 = require("./Scan2");
|
|
22
|
+
const { IS_NEW_SCAN_ENABLED: isNewScanEnabled, OVERRIDE_CONFIG: overrideConfig } = process.env;
|
|
23
|
+
function scan(parsedFlows, ruleOptions) {
|
|
24
|
+
// TD see jest.env-setup.ts for testing scan2
|
|
25
|
+
if (isNewScanEnabled === "true" && overrideConfig !== null && overrideConfig !== undefined) {
|
|
26
|
+
return (0, _Scan2.scan2)(parsedFlows, ruleOptions);
|
|
27
|
+
}
|
|
28
|
+
const flows = [];
|
|
29
|
+
for (const flow of parsedFlows){
|
|
30
|
+
if (!flow.errorMessage && flow.flow) {
|
|
31
|
+
flows.push(flow.flow);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
let scanResults;
|
|
35
|
+
if ((ruleOptions === null || ruleOptions === void 0 ? void 0 : ruleOptions.rules) && Object.entries(ruleOptions.rules).length > 0) {
|
|
36
|
+
scanResults = ScanFlows(flows, ruleOptions);
|
|
37
|
+
} else {
|
|
38
|
+
scanResults = ScanFlows(flows);
|
|
39
|
+
}
|
|
40
|
+
generalSuppressions(scanResults, ruleOptions);
|
|
41
|
+
return scanResults;
|
|
42
|
+
}
|
|
43
|
+
function ScanFlows(flows, ruleOptions) {
|
|
44
|
+
const flowResults = [];
|
|
45
|
+
let selectedRules = [];
|
|
46
|
+
if (ruleOptions && ruleOptions.rules) {
|
|
47
|
+
const ruleMap = new Map();
|
|
48
|
+
for (const [ruleName, rule] of Object.entries(ruleOptions.rules)){
|
|
49
|
+
ruleMap.set(ruleName, rule);
|
|
50
|
+
}
|
|
51
|
+
selectedRules = (0, _GetRuleDefinitions.GetRuleDefinitions)(ruleMap);
|
|
52
|
+
} else {
|
|
53
|
+
selectedRules = (0, _GetRuleDefinitions.GetRuleDefinitions)();
|
|
54
|
+
}
|
|
55
|
+
for (const flow of flows){
|
|
56
|
+
const ruleResults = [];
|
|
57
|
+
for (const rule of selectedRules){
|
|
58
|
+
try {
|
|
59
|
+
if (rule.supportedTypes.includes(flow.type)) {
|
|
60
|
+
let config = undefined;
|
|
61
|
+
if (ruleOptions && ruleOptions["rules"] && ruleOptions["rules"][rule.name]) {
|
|
62
|
+
config = ruleOptions["rules"][rule.name];
|
|
63
|
+
}
|
|
64
|
+
const result = config && Object.keys(config).length > 0 ? rule.execute(flow, config) : rule.execute(flow);
|
|
65
|
+
if (result.severity !== rule.severity) {
|
|
66
|
+
result.severity = rule.severity;
|
|
67
|
+
}
|
|
68
|
+
ruleResults.push(result);
|
|
69
|
+
} else {
|
|
70
|
+
ruleResults.push(new _internals.RuleResult(rule, []));
|
|
71
|
+
}
|
|
72
|
+
} catch (error) {
|
|
73
|
+
const message = `Something went wrong while executing ${rule.name} in the Flow: ${flow.name} with error ${error}`;
|
|
74
|
+
ruleResults.push(new _internals.RuleResult(rule, [], message));
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
flowResults.push(new _internals.ScanResult(flow, ruleResults));
|
|
78
|
+
}
|
|
79
|
+
return flowResults;
|
|
80
|
+
}
|
|
81
|
+
function generalSuppressions(scanResults, ruleOptions) {
|
|
82
|
+
if (!(ruleOptions === null || ruleOptions === void 0 ? void 0 : ruleOptions.exceptions)) {
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
const applyExceptionToResults = (ruleResult, exceptions)=>{
|
|
86
|
+
const filteredDetails = ruleResult.details.filter((detail)=>!exceptions.includes(detail.name));
|
|
87
|
+
ruleResult.details = filteredDetails;
|
|
88
|
+
ruleResult.occurs = filteredDetails.length > 0;
|
|
89
|
+
};
|
|
90
|
+
for (const [flowName, exceptionElements] of Object.entries(ruleOptions.exceptions)){
|
|
91
|
+
const matchingScanResult = scanResults.find((result)=>result.flow.name === flowName);
|
|
92
|
+
if (!matchingScanResult) {
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
for (const ruleResult of matchingScanResult.ruleResults){
|
|
96
|
+
const exceptions = exceptionElements[ruleResult.ruleName];
|
|
97
|
+
if (!exceptions) {
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
applyExceptionToResults(ruleResult, exceptions);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { AdvancedConfig } from "../interfaces/AdvancedRuleConfig";
|
|
2
|
+
import { AdvancedRuleDefinition } from "../interfaces/AdvancedRuleDefintion";
|
|
3
|
+
import { IRuleDefinition } from "../internals/internals";
|
|
4
|
+
import { Flow } from "./Flow";
|
|
5
|
+
import { RuleCommon } from "./RuleCommon";
|
|
6
|
+
import { RuleInfo } from "./RuleInfo";
|
|
7
|
+
import { RuleResult } from "./RuleResult";
|
|
8
|
+
/**
|
|
9
|
+
* Abstract base class for advanced rules, extending {@link RuleCommon} and implementing
|
|
10
|
+
* {@link AdvancedRuleDefinition} and {@link IRuleDefinition}.
|
|
11
|
+
*
|
|
12
|
+
* @remarks
|
|
13
|
+
* This class provides a structure for advanced rule implementations, including
|
|
14
|
+
* support for advanced suppression and user-defined suppressions.
|
|
15
|
+
*
|
|
16
|
+
* @param info - The rule metadata information.
|
|
17
|
+
* @param optional - Optional configuration, such as severity.
|
|
18
|
+
*/
|
|
19
|
+
export declare abstract class AdvancedRule extends RuleCommon implements AdvancedRuleDefinition, IRuleDefinition {
|
|
20
|
+
/**
|
|
21
|
+
* Constructs an instance of {@link AdvancedRule}.
|
|
22
|
+
*
|
|
23
|
+
* @param info - The rule metadata information.
|
|
24
|
+
* @param optional - Optional configuration, such as severity.
|
|
25
|
+
*/
|
|
26
|
+
constructor(info: RuleInfo, optional?: {
|
|
27
|
+
severity?: string;
|
|
28
|
+
});
|
|
29
|
+
/**
|
|
30
|
+
* Your rule
|
|
31
|
+
* @param flow - The flow to analyze.
|
|
32
|
+
* @param ruleOptions - Optional rule-specific options.
|
|
33
|
+
*/
|
|
34
|
+
abstract execute(flow: Flow, ruleOptions?: object): RuleResult;
|
|
35
|
+
/**
|
|
36
|
+
* Executes the rule with advanced configuration and applies suppressions.
|
|
37
|
+
*
|
|
38
|
+
* @param flow - The flow to analyze.
|
|
39
|
+
* @param ruleConfiguration - Optional advanced rule configuration.
|
|
40
|
+
* @param userFlowSuppressions - Optional list of user-defined suppressions.
|
|
41
|
+
* @returns The result of rule execution after applying suppressions.
|
|
42
|
+
*/
|
|
43
|
+
execute2(flow: Flow, ruleConfiguration?: AdvancedConfig, userFlowSuppressions?: string[]): RuleResult;
|
|
44
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", {
|
|
3
|
+
value: true
|
|
4
|
+
});
|
|
5
|
+
Object.defineProperty(exports, "AdvancedRule", {
|
|
6
|
+
enumerable: true,
|
|
7
|
+
get: function() {
|
|
8
|
+
return AdvancedRule;
|
|
9
|
+
}
|
|
10
|
+
});
|
|
11
|
+
const _util = require("util");
|
|
12
|
+
const _RuleCommon = require("./RuleCommon");
|
|
13
|
+
const _RuleResult = require("./RuleResult");
|
|
14
|
+
let AdvancedRule = class AdvancedRule extends _RuleCommon.RuleCommon {
|
|
15
|
+
/**
|
|
16
|
+
* Executes the rule with advanced configuration and applies suppressions.
|
|
17
|
+
*
|
|
18
|
+
* @param flow - The flow to analyze.
|
|
19
|
+
* @param ruleConfiguration - Optional advanced rule configuration.
|
|
20
|
+
* @param userFlowSuppressions - Optional list of user-defined suppressions.
|
|
21
|
+
* @returns The result of rule execution after applying suppressions.
|
|
22
|
+
*/ execute2(flow, ruleConfiguration, userFlowSuppressions) {
|
|
23
|
+
if (!hasClassicRuleDefinition(this)) {
|
|
24
|
+
return new _RuleResult.RuleResult(this, []);
|
|
25
|
+
}
|
|
26
|
+
let ruleResult;
|
|
27
|
+
try {
|
|
28
|
+
ruleResult = this.execute(flow, ruleConfiguration);
|
|
29
|
+
} catch (error) {
|
|
30
|
+
return new _RuleResult.RuleResult(this, [], (0, _util.inspect)(error));
|
|
31
|
+
}
|
|
32
|
+
if (hasAdvancedSuppression(this)) {
|
|
33
|
+
ruleResult = this.suppress(ruleResult, ruleConfiguration);
|
|
34
|
+
}
|
|
35
|
+
ruleResult = generalSuppressions(ruleResult, userFlowSuppressions);
|
|
36
|
+
return ruleResult;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Constructs an instance of {@link AdvancedRule}.
|
|
40
|
+
*
|
|
41
|
+
* @param info - The rule metadata information.
|
|
42
|
+
* @param optional - Optional configuration, such as severity.
|
|
43
|
+
*/ constructor(info, optional){
|
|
44
|
+
super(info, optional);
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* Applies user-defined suppressions to the rule result.
|
|
49
|
+
*
|
|
50
|
+
* @param ruleResult - The result of rule execution.
|
|
51
|
+
* @param userFlowRuleSuppressions - Optional list of suppression names to filter out.
|
|
52
|
+
* @returns The filtered rule result.
|
|
53
|
+
*/ function generalSuppressions(ruleResult, userFlowRuleSuppressions) {
|
|
54
|
+
if (!userFlowRuleSuppressions || userFlowRuleSuppressions.length === 0) {
|
|
55
|
+
return ruleResult;
|
|
56
|
+
}
|
|
57
|
+
const filteredDetails = ruleResult.details.filter((detail)=>!userFlowRuleSuppressions.includes(detail.name));
|
|
58
|
+
ruleResult.details = filteredDetails;
|
|
59
|
+
ruleResult.occurs = filteredDetails.length > 0;
|
|
60
|
+
return ruleResult;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Type guard to check if a value is a function.
|
|
64
|
+
*
|
|
65
|
+
* @param val - The value to check.
|
|
66
|
+
* @returns True if the value is a function, false otherwise.
|
|
67
|
+
*/ // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
|
68
|
+
const isFunction = (val)=>typeof val === "function";
|
|
69
|
+
/**
|
|
70
|
+
* Type guard to check if an instance implements {@link AdvancedSuppression}.
|
|
71
|
+
*
|
|
72
|
+
* @param instance - The instance to check.
|
|
73
|
+
* @returns True if the instance has a suppress method, false otherwise.
|
|
74
|
+
*/ function hasAdvancedSuppression(instance) {
|
|
75
|
+
return isFunction(instance.suppress);
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Type guard to check if an instance implements {@link IRuleDefinition}.
|
|
79
|
+
*
|
|
80
|
+
* @param instance - The instance to check.
|
|
81
|
+
* @returns True if the instance has an execute method, false otherwise.
|
|
82
|
+
*/ function hasClassicRuleDefinition(instance) {
|
|
83
|
+
return isFunction(instance.execute);
|
|
84
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { FlowElement } from "./FlowElement";
|
|
2
|
+
export declare class Flow {
|
|
3
|
+
/**
|
|
4
|
+
* Categorized flow contents that should be used in the rule implementation
|
|
5
|
+
*/
|
|
6
|
+
elements?: FlowElement[];
|
|
7
|
+
fsPath: any;
|
|
8
|
+
interviewLabel?: string;
|
|
9
|
+
label: string;
|
|
10
|
+
name?: string;
|
|
11
|
+
processMetadataValues?: any;
|
|
12
|
+
processType?: any;
|
|
13
|
+
root?: any;
|
|
14
|
+
start?: any;
|
|
15
|
+
startElementReference?: any;
|
|
16
|
+
startReference: any;
|
|
17
|
+
status?: any;
|
|
18
|
+
triggerOrder?: number;
|
|
19
|
+
type?: any;
|
|
20
|
+
/**
|
|
21
|
+
* XML to JSON conversion in raw format
|
|
22
|
+
*/
|
|
23
|
+
xmldata: any;
|
|
24
|
+
private flowMetadata;
|
|
25
|
+
private flowNodes;
|
|
26
|
+
private flowResources;
|
|
27
|
+
private flowVariables;
|
|
28
|
+
constructor(path?: string, data?: unknown);
|
|
29
|
+
preProcessNodes(): void;
|
|
30
|
+
toXMLString(): string;
|
|
31
|
+
private findStart;
|
|
32
|
+
private generateDoc;
|
|
33
|
+
}
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", {
|
|
3
|
+
value: true
|
|
4
|
+
});
|
|
5
|
+
Object.defineProperty(exports, "Flow", {
|
|
6
|
+
enumerable: true,
|
|
7
|
+
get: function() {
|
|
8
|
+
return Flow;
|
|
9
|
+
}
|
|
10
|
+
});
|
|
11
|
+
const _fastxmlparser = require("fast-xml-parser");
|
|
12
|
+
const _path = /*#__PURE__*/ _interop_require_wildcard(require("path"));
|
|
13
|
+
const _FlowMetadata = require("./FlowMetadata");
|
|
14
|
+
const _FlowNode = require("./FlowNode");
|
|
15
|
+
const _FlowResource = require("./FlowResource");
|
|
16
|
+
const _FlowVariable = require("./FlowVariable");
|
|
17
|
+
function _define_property(obj, key, value) {
|
|
18
|
+
if (key in obj) {
|
|
19
|
+
Object.defineProperty(obj, key, {
|
|
20
|
+
value: value,
|
|
21
|
+
enumerable: true,
|
|
22
|
+
configurable: true,
|
|
23
|
+
writable: true
|
|
24
|
+
});
|
|
25
|
+
} else {
|
|
26
|
+
obj[key] = value;
|
|
27
|
+
}
|
|
28
|
+
return obj;
|
|
29
|
+
}
|
|
30
|
+
function _getRequireWildcardCache(nodeInterop) {
|
|
31
|
+
if (typeof WeakMap !== "function") return null;
|
|
32
|
+
var cacheBabelInterop = new WeakMap();
|
|
33
|
+
var cacheNodeInterop = new WeakMap();
|
|
34
|
+
return (_getRequireWildcardCache = function(nodeInterop) {
|
|
35
|
+
return nodeInterop ? cacheNodeInterop : cacheBabelInterop;
|
|
36
|
+
})(nodeInterop);
|
|
37
|
+
}
|
|
38
|
+
function _interop_require_wildcard(obj, nodeInterop) {
|
|
39
|
+
if (!nodeInterop && obj && obj.__esModule) {
|
|
40
|
+
return obj;
|
|
41
|
+
}
|
|
42
|
+
if (obj === null || typeof obj !== "object" && typeof obj !== "function") {
|
|
43
|
+
return {
|
|
44
|
+
default: obj
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
var cache = _getRequireWildcardCache(nodeInterop);
|
|
48
|
+
if (cache && cache.has(obj)) {
|
|
49
|
+
return cache.get(obj);
|
|
50
|
+
}
|
|
51
|
+
var newObj = {
|
|
52
|
+
__proto__: null
|
|
53
|
+
};
|
|
54
|
+
var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor;
|
|
55
|
+
for(var key in obj){
|
|
56
|
+
if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) {
|
|
57
|
+
var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null;
|
|
58
|
+
if (desc && (desc.get || desc.set)) {
|
|
59
|
+
Object.defineProperty(newObj, key, desc);
|
|
60
|
+
} else {
|
|
61
|
+
newObj[key] = obj[key];
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
newObj.default = obj;
|
|
66
|
+
if (cache) {
|
|
67
|
+
cache.set(obj, newObj);
|
|
68
|
+
}
|
|
69
|
+
return newObj;
|
|
70
|
+
}
|
|
71
|
+
function _object_spread(target) {
|
|
72
|
+
for(var i = 1; i < arguments.length; i++){
|
|
73
|
+
var source = arguments[i] != null ? arguments[i] : {};
|
|
74
|
+
var ownKeys = Object.keys(source);
|
|
75
|
+
if (typeof Object.getOwnPropertySymbols === "function") {
|
|
76
|
+
ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function(sym) {
|
|
77
|
+
return Object.getOwnPropertyDescriptor(source, sym).enumerable;
|
|
78
|
+
}));
|
|
79
|
+
}
|
|
80
|
+
ownKeys.forEach(function(key) {
|
|
81
|
+
_define_property(target, key, source[key]);
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
return target;
|
|
85
|
+
}
|
|
86
|
+
let Flow = class Flow {
|
|
87
|
+
preProcessNodes() {
|
|
88
|
+
this.label = this.xmldata.label;
|
|
89
|
+
this.interviewLabel = this.xmldata.interviewLabel;
|
|
90
|
+
this.processType = this.xmldata.processType;
|
|
91
|
+
this.processMetadataValues = this.xmldata.processMetadataValues;
|
|
92
|
+
this.startElementReference = this.xmldata.startElementReference;
|
|
93
|
+
this.start = this.xmldata.start;
|
|
94
|
+
this.status = this.xmldata.status;
|
|
95
|
+
this.type = this.xmldata.processType;
|
|
96
|
+
this.triggerOrder = this.xmldata.triggerOrder;
|
|
97
|
+
const allNodes = [];
|
|
98
|
+
for(const nodeType in this.xmldata){
|
|
99
|
+
// Skip xmlns and attributes (updated: generalized from your commented line)
|
|
100
|
+
if (nodeType.startsWith("@_") || nodeType === "@xmlns") {
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
const data = this.xmldata[nodeType];
|
|
104
|
+
if (this.flowMetadata.includes(nodeType)) {
|
|
105
|
+
if (Array.isArray(data)) {
|
|
106
|
+
for (const node of data){
|
|
107
|
+
allNodes.push(new _FlowMetadata.FlowMetadata(nodeType, node));
|
|
108
|
+
}
|
|
109
|
+
} else {
|
|
110
|
+
allNodes.push(new _FlowMetadata.FlowMetadata(nodeType, data));
|
|
111
|
+
}
|
|
112
|
+
} else if (this.flowVariables.includes(nodeType)) {
|
|
113
|
+
if (Array.isArray(data)) {
|
|
114
|
+
for (const node of data){
|
|
115
|
+
allNodes.push(new _FlowVariable.FlowVariable(node.name, nodeType, node));
|
|
116
|
+
}
|
|
117
|
+
} else {
|
|
118
|
+
allNodes.push(new _FlowVariable.FlowVariable(data.name, nodeType, data));
|
|
119
|
+
}
|
|
120
|
+
} else if (this.flowNodes.includes(nodeType)) {
|
|
121
|
+
if (Array.isArray(data)) {
|
|
122
|
+
for (const node of data){
|
|
123
|
+
allNodes.push(new _FlowNode.FlowNode(node.name, nodeType, node));
|
|
124
|
+
}
|
|
125
|
+
} else {
|
|
126
|
+
allNodes.push(new _FlowNode.FlowNode(data.name, nodeType, data));
|
|
127
|
+
}
|
|
128
|
+
} else if (this.flowResources.includes(nodeType)) {
|
|
129
|
+
if (Array.isArray(data)) {
|
|
130
|
+
for (const node of data){
|
|
131
|
+
allNodes.push(new _FlowResource.FlowResource(node.name, nodeType, node));
|
|
132
|
+
}
|
|
133
|
+
} else {
|
|
134
|
+
allNodes.push(new _FlowResource.FlowResource(data.name, nodeType, data));
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
this.elements = allNodes;
|
|
139
|
+
this.startReference = this.findStart();
|
|
140
|
+
}
|
|
141
|
+
toXMLString() {
|
|
142
|
+
try {
|
|
143
|
+
return this.generateDoc();
|
|
144
|
+
} catch (exception) {
|
|
145
|
+
console.warn(`Unable to write xml, caught an error ${exception.toString()}`);
|
|
146
|
+
return "";
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
findStart() {
|
|
150
|
+
let start = "";
|
|
151
|
+
const flowElements = this.elements.filter((node)=>node instanceof _FlowNode.FlowNode);
|
|
152
|
+
if (this.startElementReference) {
|
|
153
|
+
start = this.startElementReference;
|
|
154
|
+
} else if (flowElements.find((n)=>{
|
|
155
|
+
return n.subtype === "start";
|
|
156
|
+
})) {
|
|
157
|
+
const startElement = flowElements.find((n)=>{
|
|
158
|
+
return n.subtype === "start";
|
|
159
|
+
});
|
|
160
|
+
start = startElement.connectors[0]["reference"];
|
|
161
|
+
}
|
|
162
|
+
return start;
|
|
163
|
+
}
|
|
164
|
+
generateDoc() {
|
|
165
|
+
// eslint-disable-next-line sonarjs/no-clear-text-protocols
|
|
166
|
+
const flowXmlNamespace = "http://soap.sforce.com/2006/04/metadata";
|
|
167
|
+
const builderOptions = {
|
|
168
|
+
format: true,
|
|
169
|
+
ignoreAttributes: false,
|
|
170
|
+
attributeNamePrefix: "@_",
|
|
171
|
+
suppressEmptyNode: false,
|
|
172
|
+
suppressBooleanAttributes: false // NEW: Force ="true" for boolean-like strings (fixes missing value)
|
|
173
|
+
};
|
|
174
|
+
const builder = new _fastxmlparser.XMLBuilder(builderOptions);
|
|
175
|
+
// Fallback: Inject xmlns as attribute if missing
|
|
176
|
+
const xmldataWithNs = _object_spread({}, this.xmldata);
|
|
177
|
+
if (!xmldataWithNs["@_xmlns"]) {
|
|
178
|
+
xmldataWithNs["@_xmlns"] = flowXmlNamespace;
|
|
179
|
+
}
|
|
180
|
+
// Optional: Add xsi if needed (often in parsed data; test has it in root)
|
|
181
|
+
if (!xmldataWithNs["@_xmlns:xsi"]) {
|
|
182
|
+
xmldataWithNs["@_xmlns:xsi"] = "http://www.w3.org/2001/XMLSchema-instance";
|
|
183
|
+
}
|
|
184
|
+
// Build: Wrap in { Flow: ... }
|
|
185
|
+
const rootObj = {
|
|
186
|
+
Flow: xmldataWithNs
|
|
187
|
+
};
|
|
188
|
+
return builder.build(rootObj);
|
|
189
|
+
}
|
|
190
|
+
constructor(path, data){
|
|
191
|
+
/**
|
|
192
|
+
* Categorized flow contents that should be used in the rule implementation
|
|
193
|
+
*/ _define_property(this, "elements", void 0);
|
|
194
|
+
_define_property(this, "fsPath", void 0);
|
|
195
|
+
_define_property(this, "interviewLabel", void 0);
|
|
196
|
+
_define_property(this, "label", void 0);
|
|
197
|
+
_define_property(this, "name", void 0);
|
|
198
|
+
_define_property(this, "processMetadataValues", void 0);
|
|
199
|
+
_define_property(this, "processType", void 0);
|
|
200
|
+
_define_property(this, "root", void 0);
|
|
201
|
+
_define_property(this, "start", void 0);
|
|
202
|
+
_define_property(this, "startElementReference", void 0);
|
|
203
|
+
_define_property(this, "startReference", void 0);
|
|
204
|
+
_define_property(this, "status", void 0);
|
|
205
|
+
_define_property(this, "triggerOrder", void 0);
|
|
206
|
+
_define_property(this, "type", void 0);
|
|
207
|
+
/**
|
|
208
|
+
* XML to JSON conversion in raw format
|
|
209
|
+
*/ _define_property(this, "xmldata", void 0);
|
|
210
|
+
_define_property(this, "flowMetadata", [
|
|
211
|
+
"description",
|
|
212
|
+
"apiVersion",
|
|
213
|
+
"processMetadataValues",
|
|
214
|
+
"processType",
|
|
215
|
+
"interviewLabel",
|
|
216
|
+
"label",
|
|
217
|
+
"status",
|
|
218
|
+
"runInMode",
|
|
219
|
+
"startElementReference",
|
|
220
|
+
"isTemplate",
|
|
221
|
+
"fullName",
|
|
222
|
+
"timeZoneSidKey",
|
|
223
|
+
"isAdditionalPermissionRequiredToRun",
|
|
224
|
+
"migratedFromWorkflowRuleName",
|
|
225
|
+
"triggerOrder",
|
|
226
|
+
"environments",
|
|
227
|
+
"segment"
|
|
228
|
+
]);
|
|
229
|
+
_define_property(this, "flowNodes", [
|
|
230
|
+
"actionCalls",
|
|
231
|
+
"apexPluginCalls",
|
|
232
|
+
"assignments",
|
|
233
|
+
"collectionProcessors",
|
|
234
|
+
"decisions",
|
|
235
|
+
"loops",
|
|
236
|
+
"orchestratedStages",
|
|
237
|
+
"recordCreates",
|
|
238
|
+
"recordDeletes",
|
|
239
|
+
"recordLookups",
|
|
240
|
+
"recordUpdates",
|
|
241
|
+
"recordRollbacks",
|
|
242
|
+
"screens",
|
|
243
|
+
"start",
|
|
244
|
+
"steps",
|
|
245
|
+
"subflows",
|
|
246
|
+
"waits",
|
|
247
|
+
"transforms",
|
|
248
|
+
"customErrors"
|
|
249
|
+
]);
|
|
250
|
+
_define_property(this, "flowResources", [
|
|
251
|
+
"textTemplates",
|
|
252
|
+
"stages"
|
|
253
|
+
]);
|
|
254
|
+
_define_property(this, "flowVariables", [
|
|
255
|
+
"choices",
|
|
256
|
+
"constants",
|
|
257
|
+
"dynamicChoiceSets",
|
|
258
|
+
"formulas",
|
|
259
|
+
"variables"
|
|
260
|
+
]);
|
|
261
|
+
if (path) {
|
|
262
|
+
this.fsPath = _path.resolve(path);
|
|
263
|
+
let flowName = _path.basename(_path.basename(this.fsPath), _path.extname(this.fsPath));
|
|
264
|
+
if (flowName.includes(".")) {
|
|
265
|
+
flowName = flowName.split(".")[0];
|
|
266
|
+
}
|
|
267
|
+
this.name = flowName;
|
|
268
|
+
}
|
|
269
|
+
if (data) {
|
|
270
|
+
const hasFlowElement = typeof data === "object" && "Flow" in data;
|
|
271
|
+
if (hasFlowElement) {
|
|
272
|
+
this.xmldata = data.Flow;
|
|
273
|
+
} else this.xmldata = data;
|
|
274
|
+
this.preProcessNodes();
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
};
|