@sfdxy/mule-lint 1.20.0 → 1.22.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 +63 -17
- package/dist/package.json +1 -1
- package/dist/src/core/XPathHelper.d.ts.map +1 -1
- package/dist/src/core/XPathHelper.js +8 -0
- package/dist/src/core/XPathHelper.js.map +1 -1
- package/dist/src/engine/LintEngine.d.ts +22 -0
- package/dist/src/engine/LintEngine.d.ts.map +1 -1
- package/dist/src/engine/LintEngine.js +105 -18
- package/dist/src/engine/LintEngine.js.map +1 -1
- package/dist/src/mcp/prompts/index.d.ts +1 -1
- package/dist/src/mcp/prompts/index.d.ts.map +1 -1
- package/dist/src/mcp/prompts/index.js +62 -1
- package/dist/src/mcp/prompts/index.js.map +1 -1
- package/dist/src/mcp/resources/index.js +114 -16
- package/dist/src/mcp/resources/index.js.map +1 -1
- package/dist/src/mcp/tools/getRuleDetails.d.ts.map +1 -1
- package/dist/src/mcp/tools/getRuleDetails.js +30 -1
- package/dist/src/mcp/tools/getRuleDetails.js.map +1 -1
- package/dist/src/rules/api-led/ApikitConsoleProductionRule.d.ts +22 -0
- package/dist/src/rules/api-led/ApikitConsoleProductionRule.d.ts.map +1 -0
- package/dist/src/rules/api-led/ApikitConsoleProductionRule.js +43 -0
- package/dist/src/rules/api-led/ApikitConsoleProductionRule.js.map +1 -0
- package/dist/src/rules/api-led/ApikitMainFlowStructureRule.d.ts +24 -0
- package/dist/src/rules/api-led/ApikitMainFlowStructureRule.d.ts.map +1 -0
- package/dist/src/rules/api-led/ApikitMainFlowStructureRule.js +53 -0
- package/dist/src/rules/api-led/ApikitMainFlowStructureRule.js.map +1 -0
- package/dist/src/rules/api-led/ApikitStatusCodeVariableRule.d.ts +25 -0
- package/dist/src/rules/api-led/ApikitStatusCodeVariableRule.d.ts.map +1 -0
- package/dist/src/rules/api-led/ApikitStatusCodeVariableRule.js +59 -0
- package/dist/src/rules/api-led/ApikitStatusCodeVariableRule.js.map +1 -0
- package/dist/src/rules/connector/EventListenerNullGuardRule.d.ts +24 -0
- package/dist/src/rules/connector/EventListenerNullGuardRule.d.ts.map +1 -0
- package/dist/src/rules/connector/EventListenerNullGuardRule.js +58 -0
- package/dist/src/rules/connector/EventListenerNullGuardRule.js.map +1 -0
- package/dist/src/rules/connector/ReplayChannelConfigRule.d.ts +23 -0
- package/dist/src/rules/connector/ReplayChannelConfigRule.d.ts.map +1 -0
- package/dist/src/rules/connector/ReplayChannelConfigRule.js +52 -0
- package/dist/src/rules/connector/ReplayChannelConfigRule.js.map +1 -0
- package/dist/src/rules/dataweave/DataWeaveRules.d.ts +11 -4
- package/dist/src/rules/dataweave/DataWeaveRules.d.ts.map +1 -1
- package/dist/src/rules/dataweave/DataWeaveRules.js +20 -20
- package/dist/src/rules/dataweave/DataWeaveRules.js.map +1 -1
- package/dist/src/rules/dataweave/DuplicateTransformLogicRule.d.ts +25 -0
- package/dist/src/rules/dataweave/DuplicateTransformLogicRule.d.ts.map +1 -0
- package/dist/src/rules/dataweave/DuplicateTransformLogicRule.js +63 -0
- package/dist/src/rules/dataweave/DuplicateTransformLogicRule.js.map +1 -0
- package/dist/src/rules/error-handling/CatchAllLastRule.d.ts +24 -0
- package/dist/src/rules/error-handling/CatchAllLastRule.d.ts.map +1 -0
- package/dist/src/rules/error-handling/CatchAllLastRule.js +65 -0
- package/dist/src/rules/error-handling/CatchAllLastRule.js.map +1 -0
- package/dist/src/rules/error-handling/ErrorHandlerTypeCoverageRule.d.ts +28 -0
- package/dist/src/rules/error-handling/ErrorHandlerTypeCoverageRule.d.ts.map +1 -0
- package/dist/src/rules/error-handling/ErrorHandlerTypeCoverageRule.js +70 -0
- package/dist/src/rules/error-handling/ErrorHandlerTypeCoverageRule.js.map +1 -0
- package/dist/src/rules/error-handling/ErrorResponseStructureRule.d.ts +23 -0
- package/dist/src/rules/error-handling/ErrorResponseStructureRule.d.ts.map +1 -0
- package/dist/src/rules/error-handling/ErrorResponseStructureRule.js +73 -0
- package/dist/src/rules/error-handling/ErrorResponseStructureRule.js.map +1 -0
- package/dist/src/rules/error-handling/GenericErrorRule.d.ts +15 -3
- package/dist/src/rules/error-handling/GenericErrorRule.d.ts.map +1 -1
- package/dist/src/rules/error-handling/GenericErrorRule.js +58 -18
- package/dist/src/rules/error-handling/GenericErrorRule.js.map +1 -1
- package/dist/src/rules/error-handling/GlobalErrorHandlerRule.d.ts +14 -15
- package/dist/src/rules/error-handling/GlobalErrorHandlerRule.d.ts.map +1 -1
- package/dist/src/rules/error-handling/GlobalErrorHandlerRule.js +59 -38
- package/dist/src/rules/error-handling/GlobalErrorHandlerRule.js.map +1 -1
- package/dist/src/rules/error-handling/TryScopeRule.d.ts +5 -0
- package/dist/src/rules/error-handling/TryScopeRule.d.ts.map +1 -1
- package/dist/src/rules/error-handling/TryScopeRule.js +30 -7
- package/dist/src/rules/error-handling/TryScopeRule.js.map +1 -1
- package/dist/src/rules/http/ConnectionIdleTimeoutRule.d.ts +27 -0
- package/dist/src/rules/http/ConnectionIdleTimeoutRule.d.ts.map +1 -0
- package/dist/src/rules/http/ConnectionIdleTimeoutRule.js +46 -0
- package/dist/src/rules/http/ConnectionIdleTimeoutRule.js.map +1 -0
- package/dist/src/rules/index.d.ts +1 -1
- package/dist/src/rules/index.d.ts.map +1 -1
- package/dist/src/rules/index.js +50 -8
- package/dist/src/rules/index.js.map +1 -1
- package/dist/src/rules/logging/LoggerPayloadRule.d.ts +15 -0
- package/dist/src/rules/logging/LoggerPayloadRule.d.ts.map +1 -1
- package/dist/src/rules/logging/LoggerPayloadRule.js +48 -4
- package/dist/src/rules/logging/LoggerPayloadRule.js.map +1 -1
- package/dist/src/rules/operations/FlowRefTargetExistsRule.d.ts +23 -0
- package/dist/src/rules/operations/FlowRefTargetExistsRule.d.ts.map +1 -0
- package/dist/src/rules/operations/FlowRefTargetExistsRule.js +58 -0
- package/dist/src/rules/operations/FlowRefTargetExistsRule.js.map +1 -0
- package/dist/src/rules/operations/UnusedFlowRule.d.ts +20 -0
- package/dist/src/rules/operations/UnusedFlowRule.d.ts.map +1 -1
- package/dist/src/rules/operations/UnusedFlowRule.js +73 -7
- package/dist/src/rules/operations/UnusedFlowRule.js.map +1 -1
- package/dist/src/rules/operations/UnusedVariableRule.d.ts +31 -0
- package/dist/src/rules/operations/UnusedVariableRule.d.ts.map +1 -0
- package/dist/src/rules/operations/UnusedVariableRule.js +103 -0
- package/dist/src/rules/operations/UnusedVariableRule.js.map +1 -0
- package/dist/src/rules/performance/ListenerReconnectForeverRule.d.ts +28 -0
- package/dist/src/rules/performance/ListenerReconnectForeverRule.d.ts.map +1 -0
- package/dist/src/rules/performance/ListenerReconnectForeverRule.js +56 -0
- package/dist/src/rules/performance/ListenerReconnectForeverRule.js.map +1 -0
- package/dist/src/rules/performance/ReconnectionStrategyRule.d.ts +7 -4
- package/dist/src/rules/performance/ReconnectionStrategyRule.d.ts.map +1 -1
- package/dist/src/rules/performance/ReconnectionStrategyRule.js +44 -24
- package/dist/src/rules/performance/ReconnectionStrategyRule.js.map +1 -1
- package/dist/src/rules/security/ConnectorCredentialsSecuredRule.d.ts +36 -0
- package/dist/src/rules/security/ConnectorCredentialsSecuredRule.d.ts.map +1 -0
- package/dist/src/rules/security/ConnectorCredentialsSecuredRule.js +124 -0
- package/dist/src/rules/security/ConnectorCredentialsSecuredRule.js.map +1 -0
- package/dist/src/rules/security/HardcodedCredentialsRule.d.ts +4 -0
- package/dist/src/rules/security/HardcodedCredentialsRule.d.ts.map +1 -1
- package/dist/src/rules/security/HardcodedCredentialsRule.js +15 -0
- package/dist/src/rules/security/HardcodedCredentialsRule.js.map +1 -1
- package/dist/src/rules/security/SecurePropertiesEncryptionRule.d.ts +25 -0
- package/dist/src/rules/security/SecurePropertiesEncryptionRule.d.ts.map +1 -0
- package/dist/src/rules/security/SecurePropertiesEncryptionRule.js +59 -0
- package/dist/src/rules/security/SecurePropertiesEncryptionRule.js.map +1 -0
- package/dist/src/rules/security/SecurePropertiesKeyRule.d.ts +23 -0
- package/dist/src/rules/security/SecurePropertiesKeyRule.d.ts.map +1 -0
- package/dist/src/rules/security/SecurePropertiesKeyRule.js +45 -0
- package/dist/src/rules/security/SecurePropertiesKeyRule.js.map +1 -0
- package/dist/src/rules/security/TlsKeystorePasswordRule.d.ts +25 -0
- package/dist/src/rules/security/TlsKeystorePasswordRule.d.ts.map +1 -0
- package/dist/src/rules/security/TlsKeystorePasswordRule.js +63 -0
- package/dist/src/rules/security/TlsKeystorePasswordRule.js.map +1 -0
- package/dist/src/rules/standards/ApikitRouteVariableConsistencyRule.d.ts +26 -0
- package/dist/src/rules/standards/ApikitRouteVariableConsistencyRule.d.ts.map +1 -0
- package/dist/src/rules/standards/ApikitRouteVariableConsistencyRule.js +61 -0
- package/dist/src/rules/standards/ApikitRouteVariableConsistencyRule.js.map +1 -0
- package/dist/src/rules/standards/ConfigPropertiesOrderingRule.d.ts +34 -0
- package/dist/src/rules/standards/ConfigPropertiesOrderingRule.d.ts.map +1 -0
- package/dist/src/rules/standards/ConfigPropertiesOrderingRule.js +76 -0
- package/dist/src/rules/standards/ConfigPropertiesOrderingRule.js.map +1 -0
- package/dist/src/rules/standards/MissingEnvPropertiesDeclarationRule.d.ts +25 -0
- package/dist/src/rules/standards/MissingEnvPropertiesDeclarationRule.d.ts.map +1 -0
- package/dist/src/rules/standards/MissingEnvPropertiesDeclarationRule.js +111 -0
- package/dist/src/rules/standards/MissingEnvPropertiesDeclarationRule.js.map +1 -0
- package/dist/src/rules/yaml/YamlRules.d.ts +6 -2
- package/dist/src/rules/yaml/YamlRules.d.ts.map +1 -1
- package/dist/src/rules/yaml/YamlRules.js +15 -11
- package/dist/src/rules/yaml/YamlRules.js.map +1 -1
- package/dist/src/types/Rule.d.ts +13 -0
- package/dist/src/types/Rule.d.ts.map +1 -1
- package/docs/README.md +87 -27
- package/docs/best-practices/ci-cd.md +135 -0
- package/docs/best-practices/connector-patterns.md +253 -0
- package/docs/best-practices/dataweave-patterns.md +370 -0
- package/docs/best-practices/deployment-2026.md +171 -0
- package/docs/best-practices/error-handling.md +277 -0
- package/docs/best-practices/event-driven-patterns.md +424 -0
- package/docs/best-practices/logging.md +163 -0
- package/docs/best-practices/mulesoft-best-practices.md +72 -865
- package/docs/best-practices/performance.md +273 -0
- package/docs/best-practices/rules-catalog.md +337 -29
- package/docs/best-practices/security.md +181 -0
- package/docs/best-practices/testing.md +190 -0
- package/docs/best-practices/variable-contracts.md +191 -0
- package/docs/linter/architecture.md +119 -64
- package/package.json +1 -1
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.UnusedVariableRule = void 0;
|
|
4
|
+
const BaseRule_1 = require("../base/BaseRule");
|
|
5
|
+
/**
|
|
6
|
+
* HYG-005: Unused Variable
|
|
7
|
+
*
|
|
8
|
+
* Detects variables that are set in a flow but never referenced later
|
|
9
|
+
* in the same flow. This is a common code smell that indicates dead code
|
|
10
|
+
* or incomplete refactoring.
|
|
11
|
+
*
|
|
12
|
+
* Checks for set-variable elements whose variableName is not referenced
|
|
13
|
+
* by any subsequent expression in the same flow via `vars.variableName`
|
|
14
|
+
* or `#[vars.variableName]` patterns.
|
|
15
|
+
*
|
|
16
|
+
* Note: This is a best-effort heuristic — variables consumed outside the
|
|
17
|
+
* flow (via flow-ref caller, for example) cannot be detected.
|
|
18
|
+
*/
|
|
19
|
+
class UnusedVariableRule extends BaseRule_1.BaseRule {
|
|
20
|
+
id = 'HYG-005';
|
|
21
|
+
name = 'Unused Variable';
|
|
22
|
+
description = 'Variables set in a flow should be referenced within the same flow';
|
|
23
|
+
severity = 'info';
|
|
24
|
+
category = 'operations';
|
|
25
|
+
issueType = 'code-smell';
|
|
26
|
+
/** Well-known variables that are always considered "used" (consumed by connectors/listeners) */
|
|
27
|
+
WELL_KNOWN_VARS = new Set([
|
|
28
|
+
'httpStatus',
|
|
29
|
+
'outboundHeaders',
|
|
30
|
+
'statusCode',
|
|
31
|
+
'correlationId',
|
|
32
|
+
]);
|
|
33
|
+
validate(doc, _context) {
|
|
34
|
+
const issues = [];
|
|
35
|
+
const flows = this.select('//mule:flow | //mule:sub-flow', doc);
|
|
36
|
+
for (const flow of flows) {
|
|
37
|
+
const flowName = this.getAttribute(flow, 'name') ?? '';
|
|
38
|
+
// Collect all set-variable declarations in this flow
|
|
39
|
+
const setVars = this.select('.//*[local-name()="set-variable"]', flow);
|
|
40
|
+
const variables = [];
|
|
41
|
+
for (const setVar of setVars) {
|
|
42
|
+
const varName = this.getAttribute(setVar, 'variableName');
|
|
43
|
+
if (varName && !this.WELL_KNOWN_VARS.has(varName)) {
|
|
44
|
+
variables.push({ name: varName, node: setVar });
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
if (variables.length === 0) {
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
// Get the full text content of the flow to search for references
|
|
51
|
+
const flowText = this.serializeNode(flow);
|
|
52
|
+
for (const { name, node } of variables) {
|
|
53
|
+
// Check for common reference patterns:
|
|
54
|
+
// vars.name, vars['name'], vars["name"], vars.name
|
|
55
|
+
const patterns = [`vars.${name}`, `vars['${name}']`, `vars["${name}"]`, `#[vars.${name}]`];
|
|
56
|
+
const isReferenced = patterns.some((pattern) => {
|
|
57
|
+
// Count occurrences — must appear beyond just the set-variable itself
|
|
58
|
+
const regex = new RegExp(this.escapeRegex(pattern), 'g');
|
|
59
|
+
const matches = flowText.match(regex);
|
|
60
|
+
return matches !== null && matches.length > 0;
|
|
61
|
+
});
|
|
62
|
+
if (!isReferenced) {
|
|
63
|
+
issues.push(this.createIssue(node, `Variable "${name}" is set in flow "${flowName}" but never referenced within the same flow`, {
|
|
64
|
+
suggestion: `Remove the unused variable or verify it is consumed by a downstream flow via flow-ref`,
|
|
65
|
+
}));
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return issues;
|
|
70
|
+
}
|
|
71
|
+
serializeNode(node) {
|
|
72
|
+
// Get all text content and attribute values from the node subtree
|
|
73
|
+
const parts = [];
|
|
74
|
+
this.collectTextContent(node, parts);
|
|
75
|
+
return parts.join(' ');
|
|
76
|
+
}
|
|
77
|
+
collectTextContent(node, parts) {
|
|
78
|
+
if (node.nodeType === 3 /* TEXT_NODE */) {
|
|
79
|
+
parts.push(node.textContent ?? '');
|
|
80
|
+
}
|
|
81
|
+
else if (node.nodeType === 1 /* ELEMENT_NODE */) {
|
|
82
|
+
// Include attribute values (expressions live in attributes)
|
|
83
|
+
const element = node;
|
|
84
|
+
if (element.attributes) {
|
|
85
|
+
for (let i = 0; i < element.attributes.length; i++) {
|
|
86
|
+
parts.push(element.attributes[i].value);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
// Recurse into children
|
|
90
|
+
const children = node.childNodes;
|
|
91
|
+
if (children) {
|
|
92
|
+
for (let i = 0; i < children.length; i++) {
|
|
93
|
+
this.collectTextContent(children[i], parts);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
escapeRegex(str) {
|
|
99
|
+
return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
exports.UnusedVariableRule = UnusedVariableRule;
|
|
103
|
+
//# sourceMappingURL=UnusedVariableRule.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"UnusedVariableRule.js","sourceRoot":"","sources":["../../../../src/rules/operations/UnusedVariableRule.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { ValidationContext, Issue } from '../../types';
|
|
2
|
+
import { BaseRule } from '../base/BaseRule';
|
|
3
|
+
/**
|
|
4
|
+
* RES-002: Listener Reconnect-Forever
|
|
5
|
+
*
|
|
6
|
+
* HTTP listener-config elements should use reconnect-forever rather than
|
|
7
|
+
* bounded reconnect strategies. Unlike outbound connectors where bounded
|
|
8
|
+
* retries make sense, the listener is the application's entry point — if
|
|
9
|
+
* it can't bind to the port, the app should keep trying indefinitely.
|
|
10
|
+
*
|
|
11
|
+
* Real-world accelerator pattern:
|
|
12
|
+
* <http:listener-config>
|
|
13
|
+
* <http:listener-connection host="0.0.0.0" port="${http.port}">
|
|
14
|
+
* <reconnection>
|
|
15
|
+
* <reconnect-forever frequency="5000" />
|
|
16
|
+
* </reconnection>
|
|
17
|
+
* </http:listener-connection>
|
|
18
|
+
* </http:listener-config>
|
|
19
|
+
*/
|
|
20
|
+
export declare class ListenerReconnectForeverRule extends BaseRule {
|
|
21
|
+
id: string;
|
|
22
|
+
name: string;
|
|
23
|
+
description: string;
|
|
24
|
+
severity: "warning";
|
|
25
|
+
category: "performance";
|
|
26
|
+
validate(doc: Document, _context: ValidationContext): Issue[];
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=ListenerReconnectForeverRule.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ListenerReconnectForeverRule.d.ts","sourceRoot":"","sources":["../../../../src/rules/performance/ListenerReconnectForeverRule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AACvD,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAE5C;;;;;;;;;;;;;;;;GAgBG;AACH,qBAAa,4BAA6B,SAAQ,QAAQ;IACxD,EAAE,SAAa;IACf,IAAI,SAAgC;IACpC,WAAW,SAAsE;IACjF,QAAQ,EAAG,SAAS,CAAU;IAC9B,QAAQ,EAAG,aAAa,CAAU;IAElC,QAAQ,CAAC,GAAG,EAAE,QAAQ,EAAE,QAAQ,EAAE,iBAAiB,GAAG,KAAK,EAAE;CA2C9D"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ListenerReconnectForeverRule = void 0;
|
|
4
|
+
const BaseRule_1 = require("../base/BaseRule");
|
|
5
|
+
/**
|
|
6
|
+
* RES-002: Listener Reconnect-Forever
|
|
7
|
+
*
|
|
8
|
+
* HTTP listener-config elements should use reconnect-forever rather than
|
|
9
|
+
* bounded reconnect strategies. Unlike outbound connectors where bounded
|
|
10
|
+
* retries make sense, the listener is the application's entry point — if
|
|
11
|
+
* it can't bind to the port, the app should keep trying indefinitely.
|
|
12
|
+
*
|
|
13
|
+
* Real-world accelerator pattern:
|
|
14
|
+
* <http:listener-config>
|
|
15
|
+
* <http:listener-connection host="0.0.0.0" port="${http.port}">
|
|
16
|
+
* <reconnection>
|
|
17
|
+
* <reconnect-forever frequency="5000" />
|
|
18
|
+
* </reconnection>
|
|
19
|
+
* </http:listener-connection>
|
|
20
|
+
* </http:listener-config>
|
|
21
|
+
*/
|
|
22
|
+
class ListenerReconnectForeverRule extends BaseRule_1.BaseRule {
|
|
23
|
+
id = 'RES-002';
|
|
24
|
+
name = 'Listener Reconnect-Forever';
|
|
25
|
+
description = 'HTTP listener-config should use reconnect-forever for resilience';
|
|
26
|
+
severity = 'warning';
|
|
27
|
+
category = 'performance';
|
|
28
|
+
validate(doc, _context) {
|
|
29
|
+
const issues = [];
|
|
30
|
+
const listenerConfigs = this.select('//*[local-name()="listener-config"]', doc);
|
|
31
|
+
for (const config of listenerConfigs) {
|
|
32
|
+
const name = this.getNameAttribute(config) ?? 'HTTP Listener';
|
|
33
|
+
// Check if reconnect-forever exists anywhere inside the config
|
|
34
|
+
const hasReconnectForever = this.exists('.//*[local-name()="reconnect-forever"]', config);
|
|
35
|
+
if (hasReconnectForever) {
|
|
36
|
+
continue; // Good — has reconnect-forever
|
|
37
|
+
}
|
|
38
|
+
// Check if there's a bounded reconnect (which is suboptimal for listeners)
|
|
39
|
+
const hasBoundedReconnect = this.exists('.//*[local-name()="reconnect"]', config) ||
|
|
40
|
+
this.exists('.//*[local-name()="reconnection"]', config);
|
|
41
|
+
if (hasBoundedReconnect) {
|
|
42
|
+
issues.push(this.createIssue(config, `Listener config "${name}" uses bounded reconnect instead of reconnect-forever`, {
|
|
43
|
+
suggestion: 'Replace <reconnect count="..." .../> with <reconnect-forever frequency="5000" /> for HTTP listeners — the app should keep trying to bind',
|
|
44
|
+
}));
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
issues.push(this.createIssue(config, `Listener config "${name}" has no reconnection strategy`, {
|
|
48
|
+
suggestion: 'Add <reconnection><reconnect-forever frequency="5000" /></reconnection> inside the listener-connection element',
|
|
49
|
+
}));
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return issues;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
exports.ListenerReconnectForeverRule = ListenerReconnectForeverRule;
|
|
56
|
+
//# sourceMappingURL=ListenerReconnectForeverRule.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ListenerReconnectForeverRule.js","sourceRoot":"","sources":["../../../../src/rules/performance/ListenerReconnectForeverRule.ts"],"names":[],"mappings":";;;AACA,+CAA4C;AAE5C;;;;;;;;;;;;;;;;GAgBG;AACH,MAAa,4BAA6B,SAAQ,mBAAQ;IACxD,EAAE,GAAG,SAAS,CAAC;IACf,IAAI,GAAG,4BAA4B,CAAC;IACpC,WAAW,GAAG,kEAAkE,CAAC;IACjF,QAAQ,GAAG,SAAkB,CAAC;IAC9B,QAAQ,GAAG,aAAsB,CAAC;IAElC,QAAQ,CAAC,GAAa,EAAE,QAA2B;QACjD,MAAM,MAAM,GAAY,EAAE,CAAC;QAE3B,MAAM,eAAe,GAAG,IAAI,CAAC,MAAM,CAAC,qCAAqC,EAAE,GAAG,CAAC,CAAC;QAEhF,KAAK,MAAM,MAAM,IAAI,eAAe,EAAE,CAAC;YACrC,MAAM,IAAI,GAAG,IAAI,CAAC,gBAAgB,CAAC,MAAM,CAAC,IAAI,eAAe,CAAC;YAE9D,+DAA+D;YAC/D,MAAM,mBAAmB,GAAG,IAAI,CAAC,MAAM,CAAC,wCAAwC,EAAE,MAAM,CAAC,CAAC;YAE1F,IAAI,mBAAmB,EAAE,CAAC;gBACxB,SAAS,CAAC,+BAA+B;YAC3C,CAAC;YAED,2EAA2E;YAC3E,MAAM,mBAAmB,GACvB,IAAI,CAAC,MAAM,CAAC,gCAAgC,EAAE,MAAM,CAAC;gBACrD,IAAI,CAAC,MAAM,CAAC,mCAAmC,EAAE,MAAM,CAAC,CAAC;YAE3D,IAAI,mBAAmB,EAAE,CAAC;gBACxB,MAAM,CAAC,IAAI,CACT,IAAI,CAAC,WAAW,CACd,MAAM,EACN,oBAAoB,IAAI,uDAAuD,EAC/E;oBACE,UAAU,EACR,0IAA0I;iBAC7I,CACF,CACF,CAAC;YACJ,CAAC;iBAAM,CAAC;gBACN,MAAM,CAAC,IAAI,CACT,IAAI,CAAC,WAAW,CAAC,MAAM,EAAE,oBAAoB,IAAI,gCAAgC,EAAE;oBACjF,UAAU,EACR,gHAAgH;iBACnH,CAAC,CACH,CAAC;YACJ,CAAC;QACH,CAAC;QAED,OAAO,MAAM,CAAC;IAChB,CAAC;CACF;AAlDD,oEAkDC"}
|
|
@@ -5,12 +5,14 @@ import { BaseRule } from '../base/BaseRule';
|
|
|
5
5
|
*
|
|
6
6
|
* Connectors should have reconnection strategies configured for resilience.
|
|
7
7
|
*
|
|
8
|
+
* Enhanced to differentiate listener vs request configs:
|
|
9
|
+
* - Listeners (inbound): recommend `reconnect-forever` (service must stay alive)
|
|
10
|
+
* - Requests (outbound): recommend bounded `reconnect count="3"` (fail fast for callers)
|
|
11
|
+
*
|
|
8
12
|
* Per the Mule HTTP connector XSD, `<reconnection>` is a child element of
|
|
9
13
|
* `<http:request-connection>`, NOT a direct child of `<http:request-config>`.
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* to correctly resolve reconnection elements at any depth, including the valid
|
|
13
|
-
* `http:request-config > http:request-connection > reconnection` nesting.
|
|
14
|
+
* The rule uses a descendant search (`.//*`) to correctly resolve reconnection
|
|
15
|
+
* elements at any depth.
|
|
14
16
|
*/
|
|
15
17
|
export declare class ReconnectionStrategyRule extends BaseRule {
|
|
16
18
|
id: string;
|
|
@@ -19,5 +21,6 @@ export declare class ReconnectionStrategyRule extends BaseRule {
|
|
|
19
21
|
severity: "warning";
|
|
20
22
|
category: "performance";
|
|
21
23
|
validate(doc: Document, _context: ValidationContext): Issue[];
|
|
24
|
+
private hasAnyReconnection;
|
|
22
25
|
}
|
|
23
26
|
//# sourceMappingURL=ReconnectionStrategyRule.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ReconnectionStrategyRule.d.ts","sourceRoot":"","sources":["../../../../src/rules/performance/ReconnectionStrategyRule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AACvD,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAE5C
|
|
1
|
+
{"version":3,"file":"ReconnectionStrategyRule.d.ts","sourceRoot":"","sources":["../../../../src/rules/performance/ReconnectionStrategyRule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AACvD,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAE5C;;;;;;;;;;;;;GAaG;AACH,qBAAa,wBAAyB,SAAQ,QAAQ;IACpD,EAAE,SAAa;IACf,IAAI,SAA2B;IAC/B,WAAW,SAA+D;IAC1E,QAAQ,EAAG,SAAS,CAAU;IAC9B,QAAQ,EAAG,aAAa,CAAU;IAElC,QAAQ,CAAC,GAAG,EAAE,QAAQ,EAAE,QAAQ,EAAE,iBAAiB,GAAG,KAAK,EAAE;IAkG7D,OAAO,CAAC,kBAAkB;CAO3B"}
|
|
@@ -7,12 +7,14 @@ const BaseRule_1 = require("../base/BaseRule");
|
|
|
7
7
|
*
|
|
8
8
|
* Connectors should have reconnection strategies configured for resilience.
|
|
9
9
|
*
|
|
10
|
+
* Enhanced to differentiate listener vs request configs:
|
|
11
|
+
* - Listeners (inbound): recommend `reconnect-forever` (service must stay alive)
|
|
12
|
+
* - Requests (outbound): recommend bounded `reconnect count="3"` (fail fast for callers)
|
|
13
|
+
*
|
|
10
14
|
* Per the Mule HTTP connector XSD, `<reconnection>` is a child element of
|
|
11
15
|
* `<http:request-connection>`, NOT a direct child of `<http:request-config>`.
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* to correctly resolve reconnection elements at any depth, including the valid
|
|
15
|
-
* `http:request-config > http:request-connection > reconnection` nesting.
|
|
16
|
+
* The rule uses a descendant search (`.//*`) to correctly resolve reconnection
|
|
17
|
+
* elements at any depth.
|
|
16
18
|
*/
|
|
17
19
|
class ReconnectionStrategyRule extends BaseRule_1.BaseRule {
|
|
18
20
|
id = 'RES-001';
|
|
@@ -22,52 +24,70 @@ class ReconnectionStrategyRule extends BaseRule_1.BaseRule {
|
|
|
22
24
|
category = 'performance';
|
|
23
25
|
validate(doc, _context) {
|
|
24
26
|
const issues = [];
|
|
25
|
-
//
|
|
26
|
-
|
|
27
|
-
const
|
|
27
|
+
// Listener configs (inbound) — recommend reconnect-forever
|
|
28
|
+
const listenerConfigs = [{ pattern: 'listener-config', name: 'HTTP Listener' }];
|
|
29
|
+
for (const connector of listenerConfigs) {
|
|
30
|
+
const configs = this.select(`//*[local-name()="${connector.pattern}"]`, doc);
|
|
31
|
+
for (const config of configs) {
|
|
32
|
+
const hasReconnection = this.hasAnyReconnection(config);
|
|
33
|
+
if (!hasReconnection) {
|
|
34
|
+
const name = this.getNameAttribute(config) ?? connector.name;
|
|
35
|
+
issues.push(this.createIssue(config, `${connector.name} config "${name}" has no reconnection strategy`, {
|
|
36
|
+
suggestion: 'Add <reconnection><reconnect-forever frequency="5000"/></reconnection> for listener configs — the service should always attempt to reconnect',
|
|
37
|
+
}));
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
// Request/outbound configs — recommend bounded reconnect
|
|
42
|
+
const requestConfigs = [
|
|
28
43
|
{ pattern: 'request-config', name: 'HTTP Request' },
|
|
29
|
-
{ pattern: 'listener-config', name: 'HTTP Listener' },
|
|
30
44
|
{ pattern: 'jms-config', name: 'JMS' },
|
|
31
45
|
{ pattern: 'amqp-config', name: 'AMQP' },
|
|
32
46
|
{ pattern: 'sftp-config', name: 'SFTP' },
|
|
33
47
|
{ pattern: 'ftp-config', name: 'FTP' },
|
|
34
48
|
{ pattern: 'vm-config', name: 'VM' },
|
|
35
49
|
];
|
|
36
|
-
for (const connector of
|
|
50
|
+
for (const connector of requestConfigs) {
|
|
37
51
|
const configs = this.select(`//*[local-name()="${connector.pattern}"]`, doc);
|
|
38
52
|
for (const config of configs) {
|
|
39
|
-
|
|
40
|
-
// inside the config element are detected. This handles the XSD-correct pattern:
|
|
41
|
-
// <http:request-config>
|
|
42
|
-
// <http:request-connection>
|
|
43
|
-
// <reconnection>...</reconnection> ← correct per HTTP connector XSD
|
|
44
|
-
// </http:request-connection>
|
|
45
|
-
// </http:request-config>
|
|
46
|
-
const hasReconnection = this.exists('.//*[local-name()="reconnection"]', config) ||
|
|
47
|
-
this.exists('.//*[local-name()="reconnect"]', config) ||
|
|
48
|
-
this.exists('.//*[local-name()="reconnect-forever"]', config);
|
|
53
|
+
const hasReconnection = this.hasAnyReconnection(config);
|
|
49
54
|
if (!hasReconnection) {
|
|
50
55
|
const name = this.getNameAttribute(config) ?? connector.name;
|
|
51
56
|
issues.push(this.createIssue(config, `${connector.name} config "${name}" has no reconnection strategy`, {
|
|
52
|
-
suggestion: 'Add <reconnection><reconnect count="3" frequency="2000"/></reconnection> inside
|
|
57
|
+
suggestion: 'Add <reconnection><reconnect count="3" frequency="2000"/></reconnection> inside the connection element for outbound connectors',
|
|
53
58
|
}));
|
|
54
59
|
}
|
|
55
60
|
}
|
|
56
61
|
}
|
|
57
|
-
// Database configs
|
|
62
|
+
// Database configs — check for db namespace
|
|
58
63
|
const dbConfigs = this.select('//*[local-name()="config" and starts-with(name(), "db:")]', doc);
|
|
59
64
|
for (const config of dbConfigs) {
|
|
60
|
-
const hasReconnection = this.
|
|
61
|
-
this.exists('.//*[local-name()="reconnect"]', config);
|
|
65
|
+
const hasReconnection = this.hasAnyReconnection(config);
|
|
62
66
|
if (!hasReconnection) {
|
|
63
67
|
const name = this.getNameAttribute(config) ?? 'Database';
|
|
64
68
|
issues.push(this.createIssue(config, `Database config "${name}" has no reconnection strategy`, {
|
|
65
|
-
suggestion: 'Add <reconnection> inside the connection element',
|
|
69
|
+
suggestion: 'Add <reconnection><reconnect count="3" frequency="2000"/></reconnection> inside the connection element',
|
|
70
|
+
}));
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
// Salesforce configs — check for sfdc-config
|
|
74
|
+
const sfdcConfigs = this.select('//*[local-name()="sfdc-config" or local-name()="config" and starts-with(name(), "salesforce:")]', doc);
|
|
75
|
+
for (const config of sfdcConfigs) {
|
|
76
|
+
const hasReconnection = this.hasAnyReconnection(config);
|
|
77
|
+
if (!hasReconnection) {
|
|
78
|
+
const name = this.getNameAttribute(config) ?? 'Salesforce';
|
|
79
|
+
issues.push(this.createIssue(config, `Salesforce config "${name}" has no reconnection strategy`, {
|
|
80
|
+
suggestion: 'Add <reconnection><reconnect count="3" frequency="2000"/></reconnection> inside the Salesforce connection element',
|
|
66
81
|
}));
|
|
67
82
|
}
|
|
68
83
|
}
|
|
69
84
|
return issues;
|
|
70
85
|
}
|
|
86
|
+
hasAnyReconnection(config) {
|
|
87
|
+
return (this.exists('.//*[local-name()="reconnection"]', config) ||
|
|
88
|
+
this.exists('.//*[local-name()="reconnect"]', config) ||
|
|
89
|
+
this.exists('.//*[local-name()="reconnect-forever"]', config));
|
|
90
|
+
}
|
|
71
91
|
}
|
|
72
92
|
exports.ReconnectionStrategyRule = ReconnectionStrategyRule;
|
|
73
93
|
//# sourceMappingURL=ReconnectionStrategyRule.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ReconnectionStrategyRule.js","sourceRoot":"","sources":["../../../../src/rules/performance/ReconnectionStrategyRule.ts"],"names":[],"mappings":";;;AACA,+CAA4C;AAE5C
|
|
1
|
+
{"version":3,"file":"ReconnectionStrategyRule.js","sourceRoot":"","sources":["../../../../src/rules/performance/ReconnectionStrategyRule.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { ValidationContext, Issue, IssueType } from '../../types';
|
|
2
|
+
import { BaseRule } from '../base/BaseRule';
|
|
3
|
+
/**
|
|
4
|
+
* SEC-007: Connector Credentials Secured
|
|
5
|
+
*
|
|
6
|
+
* Salesforce, NetSuite, Database, and other connector configurations must
|
|
7
|
+
* use `${secure::...}` (not plain `${...}`) for credential attributes.
|
|
8
|
+
*
|
|
9
|
+
* Plain property placeholders like `${sf.password}` store the value in
|
|
10
|
+
* clear text in property files. The `${secure::...}` prefix ensures the
|
|
11
|
+
* value is read from an encrypted secure properties file.
|
|
12
|
+
*
|
|
13
|
+
* Covered connectors and attributes:
|
|
14
|
+
* - Salesforce: username, password, securityToken, consumerKey, consumerSecret
|
|
15
|
+
* - NetSuite: consumerKey, consumerSecret, tokenId, tokenSecret
|
|
16
|
+
* - Database: password, user (when not using connection URL)
|
|
17
|
+
* - HTTP Basic Auth: username, password
|
|
18
|
+
* - SMTP: password
|
|
19
|
+
*/
|
|
20
|
+
export declare class ConnectorCredentialsSecuredRule extends BaseRule {
|
|
21
|
+
id: string;
|
|
22
|
+
name: string;
|
|
23
|
+
description: string;
|
|
24
|
+
severity: "error";
|
|
25
|
+
category: "security";
|
|
26
|
+
issueType: IssueType;
|
|
27
|
+
/**
|
|
28
|
+
* Map of element local-name patterns to their sensitive attributes.
|
|
29
|
+
* We match on local-name() to be namespace-agnostic.
|
|
30
|
+
*/
|
|
31
|
+
private readonly CONNECTOR_SENSITIVE_ATTRS;
|
|
32
|
+
validate(doc: Document, _context: ValidationContext): Issue[];
|
|
33
|
+
private findSensitiveAttrs;
|
|
34
|
+
private extractPropertyName;
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=ConnectorCredentialsSecuredRule.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ConnectorCredentialsSecuredRule.d.ts","sourceRoot":"","sources":["../../../../src/rules/security/ConnectorCredentialsSecuredRule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAClE,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAE5C;;;;;;;;;;;;;;;;GAgBG;AACH,qBAAa,+BAAgC,SAAQ,QAAQ;IAC3D,EAAE,SAAa;IACf,IAAI,SAAmC;IACvC,WAAW,SAAsE;IACjF,QAAQ,EAAG,OAAO,CAAU;IAC5B,QAAQ,EAAG,UAAU,CAAU;IAC/B,SAAS,EAAE,SAAS,CAAmB;IAEvC;;;OAGG;IACH,OAAO,CAAC,QAAQ,CAAC,yBAAyB,CA+BxC;IAEF,QAAQ,CAAC,GAAG,EAAE,QAAQ,EAAE,QAAQ,EAAE,iBAAiB,GAAG,KAAK,EAAE;IA4D7D,OAAO,CAAC,kBAAkB;IAc1B,OAAO,CAAC,mBAAmB;CAI5B"}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ConnectorCredentialsSecuredRule = void 0;
|
|
4
|
+
const BaseRule_1 = require("../base/BaseRule");
|
|
5
|
+
/**
|
|
6
|
+
* SEC-007: Connector Credentials Secured
|
|
7
|
+
*
|
|
8
|
+
* Salesforce, NetSuite, Database, and other connector configurations must
|
|
9
|
+
* use `${secure::...}` (not plain `${...}`) for credential attributes.
|
|
10
|
+
*
|
|
11
|
+
* Plain property placeholders like `${sf.password}` store the value in
|
|
12
|
+
* clear text in property files. The `${secure::...}` prefix ensures the
|
|
13
|
+
* value is read from an encrypted secure properties file.
|
|
14
|
+
*
|
|
15
|
+
* Covered connectors and attributes:
|
|
16
|
+
* - Salesforce: username, password, securityToken, consumerKey, consumerSecret
|
|
17
|
+
* - NetSuite: consumerKey, consumerSecret, tokenId, tokenSecret
|
|
18
|
+
* - Database: password, user (when not using connection URL)
|
|
19
|
+
* - HTTP Basic Auth: username, password
|
|
20
|
+
* - SMTP: password
|
|
21
|
+
*/
|
|
22
|
+
class ConnectorCredentialsSecuredRule extends BaseRule_1.BaseRule {
|
|
23
|
+
id = 'SEC-007';
|
|
24
|
+
name = 'Connector Credentials Secured';
|
|
25
|
+
description = 'Connector credentials must use ${secure::} property placeholders';
|
|
26
|
+
severity = 'error';
|
|
27
|
+
category = 'security';
|
|
28
|
+
issueType = 'vulnerability';
|
|
29
|
+
/**
|
|
30
|
+
* Map of element local-name patterns to their sensitive attributes.
|
|
31
|
+
* We match on local-name() to be namespace-agnostic.
|
|
32
|
+
*/
|
|
33
|
+
CONNECTOR_SENSITIVE_ATTRS = {
|
|
34
|
+
// Salesforce connector
|
|
35
|
+
'sfdc-config': ['username', 'password', 'securityToken', 'consumerKey', 'consumerSecret'],
|
|
36
|
+
'salesforce-config': ['username', 'password', 'securityToken', 'consumerKey', 'consumerSecret'],
|
|
37
|
+
'basic-connection': [
|
|
38
|
+
'username',
|
|
39
|
+
'password',
|
|
40
|
+
'securityToken',
|
|
41
|
+
'consumerKey',
|
|
42
|
+
'consumerSecret',
|
|
43
|
+
'tokenId',
|
|
44
|
+
'tokenSecret',
|
|
45
|
+
],
|
|
46
|
+
'oauth-user-pass-connection': ['consumerKey', 'consumerSecret'],
|
|
47
|
+
'oauth-jwt-connection': ['consumerKey', 'keyStorePath', 'storePassword'],
|
|
48
|
+
// NetSuite connector (token-based)
|
|
49
|
+
'token-based-authentication-connection': [
|
|
50
|
+
'consumerKey',
|
|
51
|
+
'consumerSecret',
|
|
52
|
+
'tokenId',
|
|
53
|
+
'tokenSecret',
|
|
54
|
+
],
|
|
55
|
+
// Database connector
|
|
56
|
+
'derby-connection': ['password'],
|
|
57
|
+
'generic-connection': ['password'],
|
|
58
|
+
'my-sql-connection': ['password'],
|
|
59
|
+
'mssql-connection': ['password'],
|
|
60
|
+
'oracle-connection': ['password'],
|
|
61
|
+
'data-source-connection': ['password'],
|
|
62
|
+
// SMTP
|
|
63
|
+
'smtps-connection': ['password'],
|
|
64
|
+
};
|
|
65
|
+
validate(doc, _context) {
|
|
66
|
+
const issues = [];
|
|
67
|
+
const allElements = doc.getElementsByTagName('*');
|
|
68
|
+
for (let i = 0; i < allElements.length; i++) {
|
|
69
|
+
const element = allElements[i];
|
|
70
|
+
const localName = element.localName;
|
|
71
|
+
const sensitiveAttrs = this.findSensitiveAttrs(localName);
|
|
72
|
+
if (!sensitiveAttrs) {
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
for (const attrName of sensitiveAttrs) {
|
|
76
|
+
const value = element.getAttribute(attrName);
|
|
77
|
+
if (!value || value.trim() === '') {
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
// Skip DataWeave expressions — they're computed at runtime
|
|
81
|
+
if (value.startsWith('#[')) {
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
// Must use ${secure::...} — plain ${...} is not sufficient
|
|
85
|
+
if (value.includes('${') && !value.includes('${secure::')) {
|
|
86
|
+
issues.push(this.createIssue(element, `Credential "${attrName}" uses plain property placeholder — must use \${secure::} for encryption`, {
|
|
87
|
+
suggestion: `Replace ${value} with \${secure::${this.extractPropertyName(value)}}`,
|
|
88
|
+
}));
|
|
89
|
+
}
|
|
90
|
+
// Hardcoded value — no placeholder at all
|
|
91
|
+
if (!value.includes('${') && !value.startsWith('#[')) {
|
|
92
|
+
// Ignore booleans and numbers
|
|
93
|
+
if (value === 'true' || value === 'false' || !isNaN(Number(value))) {
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
issues.push(this.createIssue(element, `Credential "${attrName}" is hardcoded — must use \${secure::} property placeholder`, {
|
|
97
|
+
severity: 'error',
|
|
98
|
+
suggestion: `Use \${secure::${localName}.${attrName}} instead of the hardcoded value`,
|
|
99
|
+
}));
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return issues;
|
|
104
|
+
}
|
|
105
|
+
findSensitiveAttrs(localName) {
|
|
106
|
+
// Exact match first
|
|
107
|
+
if (this.CONNECTOR_SENSITIVE_ATTRS[localName]) {
|
|
108
|
+
return this.CONNECTOR_SENSITIVE_ATTRS[localName];
|
|
109
|
+
}
|
|
110
|
+
// Partial match for compound names
|
|
111
|
+
for (const [key, attrs] of Object.entries(this.CONNECTOR_SENSITIVE_ATTRS)) {
|
|
112
|
+
if (localName.includes(key)) {
|
|
113
|
+
return attrs;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return undefined;
|
|
117
|
+
}
|
|
118
|
+
extractPropertyName(placeholder) {
|
|
119
|
+
const match = /\$\{([^}]+)\}/.exec(placeholder);
|
|
120
|
+
return match ? match[1] : placeholder;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
exports.ConnectorCredentialsSecuredRule = ConnectorCredentialsSecuredRule;
|
|
124
|
+
//# sourceMappingURL=ConnectorCredentialsSecuredRule.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ConnectorCredentialsSecuredRule.js","sourceRoot":"","sources":["../../../../src/rules/security/ConnectorCredentialsSecuredRule.ts"],"names":[],"mappings":"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"}
|
|
@@ -4,6 +4,10 @@ import { BaseRule } from '../base/BaseRule';
|
|
|
4
4
|
* MULE-201: Hardcoded Credentials
|
|
5
5
|
*
|
|
6
6
|
* Passwords and secrets should use secure property placeholders.
|
|
7
|
+
*
|
|
8
|
+
* Enhanced to cover Salesforce connector-specific sensitive attributes
|
|
9
|
+
* (consumerKey, storePassword, tokenId, tokenSecret) and NetSuite
|
|
10
|
+
* OAuth attributes (consumerSecret, tokenKey, tokenSecret).
|
|
7
11
|
*/
|
|
8
12
|
export declare class HardcodedCredentialsRule extends BaseRule {
|
|
9
13
|
id: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"HardcodedCredentialsRule.d.ts","sourceRoot":"","sources":["../../../../src/rules/security/HardcodedCredentialsRule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAClE,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAE5C
|
|
1
|
+
{"version":3,"file":"HardcodedCredentialsRule.d.ts","sourceRoot":"","sources":["../../../../src/rules/security/HardcodedCredentialsRule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAClE,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAE5C;;;;;;;;GAQG;AACH,qBAAa,wBAAyB,SAAQ,QAAQ;IACpD,EAAE,SAAc;IAChB,IAAI,SAA2B;IAC/B,WAAW,SAA+E;IAC1F,QAAQ,EAAG,OAAO,CAAU;IAC5B,QAAQ,EAAG,UAAU,CAAU;IAC/B,SAAS,EAAE,SAAS,CAAmB;IAEvC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAqB9B;IAEF,QAAQ,CAAC,GAAG,EAAE,QAAQ,EAAE,QAAQ,EAAE,iBAAiB,GAAG,KAAK,EAAE;IA+B7D,OAAO,CAAC,oBAAoB;IAI5B,OAAO,CAAC,WAAW;CAoBpB"}
|
|
@@ -6,6 +6,10 @@ const BaseRule_1 = require("../base/BaseRule");
|
|
|
6
6
|
* MULE-201: Hardcoded Credentials
|
|
7
7
|
*
|
|
8
8
|
* Passwords and secrets should use secure property placeholders.
|
|
9
|
+
*
|
|
10
|
+
* Enhanced to cover Salesforce connector-specific sensitive attributes
|
|
11
|
+
* (consumerKey, storePassword, tokenId, tokenSecret) and NetSuite
|
|
12
|
+
* OAuth attributes (consumerSecret, tokenKey, tokenSecret).
|
|
9
13
|
*/
|
|
10
14
|
class HardcodedCredentialsRule extends BaseRule_1.BaseRule {
|
|
11
15
|
id = 'MULE-201';
|
|
@@ -24,6 +28,17 @@ class HardcodedCredentialsRule extends BaseRule_1.BaseRule {
|
|
|
24
28
|
'token',
|
|
25
29
|
'accessToken',
|
|
26
30
|
'privateKey',
|
|
31
|
+
// Salesforce connector attrs
|
|
32
|
+
'consumerKey',
|
|
33
|
+
'consumerSecret',
|
|
34
|
+
'storePassword',
|
|
35
|
+
'tokenId',
|
|
36
|
+
'tokenSecret',
|
|
37
|
+
// NetSuite OAuth attrs
|
|
38
|
+
'tokenKey',
|
|
39
|
+
// Keystore/truststore
|
|
40
|
+
'keyPassword',
|
|
41
|
+
'keystorePassword',
|
|
27
42
|
];
|
|
28
43
|
validate(doc, _context) {
|
|
29
44
|
const issues = [];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"HardcodedCredentialsRule.js","sourceRoot":"","sources":["../../../../src/rules/security/HardcodedCredentialsRule.ts"],"names":[],"mappings":";;;AACA,+CAA4C;AAE5C
|
|
1
|
+
{"version":3,"file":"HardcodedCredentialsRule.js","sourceRoot":"","sources":["../../../../src/rules/security/HardcodedCredentialsRule.ts"],"names":[],"mappings":";;;AACA,+CAA4C;AAE5C;;;;;;;;GAQG;AACH,MAAa,wBAAyB,SAAQ,mBAAQ;IACpD,EAAE,GAAG,UAAU,CAAC;IAChB,IAAI,GAAG,uBAAuB,CAAC;IAC/B,WAAW,GAAG,2EAA2E,CAAC;IAC1F,QAAQ,GAAG,OAAgB,CAAC;IAC5B,QAAQ,GAAG,UAAmB,CAAC;IAC/B,SAAS,GAAc,eAAe,CAAC;IAEtB,eAAe,GAAG;QACjC,UAAU;QACV,QAAQ;QACR,cAAc;QACd,eAAe;QACf,QAAQ;QACR,SAAS;QACT,OAAO;QACP,aAAa;QACb,YAAY;QACZ,6BAA6B;QAC7B,aAAa;QACb,gBAAgB;QAChB,eAAe;QACf,SAAS;QACT,aAAa;QACb,uBAAuB;QACvB,UAAU;QACV,sBAAsB;QACtB,aAAa;QACb,kBAAkB;KACnB,CAAC;IAEF,QAAQ,CAAC,GAAa,EAAE,QAA2B;QACjD,MAAM,MAAM,GAAY,EAAE,CAAC;QAC3B,MAAM,WAAW,GAAG,GAAG,CAAC,oBAAoB,CAAC,GAAG,CAAC,CAAC;QAElD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,WAAW,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YAC5C,MAAM,OAAO,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC;YAC/B,MAAM,KAAK,GAAG,OAAO,CAAC,UAAU,CAAC;YAEjC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;gBACtC,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;gBACtB,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;gBACzC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC;gBAEzB,yCAAyC;gBACzC,IAAI,IAAI,CAAC,oBAAoB,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,EAAE,CAAC;oBACnE,MAAM,CAAC,IAAI,CACT,IAAI,CAAC,WAAW,CACd,OAAO,EACP,aAAa,IAAI,CAAC,IAAI,0CAA0C,EAChE;wBACE,UAAU,EAAE,kBAAkB,IAAI,CAAC,IAAI,8BAA8B;qBACtE,CACF,CACF,CAAC;gBACJ,CAAC;YACH,CAAC;QACH,CAAC;QAED,OAAO,MAAM,CAAC;IAChB,CAAC;IAEO,oBAAoB,CAAC,QAAgB;QAC3C,OAAO,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,SAAS,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC;IAC9F,CAAC;IAEO,WAAW,CAAC,KAAa;QAC/B,iCAAiC;QACjC,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;YAClC,OAAO,KAAK,CAAC;QACf,CAAC;QAED,sEAAsE;QACtE,IAAI,KAAK,KAAK,MAAM,IAAI,KAAK,KAAK,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC;YACnE,OAAO,KAAK,CAAC;QACf,CAAC;QAED,+DAA+D;QAC/D,yDAAyD;QACzD,IAAI,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;YACnD,OAAO,KAAK,CAAC;QACf,CAAC;QAED,4DAA4D;QAC5D,OAAO,IAAI,CAAC;IACd,CAAC;CACF;AAtFD,4DAsFC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { ValidationContext, Issue, IssueType } from '../../types';
|
|
2
|
+
import { BaseRule } from '../base/BaseRule';
|
|
3
|
+
/**
|
|
4
|
+
* SEC-010: Secure Properties Encryption Algorithm
|
|
5
|
+
*
|
|
6
|
+
* When `<secure-properties:config>` specifies an encryption algorithm, it
|
|
7
|
+
* should use a strong algorithm (AES, Blowfish) and not a weak or
|
|
8
|
+
* deprecated one (DES, RC2, RC4).
|
|
9
|
+
*
|
|
10
|
+
* Also validates that when secure properties are used, the `encrypt`
|
|
11
|
+
* element exists (i.e., properties are actually encrypted, not just
|
|
12
|
+
* marked as "secure" with no encryption).
|
|
13
|
+
*/
|
|
14
|
+
export declare class SecurePropertiesEncryptionRule extends BaseRule {
|
|
15
|
+
id: string;
|
|
16
|
+
name: string;
|
|
17
|
+
description: string;
|
|
18
|
+
severity: "warning";
|
|
19
|
+
category: "security";
|
|
20
|
+
issueType: IssueType;
|
|
21
|
+
private readonly WEAK_ALGORITHMS;
|
|
22
|
+
private readonly STRONG_ALGORITHMS;
|
|
23
|
+
validate(doc: Document, _context: ValidationContext): Issue[];
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=SecurePropertiesEncryptionRule.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SecurePropertiesEncryptionRule.d.ts","sourceRoot":"","sources":["../../../../src/rules/security/SecurePropertiesEncryptionRule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAClE,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAE5C;;;;;;;;;;GAUG;AACH,qBAAa,8BAA+B,SAAQ,QAAQ;IAC1D,EAAE,SAAa;IACf,IAAI,SAAkC;IACtC,WAAW,SAA6D;IACxE,QAAQ,EAAG,SAAS,CAAU;IAC9B,QAAQ,EAAG,UAAU,CAAU;IAC/B,SAAS,EAAE,SAAS,CAAmB;IAEvC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAmC;IACnE,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAuB;IAEzD,QAAQ,CAAC,GAAG,EAAE,QAAQ,EAAE,QAAQ,EAAE,iBAAiB,GAAG,KAAK,EAAE;CAsD9D"}
|