x-fidelity 3.21.0 → 3.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/CHANGELOG.md +36 -0
- package/dist/core/engine/engineRunner.js +76 -8
- package/dist/core/engine/engineRunner.test.js +2 -0
- package/dist/core/engine/engineSetup.js +55 -4
- package/dist/core/engine/errorActionExecutor.d.ts +1 -2
- package/dist/core/engine/errorActionExecutor.js +23 -15
- package/dist/core/engine/errorActionExecutor.test.js +2 -0
- package/dist/facts/globalFileAnalysisFacts.js +1 -3
- package/dist/index.js +22 -1
- package/dist/notifications/reportGenerator.d.ts +4 -0
- package/dist/notifications/reportGenerator.js +62 -3
- package/dist/operators/globalPatternCount.js +1 -1
- package/dist/operators/globalPatternCount.test.js +2 -2
- package/dist/operators/globalPatternRatio.test.js +3 -3
- package/dist/types/typeDefs.d.ts +29 -0
- package/dist/utils/jsonSchemas.js +6 -0
- package/dist/utils/logger.d.ts +2 -0
- package/dist/utils/logger.js +15 -0
- package/dist/xfidelity +22 -1
- package/package.json +1 -1
- package/src/core/engine/engineRunner.test.ts +3 -1
- package/src/core/engine/engineRunner.ts +84 -2
- package/src/core/engine/engineSetup.ts +62 -2
- package/src/core/engine/errorActionExecutor.test.ts +3 -1
- package/src/core/engine/errorActionExecutor.ts +26 -18
- package/src/facts/globalFileAnalysisFacts.ts +1 -3
- package/src/index.ts +21 -1
- package/src/notifications/reportGenerator.ts +76 -3
- package/src/operators/globalPatternCount.test.ts +2 -2
- package/src/operators/globalPatternCount.ts +1 -1
- package/src/operators/globalPatternRatio.test.ts +3 -3
- package/src/types/typeDefs.ts +29 -0
- package/src/utils/jsonSchemas.ts +7 -1
- package/src/utils/logger.ts +15 -1
- package/website/docs/facts.md +119 -4
- package/website/docs/operators.md +170 -11
- package/xfi-report-2025-04-02.json +23 -11
- package/xfi-report-2025-04-02.md +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,39 @@
|
|
|
1
|
+
# [3.22.0](https://github.com/zotoio/x-fidelity/compare/v3.21.1...v3.22.0) (2025-04-02)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* access operatorThreshold and operatorValue through details property ([e427e94](https://github.com/zotoio/x-fidelity/commit/e427e942aa0072483e02c62fb6e8af967b6a76cc))
|
|
7
|
+
* add missing logger function mocks in test files ([713a1e9](https://github.com/zotoio/x-fidelity/commit/713a1e969142272f4f9e835ede0853c8e92d1620))
|
|
8
|
+
* add ruleName property to BasicTelemetryMetadata interface ([0c66b78](https://github.com/zotoio/x-fidelity/commit/0c66b787986018250f77f727425dfe012400c2e9))
|
|
9
|
+
* add type assertions for conditionType in engineSetup ([734d492](https://github.com/zotoio/x-fidelity/commit/734d492b09e76531c42d712ff0e70a4150761b55))
|
|
10
|
+
* resolve TypeScript type errors and null safety issues ([bfb892d](https://github.com/zotoio/x-fidelity/commit/bfb892d637519f3d4d7b768067e4f478ad74971a))
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### Features
|
|
14
|
+
|
|
15
|
+
* add all condition operators to rule failure details ([023f44f](https://github.com/zotoio/x-fidelity/commit/023f44fffdd82011fc00ea9ae2dabd2ba380e5f7))
|
|
16
|
+
* add all operator value objects to rule results ([c8103c5](https://github.com/zotoio/x-fidelity/commit/c8103c55f60f9da2453305980fe305c57341f961))
|
|
17
|
+
* add backward compatibility for pattern and file output grouping ([db22b70](https://github.com/zotoio/x-fidelity/commit/db22b7084532ddf3edac2b16e7fe30761b15d5d4))
|
|
18
|
+
* add condition details to rule failure output ([168aeec](https://github.com/zotoio/x-fidelity/commit/168aeec2741a22e6706d309b93e361946024a14a))
|
|
19
|
+
* add operator threshold details to rule output and telemetry ([c28974a](https://github.com/zotoio/x-fidelity/commit/c28974a0d1259a943b5afda3ec3f83424c196b16))
|
|
20
|
+
* add operator threshold details to rule output reports ([0ce279d](https://github.com/zotoio/x-fidelity/commit/0ce279d4e480645ba2636beedec5d2fd17f8dadc))
|
|
21
|
+
* add operator value information to report sections ([697a1a3](https://github.com/zotoio/x-fidelity/commit/697a1a36dbb0b6de19b1c5b3be7ca308f757cbbe))
|
|
22
|
+
* add operator value to rule failure details ([ae62f56](https://github.com/zotoio/x-fidelity/commit/ae62f561583209bc8496ff0cd07be597b04909d9))
|
|
23
|
+
* add rule name to log prefix for better traceability ([6a4c4b6](https://github.com/zotoio/x-fidelity/commit/6a4c4b6880c9dfa06bad56a024b3065db771dcdd))
|
|
24
|
+
* enhance rule condition details with descriptions and recommendations ([8eef433](https://github.com/zotoio/x-fidelity/commit/8eef43370c800b5dcb3e9150c858ed7e848abc58))
|
|
25
|
+
* enhance rule condition tracking with detailed condition info ([4e48e7c](https://github.com/zotoio/x-fidelity/commit/4e48e7c63843aef98a6ccbf73cc1252c0e48608c))
|
|
26
|
+
* enhance rule failure reporting with detailed condition information ([915ae09](https://github.com/zotoio/x-fidelity/commit/915ae09a0b3f539d7e48effd3abe26e16d28dee5))
|
|
27
|
+
* enhance XFI_RESULT with detailed rule conditions and recommendations ([7243da5](https://github.com/zotoio/x-fidelity/commit/7243da538f1c37abae2806bc405ec6c251184bb5))
|
|
28
|
+
* include all rule conditions and parameters in output ([ae802bb](https://github.com/zotoio/x-fidelity/commit/ae802bbb41a0613835e4d3e6e477c66abcd8e1ac))
|
|
29
|
+
|
|
30
|
+
## [3.21.1](https://github.com/zotoio/x-fidelity/compare/v3.21.0...v3.21.1) (2025-04-02)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
### Bug Fixes
|
|
34
|
+
|
|
35
|
+
* **tests:** logic issue ([1578b81](https://github.com/zotoio/x-fidelity/commit/1578b817ff58912a312e32f5480b2b5f655a1014))
|
|
36
|
+
|
|
1
37
|
# [3.21.0](https://github.com/zotoio/x-fidelity/compare/v3.20.0...v3.21.0) (2025-04-02)
|
|
2
38
|
|
|
3
39
|
|
|
@@ -15,7 +15,7 @@ const configManager_1 = require("../configManager");
|
|
|
15
15
|
const errorActionExecutor_1 = require("./errorActionExecutor");
|
|
16
16
|
function runEngineOnFiles(params) {
|
|
17
17
|
return __awaiter(this, void 0, void 0, function* () {
|
|
18
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
|
|
18
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p;
|
|
19
19
|
const { engine, fileData, installedDependencyVersions, minimumDependencyVersions, standardStructure } = params;
|
|
20
20
|
const msg = `\n==========================\nRUNNING FILE CHECKS..\n==========================`;
|
|
21
21
|
logger_1.logger.info(msg);
|
|
@@ -41,6 +41,8 @@ function runEngineOnFiles(params) {
|
|
|
41
41
|
};
|
|
42
42
|
const fileFailures = [];
|
|
43
43
|
try {
|
|
44
|
+
// Save the original log prefix
|
|
45
|
+
const originalLogPrefix = (0, logger_1.getLogPrefix)();
|
|
44
46
|
const { results } = yield engine.run(facts);
|
|
45
47
|
const seenFailures = new Set();
|
|
46
48
|
for (const result of results) {
|
|
@@ -49,11 +51,66 @@ function runEngineOnFiles(params) {
|
|
|
49
51
|
// Create unique key for deduplication
|
|
50
52
|
const failureKey = `${result.name}:${(_a = result.event) === null || _a === void 0 ? void 0 : _a.type}:${(_c = (_b = result.event) === null || _b === void 0 ? void 0 : _b.params) === null || _c === void 0 ? void 0 : _c.message}`;
|
|
51
53
|
if (!seenFailures.has(failureKey)) {
|
|
54
|
+
// Set the rule name as log prefix for this failure
|
|
55
|
+
const ruleName = result.name || 'unknown-rule';
|
|
56
|
+
(0, logger_1.setLogPrefix)(`${originalLogPrefix}:${ruleName}`);
|
|
57
|
+
// Extract condition details from the rule
|
|
58
|
+
let conditionDetails = undefined;
|
|
59
|
+
let ruleDescription = null;
|
|
60
|
+
let recommendations = null;
|
|
61
|
+
let allConditions = [];
|
|
62
|
+
let conditionType = 'unknown';
|
|
63
|
+
try {
|
|
64
|
+
// Find the condition that triggered this rule
|
|
65
|
+
const rule = engine.rules.find((r) => r.name === result.name);
|
|
66
|
+
if (rule) {
|
|
67
|
+
// Extract rule description if available
|
|
68
|
+
ruleDescription = rule.description || 'No description available';
|
|
69
|
+
// Extract recommendations if available
|
|
70
|
+
if ((_e = (_d = result.event) === null || _d === void 0 ? void 0 : _d.params) === null || _e === void 0 ? void 0 : _e.recommendations) {
|
|
71
|
+
recommendations = result.event.params.recommendations;
|
|
72
|
+
}
|
|
73
|
+
else if (rule.recommendations) {
|
|
74
|
+
recommendations = rule.recommendations;
|
|
75
|
+
}
|
|
76
|
+
// Extract operator value from the condition
|
|
77
|
+
const conditions = rule.conditions.all || rule.conditions.any || [];
|
|
78
|
+
conditionType = rule.conditions.all ? 'all' : 'any';
|
|
79
|
+
// Capture all conditions with their parameters
|
|
80
|
+
allConditions = conditions.map((condition) => ({
|
|
81
|
+
fact: condition.fact,
|
|
82
|
+
operator: condition.operator,
|
|
83
|
+
value: condition.value,
|
|
84
|
+
params: condition.params,
|
|
85
|
+
path: condition.path,
|
|
86
|
+
priority: condition.priority
|
|
87
|
+
}));
|
|
88
|
+
// Find the first condition with operator and value for backward compatibility
|
|
89
|
+
for (const condition of conditions) {
|
|
90
|
+
if (condition.operator && condition.value !== undefined) {
|
|
91
|
+
conditionDetails = {
|
|
92
|
+
fact: condition.fact,
|
|
93
|
+
operator: condition.operator,
|
|
94
|
+
value: condition.value,
|
|
95
|
+
params: condition.params
|
|
96
|
+
};
|
|
97
|
+
break;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
catch (err) {
|
|
103
|
+
logger_1.logger.debug(`Error extracting operator value: ${err}`);
|
|
104
|
+
}
|
|
52
105
|
fileFailures.push({
|
|
53
106
|
ruleFailure: result.name,
|
|
54
|
-
level: (
|
|
55
|
-
details: Object.assign({ message: ((
|
|
107
|
+
level: (_f = result.event) === null || _f === void 0 ? void 0 : _f.type,
|
|
108
|
+
details: Object.assign({ message: ((_h = (_g = result.event) === null || _g === void 0 ? void 0 : _g.params) === null || _h === void 0 ? void 0 : _h.message) || 'Rule failure detected', conditionDetails: conditionDetails, allConditions: allConditions, conditionType: conditionType, ruleDescription: ruleDescription, recommendations: recommendations }, (_j = result.event) === null || _j === void 0 ? void 0 : _j.params)
|
|
56
109
|
});
|
|
110
|
+
// Restore original log prefix
|
|
111
|
+
(0, logger_1.setLogPrefix)(originalLogPrefix);
|
|
112
|
+
// Restore original log prefix before continuing
|
|
113
|
+
(0, logger_1.setLogPrefix)(originalLogPrefix);
|
|
57
114
|
seenFailures.add(failureKey);
|
|
58
115
|
}
|
|
59
116
|
else {
|
|
@@ -64,8 +121,11 @@ function runEngineOnFiles(params) {
|
|
|
64
121
|
}
|
|
65
122
|
catch (e) {
|
|
66
123
|
const error = e;
|
|
67
|
-
const failedRuleName = (
|
|
124
|
+
const failedRuleName = (_k = error === null || error === void 0 ? void 0 : error.rule) === null || _k === void 0 ? void 0 : _k.name;
|
|
68
125
|
const rule = failedRuleName ? engine.rules.find((r) => r.name === failedRuleName) : null;
|
|
126
|
+
// Set the rule name as log prefix for this error
|
|
127
|
+
const originalLogPrefix = (0, logger_1.getLogPrefix)();
|
|
128
|
+
(0, logger_1.setLogPrefix)(`${originalLogPrefix}:${failedRuleName || 'unknown-rule'}`);
|
|
69
129
|
// Determine error source and level
|
|
70
130
|
let errorSource = 'unknown';
|
|
71
131
|
let errorLevel = 'error';
|
|
@@ -89,7 +149,7 @@ function runEngineOnFiles(params) {
|
|
|
89
149
|
}
|
|
90
150
|
else if (failedRuleName) {
|
|
91
151
|
errorSource = 'rule';
|
|
92
|
-
errorLevel = (rule === null || rule === void 0 ? void 0 : rule.errorBehavior) === 'fatal' || ((
|
|
152
|
+
errorLevel = (rule === null || rule === void 0 ? void 0 : rule.errorBehavior) === 'fatal' || ((_l = rule === null || rule === void 0 ? void 0 : rule.event) === null || _l === void 0 ? void 0 : _l.type) === 'fatality' ? 'fatality' : 'error';
|
|
93
153
|
}
|
|
94
154
|
logger_1.logger.error({
|
|
95
155
|
index: i,
|
|
@@ -99,10 +159,10 @@ function runEngineOnFiles(params) {
|
|
|
99
159
|
source: errorSource,
|
|
100
160
|
type: errorLevel,
|
|
101
161
|
stack: (handledError || error).stack,
|
|
102
|
-
details: ((
|
|
162
|
+
details: ((_m = error === null || error === void 0 ? void 0 : error.pluginError) === null || _m === void 0 ? void 0 : _m.details) || error.message
|
|
103
163
|
}, `Execution error occurred at file ${file.filePath} (${i + 1} of ${fileCount})`);
|
|
104
164
|
// Execute error action if specified
|
|
105
|
-
if ((
|
|
165
|
+
if ((_o = rule === null || rule === void 0 ? void 0 : rule.onError) === null || _o === void 0 ? void 0 : _o.action) {
|
|
106
166
|
try {
|
|
107
167
|
const actionResult = yield (0, errorActionExecutor_1.executeErrorAction)(rule.onError.action, {
|
|
108
168
|
error: error,
|
|
@@ -133,9 +193,17 @@ function runEngineOnFiles(params) {
|
|
|
133
193
|
message: `${errorSource} execution failed: ${(handledError || error).message}`,
|
|
134
194
|
source: errorSource,
|
|
135
195
|
stack: (handledError || error).stack,
|
|
136
|
-
details: (
|
|
196
|
+
details: (_p = error === null || error === void 0 ? void 0 : error.pluginError) === null || _p === void 0 ? void 0 : _p.details,
|
|
197
|
+
// Include rule information if available
|
|
198
|
+
rule: rule ? {
|
|
199
|
+
name: rule.name,
|
|
200
|
+
conditions: rule.conditions,
|
|
201
|
+
event: rule.event
|
|
202
|
+
} : undefined
|
|
137
203
|
}
|
|
138
204
|
});
|
|
205
|
+
// Restore original log prefix before returning
|
|
206
|
+
(0, logger_1.setLogPrefix)(originalLogPrefix);
|
|
139
207
|
}
|
|
140
208
|
if (fileFailures.length > 0) {
|
|
141
209
|
failures.push({ filePath: file.filePath, errors: fileFailures });
|
|
@@ -94,12 +94,53 @@ function setupEngine(params) {
|
|
|
94
94
|
}
|
|
95
95
|
});
|
|
96
96
|
}
|
|
97
|
-
engine.on('success', (_a) => __awaiter(this, [_a], void 0, function* ({ type, params }) {
|
|
97
|
+
engine.on('success', (_a) => __awaiter(this, [_a], void 0, function* ({ type, params, name, conditions }) {
|
|
98
|
+
const originalLogPrefix = (0, logger_1.getLogPrefix)();
|
|
99
|
+
const ruleName = name || 'unknown-rule';
|
|
100
|
+
(0, logger_1.setLogPrefix)(`${originalLogPrefix}:${ruleName}`);
|
|
101
|
+
// Extract condition details from conditions
|
|
102
|
+
let conditionDetails = undefined;
|
|
103
|
+
let allConditions = [];
|
|
104
|
+
let conditionType = 'unknown';
|
|
105
|
+
try {
|
|
106
|
+
const rule = engine.rules.find((r) => r.name === name);
|
|
107
|
+
if (rule) {
|
|
108
|
+
const conditions = rule.conditions.all || rule.conditions.any || [];
|
|
109
|
+
conditionType = rule.conditions.all ? 'all' : 'any';
|
|
110
|
+
// Capture all conditions with their parameters
|
|
111
|
+
allConditions = conditions.map((condition) => ({
|
|
112
|
+
fact: condition.fact,
|
|
113
|
+
operator: condition.operator,
|
|
114
|
+
value: condition.value,
|
|
115
|
+
params: condition.params,
|
|
116
|
+
path: condition.path,
|
|
117
|
+
priority: condition.priority
|
|
118
|
+
}));
|
|
119
|
+
// Find the first condition with operator and value for backward compatibility
|
|
120
|
+
for (const condition of conditions) {
|
|
121
|
+
if (condition.operator && condition.value !== undefined) {
|
|
122
|
+
conditionDetails = {
|
|
123
|
+
fact: condition.fact,
|
|
124
|
+
operator: condition.operator,
|
|
125
|
+
value: condition.value,
|
|
126
|
+
params: condition.params
|
|
127
|
+
};
|
|
128
|
+
break;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
catch (err) {
|
|
134
|
+
logger_1.logger.debug(`Error extracting operator threshold: ${err}`);
|
|
135
|
+
}
|
|
98
136
|
if (type === 'warning') {
|
|
99
137
|
logger_1.logger.warn(`warning detected: ${JSON.stringify(params)}`);
|
|
100
138
|
yield (0, telemetry_1.sendTelemetry)({
|
|
101
139
|
eventType: 'warning',
|
|
102
|
-
metadata: Object.assign({ archetype, repoPath: ''
|
|
140
|
+
metadata: Object.assign({ archetype, repoPath: '', ruleName,
|
|
141
|
+
conditionDetails,
|
|
142
|
+
allConditions,
|
|
143
|
+
conditionType }, params),
|
|
103
144
|
timestamp: new Date().toISOString()
|
|
104
145
|
}, executionLogPrefix);
|
|
105
146
|
}
|
|
@@ -107,7 +148,10 @@ function setupEngine(params) {
|
|
|
107
148
|
logger_1.logger.error(`fatality detected: ${JSON.stringify(params)}`);
|
|
108
149
|
yield (0, telemetry_1.sendTelemetry)({
|
|
109
150
|
eventType: 'fatality',
|
|
110
|
-
metadata: Object.assign({ archetype, repoPath: ''
|
|
151
|
+
metadata: Object.assign({ archetype, repoPath: '', ruleName,
|
|
152
|
+
conditionDetails,
|
|
153
|
+
allConditions,
|
|
154
|
+
conditionType }, params),
|
|
111
155
|
timestamp: new Date().toISOString()
|
|
112
156
|
}, executionLogPrefix);
|
|
113
157
|
}
|
|
@@ -115,10 +159,17 @@ function setupEngine(params) {
|
|
|
115
159
|
logger_1.logger.error(`exemption detected: ${JSON.stringify(params)}`);
|
|
116
160
|
yield (0, telemetry_1.sendTelemetry)({
|
|
117
161
|
eventType: 'exempt',
|
|
118
|
-
metadata: Object.assign({ archetype, repoPath: ''
|
|
162
|
+
metadata: Object.assign({ archetype, repoPath: '', ruleName,
|
|
163
|
+
conditionDetails,
|
|
164
|
+
allConditions,
|
|
165
|
+
conditionType }, params),
|
|
119
166
|
timestamp: new Date().toISOString()
|
|
120
167
|
}, executionLogPrefix);
|
|
121
168
|
}
|
|
169
|
+
// Restore original log prefix
|
|
170
|
+
(0, logger_1.setLogPrefix)(originalLogPrefix);
|
|
171
|
+
// Restore original log prefix
|
|
172
|
+
(0, logger_1.setLogPrefix)(originalLogPrefix);
|
|
122
173
|
}));
|
|
123
174
|
// Add facts to engine
|
|
124
175
|
logger_1.logger.info(`=== loading facts..`);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
interface ErrorActionParams {
|
|
1
|
+
export interface ErrorActionParams {
|
|
2
2
|
error: Error;
|
|
3
3
|
rule: string;
|
|
4
4
|
level: string;
|
|
@@ -7,4 +7,3 @@ interface ErrorActionParams {
|
|
|
7
7
|
file: any;
|
|
8
8
|
}
|
|
9
9
|
export declare function executeErrorAction(actionName: string, params: ErrorActionParams): Promise<any>;
|
|
10
|
-
export {};
|
|
@@ -15,23 +15,31 @@ const pluginRegistry_1 = require("../pluginRegistry");
|
|
|
15
15
|
function executeErrorAction(actionName, params) {
|
|
16
16
|
return __awaiter(this, void 0, void 0, function* () {
|
|
17
17
|
var _a;
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
if (
|
|
23
|
-
|
|
18
|
+
const originalLogPrefix = (0, logger_1.getLogPrefix)();
|
|
19
|
+
(0, logger_1.setLogPrefix)(`${originalLogPrefix}:${params.rule || 'unknown-rule'}`);
|
|
20
|
+
try {
|
|
21
|
+
// First check if it's a plugin action
|
|
22
|
+
if (actionName.includes(':')) {
|
|
23
|
+
const [pluginName, functionName] = actionName.split(':');
|
|
24
|
+
const result = pluginRegistry_1.pluginRegistry.executePluginFunction(pluginName, functionName, params);
|
|
25
|
+
if (!result.success) {
|
|
26
|
+
throw new Error(`Plugin error action failed: ${(_a = result.error) === null || _a === void 0 ? void 0 : _a.message}`);
|
|
27
|
+
}
|
|
28
|
+
return result.data;
|
|
29
|
+
}
|
|
30
|
+
// Otherwise check built-in actions
|
|
31
|
+
switch (actionName) {
|
|
32
|
+
case 'sendNotification':
|
|
33
|
+
return yield sendNotification(params);
|
|
34
|
+
case 'logToFile':
|
|
35
|
+
return yield logToFile(params);
|
|
36
|
+
default:
|
|
37
|
+
throw new Error(`Unknown error action: ${actionName}`);
|
|
24
38
|
}
|
|
25
|
-
return result.data;
|
|
26
39
|
}
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
return yield sendNotification(params);
|
|
31
|
-
case 'logToFile':
|
|
32
|
-
return yield logToFile(params);
|
|
33
|
-
default:
|
|
34
|
-
throw new Error(`Unknown error action: ${actionName}`);
|
|
40
|
+
finally {
|
|
41
|
+
// Always restore the original log prefix
|
|
42
|
+
(0, logger_1.setLogPrefix)(originalLogPrefix);
|
|
35
43
|
}
|
|
36
44
|
});
|
|
37
45
|
}
|
|
@@ -16,6 +16,8 @@ jest.mock('../../utils/logger', () => ({
|
|
|
16
16
|
info: jest.fn(),
|
|
17
17
|
error: jest.fn(),
|
|
18
18
|
},
|
|
19
|
+
getLogPrefix: jest.fn().mockReturnValue('test-prefix'),
|
|
20
|
+
setLogPrefix: jest.fn(),
|
|
19
21
|
}));
|
|
20
22
|
describe('executeErrorAction', () => {
|
|
21
23
|
it('should execute sendNotification action', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
@@ -125,12 +125,10 @@ exports.globalFileAnalysis = {
|
|
|
125
125
|
const patternEntry = result.patternData.find((p) => p.pattern === pattern);
|
|
126
126
|
return sum + ((patternEntry === null || patternEntry === void 0 ? void 0 : patternEntry.count) || 0);
|
|
127
127
|
}, 0);
|
|
128
|
-
// Create matchCounts
|
|
128
|
+
// Create matchCounts for convenience
|
|
129
129
|
result.matchCounts = {};
|
|
130
|
-
result.fileMatches = {};
|
|
131
130
|
result.patternData.forEach((entry) => {
|
|
132
131
|
result.matchCounts[entry.pattern] = entry.count;
|
|
133
|
-
result.fileMatches[entry.pattern] = entry.files;
|
|
134
132
|
});
|
|
135
133
|
// Add summary to result
|
|
136
134
|
result.summary = {
|
package/dist/index.js
CHANGED
|
@@ -151,7 +151,28 @@ function main() {
|
|
|
151
151
|
// if results are found, there were issues found in the codebase
|
|
152
152
|
if (resultMetadata.XFI_RESULT.totalIssues > 0) {
|
|
153
153
|
logger_1.logger.warn(`WARNING: lo-fi attributes detected in codebase. ${resultMetadata.XFI_RESULT.warningCount} are warnings, ${resultMetadata.XFI_RESULT.fatalityCount} are fatal.`);
|
|
154
|
-
|
|
154
|
+
// Create a more detailed summary of issues
|
|
155
|
+
const issuesSummary = {
|
|
156
|
+
totalIssues: resultMetadata.XFI_RESULT.totalIssues,
|
|
157
|
+
warningCount: resultMetadata.XFI_RESULT.warningCount,
|
|
158
|
+
fatalityCount: resultMetadata.XFI_RESULT.fatalityCount,
|
|
159
|
+
errorCount: resultMetadata.XFI_RESULT.errorCount,
|
|
160
|
+
exemptCount: resultMetadata.XFI_RESULT.exemptCount,
|
|
161
|
+
topIssues: resultMetadata.XFI_RESULT.issueDetails
|
|
162
|
+
.slice(0, 5)
|
|
163
|
+
.map(detail => ({
|
|
164
|
+
filePath: detail.filePath,
|
|
165
|
+
errors: detail.errors.map(err => {
|
|
166
|
+
var _a;
|
|
167
|
+
return ({
|
|
168
|
+
rule: err.ruleFailure,
|
|
169
|
+
level: err.level,
|
|
170
|
+
message: (_a = err.details) === null || _a === void 0 ? void 0 : _a.message
|
|
171
|
+
});
|
|
172
|
+
})
|
|
173
|
+
}))
|
|
174
|
+
};
|
|
175
|
+
logger_1.logger.warn(JSON.stringify(issuesSummary, null, 2));
|
|
155
176
|
if (resultMetadata.XFI_RESULT.errorCount > 0) {
|
|
156
177
|
logger_1.logger.error(outcomeMessage(`THERE WERE ${resultMetadata.XFI_RESULT.errorCount} UNEXPECTED ERRORS!`));
|
|
157
178
|
logger_1.logger.error(`\n${prettyResult}\n\n`);
|
|
@@ -20,6 +20,10 @@ export declare class XFiReportGenerator {
|
|
|
20
20
|
private generateDependencyIssuesSection;
|
|
21
21
|
private generateSensitiveDataSection;
|
|
22
22
|
private generateGlobalIssuesSection;
|
|
23
|
+
/**
|
|
24
|
+
* Helper method to add operator information to report sections
|
|
25
|
+
*/
|
|
26
|
+
private addOperatorValueToReport;
|
|
23
27
|
generateReport(): string;
|
|
24
28
|
saveReportToFile(outputPath: string): Promise<void>;
|
|
25
29
|
}
|
|
@@ -236,6 +236,14 @@ Based on the OpenAI analysis, these are the top 5 critical issues to address:
|
|
|
236
236
|
**Suggestion**: ${issue.suggestion}
|
|
237
237
|
|
|
238
238
|
`;
|
|
239
|
+
// Add condition details if available
|
|
240
|
+
if (openAiAnalysis.details && openAiAnalysis.details.conditionDetails) {
|
|
241
|
+
const { fact, operator, value, params } = openAiAnalysis.details.conditionDetails;
|
|
242
|
+
aiAnalysis += `**Condition**: Fact \`${fact}\` with operator \`${operator}\`\n`;
|
|
243
|
+
if (params) {
|
|
244
|
+
aiAnalysis += `**Parameters**: \`${JSON.stringify(params)}\`\n\n`;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
239
247
|
});
|
|
240
248
|
}
|
|
241
249
|
}
|
|
@@ -250,7 +258,7 @@ Based on the OpenAI analysis, these are the top 5 critical issues to address:
|
|
|
250
258
|
filePath: detail.filePath,
|
|
251
259
|
fileName: this.getFileName(detail.filePath),
|
|
252
260
|
fileUrl: this.getGithubUrl(detail.filePath),
|
|
253
|
-
details: error.details
|
|
261
|
+
details: error.details,
|
|
254
262
|
})));
|
|
255
263
|
if (complexityIssues.length === 0) {
|
|
256
264
|
return '';
|
|
@@ -259,7 +267,17 @@ Based on the OpenAI analysis, these are the top 5 critical issues to address:
|
|
|
259
267
|
|
|
260
268
|
The analysis identified several functions with high complexity that should be refactored:
|
|
261
269
|
|
|
262
|
-
|
|
270
|
+
`;
|
|
271
|
+
// Add threshold information if available from the first issue
|
|
272
|
+
if (complexityIssues[0].details && complexityIssues[0].details.operatorThreshold) {
|
|
273
|
+
const { operator, value } = complexityIssues[0].details.operatorThreshold;
|
|
274
|
+
section += `**Threshold**: \`${operator}: ${JSON.stringify(value)}\`\n\n`;
|
|
275
|
+
}
|
|
276
|
+
// Add operator value information if available
|
|
277
|
+
if (complexityIssues[0].details && complexityIssues[0].details.operatorValue) {
|
|
278
|
+
section += `**Required Value**: \`${JSON.stringify(complexityIssues[0].details.operatorValue)}\`\n\n`;
|
|
279
|
+
}
|
|
280
|
+
section += `| File | Function | Cyclomatic Complexity | Cognitive Complexity | Nesting Depth | Parameter Count | Return Count |
|
|
263
281
|
|------|----------|------------------------|----------------------|--------------|----------------|--------------|
|
|
264
282
|
`;
|
|
265
283
|
complexityIssues.forEach(issue => {
|
|
@@ -287,7 +305,16 @@ The analysis identified several functions with high complexity that should be re
|
|
|
287
305
|
|
|
288
306
|
The analysis detected outdated framework dependencies that need updating:
|
|
289
307
|
|
|
290
|
-
|
|
308
|
+
`;
|
|
309
|
+
// Add condition details if available
|
|
310
|
+
if (dependencyIssue.details.conditionDetails) {
|
|
311
|
+
const { fact, operator, params } = dependencyIssue.details.conditionDetails;
|
|
312
|
+
section += `**Condition**: Fact \`${fact}\` with operator \`${operator}\`\n`;
|
|
313
|
+
if (params) {
|
|
314
|
+
section += `**Parameters**: \`${JSON.stringify(params)}\`\n\n`;
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
section += `| Dependency | Current Version | Required Version |
|
|
291
318
|
|------------|-----------------|------------------|
|
|
292
319
|
`;
|
|
293
320
|
dependencies.forEach((dep) => {
|
|
@@ -341,9 +368,41 @@ Several files contain potentially sensitive data patterns that shouldn't be logg
|
|
|
341
368
|
`;
|
|
342
369
|
globalIssues.forEach(issue => {
|
|
343
370
|
section += `- **${issue.ruleFailure}** (${issue.level}): ${issue.details && issue.details.message ? issue.details.message : 'No details available'}\n`;
|
|
371
|
+
// Add condition details if available
|
|
372
|
+
if (issue.details && issue.details.conditionDetails) {
|
|
373
|
+
const { fact, operator, params } = issue.details.conditionDetails;
|
|
374
|
+
section += ` - Condition: Fact \`${fact}\` with operator \`${operator}\`\n`;
|
|
375
|
+
if (params) {
|
|
376
|
+
section += ` - Parameters: \`${JSON.stringify(params)}\`\n`;
|
|
377
|
+
}
|
|
378
|
+
}
|
|
344
379
|
});
|
|
345
380
|
return section;
|
|
346
381
|
}
|
|
382
|
+
/**
|
|
383
|
+
* Helper method to add operator information to report sections
|
|
384
|
+
*/
|
|
385
|
+
addOperatorValueToReport(issueDetails, error) {
|
|
386
|
+
// Add condition details if available
|
|
387
|
+
if (error.details.conditionDetails) {
|
|
388
|
+
const { fact, operator, params } = error.details.conditionDetails;
|
|
389
|
+
issueDetails += ` - Condition: Fact \`${fact}\` with operator \`${operator}\`\n`;
|
|
390
|
+
if (params) {
|
|
391
|
+
issueDetails += ` - Parameters: \`${JSON.stringify(params)}\`\n`;
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
// Add all condition operators if available
|
|
395
|
+
if (error.details && error.details.allConditionOperators && error.details.allConditionOperators.length > 0) {
|
|
396
|
+
issueDetails += ` - All Condition Operators:\n`;
|
|
397
|
+
error.details.allConditionOperators.forEach((condition, index) => {
|
|
398
|
+
issueDetails += ` ${index + 1}. Fact: \`${condition.fact}\` Operator: \`${condition.operator}\` Value: \`${JSON.stringify(condition.value)}\`\n`;
|
|
399
|
+
if (condition.params) {
|
|
400
|
+
issueDetails += ` Parameters: \`${JSON.stringify(condition.params)}\`\n`;
|
|
401
|
+
}
|
|
402
|
+
});
|
|
403
|
+
}
|
|
404
|
+
return issueDetails;
|
|
405
|
+
}
|
|
347
406
|
generateReport() {
|
|
348
407
|
const executiveSummary = this.generateExecutiveSummary();
|
|
349
408
|
const fileStatusChart = this.generateFileStatusChart();
|
|
@@ -13,7 +13,7 @@ const globalPatternCount = {
|
|
|
13
13
|
}
|
|
14
14
|
// Parse threshold and comparison type
|
|
15
15
|
let thresholdValue;
|
|
16
|
-
let comparisonType = '
|
|
16
|
+
let comparisonType = 'lte'; // Default to greater than or equal
|
|
17
17
|
if (typeof threshold === 'object' && threshold !== null) {
|
|
18
18
|
thresholdValue = threshold.threshold;
|
|
19
19
|
if (threshold.comparison) {
|
|
@@ -24,7 +24,7 @@ describe('globalPatternCount', () => {
|
|
|
24
24
|
totalMatches: 10
|
|
25
25
|
}
|
|
26
26
|
};
|
|
27
|
-
expect(globalPatternCount_1.globalPatternCount.fn(analysisResult, 5)).toBe(
|
|
27
|
+
expect(globalPatternCount_1.globalPatternCount.fn(analysisResult, 5)).toBe(false);
|
|
28
28
|
});
|
|
29
29
|
it('should return true when count exceeds threshold with explicit gte comparison', () => {
|
|
30
30
|
const analysisResult = {
|
|
@@ -81,7 +81,7 @@ describe('globalPatternCount', () => {
|
|
|
81
81
|
expect(globalPatternCount_1.globalPatternCount.fn({
|
|
82
82
|
patternData: [{ pattern: 'pattern1', count: 0, files: [] }],
|
|
83
83
|
summary: { totalMatches: 0 }
|
|
84
|
-
}, 1)).toBe(
|
|
84
|
+
}, 1)).toBe(true);
|
|
85
85
|
});
|
|
86
86
|
it('should handle new pattern totals with lte comparison', () => {
|
|
87
87
|
const analysisResult = {
|
|
@@ -81,12 +81,12 @@ describe('globalPatternRatio', () => {
|
|
|
81
81
|
});
|
|
82
82
|
it('should handle edge cases', () => {
|
|
83
83
|
// No patterns
|
|
84
|
-
expect(globalPatternRatio_1.globalPatternRatio.fn({ patternData: [], summary: {} }, {
|
|
84
|
+
expect(globalPatternRatio_1.globalPatternRatio.fn({ patternData: [], summary: {} }, { threshold: 1, comparison: 'gte' })).toBe(false);
|
|
85
85
|
// Only one pattern
|
|
86
86
|
expect(globalPatternRatio_1.globalPatternRatio.fn({
|
|
87
87
|
patternData: [{ pattern: 'pattern1', count: 5, files: [] }],
|
|
88
88
|
summary: { totalMatches: 5 }
|
|
89
|
-
}, {
|
|
89
|
+
}, { threshold: 1, comparison: 'gte' })).toBe(false);
|
|
90
90
|
// Denominator is zero
|
|
91
91
|
expect(globalPatternRatio_1.globalPatternRatio.fn({
|
|
92
92
|
patternData: [
|
|
@@ -94,7 +94,7 @@ describe('globalPatternRatio', () => {
|
|
|
94
94
|
{ pattern: 'pattern2', count: 0, files: [] }
|
|
95
95
|
],
|
|
96
96
|
summary: { totalMatches: 5 }
|
|
97
|
-
}, {
|
|
97
|
+
}, { threshold: 1, comparison: 'gte' })).toBe(false);
|
|
98
98
|
});
|
|
99
99
|
it('should handle new pattern totals with lte comparison', () => {
|
|
100
100
|
const analysisResult = {
|
package/dist/types/typeDefs.d.ts
CHANGED
|
@@ -21,6 +21,17 @@ export interface RuleFailure {
|
|
|
21
21
|
source?: 'operator' | 'fact' | 'plugin' | 'rule' | 'unknown';
|
|
22
22
|
originalError?: Error;
|
|
23
23
|
stack?: string;
|
|
24
|
+
operatorThreshold?: {
|
|
25
|
+
operator: string;
|
|
26
|
+
value: any;
|
|
27
|
+
};
|
|
28
|
+
operatorValue?: any;
|
|
29
|
+
conditionDetails?: {
|
|
30
|
+
fact: string;
|
|
31
|
+
operator: string;
|
|
32
|
+
value: any;
|
|
33
|
+
params?: any;
|
|
34
|
+
};
|
|
24
35
|
[key: string]: any;
|
|
25
36
|
} | undefined;
|
|
26
37
|
}
|
|
@@ -112,6 +123,8 @@ export interface RuleConfig {
|
|
|
112
123
|
action: string;
|
|
113
124
|
params?: Record<string, any>;
|
|
114
125
|
};
|
|
126
|
+
description?: string;
|
|
127
|
+
recommendations?: string[];
|
|
115
128
|
}
|
|
116
129
|
export type RuleConfigSchema = JSONSchemaType<RuleConfig>;
|
|
117
130
|
export interface OpenAIAnalysisParams {
|
|
@@ -130,6 +143,22 @@ export interface BasicTelemetryMetadata {
|
|
|
130
143
|
errorMessage?: string;
|
|
131
144
|
options?: any;
|
|
132
145
|
errorStack?: string;
|
|
146
|
+
ruleName?: string;
|
|
147
|
+
conditionDetails?: {
|
|
148
|
+
fact: string;
|
|
149
|
+
operator: string;
|
|
150
|
+
value: any;
|
|
151
|
+
params?: any;
|
|
152
|
+
};
|
|
153
|
+
allConditions?: Array<{
|
|
154
|
+
fact: string;
|
|
155
|
+
operator: string;
|
|
156
|
+
value: any;
|
|
157
|
+
params?: any;
|
|
158
|
+
path?: string;
|
|
159
|
+
priority?: number;
|
|
160
|
+
}>;
|
|
161
|
+
conditionType?: 'all' | 'any' | 'unknown';
|
|
133
162
|
}
|
|
134
163
|
export interface ExemptionTelemetryMetadata {
|
|
135
164
|
repoUrl: string;
|
|
@@ -187,6 +187,12 @@ const ruleSchema = {
|
|
|
187
187
|
required: ["action"],
|
|
188
188
|
nullable: true,
|
|
189
189
|
},
|
|
190
|
+
description: { type: "string", nullable: true },
|
|
191
|
+
recommendations: {
|
|
192
|
+
type: "array",
|
|
193
|
+
items: { type: "string" },
|
|
194
|
+
nullable: true
|
|
195
|
+
}
|
|
190
196
|
},
|
|
191
197
|
required: ["name", "conditions", "event"],
|
|
192
198
|
};
|
package/dist/utils/logger.d.ts
CHANGED
|
@@ -4,6 +4,8 @@ export declare function generateLogPrefix(): string;
|
|
|
4
4
|
export declare function resetLogPrefix(): void;
|
|
5
5
|
export declare function setLogPrefix(prefix: string): void;
|
|
6
6
|
export declare function getLogPrefix(): string;
|
|
7
|
+
export declare function appendLogPrefix(suffix: string): void;
|
|
8
|
+
export declare function withLogPrefix<T>(prefix: string, fn: () => T): T;
|
|
7
9
|
export declare const logger: pino.Logger;
|
|
8
10
|
export declare function resetLogger(): void;
|
|
9
11
|
export declare function setLogLevel(level: string): void;
|