x-fidelity 1.4.1 → 1.5.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 +15 -0
- package/README.md +117 -121
- package/dist/archetypes/index.js +6 -3
- package/dist/core/cli.js +24 -20
- package/dist/core/engine.js +88 -15
- package/dist/core/engine.test.js +21 -46
- package/dist/facts/openaiAnalysisFacts.js +2 -3
- package/dist/facts/openaiAnalysisFacts.test.js +2 -2
- package/dist/facts/repoDependencyFacts.js +5 -1
- package/dist/index.js +32 -17
- package/dist/operators/fileContains.test.js +37 -2
- package/dist/operators/openaiAnalysisHighSeverity.js +11 -7
- package/dist/operators/openaiAnalysisHighSeverity.test.js +41 -0
- package/dist/rules/index.js +10 -6
- package/dist/rules/index.test.js +11 -2
- package/dist/rules/{noDatabases-rule.json → noDatabases-iterative-rule.json} +2 -2
- package/dist/rules/{nonStandardDirectoryStructure-rule.json → nonStandardDirectoryStructure-global-rule.json} +3 -3
- package/{src/rules/openaiAnalysisA11y-rule.json → dist/rules/openaiAnalysisA11y-global-rule.json} +1 -1
- package/{src/rules/openaiAnalysisTop5-rule.json → dist/rules/openaiAnalysisTop5-global-rule.json} +1 -1
- package/dist/rules/{outdatedFramework-rule.json → outdatedFramework-global-rule.json} +4 -3
- package/dist/rules/{sensitiveLogging-rule.json → sensitiveLogging-iterative-rule.json} +4 -2
- package/dist/server/configServer.js +26 -15
- package/dist/server/expressLogger.js +36 -0
- package/dist/utils/config.js +9 -4
- package/dist/utils/logger.js +26 -4
- package/dist/utils/telemetry.js +29 -0
- package/dist/xfidelity +32 -17
- package/package.json +7 -4
- package/src/archetypes/index.ts +7 -4
- package/src/core/cli.ts +28 -25
- package/src/core/engine.test.ts +21 -53
- package/src/core/engine.ts +111 -23
- package/src/facts/openaiAnalysisFacts.test.ts +2 -2
- package/src/facts/openaiAnalysisFacts.ts +2 -3
- package/src/facts/repoDependencyFacts.ts +9 -2
- package/src/facts/repoFilesystemFacts.test.ts +1 -1
- package/src/facts/repoFilesystemFacts.ts +1 -1
- package/src/index.ts +30 -16
- package/src/operators/fileContains.test.ts +43 -2
- package/src/operators/fileContains.ts +1 -1
- package/src/operators/index.ts +1 -1
- package/src/operators/nonStandardDirectoryStructure.ts +2 -1
- package/src/operators/openaiAnalysisHighSeverity.test.ts +46 -0
- package/src/operators/openaiAnalysisHighSeverity.ts +14 -8
- package/src/operators/outdatedFramework.ts +1 -1
- package/src/rules/index.test.ts +12 -2
- package/src/rules/index.ts +13 -7
- package/src/rules/{noDatabases-rule.json → noDatabases-iterative-rule.json} +2 -2
- package/src/rules/{nonStandardDirectoryStructure-rule.json → nonStandardDirectoryStructure-global-rule.json} +3 -3
- package/{dist/rules/openaiAnalysisA11y-rule.json → src/rules/openaiAnalysisA11y-global-rule.json} +1 -1
- package/{dist/rules/openaiAnalysisTop5-rule.json → src/rules/openaiAnalysisTop5-global-rule.json} +1 -1
- package/src/rules/{outdatedFramework-rule.json → outdatedFramework-global-rule.json} +4 -3
- package/src/rules/{sensitiveLogging-rule.json → sensitiveLogging-iterative-rule.json} +4 -2
- package/src/server/configServer.ts +27 -17
- package/src/server/expressLogger.ts +37 -0
- package/src/utils/config.ts +10 -6
- package/src/utils/logger.ts +30 -4
- package/src/utils/telemetry.ts +23 -0
- /package/dist/{typeDefs.js → types/typeDefs.js} +0 -0
- /package/src/{typeDefs.ts → types/typeDefs.ts} +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,18 @@
|
|
|
1
|
+
# [1.5.0](https://github.com/zotoio/x-fidelity/compare/v1.4.1...v1.5.0) (2024-07-24)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* **logger:** console transport ([69c62b1](https://github.com/zotoio/x-fidelity/commit/69c62b1b9859bc71801c576b969e9698ecc53a61))
|
|
7
|
+
* **log:** remove console and add process exit codes ([3ec801f](https://github.com/zotoio/x-fidelity/commit/3ec801fe3f913452d1f6ef14723fd0f8cb3a8439))
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
### Features
|
|
11
|
+
|
|
12
|
+
* **telemetry:** basic start ([47faf3b](https://github.com/zotoio/x-fidelity/commit/47faf3b5f7a88afc07be1b256a611ce46ef64872))
|
|
13
|
+
* **telemetry:** basics including tracing ([f07f6b4](https://github.com/zotoio/x-fidelity/commit/f07f6b4d7ad886008cd20d3ccfa80e0e980bfd34))
|
|
14
|
+
* **telemetry:** request ids ([e19489e](https://github.com/zotoio/x-fidelity/commit/e19489eadd08b06119244a8e1798c2f22bac898b))
|
|
15
|
+
|
|
1
16
|
## [1.4.1](https://github.com/zotoio/x-fidelity/compare/v1.4.0...v1.4.1) (2024-07-14)
|
|
2
17
|
|
|
3
18
|
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# x-fidelity
|
|
2
2
|
|
|
3
|
-
CLI
|
|
3
|
+
x-fidelity is an advanced CLI tool designed to enforce opinionated framework adherence checks within a codebase. It provides a flexible and extensible way to ensure your projects follow specific standards and best practices.
|
|
4
4
|
|
|
5
5
|
```
|
|
6
6
|
=====================================
|
|
@@ -16,107 +16,131 @@ CLI for opinionated framework adherence checks
|
|
|
16
16
|
-------------------------------------
|
|
17
17
|
```
|
|
18
18
|
|
|
19
|
-
##
|
|
19
|
+
## Table of Contents
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
1. [Intent and Purpose](#intent-and-purpose)
|
|
22
|
+
2. [Key Features](#key-features)
|
|
23
|
+
3. [Installation](#installation)
|
|
24
|
+
4. [Usage](#usage)
|
|
25
|
+
5. [Configuration](#configuration)
|
|
26
|
+
6. [Extending x-fidelity](#extending-x-fidelity)
|
|
27
|
+
7. [OpenAI Integration](#openai-integration)
|
|
28
|
+
8. [Hosting Config Servers](#hosting-config-servers)
|
|
29
|
+
9. [Best Practices](#best-practices)
|
|
30
|
+
10. [Contributing](#contributing)
|
|
31
|
+
11. [License](#license)
|
|
22
32
|
|
|
23
|
-
##
|
|
33
|
+
## Intent and Purpose
|
|
24
34
|
|
|
25
|
-
-
|
|
26
|
-
|
|
27
|
-
-
|
|
28
|
-
-
|
|
29
|
-
-
|
|
35
|
+
x-fidelity aims to streamline the process of maintaining code quality and consistency across projects. By providing a flexible, rule-based system, it allows teams to:
|
|
36
|
+
|
|
37
|
+
- Enforce coding standards and best practices
|
|
38
|
+
- Ensure consistent project structures
|
|
39
|
+
- Maintain up-to-date dependencies
|
|
40
|
+
- Catch potential issues early in the development process
|
|
41
|
+
- Integrate advanced code analysis using AI (via OpenAI)
|
|
42
|
+
|
|
43
|
+
The tool is designed to be highly customizable, allowing teams to define their own archetypes, rules, and checks tailored to their specific needs and tech stacks.
|
|
44
|
+
|
|
45
|
+
## Key Features
|
|
46
|
+
|
|
47
|
+
- **Flexible Archetype System**: Define custom project archetypes with specific rules and configurations.
|
|
48
|
+
- **Customizable Rules**: Create and apply rules for various aspects of your codebase.
|
|
49
|
+
- **Directory Structure Validation**: Ensure your project follows a predefined directory structure.
|
|
50
|
+
- **Dependency Version Checking**: Verify that your project uses up-to-date dependencies.
|
|
51
|
+
- **Content Analysis**: Search for specific patterns or strings within your codebase.
|
|
52
|
+
- **Remote Configuration**: Fetch configurations from a remote server for centralized management.
|
|
53
|
+
- **OpenAI Integration**: Leverage AI for advanced code analysis and suggestions.
|
|
54
|
+
- **Extensible Architecture**: Easily add new operators, facts, and rules to suit your needs.
|
|
30
55
|
|
|
31
56
|
## Installation
|
|
32
57
|
|
|
33
|
-
Install x-fidelity
|
|
58
|
+
Install x-fidelity using Node.js 18+ and Yarn:
|
|
34
59
|
|
|
35
60
|
```sh
|
|
36
61
|
yarn global add x-fidelity
|
|
37
62
|
export PATH="$PATH:$(yarn global bin)"
|
|
38
63
|
```
|
|
39
64
|
|
|
40
|
-
|
|
65
|
+
For persistent access, add the PATH line to your `~/.bashrc` or `~/.zshrc` file.
|
|
41
66
|
|
|
42
67
|
## Usage
|
|
43
68
|
|
|
44
|
-
###
|
|
69
|
+
### Basic Usage
|
|
45
70
|
|
|
46
|
-
|
|
71
|
+
Run x-fidelity in your project directory:
|
|
47
72
|
|
|
48
73
|
```sh
|
|
49
74
|
xfidelity
|
|
50
|
-
|
|
51
|
-
# you can use the following options for more advanced setups such as the remote config server
|
|
52
|
-
xfidelity [-d --dir <directory>] [-c --configUrl <url>] [-a --archtype <archetype>]
|
|
53
75
|
```
|
|
54
76
|
|
|
55
|
-
|
|
56
|
-
- `-c --configUrl <url>`: (Optional) The URL to fetch the configuration from.
|
|
57
|
-
- `-a --archetype <archetype>`: (Optional) The archetype to use for analysis. 'node-fullstack' is the default, or 'java-microservice' and these are extensible)
|
|
77
|
+
### Advanced Usage
|
|
58
78
|
|
|
59
|
-
|
|
79
|
+
Use command-line options for more control:
|
|
60
80
|
|
|
61
81
|
```sh
|
|
62
|
-
xfidelity --
|
|
82
|
+
xfidelity [-d --dir <directory>] [-c --configServer <url>] [-a --archetype <archetype>] [-m --mode <mode>] [-p --port <port>]
|
|
63
83
|
```
|
|
64
84
|
|
|
65
|
-
|
|
85
|
+
- `-d --dir <directory>`: Specify the root directory to analyze (default: current directory)
|
|
86
|
+
- `-c --configServer <url>`: URL to fetch the configuration from
|
|
87
|
+
- `-a --archetype <archetype>`: Archetype to use for analysis (default: 'node-fullstack')
|
|
88
|
+
- `-m --mode <mode>`: Run mode: 'cli' or 'server' (default: 'cli')
|
|
89
|
+
- `-p --port <port>`: Port number for server mode (default: 8888)
|
|
90
|
+
|
|
91
|
+
Examples:
|
|
66
92
|
|
|
67
93
|
```sh
|
|
68
|
-
|
|
69
|
-
|
|
94
|
+
# Use remote config server
|
|
95
|
+
xfidelity --configServer https://localhost:8888
|
|
70
96
|
|
|
71
|
-
|
|
97
|
+
# Analyze parent directory with java-microservice archetype
|
|
98
|
+
xfidelity -d .. -a java-microservice -c https://localhost:8888
|
|
72
99
|
|
|
73
|
-
|
|
100
|
+
# Run in server mode with custom port
|
|
101
|
+
xfidelity --mode server --port 9999
|
|
74
102
|
|
|
75
|
-
|
|
103
|
+
```
|
|
76
104
|
|
|
77
|
-
|
|
105
|
+
### Configuration Server
|
|
78
106
|
|
|
79
|
-
|
|
80
|
-
2. Run the following command:
|
|
107
|
+
Start the built-in configuration server:
|
|
81
108
|
|
|
82
109
|
```sh
|
|
83
|
-
yarn start-
|
|
110
|
+
yarn start-server
|
|
84
111
|
```
|
|
85
112
|
|
|
86
|
-
|
|
113
|
+
Or use the CLI:
|
|
114
|
+
|
|
87
115
|
```sh
|
|
88
|
-
|
|
116
|
+
xfidelity --mode server
|
|
89
117
|
```
|
|
90
118
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
To enable OpenAI features for experimental LLM-based codebase analysis:
|
|
94
|
-
|
|
95
|
-
1. Sign up for a developer account at [OpenAI](https://platform.openai.com).
|
|
96
|
-
2. Navigate to the API section and generate a new API key.
|
|
97
|
-
3. Set the `OPENAI_API_KEY` environment variable:
|
|
119
|
+
Set a custom port:
|
|
98
120
|
|
|
99
121
|
```sh
|
|
100
|
-
|
|
122
|
+
xfidelity --mode server --port 9999
|
|
101
123
|
```
|
|
102
|
-
|
|
124
|
+
|
|
125
|
+
You can also set the port using an environment variable:
|
|
126
|
+
|
|
103
127
|
```sh
|
|
104
|
-
export
|
|
128
|
+
export XFI_SERVER_PORT=8888
|
|
129
|
+
xfidelity --mode server
|
|
105
130
|
```
|
|
106
|
-
Note that not all models consistently return parseable JSON results, so some experimentation is required.
|
|
107
|
-
|
|
108
|
-
> [!IMPORTANT]
|
|
109
|
-
> Using OpenAI's API may incur costs. Please refer to OpenAI's pricing page for more details.
|
|
110
|
-
>
|
|
111
|
-
>The 'collectOpenaiAnalysisFacts' function will concatenate all files that are not blacklisted but are included in the whitelist and send this to OpenAI. Ensure you consider any sensitive data that may be sent, and the cost based on the token count this will be per rule check that is executed.
|
|
112
131
|
|
|
113
132
|
## Configuration
|
|
114
133
|
|
|
115
|
-
|
|
134
|
+
x-fidelity uses archetypes to define project-specific configurations. Archetypes specify:
|
|
116
135
|
|
|
117
|
-
|
|
136
|
+
- Rules to apply
|
|
137
|
+
- Operators to use
|
|
138
|
+
- Facts to gather
|
|
139
|
+
- Dependency version requirements
|
|
140
|
+
- Standard directory structure
|
|
141
|
+
- File patterns to include or exclude
|
|
118
142
|
|
|
119
|
-
|
|
143
|
+
Example archetype structure:
|
|
120
144
|
|
|
121
145
|
```typescript
|
|
122
146
|
interface ArchetypeConfig {
|
|
@@ -132,54 +156,19 @@ interface ArchetypeConfig {
|
|
|
132
156
|
}
|
|
133
157
|
```
|
|
134
158
|
|
|
135
|
-
|
|
136
|
-
- `operators`: An array of operator names used in the rules.
|
|
137
|
-
- `facts`: An array of fact provider names used to gather information about the codebase.
|
|
138
|
-
- `config`: Additional configuration specific to the archetype:
|
|
139
|
-
- `minimumDependencyVersions`: Minimum required versions for dependencies.
|
|
140
|
-
- `standardStructure`: Expected directory structure for the project.
|
|
141
|
-
- `blacklistPatterns`: Patterns for files/directories to be ignored.
|
|
142
|
-
- `whitelistPatterns`: Patterns for files/directories to be included.
|
|
143
|
-
|
|
144
|
-
### Rule Structure
|
|
145
|
-
|
|
146
|
-
Each rule is defined in a JSON file with the following structure:
|
|
147
|
-
|
|
148
|
-
```json
|
|
149
|
-
{
|
|
150
|
-
"name": "ruleName",
|
|
151
|
-
"conditions": {
|
|
152
|
-
"all": [
|
|
153
|
-
{
|
|
154
|
-
"fact": "factName",
|
|
155
|
-
"operator": "operatorName",
|
|
156
|
-
"value": "expectedValue"
|
|
157
|
-
}
|
|
158
|
-
]
|
|
159
|
-
},
|
|
160
|
-
"event": {
|
|
161
|
-
"type": "violation",
|
|
162
|
-
"params": {
|
|
163
|
-
"message": "Error message when the rule fails"
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
```
|
|
168
|
-
|
|
169
|
-
## Creating New Archetypes
|
|
159
|
+
## Extending x-fidelity
|
|
170
160
|
|
|
171
|
-
|
|
161
|
+
x-fidelity is designed to be highly extensible:
|
|
172
162
|
|
|
173
|
-
1.
|
|
174
|
-
2.
|
|
175
|
-
3.
|
|
176
|
-
4.
|
|
177
|
-
5. If needed, create custom fact providers in the `src/facts` directory.
|
|
178
|
-
6. Update the `src/archetypes/index.ts` file to include your new archetype.
|
|
163
|
+
1. **Custom Rules**: Add new JSON rule files in `src/rules`.
|
|
164
|
+
2. **Custom Operators**: Implement new operators in `src/operators` and add them to `src/operators/index.ts`.
|
|
165
|
+
3. **Custom Facts**: Create new fact providers in `src/facts` and add them to `src/facts/index.ts`.
|
|
166
|
+
4. **New Archetypes**: Define new archetypes in `src/archetypes` and include them in `src/archetypes/index.ts`.
|
|
179
167
|
|
|
180
|
-
Example of a new archetype:
|
|
168
|
+
Example of creating a new archetype:
|
|
181
169
|
|
|
182
170
|
```typescript
|
|
171
|
+
// src/archetypes/myNewArchetype.ts
|
|
183
172
|
export const myNewArchetype: ArchetypeConfig = {
|
|
184
173
|
rules: ['myCustomRule', 'standardRule1', 'standardRule2'],
|
|
185
174
|
operators: ['myCustomOperator', 'standardOperator1'],
|
|
@@ -201,41 +190,43 @@ export const myNewArchetype: ArchetypeConfig = {
|
|
|
201
190
|
};
|
|
202
191
|
```
|
|
203
192
|
|
|
204
|
-
##
|
|
193
|
+
## OpenAI Integration
|
|
194
|
+
|
|
195
|
+
To enable AI-powered code analysis:
|
|
205
196
|
|
|
206
|
-
|
|
197
|
+
1. Sign up for an [OpenAI API key](https://platform.openai.com).
|
|
198
|
+
2. Set environment variables:
|
|
207
199
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
200
|
+
```sh
|
|
201
|
+
export OPENAI_API_KEY=your_openai_api_key
|
|
202
|
+
export OPENAI_MODEL=gpt-4 # Optional, default is gpt-4o
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
> [!IMPORTANT]
|
|
206
|
+
> Be aware of potential costs and data privacy concerns when using OpenAI's API.
|
|
212
207
|
|
|
213
208
|
## Hosting Config Servers
|
|
214
209
|
|
|
215
210
|
To host a config server for x-fidelity:
|
|
216
211
|
|
|
217
|
-
1. Set up a Node.js server
|
|
218
|
-
2. Implement endpoints
|
|
219
|
-
3. Ensure
|
|
220
|
-
4. Use HTTPS
|
|
221
|
-
5.
|
|
222
|
-
6. Consider using a CDN for global distribution and lower latency.
|
|
212
|
+
1. Set up a Node.js server (e.g., Express.js)
|
|
213
|
+
2. Implement endpoints for archetype configurations and rules
|
|
214
|
+
3. Ensure security, scalability, and performance
|
|
215
|
+
4. Use HTTPS and implement proper authentication
|
|
216
|
+
5. Consider using a CDN for global distribution
|
|
223
217
|
|
|
224
|
-
Example server setup
|
|
218
|
+
Example server setup:
|
|
225
219
|
|
|
226
220
|
```javascript
|
|
227
221
|
const express = require('express');
|
|
228
222
|
const app = express();
|
|
229
223
|
|
|
230
224
|
app.get('/archetypes/:archetype', (req, res) => {
|
|
231
|
-
|
|
232
|
-
// Fetch and return the archetype configuration
|
|
225
|
+
// Fetch and return archetype configuration
|
|
233
226
|
});
|
|
234
227
|
|
|
235
228
|
app.get('/archetypes/:archetype/rules/:rule', (req, res) => {
|
|
236
|
-
|
|
237
|
-
const rule = req.params.rule;
|
|
238
|
-
// Fetch and return the specific rule for the archetype
|
|
229
|
+
// Fetch and return specific rule
|
|
239
230
|
});
|
|
240
231
|
|
|
241
232
|
app.listen(8888, () => {
|
|
@@ -243,15 +234,20 @@ app.listen(8888, () => {
|
|
|
243
234
|
});
|
|
244
235
|
```
|
|
245
236
|
|
|
246
|
-
Best
|
|
237
|
+
## Best Practices
|
|
238
|
+
|
|
239
|
+
1. **Version Control**: Keep your x-fidelity configurations in version control.
|
|
240
|
+
2. **Continuous Integration**: Integrate x-fidelity checks into your CI/CD pipeline.
|
|
241
|
+
3. **Regular Updates**: Keep your archetypes, rules, and dependencies up to date.
|
|
242
|
+
4. **Documentation**: Document custom rules, operators, and archetypes for your team.
|
|
243
|
+
5. **Gradual Implementation**: When introducing x-fidelity to an existing project, start with basic checks and gradually increase strictness.
|
|
244
|
+
6. **Team Alignment**: Ensure your team understands and agrees on the rules being enforced.
|
|
245
|
+
7. **Performance**: Be mindful of the performance impact, especially for large codebases.
|
|
246
|
+
|
|
247
|
+
## Contributing
|
|
247
248
|
|
|
248
|
-
-
|
|
249
|
-
- Implement proper error handling and logging.
|
|
250
|
-
- Set up monitoring and alerting for the server.
|
|
251
|
-
- Regularly update and maintain the server and its dependencies.
|
|
252
|
-
- Implement rate limiting to prevent abuse.
|
|
253
|
-
- Consider using containerization (e.g., Docker) for easy deployment and scaling.
|
|
249
|
+
Contributions to x-fidelity are welcome! Please refer to the `CONTRIBUTING.md` file for guidelines on how to contribute to this project.
|
|
254
250
|
|
|
255
251
|
## License
|
|
256
252
|
|
|
257
|
-
This project is licensed under the MIT License.
|
|
253
|
+
This project is licensed under the MIT License. See the `LICENSE` file for details.
|
package/dist/archetypes/index.js
CHANGED
|
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.archetypes = void 0;
|
|
4
4
|
exports.archetypes = {
|
|
5
5
|
'node-fullstack': {
|
|
6
|
-
rules: ['sensitiveLogging', 'outdatedFramework', 'noDatabases', 'nonStandardDirectoryStructure', 'openaiAnalysisTop5', 'openaiAnalysisA11y'],
|
|
6
|
+
rules: ['sensitiveLogging-iterative', 'outdatedFramework-global', 'noDatabases-iterative', 'nonStandardDirectoryStructure-global', 'openaiAnalysisTop5-global', 'openaiAnalysisA11y-global'],
|
|
7
7
|
operators: ['fileContains', 'outdatedFramework', 'nonStandardDirectoryStructure', 'openaiAnalysisHighSeverity'],
|
|
8
8
|
facts: ['repoFilesystemFacts', 'repoDependencyFacts', 'openaiAnalysisFacts'],
|
|
9
9
|
config: {
|
|
@@ -14,9 +14,12 @@ exports.archetypes = {
|
|
|
14
14
|
standardStructure: {
|
|
15
15
|
app: {
|
|
16
16
|
frontend: null,
|
|
17
|
-
common: null,
|
|
18
17
|
server: null
|
|
19
18
|
}
|
|
19
|
+
// app: {
|
|
20
|
+
// frontend: 'required',
|
|
21
|
+
// server: 'required'
|
|
22
|
+
// }
|
|
20
23
|
},
|
|
21
24
|
blacklistPatterns: [
|
|
22
25
|
'.*\\/\\..*', // dot files
|
|
@@ -29,7 +32,7 @@ exports.archetypes = {
|
|
|
29
32
|
}
|
|
30
33
|
},
|
|
31
34
|
'java-microservice': {
|
|
32
|
-
rules: ['sensitiveLogging', 'outdatedFramework', 'noDatabases', 'nonStandardDirectoryStructure'],
|
|
35
|
+
rules: ['sensitiveLogging-iterative', 'outdatedFramework-global', 'noDatabases-iterative', 'nonStandardDirectoryStructure-global'],
|
|
33
36
|
operators: ['fileContains', 'outdatedFramework', 'nonStandardDirectoryStructure'],
|
|
34
37
|
facts: ['repoFilesystemFacts', 'repoDependencyFacts'],
|
|
35
38
|
config: {
|
package/dist/core/cli.js
CHANGED
|
@@ -3,6 +3,20 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.options = void 0;
|
|
4
4
|
const logger_1 = require("../utils/logger");
|
|
5
5
|
const commander_1 = require("commander");
|
|
6
|
+
// Ensure logger is initialized
|
|
7
|
+
if (!logger_1.logger || typeof logger_1.logger.info !== 'function') {
|
|
8
|
+
console.error('Logger is not properly initialized');
|
|
9
|
+
process.exit(1);
|
|
10
|
+
}
|
|
11
|
+
commander_1.program
|
|
12
|
+
.option("-d, --dir <directory>", "The checkout directory to analyze", ".")
|
|
13
|
+
.option("-a, --archetype <archetype>", "The archetype to use for analysis", "node-fullstack")
|
|
14
|
+
.option("-c, --configServer <configServer>", "The config server URL for fetching remote archetype configurations and rules")
|
|
15
|
+
.option("-m, --mode <mode>", "Run mode: 'cli' or 'server'", "cli")
|
|
16
|
+
.option("-p, --port <port>", "Port number for server mode", "8888");
|
|
17
|
+
commander_1.program.parse();
|
|
18
|
+
const options = commander_1.program.opts();
|
|
19
|
+
exports.options = options;
|
|
6
20
|
const banner = (`
|
|
7
21
|
=====================================
|
|
8
22
|
__ __ ________ ______
|
|
@@ -14,26 +28,16 @@ const banner = (`
|
|
|
14
28
|
| ## | ## | ## | ## \\
|
|
15
29
|
\\## \\## \\## \\######
|
|
16
30
|
|
|
17
|
-
|
|
18
|
-
${new Date().toString()}
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
exports.options = options;
|
|
31
|
+
--------------------
|
|
32
|
+
${new Date().toString().slice(0, 24)}
|
|
33
|
+
archetype: ${options.archetype}
|
|
34
|
+
directory: ${process.env.PWD}/${options.dir}
|
|
35
|
+
configServer: ${options.configServer ? options.configServer : 'none'}
|
|
36
|
+
mode: ${options.mode}
|
|
37
|
+
port: ${options.mode === 'server' ? options.port : 'N/A'}
|
|
38
|
+
for available options run: xfidelity --help
|
|
39
|
+
=====================================`);
|
|
40
|
+
logger_1.logger.info(banner);
|
|
28
41
|
// print help if no arguments are passed
|
|
29
42
|
if (commander_1.program.options.length === 0)
|
|
30
43
|
commander_1.program.help();
|
|
31
|
-
if (!options.dir) {
|
|
32
|
-
console.error("Checkout directory not provided. Defaulting to current directory.");
|
|
33
|
-
}
|
|
34
|
-
let msg = `Archetype ${options.archetype}: analysis of: ${process.env.PWD}/${options.dir}`;
|
|
35
|
-
logger_1.logger.info(msg) && console.log(msg);
|
|
36
|
-
msg = `configServer: ${options.configServer ? options.configServer : 'local'}`;
|
|
37
|
-
logger_1.logger.info(msg) && console.log(msg);
|
|
38
|
-
msg = '=====================================';
|
|
39
|
-
logger_1.logger.info(msg) && console.log(msg);
|
package/dist/core/engine.js
CHANGED
|
@@ -19,6 +19,7 @@ const operators_1 = require("../operators");
|
|
|
19
19
|
const facts_1 = require("../facts");
|
|
20
20
|
const rules_1 = require("../rules");
|
|
21
21
|
const config_1 = require("../utils/config");
|
|
22
|
+
const telemetry_1 = require("../utils/telemetry");
|
|
22
23
|
function analyzeCodebase(repoPath_1) {
|
|
23
24
|
return __awaiter(this, arguments, void 0, function* (repoPath, archetype = 'node-fullstack', configServer = '') {
|
|
24
25
|
const configManager = config_1.ConfigManager.getInstance();
|
|
@@ -35,23 +36,34 @@ function analyzeCodebase(repoPath_1) {
|
|
|
35
36
|
const { minimumDependencyVersions, standardStructure } = archetypeConfig.config;
|
|
36
37
|
const openaiSystemPrompt = yield (0, openaiAnalysisFacts_1.collectOpenaiAnalysisFacts)(fileData);
|
|
37
38
|
const engine = new json_rules_engine_1.Engine([], { replaceFactsInEventParams: true, allowUndefinedFacts: true });
|
|
39
|
+
// Send telemetry for analysis start
|
|
40
|
+
yield (0, telemetry_1.sendTelemetry)({
|
|
41
|
+
eventType: 'analysisStart',
|
|
42
|
+
metadata: {
|
|
43
|
+
archetype,
|
|
44
|
+
repoPath,
|
|
45
|
+
fileCount: fileData.length,
|
|
46
|
+
configServer: configServer || 'none'
|
|
47
|
+
},
|
|
48
|
+
timestamp: new Date().toISOString()
|
|
49
|
+
});
|
|
38
50
|
// Add operators to engine
|
|
39
|
-
|
|
51
|
+
logger_1.logger.info(`### loading custom operators..`);
|
|
40
52
|
const operators = yield (0, operators_1.loadOperators)(archetypeConfig.operators);
|
|
41
53
|
operators.forEach((operator) => {
|
|
42
54
|
var _a, _b;
|
|
43
55
|
if (!((_a = operator === null || operator === void 0 ? void 0 : operator.name) === null || _a === void 0 ? void 0 : _a.includes('openai')) || (process.env.OPENAI_API_KEY && ((_b = operator === null || operator === void 0 ? void 0 : operator.name) === null || _b === void 0 ? void 0 : _b.includes('openai')))) {
|
|
44
|
-
|
|
56
|
+
logger_1.logger.info(`adding custom operator: ${operator.name}`);
|
|
45
57
|
engine.addOperator(operator.name, operator.fn);
|
|
46
58
|
}
|
|
47
59
|
});
|
|
48
60
|
// Add rules to engine
|
|
49
|
-
|
|
50
|
-
const rules = yield (0, rules_1.loadRules)(archetype, archetypeConfig.rules, configManager.configServer);
|
|
61
|
+
logger_1.logger.info(`### loading json rules..`);
|
|
62
|
+
const rules = yield (0, rules_1.loadRules)(archetype, archetypeConfig.rules, configManager.configServer, logger_1.logPrefix);
|
|
51
63
|
logger_1.logger.debug(rules);
|
|
52
64
|
rules.forEach((rule) => {
|
|
53
65
|
try {
|
|
54
|
-
|
|
66
|
+
logger_1.logger.info(`adding rule: ${rule === null || rule === void 0 ? void 0 : rule.name}`);
|
|
55
67
|
engine.addRule(rule);
|
|
56
68
|
}
|
|
57
69
|
catch (e) {
|
|
@@ -61,38 +73,52 @@ function analyzeCodebase(repoPath_1) {
|
|
|
61
73
|
});
|
|
62
74
|
engine.on('success', (_a, almanac_1) => __awaiter(this, [_a, almanac_1], void 0, function* ({ type, params }, almanac) {
|
|
63
75
|
if (type === 'violation') {
|
|
64
|
-
|
|
65
|
-
|
|
76
|
+
logger_1.logger.warn(`violation detected: ${JSON.stringify(params)}}`);
|
|
77
|
+
yield (0, telemetry_1.sendTelemetry)({
|
|
78
|
+
eventType: 'violation',
|
|
79
|
+
metadata: Object.assign({ archetype,
|
|
80
|
+
repoPath }, params),
|
|
81
|
+
timestamp: new Date().toISOString()
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
if (type === 'fatality') {
|
|
85
|
+
logger_1.logger.error(`fatality detected: ${JSON.stringify(params)}}`);
|
|
86
|
+
yield (0, telemetry_1.sendTelemetry)({
|
|
87
|
+
eventType: 'fatality',
|
|
88
|
+
metadata: Object.assign({ archetype,
|
|
89
|
+
repoPath }, params),
|
|
90
|
+
timestamp: new Date().toISOString()
|
|
91
|
+
});
|
|
66
92
|
}
|
|
67
93
|
}));
|
|
68
94
|
// Add facts to engine
|
|
69
|
-
|
|
95
|
+
logger_1.logger.info(`### loading facts..`);
|
|
70
96
|
const facts = yield (0, facts_1.loadFacts)(archetypeConfig.facts);
|
|
71
97
|
facts.forEach((fact) => {
|
|
72
98
|
var _a, _b;
|
|
73
99
|
if (!((_a = fact === null || fact === void 0 ? void 0 : fact.name) === null || _a === void 0 ? void 0 : _a.includes('openai')) || (process.env.OPENAI_API_KEY && ((_b = fact === null || fact === void 0 ? void 0 : fact.name) === null || _b === void 0 ? void 0 : _b.includes('openai')))) {
|
|
74
|
-
|
|
100
|
+
logger_1.logger.info(`adding fact: ${fact.name}`);
|
|
75
101
|
engine.addFact(fact.name, fact.fn);
|
|
76
102
|
}
|
|
77
103
|
});
|
|
78
104
|
if (process.env.OPENAI_API_KEY && archetypeConfig.facts.includes('openaiAnalysisFacts')) {
|
|
79
|
-
|
|
105
|
+
logger_1.logger.info(`adding additional openai facts to engine..`);
|
|
80
106
|
engine.addFact('openaiAnalysis', openaiAnalysisFacts_1.openaiAnalysis);
|
|
81
107
|
engine.addFact('openaiSystemPrompt', openaiSystemPrompt);
|
|
82
108
|
}
|
|
83
109
|
// add output facts
|
|
84
110
|
engine.addFact('repoDependencyAnalysis', repoDependencyFacts_1.repoDependencyAnalysis);
|
|
85
111
|
// Run the engine for each file's data
|
|
86
|
-
|
|
112
|
+
logger_1.logger.info(`### Executing rules..`);
|
|
87
113
|
let failures = [];
|
|
88
114
|
for (const file of fileData) {
|
|
89
|
-
if (file.fileName ===
|
|
115
|
+
if (file.fileName === config_1.REPO_GLOBAL_CHECK) {
|
|
90
116
|
let msg = `\n==========================\nSTARTING GLOBAL REPO CHECKS..\n==========================`;
|
|
91
|
-
logger_1.logger.info(msg)
|
|
117
|
+
logger_1.logger.info(msg);
|
|
92
118
|
}
|
|
93
119
|
else {
|
|
94
120
|
let msg = `running engine for ${file.filePath}`;
|
|
95
|
-
logger_1.logger.info(msg)
|
|
121
|
+
logger_1.logger.info(msg);
|
|
96
122
|
}
|
|
97
123
|
const facts = {
|
|
98
124
|
fileData: file,
|
|
@@ -105,7 +131,6 @@ function analyzeCodebase(repoPath_1) {
|
|
|
105
131
|
let fileFailures = [];
|
|
106
132
|
yield engine.run(facts)
|
|
107
133
|
.then(({ results }) => {
|
|
108
|
-
//console.log(events);
|
|
109
134
|
results.map((result) => {
|
|
110
135
|
var _a;
|
|
111
136
|
logger_1.logger.debug(result);
|
|
@@ -122,6 +147,54 @@ function analyzeCodebase(repoPath_1) {
|
|
|
122
147
|
}
|
|
123
148
|
}
|
|
124
149
|
logger_1.logger.info(`${fileData.length} files analyzed. ${failures.length} files with errors.`);
|
|
150
|
+
const fatalities = findKeyValuePair(failures, 'level', 'fatality');
|
|
151
|
+
// Send telemetry for analysis end
|
|
152
|
+
yield (0, telemetry_1.sendTelemetry)({
|
|
153
|
+
eventType: 'analysisEnd',
|
|
154
|
+
metadata: {
|
|
155
|
+
archetype,
|
|
156
|
+
repoPath,
|
|
157
|
+
fileCount: fileData.length,
|
|
158
|
+
failureCount: failures.length,
|
|
159
|
+
fatalityCount: fatalities.length
|
|
160
|
+
},
|
|
161
|
+
timestamp: new Date().toISOString()
|
|
162
|
+
});
|
|
163
|
+
if (fatalities.length > 0) {
|
|
164
|
+
throw new Error(JSON.stringify(fatalities));
|
|
165
|
+
}
|
|
125
166
|
return failures;
|
|
126
167
|
});
|
|
127
168
|
}
|
|
169
|
+
const findKeyValuePair = (data, targetKey, targetValue) => {
|
|
170
|
+
let results = [];
|
|
171
|
+
const recursiveSearch = (obj) => {
|
|
172
|
+
if (typeof obj === 'object' && obj !== null) {
|
|
173
|
+
for (let key in obj) {
|
|
174
|
+
if (obj.hasOwnProperty(key)) {
|
|
175
|
+
if (key === targetKey && obj[key] === targetValue) {
|
|
176
|
+
results.push(obj);
|
|
177
|
+
return; // Stop searching this branch as we've found the target in this object
|
|
178
|
+
}
|
|
179
|
+
if (typeof obj[key] === 'object' || Array.isArray(obj[key])) {
|
|
180
|
+
recursiveSearch(obj[key]);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
else if (Array.isArray(obj)) {
|
|
186
|
+
obj.forEach((item) => {
|
|
187
|
+
recursiveSearch(item);
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
};
|
|
191
|
+
if (Array.isArray(data)) {
|
|
192
|
+
data.forEach((item) => {
|
|
193
|
+
recursiveSearch(item);
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
else {
|
|
197
|
+
recursiveSearch(data);
|
|
198
|
+
}
|
|
199
|
+
return results;
|
|
200
|
+
};
|