@greenarmor/ges-scoring-engine 0.5.0 → 0.5.2

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/dist/index.d.ts CHANGED
@@ -14,4 +14,5 @@ export declare function generateScoreFile(controls: Control[], frameworks: Frame
14
14
  export declare function formatScoreOutput(score: ScoreFile): string;
15
15
  export { SEVERITY_WEIGHTS, STATUS_CREDIT, SEVERITY_PENALTY, computeGrade };
16
16
  export declare function generateBadgeSvg(score: ScoreFile): string;
17
- export declare function injectBadgeIntoReadme(readmeContent: string, badgeSvgPath: string): string;
17
+ export declare function generateScoreExplainer(score: ScoreFile): string;
18
+ export declare function injectBadgeIntoReadme(readmeContent: string, badgeSvgPath: string, scoreExplainer?: string): string;
package/dist/index.js CHANGED
@@ -260,17 +260,74 @@ export function generateBadgeSvg(score) {
260
260
  </g>
261
261
  </svg>`;
262
262
  }
263
- export function injectBadgeIntoReadme(readmeContent, badgeSvgPath) {
263
+ export function generateScoreExplainer(score) {
264
+ const lines = [];
265
+ const indent = "> ";
266
+ lines.push(`${indent}**GESF Compliance Score: ${score.overall}% (${score.overall_grade})**`);
267
+ lines.push(">");
268
+ lines.push(`${indent}| Framework | Score | Grade | Controls |`);
269
+ lines.push(`${indent}|-----------|-------|-------|----------|`);
270
+ for (const [fw, data] of Object.entries(score.frameworks)) {
271
+ const passed = data.passed_controls;
272
+ const total = data.total_controls;
273
+ lines.push(`${indent}| ${fw} | ${data.score}% | ${data.grade} | ${passed}/${total} passed |`);
274
+ }
275
+ if (score.audit_impact) {
276
+ const ai = score.audit_impact;
277
+ lines.push(">");
278
+ const parts = [];
279
+ if (ai.critical_findings > 0)
280
+ parts.push(`${ai.critical_findings} critical`);
281
+ if (ai.high_findings > 0)
282
+ parts.push(`${ai.high_findings} high`);
283
+ if (ai.medium_findings > 0)
284
+ parts.push(`${ai.medium_findings} medium`);
285
+ if (ai.low_findings > 0)
286
+ parts.push(`${ai.low_findings} low`);
287
+ if (parts.length > 0) {
288
+ lines.push(`${indent}Audit findings: ${parts.join(", ")} (score deduction: -${ai.total_deduction}%)`);
289
+ }
290
+ }
291
+ lines.push(">");
292
+ lines.push(`${indent}_(Last evaluated: ${score.evaluated_at.split("T")[0]})_`);
293
+ return lines.join("\n");
294
+ }
295
+ const EXPLAINER_START = "<!-- GESF-SCORE-START -->";
296
+ const EXPLAINER_END = "<!-- GESF-SCORE-END -->";
297
+ export function injectBadgeIntoReadme(readmeContent, badgeSvgPath, scoreExplainer) {
264
298
  const badgeLine = `![GESF Compliance](${badgeSvgPath})`;
265
- const existingPattern = /!\[GESF Compliance\]\([^)]*\)/;
266
- if (existingPattern.test(readmeContent)) {
267
- return readmeContent.replace(existingPattern, badgeLine);
299
+ const explainerBlock = scoreExplainer
300
+ ? `\n${EXPLAINER_START}\n${scoreExplainer}\n${EXPLAINER_END}`
301
+ : "";
302
+ const existingBadge = /!\[GESF Compliance\]\([^)]*\)/;
303
+ const hasExistingExplainer = readmeContent.includes(EXPLAINER_START) && readmeContent.includes(EXPLAINER_END);
304
+ if (hasExistingExplainer) {
305
+ let updated = readmeContent;
306
+ if (existingBadge.test(updated)) {
307
+ updated = updated.replace(existingBadge, badgeLine);
308
+ }
309
+ const explainerRegex = new RegExp(EXPLAINER_START.replace(/[.*+?^${}()|[\]\\]/g, "\\$&") +
310
+ "[\\s\\S]*?" +
311
+ EXPLAINER_END.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"));
312
+ updated = updated.replace(explainerRegex, `${EXPLAINER_START}\n${scoreExplainer || ""}\n${EXPLAINER_END}\n`);
313
+ return updated;
314
+ }
315
+ if (existingBadge.test(readmeContent)) {
316
+ if (scoreExplainer) {
317
+ const badgeIdx = readmeContent.indexOf("![GESF Compliance]");
318
+ const lineEnd = readmeContent.indexOf("\n", badgeIdx);
319
+ const afterBadge = lineEnd !== -1 ? lineEnd : readmeContent.length;
320
+ return readmeContent.slice(0, badgeIdx) + badgeLine +
321
+ "\n" + EXPLAINER_START + "\n" + scoreExplainer + "\n" + EXPLAINER_END + "\n\n" +
322
+ readmeContent.slice(afterBadge + 1);
323
+ }
324
+ return readmeContent.replace(existingBadge, badgeLine);
268
325
  }
269
326
  const headingMatch = readmeContent.match(/^#\s+.+$/m);
270
327
  if (headingMatch && headingMatch.index !== undefined) {
271
328
  const afterHeading = headingMatch.index + headingMatch[0].length;
272
- const insertion = `\n\n${badgeLine}`;
329
+ const insertion = `\n\n${badgeLine}${explainerBlock}\n`;
273
330
  return readmeContent.slice(0, afterHeading) + insertion + readmeContent.slice(afterHeading);
274
331
  }
275
- return badgeLine + "\n\n" + readmeContent;
332
+ return badgeLine + explainerBlock + "\n\n" + readmeContent;
276
333
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@greenarmor/ges-scoring-engine",
3
- "version": "0.5.0",
3
+ "version": "0.5.2",
4
4
  "type": "module",
5
5
  "description": "GESF Scoring Engine - Compliance scoring across frameworks",
6
6
  "main": "./dist/index.js",
@@ -12,7 +12,7 @@
12
12
  }
13
13
  },
14
14
  "dependencies": {
15
- "@greenarmor/ges-core": "0.5.0"
15
+ "@greenarmor/ges-core": "0.5.2"
16
16
  },
17
17
  "devDependencies": {
18
18
  "@types/node": "^22.0.0",