x-fidelity 3.19.1 → 3.20.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 CHANGED
@@ -1,3 +1,31 @@
1
+ # [3.20.0](https://github.com/zotoio/x-fidelity/compare/v3.19.1...v3.20.0) (2025-04-01)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * add error handling and await for markdown report generation ([c199ffe](https://github.com/zotoio/x-fidelity/commit/c199ffe7bdc2de689baf939441e0819f7637b2ea))
7
+ * add missing imports and convert fs operations to async ([a9185b0](https://github.com/zotoio/x-fidelity/commit/a9185b0da399ba3b5500a6a5c27415b41f220061))
8
+ * correct line number property reference in sensitive data report ([53c3da2](https://github.com/zotoio/x-fidelity/commit/53c3da25f4d47b008d3159de03c39fb54052731a))
9
+ * update execution time precision to 4 decimal places and round totals ([04ef987](https://github.com/zotoio/x-fidelity/commit/04ef9878c43cef8184f6cb26f0ec1bab1363dc5e))
10
+ * update getDependencyVersionFacts to use correct argument ([890f27c](https://github.com/zotoio/x-fidelity/commit/890f27c8960186ae4cbeed0589b92833cfdacb27))
11
+ * update property names for sensitive data table generation ([1e1bfaf](https://github.com/zotoio/x-fidelity/commit/1e1bfafc2753aca5f64819a1c82ec5e8c9dbf222))
12
+ * use correct property name for sensitive data match pattern ([49b3914](https://github.com/zotoio/x-fidelity/commit/49b3914ee5bfd744eb06fe62ee765622ffaab8f2))
13
+
14
+
15
+ ### Features
16
+
17
+ * add average execution time per fact in pie chart labels ([4cdf959](https://github.com/zotoio/x-fidelity/commit/4cdf95916389b5b4181a502b7edcbb1ce3f9e958))
18
+ * add debug logging for empty report diagnosis ([ef55d2c](https://github.com/zotoio/x-fidelity/commit/ef55d2ca9570f30f0ae5f9d1c12c924bbcd5caa9))
19
+ * add fact execution metrics tracking and reporting ([dc3c541](https://github.com/zotoio/x-fidelity/commit/dc3c541370f93225280b713c8017306dd2f3c4d8))
20
+ * add getFormattedDate utility function for date formatting ([3586378](https://github.com/zotoio/x-fidelity/commit/3586378394f646102e80ca1d22d4f691fdb66a1a))
21
+ * add line number anchors to report table URLs ([a8723ce](https://github.com/zotoio/x-fidelity/commit/a8723cefc2250c096c8af5fd9cd1fc66649fce73))
22
+ * add line number anchors to sensitive data issue links ([f8488ea](https://github.com/zotoio/x-fidelity/commit/f8488ea01a12edb579b6cf370ea16cf6ab139cb5))
23
+ * add metrics tracking for dynamic fact executions ([757853f](https://github.com/zotoio/x-fidelity/commit/757853f5a3241e72d88ff57165b99e05334ab01d))
24
+ * add minimum version requirements to outdatedFramework rule ([3412333](https://github.com/zotoio/x-fidelity/commit/3412333dd2110e0cf0b50664407736528e531117))
25
+ * add repo name and date to report title ([f00855e](https://github.com/zotoio/x-fidelity/commit/f00855e4b5c2b400baaa4e8ee769cca8553d1ace))
26
+ * add report generation with JSON and Markdown output ([0b735e4](https://github.com/zotoio/x-fidelity/commit/0b735e4a86cd043c57f1e01160264641b151247d))
27
+ * add report generator for X-Fidelity analysis results ([e58d56d](https://github.com/zotoio/x-fidelity/commit/e58d56d510aadbd417c3950372680f704389788f))
28
+
1
29
  ## [3.19.1](https://github.com/zotoio/x-fidelity/compare/v3.19.0...v3.19.1) (2025-03-25)
2
30
 
3
31
 
@@ -8,13 +8,21 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
8
8
  step((generator = generator.apply(thisArg, _arguments || [])).next());
9
9
  });
10
10
  };
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
11
14
  Object.defineProperty(exports, "__esModule", { value: true });
12
15
  exports.analyzeCodebase = analyzeCodebase;
16
+ const promises_1 = __importDefault(require("fs/promises"));
17
+ const path_1 = __importDefault(require("path"));
13
18
  const logger_1 = require("../../utils/logger");
19
+ const utils_1 = require("../../utils/utils");
14
20
  const configManager_1 = require("../configManager");
21
+ const reportGenerator_1 = require("../../notifications/reportGenerator");
15
22
  const package_json_1 = require("../../../package.json");
16
23
  const openaiUtils_1 = require("../../utils/openaiUtils");
17
24
  const telemetry_1 = require("../../utils/telemetry");
25
+ const factMetricsTracker_1 = require("../../utils/factMetricsTracker");
18
26
  const repoFilesystemFacts_1 = require("../../facts/repoFilesystemFacts");
19
27
  const repoXFIConfigLoader_1 = require("../../utils/repoXFIConfigLoader");
20
28
  const repoDependencyFacts_1 = require("../../facts/repoDependencyFacts");
@@ -22,13 +30,15 @@ const openaiAnalysisFacts_1 = require("../../facts/openaiAnalysisFacts");
22
30
  const telemetryCollector_1 = require("./telemetryCollector");
23
31
  const engineSetup_1 = require("./engineSetup");
24
32
  const engineRunner_1 = require("./engineRunner");
25
- const utils_1 = require("../../utils/utils");
33
+ const utils_2 = require("../../utils/utils");
26
34
  const jsonSchemas_1 = require("../../utils/jsonSchemas");
27
35
  const pluginRegistry_1 = require("../pluginRegistry");
28
36
  const cli_1 = require("../cli");
29
37
  function analyzeCodebase(params) {
30
38
  return __awaiter(this, void 0, void 0, function* () {
31
39
  const { repoPath, archetype = 'node-fullstack', configServer = '', localConfigPath = '', executionLogPrefix = '' } = params;
40
+ // Reset metrics at start of analysis
41
+ factMetricsTracker_1.factMetricsTracker.reset();
32
42
  logger_1.logger.info(`STARTING..`);
33
43
  const telemetryData = yield (0, telemetryCollector_1.collectTelemetryData)({ repoPath, configServer });
34
44
  const repoUrl = telemetryData.repoUrl;
@@ -125,12 +135,20 @@ function analyzeCodebase(params) {
125
135
  }
126
136
  if ((0, openaiUtils_1.isOpenAIEnabled)() && archetypeConfig.facts.includes('openaiAnalysisFacts')) {
127
137
  logger_1.logger.info(`adding additional openai facts to engine..`);
128
- engine.addFact('openaiAnalysis', openaiAnalysisFacts_1.openaiAnalysis);
138
+ engine.addFact('openaiAnalysis', (params, almanac) => __awaiter(this, void 0, void 0, function* () {
139
+ return factMetricsTracker_1.factMetricsTracker.trackFactExecution('openaiAnalysis', () => (0, openaiAnalysisFacts_1.openaiAnalysis)(params, almanac));
140
+ }));
141
+ // Static data doesn't need execution tracking
129
142
  engine.addFact('openaiSystemPrompt', openaiSystemPrompt);
130
143
  }
131
144
  // add functions for dependency and file analysis
132
- engine.addFact('repoDependencyAnalysis', repoDependencyFacts_1.repoDependencyAnalysis);
133
- engine.addFact('repoFileAnalysis', repoFilesystemFacts_1.repoFileAnalysis);
145
+ engine.addFact('repoDependencyAnalysis', (params, almanac) => __awaiter(this, void 0, void 0, function* () {
146
+ return factMetricsTracker_1.factMetricsTracker.trackFactExecution('repoDependencyAnalysis', () => (0, repoDependencyFacts_1.repoDependencyAnalysis)(params, almanac));
147
+ }));
148
+ engine.addFact('repoFileAnalysis', (params, almanac) => __awaiter(this, void 0, void 0, function* () {
149
+ return factMetricsTracker_1.factMetricsTracker.trackFactExecution('repoFileAnalysis', () => (0, repoFilesystemFacts_1.repoFileAnalysis)(params, almanac));
150
+ }));
151
+ // Static data doesn't need execution tracking
134
152
  engine.addFact('globalFileMetadata', fileData, { priority: 50 });
135
153
  logger_1.logger.trace(fileData, 'Added globalFileData as fact');
136
154
  // add xfiConfig as a fact
@@ -146,12 +164,12 @@ function analyzeCodebase(params) {
146
164
  });
147
165
  const finishMsg = `\n==========================\nCHECKS COMPLETED..\n==========================`;
148
166
  logger_1.logger.info(finishMsg);
149
- const totalFailureCount = (0, utils_1.countRuleFailures)(failures);
167
+ const totalFailureCount = (0, utils_2.countRuleFailures)(failures);
150
168
  logger_1.logger.info(`${fileData.length - 1} files analyzed. ${totalFailureCount} rule failures.`);
151
- const fatalityCount = (0, utils_1.countRuleFailures)(failures, 'fatality');
152
- const warningCount = (0, utils_1.countRuleFailures)(failures, 'warning');
153
- const exemptCount = (0, utils_1.countRuleFailures)(failures, 'exempt');
154
- const errorCount = (0, utils_1.countRuleFailures)(failures, 'error');
169
+ const fatalityCount = (0, utils_2.countRuleFailures)(failures, 'fatality');
170
+ const warningCount = (0, utils_2.countRuleFailures)(failures, 'warning');
171
+ const exemptCount = (0, utils_2.countRuleFailures)(failures, 'exempt');
172
+ const errorCount = (0, utils_2.countRuleFailures)(failures, 'error');
155
173
  const finishTime = new Date().getTime();
156
174
  const memoryUsage = process.memoryUsage();
157
175
  logger_1.logger.info('Assemblying result metadata..');
@@ -161,6 +179,7 @@ function analyzeCodebase(params) {
161
179
  telemetryData,
162
180
  memoryUsage,
163
181
  repoXFIConfig: repoXFIConfig,
182
+ factMetrics: factMetricsTracker_1.factMetricsTracker.getMetrics(),
164
183
  issueDetails: failures,
165
184
  startTime: telemetryData.startTime,
166
185
  finishTime: finishTime,
@@ -177,6 +196,25 @@ function analyzeCodebase(params) {
177
196
  xfiVersion: package_json_1.version
178
197
  }
179
198
  };
199
+ // Generate reports
200
+ try {
201
+ // Save JSON report
202
+ const jsonReportPath = path_1.default.join(process.cwd(), `xfi-report-${(0, utils_1.getFormattedDate)()}.json`);
203
+ yield promises_1.default.writeFile(jsonReportPath, JSON.stringify(resultMetadata, null, 2));
204
+ logger_1.logger.info(`JSON report saved to: ${jsonReportPath}`);
205
+ // Generate and save markdown report
206
+ const generator = new reportGenerator_1.XFiReportGenerator(resultMetadata);
207
+ const markdownReportPath = path_1.default.join(process.cwd(), `xfi-report-${(0, utils_1.getFormattedDate)()}.md`);
208
+ try {
209
+ yield generator.saveReportToFile(markdownReportPath);
210
+ }
211
+ catch (error) {
212
+ logger_1.logger.error(`Failed to generate markdown report: ${error instanceof Error ? error.message : 'Unknown error'}`);
213
+ }
214
+ }
215
+ catch (error) {
216
+ logger_1.logger.error('Failed to save analysis reports - continuing with execution');
217
+ }
180
218
  // Send telemetry for analysis end
181
219
  yield (0, telemetry_1.sendTelemetry)({
182
220
  eventType: 'analysisResults',
@@ -9,9 +9,15 @@
9
9
  "value": "REPO_GLOBAL_CHECK"
10
10
  },
11
11
  {
12
- "fact": "repoDependencyAnalysis",
12
+ "fact": "repoDependencyAnalysis",
13
13
  "params": {
14
- "resultFact": "repoDependencyResults"
14
+ "resultFact": "repoDependencyResults",
15
+ "minimumDependencyVersions": {
16
+ "typescript": "5.0.0",
17
+ "node": "18.0.0",
18
+ "react": "18.2.0",
19
+ "next": "13.0.0"
20
+ }
15
21
  },
16
22
  "operator": "outdatedFramework",
17
23
  "value": true
@@ -21,7 +27,7 @@
21
27
  "event": {
22
28
  "type": "fatality",
23
29
  "params": {
24
- "message": "some core framework dependencies have expired!",
30
+ "message": "Core framework dependencies do not meet minimum version requirements! Please update your dependencies to the required versions.",
25
31
  "details": {
26
32
  "fact": "repoDependencyResults"
27
33
  }
@@ -29,7 +35,7 @@
29
35
  },
30
36
  "errorBehavior": "fatal",
31
37
  "onError": {
32
- "action": "sendNotification",
38
+ "action": "sendNotification",
33
39
  "params": {
34
40
  "channel": "security-alerts",
35
41
  "priority": "high"
@@ -17,24 +17,39 @@ const globalFileAnalysisFacts_1 = require("./globalFileAnalysisFacts");
17
17
  const pluginRegistry_1 = require("../core/pluginRegistry");
18
18
  const openaiUtils_1 = require("../utils/openaiUtils");
19
19
  const logger_1 = require("../utils/logger");
20
+ const factMetricsTracker_1 = require("../utils/factMetricsTracker");
20
21
  function loadFacts(factNames) {
21
22
  return __awaiter(this, void 0, void 0, function* () {
22
23
  // Get the latest facts including plugins
23
24
  const allAvailableFacts = Object.assign({ repoFilesystemFacts: {
24
25
  name: 'fileData',
25
- fn: repoFilesystemFacts_1.collectRepoFileData,
26
+ fn: (params, almanac) => __awaiter(this, void 0, void 0, function* () {
27
+ return factMetricsTracker_1.factMetricsTracker.trackFactExecution('fileData', () => (0, repoFilesystemFacts_1.collectRepoFileData)(params, almanac));
28
+ }),
26
29
  priority: 100
27
30
  }, repoDependencyFacts: {
28
31
  name: 'dependencyData',
29
- fn: repoDependencyFacts_1.getDependencyVersionFacts
32
+ fn: (params, almanac) => __awaiter(this, void 0, void 0, function* () {
33
+ const archetypeConfig = yield almanac.factValue('archetypeConfig');
34
+ return factMetricsTracker_1.factMetricsTracker.trackFactExecution('dependencyData', () => (0, repoDependencyFacts_1.getDependencyVersionFacts)(archetypeConfig));
35
+ })
30
36
  }, openaiAnalysisFacts: {
31
37
  name: 'openaiAnalysis',
32
- fn: openaiAnalysisFacts_1.openaiAnalysis
38
+ fn: (params, almanac) => __awaiter(this, void 0, void 0, function* () {
39
+ return factMetricsTracker_1.factMetricsTracker.trackFactExecution('openaiAnalysis', () => (0, openaiAnalysisFacts_1.openaiAnalysis)(params, almanac));
40
+ })
33
41
  }, globalFileAnalysisFacts: {
34
42
  name: 'globalFileAnalysis',
35
- fn: globalFileAnalysisFacts_1.globalFileAnalysis.fn,
36
- priority: 50 // Set priority to run after globalFileMetadata
37
- } }, Object.fromEntries(pluginRegistry_1.pluginRegistry.getPluginFacts().map(fact => [fact.name, fact])));
43
+ fn: (params, almanac) => __awaiter(this, void 0, void 0, function* () {
44
+ return factMetricsTracker_1.factMetricsTracker.trackFactExecution('globalFileAnalysis', () => globalFileAnalysisFacts_1.globalFileAnalysis.fn(params, almanac));
45
+ }),
46
+ priority: 50
47
+ } }, Object.fromEntries(pluginRegistry_1.pluginRegistry.getPluginFacts().map(fact => [
48
+ fact.name,
49
+ Object.assign(Object.assign({}, fact), { fn: (params, almanac) => __awaiter(this, void 0, void 0, function* () {
50
+ return factMetricsTracker_1.factMetricsTracker.trackFactExecution(fact.name, () => fact.fn(params, almanac));
51
+ }) })
52
+ ])));
38
53
  logger_1.logger.info(`Loading facts: ${factNames.join(',')}`);
39
54
  const pluginFacts = pluginRegistry_1.pluginRegistry.getPluginFacts();
40
55
  logger_1.logger.info(`Found ${pluginFacts.length} plugin facts available`);
@@ -272,16 +272,24 @@ function repoDependencyAnalysis(params, almanac) {
272
272
  const analysis = [];
273
273
  const dependencyData = yield almanac.factValue('dependencyData');
274
274
  const safeDependencyData = (0, utils_1.safeClone)(dependencyData);
275
- safeDependencyData.installedDependencyVersions.forEach((versionData) => {
275
+ // Use rule-provided versions if they exist, otherwise use the ones from dependencyData
276
+ const versionsToCheck = (params === null || params === void 0 ? void 0 : params.minimumDependencyVersions) ?
277
+ safeDependencyData.installedDependencyVersions.filter((versionData) => params.minimumDependencyVersions[versionData.dep]) :
278
+ safeDependencyData.installedDependencyVersions;
279
+ versionsToCheck.forEach((versionData) => {
280
+ var _a;
276
281
  logger_1.logger.debug(`outdatedFramework: checking ${versionData.dep}`);
277
282
  try {
278
283
  // Check if the installed version satisfies the required version, supporting both ranges and specific versions
279
- const isValid = semverValid(versionData.ver, versionData.min);
284
+ // Get required version from rule params if it exists, otherwise from archetype config
285
+ const requiredVersion = ((_a = params === null || params === void 0 ? void 0 : params.minimumDependencyVersions) === null || _a === void 0 ? void 0 : _a[versionData.dep]) || versionData.min;
286
+ // Check if the installed version satisfies the required version
287
+ const isValid = semverValid(versionData.ver, requiredVersion);
280
288
  if (!isValid && semver.valid(versionData.ver)) {
281
289
  const dependencyFailure = {
282
290
  'dependency': versionData.dep,
283
291
  'currentVersion': versionData.ver,
284
- 'requiredVersion': versionData.min
292
+ 'requiredVersion': requiredVersion
285
293
  };
286
294
  logger_1.logger.error(`dependencyFailure: ${(0, utils_1.safeStringify)(dependencyFailure)}`);
287
295
  analysis.push(dependencyFailure);
@@ -0,0 +1,25 @@
1
+ import { ResultMetadata } from '../types/typeDefs';
2
+ export declare class XFiReportGenerator {
3
+ private data;
4
+ repoName: string;
5
+ private githubBaseUrl;
6
+ private githubHostname;
7
+ constructor(jsonData: ResultMetadata);
8
+ private getFileName;
9
+ private getGithubUrl;
10
+ private countIssuesByFile;
11
+ private countIssuesByRuleType;
12
+ private calculateSuccessRate;
13
+ private generateExecutiveSummary;
14
+ private generateFileStatusChart;
15
+ private generateTopRuleFailuresChart;
16
+ private generateFileIssuesChart;
17
+ private generateFactMetricsChart;
18
+ private generateTopIssuesSection;
19
+ private generateFunctionComplexitySection;
20
+ private generateDependencyIssuesSection;
21
+ private generateSensitiveDataSection;
22
+ private generateGlobalIssuesSection;
23
+ generateReport(): string;
24
+ saveReportToFile(outputPath: string): Promise<void>;
25
+ }