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.
- package/CHANGELOG.md +46 -0
- package/dist/core/engine/analyzer.test.js +17 -9
- package/dist/core/engine/engineRunner.test.js +1 -0
- package/dist/core/engine/engineSetup.js +7 -8
- package/dist/core/engine/engineSetup.test.js +106 -77
- package/dist/facts/openaiAnalysisFacts.test.js +1 -0
- package/dist/server/cacheManager.js +66 -0
- package/dist/server/configServer.js +50 -153
- package/dist/server/configServer.test.js +40 -29
- package/dist/server/middleware/checkSharedSecret.js +14 -0
- package/dist/server/middleware/validateGithubWebhook.js +23 -0
- package/dist/server/middleware/validateTelemetryData.js +10 -0
- package/dist/server/middleware/validateUrlInput.js +10 -0
- package/dist/server/routes/archetypeRoute.js +50 -0
- package/dist/server/routes/archetypeRuleRoute.js +58 -0
- package/dist/server/routes/archetypeRulesRoute.js +46 -0
- package/dist/server/routes/clearCacheRoute.js +14 -0
- package/dist/server/routes/githubWebhookRoute.js +140 -0
- package/dist/server/routes/telemetryRoute.js +15 -0
- package/dist/server/routes/viewCacheRoute.js +13 -0
- package/dist/utils/configManager.js +34 -5
- package/dist/utils/inputValidation.js +12 -0
- package/dist/utils/inputValidation.test.js +51 -0
- package/dist/utils/logger.test.js +32 -0
- package/dist/utils/maskSensitiveData.test.js +40 -0
- package/package.json +4 -2
- package/src/core/engine/analyzer.test.ts +17 -9
- package/src/core/engine/engineRunner.test.ts +1 -0
- package/src/core/engine/engineSetup.test.ts +119 -81
- package/src/core/engine/engineSetup.ts +10 -10
- package/src/facts/openaiAnalysisFacts.test.ts +1 -0
- package/src/rules/index.ts +7 -31
- package/src/server/cacheManager.ts +65 -0
- package/src/server/configServer.test.ts +42 -31
- package/src/server/configServer.ts +61 -156
- package/src/server/middleware/checkSharedSecret.ts +14 -0
- package/src/server/middleware/validateGithubWebhook.ts +24 -0
- package/src/server/middleware/validateTelemetryData.ts +9 -0
- package/src/server/middleware/validateUrlInput.ts +9 -0
- package/src/server/routes/archetypeRoute.ts +40 -0
- package/src/server/routes/archetypeRuleRoute.ts +46 -0
- package/src/server/routes/archetypeRulesRoute.ts +35 -0
- package/src/server/routes/clearCacheRoute.ts +13 -0
- package/src/server/routes/githubWebhookRoute.ts +131 -0
- package/src/server/routes/telemetryRoute.ts +14 -0
- package/src/server/routes/viewCacheRoute.ts +15 -0
- package/src/types/typeDefs.ts +20 -0
- package/src/utils/configManager.ts +38 -6
- package/src/utils/inputValidation.test.ts +58 -0
- package/src/utils/inputValidation.ts +14 -0
- package/src/utils/logger.test.ts +34 -0
- package/src/utils/maskSensitiveData.test.ts +45 -0
- 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:
|
|
68
|
-
|
|
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(
|
|
108
|
-
expect(
|
|
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(
|
|
161
|
-
expect(
|
|
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({
|
|
@@ -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
|
|
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
|
|
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
|
|
19
|
-
const
|
|
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/
|
|
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
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
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: '
|
|
57
|
+
{ name: 'operator2', fn: jest.fn() }
|
|
96
58
|
]);
|
|
97
59
|
facts_1.loadFacts.mockResolvedValue([
|
|
98
60
|
{ name: 'fact1', fn: jest.fn() },
|
|
99
|
-
{ name: '
|
|
61
|
+
{ name: 'fact2', fn: jest.fn() }
|
|
100
62
|
]);
|
|
101
|
-
|
|
102
|
-
|
|
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(
|
|
105
|
-
expect(
|
|
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
|
|
110
|
-
const
|
|
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:
|
|
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
|
-
|
|
109
|
+
expect(mockOn).toHaveBeenCalledWith('success', expect.any(Function));
|
|
122
110
|
// Test warning event
|
|
123
|
-
|
|
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
|
|
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
|
});
|
|
@@ -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
|
+
}
|