x-fidelity 2.11.0 → 2.12.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/CHANGELOG.md CHANGED
@@ -1,3 +1,24 @@
1
+ ## [2.12.1](https://github.com/zotoio/x-fidelity/compare/v2.12.0...v2.12.1) (2024-08-25)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * **deps:** reduce noise in dependency checks ([88f3ecb](https://github.com/zotoio/x-fidelity/commit/88f3ecb74a29624ee91594a06900c54a2d9fd7f9))
7
+
8
+ # [2.12.0](https://github.com/zotoio/x-fidelity/compare/v2.11.0...v2.12.0) (2024-08-25)
9
+
10
+
11
+ ### Bug Fixes
12
+
13
+ * **analysis:** ensure long single-line files are catered for and npm namespaces ([509b4db](https://github.com/zotoio/x-fidelity/commit/509b4db1c3bb3f2cb487e043f5d23689bc8ed42d))
14
+ * Handle [@namespace](https://github.com/namespace) packages in dependency analysis ([b5314ac](https://github.com/zotoio/x-fidelity/commit/b5314ac2b67f1bf241cbad3229c0ce3498bd9175))
15
+ * Refactor repoFileAnalysis function to improve performance ([ae725bb](https://github.com/zotoio/x-fidelity/commit/ae725bbab307008424bb7494444e66eaca684aad))
16
+
17
+
18
+ ### Features
19
+
20
+ * Implement file content splitting for analysis ([4d5f049](https://github.com/zotoio/x-fidelity/commit/4d5f04938d7fd78425e79ac74143439be1552879))
21
+
1
22
  # [2.11.0](https://github.com/zotoio/x-fidelity/compare/v2.10.0...v2.11.0) (2024-08-24)
2
23
 
3
24
 
@@ -1,8 +1,8 @@
1
1
  [
2
2
  {
3
- "repoUrl": "https://github.com/example/node-fullstack-project",
3
+ "repoUrl": "git@github.com:zotoio/x-fidelity.git",
4
4
  "rule": "outdatedFramework-global",
5
- "expirationDate": "2024-12-31",
5
+ "expirationDate": "2023-12-31",
6
6
  "reason": "Upgrading dependencies is scheduled for Q4 2024"
7
7
  },
8
8
  {
@@ -21,6 +21,9 @@
21
21
  ],
22
22
  "config": {
23
23
  "minimumDependencyVersions": {
24
+ "@types/react": "^17.0.0",
25
+ "react": "^17.0.0",
26
+ "@yarnpkg/lockfile": "^1.2.0",
24
27
  "commander": "^2.0.0",
25
28
  "nodemon": "^3.9.0"
26
29
  },
@@ -40,6 +40,7 @@ exports.getDependencyVersionFacts = getDependencyVersionFacts;
40
40
  exports.findPropertiesInTree = findPropertiesInTree;
41
41
  exports.repoDependencyAnalysis = repoDependencyAnalysis;
42
42
  exports.semverValid = semverValid;
43
+ exports.normalizePackageName = normalizePackageName;
43
44
  const logger_1 = require("../utils/logger");
44
45
  const child_process_1 = require("child_process");
45
46
  const semver = __importStar(require("semver"));
@@ -105,6 +106,13 @@ function processYarnDependencies(yarnOutput) {
105
106
  return newDep;
106
107
  };
107
108
  const extractNameAndVersion = (nameAndVersion) => {
109
+ const lastAtIndex = nameAndVersion.lastIndexOf('@');
110
+ if (nameAndVersion.startsWith('@') && lastAtIndex > 0) {
111
+ return {
112
+ name: nameAndVersion.substring(0, lastAtIndex),
113
+ version: nameAndVersion.substring(lastAtIndex + 1)
114
+ };
115
+ }
108
116
  const parts = nameAndVersion.split('@');
109
117
  return { name: parts[0], version: parts[1] };
110
118
  };
@@ -160,8 +168,10 @@ function findPropertiesInTree(depGraph, minVersions) {
160
168
  logger_1.logger.debug(`depGraph: ${JSON.stringify(depGraph)}`);
161
169
  function walk(dep, parentName = '') {
162
170
  const fullName = parentName ? `${parentName}/${dep.name}` : dep.name;
163
- if (Object.keys(minVersions).includes(dep.name)) {
164
- results.push({ dep: fullName, ver: dep.version, min: minVersions[dep.name] });
171
+ if (Object.keys(minVersions).some(key => key === dep.name || `@${key}` === dep.name)) {
172
+ const minVersionKey = Object.keys(minVersions).find(key => key === dep.name || `@${key}` === dep.name);
173
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
174
+ results.push({ dep: fullName, ver: dep.version, min: minVersions[minVersionKey] });
165
175
  }
166
176
  if (dep.dependencies) {
167
177
  dep.dependencies.forEach(childDep => {
@@ -186,7 +196,7 @@ function repoDependencyAnalysis(params, almanac) {
186
196
  logger_1.logger.debug(`outdatedFramework: checking ${versionData.dep}`);
187
197
  // Check if the installed version satisfies the required version, supporting both ranges and specific versions
188
198
  const isValid = semverValid(versionData.ver, versionData.min);
189
- if (!isValid) {
199
+ if (!isValid && semver.valid(versionData.ver)) {
190
200
  const dependencyFailure = {
191
201
  'dependency': versionData.dep,
192
202
  'currentVersion': versionData.ver,
@@ -201,8 +211,11 @@ function repoDependencyAnalysis(params, almanac) {
201
211
  return result;
202
212
  });
203
213
  }
204
- function semverValid(required, installed) {
205
- if (!required || !installed) {
214
+ function semverValid(installed, required) {
215
+ // Remove potential @namespace from installed version
216
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
217
+ const installedVersion = installed.includes('@') ? installed.split('@').pop() : installed;
218
+ if (!required || !installedVersion) {
206
219
  return true;
207
220
  }
208
221
  // If 'installed' is a single version and 'required' is a range
@@ -227,3 +240,6 @@ function semverValid(required, installed) {
227
240
  }
228
241
  return false;
229
242
  }
243
+ function normalizePackageName(name) {
244
+ return name.startsWith('@') ? name : `@${name}`;
245
+ }
@@ -89,17 +89,49 @@ function repoFileAnalysis(params, almanac) {
89
89
  const analysis = [];
90
90
  const lines = fileContent.split('\n');
91
91
  logger_1.logger.debug(`lines: ${lines.length}`);
92
- let lineNumber = 0;
92
+ const processedLines = [];
93
+ let splitOccurred = false;
93
94
  for (const line of lines) {
94
- lineNumber++;
95
+ if (line.length > 200) {
96
+ let startIndex = 0;
97
+ while (startIndex < line.length) {
98
+ let endIndex = startIndex + 200;
99
+ if (endIndex < line.length) {
100
+ // Find the nearest space or end of token
101
+ while (endIndex > startIndex && !(/\s/.test(line[endIndex]) || /\W/.test(line[endIndex]))) {
102
+ endIndex--;
103
+ }
104
+ // If no suitable break point found, use the full 200 characters
105
+ if (endIndex === startIndex) {
106
+ endIndex = startIndex + 200;
107
+ }
108
+ }
109
+ else {
110
+ endIndex = line.length;
111
+ }
112
+ processedLines.push(line.substring(startIndex, endIndex));
113
+ startIndex = endIndex;
114
+ }
115
+ splitOccurred = true;
116
+ }
117
+ else {
118
+ processedLines.push(line);
119
+ }
120
+ }
121
+ if (lines.length === 1 || splitOccurred) {
122
+ logger_1.logger.debug(`File content was split for analysis`);
123
+ }
124
+ for (let i = 0; i < processedLines.length; i++) {
125
+ const line = processedLines[i];
95
126
  for (const pattern of checkPatterns) {
96
127
  const regex = new RegExp(pattern, 'g');
97
128
  if (regex.test(line)) {
98
- logger_1.logger.debug(`match on line ${lineNumber} for pattern ${pattern}`);
129
+ logger_1.logger.debug(`match on line ${i + 1} for pattern ${pattern}`);
99
130
  const match = {
100
131
  'match': pattern,
101
- 'lineNumber': lineNumber,
102
- 'line': line
132
+ 'lineNumber': i + 1,
133
+ 'line': line,
134
+ 'splitOccurred': splitOccurred
103
135
  };
104
136
  analysis.push(match);
105
137
  }
package/dist/index.js CHANGED
@@ -59,19 +59,18 @@ function main() {
59
59
  if (resultMetadata.XFI_RESULT.totalIssues > 0) {
60
60
  logger_1.logger.warn(`WARNING: lo-fi attributes detected in codebase. ${resultMetadata.XFI_RESULT.warningCount} are warnings, ${resultMetadata.XFI_RESULT.fatalityCount} are fatal.`);
61
61
  logger_1.logger.warn(JSON.stringify(resultMetadata));
62
- logger_1.logger.warn(`\n${prettyjson_1.default.render(resultMetadata)}\n\n`);
63
62
  if (resultMetadata.XFI_RESULT.fatalityCount > 0) {
64
63
  logger_1.logger.error(outcomeMessage(`THERE WERE ${resultMetadata.XFI_RESULT.fatalityCount} FATAL ERRORS DETECTED TO BE IMMEDIATELY ADDRESSED!`));
65
64
  logger_1.logger.on('finish', function () {
66
65
  process.exit(1);
67
66
  });
68
- logger_1.logger.error(`\n${prettyjson_1.default.render(resultMetadata.XFI_RESULT.issueDetails)}\n\n`);
67
+ logger_1.logger.error(`\n${prettyjson_1.default.render(resultMetadata.XFI_RESULT)}\n\n`);
69
68
  logger_1.logger.error(outcomeMessage(`THERE WERE ${resultMetadata.XFI_RESULT.fatalityCount} FATAL ERRORS DETECTED TO BE IMMEDIATELY ADDRESSED!`));
70
69
  logger_1.logger.end();
71
70
  }
72
71
  else {
73
72
  logger_1.logger.warn(outcomeMessage('No fatal errors were found, however please review the following warnings.'));
74
- logger_1.logger.warn(`\n${prettyjson_1.default.render(resultMetadata.XFI_RESULT.issueDetails)}\n\n`);
73
+ logger_1.logger.warn(`\n${prettyjson_1.default.render(resultMetadata.XFI_RESULT)}\n\n`);
75
74
  logger_1.logger.warn(outcomeMessage('No fatal errors were found, however please review the above warnings.'));
76
75
  }
77
76
  }
@@ -18,7 +18,8 @@ const archetypeRuleRoute_1 = require("./routes/archetypeRuleRoute");
18
18
  const telemetryRoute_1 = require("./routes/telemetryRoute");
19
19
  const clearCacheRoute_1 = require("./routes/clearCacheRoute");
20
20
  const viewCacheRoute_1 = require("./routes/viewCacheRoute");
21
- const githubWebhookRoute_1 = require("./routes/githubWebhookRoute");
21
+ const githubWebhookConfigUpdateRoute_1 = require("./routes/githubWebhookConfigUpdateRoute");
22
+ const githubWebhookPullRequestCheckRoute_1 = require("./routes/githubWebhookPullRequestCheckRoute");
22
23
  const exemptionsRoute_1 = require("./routes/exemptionsRoute");
23
24
  const validateUrlInput_1 = require("./middleware/validateUrlInput");
24
25
  const validateTelemetryData_1 = require("./middleware/validateTelemetryData");
@@ -57,7 +58,8 @@ app.post('/telemetry', checkSharedSecret, validateTelemetryData_1.validateTeleme
57
58
  app.post('/clearcache', checkSharedSecret, clearCacheRoute_1.clearCacheRoute);
58
59
  app.get('/viewcache', checkSharedSecret, viewCacheRoute_1.viewCacheRoute);
59
60
  app.get('/archetypes/:archetype/exemptions', checkSharedSecret, exemptionsRoute_1.exemptionsRoute);
60
- app.post('/github-webhook', validateGithubWebhook_1.validateGithubWebhook, githubWebhookRoute_1.githubWebhookRoute);
61
+ app.post('/github-config-update', validateGithubWebhook_1.validateGithubWebhook, githubWebhookConfigUpdateRoute_1.githubWebhookConfigUpdateRoute);
62
+ app.post('/github-pull-request-check', validateGithubWebhook_1.validateGithubWebhook, githubWebhookPullRequestCheckRoute_1.githubWebhookPullRequestCheckRoute);
61
63
  function startServer({ customPort, executionLogPrefix }) {
62
64
  const serverPort = customPort ? parseInt(customPort) : port;
63
65
  executionLogPrefix && (0, logger_1.setLogPrefix)(executionLogPrefix);
@@ -6,15 +6,16 @@ const archetypeRuleRoute_1 = require("./routes/archetypeRuleRoute");
6
6
  const telemetryRoute_1 = require("./routes/telemetryRoute");
7
7
  const clearCacheRoute_1 = require("./routes/clearCacheRoute");
8
8
  const viewCacheRoute_1 = require("./routes/viewCacheRoute");
9
- const githubWebhookRoute_1 = require("./routes/githubWebhookRoute");
9
+ const githubWebhookConfigUpdateRoute_1 = require("./routes/githubWebhookConfigUpdateRoute");
10
10
  const checkSharedSecret_1 = require("./middleware/checkSharedSecret");
11
+ const validateGithubWebhook_1 = require("./middleware/validateGithubWebhook");
11
12
  jest.mock('./routes/archetypeRoute');
12
13
  jest.mock('./routes/archetypeRulesRoute');
13
14
  jest.mock('./routes/archetypeRuleRoute');
14
15
  jest.mock('./routes/telemetryRoute');
15
16
  jest.mock('./routes/clearCacheRoute');
16
17
  jest.mock('./routes/viewCacheRoute');
17
- jest.mock('./routes/githubWebhookRoute');
18
+ jest.mock('./routes/githubWebhookConfigUpdateRoute');
18
19
  jest.mock('./middleware/checkSharedSecret');
19
20
  describe('configServer', () => {
20
21
  let app;
@@ -32,7 +33,7 @@ describe('configServer', () => {
32
33
  app.post('/telemetry', checkSharedSecret_1.checkSharedSecret, telemetryRoute_1.telemetryRoute);
33
34
  app.post('/clearcache', checkSharedSecret_1.checkSharedSecret, clearCacheRoute_1.clearCacheRoute);
34
35
  app.get('/viewcache', checkSharedSecret_1.checkSharedSecret, viewCacheRoute_1.viewCacheRoute);
35
- app.post('/github-webhook', githubWebhookRoute_1.githubWebhookRoute);
36
+ app.post('/github-config-update', validateGithubWebhook_1.validateGithubWebhook, githubWebhookConfigUpdateRoute_1.githubWebhookConfigUpdateRoute);
36
37
  // Verify routes are set up correctly
37
38
  expect(app.get).toHaveBeenCalledWith('/archetypes/:archetype', archetypeRoute_1.archetypeRoute);
38
39
  expect(app.get).toHaveBeenCalledWith('/archetypes/:archetype/rules', archetypeRulesRoute_1.archetypeRulesRoute);
@@ -40,7 +41,7 @@ describe('configServer', () => {
40
41
  expect(app.post).toHaveBeenCalledWith('/telemetry', checkSharedSecret_1.checkSharedSecret, telemetryRoute_1.telemetryRoute);
41
42
  expect(app.post).toHaveBeenCalledWith('/clearcache', checkSharedSecret_1.checkSharedSecret, clearCacheRoute_1.clearCacheRoute);
42
43
  expect(app.get).toHaveBeenCalledWith('/viewcache', checkSharedSecret_1.checkSharedSecret, viewCacheRoute_1.viewCacheRoute);
43
- expect(app.post).toHaveBeenCalledWith('/github-webhook', githubWebhookRoute_1.githubWebhookRoute);
44
+ expect(app.post).toHaveBeenCalledWith('/github-config-update', validateGithubWebhook_1.validateGithubWebhook, githubWebhookConfigUpdateRoute_1.githubWebhookConfigUpdateRoute);
44
45
  });
45
46
  afterEach(() => {
46
47
  jest.resetAllMocks();
@@ -12,7 +12,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
12
12
  return (mod && mod.__esModule) ? mod : { "default": mod };
13
13
  };
14
14
  Object.defineProperty(exports, "__esModule", { value: true });
15
- exports.githubWebhookRoute = githubWebhookRoute;
15
+ exports.githubWebhookConfigUpdateRoute = githubWebhookConfigUpdateRoute;
16
16
  const logger_1 = require("../../utils/logger");
17
17
  const crypto_1 = __importDefault(require("crypto"));
18
18
  const axios_1 = __importDefault(require("axios"));
@@ -22,7 +22,7 @@ const fs_1 = __importDefault(require("fs"));
22
22
  const cacheManager_1 = require("../cacheManager");
23
23
  const configManager_1 = require("../../utils/configManager");
24
24
  const cli_1 = require("../../core/cli");
25
- function githubWebhookRoute(req, res) {
25
+ function githubWebhookConfigUpdateRoute(req, res) {
26
26
  return __awaiter(this, void 0, void 0, function* () {
27
27
  const requestLogPrefix = req.headers['x-log-prefix'] || '';
28
28
  (0, logger_1.setLogPrefix)(requestLogPrefix);
@@ -0,0 +1,45 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.githubWebhookPullRequestCheckRoute = githubWebhookPullRequestCheckRoute;
16
+ const logger_1 = require("../../utils/logger");
17
+ const crypto_1 = __importDefault(require("crypto"));
18
+ function githubWebhookPullRequestCheckRoute(req, res) {
19
+ return __awaiter(this, void 0, void 0, function* () {
20
+ const requestLogPrefix = req.headers['x-log-prefix'] || '';
21
+ (0, logger_1.setLogPrefix)(requestLogPrefix);
22
+ const signature = req.headers['x-hub-signature-256'];
23
+ const githubSecret = process.env.GITHUB_WEBHOOK_SECRET;
24
+ if (!githubSecret) {
25
+ logger_1.logger.error('GitHub webhook secret is not set');
26
+ return res.status(500).send('Server is not configured for webhooks');
27
+ }
28
+ if (!signature) {
29
+ logger_1.logger.error('No X-Hub-Signature-256 found on request');
30
+ return res.status(400).send('No X-Hub-Signature-256 found on request');
31
+ }
32
+ const hmac = crypto_1.default.createHmac('sha256', githubSecret);
33
+ const digest = 'sha256=' + hmac.update(JSON.stringify(req.body)).digest('hex');
34
+ if (signature !== digest) {
35
+ logger_1.logger.error('Request body digest did not match X-Hub-Signature-256');
36
+ return res.status(400).send('Invalid signature');
37
+ }
38
+ const event = req.headers['x-github-event'];
39
+ if (event === 'push') {
40
+ // TODO: Implement pull request check
41
+ return res.status(200).send('Webhook received and processed');
42
+ }
43
+ res.status(200).send('Received');
44
+ });
45
+ }
package/dist/xfidelity CHANGED
@@ -59,19 +59,18 @@ function main() {
59
59
  if (resultMetadata.XFI_RESULT.totalIssues > 0) {
60
60
  logger_1.logger.warn(`WARNING: lo-fi attributes detected in codebase. ${resultMetadata.XFI_RESULT.warningCount} are warnings, ${resultMetadata.XFI_RESULT.fatalityCount} are fatal.`);
61
61
  logger_1.logger.warn(JSON.stringify(resultMetadata));
62
- logger_1.logger.warn(`\n${prettyjson_1.default.render(resultMetadata)}\n\n`);
63
62
  if (resultMetadata.XFI_RESULT.fatalityCount > 0) {
64
63
  logger_1.logger.error(outcomeMessage(`THERE WERE ${resultMetadata.XFI_RESULT.fatalityCount} FATAL ERRORS DETECTED TO BE IMMEDIATELY ADDRESSED!`));
65
64
  logger_1.logger.on('finish', function () {
66
65
  process.exit(1);
67
66
  });
68
- logger_1.logger.error(`\n${prettyjson_1.default.render(resultMetadata.XFI_RESULT.issueDetails)}\n\n`);
67
+ logger_1.logger.error(`\n${prettyjson_1.default.render(resultMetadata.XFI_RESULT)}\n\n`);
69
68
  logger_1.logger.error(outcomeMessage(`THERE WERE ${resultMetadata.XFI_RESULT.fatalityCount} FATAL ERRORS DETECTED TO BE IMMEDIATELY ADDRESSED!`));
70
69
  logger_1.logger.end();
71
70
  }
72
71
  else {
73
72
  logger_1.logger.warn(outcomeMessage('No fatal errors were found, however please review the following warnings.'));
74
- logger_1.logger.warn(`\n${prettyjson_1.default.render(resultMetadata.XFI_RESULT.issueDetails)}\n\n`);
73
+ logger_1.logger.warn(`\n${prettyjson_1.default.render(resultMetadata.XFI_RESULT)}\n\n`);
75
74
  logger_1.logger.warn(outcomeMessage('No fatal errors were found, however please review the above warnings.'));
76
75
  }
77
76
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "x-fidelity",
3
- "version": "2.11.0",
3
+ "version": "2.12.1",
4
4
  "description": "cli for opinionated framework adherence checks",
5
5
  "main": "dist/xfidelity",
6
6
  "bin": {
@@ -73,7 +73,7 @@
73
73
  "dotenv": "^16.4.5",
74
74
  "esprima": "^4.0.1",
75
75
  "express": "^4.18.2",
76
- "express-rate-limit": "^6.7.0",
76
+ "express-rate-limit": "^7.4.0",
77
77
  "helmet": "^7.1.0",
78
78
  "json-rules-engine": "^6.5.0",
79
79
  "lodash": "^4.17.21",
@@ -1,8 +1,8 @@
1
1
  [
2
2
  {
3
- "repoUrl": "https://github.com/example/node-fullstack-project",
3
+ "repoUrl": "git@github.com:zotoio/x-fidelity.git",
4
4
  "rule": "outdatedFramework-global",
5
- "expirationDate": "2024-12-31",
5
+ "expirationDate": "2023-12-31",
6
6
  "reason": "Upgrading dependencies is scheduled for Q4 2024"
7
7
  },
8
8
  {
@@ -21,6 +21,9 @@
21
21
  ],
22
22
  "config": {
23
23
  "minimumDependencyVersions": {
24
+ "@types/react": "^17.0.0",
25
+ "react": "^17.0.0",
26
+ "@yarnpkg/lockfile": "^1.2.0",
24
27
  "commander": "^2.0.0",
25
28
  "nodemon": "^3.9.0"
26
29
  },
@@ -64,9 +64,16 @@ function processYarnDependencies(yarnOutput: any): LocalDependencies[] {
64
64
  return newDep;
65
65
  };
66
66
  const extractNameAndVersion = (nameAndVersion: string) => {
67
+ const lastAtIndex = nameAndVersion.lastIndexOf('@');
68
+ if (nameAndVersion.startsWith('@') && lastAtIndex > 0) {
69
+ return {
70
+ name: nameAndVersion.substring(0, lastAtIndex),
71
+ version: nameAndVersion.substring(lastAtIndex + 1)
72
+ };
73
+ }
67
74
  const parts = nameAndVersion.split('@');
68
75
  return { name: parts[0], version: parts[1] };
69
- }
76
+ };
70
77
  yarnOutput.data.trees.forEach((tree: any) => {
71
78
  dependencies.push(processDependency(tree));
72
79
  });
@@ -125,8 +132,10 @@ export function findPropertiesInTree(depGraph: LocalDependencies[], minVersions:
125
132
 
126
133
  function walk(dep: LocalDependencies, parentName = '') {
127
134
  const fullName = parentName ? `${parentName}/${dep.name}` : dep.name;
128
- if (Object.keys(minVersions).includes(dep.name)) {
129
- results.push({ dep: fullName, ver: dep.version, min: minVersions[dep.name] });
135
+ if (Object.keys(minVersions).some(key => key === dep.name || `@${key}` === dep.name)) {
136
+ const minVersionKey = Object.keys(minVersions).find(key => key === dep.name || `@${key}` === dep.name);
137
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
138
+ results.push({ dep: fullName, ver: dep.version, min: minVersions[minVersionKey!] });
130
139
  }
131
140
  if (dep.dependencies) {
132
141
  dep.dependencies.forEach(childDep => {
@@ -157,7 +166,7 @@ export async function repoDependencyAnalysis(params: any, almanac: Almanac) {
157
166
 
158
167
  // Check if the installed version satisfies the required version, supporting both ranges and specific versions
159
168
  const isValid = semverValid(versionData.ver, versionData.min);
160
- if (!isValid) {
169
+ if (!isValid && semver.valid(versionData.ver)) {
161
170
  const dependencyFailure = {
162
171
  'dependency': versionData.dep,
163
172
  'currentVersion': versionData.ver,
@@ -176,9 +185,12 @@ export async function repoDependencyAnalysis(params: any, almanac: Almanac) {
176
185
  return result;
177
186
  }
178
187
 
179
- export function semverValid(required: string, installed: string): boolean {
180
-
181
- if (!required || !installed) {
188
+ export function semverValid(installed: string, required: string): boolean {
189
+ // Remove potential @namespace from installed version
190
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
191
+ const installedVersion = installed.includes('@') ? installed.split('@').pop()! : installed;
192
+
193
+ if (!required || !installedVersion) {
182
194
  return true;
183
195
  }
184
196
 
@@ -208,3 +220,6 @@ export function semverValid(required: string, installed: string): boolean {
208
220
 
209
221
  return false;
210
222
  }
223
+ export function normalizePackageName(name: string): string {
224
+ return name.startsWith('@') ? name : `@${name}`;
225
+ }
@@ -83,17 +83,50 @@ async function repoFileAnalysis(params: any, almanac: any) {
83
83
  const analysis: any = [];
84
84
  const lines = fileContent.split('\n');
85
85
  logger.debug(`lines: ${lines.length}`);
86
- let lineNumber = 0;
86
+ const processedLines: string[] = [];
87
+ let splitOccurred = false;
88
+
87
89
  for (const line of lines) {
88
- lineNumber++;
90
+ if (line.length > 200) {
91
+ let startIndex = 0;
92
+ while (startIndex < line.length) {
93
+ let endIndex = startIndex + 200;
94
+ if (endIndex < line.length) {
95
+ // Find the nearest space or end of token
96
+ while (endIndex > startIndex && !(/\s/.test(line[endIndex]) || /\W/.test(line[endIndex]))) {
97
+ endIndex--;
98
+ }
99
+ // If no suitable break point found, use the full 200 characters
100
+ if (endIndex === startIndex) {
101
+ endIndex = startIndex + 200;
102
+ }
103
+ } else {
104
+ endIndex = line.length;
105
+ }
106
+ processedLines.push(line.substring(startIndex, endIndex));
107
+ startIndex = endIndex;
108
+ }
109
+ splitOccurred = true;
110
+ } else {
111
+ processedLines.push(line);
112
+ }
113
+ }
114
+
115
+ if (lines.length === 1 || splitOccurred) {
116
+ logger.debug(`File content was split for analysis`);
117
+ }
118
+
119
+ for (let i = 0; i < processedLines.length; i++) {
120
+ const line = processedLines[i];
89
121
  for (const pattern of checkPatterns) {
90
122
  const regex = new RegExp(pattern, 'g');
91
123
  if (regex.test(line)) {
92
- logger.debug(`match on line ${lineNumber} for pattern ${pattern}`)
124
+ logger.debug(`match on line ${i + 1} for pattern ${pattern}`);
93
125
  const match = {
94
126
  'match': pattern,
95
- 'lineNumber': lineNumber,
96
- 'line': line
127
+ 'lineNumber': i + 1,
128
+ 'line': line,
129
+ 'splitOccurred': splitOccurred
97
130
  };
98
131
  analysis.push(match);
99
132
  }
package/src/index.ts CHANGED
@@ -49,19 +49,18 @@ export async function main() {
49
49
  if (resultMetadata.XFI_RESULT.totalIssues > 0) {
50
50
  logger.warn(`WARNING: lo-fi attributes detected in codebase. ${resultMetadata.XFI_RESULT.warningCount} are warnings, ${resultMetadata.XFI_RESULT.fatalityCount} are fatal.`);
51
51
  logger.warn(JSON.stringify(resultMetadata));
52
- logger.warn(`\n${json.render(resultMetadata)}\n\n`);
53
52
 
54
53
  if (resultMetadata.XFI_RESULT.fatalityCount > 0) {
55
54
  logger.error(outcomeMessage(`THERE WERE ${resultMetadata.XFI_RESULT.fatalityCount} FATAL ERRORS DETECTED TO BE IMMEDIATELY ADDRESSED!`));
56
55
  logger.on('finish', function () {
57
56
  process.exit(1);
58
57
  });
59
- logger.error(`\n${json.render(resultMetadata.XFI_RESULT.issueDetails)}\n\n`);
58
+ logger.error(`\n${json.render(resultMetadata.XFI_RESULT)}\n\n`);
60
59
  logger.error(outcomeMessage(`THERE WERE ${resultMetadata.XFI_RESULT.fatalityCount} FATAL ERRORS DETECTED TO BE IMMEDIATELY ADDRESSED!`));
61
60
  logger.end();
62
61
  } else {
63
62
  logger.warn(outcomeMessage('No fatal errors were found, however please review the following warnings.'));
64
- logger.warn(`\n${json.render(resultMetadata.XFI_RESULT.issueDetails)}\n\n`);
63
+ logger.warn(`\n${json.render(resultMetadata.XFI_RESULT)}\n\n`);
65
64
  logger.warn(outcomeMessage('No fatal errors were found, however please review the above warnings.'));
66
65
 
67
66
  }
@@ -5,8 +5,9 @@ import { archetypeRuleRoute } from './routes/archetypeRuleRoute';
5
5
  import { telemetryRoute } from './routes/telemetryRoute';
6
6
  import { clearCacheRoute } from './routes/clearCacheRoute';
7
7
  import { viewCacheRoute } from './routes/viewCacheRoute';
8
- import { githubWebhookRoute } from './routes/githubWebhookRoute';
8
+ import { githubWebhookConfigUpdateRoute } from './routes/githubWebhookConfigUpdateRoute';
9
9
  import { checkSharedSecret } from './middleware/checkSharedSecret';
10
+ import { validateGithubWebhook } from './middleware/validateGithubWebhook';
10
11
 
11
12
  jest.mock('./routes/archetypeRoute');
12
13
  jest.mock('./routes/archetypeRulesRoute');
@@ -14,7 +15,7 @@ jest.mock('./routes/archetypeRuleRoute');
14
15
  jest.mock('./routes/telemetryRoute');
15
16
  jest.mock('./routes/clearCacheRoute');
16
17
  jest.mock('./routes/viewCacheRoute');
17
- jest.mock('./routes/githubWebhookRoute');
18
+ jest.mock('./routes/githubWebhookConfigUpdateRoute');
18
19
  jest.mock('./middleware/checkSharedSecret');
19
20
 
20
21
  describe('configServer', () => {
@@ -35,7 +36,7 @@ describe('configServer', () => {
35
36
  app.post('/telemetry', checkSharedSecret, telemetryRoute);
36
37
  app.post('/clearcache', checkSharedSecret, clearCacheRoute);
37
38
  app.get('/viewcache', checkSharedSecret, viewCacheRoute);
38
- app.post('/github-webhook', githubWebhookRoute);
39
+ app.post('/github-config-update', validateGithubWebhook, githubWebhookConfigUpdateRoute);
39
40
 
40
41
  // Verify routes are set up correctly
41
42
  expect(app.get).toHaveBeenCalledWith('/archetypes/:archetype', archetypeRoute);
@@ -44,7 +45,7 @@ describe('configServer', () => {
44
45
  expect(app.post).toHaveBeenCalledWith('/telemetry', checkSharedSecret, telemetryRoute);
45
46
  expect(app.post).toHaveBeenCalledWith('/clearcache', checkSharedSecret, clearCacheRoute);
46
47
  expect(app.get).toHaveBeenCalledWith('/viewcache', checkSharedSecret, viewCacheRoute);
47
- expect(app.post).toHaveBeenCalledWith('/github-webhook', githubWebhookRoute);
48
+ expect(app.post).toHaveBeenCalledWith('/github-config-update', validateGithubWebhook, githubWebhookConfigUpdateRoute);
48
49
  });
49
50
 
50
51
  afterEach(() => {
@@ -13,7 +13,8 @@ import { archetypeRuleRoute } from './routes/archetypeRuleRoute';
13
13
  import { telemetryRoute } from './routes/telemetryRoute';
14
14
  import { clearCacheRoute } from './routes/clearCacheRoute';
15
15
  import { viewCacheRoute } from './routes/viewCacheRoute';
16
- import { githubWebhookRoute } from './routes/githubWebhookRoute';
16
+ import { githubWebhookConfigUpdateRoute } from './routes/githubWebhookConfigUpdateRoute';
17
+ import { githubWebhookPullRequestCheckRoute } from './routes/githubWebhookPullRequestCheckRoute';
17
18
  import { exemptionsRoute } from './routes/exemptionsRoute';
18
19
  import { validateUrlInput } from './middleware/validateUrlInput';
19
20
  import { validateTelemetryData } from './middleware/validateTelemetryData';
@@ -62,7 +63,8 @@ app.post('/clearcache', checkSharedSecret, clearCacheRoute);
62
63
  app.get('/viewcache', checkSharedSecret, viewCacheRoute);
63
64
  app.get('/archetypes/:archetype/exemptions', checkSharedSecret, exemptionsRoute);
64
65
 
65
- app.post('/github-webhook', validateGithubWebhook, githubWebhookRoute);
66
+ app.post('/github-config-update', validateGithubWebhook, githubWebhookConfigUpdateRoute);
67
+ app.post('/github-pull-request-check', validateGithubWebhook, githubWebhookPullRequestCheckRoute);
66
68
 
67
69
  export function startServer({ customPort, executionLogPrefix }: StartServerParams): any {
68
70
  const serverPort = customPort ? parseInt(customPort) : port;
@@ -9,7 +9,7 @@ import { clearCache } from '../cacheManager';
9
9
  import { ConfigManager } from '../../utils/configManager';
10
10
  import { options } from '../../core/cli';
11
11
 
12
- export async function githubWebhookRoute(req: Request, res: Response) {
12
+ export async function githubWebhookConfigUpdateRoute(req: Request, res: Response) {
13
13
  const requestLogPrefix = req.headers['x-log-prefix'] as string || '';
14
14
  setLogPrefix(requestLogPrefix);
15
15
 
@@ -0,0 +1,40 @@
1
+ import { Request, Response } from 'express';
2
+ import { logger, setLogPrefix } from '../../utils/logger';
3
+ import crypto from 'crypto';
4
+
5
+ export async function githubWebhookPullRequestCheckRoute(req: Request, res: Response) {
6
+ const requestLogPrefix = req.headers['x-log-prefix'] as string || '';
7
+ setLogPrefix(requestLogPrefix);
8
+
9
+ const signature = req.headers['x-hub-signature-256'] as string;
10
+ const githubSecret = process.env.GITHUB_WEBHOOK_SECRET;
11
+
12
+ if (!githubSecret) {
13
+ logger.error('GitHub webhook secret is not set');
14
+ return res.status(500).send('Server is not configured for webhooks');
15
+ }
16
+
17
+ if (!signature) {
18
+ logger.error('No X-Hub-Signature-256 found on request');
19
+ return res.status(400).send('No X-Hub-Signature-256 found on request');
20
+ }
21
+
22
+ const hmac = crypto.createHmac('sha256', githubSecret);
23
+ const digest = 'sha256=' + hmac.update(JSON.stringify(req.body)).digest('hex');
24
+
25
+ if (signature !== digest) {
26
+ logger.error('Request body digest did not match X-Hub-Signature-256');
27
+ return res.status(400).send('Invalid signature');
28
+ }
29
+
30
+ const event = req.headers['x-github-event'] as string;
31
+ if (event === 'push') {
32
+ // TODO: Implement pull request check
33
+
34
+ return res.status(200).send('Webhook received and processed');
35
+ }
36
+
37
+ res.status(200).send('Received');
38
+ }
39
+
40
+