x-fidelity 2.2.0 → 2.4.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.
Files changed (68) hide show
  1. package/CHANGELOG.md +76 -0
  2. package/dist/core/engine/analyzer.test.js +17 -9
  3. package/dist/core/engine/engineRunner.test.js +1 -0
  4. package/dist/core/engine/engineSetup.js +8 -9
  5. package/dist/core/engine/engineSetup.test.js +106 -77
  6. package/dist/facts/openaiAnalysisFacts.test.js +1 -0
  7. package/dist/index.js +45 -34
  8. package/dist/index.test.js +160 -0
  9. package/dist/rules/index.js +2 -2
  10. package/dist/rules/index.test.js +6 -6
  11. package/dist/server/cacheManager.js +66 -0
  12. package/dist/server/configServer.js +50 -153
  13. package/dist/server/configServer.test.js +40 -29
  14. package/dist/server/middleware/checkSharedSecret.js +14 -0
  15. package/dist/server/middleware/validateGithubWebhook.js +23 -0
  16. package/dist/server/middleware/validateTelemetryData.js +10 -0
  17. package/dist/server/middleware/validateUrlInput.js +10 -0
  18. package/dist/server/routes/archetypeRoute.js +50 -0
  19. package/dist/server/routes/archetypeRuleRoute.js +58 -0
  20. package/dist/server/routes/archetypeRulesRoute.js +46 -0
  21. package/dist/server/routes/clearCacheRoute.js +14 -0
  22. package/dist/server/routes/githubWebhookRoute.js +140 -0
  23. package/dist/server/routes/telemetryRoute.js +15 -0
  24. package/dist/server/routes/viewCacheRoute.js +13 -0
  25. package/dist/utils/axiosClient.js +61 -0
  26. package/dist/utils/configManager.js +36 -7
  27. package/dist/utils/configManager.test.js +10 -10
  28. package/dist/utils/inputValidation.js +12 -0
  29. package/dist/utils/inputValidation.test.js +51 -0
  30. package/dist/utils/logger.js +1 -0
  31. package/dist/utils/logger.test.js +32 -0
  32. package/dist/utils/maskSensitiveData.test.js +40 -0
  33. package/dist/utils/telemetry.js +3 -6
  34. package/dist/xfidelity +45 -34
  35. package/package.json +6 -2
  36. package/src/core/engine/analyzer.test.ts +17 -9
  37. package/src/core/engine/engineRunner.test.ts +1 -0
  38. package/src/core/engine/engineSetup.test.ts +119 -81
  39. package/src/core/engine/engineSetup.ts +11 -11
  40. package/src/facts/openaiAnalysisFacts.test.ts +1 -0
  41. package/src/index.test.ts +157 -0
  42. package/src/index.ts +55 -45
  43. package/src/rules/index.test.ts +6 -6
  44. package/src/rules/index.ts +9 -33
  45. package/src/server/cacheManager.ts +65 -0
  46. package/src/server/configServer.test.ts +43 -32
  47. package/src/server/configServer.ts +61 -156
  48. package/src/server/middleware/checkSharedSecret.ts +14 -0
  49. package/src/server/middleware/validateGithubWebhook.ts +24 -0
  50. package/src/server/middleware/validateTelemetryData.ts +9 -0
  51. package/src/server/middleware/validateUrlInput.ts +9 -0
  52. package/src/server/routes/archetypeRoute.ts +40 -0
  53. package/src/server/routes/archetypeRuleRoute.ts +46 -0
  54. package/src/server/routes/archetypeRulesRoute.ts +35 -0
  55. package/src/server/routes/clearCacheRoute.ts +13 -0
  56. package/src/server/routes/githubWebhookRoute.ts +131 -0
  57. package/src/server/routes/telemetryRoute.ts +14 -0
  58. package/src/server/routes/viewCacheRoute.ts +15 -0
  59. package/src/types/typeDefs.ts +20 -0
  60. package/src/utils/axiosClient.ts +40 -0
  61. package/src/utils/configManager.test.ts +10 -10
  62. package/src/utils/configManager.ts +40 -8
  63. package/src/utils/inputValidation.test.ts +58 -0
  64. package/src/utils/inputValidation.ts +14 -0
  65. package/src/utils/logger.test.ts +34 -0
  66. package/src/utils/logger.ts +1 -0
  67. package/src/utils/maskSensitiveData.test.ts +45 -0
  68. package/src/utils/telemetry.ts +3 -4
