@flow-scanner/lightning-flow-scanner-core 6.16.2 → 6.17.0
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/README.md +9 -2
- package/main/config/RegexAdapter.d.ts +32 -0
- package/main/config/RegexAdapter.js +86 -0
- package/main/config/RuleRegistry.js +2 -0
- package/main/rules/FlowName.d.ts +6 -0
- package/main/rules/FlowName.js +24 -11
- package/main/rules/HardcodedId.d.ts +6 -0
- package/main/rules/HardcodedId.js +23 -4
- package/main/rules/HardcodedSecret.d.ts +13 -0
- package/main/rules/HardcodedSecret.js +101 -0
- package/main/rules/HardcodedUrl.d.ts +6 -0
- package/main/rules/HardcodedUrl.js +23 -5
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -70,20 +70,27 @@ Executing DML operations (insert, update, delete) inside a loop is a high-risk a
|
|
|
70
70
|
**Class Name:** _[DMLStatementInLoop](https://github.com/Flow-Scanner/lightning-flow-scanner/blob/main/packages/core/src/main/rules/DMLStatementInLoop.ts)_
|
|
71
71
|
**Severity:** 🔴 *Error*
|
|
72
72
|
|
|
73
|
-
#### Hardcoded Id
|
|
73
|
+
#### Hardcoded Salesforce Id
|
|
74
74
|
Avoid hard-coding record IDs, as they are unique to a specific org and will not work in other environments. Instead, store IDs in variables—such as merge-field URL parameters or a **Get Records** element—to make the Flow portable, maintainable, and flexible.
|
|
75
75
|
|
|
76
76
|
**Rule ID:** `hardcoded-id`
|
|
77
77
|
**Class Name:** _[HardcodedId](https://github.com/Flow-Scanner/lightning-flow-scanner/blob/main/packages/core/src/main/rules/HardcodedId.ts)_
|
|
78
78
|
**Severity:** 🔴 *Error*
|
|
79
79
|
|
|
80
|
-
#### Hardcoded Url
|
|
80
|
+
#### Hardcoded Salesforce Url
|
|
81
81
|
Avoid hard-coding URLs, as they may change between environments or over time. Instead, store URLs in variables or custom settings to make the Flow adaptable, maintainable, and environment-independent.
|
|
82
82
|
|
|
83
83
|
**Rule ID:** `hardcoded-url`
|
|
84
84
|
**Class Name:** _[HardcodedUrl](https://github.com/Flow-Scanner/lightning-flow-scanner/blob/main/packages/core/src/main/rules/HardcodedUrl.ts)_
|
|
85
85
|
**Severity:** 🔴 *Error*
|
|
86
86
|
|
|
87
|
+
#### Hardcoded Secret 
|
|
88
|
+
Avoid hardcoding secrets, API keys, tokens, or credentials in Flows. These should be stored securely in Named Credentials, Custom Settings, Custom Metadata, or external secret management systems.
|
|
89
|
+
|
|
90
|
+
**Rule ID:** `hardcoded-secret`
|
|
91
|
+
**Class Name:** _[HardcodedSecret](https://github.com/Flow-Scanner/lightning-flow-scanner/blob/main/packages/core/src/main/rules/HardcodedSecret.ts)_
|
|
92
|
+
**Severity:** 🔴 *Error*
|
|
93
|
+
|
|
87
94
|
#### Process Builder
|
|
88
95
|
Process Builder is retired. Continuing to use it increases maintenance overhead and risks future compatibility issues. Migrating automation to Flow reduces risk and improves maintainability.
|
|
89
96
|
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Adapter functions to convert between core models (Flow, Violation)
|
|
3
|
+
* and regex-scanner models (MetadataFile, RegexViolation).
|
|
4
|
+
*
|
|
5
|
+
* This allows core rules to delegate to regex-scanner while maintaining
|
|
6
|
+
* backward compatibility with existing consumers.
|
|
7
|
+
*/
|
|
8
|
+
import type { MetadataFile, MetadataElement, RegexViolation } from "@flow-scanner/regex-scanner";
|
|
9
|
+
import { Flow } from "../models/Flow";
|
|
10
|
+
import { FlowElement } from "../models/FlowElement";
|
|
11
|
+
import { Violation } from "../models/Violation";
|
|
12
|
+
/**
|
|
13
|
+
* Convert a Flow object to a MetadataFile for regex-scanner.
|
|
14
|
+
*/
|
|
15
|
+
export declare function toMetadataFile(flow: Flow): MetadataFile;
|
|
16
|
+
/**
|
|
17
|
+
* Convert Flow elements to MetadataElements for element-level scanning.
|
|
18
|
+
*/
|
|
19
|
+
export declare function toMetadataElements(flow: Flow): MetadataElement[];
|
|
20
|
+
/**
|
|
21
|
+
* Convert a RegexViolation to a core Violation.
|
|
22
|
+
* Creates the appropriate FlowElement subclass based on metaType.
|
|
23
|
+
*/
|
|
24
|
+
export declare function toViolation(rv: RegexViolation): Violation;
|
|
25
|
+
/**
|
|
26
|
+
* Convert a core FlowElement to a regex-scanner MetadataElement.
|
|
27
|
+
*/
|
|
28
|
+
export declare function flowElementToMetadataElement(element: FlowElement): MetadataElement;
|
|
29
|
+
/**
|
|
30
|
+
* Convert multiple RegexViolations to core Violations.
|
|
31
|
+
*/
|
|
32
|
+
export declare function toViolations(violations: RegexViolation[]): Violation[];
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Adapter functions to convert between core models (Flow, Violation)
|
|
3
|
+
* and regex-scanner models (MetadataFile, RegexViolation).
|
|
4
|
+
*
|
|
5
|
+
* This allows core rules to delegate to regex-scanner while maintaining
|
|
6
|
+
* backward compatibility with existing consumers.
|
|
7
|
+
*/ "use strict";
|
|
8
|
+
Object.defineProperty(exports, "__esModule", {
|
|
9
|
+
value: true
|
|
10
|
+
});
|
|
11
|
+
function _export(target, all) {
|
|
12
|
+
for(var name in all)Object.defineProperty(target, name, {
|
|
13
|
+
enumerable: true,
|
|
14
|
+
get: Object.getOwnPropertyDescriptor(all, name).get
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
_export(exports, {
|
|
18
|
+
get flowElementToMetadataElement () {
|
|
19
|
+
return flowElementToMetadataElement;
|
|
20
|
+
},
|
|
21
|
+
get toMetadataElements () {
|
|
22
|
+
return toMetadataElements;
|
|
23
|
+
},
|
|
24
|
+
get toMetadataFile () {
|
|
25
|
+
return toMetadataFile;
|
|
26
|
+
},
|
|
27
|
+
get toViolation () {
|
|
28
|
+
return toViolation;
|
|
29
|
+
},
|
|
30
|
+
get toViolations () {
|
|
31
|
+
return toViolations;
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
const _FlowAttribute = require("../models/FlowAttribute");
|
|
35
|
+
const _Violation = require("../models/Violation");
|
|
36
|
+
function toMetadataFile(flow) {
|
|
37
|
+
// Handle case where toXMLString may not exist (e.g., in tests with partial Flow objects)
|
|
38
|
+
let content = "";
|
|
39
|
+
if (typeof flow.toXMLString === "function") {
|
|
40
|
+
content = flow.toXMLString();
|
|
41
|
+
}
|
|
42
|
+
var _flow_uri_split_pop;
|
|
43
|
+
return {
|
|
44
|
+
name: flow.name,
|
|
45
|
+
fileName: flow.uri ? (_flow_uri_split_pop = flow.uri.split(/[\\/]/).pop()) !== null && _flow_uri_split_pop !== void 0 ? _flow_uri_split_pop : `${flow.name}.flow-meta.xml` : `${flow.name}.flow-meta.xml`,
|
|
46
|
+
filePath: flow.fsPath,
|
|
47
|
+
metadataType: "Flow",
|
|
48
|
+
content,
|
|
49
|
+
elements: toMetadataElements(flow)
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
function toMetadataElements(flow) {
|
|
53
|
+
if (!flow.elements || flow.elements.length === 0) {
|
|
54
|
+
return [];
|
|
55
|
+
}
|
|
56
|
+
return flow.elements.map((element)=>{
|
|
57
|
+
var _element_element;
|
|
58
|
+
return {
|
|
59
|
+
name: element.name,
|
|
60
|
+
type: element.subtype,
|
|
61
|
+
content: (_element_element = element.element) !== null && _element_element !== void 0 ? _element_element : element
|
|
62
|
+
};
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
function toViolation(rv) {
|
|
66
|
+
var _rv_expression;
|
|
67
|
+
// Create a FlowAttribute to represent the violation
|
|
68
|
+
// This is the simplest approach that works for all regex rules
|
|
69
|
+
const flowElement = new _FlowAttribute.FlowAttribute(rv.name, rv.type, (_rv_expression = rv.expression) !== null && _rv_expression !== void 0 ? _rv_expression : rv.matchedText);
|
|
70
|
+
const violation = new _Violation.Violation(flowElement);
|
|
71
|
+
// Override line/column from regex violation
|
|
72
|
+
violation.lineNumber = rv.lineNumber;
|
|
73
|
+
violation.columnNumber = rv.columnNumber;
|
|
74
|
+
return violation;
|
|
75
|
+
}
|
|
76
|
+
function flowElementToMetadataElement(element) {
|
|
77
|
+
var _element_element;
|
|
78
|
+
return {
|
|
79
|
+
name: element.name,
|
|
80
|
+
type: element.subtype,
|
|
81
|
+
content: (_element_element = element.element) !== null && _element_element !== void 0 ? _element_element : element
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
function toViolations(violations) {
|
|
85
|
+
return violations.map(toViolation);
|
|
86
|
+
}
|
|
@@ -20,6 +20,7 @@ const _FlowName = require("../rules/FlowName");
|
|
|
20
20
|
const _GetRecordAllFields = require("../rules/GetRecordAllFields");
|
|
21
21
|
const _HardcodedId = require("../rules/HardcodedId");
|
|
22
22
|
const _HardcodedUrl = require("../rules/HardcodedUrl");
|
|
23
|
+
const _HardcodedSecret = require("../rules/HardcodedSecret");
|
|
23
24
|
const _InactiveFlow = require("../rules/InactiveFlow");
|
|
24
25
|
const _MissingFaultPath = require("../rules/MissingFaultPath");
|
|
25
26
|
const _MissingNullHandler = require("../rules/MissingNullHandler");
|
|
@@ -202,4 +203,5 @@ registry.register("missing-metadata-description", _MissingMetadataDescription.Mi
|
|
|
202
203
|
registry.register("missing-record-trigger-filter", _MissingRecordTriggerFilter.MissingRecordTriggerFilter, "MissingFilterRecordTrigger", true);
|
|
203
204
|
registry.register("transform-instead-of-loop", _TransformInsteadOfLoop.TransformInsteadOfLoop, "TransformInsteadOfLoop", true);
|
|
204
205
|
registry.register("record-id-as-string", _RecordIdAsString.RecordIdAsString, "RecordIdAsString", true);
|
|
206
|
+
registry.register("hardcoded-secret", _HardcodedSecret.HardcodedSecret, "HardcodedSecret", true);
|
|
205
207
|
const ruleRegistry = registry;
|
package/main/rules/FlowName.d.ts
CHANGED
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
import * as core from "../internals/internals";
|
|
2
2
|
import { RuleCommon } from "../models/RuleCommon";
|
|
3
3
|
import { IRuleDefinition } from "../interfaces/IRuleDefinition";
|
|
4
|
+
/**
|
|
5
|
+
* Flow naming convention rule.
|
|
6
|
+
* This is a wrapper around the regex-scanner's NamingConvention rule,
|
|
7
|
+
* maintaining backward compatibility with the core scanner interface.
|
|
8
|
+
*/
|
|
4
9
|
export declare class FlowName extends RuleCommon implements IRuleDefinition {
|
|
10
|
+
private regexRule;
|
|
5
11
|
constructor();
|
|
6
12
|
protected check(flow: core.Flow, options: {
|
|
7
13
|
expression?: string;
|
package/main/rules/FlowName.js
CHANGED
|
@@ -10,6 +10,21 @@ Object.defineProperty(exports, "FlowName", {
|
|
|
10
10
|
});
|
|
11
11
|
const _internals = /*#__PURE__*/ _interop_require_wildcard(require("../internals/internals"));
|
|
12
12
|
const _RuleCommon = require("../models/RuleCommon");
|
|
13
|
+
const _regexscanner = require("@flow-scanner/regex-scanner");
|
|
14
|
+
const _RegexAdapter = require("../config/RegexAdapter");
|
|
15
|
+
function _define_property(obj, key, value) {
|
|
16
|
+
if (key in obj) {
|
|
17
|
+
Object.defineProperty(obj, key, {
|
|
18
|
+
value: value,
|
|
19
|
+
enumerable: true,
|
|
20
|
+
configurable: true,
|
|
21
|
+
writable: true
|
|
22
|
+
});
|
|
23
|
+
} else {
|
|
24
|
+
obj[key] = value;
|
|
25
|
+
}
|
|
26
|
+
return obj;
|
|
27
|
+
}
|
|
13
28
|
function _getRequireWildcardCache(nodeInterop) {
|
|
14
29
|
if (typeof WeakMap !== "function") return null;
|
|
15
30
|
var cacheBabelInterop = new WeakMap();
|
|
@@ -53,16 +68,14 @@ function _interop_require_wildcard(obj, nodeInterop) {
|
|
|
53
68
|
}
|
|
54
69
|
let FlowName = class FlowName extends _RuleCommon.RuleCommon {
|
|
55
70
|
check(flow, options, _suppressions) {
|
|
56
|
-
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
const
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
return
|
|
64
|
-
new _internals.Violation(new _internals.FlowAttribute(flowName, "name", rawRegexp))
|
|
65
|
-
];
|
|
71
|
+
// Convert Flow to MetadataFile for regex-scanner
|
|
72
|
+
const metadataFile = (0, _RegexAdapter.toMetadataFile)(flow);
|
|
73
|
+
// Execute regex rule
|
|
74
|
+
const regexViolations = this.regexRule.execute(metadataFile, {
|
|
75
|
+
expression: options === null || options === void 0 ? void 0 : options.expression
|
|
76
|
+
});
|
|
77
|
+
// Convert back to core Violations
|
|
78
|
+
return (0, _RegexAdapter.toViolations)(regexViolations);
|
|
66
79
|
}
|
|
67
80
|
constructor(){
|
|
68
81
|
super({
|
|
@@ -81,6 +94,6 @@ let FlowName = class FlowName extends _RuleCommon.RuleCommon {
|
|
|
81
94
|
supportedTypes: _internals.FlowType.allTypes()
|
|
82
95
|
}, {
|
|
83
96
|
severity: "error"
|
|
84
|
-
});
|
|
97
|
+
}), _define_property(this, "regexRule", new _regexscanner.NamingConvention());
|
|
85
98
|
}
|
|
86
99
|
};
|
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
import * as core from "../internals/internals";
|
|
2
2
|
import { RuleCommon } from "../models/RuleCommon";
|
|
3
3
|
import { IRuleDefinition } from "../interfaces/IRuleDefinition";
|
|
4
|
+
/**
|
|
5
|
+
* Hardcoded Salesforce ID detection rule.
|
|
6
|
+
* This is a wrapper around the regex-scanner's HardcodedId rule,
|
|
7
|
+
* maintaining backward compatibility with the core scanner interface.
|
|
8
|
+
*/
|
|
4
9
|
export declare class HardcodedId extends RuleCommon implements IRuleDefinition {
|
|
10
|
+
private regexRule;
|
|
5
11
|
constructor();
|
|
6
12
|
protected check(flow: core.Flow, _options: object | undefined, _suppressions: Set<string>): core.Violation[];
|
|
7
13
|
}
|
|
@@ -10,6 +10,21 @@ Object.defineProperty(exports, "HardcodedId", {
|
|
|
10
10
|
});
|
|
11
11
|
const _internals = /*#__PURE__*/ _interop_require_wildcard(require("../internals/internals"));
|
|
12
12
|
const _RuleCommon = require("../models/RuleCommon");
|
|
13
|
+
const _regexscanner = require("@flow-scanner/regex-scanner");
|
|
14
|
+
const _RegexAdapter = require("../config/RegexAdapter");
|
|
15
|
+
function _define_property(obj, key, value) {
|
|
16
|
+
if (key in obj) {
|
|
17
|
+
Object.defineProperty(obj, key, {
|
|
18
|
+
value: value,
|
|
19
|
+
enumerable: true,
|
|
20
|
+
configurable: true,
|
|
21
|
+
writable: true
|
|
22
|
+
});
|
|
23
|
+
} else {
|
|
24
|
+
obj[key] = value;
|
|
25
|
+
}
|
|
26
|
+
return obj;
|
|
27
|
+
}
|
|
13
28
|
function _getRequireWildcardCache(nodeInterop) {
|
|
14
29
|
if (typeof WeakMap !== "function") return null;
|
|
15
30
|
var cacheBabelInterop = new WeakMap();
|
|
@@ -53,15 +68,19 @@ function _interop_require_wildcard(obj, nodeInterop) {
|
|
|
53
68
|
}
|
|
54
69
|
let HardcodedId = class HardcodedId extends _RuleCommon.RuleCommon {
|
|
55
70
|
check(flow, _options, _suppressions) {
|
|
56
|
-
|
|
57
|
-
|
|
71
|
+
// Convert Flow to MetadataFile for regex-scanner
|
|
72
|
+
const metadataFile = (0, _RegexAdapter.toMetadataFile)(flow);
|
|
73
|
+
// Execute regex rule
|
|
74
|
+
const regexViolations = this.regexRule.execute(metadataFile);
|
|
75
|
+
// Convert back to core Violations
|
|
76
|
+
return (0, _RegexAdapter.toViolations)(regexViolations);
|
|
58
77
|
}
|
|
59
78
|
constructor(){
|
|
60
79
|
super({
|
|
61
80
|
ruleId: "hardcoded-id",
|
|
62
81
|
name: "HardcodedId",
|
|
63
82
|
category: "problem",
|
|
64
|
-
label: "Hardcoded Id",
|
|
83
|
+
label: "Hardcoded Salesforce Id",
|
|
65
84
|
description: "Avoid hard-coding record IDs, as they are unique to a specific org and will not work in other environments. Instead, store IDs in variables—such as merge-field URL parameters or a **Get Records** element—to make the Flow portable, maintainable, and flexible.",
|
|
66
85
|
summary: "Hardcoded IDs break portability across environments",
|
|
67
86
|
supportedTypes: _internals.FlowType.allTypes(),
|
|
@@ -77,6 +96,6 @@ let HardcodedId = class HardcodedId extends _RuleCommon.RuleCommon {
|
|
|
77
96
|
]
|
|
78
97
|
}, {
|
|
79
98
|
severity: "error"
|
|
80
|
-
});
|
|
99
|
+
}), _define_property(this, "regexRule", new _regexscanner.HardcodedId());
|
|
81
100
|
}
|
|
82
101
|
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import * as core from "../internals/internals";
|
|
2
|
+
import { RuleCommon } from "../models/RuleCommon";
|
|
3
|
+
import { IRuleDefinition } from "../interfaces/IRuleDefinition";
|
|
4
|
+
/**
|
|
5
|
+
* Hardcoded secrets detection rule.
|
|
6
|
+
* This is a wrapper around the regex-scanner's HardcodedSecret rule,
|
|
7
|
+
* maintaining backward compatibility with the core scanner interface.
|
|
8
|
+
*/
|
|
9
|
+
export declare class HardcodedSecret extends RuleCommon implements IRuleDefinition {
|
|
10
|
+
private regexRule;
|
|
11
|
+
constructor();
|
|
12
|
+
protected check(flow: core.Flow, _options: object | undefined, _suppressions: Set<string>): core.Violation[];
|
|
13
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", {
|
|
3
|
+
value: true
|
|
4
|
+
});
|
|
5
|
+
Object.defineProperty(exports, "HardcodedSecret", {
|
|
6
|
+
enumerable: true,
|
|
7
|
+
get: function() {
|
|
8
|
+
return HardcodedSecret;
|
|
9
|
+
}
|
|
10
|
+
});
|
|
11
|
+
const _internals = /*#__PURE__*/ _interop_require_wildcard(require("../internals/internals"));
|
|
12
|
+
const _RuleCommon = require("../models/RuleCommon");
|
|
13
|
+
const _regexscanner = require("@flow-scanner/regex-scanner");
|
|
14
|
+
const _RegexAdapter = require("../config/RegexAdapter");
|
|
15
|
+
function _define_property(obj, key, value) {
|
|
16
|
+
if (key in obj) {
|
|
17
|
+
Object.defineProperty(obj, key, {
|
|
18
|
+
value: value,
|
|
19
|
+
enumerable: true,
|
|
20
|
+
configurable: true,
|
|
21
|
+
writable: true
|
|
22
|
+
});
|
|
23
|
+
} else {
|
|
24
|
+
obj[key] = value;
|
|
25
|
+
}
|
|
26
|
+
return obj;
|
|
27
|
+
}
|
|
28
|
+
function _getRequireWildcardCache(nodeInterop) {
|
|
29
|
+
if (typeof WeakMap !== "function") return null;
|
|
30
|
+
var cacheBabelInterop = new WeakMap();
|
|
31
|
+
var cacheNodeInterop = new WeakMap();
|
|
32
|
+
return (_getRequireWildcardCache = function(nodeInterop) {
|
|
33
|
+
return nodeInterop ? cacheNodeInterop : cacheBabelInterop;
|
|
34
|
+
})(nodeInterop);
|
|
35
|
+
}
|
|
36
|
+
function _interop_require_wildcard(obj, nodeInterop) {
|
|
37
|
+
if (!nodeInterop && obj && obj.__esModule) {
|
|
38
|
+
return obj;
|
|
39
|
+
}
|
|
40
|
+
if (obj === null || typeof obj !== "object" && typeof obj !== "function") {
|
|
41
|
+
return {
|
|
42
|
+
default: obj
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
var cache = _getRequireWildcardCache(nodeInterop);
|
|
46
|
+
if (cache && cache.has(obj)) {
|
|
47
|
+
return cache.get(obj);
|
|
48
|
+
}
|
|
49
|
+
var newObj = {
|
|
50
|
+
__proto__: null
|
|
51
|
+
};
|
|
52
|
+
var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor;
|
|
53
|
+
for(var key in obj){
|
|
54
|
+
if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) {
|
|
55
|
+
var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null;
|
|
56
|
+
if (desc && (desc.get || desc.set)) {
|
|
57
|
+
Object.defineProperty(newObj, key, desc);
|
|
58
|
+
} else {
|
|
59
|
+
newObj[key] = obj[key];
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
newObj.default = obj;
|
|
64
|
+
if (cache) {
|
|
65
|
+
cache.set(obj, newObj);
|
|
66
|
+
}
|
|
67
|
+
return newObj;
|
|
68
|
+
}
|
|
69
|
+
let HardcodedSecret = class HardcodedSecret extends _RuleCommon.RuleCommon {
|
|
70
|
+
check(flow, _options, _suppressions) {
|
|
71
|
+
// Convert Flow to MetadataFile for regex-scanner
|
|
72
|
+
const metadataFile = (0, _RegexAdapter.toMetadataFile)(flow);
|
|
73
|
+
// Execute regex rule
|
|
74
|
+
const regexViolations = this.regexRule.execute(metadataFile);
|
|
75
|
+
// Convert back to core Violations
|
|
76
|
+
return (0, _RegexAdapter.toViolations)(regexViolations);
|
|
77
|
+
}
|
|
78
|
+
constructor(){
|
|
79
|
+
super({
|
|
80
|
+
ruleId: "hardcoded-secret",
|
|
81
|
+
name: "HardcodedSecret",
|
|
82
|
+
category: "problem",
|
|
83
|
+
label: "Hardcoded Secret",
|
|
84
|
+
description: "Avoid hardcoding secrets, API keys, tokens, or credentials in Flows. These should be stored securely in Named Credentials, Custom Settings, Custom Metadata, or external secret management systems.",
|
|
85
|
+
summary: "Hardcoded secrets pose security risks",
|
|
86
|
+
supportedTypes: _internals.FlowType.allTypes(),
|
|
87
|
+
docRefs: [
|
|
88
|
+
{
|
|
89
|
+
label: "Salesforce Named Credentials",
|
|
90
|
+
path: "https://help.salesforce.com/s/articleView?id=sf.named_credentials_about.htm"
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
label: "OWASP Secrets Management Cheat Sheet",
|
|
94
|
+
path: "https://cheatsheetseries.owasp.org/cheatsheets/Secrets_Management_Cheat_Sheet.html"
|
|
95
|
+
}
|
|
96
|
+
]
|
|
97
|
+
}, {
|
|
98
|
+
severity: "error"
|
|
99
|
+
}), _define_property(this, "regexRule", new _regexscanner.HardcodedSecret());
|
|
100
|
+
}
|
|
101
|
+
};
|
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
import { Flow, Violation } from "../internals/internals";
|
|
2
2
|
import { RuleCommon } from "../models/RuleCommon";
|
|
3
3
|
import { IRuleDefinition } from "../interfaces/IRuleDefinition";
|
|
4
|
+
/**
|
|
5
|
+
* Hardcoded Salesforce URL detection rule.
|
|
6
|
+
* This is a wrapper around the regex-scanner's HardcodedUrl rule,
|
|
7
|
+
* maintaining backward compatibility with the core scanner interface.
|
|
8
|
+
*/
|
|
4
9
|
export declare class HardcodedUrl extends RuleCommon implements IRuleDefinition {
|
|
10
|
+
private regexRule;
|
|
5
11
|
constructor();
|
|
6
12
|
protected check(flow: Flow, _options: object | undefined, _suppressions: Set<string>): Violation[];
|
|
7
13
|
}
|
|
@@ -10,11 +10,29 @@ Object.defineProperty(exports, "HardcodedUrl", {
|
|
|
10
10
|
});
|
|
11
11
|
const _internals = require("../internals/internals");
|
|
12
12
|
const _RuleCommon = require("../models/RuleCommon");
|
|
13
|
+
const _regexscanner = require("@flow-scanner/regex-scanner");
|
|
14
|
+
const _RegexAdapter = require("../config/RegexAdapter");
|
|
15
|
+
function _define_property(obj, key, value) {
|
|
16
|
+
if (key in obj) {
|
|
17
|
+
Object.defineProperty(obj, key, {
|
|
18
|
+
value: value,
|
|
19
|
+
enumerable: true,
|
|
20
|
+
configurable: true,
|
|
21
|
+
writable: true
|
|
22
|
+
});
|
|
23
|
+
} else {
|
|
24
|
+
obj[key] = value;
|
|
25
|
+
}
|
|
26
|
+
return obj;
|
|
27
|
+
}
|
|
13
28
|
let HardcodedUrl = class HardcodedUrl extends _RuleCommon.RuleCommon {
|
|
14
29
|
check(flow, _options, _suppressions) {
|
|
15
|
-
|
|
16
|
-
const
|
|
17
|
-
|
|
30
|
+
// Convert Flow to MetadataFile for regex-scanner
|
|
31
|
+
const metadataFile = (0, _RegexAdapter.toMetadataFile)(flow);
|
|
32
|
+
// Execute regex rule
|
|
33
|
+
const regexViolations = this.regexRule.execute(metadataFile);
|
|
34
|
+
// Convert back to core Violations
|
|
35
|
+
return (0, _RegexAdapter.toViolations)(regexViolations);
|
|
18
36
|
}
|
|
19
37
|
constructor(){
|
|
20
38
|
super({
|
|
@@ -32,11 +50,11 @@ let HardcodedUrl = class HardcodedUrl extends _RuleCommon.RuleCommon {
|
|
|
32
50
|
path: "https://admin.salesforce.com/blog/2021/why-you-should-avoid-hard-coding-and-three-alternative-solutions"
|
|
33
51
|
}
|
|
34
52
|
],
|
|
35
|
-
label: "Hardcoded Url",
|
|
53
|
+
label: "Hardcoded Salesforce Url",
|
|
36
54
|
name: "HardcodedUrl",
|
|
37
55
|
supportedTypes: _internals.FlowType.allTypes()
|
|
38
56
|
}, {
|
|
39
57
|
severity: "error"
|
|
40
|
-
});
|
|
58
|
+
}), _define_property(this, "regexRule", new _regexscanner.HardcodedUrl());
|
|
41
59
|
}
|
|
42
60
|
};
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@flow-scanner/lightning-flow-scanner-core",
|
|
3
3
|
"description": "A lightweight engine for Flow metadata in Node.js, and browser environments. Assess and enhance Salesforce Flow automations for best practices, security, governor limits, and performance issues.",
|
|
4
|
-
"version": "6.
|
|
4
|
+
"version": "6.17.0",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"exports": {
|
|
7
7
|
".": {
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
"directory": "packages/core"
|
|
21
21
|
},
|
|
22
22
|
"dependencies": {
|
|
23
|
+
"@flow-scanner/regex-scanner": "workspace:*",
|
|
23
24
|
"fast-xml-parser": "^5.3.0"
|
|
24
25
|
},
|
|
25
26
|
"homepage": "https://flow-scanner.github.io/lightning-flow-scanner/",
|