@sfdxy/mule-lint 1.18.1 → 1.19.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 +154 -150
- package/dist/bin/mule-lint-mcp.js.map +1 -1
- package/dist/bin/mule-lint.js +2 -2
- package/dist/bin/mule-lint.js.map +1 -1
- package/dist/package.json +26 -15
- package/dist/src/core/ComplexityCalculator.d.ts.map +1 -1
- package/dist/src/core/ComplexityCalculator.js.map +1 -1
- package/dist/src/core/FileScanner.d.ts.map +1 -1
- package/dist/src/core/FileScanner.js.map +1 -1
- package/dist/src/core/MetricsAggregator.d.ts.map +1 -1
- package/dist/src/core/MetricsAggregator.js +1 -1
- package/dist/src/core/MetricsAggregator.js.map +1 -1
- package/dist/src/core/MetricsCollector.d.ts.map +1 -1
- package/dist/src/core/MetricsCollector.js +1 -2
- package/dist/src/core/MetricsCollector.js.map +1 -1
- package/dist/src/core/QualityGateEvaluator.d.ts.map +1 -1
- package/dist/src/core/QualityGateEvaluator.js.map +1 -1
- package/dist/src/core/XPathHelper.d.ts.map +1 -1
- package/dist/src/core/XPathHelper.js +0 -1
- package/dist/src/core/XPathHelper.js.map +1 -1
- package/dist/src/core/XmlParser.d.ts.map +1 -1
- package/dist/src/core/XmlParser.js.map +1 -1
- package/dist/src/core/YamlParser.d.ts.map +1 -1
- package/dist/src/core/YamlParser.js.map +1 -1
- package/dist/src/core/errors.js.map +1 -1
- package/dist/src/engine/LintEngine.d.ts.map +1 -1
- package/dist/src/engine/LintEngine.js.map +1 -1
- package/dist/src/formatter/MuleXmlFormatter.d.ts.map +1 -1
- package/dist/src/formatter/MuleXmlFormatter.js +7 -1
- package/dist/src/formatter/MuleXmlFormatter.js.map +1 -1
- package/dist/src/formatter/index.d.ts +2 -2
- package/dist/src/formatter/index.d.ts.map +1 -1
- package/dist/src/formatter/index.js.map +1 -1
- package/dist/src/formatters/CsvFormatter.js.map +1 -1
- package/dist/src/formatters/HtmlFormatter.js +0 -1
- package/dist/src/formatters/HtmlFormatter.js.map +1 -1
- package/dist/src/formatters/JsonFormatter.d.ts.map +1 -1
- package/dist/src/formatters/JsonFormatter.js.map +1 -1
- package/dist/src/formatters/SarifFormatter.js.map +1 -1
- package/dist/src/formatters/TableFormatter.js.map +1 -1
- package/dist/src/formatters/html/components/Icons.js +1 -1
- package/dist/src/formatters/html/components/Icons.js.map +1 -1
- package/dist/src/formatters/html/components/MetricCard.d.ts.map +1 -1
- package/dist/src/formatters/html/components/MetricCard.js.map +1 -1
- package/dist/src/formatters/html/components/Modal.d.ts.map +1 -1
- package/dist/src/formatters/html/components/Modal.js.map +1 -1
- package/dist/src/formatters/html/components/RatingBadge.d.ts.map +1 -1
- package/dist/src/formatters/html/components/RatingBadge.js +6 -6
- package/dist/src/formatters/html/components/RatingBadge.js.map +1 -1
- package/dist/src/formatters/html/index.d.ts +2 -2
- package/dist/src/formatters/html/index.d.ts.map +1 -1
- package/dist/src/formatters/html/index.js.map +1 -1
- package/dist/src/formatters/html/scripts/charts.d.ts.map +1 -1
- package/dist/src/formatters/html/scripts/charts.js +45 -12
- package/dist/src/formatters/html/scripts/charts.js.map +1 -1
- package/dist/src/formatters/html/scripts/index.d.ts.map +1 -1
- package/dist/src/formatters/html/scripts/index.js.map +1 -1
- package/dist/src/formatters/html/scripts/renderer.d.ts.map +1 -1
- package/dist/src/formatters/html/scripts/renderer.js +175 -35
- package/dist/src/formatters/html/scripts/renderer.js.map +1 -1
- package/dist/src/formatters/html/sections/Header.d.ts.map +1 -1
- package/dist/src/formatters/html/sections/Header.js.map +1 -1
- package/dist/src/formatters/html/sections/LintSummary.d.ts.map +1 -1
- package/dist/src/formatters/html/sections/LintSummary.js.map +1 -1
- package/dist/src/formatters/html/sections/QualityRatings.js.map +1 -1
- package/dist/src/formatters/html/sections/Sidebar.d.ts.map +1 -1
- package/dist/src/formatters/html/sections/Sidebar.js.map +1 -1
- package/dist/src/formatters/html/styles/badges.d.ts.map +1 -1
- package/dist/src/formatters/html/styles/badges.js.map +1 -1
- package/dist/src/formatters/html/styles/index.d.ts.map +1 -1
- package/dist/src/formatters/html/styles/index.js.map +1 -1
- package/dist/src/formatters/html/theme.js.map +1 -1
- package/dist/src/formatters/html/views/Dashboard.js +5 -5
- package/dist/src/formatters/html/views/Dashboard.js.map +1 -1
- package/dist/src/formatters/html/views/IssuesView.d.ts.map +1 -1
- package/dist/src/formatters/html/views/IssuesView.js.map +1 -1
- package/dist/src/formatters/index.js.map +1 -1
- package/dist/src/mcp/index.d.ts.map +1 -1
- package/dist/src/mcp/index.js +0 -1
- package/dist/src/mcp/index.js.map +1 -1
- package/dist/src/mcp/prompts/index.js.map +1 -1
- package/dist/src/mcp/resources/index.js +30 -7
- package/dist/src/mcp/resources/index.js.map +1 -1
- package/dist/src/mcp/tools/formatMuleXml.d.ts.map +1 -1
- package/dist/src/mcp/tools/formatMuleXml.js +4 -13
- package/dist/src/mcp/tools/formatMuleXml.js.map +1 -1
- package/dist/src/mcp/tools/getRuleDetails.d.ts.map +1 -1
- package/dist/src/mcp/tools/getRuleDetails.js +1 -3
- package/dist/src/mcp/tools/getRuleDetails.js.map +1 -1
- package/dist/src/mcp/tools/runLintAnalysis.js +9 -9
- package/dist/src/mcp/tools/runLintAnalysis.js.map +1 -1
- package/dist/src/mcp/tools/validateSnippet.d.ts.map +1 -1
- package/dist/src/mcp/tools/validateSnippet.js.map +1 -1
- package/dist/src/quality/calculator.d.ts.map +1 -1
- package/dist/src/quality/calculator.js +1 -1
- package/dist/src/quality/calculator.js.map +1 -1
- package/dist/src/quality/index.d.ts.map +1 -1
- package/dist/src/quality/index.js.map +1 -1
- package/dist/src/quality/thresholds.js.map +1 -1
- package/dist/src/quality/types.d.ts.map +1 -1
- package/dist/src/rules/api-led/ApiLedRules.d.ts.map +1 -1
- package/dist/src/rules/api-led/ApiLedRules.js +6 -2
- package/dist/src/rules/api-led/ApiLedRules.js.map +1 -1
- package/dist/src/rules/api-led/SingleSystemSapiRule.d.ts.map +1 -1
- package/dist/src/rules/api-led/SingleSystemSapiRule.js.map +1 -1
- package/dist/src/rules/base/BaseRule.d.ts.map +1 -1
- package/dist/src/rules/base/BaseRule.js.map +1 -1
- package/dist/src/rules/base/ProjectRule.d.ts.map +1 -1
- package/dist/src/rules/base/ProjectRule.js.map +1 -1
- package/dist/src/rules/complexity/FlowComplexityRule.d.ts.map +1 -1
- package/dist/src/rules/complexity/FlowComplexityRule.js.map +1 -1
- package/dist/src/rules/dataweave/DataWeaveRules.d.ts.map +1 -1
- package/dist/src/rules/dataweave/DataWeaveRules.js +1 -3
- package/dist/src/rules/dataweave/DataWeaveRules.js.map +1 -1
- package/dist/src/rules/dataweave/Java17DWErrorHandlingRule.d.ts.map +1 -1
- package/dist/src/rules/dataweave/Java17DWErrorHandlingRule.js.map +1 -1
- package/dist/src/rules/documentation/DisplayNameRule.d.ts.map +1 -1
- package/dist/src/rules/documentation/DisplayNameRule.js.map +1 -1
- package/dist/src/rules/documentation/FlowDescriptionRule.d.ts.map +1 -1
- package/dist/src/rules/documentation/FlowDescriptionRule.js.map +1 -1
- package/dist/src/rules/documentation/MissingDocNameRule.d.ts.map +1 -1
- package/dist/src/rules/documentation/MissingDocNameRule.js.map +1 -1
- package/dist/src/rules/error-handling/CorrelationIdRule.d.ts.map +1 -1
- package/dist/src/rules/error-handling/CorrelationIdRule.js.map +1 -1
- package/dist/src/rules/error-handling/GenericErrorRule.d.ts.map +1 -1
- package/dist/src/rules/error-handling/GenericErrorRule.js.map +1 -1
- package/dist/src/rules/error-handling/GlobalErrorHandlerRule.d.ts.map +1 -1
- package/dist/src/rules/error-handling/GlobalErrorHandlerRule.js.map +1 -1
- package/dist/src/rules/error-handling/HttpStatusRule.d.ts.map +1 -1
- package/dist/src/rules/error-handling/HttpStatusRule.js.map +1 -1
- package/dist/src/rules/error-handling/MissingErrorHandlerRule.d.ts.map +1 -1
- package/dist/src/rules/error-handling/MissingErrorHandlerRule.js.map +1 -1
- package/dist/src/rules/error-handling/TryScopeRule.d.ts.map +1 -1
- package/dist/src/rules/error-handling/TryScopeRule.js.map +1 -1
- package/dist/src/rules/experimental/ExperimentalRules.d.ts.map +1 -1
- package/dist/src/rules/experimental/ExperimentalRules.js +6 -2
- package/dist/src/rules/experimental/ExperimentalRules.js.map +1 -1
- package/dist/src/rules/governance/GovernanceRules.d.ts.map +1 -1
- package/dist/src/rules/governance/GovernanceRules.js.map +1 -1
- package/dist/src/rules/http/HttpContentTypeRule.d.ts.map +1 -1
- package/dist/src/rules/http/HttpContentTypeRule.js.map +1 -1
- package/dist/src/rules/http/HttpTimeoutRule.d.ts.map +1 -1
- package/dist/src/rules/http/HttpTimeoutRule.js.map +1 -1
- package/dist/src/rules/http/HttpUserAgentRule.d.ts.map +1 -1
- package/dist/src/rules/http/HttpUserAgentRule.js.map +1 -1
- package/dist/src/rules/index.js.map +1 -1
- package/dist/src/rules/logging/ExcessiveLoggersRule.d.ts.map +1 -1
- package/dist/src/rules/logging/ExcessiveLoggersRule.js.map +1 -1
- package/dist/src/rules/logging/LoggerCategoryRule.d.ts.map +1 -1
- package/dist/src/rules/logging/LoggerCategoryRule.js.map +1 -1
- package/dist/src/rules/logging/LoggerInUntilSuccessfulRule.d.ts.map +1 -1
- package/dist/src/rules/logging/LoggerInUntilSuccessfulRule.js.map +1 -1
- package/dist/src/rules/logging/LoggerPayloadRule.d.ts.map +1 -1
- package/dist/src/rules/logging/LoggerPayloadRule.js.map +1 -1
- package/dist/src/rules/logging/NewLoggingRules.d.ts.map +1 -1
- package/dist/src/rules/logging/NewLoggingRules.js.map +1 -1
- package/dist/src/rules/naming/FlowCasingRule.d.ts.map +1 -1
- package/dist/src/rules/naming/FlowCasingRule.js.map +1 -1
- package/dist/src/rules/naming/FlowNamingRule.d.ts.map +1 -1
- package/dist/src/rules/naming/FlowNamingRule.js +3 -1
- package/dist/src/rules/naming/FlowNamingRule.js.map +1 -1
- package/dist/src/rules/naming/VariableNamingRule.d.ts.map +1 -1
- package/dist/src/rules/naming/VariableNamingRule.js.map +1 -1
- package/dist/src/rules/operations/CommentedCodeRule.d.ts.map +1 -1
- package/dist/src/rules/operations/CommentedCodeRule.js.map +1 -1
- package/dist/src/rules/operations/UnusedFlowRule.d.ts.map +1 -1
- package/dist/src/rules/operations/UnusedFlowRule.js.map +1 -1
- package/dist/src/rules/performance/AsyncErrorHandlerRule.d.ts.map +1 -1
- package/dist/src/rules/performance/AsyncErrorHandlerRule.js.map +1 -1
- package/dist/src/rules/performance/ConnectionPoolingRule.d.ts.map +1 -1
- package/dist/src/rules/performance/ConnectionPoolingRule.js.map +1 -1
- package/dist/src/rules/performance/LargeChoiceBlockRule.d.ts.map +1 -1
- package/dist/src/rules/performance/LargeChoiceBlockRule.js.map +1 -1
- package/dist/src/rules/performance/ReconnectionStrategyRule.d.ts.map +1 -1
- package/dist/src/rules/performance/ReconnectionStrategyRule.js.map +1 -1
- package/dist/src/rules/performance/ScatterGatherRoutesRule.d.ts.map +1 -1
- package/dist/src/rules/performance/ScatterGatherRoutesRule.js.map +1 -1
- package/dist/src/rules/security/EncryptionKeyInLogsRule.d.ts.map +1 -1
- package/dist/src/rules/security/EncryptionKeyInLogsRule.js.map +1 -1
- package/dist/src/rules/security/HardcodedCredentialsRule.d.ts.map +1 -1
- package/dist/src/rules/security/HardcodedCredentialsRule.js.map +1 -1
- package/dist/src/rules/security/HardcodedHttpRule.d.ts.map +1 -1
- package/dist/src/rules/security/HardcodedHttpRule.js +1 -9
- package/dist/src/rules/security/HardcodedHttpRule.js.map +1 -1
- package/dist/src/rules/security/InputValidationRule.d.ts.map +1 -1
- package/dist/src/rules/security/InputValidationRule.js +1 -3
- package/dist/src/rules/security/InputValidationRule.js.map +1 -1
- package/dist/src/rules/security/InsecureTlsRule.d.ts.map +1 -1
- package/dist/src/rules/security/InsecureTlsRule.js.map +1 -1
- package/dist/src/rules/security/RateLimitingRule.d.ts.map +1 -1
- package/dist/src/rules/security/RateLimitingRule.js.map +1 -1
- package/dist/src/rules/security/TlsVersionRule.d.ts.map +1 -1
- package/dist/src/rules/security/TlsVersionRule.js.map +1 -1
- package/dist/src/rules/standards/ApiKitValidationRule.d.ts.map +1 -1
- package/dist/src/rules/standards/ApiKitValidationRule.js.map +1 -1
- package/dist/src/rules/standards/AutoDiscoveryRule.d.ts.map +1 -1
- package/dist/src/rules/standards/AutoDiscoveryRule.js.map +1 -1
- package/dist/src/rules/standards/ChoiceAntiPatternRule.d.ts.map +1 -1
- package/dist/src/rules/standards/ChoiceAntiPatternRule.js.map +1 -1
- package/dist/src/rules/standards/CronExternalizedRule.d.ts.map +1 -1
- package/dist/src/rules/standards/CronExternalizedRule.js.map +1 -1
- package/dist/src/rules/standards/DeprecatedComponentRule.d.ts.map +1 -1
- package/dist/src/rules/standards/DeprecatedComponentRule.js.map +1 -1
- package/dist/src/rules/standards/DwlStandardsRule.d.ts.map +1 -1
- package/dist/src/rules/standards/DwlStandardsRule.js.map +1 -1
- package/dist/src/rules/standards/HttpPortPlaceholderRule.d.ts.map +1 -1
- package/dist/src/rules/standards/HttpPortPlaceholderRule.js.map +1 -1
- package/dist/src/rules/structure/StructureRules.d.ts.map +1 -1
- package/dist/src/rules/structure/StructureRules.js.map +1 -1
- package/dist/src/rules/yaml/YamlRules.d.ts.map +1 -1
- package/dist/src/rules/yaml/YamlRules.js +2 -7
- package/dist/src/rules/yaml/YamlRules.js.map +1 -1
- package/dist/src/types/Config.d.ts.map +1 -1
- package/dist/src/types/Config.js.map +1 -1
- package/dist/src/types/QualityGate.d.ts.map +1 -1
- package/dist/src/types/QualityGate.js.map +1 -1
- package/dist/src/types/Report.d.ts.map +1 -1
- package/dist/src/types/Rule.d.ts.map +1 -1
- package/docs/README.md +27 -27
- package/docs/best-practices/documentation-standards.md +20 -11
- package/docs/best-practices/folder-structure.md +16 -10
- package/docs/best-practices/mulesoft-best-practices.md +96 -94
- package/docs/best-practices/rules-catalog.md +316 -287
- package/docs/linter/architecture.md +70 -64
- package/docs/linter/extending.md +137 -128
- package/docs/linter/folder-structure.md +39 -38
- package/docs/linter/naming-conventions.md +80 -78
- package/docs/linter/rule-engine.md +306 -306
- package/docs/mcp-design.md +35 -21
- package/package.json +84 -73
|
@@ -27,62 +27,62 @@ import { Document } from '@xmldom/xmldom';
|
|
|
27
27
|
|
|
28
28
|
export type Severity = 'error' | 'warning' | 'info';
|
|
29
29
|
|
|
30
|
-
export type RuleCategory =
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
30
|
+
export type RuleCategory =
|
|
31
|
+
| 'naming'
|
|
32
|
+
| 'error-handling'
|
|
33
|
+
| 'security'
|
|
34
|
+
| 'logging'
|
|
35
|
+
| 'standards'
|
|
36
|
+
| 'performance'
|
|
37
|
+
| 'documentation';
|
|
38
38
|
|
|
39
39
|
// Issue type for quality metrics classification
|
|
40
40
|
export type IssueType = 'code-smell' | 'bug' | 'vulnerability';
|
|
41
41
|
|
|
42
42
|
export interface Issue {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
43
|
+
line: number;
|
|
44
|
+
column?: number;
|
|
45
|
+
message: string;
|
|
46
|
+
ruleId: string;
|
|
47
|
+
severity: Severity;
|
|
48
|
+
suggestion?: string; // Optional fix suggestion
|
|
49
|
+
codeSnippet?: string; // Relevant code context
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
export interface ValidationContext {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
53
|
+
filePath: string; // Absolute path to file
|
|
54
|
+
relativePath: string; // Path relative to project root
|
|
55
|
+
projectRoot: string; // Project root directory
|
|
56
|
+
config: RuleConfig; // Rule-specific configuration
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
export interface Rule {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
60
|
+
// Unique identifier (e.g., "MULE-001")
|
|
61
|
+
id: string;
|
|
62
|
+
|
|
63
|
+
// Human-readable name
|
|
64
|
+
name: string;
|
|
65
|
+
|
|
66
|
+
// Detailed description for documentation
|
|
67
|
+
description: string;
|
|
68
|
+
|
|
69
|
+
// Default severity (can be overridden by config)
|
|
70
|
+
severity: Severity;
|
|
71
|
+
|
|
72
|
+
// Category for grouping in reports
|
|
73
|
+
category: RuleCategory;
|
|
74
|
+
|
|
75
|
+
// Issue type for quality metrics (defaults to 'code-smell')
|
|
76
|
+
// - 'bug': Reliability issues (error-handling rules)
|
|
77
|
+
// - 'vulnerability': Security issues (security rules)
|
|
78
|
+
// - 'code-smell': Maintainability issues (default)
|
|
79
|
+
issueType: IssueType;
|
|
80
|
+
|
|
81
|
+
// Optional: documentation URL
|
|
82
|
+
docsUrl?: string;
|
|
83
|
+
|
|
84
|
+
// The validation function
|
|
85
|
+
validate(doc: Document, context: ValidationContext): Issue[];
|
|
86
86
|
}
|
|
87
87
|
```
|
|
88
88
|
|
|
@@ -92,9 +92,9 @@ Per-rule configuration:
|
|
|
92
92
|
|
|
93
93
|
```typescript
|
|
94
94
|
export interface RuleConfig {
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
95
|
+
enabled: boolean;
|
|
96
|
+
severity?: Severity; // Override default
|
|
97
|
+
options?: Record<string, unknown>; // Rule-specific options
|
|
98
98
|
}
|
|
99
99
|
```
|
|
100
100
|
|
|
@@ -110,78 +110,78 @@ import { Rule, Issue, Severity, RuleCategory, ValidationContext } from '@types';
|
|
|
110
110
|
import { XPathHelper } from '@core/XPathHelper';
|
|
111
111
|
|
|
112
112
|
export abstract class BaseRule implements Rule {
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
113
|
+
abstract id: string;
|
|
114
|
+
abstract name: string;
|
|
115
|
+
abstract description: string;
|
|
116
|
+
abstract severity: Severity;
|
|
117
|
+
abstract category: RuleCategory;
|
|
118
|
+
|
|
119
|
+
docsUrl?: string;
|
|
120
|
+
|
|
121
|
+
protected xpath = XPathHelper.getInstance();
|
|
122
|
+
|
|
123
|
+
abstract validate(doc: Document, context: ValidationContext): Issue[];
|
|
124
|
+
|
|
125
|
+
// --- Utility Methods ---
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Execute XPath and return matching nodes
|
|
129
|
+
*/
|
|
130
|
+
protected select(expression: string, doc: Document): Node[] {
|
|
131
|
+
return this.xpath.select(expression, doc);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Create an issue with consistent formatting
|
|
136
|
+
*/
|
|
137
|
+
protected createIssue(
|
|
138
|
+
node: Node,
|
|
139
|
+
message: string,
|
|
140
|
+
options?: {
|
|
141
|
+
suggestion?: string;
|
|
142
|
+
severity?: Severity;
|
|
143
|
+
},
|
|
144
|
+
): Issue {
|
|
145
|
+
return {
|
|
146
|
+
line: this.getLineNumber(node),
|
|
147
|
+
column: this.getColumnNumber(node),
|
|
148
|
+
message,
|
|
149
|
+
ruleId: this.id,
|
|
150
|
+
severity: options?.severity ?? this.severity,
|
|
151
|
+
suggestion: options?.suggestion,
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Get line number from node (xmldom stores this)
|
|
157
|
+
*/
|
|
158
|
+
protected getLineNumber(node: Node): number {
|
|
159
|
+
// xmldom stores line info in columnNumber/lineNumber
|
|
160
|
+
return (node as any).lineNumber ?? 1;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Get column number from node
|
|
165
|
+
*/
|
|
166
|
+
protected getColumnNumber(node: Node): number | undefined {
|
|
167
|
+
return (node as any).columnNumber;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Check if a node has a specific attribute
|
|
172
|
+
*/
|
|
173
|
+
protected hasAttribute(node: Node, attrName: string): boolean {
|
|
174
|
+
const element = node as Element;
|
|
175
|
+
return element.hasAttribute?.(attrName) ?? false;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Get attribute value from node
|
|
180
|
+
*/
|
|
181
|
+
protected getAttribute(node: Node, attrName: string): string | null {
|
|
182
|
+
const element = node as Element;
|
|
183
|
+
return element.getAttribute?.(attrName) ?? null;
|
|
184
|
+
}
|
|
185
185
|
}
|
|
186
186
|
```
|
|
187
187
|
|
|
@@ -193,43 +193,43 @@ export abstract class BaseRule implements Rule {
|
|
|
193
193
|
|
|
194
194
|
```typescript
|
|
195
195
|
const MULE_NAMESPACES = {
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
196
|
+
mule: 'http://www.mulesoft.org/schema/mule/core',
|
|
197
|
+
http: 'http://www.mulesoft.org/schema/mule/http',
|
|
198
|
+
https: 'http://www.mulesoft.org/schema/mule/https',
|
|
199
|
+
ee: 'http://www.mulesoft.org/schema/mule/ee/core',
|
|
200
|
+
doc: 'http://www.mulesoft.org/schema/mule/documentation',
|
|
201
|
+
tls: 'http://www.mulesoft.org/schema/mule/tls',
|
|
202
|
+
db: 'http://www.mulesoft.org/schema/mule/db',
|
|
203
|
+
file: 'http://www.mulesoft.org/schema/mule/file',
|
|
204
|
+
sftp: 'http://www.mulesoft.org/schema/mule/sftp',
|
|
205
|
+
vm: 'http://www.mulesoft.org/schema/mule/vm',
|
|
206
|
+
jms: 'http://www.mulesoft.org/schema/mule/jms',
|
|
207
|
+
apikit: 'http://www.mulesoft.org/schema/mule/mule-apikit',
|
|
208
|
+
'api-gateway': 'http://www.mulesoft.org/schema/mule/api-gateway',
|
|
209
|
+
'secure-properties': 'http://www.mulesoft.org/schema/mule/secure-properties',
|
|
210
|
+
os: 'http://www.mulesoft.org/schema/mule/os',
|
|
211
|
+
batch: 'http://www.mulesoft.org/schema/mule/batch',
|
|
212
212
|
};
|
|
213
213
|
```
|
|
214
214
|
|
|
215
215
|
### Common XPath Patterns
|
|
216
216
|
|
|
217
|
-
| Purpose
|
|
218
|
-
|
|
219
|
-
| All flows
|
|
220
|
-
| All sub-flows
|
|
221
|
-
| Flow by name
|
|
222
|
-
| All loggers
|
|
223
|
-
| Loggers without category
|
|
224
|
-
| All error handlers
|
|
225
|
-
| All on-error blocks
|
|
226
|
-
| All HTTP listeners
|
|
227
|
-
| All HTTP requests
|
|
228
|
-
| Choice blocks
|
|
229
|
-
| DataWeave transforms
|
|
230
|
-
| Set-variable
|
|
231
|
-
| Flows without error handler
|
|
232
|
-
| Attributes starting with http | `//*[@*[starts-with(., 'http:')]]`
|
|
217
|
+
| Purpose | XPath Expression |
|
|
218
|
+
| ----------------------------- | ------------------------------------------------------- |
|
|
219
|
+
| All flows | `//mule:flow` |
|
|
220
|
+
| All sub-flows | `//mule:sub-flow` |
|
|
221
|
+
| Flow by name | `//mule:flow[@name='my-flow']` |
|
|
222
|
+
| All loggers | `//mule:logger` |
|
|
223
|
+
| Loggers without category | `//mule:logger[not(@category)]` |
|
|
224
|
+
| All error handlers | `//mule:error-handler` |
|
|
225
|
+
| All on-error blocks | `//mule:on-error-continue \| //mule:on-error-propagate` |
|
|
226
|
+
| All HTTP listeners | `//http:listener` |
|
|
227
|
+
| All HTTP requests | `//http:request` |
|
|
228
|
+
| Choice blocks | `//mule:choice` |
|
|
229
|
+
| DataWeave transforms | `//ee:transform` |
|
|
230
|
+
| Set-variable | `//mule:set-variable` |
|
|
231
|
+
| Flows without error handler | `//mule:flow[not(mule:error-handler)]` |
|
|
232
|
+
| Attributes starting with http | `//*[@*[starts-with(., 'http:')]]` |
|
|
233
233
|
|
|
234
234
|
---
|
|
235
235
|
|
|
@@ -242,43 +242,43 @@ import { Document, Node } from '@xmldom/xmldom';
|
|
|
242
242
|
import { BaseRule, Issue, ValidationContext } from '@types';
|
|
243
243
|
|
|
244
244
|
export class FlowNamingRule extends BaseRule {
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
return issues;
|
|
245
|
+
id = 'MULE-002';
|
|
246
|
+
name = 'Flow Naming Convention';
|
|
247
|
+
description = 'Flows must end with "-flow", sub-flows with "-subflow"';
|
|
248
|
+
severity = 'warning' as const;
|
|
249
|
+
category = 'naming' as const;
|
|
250
|
+
|
|
251
|
+
validate(doc: Document, context: ValidationContext): Issue[] {
|
|
252
|
+
const issues: Issue[] = [];
|
|
253
|
+
|
|
254
|
+
// Check flows
|
|
255
|
+
const flows = this.select('//mule:flow', doc);
|
|
256
|
+
for (const flow of flows) {
|
|
257
|
+
const name = this.getAttribute(flow, 'name');
|
|
258
|
+
if (name && !name.endsWith('-flow')) {
|
|
259
|
+
issues.push(
|
|
260
|
+
this.createIssue(flow, `Flow "${name}" should end with "-flow"`, {
|
|
261
|
+
suggestion: `Rename to "${name}-flow"`,
|
|
262
|
+
}),
|
|
263
|
+
);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// Check sub-flows
|
|
268
|
+
const subflows = this.select('//mule:sub-flow', doc);
|
|
269
|
+
for (const subflow of subflows) {
|
|
270
|
+
const name = this.getAttribute(subflow, 'name');
|
|
271
|
+
if (name && !name.endsWith('-subflow')) {
|
|
272
|
+
issues.push(
|
|
273
|
+
this.createIssue(subflow, `Sub-flow "${name}" should end with "-subflow"`, {
|
|
274
|
+
suggestion: `Rename to "${name}-subflow"`,
|
|
275
|
+
}),
|
|
276
|
+
);
|
|
277
|
+
}
|
|
281
278
|
}
|
|
279
|
+
|
|
280
|
+
return issues;
|
|
281
|
+
}
|
|
282
282
|
}
|
|
283
283
|
```
|
|
284
284
|
|
|
@@ -289,30 +289,28 @@ import { Document, Node } from '@xmldom/xmldom';
|
|
|
289
289
|
import { BaseRule, Issue, ValidationContext } from '@types';
|
|
290
290
|
|
|
291
291
|
export class LoggerCategoryRule extends BaseRule {
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
}
|
|
311
|
-
));
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
return issues;
|
|
292
|
+
id = 'MULE-006';
|
|
293
|
+
name = 'Logger Category Required';
|
|
294
|
+
description = 'All loggers must have a category attribute for proper log filtering';
|
|
295
|
+
severity = 'warning' as const;
|
|
296
|
+
category = 'logging' as const;
|
|
297
|
+
|
|
298
|
+
validate(doc: Document, context: ValidationContext): Issue[] {
|
|
299
|
+
const issues: Issue[] = [];
|
|
300
|
+
|
|
301
|
+
const loggers = this.select('//mule:logger[not(@category)]', doc);
|
|
302
|
+
|
|
303
|
+
for (const logger of loggers) {
|
|
304
|
+
const message = this.getAttribute(logger, 'message') || 'unknown';
|
|
305
|
+
issues.push(
|
|
306
|
+
this.createIssue(logger, `Logger is missing 'category' attribute`, {
|
|
307
|
+
suggestion: `Add category="com.myorg.${context.relativePath.replace(/\//g, '.')}"`,
|
|
308
|
+
}),
|
|
309
|
+
);
|
|
315
310
|
}
|
|
311
|
+
|
|
312
|
+
return issues;
|
|
313
|
+
}
|
|
316
314
|
}
|
|
317
315
|
```
|
|
318
316
|
|
|
@@ -323,54 +321,56 @@ import { Document, Node, Element } from '@xmldom/xmldom';
|
|
|
323
321
|
import { BaseRule, Issue, ValidationContext } from '@types';
|
|
324
322
|
|
|
325
323
|
export class HardcodedHttpRule extends BaseRule {
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
324
|
+
id = 'MULE-004';
|
|
325
|
+
name = 'Hardcoded HTTP URLs';
|
|
326
|
+
description = 'HTTP/HTTPS URLs should use properties, not hardcoded values';
|
|
327
|
+
severity = 'error' as const;
|
|
328
|
+
category = 'security' as const;
|
|
329
|
+
|
|
330
|
+
private readonly URL_PATTERN = /^https?:\/\//i;
|
|
331
|
+
private readonly ALLOWED_PATTERNS = [
|
|
332
|
+
/\$\{[^}]+\}/, // Property placeholders ${...}
|
|
333
|
+
/\#\[[^\]]+\]/, // DataWeave expressions #[...]
|
|
334
|
+
];
|
|
335
|
+
|
|
336
|
+
validate(doc: Document, context: ValidationContext): Issue[] {
|
|
337
|
+
const issues: Issue[] = [];
|
|
338
|
+
|
|
339
|
+
// Find all elements with attributes containing http:// or https://
|
|
340
|
+
const allElements = doc.getElementsByTagName('*');
|
|
341
|
+
|
|
342
|
+
for (let i = 0; i < allElements.length; i++) {
|
|
343
|
+
const element = allElements[i];
|
|
344
|
+
const attrs = element.attributes;
|
|
345
|
+
|
|
346
|
+
for (let j = 0; j < attrs.length; j++) {
|
|
347
|
+
const attr = attrs[j];
|
|
348
|
+
const value = attr.value;
|
|
349
|
+
|
|
350
|
+
if (this.URL_PATTERN.test(value) && !this.isAllowedPattern(value)) {
|
|
351
|
+
issues.push(
|
|
352
|
+
this.createIssue(
|
|
353
|
+
element,
|
|
354
|
+
`Hardcoded URL "${this.truncate(value)}" found in attribute "${attr.name}"`,
|
|
355
|
+
{
|
|
356
|
+
suggestion: 'Use property placeholder: ${http.url}',
|
|
357
|
+
},
|
|
358
|
+
),
|
|
359
|
+
);
|
|
362
360
|
}
|
|
363
|
-
|
|
364
|
-
return issues;
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
private isAllowedPattern(value: string): boolean {
|
|
368
|
-
return this.ALLOWED_PATTERNS.some(pattern => pattern.test(value));
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
private truncate(value: string, maxLen = 50): string {
|
|
372
|
-
return value.length > maxLen ? value.substring(0, maxLen) + '...' : value;
|
|
361
|
+
}
|
|
373
362
|
}
|
|
363
|
+
|
|
364
|
+
return issues;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
private isAllowedPattern(value: string): boolean {
|
|
368
|
+
return this.ALLOWED_PATTERNS.some((pattern) => pattern.test(value));
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
private truncate(value: string, maxLen = 50): string {
|
|
372
|
+
return value.length > maxLen ? value.substring(0, maxLen) + '...' : value;
|
|
373
|
+
}
|
|
374
374
|
}
|
|
375
375
|
```
|
|
376
376
|
|
|
@@ -397,16 +397,16 @@ import { DwlStandardsRule } from './standards/DwlStandardsRule';
|
|
|
397
397
|
|
|
398
398
|
// Rule registry
|
|
399
399
|
export const RULES: Rule[] = [
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
400
|
+
new FlowNamingRule(),
|
|
401
|
+
new GlobalErrorHandlerRule(),
|
|
402
|
+
new MissingErrorHandlerRule(),
|
|
403
|
+
new HardcodedHttpRule(),
|
|
404
|
+
new HttpStatusRule(),
|
|
405
|
+
new LoggerCategoryRule(),
|
|
406
|
+
new CorrelationIdRule(),
|
|
407
|
+
new ChoiceAntiPatternRule(),
|
|
408
|
+
new GenericErrorRule(),
|
|
409
|
+
new DwlStandardsRule(),
|
|
410
410
|
];
|
|
411
411
|
|
|
412
412
|
// Export for external use
|
|
@@ -446,8 +446,8 @@ flowchart TD
|
|
|
446
446
|
"rules": {
|
|
447
447
|
"MULE-001": { "enabled": true, "severity": "error" },
|
|
448
448
|
"MULE-002": { "enabled": true, "severity": "warning" },
|
|
449
|
-
"MULE-003": {
|
|
450
|
-
"enabled": true,
|
|
449
|
+
"MULE-003": {
|
|
450
|
+
"enabled": true,
|
|
451
451
|
"options": {
|
|
452
452
|
"excludePatterns": ["*-api-main"]
|
|
453
453
|
}
|
|
@@ -466,12 +466,12 @@ flowchart TD
|
|
|
466
466
|
|
|
467
467
|
Some rules support additional options:
|
|
468
468
|
|
|
469
|
-
| Rule
|
|
470
|
-
|
|
471
|
-
| MULE-002 | `flowSuffix`
|
|
472
|
-
| MULE-002 | `subflowSuffix`
|
|
473
|
-
| MULE-003 | `excludePatterns` | string[] | `[]`
|
|
474
|
-
| MULE-006 | `requirePrefix`
|
|
469
|
+
| Rule | Option | Type | Default | Description |
|
|
470
|
+
| -------- | ----------------- | -------- | ---------- | ----------------------------- |
|
|
471
|
+
| MULE-002 | `flowSuffix` | string | `-flow` | Expected flow name suffix |
|
|
472
|
+
| MULE-002 | `subflowSuffix` | string | `-subflow` | Expected sub-flow suffix |
|
|
473
|
+
| MULE-003 | `excludePatterns` | string[] | `[]` | Flow name patterns to exclude |
|
|
474
|
+
| MULE-006 | `requirePrefix` | string | `null` | Required category prefix |
|
|
475
475
|
|
|
476
476
|
---
|
|
477
477
|
|
|
@@ -484,37 +484,37 @@ import { FlowNamingRule } from '../rules/naming/FlowNamingRule';
|
|
|
484
484
|
import { parseXml } from '../core/XmlParser';
|
|
485
485
|
|
|
486
486
|
describe('FlowNamingRule', () => {
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
487
|
+
const rule = new FlowNamingRule();
|
|
488
|
+
|
|
489
|
+
it('should pass for correctly named flow', () => {
|
|
490
|
+
const xml = `
|
|
491
491
|
<mule xmlns="http://www.mulesoft.org/schema/mule/core">
|
|
492
492
|
<flow name="my-process-flow">
|
|
493
493
|
<logger message="test"/>
|
|
494
494
|
</flow>
|
|
495
495
|
</mule>
|
|
496
496
|
`;
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
497
|
+
const doc = parseXml(xml);
|
|
498
|
+
const issues = rule.validate(doc, mockContext);
|
|
499
|
+
|
|
500
|
+
expect(issues).toHaveLength(0);
|
|
501
|
+
});
|
|
502
|
+
|
|
503
|
+
it('should fail for incorrectly named flow', () => {
|
|
504
|
+
const xml = `
|
|
505
505
|
<mule xmlns="http://www.mulesoft.org/schema/mule/core">
|
|
506
506
|
<flow name="myProcess">
|
|
507
507
|
<logger message="test"/>
|
|
508
508
|
</flow>
|
|
509
509
|
</mule>
|
|
510
510
|
`;
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
511
|
+
const doc = parseXml(xml);
|
|
512
|
+
const issues = rule.validate(doc, mockContext);
|
|
513
|
+
|
|
514
|
+
expect(issues).toHaveLength(1);
|
|
515
|
+
expect(issues[0].ruleId).toBe('MULE-002');
|
|
516
|
+
expect(issues[0].message).toContain('myProcess');
|
|
517
|
+
});
|
|
518
518
|
});
|
|
519
519
|
```
|
|
520
520
|
|
|
@@ -522,20 +522,20 @@ describe('FlowNamingRule', () => {
|
|
|
522
522
|
|
|
523
523
|
## Performance Specifications
|
|
524
524
|
|
|
525
|
-
| Metric
|
|
526
|
-
|
|
527
|
-
| Files per second
|
|
528
|
-
| Memory per file
|
|
529
|
-
| Rule execution
|
|
530
|
-
| Total for 100 files | < 5 seconds
|
|
525
|
+
| Metric | Target |
|
|
526
|
+
| ------------------- | --------------- |
|
|
527
|
+
| Files per second | > 100 |
|
|
528
|
+
| Memory per file | < 10MB |
|
|
529
|
+
| Rule execution | < 50ms per rule |
|
|
530
|
+
| Total for 100 files | < 5 seconds |
|
|
531
531
|
|
|
532
532
|
---
|
|
533
533
|
|
|
534
534
|
## Exit Codes
|
|
535
535
|
|
|
536
|
-
| Code | Meaning
|
|
537
|
-
|
|
538
|
-
| 0
|
|
539
|
-
| 1
|
|
540
|
-
| 2
|
|
541
|
-
| 3
|
|
536
|
+
| Code | Meaning |
|
|
537
|
+
| ---- | ------------------------------ |
|
|
538
|
+
| 0 | No errors or warnings |
|
|
539
|
+
| 1 | At least one error found |
|
|
540
|
+
| 2 | Configuration error |
|
|
541
|
+
| 3 | Critical error (parse failure) |
|