x-fidelity 2.2.0 → 2.3.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 (53) hide show
  1. package/CHANGELOG.md +46 -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 +7 -8
  5. package/dist/core/engine/engineSetup.test.js +106 -77
  6. package/dist/facts/openaiAnalysisFacts.test.js +1 -0
  7. package/dist/server/cacheManager.js +66 -0
  8. package/dist/server/configServer.js +50 -153
  9. package/dist/server/configServer.test.js +40 -29
  10. package/dist/server/middleware/checkSharedSecret.js +14 -0
  11. package/dist/server/middleware/validateGithubWebhook.js +23 -0
  12. package/dist/server/middleware/validateTelemetryData.js +10 -0
  13. package/dist/server/middleware/validateUrlInput.js +10 -0
  14. package/dist/server/routes/archetypeRoute.js +50 -0
  15. package/dist/server/routes/archetypeRuleRoute.js +58 -0
  16. package/dist/server/routes/archetypeRulesRoute.js +46 -0
  17. package/dist/server/routes/clearCacheRoute.js +14 -0
  18. package/dist/server/routes/githubWebhookRoute.js +140 -0
  19. package/dist/server/routes/telemetryRoute.js +15 -0
  20. package/dist/server/routes/viewCacheRoute.js +13 -0
  21. package/dist/utils/configManager.js +34 -5
  22. package/dist/utils/inputValidation.js +12 -0
  23. package/dist/utils/inputValidation.test.js +51 -0
  24. package/dist/utils/logger.test.js +32 -0
  25. package/dist/utils/maskSensitiveData.test.js +40 -0
  26. package/package.json +4 -2
  27. package/src/core/engine/analyzer.test.ts +17 -9
  28. package/src/core/engine/engineRunner.test.ts +1 -0
  29. package/src/core/engine/engineSetup.test.ts +119 -81
  30. package/src/core/engine/engineSetup.ts +10 -10
  31. package/src/facts/openaiAnalysisFacts.test.ts +1 -0
  32. package/src/rules/index.ts +7 -31
  33. package/src/server/cacheManager.ts +65 -0
  34. package/src/server/configServer.test.ts +42 -31
  35. package/src/server/configServer.ts +61 -156
  36. package/src/server/middleware/checkSharedSecret.ts +14 -0
  37. package/src/server/middleware/validateGithubWebhook.ts +24 -0
  38. package/src/server/middleware/validateTelemetryData.ts +9 -0
  39. package/src/server/middleware/validateUrlInput.ts +9 -0
  40. package/src/server/routes/archetypeRoute.ts +40 -0
  41. package/src/server/routes/archetypeRuleRoute.ts +46 -0
  42. package/src/server/routes/archetypeRulesRoute.ts +35 -0
  43. package/src/server/routes/clearCacheRoute.ts +13 -0
  44. package/src/server/routes/githubWebhookRoute.ts +131 -0
  45. package/src/server/routes/telemetryRoute.ts +14 -0
  46. package/src/server/routes/viewCacheRoute.ts +15 -0
  47. package/src/types/typeDefs.ts +20 -0
  48. package/src/utils/configManager.ts +38 -6
  49. package/src/utils/inputValidation.test.ts +58 -0
  50. package/src/utils/inputValidation.ts +14 -0
  51. package/src/utils/logger.test.ts +34 -0
  52. package/src/utils/maskSensitiveData.test.ts +45 -0
  53. package/src/utils/telemetry.ts +0 -1
