x-fidelity 1.6.0 → 1.6.1
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/.eslintrc.js +19 -0
- package/CHANGELOG.md +7 -0
- package/README.md +19 -2
- package/dist/core/engine.js +8 -8
- package/dist/core/engine.test.js +2 -2
- package/dist/facts/index.js +2 -0
- package/dist/facts/repoDependencyFacts.js +7 -8
- package/dist/index.js +1 -1
- package/dist/operators/fileContains.js +2 -2
- package/dist/operators/outdatedFramework.js +1 -1
- package/dist/rules/index.js +0 -3
- package/dist/server/configServer.js +3 -3
- package/dist/xfidelity +1 -1
- package/package.json +9 -4
- package/src/core/engine.test.ts +2 -2
- package/src/core/engine.ts +11 -11
- package/src/facts/index.ts +2 -0
- package/src/facts/repoDependencyFacts.test.ts +0 -2
- package/src/facts/repoDependencyFacts.ts +8 -8
- package/src/facts/repoFilesystemFacts.test.ts +0 -1
- package/src/index.ts +1 -1
- package/src/operators/fileContains.ts +2 -2
- package/src/operators/index.test.ts +0 -1
- package/src/operators/outdatedFramework.test.ts +0 -1
- package/src/operators/outdatedFramework.ts +2 -3
- package/src/rules/index.ts +0 -3
- package/src/server/configServer.ts +3 -3
- package/src/utils/config.ts +1 -1
package/.eslintrc.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
module.exports = {
|
|
2
|
+
parser: '@typescript-eslint/parser',
|
|
3
|
+
extends: [
|
|
4
|
+
'eslint:recommended',
|
|
5
|
+
'plugin:@typescript-eslint/recommended',
|
|
6
|
+
],
|
|
7
|
+
parserOptions: {
|
|
8
|
+
ecmaVersion: 2020,
|
|
9
|
+
sourceType: 'module',
|
|
10
|
+
},
|
|
11
|
+
env: {
|
|
12
|
+
node: true,
|
|
13
|
+
es6: true,
|
|
14
|
+
},
|
|
15
|
+
rules: {
|
|
16
|
+
// Add any custom rules here
|
|
17
|
+
'@typescript-eslint/no-explicit-any': 'off', // Disable no-explicit-any rule
|
|
18
|
+
},
|
|
19
|
+
};
|
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
## [1.6.1](https://github.com/zotoio/x-fidelity/compare/v1.6.0...v1.6.1) (2024-07-27)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* **lint:** setup ([f559331](https://github.com/zotoio/x-fidelity/commit/f5593314bc591cb2d6e8cb809c828455fa294b62))
|
|
7
|
+
|
|
1
8
|
# [1.6.0](https://github.com/zotoio/x-fidelity/compare/v1.5.1...v1.6.0) (2024-07-27)
|
|
2
9
|
|
|
3
10
|
|
package/README.md
CHANGED
|
@@ -45,8 +45,9 @@ x-fidelity is an advanced CLI tool designed to enforce opinionated framework adh
|
|
|
45
45
|
7. [OpenAI Integration](#openai-integration)
|
|
46
46
|
8. [Hosting Config Servers](#hosting-config-servers)
|
|
47
47
|
9. [Best Practices](#best-practices)
|
|
48
|
-
10. [
|
|
49
|
-
11. [
|
|
48
|
+
10. [Linting](#linting)
|
|
49
|
+
11. [Contributing](#contributing)
|
|
50
|
+
12. [License](#license)
|
|
50
51
|
|
|
51
52
|
## Intent and Purpose
|
|
52
53
|
|
|
@@ -316,6 +317,22 @@ app.listen(8888, () => {
|
|
|
316
317
|
|
|
317
318
|
Contributions to x-fidelity are welcome! Please refer to the `CONTRIBUTING.md` file for guidelines on how to contribute to this project.
|
|
318
319
|
|
|
320
|
+
## Linting
|
|
321
|
+
|
|
322
|
+
This project uses ESLint for static code analysis. To run the linter:
|
|
323
|
+
|
|
324
|
+
```sh
|
|
325
|
+
yarn lint
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
To automatically fix linting issues:
|
|
329
|
+
|
|
330
|
+
```sh
|
|
331
|
+
yarn lint:fix
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
ESLint is also integrated into the CI pipeline and runs alongside unit tests in GitHub Actions.
|
|
335
|
+
|
|
319
336
|
## License
|
|
320
337
|
|
|
321
338
|
This project is licensed under the MIT License. See the `LICENSE` file for details.
|
package/dist/core/engine.js
CHANGED
|
@@ -107,7 +107,7 @@ function analyzeCodebase(repoPath_1) {
|
|
|
107
107
|
logger_1.logger.error(e.message);
|
|
108
108
|
}
|
|
109
109
|
});
|
|
110
|
-
engine.on('success', (_a
|
|
110
|
+
engine.on('success', (_a) => __awaiter(this, [_a], void 0, function* ({ type, params }) {
|
|
111
111
|
if (type === 'violation') {
|
|
112
112
|
logger_1.logger.warn(`violation detected: ${JSON.stringify(params)}}`);
|
|
113
113
|
yield (0, telemetry_1.sendTelemetry)({
|
|
@@ -146,14 +146,14 @@ function analyzeCodebase(repoPath_1) {
|
|
|
146
146
|
engine.addFact('repoDependencyAnalysis', repoDependencyFacts_1.repoDependencyAnalysis);
|
|
147
147
|
// Run the engine for each file's data
|
|
148
148
|
logger_1.logger.info(`### Executing rules..`);
|
|
149
|
-
|
|
149
|
+
const failures = [];
|
|
150
150
|
for (const file of fileData) {
|
|
151
151
|
if (file.fileName === config_1.REPO_GLOBAL_CHECK) {
|
|
152
|
-
|
|
152
|
+
const msg = `\n==========================\nSTARTING GLOBAL REPO CHECKS..\n==========================`;
|
|
153
153
|
logger_1.logger.info(msg);
|
|
154
154
|
}
|
|
155
155
|
else {
|
|
156
|
-
|
|
156
|
+
const msg = `running engine for ${file.filePath}`;
|
|
157
157
|
logger_1.logger.info(msg);
|
|
158
158
|
}
|
|
159
159
|
const facts = {
|
|
@@ -164,7 +164,7 @@ function analyzeCodebase(repoPath_1) {
|
|
|
164
164
|
},
|
|
165
165
|
standardStructure
|
|
166
166
|
};
|
|
167
|
-
|
|
167
|
+
const fileFailures = [];
|
|
168
168
|
yield engine.run(facts)
|
|
169
169
|
.then(({ results }) => {
|
|
170
170
|
results.map((result) => {
|
|
@@ -206,11 +206,11 @@ function analyzeCodebase(repoPath_1) {
|
|
|
206
206
|
});
|
|
207
207
|
}
|
|
208
208
|
const findKeyValuePair = (data, targetKey, targetValue) => {
|
|
209
|
-
|
|
209
|
+
const results = [];
|
|
210
210
|
const recursiveSearch = (obj) => {
|
|
211
211
|
if (typeof obj === 'object' && obj !== null) {
|
|
212
|
-
for (
|
|
213
|
-
if (
|
|
212
|
+
for (const key in obj) {
|
|
213
|
+
if (Object.prototype.hasOwnProperty.call(obj, key)) {
|
|
214
214
|
if (key === targetKey && obj[key] === targetValue) {
|
|
215
215
|
results.push(obj);
|
|
216
216
|
return; // Stop searching this branch as we've found the target in this object
|
package/dist/core/engine.test.js
CHANGED
|
@@ -67,8 +67,8 @@ describe('analyzeCodebase', () => {
|
|
|
67
67
|
getConfig: jest.fn().mockReturnValue(archetypes_1.archetypes['node-fullstack']),
|
|
68
68
|
configServer: ''
|
|
69
69
|
});
|
|
70
|
-
jest.spyOn(console, 'log').mockImplementation(() => { });
|
|
71
|
-
jest.spyOn(console, 'error').mockImplementation(() => { });
|
|
70
|
+
jest.spyOn(console, 'log').mockImplementation(() => { console.log('z'); });
|
|
71
|
+
jest.spyOn(console, 'error').mockImplementation(() => { console.log('z'); });
|
|
72
72
|
});
|
|
73
73
|
it('should analyze the codebase and return results', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
74
74
|
const mockFileData = [
|
package/dist/facts/index.js
CHANGED
|
@@ -13,11 +13,13 @@ exports.loadFacts = loadFacts;
|
|
|
13
13
|
const repoFilesystemFacts_1 = require("./repoFilesystemFacts");
|
|
14
14
|
const repoDependencyFacts_1 = require("./repoDependencyFacts");
|
|
15
15
|
const openaiAnalysisFacts_1 = require("./openaiAnalysisFacts");
|
|
16
|
+
// eslint-disable-next-line @typescript-eslint/ban-types
|
|
16
17
|
const allFacts = {
|
|
17
18
|
repoFilesystemFacts: { name: 'fileData', fn: repoFilesystemFacts_1.collectRepoFileData },
|
|
18
19
|
repoDependencyFacts: { name: 'dependencyData', fn: repoDependencyFacts_1.getDependencyVersionFacts },
|
|
19
20
|
openaiAnalysisFacts: { name: 'openaiAnalysis', fn: openaiAnalysisFacts_1.openaiAnalysis }
|
|
20
21
|
};
|
|
22
|
+
// eslint-disable-next-line @typescript-eslint/ban-types
|
|
21
23
|
function loadFacts(factNames) {
|
|
22
24
|
return __awaiter(this, void 0, void 0, function* () {
|
|
23
25
|
return factNames
|
|
@@ -50,7 +50,7 @@ const semver = __importStar(require("semver"));
|
|
|
50
50
|
function collectLocalDependencies() {
|
|
51
51
|
let result = {};
|
|
52
52
|
try {
|
|
53
|
-
|
|
53
|
+
const stdout = (0, child_process_1.execSync)('npm ls -a --json');
|
|
54
54
|
result = JSON.parse(stdout.toString());
|
|
55
55
|
}
|
|
56
56
|
catch (e) {
|
|
@@ -80,11 +80,11 @@ function getDependencyVersionFacts(archetypeConfig) {
|
|
|
80
80
|
* @returns An array of results.
|
|
81
81
|
*/
|
|
82
82
|
function findPropertiesInTree(depGraph, minVersions) {
|
|
83
|
-
|
|
83
|
+
const results = [];
|
|
84
84
|
logger_1.logger.debug(`depGraph: ${depGraph}`);
|
|
85
85
|
function walk(depGraph) {
|
|
86
86
|
if (lodash_1.default.isObject(depGraph) && !lodash_1.default.isArray(depGraph)) {
|
|
87
|
-
for (
|
|
87
|
+
for (const depName in depGraph) {
|
|
88
88
|
if (Object.keys(minVersions).includes(depName)) {
|
|
89
89
|
results.push({ dep: depName, ver: depGraph[depName].version, min: minVersions[depName] });
|
|
90
90
|
}
|
|
@@ -94,7 +94,7 @@ function findPropertiesInTree(depGraph, minVersions) {
|
|
|
94
94
|
}
|
|
95
95
|
}
|
|
96
96
|
else if (lodash_1.default.isArray(depGraph)) {
|
|
97
|
-
for (
|
|
97
|
+
for (const item of depGraph) {
|
|
98
98
|
walk(item);
|
|
99
99
|
}
|
|
100
100
|
}
|
|
@@ -104,18 +104,18 @@ function findPropertiesInTree(depGraph, minVersions) {
|
|
|
104
104
|
}
|
|
105
105
|
function repoDependencyAnalysis(params, almanac) {
|
|
106
106
|
return __awaiter(this, void 0, void 0, function* () {
|
|
107
|
-
|
|
107
|
+
const result = { 'result': [] };
|
|
108
108
|
const fileData = yield almanac.factValue('fileData');
|
|
109
109
|
if (fileData.fileName !== 'REPO_GLOBAL_CHECK') {
|
|
110
110
|
return result;
|
|
111
111
|
}
|
|
112
|
-
|
|
112
|
+
const analysis = [];
|
|
113
113
|
const dependencyData = yield almanac.factValue('dependencyData');
|
|
114
114
|
dependencyData.installedDependencyVersions.map((versionData) => {
|
|
115
115
|
logger_1.logger.debug(`outdatedFramework: checking ${versionData.dep}`);
|
|
116
116
|
const requiredRange = new semver.Range(versionData.min);
|
|
117
117
|
if (!semver.gtr(versionData.ver, requiredRange)) {
|
|
118
|
-
|
|
118
|
+
const dependencyFailure = {
|
|
119
119
|
'dependency': versionData.dep,
|
|
120
120
|
'currentVersion': versionData.ver,
|
|
121
121
|
'requiredVersion': versionData.min
|
|
@@ -129,4 +129,3 @@ function repoDependencyAnalysis(params, almanac) {
|
|
|
129
129
|
return result;
|
|
130
130
|
});
|
|
131
131
|
}
|
|
132
|
-
;
|
package/dist/index.js
CHANGED
|
@@ -25,7 +25,7 @@ try {
|
|
|
25
25
|
}
|
|
26
26
|
else {
|
|
27
27
|
(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
28
|
-
|
|
28
|
+
const results = yield (0, engine_1.analyzeCodebase)(`${process.env.PWD}/${cli_1.options.dir}`, cli_1.options.archetype, cli_1.options.configServer);
|
|
29
29
|
// if results are found, there were warning level issues found in the codebase
|
|
30
30
|
if (results.length > 0) {
|
|
31
31
|
logger_1.logger.warn('WARNING: lo-fi attributes detected in codebase!');
|
|
@@ -7,9 +7,9 @@ const fileContains = {
|
|
|
7
7
|
'fn': (fileContent, checkString) => {
|
|
8
8
|
let result = false;
|
|
9
9
|
const regex = new RegExp(checkString, 'g');
|
|
10
|
-
|
|
10
|
+
const lines = fileContent.split('\n');
|
|
11
11
|
let lineNumber = 0;
|
|
12
|
-
for (
|
|
12
|
+
for (const line of lines) {
|
|
13
13
|
lineNumber++;
|
|
14
14
|
if (regex.test(line)) {
|
|
15
15
|
logger_1.logger.debug(`fileContains '${checkString}' found in line ${lineNumber}: ${line}`);
|
|
@@ -4,7 +4,7 @@ exports.outdatedFramework = void 0;
|
|
|
4
4
|
const logger_1 = require("../utils/logger");
|
|
5
5
|
const outdatedFramework = {
|
|
6
6
|
'name': 'outdatedFramework',
|
|
7
|
-
'fn': (repoDependencyAnalysis
|
|
7
|
+
'fn': (repoDependencyAnalysis) => {
|
|
8
8
|
var _a;
|
|
9
9
|
let result = false;
|
|
10
10
|
try {
|
package/dist/rules/index.js
CHANGED
|
@@ -67,9 +67,6 @@ function loadRules(archetype, ruleNames, configServer, logPrefix, localConfigPat
|
|
|
67
67
|
else if (localConfigPath) {
|
|
68
68
|
rule = yield loadLocalConfigRule(ruleName, localConfigPath);
|
|
69
69
|
}
|
|
70
|
-
else if (localConfigPath) {
|
|
71
|
-
rule = yield loadLocalConfigRule(ruleName, localConfigPath);
|
|
72
|
-
}
|
|
73
70
|
else {
|
|
74
71
|
rule = yield loadLocalRule(ruleName);
|
|
75
72
|
}
|
|
@@ -25,7 +25,7 @@ app.use(express_1.default.json());
|
|
|
25
25
|
app.use(expressLogger_1.expressLogger);
|
|
26
26
|
const validInput = (value) => {
|
|
27
27
|
// Ensure input contains only alphanumeric characters, hyphens, and underscores
|
|
28
|
-
const validName = /^[a-zA-Z0-9-_
|
|
28
|
+
const validName = /^[a-zA-Z0-9-_-]{1,50}$/;
|
|
29
29
|
return validName.test(value);
|
|
30
30
|
};
|
|
31
31
|
app.get('/archetypes/:archetype', (req, res) => {
|
|
@@ -46,7 +46,7 @@ app.get('/archetypes', (req, res) => {
|
|
|
46
46
|
app.get('/archetypes/:archetype/rules', (req, res) => __awaiter(void 0, void 0, void 0, function* () {
|
|
47
47
|
logger_1.logger.info(`serving rules for archetype: ${req.params.archetype}`);
|
|
48
48
|
const archetype = req.params.archetype;
|
|
49
|
-
if (validInput(archetype) &&
|
|
49
|
+
if (validInput(archetype) && Object.prototype.hasOwnProperty.call(archetypes_1.archetypes, archetype) && archetypes_1.archetypes[archetype].rules) {
|
|
50
50
|
const rules = yield (0, rules_1.loadRules)(archetype, archetypes_1.archetypes[archetype].rules);
|
|
51
51
|
res.json(rules);
|
|
52
52
|
}
|
|
@@ -58,7 +58,7 @@ app.get('/archetypes/:archetype/rules/:rule', (req, res) => __awaiter(void 0, vo
|
|
|
58
58
|
logger_1.logger.info(`serving rule ${req.params.rule} for archetype ${req.params.archetype}..`);
|
|
59
59
|
const archetype = req.params.archetype;
|
|
60
60
|
const rule = req.params.rule;
|
|
61
|
-
if (validInput(archetype) && validInput(rule) &&
|
|
61
|
+
if (validInput(archetype) && validInput(rule) && Object.prototype.hasOwnProperty.call(archetypes_1.archetypes, archetype) && archetypes_1.archetypes[archetype].rules.includes(rule)) {
|
|
62
62
|
const rules = yield (0, rules_1.loadRules)(archetype, archetypes_1.archetypes[archetype].rules);
|
|
63
63
|
const ruleJson = rules.find((r) => r.name === rule);
|
|
64
64
|
res.json(ruleJson);
|
package/dist/xfidelity
CHANGED
|
@@ -25,7 +25,7 @@ try {
|
|
|
25
25
|
}
|
|
26
26
|
else {
|
|
27
27
|
(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
28
|
-
|
|
28
|
+
const results = yield (0, engine_1.analyzeCodebase)(`${process.env.PWD}/${cli_1.options.dir}`, cli_1.options.archetype, cli_1.options.configServer);
|
|
29
29
|
// if results are found, there were warning level issues found in the codebase
|
|
30
30
|
if (results.length > 0) {
|
|
31
31
|
logger_1.logger.warn('WARNING: lo-fi attributes detected in codebase!');
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "x-fidelity",
|
|
3
|
-
"version": "1.6.
|
|
3
|
+
"version": "1.6.1",
|
|
4
4
|
"description": "cli for opinionated framework adherence checks",
|
|
5
5
|
"main": "dist/xfidelity",
|
|
6
6
|
"bin": {
|
|
@@ -10,13 +10,15 @@
|
|
|
10
10
|
"build": "rimraf dist/ && tsc && yarn copy-files",
|
|
11
11
|
"copy-files": "cp src/rules/*.json dist/rules/ && cp dist/index.js dist/xfidelity",
|
|
12
12
|
"start": "OPENAI_API_KEY= jest --watch",
|
|
13
|
-
"test": "OPENAI_API_KEY= jest",
|
|
13
|
+
"test": "yarn lint && OPENAI_API_KEY= jest",
|
|
14
14
|
"test:coverage": "OPENAI_API_KEY= jest --coverage",
|
|
15
|
-
"commit": "git-cz",
|
|
15
|
+
"commit": "yarn test && git-cz",
|
|
16
16
|
"release": "semantic-release",
|
|
17
17
|
"test-bin-install": "yarn build && yarn global bin && yarn global add file:$PWD",
|
|
18
18
|
"build-run": "yarn build && node . --dir . | jq -R -r '. as $line | try fromjson catch $line'",
|
|
19
|
-
"start-server": "ts-node src/index.ts --mode server"
|
|
19
|
+
"start-server": "ts-node src/index.ts --mode server",
|
|
20
|
+
"lint": "eslint . --ext .ts",
|
|
21
|
+
"lint:fix": "eslint . --ext .ts --fix"
|
|
20
22
|
},
|
|
21
23
|
"repository": "git@github.com:zotoio/x-fidelity.git",
|
|
22
24
|
"author": "wyvern8 <io@zoto.io>",
|
|
@@ -28,6 +30,9 @@
|
|
|
28
30
|
"devDependencies": {
|
|
29
31
|
"@jest/globals": "^29.7.0",
|
|
30
32
|
"@semantic-release/changelog": "^6.0.3",
|
|
33
|
+
"@typescript-eslint/eslint-plugin": "^5.59.0",
|
|
34
|
+
"@typescript-eslint/parser": "^5.59.0",
|
|
35
|
+
"eslint": "^8.38.0",
|
|
31
36
|
"@semantic-release/commit-analyzer": "^13.0.0",
|
|
32
37
|
"@semantic-release/git": "^10.0.1",
|
|
33
38
|
"@semantic-release/github": "^10.0.6",
|
package/src/core/engine.test.ts
CHANGED
|
@@ -59,8 +59,8 @@ describe('analyzeCodebase', () => {
|
|
|
59
59
|
getConfig: jest.fn().mockReturnValue(archetypes['node-fullstack']),
|
|
60
60
|
configServer: ''
|
|
61
61
|
});
|
|
62
|
-
jest.spyOn(console, 'log').mockImplementation(() => {});
|
|
63
|
-
jest.spyOn(console, 'error').mockImplementation(() => {});
|
|
62
|
+
jest.spyOn(console, 'log').mockImplementation(() => {console.log('z')});
|
|
63
|
+
jest.spyOn(console, 'error').mockImplementation(() => {console.log('z')});
|
|
64
64
|
});
|
|
65
65
|
|
|
66
66
|
it('should analyze the codebase and return results', async () => {
|
package/src/core/engine.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { logger, logPrefix } from '../utils/logger';
|
|
2
|
-
import {
|
|
2
|
+
import { Engine, EngineResult, Event, RuleProperties, RuleResult } from 'json-rules-engine';
|
|
3
3
|
import { FileData, collectRepoFileData } from '../facts/repoFilesystemFacts';
|
|
4
|
-
import { ScanResult, RuleFailure
|
|
4
|
+
import { ScanResult, RuleFailure} from '../types/typeDefs';
|
|
5
5
|
import { getDependencyVersionFacts, repoDependencyAnalysis } from '../facts/repoDependencyFacts';
|
|
6
6
|
import { collectOpenaiAnalysisFacts, openaiAnalysis } from '../facts/openaiAnalysisFacts';
|
|
7
7
|
import { loadOperators } from '../operators';
|
|
@@ -13,7 +13,7 @@ import { sendTelemetry } from '../utils/telemetry';
|
|
|
13
13
|
import { execSync } from 'child_process';
|
|
14
14
|
import os from 'os';
|
|
15
15
|
|
|
16
|
-
async function analyzeCodebase(repoPath: string, archetype
|
|
16
|
+
async function analyzeCodebase(repoPath: string, archetype = 'node-fullstack', configServer = '', localConfigPath = ''): Promise<any[]> {
|
|
17
17
|
const configManager = ConfigManager.getInstance();
|
|
18
18
|
await configManager.initialize(archetype, configServer, localConfigPath);
|
|
19
19
|
const archetypeConfig = configManager.getConfig();
|
|
@@ -104,7 +104,7 @@ async function analyzeCodebase(repoPath: string, archetype: string = 'node-fulls
|
|
|
104
104
|
}
|
|
105
105
|
});
|
|
106
106
|
|
|
107
|
-
engine.on('success', async ({ type, params }: Event
|
|
107
|
+
engine.on('success', async ({ type, params }: Event) => {
|
|
108
108
|
if (type === 'violation') {
|
|
109
109
|
logger.warn(`violation detected: ${JSON.stringify(params)}}`);
|
|
110
110
|
await sendTelemetry({
|
|
@@ -152,14 +152,14 @@ async function analyzeCodebase(repoPath: string, archetype: string = 'node-fulls
|
|
|
152
152
|
|
|
153
153
|
// Run the engine for each file's data
|
|
154
154
|
logger.info(`### Executing rules..`);
|
|
155
|
-
|
|
155
|
+
const failures: ScanResult[] = [];
|
|
156
156
|
for (const file of fileData) {
|
|
157
157
|
if (file.fileName === REPO_GLOBAL_CHECK) {
|
|
158
|
-
|
|
158
|
+
const msg = `\n==========================\nSTARTING GLOBAL REPO CHECKS..\n==========================`
|
|
159
159
|
logger.info(msg);
|
|
160
160
|
|
|
161
161
|
} else {
|
|
162
|
-
|
|
162
|
+
const msg = `running engine for ${file.filePath}`
|
|
163
163
|
logger.info(msg);
|
|
164
164
|
}
|
|
165
165
|
const facts = {
|
|
@@ -171,7 +171,7 @@ async function analyzeCodebase(repoPath: string, archetype: string = 'node-fulls
|
|
|
171
171
|
standardStructure
|
|
172
172
|
|
|
173
173
|
};
|
|
174
|
-
|
|
174
|
+
const fileFailures: RuleFailure[] = [];
|
|
175
175
|
|
|
176
176
|
await engine.run(facts)
|
|
177
177
|
.then(({ results }: EngineResult) => {
|
|
@@ -222,12 +222,12 @@ const findKeyValuePair = (
|
|
|
222
222
|
targetKey: string,
|
|
223
223
|
targetValue: any
|
|
224
224
|
): any[] => {
|
|
225
|
-
|
|
225
|
+
const results: any[] = [];
|
|
226
226
|
|
|
227
227
|
const recursiveSearch = (obj: any): void => {
|
|
228
228
|
if (typeof obj === 'object' && obj !== null) {
|
|
229
|
-
for (
|
|
230
|
-
if (
|
|
229
|
+
for (const key in obj) {
|
|
230
|
+
if (Object.prototype.hasOwnProperty.call(obj, key)) {
|
|
231
231
|
if (key === targetKey && obj[key] === targetValue) {
|
|
232
232
|
results.push(obj);
|
|
233
233
|
return; // Stop searching this branch as we've found the target in this object
|
package/src/facts/index.ts
CHANGED
|
@@ -2,12 +2,14 @@ import { collectRepoFileData } from './repoFilesystemFacts';
|
|
|
2
2
|
import { getDependencyVersionFacts } from './repoDependencyFacts';
|
|
3
3
|
import { openaiAnalysis } from './openaiAnalysisFacts';
|
|
4
4
|
|
|
5
|
+
// eslint-disable-next-line @typescript-eslint/ban-types
|
|
5
6
|
const allFacts: Record<string, { name: string, fn: Function }> = {
|
|
6
7
|
repoFilesystemFacts: { name: 'fileData', fn: collectRepoFileData },
|
|
7
8
|
repoDependencyFacts: { name: 'dependencyData', fn: getDependencyVersionFacts },
|
|
8
9
|
openaiAnalysisFacts: { name: 'openaiAnalysis', fn: openaiAnalysis }
|
|
9
10
|
};
|
|
10
11
|
|
|
12
|
+
// eslint-disable-next-line @typescript-eslint/ban-types
|
|
11
13
|
async function loadFacts(factNames: string[]): Promise<{ name: string, fn: Function }[]> {
|
|
12
14
|
return factNames
|
|
13
15
|
.map(name => allFacts[name])
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import { execSync } from 'child_process';
|
|
2
2
|
import { collectLocalDependencies, getDependencyVersionFacts, findPropertiesInTree } from './repoDependencyFacts';
|
|
3
3
|
import { logger } from '../utils/logger';
|
|
4
|
-
import { ConfigManager } from '../utils/config';
|
|
5
|
-
import { arch } from 'os';
|
|
6
4
|
import { archetypes } from '../archetypes';
|
|
7
5
|
|
|
8
6
|
jest.mock('child_process', () => ({
|
|
@@ -13,7 +13,7 @@ import { FileData } from './repoFilesystemFacts';
|
|
|
13
13
|
export function collectLocalDependencies(): LocalDependencies {
|
|
14
14
|
let result: LocalDependencies = {};
|
|
15
15
|
try {
|
|
16
|
-
|
|
16
|
+
const stdout = execSync('npm ls -a --json');
|
|
17
17
|
result = JSON.parse(stdout.toString());
|
|
18
18
|
} catch (e) {
|
|
19
19
|
logger.error(`exec error: ${e}`);
|
|
@@ -44,13 +44,13 @@ export async function getDependencyVersionFacts(archetypeConfig: ArchetypeConfig
|
|
|
44
44
|
* @returns An array of results.
|
|
45
45
|
*/
|
|
46
46
|
export function findPropertiesInTree(depGraph: LocalDependencies, minVersions: MinimumDepVersions): VersionData[] {
|
|
47
|
-
|
|
47
|
+
const results: VersionData[] = [];
|
|
48
48
|
|
|
49
49
|
logger.debug(`depGraph: ${depGraph}`);
|
|
50
50
|
|
|
51
51
|
function walk(depGraph: LocalDependencies) {
|
|
52
52
|
if (_.isObject(depGraph) && !_.isArray(depGraph)) {
|
|
53
|
-
for (
|
|
53
|
+
for (const depName in depGraph) {
|
|
54
54
|
if (Object.keys(minVersions).includes(depName)) {
|
|
55
55
|
results.push({ dep: depName, ver: depGraph[depName].version, min: minVersions[depName] });
|
|
56
56
|
}
|
|
@@ -59,7 +59,7 @@ export function findPropertiesInTree(depGraph: LocalDependencies, minVersions: M
|
|
|
59
59
|
}
|
|
60
60
|
}
|
|
61
61
|
} else if (_.isArray(depGraph)) {
|
|
62
|
-
for (
|
|
62
|
+
for (const item of depGraph) {
|
|
63
63
|
walk(item);
|
|
64
64
|
}
|
|
65
65
|
}
|
|
@@ -71,14 +71,14 @@ export function findPropertiesInTree(depGraph: LocalDependencies, minVersions: M
|
|
|
71
71
|
|
|
72
72
|
export async function repoDependencyAnalysis(params: any, almanac: Almanac) {
|
|
73
73
|
|
|
74
|
-
|
|
74
|
+
const result: any = {'result': []};
|
|
75
75
|
const fileData: FileData = await almanac.factValue('fileData');
|
|
76
76
|
|
|
77
77
|
if (fileData.fileName !== 'REPO_GLOBAL_CHECK') {
|
|
78
78
|
return result;
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
-
|
|
81
|
+
const analysis: any = [];
|
|
82
82
|
const dependencyData: any = await almanac.factValue('dependencyData');
|
|
83
83
|
|
|
84
84
|
dependencyData.installedDependencyVersions.map((versionData: VersionData) => {
|
|
@@ -86,7 +86,7 @@ export async function repoDependencyAnalysis(params: any, almanac: Almanac) {
|
|
|
86
86
|
|
|
87
87
|
const requiredRange = new semver.Range(versionData.min);
|
|
88
88
|
if (!semver.gtr(versionData.ver, requiredRange)) {
|
|
89
|
-
|
|
89
|
+
const dependencyFailure = {
|
|
90
90
|
'dependency': versionData.dep,
|
|
91
91
|
'currentVersion': versionData.ver,
|
|
92
92
|
'requiredVersion': versionData.min
|
|
@@ -102,4 +102,4 @@ export async function repoDependencyAnalysis(params: any, almanac: Almanac) {
|
|
|
102
102
|
almanac.addRuntimeFact(params.resultFact, result);
|
|
103
103
|
|
|
104
104
|
return result;
|
|
105
|
-
}
|
|
105
|
+
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { collectRepoFileData, parseFile, isBlacklisted, isWhitelisted } from './repoFilesystemFacts';
|
|
2
2
|
import fs from 'fs';
|
|
3
3
|
import path from 'path';
|
|
4
|
-
import { logger } from '../utils/logger';
|
|
5
4
|
import { ArchetypeConfig } from '../types/typeDefs';
|
|
6
5
|
|
|
7
6
|
jest.mock('fs', () => ({
|
package/src/index.ts
CHANGED
|
@@ -12,7 +12,7 @@ try {
|
|
|
12
12
|
startServer(options.port);
|
|
13
13
|
} else {
|
|
14
14
|
(async () => {
|
|
15
|
-
|
|
15
|
+
const results = await analyzeCodebase(`${process.env.PWD}/${options.dir}`, options.archetype, options.configServer);
|
|
16
16
|
|
|
17
17
|
// if results are found, there were warning level issues found in the codebase
|
|
18
18
|
if (results.length > 0) {
|
|
@@ -7,9 +7,9 @@ const fileContains: OperatorDefn = {
|
|
|
7
7
|
let result = false;
|
|
8
8
|
|
|
9
9
|
const regex = new RegExp(checkString, 'g');
|
|
10
|
-
|
|
10
|
+
const lines = fileContent.split('\n');
|
|
11
11
|
let lineNumber = 0;
|
|
12
|
-
for (
|
|
12
|
+
for (const line of lines) {
|
|
13
13
|
lineNumber++;
|
|
14
14
|
if (regex.test(line)) {
|
|
15
15
|
logger.debug(`fileContains '${checkString}' found in line ${lineNumber}: ${line}`);
|
|
@@ -2,7 +2,6 @@ import { loadOperators } from './index';
|
|
|
2
2
|
import { outdatedFramework } from './outdatedFramework';
|
|
3
3
|
import { fileContains } from './fileContains';
|
|
4
4
|
import { nonStandardDirectoryStructure } from './nonStandardDirectoryStructure';
|
|
5
|
-
import { openaiAnalysisHighSeverity } from './openaiAnalysisHighSeverity';
|
|
6
5
|
|
|
7
6
|
jest.mock('./outdatedFramework');
|
|
8
7
|
jest.mock('./fileContains');
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import { logger } from '../utils/logger';
|
|
2
|
-
import { OperatorDefn
|
|
3
|
-
import { REPO_GLOBAL_CHECK } from '../utils/config';
|
|
2
|
+
import { OperatorDefn } from '../types/typeDefs';
|
|
4
3
|
|
|
5
4
|
const outdatedFramework: OperatorDefn = {
|
|
6
5
|
'name': 'outdatedFramework',
|
|
7
|
-
'fn': (repoDependencyAnalysis: any
|
|
6
|
+
'fn': (repoDependencyAnalysis: any) => {
|
|
8
7
|
let result = false;
|
|
9
8
|
|
|
10
9
|
try {
|
package/src/rules/index.ts
CHANGED
|
@@ -3,7 +3,6 @@ import { RuleProperties } from 'json-rules-engine';
|
|
|
3
3
|
import * as fs from 'fs';
|
|
4
4
|
import * as path from 'path';
|
|
5
5
|
import axios from 'axios';
|
|
6
|
-
import { options } from "../core/cli";
|
|
7
6
|
import { isOpenAIEnabled } from '../utils/openaiUtils';
|
|
8
7
|
|
|
9
8
|
async function loadRules(archetype: string, ruleNames: string[], configServer?: string, logPrefix?: string, localConfigPath?: string): Promise<RuleProperties[]> {
|
|
@@ -31,8 +30,6 @@ async function loadRules(archetype: string, ruleNames: string[], configServer?:
|
|
|
31
30
|
}
|
|
32
31
|
} else if (localConfigPath) {
|
|
33
32
|
rule = await loadLocalConfigRule(ruleName, localConfigPath);
|
|
34
|
-
} else if (localConfigPath) {
|
|
35
|
-
rule = await loadLocalConfigRule(ruleName, localConfigPath);
|
|
36
33
|
} else {
|
|
37
34
|
rule = await loadLocalRule(ruleName);
|
|
38
35
|
}
|
|
@@ -13,7 +13,7 @@ app.use(expressLogger);
|
|
|
13
13
|
|
|
14
14
|
const validInput = (value: string): boolean => {
|
|
15
15
|
// Ensure input contains only alphanumeric characters, hyphens, and underscores
|
|
16
|
-
const validName = /^[a-zA-Z0-9-_
|
|
16
|
+
const validName = /^[a-zA-Z0-9-_-]{1,50}$/;
|
|
17
17
|
return validName.test(value);
|
|
18
18
|
}
|
|
19
19
|
|
|
@@ -36,7 +36,7 @@ app.get('/archetypes', (req, res) => {
|
|
|
36
36
|
app.get('/archetypes/:archetype/rules', async (req, res) => {
|
|
37
37
|
logger.info(`serving rules for archetype: ${req.params.archetype}`);
|
|
38
38
|
const archetype = req.params.archetype;
|
|
39
|
-
if (validInput(archetype) &&
|
|
39
|
+
if (validInput(archetype) && Object.prototype.hasOwnProperty.call(archetypes, archetype) && archetypes[archetype].rules) {
|
|
40
40
|
const rules = await loadRules(archetype, archetypes[archetype].rules);
|
|
41
41
|
res.json(rules);
|
|
42
42
|
} else {
|
|
@@ -48,7 +48,7 @@ app.get('/archetypes/:archetype/rules/:rule', async (req, res) => {
|
|
|
48
48
|
logger.info(`serving rule ${req.params.rule} for archetype ${req.params.archetype}..`);
|
|
49
49
|
const archetype = req.params.archetype;
|
|
50
50
|
const rule = req.params.rule;
|
|
51
|
-
if (validInput(archetype) && validInput(rule) &&
|
|
51
|
+
if (validInput(archetype) && validInput(rule) && Object.prototype.hasOwnProperty.call(archetypes, archetype) && archetypes[archetype].rules.includes(rule)) {
|
|
52
52
|
const rules = await loadRules(archetype, archetypes[archetype].rules);
|
|
53
53
|
const ruleJson = rules.find((r) => r.name === rule);
|
|
54
54
|
res.json(ruleJson);
|
package/src/utils/config.ts
CHANGED
|
@@ -29,7 +29,7 @@ export class ConfigManager {
|
|
|
29
29
|
return ConfigManager.instance;
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
public async initialize(archetype
|
|
32
|
+
public async initialize(archetype = 'node-fullstack', configServer?: string, localConfigPath?: string): Promise<void> {
|
|
33
33
|
this.config = archetypes[archetype] || archetypes['node-fullstack'];
|
|
34
34
|
this.configServer = configServer || '';
|
|
35
35
|
this.localConfigPath = localConfigPath || '';
|