package/CHANGELOG.md CHANGED
@@ -1,3 +1,79 @@
1
+ # [2.4.0](https://github.com/zotoio/x-fidelity/compare/v2.3.0...v2.4.0) (2024-08-21)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * add unit tests for all files except typeDefs ([b1d1fd9](https://github.com/zotoio/x-fidelity/commit/b1d1fd9d069d2bd54712ae0cf96a9e48ebec903e))
7
+ * **deps:** make artillery a peer dependency ([d89361f](https://github.com/zotoio/x-fidelity/commit/d89361fcc24744cfa52df6cbe52b6117b92e46d8))
8
+ * Handle errors during execution ([48a9488](https://github.com/zotoio/x-fidelity/commit/48a94882eff7db64e0f996a6b55d3dde9eca080b))
9
+ * Improve error handling and execution flow in index.ts ([0b8ca73](https://github.com/zotoio/x-fidelity/commit/0b8ca73d3cc4b0b498ab9a8cf93d9d67965243af))
10
+ * Increase timeout for test case to prevent "force exited" issue ([ab3d0b3](https://github.com/zotoio/x-fidelity/commit/ab3d0b30696f6056df8a386b5eb366f4c545dbfa))
11
+ * **logging:** silence in logs and increase unit test coverage ([5017a12](https://github.com/zotoio/x-fidelity/commit/5017a1216c5dedc443d40787aa70ea40814de49c))
12
+ * Mock axiosClient instead of axios in rules/index.test.ts ([40e7410](https://github.com/zotoio/x-fidelity/commit/40e74108b45813244fa3810649d1b681a1e85af5))
13
+ * Pass executionLogPrefix to startServer and analyzeCodebase ([99a6692](https://github.com/zotoio/x-fidelity/commit/99a669232d77a8bfaeca22590efcdc9fd9ecb80a))
14
+ * Prevent unnecessary process exit in test environment ([aab777f](https://github.com/zotoio/x-fidelity/commit/aab777f76f4ab3c98322c93250bbfa5d35f2812a))
15
+ * Remove setTimeout and directly call process.exit(1) in error handling function ([8a7587e](https://github.com/zotoio/x-fidelity/commit/8a7587ea169ff8313d187f4ea71487eb8273623f))
16
+ * resolve TypeScript errors in telemetry utility ([3c68aa1](https://github.com/zotoio/x-fidelity/commit/3c68aa1d4d3b2594dd05fa120233a56ff06d5206))
17
+ * Update import and call of main function in index.test.ts ([70e11b1](https://github.com/zotoio/x-fidelity/commit/70e11b18dfbf272035e96a004b6f7b889644c229))
18
+ * Update import and export in index.test.ts and index.ts ([b15203d](https://github.com/zotoio/x-fidelity/commit/b15203d8aa3b37b211c5a57edd8dd0ea58c9d93f))
19
+ * Update index.test.ts ([b3d1799](https://github.com/zotoio/x-fidelity/commit/b3d1799153de1f502f5b897d268c30999189f1ca))
20
+ * Update process.exit usage in src/index.ts ([ac398c3](https://github.com/zotoio/x-fidelity/commit/ac398c31cd944e2c062db5d7b57254f026a89f8c))
21
+ * Update startServer function call and process exit handling ([7be6c28](https://github.com/zotoio/x-fidelity/commit/7be6c280dda46f49a8c1c45149b93fcadd261ccb))
22
+ * update test expectations ([d24279e](https://github.com/zotoio/x-fidelity/commit/d24279e8520f882260299190b9562831b7c79528))
23
+ * Update test expectations to match actual error messages ([454aea6](https://github.com/zotoio/x-fidelity/commit/454aea6a8cf38cfdd1b9ed31288c119f7f37dbbf))
24
+ * update type of `code` parameter in `mockImplementation` function ([f7aab15](https://github.com/zotoio/x-fidelity/commit/f7aab15c67e155da2fedb29a704633629561964a))
25
+
26
+
27
+ ### Features
28
+
29
+ * centralise the axios client usage in one file and implement exponential backoff ([9a6d000](https://github.com/zotoio/x-fidelity/commit/9a6d00051c92cbe498c429503356f445d64d1685))
30
+
31
+ # [2.3.0](https://github.com/zotoio/x-fidelity/compare/v2.2.0...v2.3.0) (2024-08-20)
32
+
33
+
34
+ ### Bug Fixes
35
+
36
+ * Add clearCache import and ConfigManager import to configServer.ts ([4212096](https://github.com/zotoio/x-fidelity/commit/4212096ea2459e4c55a7c6b396755d72f440ecb6))
37
+ * address SSRF risks in `githubWebhookRoute` ([9b6d64a](https://github.com/zotoio/x-fidelity/commit/9b6d64acf7e49604815b606292890a4b50a11ab8))
38
+ * Clear rule list cache correctly ([3cb17c7](https://github.com/zotoio/x-fidelity/commit/3cb17c7e54e9c8be19b03405a3b13eeaf21bf16c))
39
+ * import missing middleware functions ([fd00a4f](https://github.com/zotoio/x-fidelity/commit/fd00a4f9b4b2dac3ef41782f517186a07ce1c53c))
40
+ * Improve security and mitigate potential SSRF risks in configManager and configServer ([f87ccab](https://github.com/zotoio/x-fidelity/commit/f87ccab24c465d374ef191973d4f3f500ebcca00))
41
+ * move the github webhook route and related update code to separate route file ([4deb57e](https://github.com/zotoio/x-fidelity/commit/4deb57e57f4b95cda757aaa84e8c3f89485e24cd))
42
+ * Properly handle asynchronous operations in configServer.test.ts ([2de6174](https://github.com/zotoio/x-fidelity/commit/2de6174330e518a49c0530455f9bfd4118f648d2))
43
+ * Refactor ConfigManager class ([7f1c2fe](https://github.com/zotoio/x-fidelity/commit/7f1c2fe2e895684145c843615876e3118be095a9))
44
+ * Update configServer to use RuleConfig from ConfigManager ([100c0b3](https://github.com/zotoio/x-fidelity/commit/100c0b301423ca111aa8e452073d3c190a317fbc))
45
+ * Update engineSetup.test.ts to use ConfigManager and setLogPrefix ([c36c64d](https://github.com/zotoio/x-fidelity/commit/c36c64de689a952a500a360dffe8e57605d2dbac))
46
+ * Update error message and fix warning detection test ([9f37bf5](https://github.com/zotoio/x-fidelity/commit/9f37bf502407404ddad355a5ac7d4d8710153b80))
47
+ * Update mock configuration in engineSetup.test.ts ([fd3d7db](https://github.com/zotoio/x-fidelity/commit/fd3d7dbaab3ae51b5c6cbc6d0970f3bf2b2db32c))
48
+ * Update mockParams object to include archetypeConfig property ([b0c9628](https://github.com/zotoio/x-fidelity/commit/b0c9628050643fbc508a087f2387144961a74264))
49
+ * Update rule schema definition ([ed5d87a](https://github.com/zotoio/x-fidelity/commit/ed5d87a3579cd2f6d7eb414207a3cfb9bd571d70))
50
+ * Update ruleSchema to match RuleProperties type ([0f09992](https://github.com/zotoio/x-fidelity/commit/0f099925b14d32d044e5c8fb3583cfec4bb39ae7))
51
+ * Update test configuration for analyzer ([f0b139d](https://github.com/zotoio/x-fidelity/commit/f0b139d7b208553d131d682e19a7b0ccebd5febf))
52
+ * Update test expectation for loadFacts ([ee04ee0](https://github.com/zotoio/x-fidelity/commit/ee04ee0c38883e5c073e6360fe6febed45e42cc6))
53
+ * Update test expectations for loadFacts ([4c57421](https://github.com/zotoio/x-fidelity/commit/4c57421de624622404e6232f450057f200781a2b))
54
+ * Validate and sanitize archetype input in configManager ([3a92119](https://github.com/zotoio/x-fidelity/commit/3a921195beb1ac0da0421aafb443adbf77d83eb2))
55
+
56
+
57
+ ### Features
58
+
59
+ * add clearcache route ([89e9633](https://github.com/zotoio/x-fidelity/commit/89e96333ae1bb36b4673c09ddc8b69f6b1ed9740))
60
+ * Add file watcher for local config path ([43da2f3](https://github.com/zotoio/x-fidelity/commit/43da2f3b84f2c852426813068487702c4745d549))
61
+ * Add GitHub webhook route ([cab04ae](https://github.com/zotoio/x-fidelity/commit/cab04ae5c7fbba954e9b85c8182ec3595215a8f9))
62
+ * Add GitHub webhook route to handle archetype or rule config updates ([5af3d63](https://github.com/zotoio/x-fidelity/commit/5af3d63d4d9ef6f3f48a5be4001d3f28f1522d6c))
63
+ * Add high-value unit tests for utility functions ([f90d5e2](https://github.com/zotoio/x-fidelity/commit/f90d5e218cc8c5743515744fc9584801c3003398))
64
+ * Add input validation for URL parameters and telemetry data ([77bdfaa](https://github.com/zotoio/x-fidelity/commit/77bdfaa4e35c34e62c341fca6f87db46726db993))
65
+ * Add support for additional properties in rule schema ([98826e5](https://github.com/zotoio/x-fidelity/commit/98826e5edad1a443d6299a1ad3b970517c384783))
66
+ * add viewcache route ([1fa105c](https://github.com/zotoio/x-fidelity/commit/1fa105c6c29ef05b8eb5312c638785dd5d56af07))
67
+ * Create comprehensive unit test file for engineSetup.ts ([50556ce](https://github.com/zotoio/x-fidelity/commit/50556ce257cb579ef856b74a012b986b81490f2b))
68
+ * Implement GitHub webhook to update local config ([351fbf0](https://github.com/zotoio/x-fidelity/commit/351fbf022100f8e9acc3734b291afdac9057e386))
69
+ * Implement server routes and middleware ([3de18a6](https://github.com/zotoio/x-fidelity/commit/3de18a6cab08a26ecd51f7dc9148da5fee93aeba))
70
+ * load all RuleConfig for a given archetype into the ExecutionConfig ([a122c52](https://github.com/zotoio/x-fidelity/commit/a122c52986adeadeec9085b5c94bfd5e142e80d8))
71
+ * move the configServer features related to caching into a new file ([95a9908](https://github.com/zotoio/x-fidelity/commit/95a9908c97878b062337e100b37ae1d4919b53e5))
72
+ * **rules:** github hook to refresh config ([2485fbe](https://github.com/zotoio/x-fidelity/commit/2485fbe36a9f5102fe815209e0961f604b0e6f30))
73
+ * **rules:** optimise loading of rules, and filesystem watcher for server ([6e6c5ac](https://github.com/zotoio/x-fidelity/commit/6e6c5ac0a784ab76b9d3ec476de580225932b443))
74
+ * update configserver to use new route files ([eda8240](https://github.com/zotoio/x-fidelity/commit/eda8240a09073612f060d65494d4973b1b170c94))
75
+ * Update rule schema to improve flexibility and compatibility ([989bdf6](https://github.com/zotoio/x-fidelity/commit/989bdf64ead3148a366c046f6c0e883d6b1b1109))
76
+
1
77
  # [2.2.0](https://github.com/zotoio/x-fidelity/compare/v2.1.0...v2.2.0) (2024-08-20)
2
78
 
3
79
 
@@ -17,7 +17,6 @@ const openaiAnalysisFacts_1 = require("../../facts/openaiAnalysisFacts");
17
17
  const rules_1 = require("../../rules");
18
18
  const operators_1 = require("../../operators");
19
19
  const facts_1 = require("../../facts");
20
- const archetypes_1 = require("../../archetypes");
21
20
  const configManager_1 = require("../../utils/configManager");
22
21
  const telemetry_1 = require("../../utils/telemetry");
23
22
  const openaiUtils_1 = require("../../utils/openaiUtils");
@@ -64,8 +63,19 @@ describe('analyzeCodebase', () => {
64
63
  beforeEach(() => {
65
64
  jest.clearAllMocks();
66
65
  configManager_1.ConfigManager.getConfig.mockResolvedValue({
67
- archetype: archetypes_1.archetypes['node-fullstack'],
68
- rules: [],
66
+ archetype: {
67
+ name: 'test-archetype',
68
+ rules: ['rule1'],
69
+ operators: ['operator1'],
70
+ facts: ['fact1'],
71
+ config: {
72
+ minimumDependencyVersions: {},
73
+ standardStructure: {},
74
+ blacklistPatterns: [],
75
+ whitelistPatterns: []
76
+ }
77
+ },
78
+ rules: [{ name: 'rule1', conditions: { all: [] }, event: { type: 'test', params: {} } }],
69
79
  cliOptions: {}
70
80
  });
71
81
  // eslint-disable-next-line @typescript-eslint/no-empty-function
@@ -104,9 +114,8 @@ describe('analyzeCodebase', () => {
104
114
  });
105
115
  expect(repoFilesystemFacts_1.collectRepoFileData).toHaveBeenCalledWith('mockRepoPath', expect.any(Object));
106
116
  expect(repoDependencyFacts_1.getDependencyVersionFacts).toHaveBeenCalledWith(expect.any(Object));
107
- expect(rules_1.loadRules).toHaveBeenCalledWith({ archetype: 'node-fullstack', ruleNames: ['mockRule'], configServer: '', logPrefix: '', localConfigPath: '' });
108
- expect(operators_1.loadOperators).toHaveBeenCalledWith(['mockOperator']);
109
- expect(facts_1.loadFacts).toHaveBeenCalledWith(['mockFact']);
117
+ expect(operators_1.loadOperators).toHaveBeenCalledWith(['operator1']);
118
+ expect(facts_1.loadFacts).toHaveBeenCalledWith(['fact1']);
110
119
  expect(engineRunMock).toHaveBeenCalledTimes(mockFileData.length);
111
120
  expect(results).toEqual({
112
121
  XFI_RESULT: expect.objectContaining({
@@ -157,9 +166,8 @@ describe('analyzeCodebase', () => {
157
166
  });
158
167
  expect(repoFilesystemFacts_1.collectRepoFileData).toHaveBeenCalledWith('mockRepoPath', expect.any(Object));
159
168
  expect(repoDependencyFacts_1.getDependencyVersionFacts).toHaveBeenCalled();
160
- expect(rules_1.loadRules).toHaveBeenCalledWith({ archetype: 'node-fullstack', ruleNames: ['mockRule'], configServer: '', logPrefix: '', localConfigPath: '' });
161
- expect(operators_1.loadOperators).toHaveBeenCalledWith(['mockOperator']);
162
- expect(facts_1.loadFacts).toHaveBeenCalledWith(['mockFact']);
169
+ expect(operators_1.loadOperators).toHaveBeenCalledWith(['operator1']);
170
+ expect(facts_1.loadFacts).toHaveBeenCalledWith(['fact1']);
163
171
  expect(engineRunMock).toHaveBeenCalledTimes(mockFileData.length);
164
172
  expect(results).toEqual({
165
173
  XFI_RESULT: expect.objectContaining({
@@ -18,6 +18,7 @@ jest.mock('../../utils/logger', () => ({
18
18
  info: jest.fn(),
19
19
  debug: jest.fn(),
20
20
  error: jest.fn(),
21
+ warn: jest.fn(),
21
22
  },
22
23
  }));
23
24
  describe('runEngineOnFiles', () => {
@@ -14,12 +14,11 @@ const json_rules_engine_1 = require("json-rules-engine");
14
14
  const logger_1 = require("../../utils/logger");
15
15
  const operators_1 = require("../../operators");
16
16
  const facts_1 = require("../../facts");
17
- const rules_1 = require("../../rules");
18
- const cli_1 = require("../../core/cli");
19
17
  const telemetry_1 = require("../../utils/telemetry");
18
+ const configManager_1 = require("../../utils/configManager");
20
19
  function setupEngine(params) {
21
20
  return __awaiter(this, void 0, void 0, function* () {
22
- const { archetypeConfig, archetype, executionLogPrefix, localConfigPath } = params;
21
+ const { archetypeConfig, archetype, executionLogPrefix } = params;
23
22
  const engine = new json_rules_engine_1.Engine([], { replaceFactsInEventParams: true, allowUndefinedFacts: true });
24
23
  // Add operators to engine
25
24
  logger_1.logger.info(`=== loading custom operators..`);
@@ -33,21 +32,21 @@ function setupEngine(params) {
33
32
  });
34
33
  // Add rules to engine
35
34
  logger_1.logger.info(`=== loading json rules..`);
36
- const rules = yield (0, rules_1.loadRules)({ archetype, ruleNames: archetypeConfig.rules, configServer: cli_1.options.configServer, logPrefix: executionLogPrefix, localConfigPath });
37
- logger_1.logger.debug(rules);
38
- rules.forEach((rule) => {
35
+ const config = yield configManager_1.ConfigManager.getConfig({ archetype, logPrefix: executionLogPrefix });
36
+ logger_1.logger.debug(config.rules);
37
+ config.rules.forEach((rule) => {
39
38
  try {
40
39
  logger_1.logger.info(`adding rule: ${rule === null || rule === void 0 ? void 0 : rule.name}`);
41
40
  engine.addRule(rule);
42
41
  }
43
42
  catch (e) {
44
- console.error(`Error loading rule: ${rule === null || rule === void 0 ? void 0 : rule.name}`);
43
+ logger_1.logger.error(`Error loading rule: ${rule === null || rule === void 0 ? void 0 : rule.name}`);
45
44
  logger_1.logger.error(e.message);
46
45
  }
47
46
  });
48
47
  engine.on('success', (_a) => __awaiter(this, [_a], void 0, function* ({ type, params }) {
49
48
  if (type === 'warning') {
50
- logger_1.logger.warn(`warning detected: ${JSON.stringify(params)}}`);
49
+ logger_1.logger.warn(`warning detected: ${JSON.stringify(params)}`);
51
50
  yield (0, telemetry_1.sendTelemetry)({
52
51
  eventType: 'warning',
53
52
  metadata: Object.assign({ archetype, repoPath: '' }, params),
@@ -55,7 +54,7 @@ function setupEngine(params) {
55
54
  }, executionLogPrefix);
56
55
  }
57
56
  if (type === 'fatality') {
58
- logger_1.logger.error(`fatality detected: ${JSON.stringify(params)}}`);
57
+ logger_1.logger.error(`fatality detected: ${JSON.stringify(params)}`);
59
58
  yield (0, telemetry_1.sendTelemetry)({
60
59
  eventType: 'fatality',
61
60
  metadata: Object.assign({ archetype, repoPath: '' }, params),
@@ -13,127 +13,156 @@ const engineSetup_1 = require("./engineSetup");
13
13
  const json_rules_engine_1 = require("json-rules-engine");
14
14
  const operators_1 = require("../../operators");
15
15
  const facts_1 = require("../../facts");
16
- const rules_1 = require("../../rules");
17
16
  const telemetry_1 = require("../../utils/telemetry");
18
- const openaiUtils_1 = require("../../utils/openaiUtils");
19
- const cli_1 = require("../cli");
17
+ const configManager_1 = require("../../utils/configManager");
18
+ const logger_1 = require("../../utils/logger");
20
19
  jest.mock('json-rules-engine');
21
20
  jest.mock('../../operators');
22
21
  jest.mock('../../facts');
23
- jest.mock('../../rules');
24
22
  jest.mock('../../utils/telemetry');
25
- jest.mock('../../utils/openaiUtils');
26
- jest.mock('../../utils/logger', () => ({
27
- logger: {
28
- info: jest.fn(),
29
- debug: jest.fn(),
30
- error: jest.fn(),
31
- warn: jest.fn(),
32
- },
33
- }));
23
+ jest.mock('../../utils/configManager');
24
+ jest.mock('../../utils/logger');
34
25
  describe('setupEngine', () => {
35
26
  const mockArchetypeConfig = {
36
27
  name: 'test-archetype',
37
- rules: ['rule1'],
38
- operators: ['operator1'],
39
- facts: ['fact1'],
28
+ rules: ['rule1', 'rule2'],
29
+ operators: ['operator1', 'operator2'],
30
+ facts: ['fact1', 'fact2'],
40
31
  config: {
41
32
  minimumDependencyVersions: {},
42
33
  standardStructure: {},
43
34
  blacklistPatterns: [],
44
35
  whitelistPatterns: []
45
- },
36
+ }
46
37
  };
47
38
  const mockParams = {
48
39
  archetypeConfig: mockArchetypeConfig,
49
40
  archetype: 'test-archetype',
50
- configManager: {},
41
+ configManager: configManager_1.ConfigManager,
51
42
  executionLogPrefix: 'test-prefix',
52
- localConfigPath: '/test/path',
43
+ localConfigPath: '/test/path'
53
44
  };
54
45
  beforeEach(() => {
55
46
  jest.clearAllMocks();
56
- process.env.OPENAI_API_KEY = 'test-key';
57
- });
58
- it('should set up engine with correct configurations', () => __awaiter(void 0, void 0, void 0, function* () {
59
- const mockEngine = {
60
- addOperator: jest.fn(),
61
- addRule: jest.fn(),
62
- addFact: jest.fn(),
63
- on: jest.fn(),
64
- };
65
- json_rules_engine_1.Engine.mockImplementation(() => mockEngine);
66
- operators_1.loadOperators.mockResolvedValue([{ name: 'operator1', fn: jest.fn() }]);
67
- facts_1.loadFacts.mockResolvedValue([{ name: 'fact1', fn: jest.fn() }]);
68
- rules_1.loadRules.mockResolvedValue([{ name: 'rule1' }]);
69
- openaiUtils_1.isOpenAIEnabled.mockReturnValue(true);
70
- yield (0, engineSetup_1.setupEngine)(mockParams);
71
- expect(json_rules_engine_1.Engine).toHaveBeenCalled();
72
- expect(operators_1.loadOperators).toHaveBeenCalledWith(['operator1']);
73
- expect(facts_1.loadFacts).toHaveBeenCalledWith(['fact1']);
74
- expect(rules_1.loadRules).toHaveBeenCalledWith(expect.objectContaining({
75
- archetype: 'test-archetype',
76
- ruleNames: ['rule1'],
77
- }));
78
- expect(mockEngine.addOperator).toHaveBeenCalled();
79
- expect(mockEngine.addRule).toHaveBeenCalled();
80
- expect(mockEngine.addFact).toHaveBeenCalled();
81
- expect(mockEngine.on).toHaveBeenCalledWith('success', expect.any(Function));
82
- }));
83
- it('should not add OpenAI-related operators and facts when OpenAI is disabled', () => __awaiter(void 0, void 0, void 0, function* () {
84
- cli_1.options.openai = false;
85
- process.env.OPENAI_API_KEY = '';
86
- const mockEngine = {
87
- addOperator: jest.fn(),
88
- addRule: jest.fn(),
89
- addFact: jest.fn(),
90
- on: jest.fn(),
91
- };
92
- json_rules_engine_1.Engine.mockImplementation(() => mockEngine);
47
+ configManager_1.ConfigManager.getConfig.mockResolvedValue({
48
+ archetype: mockArchetypeConfig,
49
+ rules: [
50
+ { name: 'rule1', conditions: { all: [] }, event: { type: 'test', params: {} } },
51
+ { name: 'rule2', conditions: { all: [] }, event: { type: 'test', params: {} } }
52
+ ],
53
+ cliOptions: {}
54
+ });
93
55
  operators_1.loadOperators.mockResolvedValue([
94
56
  { name: 'operator1', fn: jest.fn() },
95
- { name: 'openaiOperator', fn: jest.fn() },
57
+ { name: 'operator2', fn: jest.fn() }
96
58
  ]);
97
59
  facts_1.loadFacts.mockResolvedValue([
98
60
  { name: 'fact1', fn: jest.fn() },
99
- { name: 'openaiAnalysis', fn: jest.fn() },
61
+ { name: 'fact2', fn: jest.fn() }
100
62
  ]);
101
- rules_1.loadRules.mockResolvedValue([{ name: 'rule1' }]);
102
- openaiUtils_1.isOpenAIEnabled.mockReturnValue(false);
63
+ });
64
+ it('should create and configure an engine instance', () => __awaiter(void 0, void 0, void 0, function* () {
65
+ const mockAddOperator = jest.fn();
66
+ const mockAddRule = jest.fn();
67
+ const mockAddFact = jest.fn();
68
+ const mockOn = jest.fn();
69
+ json_rules_engine_1.Engine.mockImplementation(() => ({
70
+ addOperator: mockAddOperator,
71
+ addRule: mockAddRule,
72
+ addFact: mockAddFact,
73
+ on: mockOn
74
+ }));
75
+ const engine = yield (0, engineSetup_1.setupEngine)(mockParams);
76
+ expect(json_rules_engine_1.Engine).toHaveBeenCalledWith([], { replaceFactsInEventParams: true, allowUndefinedFacts: true });
77
+ expect(operators_1.loadOperators).toHaveBeenCalledWith(['operator1', 'operator2']);
78
+ expect(facts_1.loadFacts).toHaveBeenCalledWith(['fact1', 'fact2']);
79
+ expect(configManager_1.ConfigManager.getConfig).toHaveBeenCalledWith({ archetype: 'test-archetype', logPrefix: 'test-prefix' });
80
+ expect(mockAddOperator).toHaveBeenCalledTimes(2);
81
+ expect(mockAddRule).toHaveBeenCalledTimes(2);
82
+ expect(mockAddFact).toHaveBeenCalledTimes(2);
83
+ expect(mockOn).toHaveBeenCalledTimes(1);
84
+ expect(engine).toBeDefined();
85
+ }));
86
+ it('should handle errors when loading rules', () => __awaiter(void 0, void 0, void 0, function* () {
87
+ const mockAddRule = jest.fn().mockImplementationOnce(() => {
88
+ throw new Error('Rule loading error');
89
+ });
90
+ json_rules_engine_1.Engine.mockImplementation(() => ({
91
+ addOperator: jest.fn(),
92
+ addRule: mockAddRule,
93
+ addFact: jest.fn(),
94
+ on: jest.fn()
95
+ }));
103
96
  yield (0, engineSetup_1.setupEngine)(mockParams);
104
- expect(mockEngine.addOperator).toHaveBeenCalledTimes(1);
105
- expect(mockEngine.addFact).toHaveBeenCalledTimes(1);
106
- expect(mockEngine.addOperator).not.toHaveBeenCalledWith('openaiOperator', expect.any(Function));
107
- expect(mockEngine.addFact).not.toHaveBeenCalledWith('openaiAnalysis', expect.any(Function));
97
+ expect(mockAddRule).toHaveBeenCalledTimes(2);
98
+ expect(logger_1.logger.error).toHaveBeenCalledWith('Rule loading error');
108
99
  }));
109
- it('should send telemetry on warning and fatality events', () => __awaiter(void 0, void 0, void 0, function* () {
110
- const mockEngine = {
100
+ it('should set up event listeners for success events', () => __awaiter(void 0, void 0, void 0, function* () {
101
+ const mockOn = jest.fn();
102
+ json_rules_engine_1.Engine.mockImplementation(() => ({
111
103
  addOperator: jest.fn(),
112
104
  addRule: jest.fn(),
113
105
  addFact: jest.fn(),
114
- on: jest.fn(),
115
- };
116
- json_rules_engine_1.Engine.mockImplementation(() => mockEngine);
117
- operators_1.loadOperators.mockResolvedValue([{ name: 'operator1', fn: jest.fn() }]);
118
- facts_1.loadFacts.mockResolvedValue([{ name: 'fact1', fn: jest.fn() }]);
119
- rules_1.loadRules.mockResolvedValue([{ name: 'rule1' }]);
106
+ on: mockOn
107
+ }));
120
108
  yield (0, engineSetup_1.setupEngine)(mockParams);
121
- const successCallback = mockEngine.on.mock.calls[0][1];
109
+ expect(mockOn).toHaveBeenCalledWith('success', expect.any(Function));
122
110
  // Test warning event
123
- yield successCallback({ type: 'warning', params: { message: 'Test warning' } });
111
+ const warningCallback = mockOn.mock.calls[0][1];
112
+ yield warningCallback({ type: 'warning', params: { message: 'Test warning' } });
113
+ expect(logger_1.logger.warn).toHaveBeenCalledWith('warning detected: {"message":"Test warning"}');
124
114
  expect(telemetry_1.sendTelemetry).toHaveBeenCalledWith(expect.objectContaining({
125
115
  eventType: 'warning',
126
116
  metadata: expect.objectContaining({
127
117
  archetype: 'test-archetype',
128
- }),
118
+ message: 'Test warning'
119
+ })
129
120
  }), 'test-prefix');
130
121
  // Test fatality event
131
- yield successCallback({ type: 'fatality', params: { message: 'Test fatality' } });
122
+ yield warningCallback({ type: 'fatality', params: { message: 'Test fatality' } });
123
+ expect(logger_1.logger.error).toHaveBeenCalledWith('fatality detected: {"message":"Test fatality"}');
132
124
  expect(telemetry_1.sendTelemetry).toHaveBeenCalledWith(expect.objectContaining({
133
125
  eventType: 'fatality',
134
126
  metadata: expect.objectContaining({
135
127
  archetype: 'test-archetype',
136
- }),
128
+ message: 'Test fatality'
129
+ })
137
130
  }), 'test-prefix');
138
131
  }));
132
+ it('should not add OpenAI-related facts when OpenAI is not enabled', () => __awaiter(void 0, void 0, void 0, function* () {
133
+ process.env.OPENAI_API_KEY = '';
134
+ const mockAddFact = jest.fn();
135
+ json_rules_engine_1.Engine.mockImplementation(() => ({
136
+ addOperator: jest.fn(),
137
+ addRule: jest.fn(),
138
+ addFact: mockAddFact,
139
+ on: jest.fn()
140
+ }));
141
+ facts_1.loadFacts.mockResolvedValue([
142
+ { name: 'fact1', fn: jest.fn() },
143
+ { name: 'openaiAnalysis', fn: jest.fn() }
144
+ ]);
145
+ yield (0, engineSetup_1.setupEngine)(mockParams);
146
+ expect(mockAddFact).toHaveBeenCalledTimes(1);
147
+ expect(mockAddFact).toHaveBeenCalledWith('fact1', expect.any(Function));
148
+ expect(mockAddFact).not.toHaveBeenCalledWith('openaiAnalysis', expect.any(Function));
149
+ }));
150
+ it('should add OpenAI-related facts when OpenAI is enabled', () => __awaiter(void 0, void 0, void 0, function* () {
151
+ process.env.OPENAI_API_KEY = 'test-key';
152
+ const mockAddFact = jest.fn();
153
+ json_rules_engine_1.Engine.mockImplementation(() => ({
154
+ addOperator: jest.fn(),
155
+ addRule: jest.fn(),
156
+ addFact: mockAddFact,
157
+ on: jest.fn()
158
+ }));
159
+ facts_1.loadFacts.mockResolvedValue([
160
+ { name: 'fact1', fn: jest.fn() },
161
+ { name: 'openaiAnalysis', fn: jest.fn() }
162
+ ]);
163
+ yield (0, engineSetup_1.setupEngine)(mockParams);
164
+ expect(mockAddFact).toHaveBeenCalledTimes(2);
165
+ expect(mockAddFact).toHaveBeenCalledWith('fact1', expect.any(Function));
166
+ expect(mockAddFact).toHaveBeenCalledWith('openaiAnalysis', expect.any(Function));
167
+ }));
139
168
  });
@@ -21,6 +21,7 @@ jest.mock('../utils/logger', () => ({
21
21
  debug: jest.fn(),
22
22
  error: jest.fn(),
23
23
  info: jest.fn(),
24
+ warn: jest.fn(),
24
25
  },
25
26
  }));
26
27
  jest.mock('openai', () => {
package/dist/index.js CHANGED
@@ -13,6 +13,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
13
13
  return (mod && mod.__esModule) ? mod : { "default": mod };
14
14
  };
15
15
  Object.defineProperty(exports, "__esModule", { value: true });
16
+ exports.main = main;
16
17
  const logger_1 = require("./utils/logger");
17
18
  const prettyjson_1 = __importDefault(require("prettyjson"));
18
19
  const cli_1 = require("./core/cli");
@@ -21,18 +22,32 @@ const configServer_1 = require("./server/configServer");
21
22
  const telemetry_1 = require("./utils/telemetry");
22
23
  const executionLogPrefix = (0, logger_1.generateLogPrefix)();
23
24
  (0, logger_1.setLogPrefix)(executionLogPrefix);
25
+ // Function to handle errors and send telemetry
26
+ const handleError = (error) => __awaiter(void 0, void 0, void 0, function* () {
27
+ yield (0, telemetry_1.sendTelemetry)({
28
+ eventType: 'execution failure',
29
+ metadata: {
30
+ archetype: cli_1.options.archetype,
31
+ repoPath: cli_1.options.dir,
32
+ options: cli_1.options,
33
+ errorMessage: error.message
34
+ },
35
+ timestamp: new Date().toISOString()
36
+ }, executionLogPrefix);
37
+ logger_1.logger.error(JSON.stringify(error));
38
+ });
24
39
  const outcomeMessage = (message) => `\n
25
40
  ==========================================================================
26
41
  ${message}
27
42
  ==========================================================================`;
28
- logger_1.logger.debug(`startup options: ${cli_1.options}`);
29
- (() => __awaiter(void 0, void 0, void 0, function* () {
30
- try {
31
- if (cli_1.options.mode === 'server') {
32
- (0, configServer_1.startServer)({ customPort: cli_1.options.port, executionLogPrefix });
33
- }
34
- else {
35
- (() => __awaiter(void 0, void 0, void 0, function* () {
43
+ logger_1.logger.debug(`startup options: ${JSON.stringify(cli_1.options)}`);
44
+ function main() {
45
+ return __awaiter(this, void 0, void 0, function* () {
46
+ try {
47
+ if (cli_1.options.mode === 'server') {
48
+ yield (0, configServer_1.startServer)({ customPort: cli_1.options.port, executionLogPrefix });
49
+ }
50
+ else {
36
51
  const resultMetadata = yield (0, analyzer_1.analyzeCodebase)({
37
52
  repoPath: cli_1.options.dir,
38
53
  archetype: cli_1.options.archetype,
@@ -43,40 +58,36 @@ logger_1.logger.debug(`startup options: ${cli_1.options}`);
43
58
  // if results are found, there were issues found in the codebase
44
59
  if (resultMetadata.XFI_RESULT.totalIssues > 0) {
45
60
  logger_1.logger.warn(`WARNING: lo-fi attributes detected in codebase. ${resultMetadata.XFI_RESULT.warningCount} are warnings, ${resultMetadata.XFI_RESULT.fatalityCount} are fatal.`);
46
- logger_1.logger.warn(JSON.stringify({ XFI_RESULT: resultMetadata }));
61
+ logger_1.logger.warn(JSON.stringify(resultMetadata));
62
+ logger_1.logger.warn(`\n${prettyjson_1.default.render(resultMetadata)}\n\n`);
47
63
  if (resultMetadata.XFI_RESULT.fatalityCount > 0) {
48
64
  logger_1.logger.error(outcomeMessage(`THERE WERE ${resultMetadata.XFI_RESULT.fatalityCount} FATAL ERRORS DETECTED TO BE IMMEDIATELY ADDRESSED!`));
49
- setTimeout(() => process.exit(1), 1000);
65
+ logger_1.logger.on('finish', function () {
66
+ process.exit(1);
67
+ });
68
+ logger_1.logger.error(`\n${prettyjson_1.default.render(resultMetadata.XFI_RESULT.issueDetails)}\n\n`);
69
+ logger_1.logger.error(outcomeMessage(`THERE WERE ${resultMetadata.XFI_RESULT.fatalityCount} FATAL ERRORS DETECTED TO BE IMMEDIATELY ADDRESSED!`));
70
+ logger_1.logger.end();
50
71
  }
51
72
  else {
52
73
  logger_1.logger.warn(outcomeMessage('No fatal errors were found, however please review the following warnings.'));
74
+ logger_1.logger.warn(`\n${prettyjson_1.default.render(resultMetadata.XFI_RESULT.issueDetails)}\n\n`);
75
+ logger_1.logger.warn(outcomeMessage('No fatal errors were found, however please review the above warnings.'));
53
76
  }
54
- console.log(`\n${prettyjson_1.default.render(resultMetadata.XFI_RESULT.issueDetails)}\n\n`);
55
77
  }
56
78
  else {
57
79
  logger_1.logger.info(outcomeMessage('SUCCESS! hi-fi codebase detected.'));
80
+ logger_1.logger.info(JSON.stringify(resultMetadata));
81
+ logger_1.logger.info(`\n${prettyjson_1.default.render(resultMetadata)}\n\n`);
82
+ logger_1.logger.info(outcomeMessage('SUCCESS! hi-fi codebase detected.'));
58
83
  }
59
- }))().catch((e) => {
60
- // analyzeCodebase failed
61
- logger_1.logger.error(outcomeMessage('FATAL: execution failed!'));
62
- logger_1.logger.error(e.message);
63
- logger_1.logger.error(`\n${prettyjson_1.default.render(JSON.parse(e.message))}`);
64
- setTimeout(() => process.exit(1), 1000);
65
- });
84
+ }
66
85
  }
67
- }
68
- catch (e) {
69
- yield (0, telemetry_1.sendTelemetry)({
70
- eventType: 'execution failure',
71
- metadata: {
72
- archetype: cli_1.options.archetype,
73
- repoPath: cli_1.options.dir,
74
- options: cli_1.options,
75
- errorMessage: e.message
76
- },
77
- timestamp: new Date().toISOString()
78
- }, executionLogPrefix);
79
- logger_1.logger.error(JSON.stringify(e));
80
- setTimeout(() => process.exit(1), 1000);
81
- }
82
- }))();
86
+ catch (e) {
87
+ yield handleError(e);
88
+ }
89
+ });
90
+ }
91
+ if (require.main === module) {
92
+ main();
93
+ }