package/CHANGELOG.md CHANGED
@@ -1,3 +1,49 @@
1
+ # [2.3.0](https://github.com/zotoio/x-fidelity/compare/v2.2.0...v2.3.0) (2024-08-20)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * Add clearCache import and ConfigManager import to configServer.ts ([4212096](https://github.com/zotoio/x-fidelity/commit/4212096ea2459e4c55a7c6b396755d72f440ecb6))
7
+ * address SSRF risks in `githubWebhookRoute` ([9b6d64a](https://github.com/zotoio/x-fidelity/commit/9b6d64acf7e49604815b606292890a4b50a11ab8))
8
+ * Clear rule list cache correctly ([3cb17c7](https://github.com/zotoio/x-fidelity/commit/3cb17c7e54e9c8be19b03405a3b13eeaf21bf16c))
9
+ * import missing middleware functions ([fd00a4f](https://github.com/zotoio/x-fidelity/commit/fd00a4f9b4b2dac3ef41782f517186a07ce1c53c))
10
+ * Improve security and mitigate potential SSRF risks in configManager and configServer ([f87ccab](https://github.com/zotoio/x-fidelity/commit/f87ccab24c465d374ef191973d4f3f500ebcca00))
11
+ * move the github webhook route and related update code to separate route file ([4deb57e](https://github.com/zotoio/x-fidelity/commit/4deb57e57f4b95cda757aaa84e8c3f89485e24cd))
12
+ * Properly handle asynchronous operations in configServer.test.ts ([2de6174](https://github.com/zotoio/x-fidelity/commit/2de6174330e518a49c0530455f9bfd4118f648d2))
13
+ * Refactor ConfigManager class ([7f1c2fe](https://github.com/zotoio/x-fidelity/commit/7f1c2fe2e895684145c843615876e3118be095a9))
14
+ * Update configServer to use RuleConfig from ConfigManager ([100c0b3](https://github.com/zotoio/x-fidelity/commit/100c0b301423ca111aa8e452073d3c190a317fbc))
15
+ * Update engineSetup.test.ts to use ConfigManager and setLogPrefix ([c36c64d](https://github.com/zotoio/x-fidelity/commit/c36c64de689a952a500a360dffe8e57605d2dbac))
16
+ * Update error message and fix warning detection test ([9f37bf5](https://github.com/zotoio/x-fidelity/commit/9f37bf502407404ddad355a5ac7d4d8710153b80))
17
+ * Update mock configuration in engineSetup.test.ts ([fd3d7db](https://github.com/zotoio/x-fidelity/commit/fd3d7dbaab3ae51b5c6cbc6d0970f3bf2b2db32c))
18
+ * Update mockParams object to include archetypeConfig property ([b0c9628](https://github.com/zotoio/x-fidelity/commit/b0c9628050643fbc508a087f2387144961a74264))
19
+ * Update rule schema definition ([ed5d87a](https://github.com/zotoio/x-fidelity/commit/ed5d87a3579cd2f6d7eb414207a3cfb9bd571d70))
20
+ * Update ruleSchema to match RuleProperties type ([0f09992](https://github.com/zotoio/x-fidelity/commit/0f099925b14d32d044e5c8fb3583cfec4bb39ae7))
21
+ * Update test configuration for analyzer ([f0b139d](https://github.com/zotoio/x-fidelity/commit/f0b139d7b208553d131d682e19a7b0ccebd5febf))
22
+ * Update test expectation for loadFacts ([ee04ee0](https://github.com/zotoio/x-fidelity/commit/ee04ee0c38883e5c073e6360fe6febed45e42cc6))
23
+ * Update test expectations for loadFacts ([4c57421](https://github.com/zotoio/x-fidelity/commit/4c57421de624622404e6232f450057f200781a2b))
24
+ * Validate and sanitize archetype input in configManager ([3a92119](https://github.com/zotoio/x-fidelity/commit/3a921195beb1ac0da0421aafb443adbf77d83eb2))
25
+
26
+
27
+ ### Features
28
+
29
+ * add clearcache route ([89e9633](https://github.com/zotoio/x-fidelity/commit/89e96333ae1bb36b4673c09ddc8b69f6b1ed9740))
30
+ * Add file watcher for local config path ([43da2f3](https://github.com/zotoio/x-fidelity/commit/43da2f3b84f2c852426813068487702c4745d549))
31
+ * Add GitHub webhook route ([cab04ae](https://github.com/zotoio/x-fidelity/commit/cab04ae5c7fbba954e9b85c8182ec3595215a8f9))
32
+ * Add GitHub webhook route to handle archetype or rule config updates ([5af3d63](https://github.com/zotoio/x-fidelity/commit/5af3d63d4d9ef6f3f48a5be4001d3f28f1522d6c))
33
+ * Add high-value unit tests for utility functions ([f90d5e2](https://github.com/zotoio/x-fidelity/commit/f90d5e218cc8c5743515744fc9584801c3003398))
34
+ * Add input validation for URL parameters and telemetry data ([77bdfaa](https://github.com/zotoio/x-fidelity/commit/77bdfaa4e35c34e62c341fca6f87db46726db993))
35
+ * Add support for additional properties in rule schema ([98826e5](https://github.com/zotoio/x-fidelity/commit/98826e5edad1a443d6299a1ad3b970517c384783))
36
+ * add viewcache route ([1fa105c](https://github.com/zotoio/x-fidelity/commit/1fa105c6c29ef05b8eb5312c638785dd5d56af07))
37
+ * Create comprehensive unit test file for engineSetup.ts ([50556ce](https://github.com/zotoio/x-fidelity/commit/50556ce257cb579ef856b74a012b986b81490f2b))
38
+ * Implement GitHub webhook to update local config ([351fbf0](https://github.com/zotoio/x-fidelity/commit/351fbf022100f8e9acc3734b291afdac9057e386))
39
+ * Implement server routes and middleware ([3de18a6](https://github.com/zotoio/x-fidelity/commit/3de18a6cab08a26ecd51f7dc9148da5fee93aeba))
40
+ * load all RuleConfig for a given archetype into the ExecutionConfig ([a122c52](https://github.com/zotoio/x-fidelity/commit/a122c52986adeadeec9085b5c94bfd5e142e80d8))
41
+ * move the configServer features related to caching into a new file ([95a9908](https://github.com/zotoio/x-fidelity/commit/95a9908c97878b062337e100b37ae1d4919b53e5))
42
+ * **rules:** github hook to refresh config ([2485fbe](https://github.com/zotoio/x-fidelity/commit/2485fbe36a9f5102fe815209e0961f604b0e6f30))
43
+ * **rules:** optimise loading of rules, and filesystem watcher for server ([6e6c5ac](https://github.com/zotoio/x-fidelity/commit/6e6c5ac0a784ab76b9d3ec476de580225932b443))
44
+ * update configserver to use new route files ([eda8240](https://github.com/zotoio/x-fidelity/commit/eda8240a09073612f060d65494d4973b1b170c94))
45
+ * Update rule schema to improve flexibility and compatibility ([989bdf6](https://github.com/zotoio/x-fidelity/commit/989bdf64ead3148a366c046f6c0e883d6b1b1109))
46
+
1
47
  # [2.2.0](https://github.com/zotoio/x-fidelity/compare/v2.1.0...v2.2.0) (2024-08-20)
2
48
 
3
49
 
@@ -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,9 +32,9 @@ 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);
@@ -47,7 +46,7 @@ function setupEngine(params) {
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', () => {
@@ -0,0 +1,66 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getCachedData = getCachedData;
4
+ exports.setCachedData = setCachedData;
5
+ exports.clearCache = clearCache;
6
+ exports.getCacheContent = getCacheContent;
7
+ exports.setRuleListCache = setRuleListCache;
8
+ exports.getRuleListCache = getRuleListCache;
9
+ exports.handleConfigChange = handleConfigChange;
10
+ const configManager_1 = require("../utils/configManager");
11
+ const logger_1 = require("../utils/logger");
12
+ // Simple in-memory cache
13
+ const cache = {};
14
+ const DEFAULT_TTL = 10 * 60 * 1000; // 10 minutes in milliseconds
15
+ // Cache for archetype lists and rule lists
16
+ const ruleListCache = {};
17
+ function getCachedData(key) {
18
+ logger_1.logger.debug(`checking cache for key: ${key}`);
19
+ const item = cache[key];
20
+ if (item && item.expiry > Date.now()) {
21
+ return item.data;
22
+ }
23
+ return null;
24
+ }
25
+ function setCachedData(key, data, ttl = DEFAULT_TTL) {
26
+ logger_1.logger.debug(`setting cache for key: ${key}`);
27
+ cache[key] = {
28
+ data,
29
+ expiry: Date.now() + ttl
30
+ };
31
+ logger_1.logger.debug(JSON.stringify(cache));
32
+ }
33
+ function clearCache() {
34
+ logger_1.logger.debug('Clearing cache');
35
+ Object.keys(cache).forEach((key) => {
36
+ delete cache[key];
37
+ });
38
+ Object.keys(ruleListCache).forEach((key) => {
39
+ delete ruleListCache[key];
40
+ });
41
+ }
42
+ function getCacheContent() {
43
+ return {
44
+ cache: cache,
45
+ ruleListCache: ruleListCache
46
+ };
47
+ }
48
+ function setRuleListCache(archetype, data, ttl = DEFAULT_TTL) {
49
+ ruleListCache[archetype] = {
50
+ data: data,
51
+ expiry: Date.now() + ttl
52
+ };
53
+ }
54
+ function getRuleListCache(archetype) {
55
+ const item = ruleListCache[archetype];
56
+ if (item && item.expiry > Date.now()) {
57
+ return item.data;
58
+ }
59
+ return null;
60
+ }
61
+ function handleConfigChange(path, fileType, changeType) {
62
+ logger_1.logger.info(`${fileType} ${path} has been ${changeType}`);
63
+ clearCache();
64
+ configManager_1.ConfigManager.clearLoadedConfigs();
65
+ logger_1.logger.debug('Cache and loaded configs cleared due to local config change');
66
+ }