x-fidelity 2.6.0 → 2.7.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 +12 -0
- package/dist/core/engine/analyzer.js +7 -1
- package/dist/core/engine/engineRunner.js +2 -2
- package/dist/core/engine/engineRunner.test.js +1 -0
- package/dist/utils/configManager.js +34 -0
- package/package.json +1 -1
- package/src/core/engine/analyzer.ts +8 -1
- package/src/core/engine/engineRunner.test.ts +1 -0
- package/src/core/engine/engineRunner.ts +3 -3
- package/src/types/typeDefs.ts +8 -0
- package/src/utils/configManager.ts +40 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
# [2.7.0](https://github.com/zotoio/x-fidelity/compare/v2.6.0...v2.7.0) (2024-08-22)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* Add missing `repoUrl` property to `mockParams` object ([e94114d](https://github.com/zotoio/x-fidelity/commit/e94114dced7c9f6b4f53caa034937d07313abd71))
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### Features
|
|
10
|
+
|
|
11
|
+
* **exemption process:** allow a repo to have a timelimited waiver for a given rule ([42d4b7d](https://github.com/zotoio/x-fidelity/commit/42d4b7de181131bbc3400710fc86876f4f3d8748))
|
|
12
|
+
|
|
1
13
|
# [2.6.0](https://github.com/zotoio/x-fidelity/compare/v2.5.0...v2.6.0) (2024-08-22)
|
|
2
14
|
|
|
3
15
|
|
|
@@ -26,7 +26,12 @@ function analyzeCodebase(params) {
|
|
|
26
26
|
return __awaiter(this, void 0, void 0, function* () {
|
|
27
27
|
const { repoPath, archetype = 'node-fullstack', configServer = '', localConfigPath = '', executionLogPrefix = '' } = params;
|
|
28
28
|
logger_1.logger.info(`STARTING..`);
|
|
29
|
+
// Load exemptions
|
|
30
|
+
if (localConfigPath) {
|
|
31
|
+
yield configManager_1.ConfigManager.loadExemptions(localConfigPath);
|
|
32
|
+
}
|
|
29
33
|
const telemetryData = yield (0, telemetryCollector_1.collectTelemetryData)({ repoPath, configServer });
|
|
34
|
+
const repoUrl = telemetryData.repoUrl;
|
|
30
35
|
// Send telemetry for analysis start
|
|
31
36
|
yield (0, telemetry_1.sendTelemetry)({
|
|
32
37
|
eventType: 'analysisStart',
|
|
@@ -72,7 +77,8 @@ function analyzeCodebase(params) {
|
|
|
72
77
|
fileData,
|
|
73
78
|
installedDependencyVersions,
|
|
74
79
|
minimumDependencyVersions,
|
|
75
|
-
standardStructure
|
|
80
|
+
standardStructure,
|
|
81
|
+
repoUrl
|
|
76
82
|
});
|
|
77
83
|
const finishMsg = `\n==========================\nCHECKS COMPLETED..\n==========================`;
|
|
78
84
|
logger_1.logger.info(finishMsg);
|
|
@@ -14,7 +14,7 @@ const logger_1 = require("../../utils/logger");
|
|
|
14
14
|
const configManager_1 = require("../../utils/configManager");
|
|
15
15
|
function runEngineOnFiles(params) {
|
|
16
16
|
return __awaiter(this, void 0, void 0, function* () {
|
|
17
|
-
const { engine, fileData, installedDependencyVersions, minimumDependencyVersions, standardStructure } = params;
|
|
17
|
+
const { engine, fileData, installedDependencyVersions, minimumDependencyVersions, standardStructure, repoUrl } = params;
|
|
18
18
|
const msg = `\n==========================\nRUNNING FILE CHECKS..\n==========================`;
|
|
19
19
|
logger_1.logger.info(msg);
|
|
20
20
|
const failures = [];
|
|
@@ -41,7 +41,7 @@ function runEngineOnFiles(params) {
|
|
|
41
41
|
results.forEach((result) => {
|
|
42
42
|
var _a, _b;
|
|
43
43
|
logger_1.logger.debug(JSON.stringify(result));
|
|
44
|
-
if (result.result) {
|
|
44
|
+
if (result.result && !configManager_1.ConfigManager.isExempt(repoUrl, result.name)) {
|
|
45
45
|
fileFailures.push({
|
|
46
46
|
ruleFailure: result.name,
|
|
47
47
|
level: (_a = result.event) === null || _a === void 0 ? void 0 : _a.type,
|
|
@@ -63,6 +63,39 @@ class ConfigManager {
|
|
|
63
63
|
return ConfigManager.configs[archetype];
|
|
64
64
|
});
|
|
65
65
|
}
|
|
66
|
+
static loadExemptions(localConfigPath) {
|
|
67
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
68
|
+
const exemptionsPath = path.join(localConfigPath, 'exemptions.json');
|
|
69
|
+
try {
|
|
70
|
+
const exemptionsData = yield fs_1.default.promises.readFile(exemptionsPath, 'utf-8');
|
|
71
|
+
const exemptionsJson = JSON.parse(exemptionsData);
|
|
72
|
+
ConfigManager.exemptions = exemptionsJson.exemptions;
|
|
73
|
+
logger_1.logger.info(`Loaded ${ConfigManager.exemptions.length} exemptions`);
|
|
74
|
+
}
|
|
75
|
+
catch (error) {
|
|
76
|
+
logger_1.logger.warn(`Failed to load exemptions: ${error}`);
|
|
77
|
+
ConfigManager.exemptions = [];
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
static getExemptions() {
|
|
82
|
+
return ConfigManager.exemptions;
|
|
83
|
+
}
|
|
84
|
+
static getExemptionExpirationDate(repoUrl, ruleName) {
|
|
85
|
+
const exemption = ConfigManager.exemptions.find(exemption => exemption.repoUrl === repoUrl &&
|
|
86
|
+
exemption.rule === ruleName);
|
|
87
|
+
return exemption ? new Date(exemption.expirationDate) : undefined;
|
|
88
|
+
}
|
|
89
|
+
static isExempt(repoUrl, ruleName) {
|
|
90
|
+
const now = new Date();
|
|
91
|
+
const result = ConfigManager.exemptions.some(exemption => exemption.repoUrl === repoUrl &&
|
|
92
|
+
exemption.rule === ruleName &&
|
|
93
|
+
new Date(exemption.expirationDate) > now);
|
|
94
|
+
if (result) {
|
|
95
|
+
logger_1.logger.error(`Exempting rule ${ruleName} for repo ${repoUrl} until ${ConfigManager.getExemptionExpirationDate(repoUrl, ruleName)}`);
|
|
96
|
+
}
|
|
97
|
+
return result;
|
|
98
|
+
}
|
|
66
99
|
static initialize(params) {
|
|
67
100
|
return __awaiter(this, void 0, void 0, function* () {
|
|
68
101
|
var _a;
|
|
@@ -176,3 +209,4 @@ class ConfigManager {
|
|
|
176
209
|
}
|
|
177
210
|
exports.ConfigManager = ConfigManager;
|
|
178
211
|
ConfigManager.configs = {};
|
|
212
|
+
ConfigManager.exemptions = [];
|
package/package.json
CHANGED
|
@@ -20,7 +20,13 @@ export async function analyzeCodebase(params: AnalyzeCodebaseParams): Promise<Re
|
|
|
20
20
|
|
|
21
21
|
logger.info(`STARTING..`);
|
|
22
22
|
|
|
23
|
+
// Load exemptions
|
|
24
|
+
if (localConfigPath) {
|
|
25
|
+
await ConfigManager.loadExemptions(localConfigPath);
|
|
26
|
+
}
|
|
27
|
+
|
|
23
28
|
const telemetryData = await collectTelemetryData({ repoPath, configServer});
|
|
29
|
+
const repoUrl = telemetryData.repoUrl;
|
|
24
30
|
|
|
25
31
|
// Send telemetry for analysis start
|
|
26
32
|
await sendTelemetry({
|
|
@@ -76,7 +82,8 @@ export async function analyzeCodebase(params: AnalyzeCodebaseParams): Promise<Re
|
|
|
76
82
|
fileData,
|
|
77
83
|
installedDependencyVersions,
|
|
78
84
|
minimumDependencyVersions,
|
|
79
|
-
standardStructure
|
|
85
|
+
standardStructure,
|
|
86
|
+
repoUrl
|
|
80
87
|
});
|
|
81
88
|
|
|
82
89
|
const finishMsg = `\n==========================\nCHECKS COMPLETED..\n==========================`;
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { EngineResult, RuleResult } from 'json-rules-engine';
|
|
2
2
|
import { ScanResult, RuleFailure } from '../../types/typeDefs';
|
|
3
3
|
import { logger } from '../../utils/logger';
|
|
4
|
-
import { REPO_GLOBAL_CHECK } from '../../utils/configManager';
|
|
4
|
+
import { REPO_GLOBAL_CHECK, ConfigManager } from '../../utils/configManager';
|
|
5
5
|
|
|
6
6
|
import { RunEngineOnFilesParams } from '../../types/typeDefs';
|
|
7
7
|
|
|
8
8
|
export async function runEngineOnFiles(params: RunEngineOnFilesParams): Promise<ScanResult[]> {
|
|
9
|
-
const { engine, fileData, installedDependencyVersions, minimumDependencyVersions, standardStructure } = params;
|
|
9
|
+
const { engine, fileData, installedDependencyVersions, minimumDependencyVersions, standardStructure, repoUrl } = params;
|
|
10
10
|
const msg = `\n==========================\nRUNNING FILE CHECKS..\n==========================`;
|
|
11
11
|
logger.info(msg);
|
|
12
12
|
const failures: ScanResult[] = [];
|
|
@@ -32,7 +32,7 @@ export async function runEngineOnFiles(params: RunEngineOnFilesParams): Promise<
|
|
|
32
32
|
const { results }: EngineResult = await engine.run(facts);
|
|
33
33
|
results.forEach((result: RuleResult) => {
|
|
34
34
|
logger.debug(JSON.stringify(result));
|
|
35
|
-
if (result.result) {
|
|
35
|
+
if (result.result && !ConfigManager.isExempt(repoUrl, result.name)) {
|
|
36
36
|
fileFailures.push({
|
|
37
37
|
ruleFailure: result.name,
|
|
38
38
|
level: result.event?.type,
|
package/src/types/typeDefs.ts
CHANGED
|
@@ -53,6 +53,14 @@ export interface RunEngineOnFilesParams {
|
|
|
53
53
|
installedDependencyVersions: any;
|
|
54
54
|
minimumDependencyVersions: any;
|
|
55
55
|
standardStructure: any;
|
|
56
|
+
repoUrl: string;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export interface Exemption {
|
|
60
|
+
repoUrl: string;
|
|
61
|
+
rule: string;
|
|
62
|
+
expirationDate: string;
|
|
63
|
+
reason: string;
|
|
56
64
|
}
|
|
57
65
|
|
|
58
66
|
export interface AnalyzeCodebaseParams {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { axiosClient } from "./axiosClient";
|
|
2
2
|
import { logger, setLogPrefix } from "./logger";
|
|
3
|
-
import { ArchetypeConfig, ExecutionConfig, GetConfigParams, InitializeParams, LoadLocalConfigParams, RuleConfig } from "../types/typeDefs";
|
|
3
|
+
import { ArchetypeConfig, ExecutionConfig, GetConfigParams, InitializeParams, LoadLocalConfigParams, RuleConfig, Exemption } from "../types/typeDefs";
|
|
4
4
|
import { archetypes } from "../archetypes";
|
|
5
5
|
import { options } from '../core/cli';
|
|
6
6
|
import fs from 'fs';
|
|
@@ -12,6 +12,7 @@ export const REPO_GLOBAL_CHECK = 'REPO_GLOBAL_CHECK';
|
|
|
12
12
|
|
|
13
13
|
export class ConfigManager {
|
|
14
14
|
private static configs: { [key: string]: ExecutionConfig } = {};
|
|
15
|
+
private static exemptions: Exemption[] = [];
|
|
15
16
|
|
|
16
17
|
public static getLoadedConfigs(): string[] {
|
|
17
18
|
return Object.keys(ConfigManager.configs);
|
|
@@ -31,6 +32,44 @@ export class ConfigManager {
|
|
|
31
32
|
return ConfigManager.configs[archetype];
|
|
32
33
|
}
|
|
33
34
|
|
|
35
|
+
public static async loadExemptions(localConfigPath: string): Promise<void> {
|
|
36
|
+
const exemptionsPath = path.join(localConfigPath, 'exemptions.json');
|
|
37
|
+
try {
|
|
38
|
+
const exemptionsData = await fs.promises.readFile(exemptionsPath, 'utf-8');
|
|
39
|
+
const exemptionsJson = JSON.parse(exemptionsData);
|
|
40
|
+
ConfigManager.exemptions = exemptionsJson.exemptions;
|
|
41
|
+
logger.info(`Loaded ${ConfigManager.exemptions.length} exemptions`);
|
|
42
|
+
} catch (error) {
|
|
43
|
+
logger.warn(`Failed to load exemptions: ${error}`);
|
|
44
|
+
ConfigManager.exemptions = [];
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
public static getExemptions(): Exemption[] {
|
|
49
|
+
return ConfigManager.exemptions;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
public static getExemptionExpirationDate(repoUrl: string, ruleName: string): Date | undefined {
|
|
53
|
+
const exemption = ConfigManager.exemptions.find(exemption =>
|
|
54
|
+
exemption.repoUrl === repoUrl &&
|
|
55
|
+
exemption.rule === ruleName
|
|
56
|
+
);
|
|
57
|
+
return exemption ? new Date(exemption.expirationDate) : undefined;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
public static isExempt(repoUrl: string, ruleName: string): boolean {
|
|
61
|
+
const now = new Date();
|
|
62
|
+
const result = ConfigManager.exemptions.some(exemption =>
|
|
63
|
+
exemption.repoUrl === repoUrl &&
|
|
64
|
+
exemption.rule === ruleName &&
|
|
65
|
+
new Date(exemption.expirationDate) > now
|
|
66
|
+
);
|
|
67
|
+
if (result) {
|
|
68
|
+
logger.error(`Exempting rule ${ruleName} for repo ${repoUrl} until ${ConfigManager.getExemptionExpirationDate(repoUrl, ruleName)}`);
|
|
69
|
+
}
|
|
70
|
+
return result;
|
|
71
|
+
}
|
|
72
|
+
|
|
34
73
|
private static async initialize(params: InitializeParams): Promise<ExecutionConfig> {
|
|
35
74
|
const { archetype, logPrefix } = params;
|
|
36
75
|
const configServer = options.configServer;
|