@greenarmor/ges-web-dashboard 1.2.4 → 1.2.6
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 +2 -1
- package/dist/index.js +53 -39
- package/dist/template.js +142 -4
- package/package.json +5 -5
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as http from "node:http";
|
|
2
|
-
import type { ScoreFile, Control, FixHistoryEntry } from "@greenarmor/ges-core";
|
|
2
|
+
import type { ScoreFile, Control, FixHistoryEntry, ActivityLogEntry } from "@greenarmor/ges-core";
|
|
3
3
|
import type { Finding } from "@greenarmor/ges-audit-engine";
|
|
4
4
|
export interface DashboardOptions {
|
|
5
5
|
port?: number;
|
|
@@ -77,6 +77,7 @@ export interface DashboardData {
|
|
|
77
77
|
findings: Finding[];
|
|
78
78
|
packs: PackSummary[];
|
|
79
79
|
fixHistory: FixHistoryEntry[];
|
|
80
|
+
activityLog: ActivityLogEntry[];
|
|
80
81
|
lastAudit: string;
|
|
81
82
|
}
|
|
82
83
|
export declare function collectDashboardData(projectPath: string): DashboardData;
|
package/dist/index.js
CHANGED
|
@@ -4,7 +4,8 @@ import * as path from "node:path";
|
|
|
4
4
|
import { runAudit, deduplicateFindings } from "@greenarmor/ges-audit-engine";
|
|
5
5
|
import { getAllPacks, getPack } from "@greenarmor/ges-policy-engine";
|
|
6
6
|
import { generateScoreFile } from "@greenarmor/ges-scoring-engine";
|
|
7
|
-
import { loadFixHistory } from "@greenarmor/ges-core";
|
|
7
|
+
import { loadFixHistory, loadActivityLog, loadControlsFromDisk, loadControlOverrides, applyOverridesToControls } from "@greenarmor/ges-core";
|
|
8
|
+
import { getInstalledPackIds as getInstalledPackIdsFromDisk } from "@greenarmor/ges-core";
|
|
8
9
|
import { renderDashboard } from "./template.js";
|
|
9
10
|
function loadConfig(projectPath) {
|
|
10
11
|
const configPath = path.join(projectPath, ".ges", "config.json");
|
|
@@ -31,21 +32,13 @@ function loadControlsForConfig(projectPath, config) {
|
|
|
31
32
|
const fwLower = new Set(config.frameworks.map(f => f.toLowerCase()));
|
|
32
33
|
const allPacks = getAllPacks();
|
|
33
34
|
const packs = allPacks.filter(pack => fwLower.has(pack.id.toLowerCase()));
|
|
34
|
-
const
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
control.status = override.status;
|
|
42
|
-
for (const check of control.checks) {
|
|
43
|
-
check.status = override.status;
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
return controls;
|
|
35
|
+
const inMemoryControls = packs.flatMap(p => p.controls);
|
|
36
|
+
const diskControls = loadControlsFromDisk(projectPath);
|
|
37
|
+
const seenIds = new Set(inMemoryControls.map((c) => c.id));
|
|
38
|
+
const extraFromDisk = diskControls.filter(c => !seenIds.has(c.id));
|
|
39
|
+
const controls = [...inMemoryControls, ...extraFromDisk];
|
|
40
|
+
const overrides = loadControlOverrides(projectPath);
|
|
41
|
+
return applyOverridesToControls(controls, overrides);
|
|
49
42
|
}
|
|
50
43
|
catch {
|
|
51
44
|
return [];
|
|
@@ -139,32 +132,38 @@ function getInstalledPackIds(projectPath, config) {
|
|
|
139
132
|
}
|
|
140
133
|
}
|
|
141
134
|
}
|
|
142
|
-
const
|
|
143
|
-
|
|
144
|
-
const entries = fs.readdirSync(controlsDir, { withFileTypes: true });
|
|
145
|
-
for (const entry of entries) {
|
|
146
|
-
if (entry.isDirectory()) {
|
|
147
|
-
const ctrlFile = path.join(controlsDir, entry.name, "controls.json");
|
|
148
|
-
if (fs.existsSync(ctrlFile)) {
|
|
149
|
-
ids.add(entry.name);
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
catch {
|
|
155
|
-
// controls dir may not exist
|
|
135
|
+
for (const id of getInstalledPackIdsFromDisk(projectPath)) {
|
|
136
|
+
ids.add(id);
|
|
156
137
|
}
|
|
157
138
|
return ids;
|
|
158
139
|
}
|
|
140
|
+
function getFrameworksFromControls(controls) {
|
|
141
|
+
const fwSet = new Set();
|
|
142
|
+
for (const c of controls) {
|
|
143
|
+
if (c.framework)
|
|
144
|
+
fwSet.add(c.framework);
|
|
145
|
+
}
|
|
146
|
+
return [...fwSet];
|
|
147
|
+
}
|
|
159
148
|
export function collectDashboardData(projectPath) {
|
|
160
149
|
const config = loadConfig(projectPath);
|
|
161
150
|
let score = loadScore(projectPath);
|
|
162
|
-
|
|
151
|
+
let baseControls;
|
|
152
|
+
let frameworks;
|
|
153
|
+
if (config) {
|
|
154
|
+
baseControls = loadControlsForConfig(projectPath, config);
|
|
155
|
+
frameworks = config.frameworks;
|
|
156
|
+
}
|
|
157
|
+
else {
|
|
158
|
+
baseControls = loadControlsFromDisk(projectPath);
|
|
159
|
+
frameworks = [];
|
|
160
|
+
}
|
|
163
161
|
const findings = loadFindings(projectPath);
|
|
164
162
|
const controls = updateControlsFromFindings(baseControls, findings);
|
|
165
|
-
if (config) {
|
|
163
|
+
if (config || controls.length > 0) {
|
|
166
164
|
try {
|
|
167
|
-
const
|
|
165
|
+
const scoreFrameworks = getFrameworksFromControls(controls);
|
|
166
|
+
const freshScore = generateScoreFile(controls, scoreFrameworks, findings);
|
|
168
167
|
score = freshScore;
|
|
169
168
|
}
|
|
170
169
|
catch {
|
|
@@ -176,6 +175,7 @@ export function collectDashboardData(projectPath) {
|
|
|
176
175
|
const installedPacks = getInstalledPackIds(projectPath, config || undefined);
|
|
177
176
|
const packs = allPacks.map(p => buildPackSummary(p, controls, findings, installedPacks));
|
|
178
177
|
const fixHistory = loadFixHistory(projectPath);
|
|
178
|
+
const activityLog = loadActivityLog(projectPath);
|
|
179
179
|
const metadataPath = path.join(projectPath, ".ges", "metadata.json");
|
|
180
180
|
let lastAudit = "";
|
|
181
181
|
try {
|
|
@@ -185,16 +185,18 @@ export function collectDashboardData(projectPath) {
|
|
|
185
185
|
catch {
|
|
186
186
|
lastAudit = new Date().toISOString();
|
|
187
187
|
}
|
|
188
|
+
const allFrameworks = getFrameworksFromControls(controls);
|
|
188
189
|
return {
|
|
189
190
|
projectName: config?.project_name || "Unknown Project",
|
|
190
191
|
projectType: config?.project_type || "unknown",
|
|
191
|
-
frameworks:
|
|
192
|
-
gesfVersion: "1.2.
|
|
192
|
+
frameworks: allFrameworks,
|
|
193
|
+
gesfVersion: "1.2.6",
|
|
193
194
|
score,
|
|
194
195
|
controls,
|
|
195
196
|
findings,
|
|
196
197
|
packs,
|
|
197
198
|
fixHistory,
|
|
199
|
+
activityLog,
|
|
198
200
|
lastAudit,
|
|
199
201
|
};
|
|
200
202
|
}
|
|
@@ -203,7 +205,9 @@ export function collectPackDetail(projectPath, packId) {
|
|
|
203
205
|
if (!pack)
|
|
204
206
|
return null;
|
|
205
207
|
const config = loadConfig(projectPath);
|
|
206
|
-
const baseControls = config
|
|
208
|
+
const baseControls = config
|
|
209
|
+
? loadControlsForConfig(projectPath, config)
|
|
210
|
+
: loadControlsFromDisk(projectPath);
|
|
207
211
|
const findings = loadFindings(projectPath);
|
|
208
212
|
const controls = updateControlsFromFindings(baseControls, findings);
|
|
209
213
|
const packControlIds = new Set(pack.controls.map(c => c.id));
|
|
@@ -281,9 +285,9 @@ export function collectPackDetail(projectPath, packId) {
|
|
|
281
285
|
}
|
|
282
286
|
export function collectControlDetail(projectPath, controlId) {
|
|
283
287
|
const config = loadConfig(projectPath);
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
288
|
+
const baseControls = config
|
|
289
|
+
? loadControlsForConfig(projectPath, config)
|
|
290
|
+
: loadControlsFromDisk(projectPath);
|
|
287
291
|
const findings = loadFindings(projectPath);
|
|
288
292
|
const controls = updateControlsFromFindings(baseControls, findings);
|
|
289
293
|
const control = controls.find(c => c.id === controlId);
|
|
@@ -380,6 +384,16 @@ export function startDashboard(options) {
|
|
|
380
384
|
}
|
|
381
385
|
return;
|
|
382
386
|
}
|
|
387
|
+
if (pathname === "/api/activity-log") {
|
|
388
|
+
try {
|
|
389
|
+
const data = collectDashboardData(options.projectPath);
|
|
390
|
+
jsonResponse(res, data.activityLog);
|
|
391
|
+
}
|
|
392
|
+
catch (err) {
|
|
393
|
+
jsonError(res, err instanceof Error ? err.message : String(err));
|
|
394
|
+
}
|
|
395
|
+
return;
|
|
396
|
+
}
|
|
383
397
|
const packMatch = pathname.match(/^\/api\/packs\/([a-z0-9-]+)$/);
|
|
384
398
|
if (packMatch) {
|
|
385
399
|
try {
|
package/dist/template.js
CHANGED
|
@@ -317,8 +317,8 @@ export function renderDashboard(data) {
|
|
|
317
317
|
|
|
318
318
|
<div class="header">
|
|
319
319
|
<div>
|
|
320
|
-
<h1
|
|
321
|
-
<div class="subtitle"
|
|
320
|
+
<h1>${escapeHtml(data.projectName)}</h1>
|
|
321
|
+
<div class="subtitle">GESF v${escapeHtml(data.gesfVersion)}</div>
|
|
322
322
|
</div>
|
|
323
323
|
<div class="nav-tabs">
|
|
324
324
|
<button class="nav-tab active" onclick="showPage('overview', this)">Overview</button>
|
|
@@ -326,6 +326,7 @@ export function renderDashboard(data) {
|
|
|
326
326
|
<button class="nav-tab" onclick="showPage('fixes', this)">Fixes Detail</button>
|
|
327
327
|
<button class="nav-tab" onclick="showPage('findings', this)">Findings</button>
|
|
328
328
|
<button class="nav-tab" onclick="showPage('traceability', this)">Traceability</button>
|
|
329
|
+
<button class="nav-tab" onclick="showPage('activity', this)">Activity Log</button>
|
|
329
330
|
</div>
|
|
330
331
|
</div>
|
|
331
332
|
|
|
@@ -589,12 +590,16 @@ export function renderDashboard(data) {
|
|
|
589
590
|
</div>
|
|
590
591
|
</div>
|
|
591
592
|
|
|
593
|
+
<div id="page-activity" class="page">
|
|
594
|
+
${renderActivityLogSection(data.activityLog || [])}
|
|
595
|
+
</div>
|
|
596
|
+
|
|
592
597
|
<div id="control-detail-modal" style="display:none;"></div>
|
|
593
598
|
|
|
594
599
|
</div>
|
|
595
600
|
|
|
596
601
|
<div class="footer">
|
|
597
|
-
Generated by GESF v${escapeHtml(data.gesfVersion)} | Last audit: ${escapeHtml(new Date(data.lastAudit).toLocaleString())} | <a href="/api/data">JSON API</a> | <a href="/api/packs">Packs API</a> | <a href="/api/fix-history">Fix History API</a>
|
|
602
|
+
Generated by GESF v${escapeHtml(data.gesfVersion)} | Last audit: ${escapeHtml(new Date(data.lastAudit).toLocaleString())} | <a href="/api/data">JSON API</a> | <a href="/api/packs">Packs API</a> | <a href="/api/fix-history">Fix History API</a> | <a href="/api/activity-log">Activity Log API</a>
|
|
598
603
|
</div>
|
|
599
604
|
|
|
600
605
|
<script>
|
|
@@ -615,7 +620,7 @@ export function renderDashboard(data) {
|
|
|
615
620
|
}
|
|
616
621
|
};
|
|
617
622
|
|
|
618
|
-
var navTabMap = { overview: 0, packs: 1, fixes: 2, findings: 3, traceability: 4 };
|
|
623
|
+
var navTabMap = { overview: 0, packs: 1, fixes: 2, findings: 3, traceability: 4, activity: 5 };
|
|
619
624
|
|
|
620
625
|
window.navigateToPage = function(page) {
|
|
621
626
|
var tabs = document.querySelectorAll('.nav-tab');
|
|
@@ -1363,6 +1368,139 @@ function renderComplianceFixCards(issues, idPrefix) {
|
|
|
1363
1368
|
}
|
|
1364
1369
|
return html;
|
|
1365
1370
|
}
|
|
1371
|
+
function renderActivityLogSection(entries) {
|
|
1372
|
+
if (!entries || entries.length === 0) {
|
|
1373
|
+
return `<div class="card">
|
|
1374
|
+
<h2 style="font-size:20px;font-weight:700;margin-bottom:8px;">Activity Log</h2>
|
|
1375
|
+
<p style="color:#6b7280;font-size:14px;margin-bottom:16px;">Every GESF operation performed via CLI or MCP is recorded here — the single source of truth for what GESF did to your project.</p>
|
|
1376
|
+
<div class="empty-state">
|
|
1377
|
+
<div class="icon">📋</div>
|
|
1378
|
+
<div class="msg">No activity recorded yet</div>
|
|
1379
|
+
<div class="sub">Operations will appear here as you use GESF commands (<code style="background:#f3f4f6;padding:2px 6px;border-radius:4px;font-size:12px;">ges init</code>, <code style="background:#f3f4f6;padding:2px 6px;border-radius:4px;font-size:12px;">ges audit</code>, <code style="background:#f3f4f6;padding:2px 6px;border-radius:4px;font-size:12px;">ges fix</code>, MCP tools, etc.)</div>
|
|
1380
|
+
</div>
|
|
1381
|
+
</div>`;
|
|
1382
|
+
}
|
|
1383
|
+
const sorted = [...entries].sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime());
|
|
1384
|
+
const bySource = { cli: entries.filter(e => e.source === "cli").length, mcp: entries.filter(e => e.source === "mcp").length };
|
|
1385
|
+
const byStatus = {
|
|
1386
|
+
success: entries.filter(e => e.status === "success").length,
|
|
1387
|
+
partial: entries.filter(e => e.status === "partial").length,
|
|
1388
|
+
failed: entries.filter(e => e.status === "failed").length,
|
|
1389
|
+
info: entries.filter(e => e.status === "info").length,
|
|
1390
|
+
};
|
|
1391
|
+
const byAction = {};
|
|
1392
|
+
for (const e of entries) {
|
|
1393
|
+
byAction[e.action] = (byAction[e.action] || 0) + 1;
|
|
1394
|
+
}
|
|
1395
|
+
const actionLabels = {
|
|
1396
|
+
init: "Project Init",
|
|
1397
|
+
audit: "Audit Run",
|
|
1398
|
+
fix: "Auto-Fix",
|
|
1399
|
+
policy_install: "Pack Installed",
|
|
1400
|
+
policy_remove: "Pack Removed",
|
|
1401
|
+
control_override: "Control Override",
|
|
1402
|
+
implement_control: "Control Implemented",
|
|
1403
|
+
score: "Score Generated",
|
|
1404
|
+
scan: "Scanners Run",
|
|
1405
|
+
validate: "Validation",
|
|
1406
|
+
generate: "Docs Generated",
|
|
1407
|
+
hooks_install: "Hooks Installed",
|
|
1408
|
+
hooks_uninstall: "Hooks Removed",
|
|
1409
|
+
dashboard_start: "Dashboard Started",
|
|
1410
|
+
badge_generate: "Badge Generated",
|
|
1411
|
+
};
|
|
1412
|
+
const actionColors = {
|
|
1413
|
+
init: "#0f766e",
|
|
1414
|
+
audit: "#3b82f6",
|
|
1415
|
+
fix: "#22c55e",
|
|
1416
|
+
policy_install: "#8b5cf6",
|
|
1417
|
+
policy_remove: "#ef4444",
|
|
1418
|
+
control_override: "#eab308",
|
|
1419
|
+
implement_control: "#22c55e",
|
|
1420
|
+
score: "#3b82f6",
|
|
1421
|
+
scan: "#f97316",
|
|
1422
|
+
validate: "#6b7280",
|
|
1423
|
+
generate: "#0f766e",
|
|
1424
|
+
hooks_install: "#6b7280",
|
|
1425
|
+
hooks_uninstall: "#6b7280",
|
|
1426
|
+
dashboard_start: "#8b5cf6",
|
|
1427
|
+
badge_generate: "#0f766e",
|
|
1428
|
+
};
|
|
1429
|
+
const statusColors = {
|
|
1430
|
+
success: "#22c55e",
|
|
1431
|
+
partial: "#eab308",
|
|
1432
|
+
failed: "#ef4444",
|
|
1433
|
+
info: "#3b82f6",
|
|
1434
|
+
};
|
|
1435
|
+
let html = '';
|
|
1436
|
+
html += `<h2 style="font-size:20px;font-weight:700;margin-bottom:8px;">Activity Log</h2>`;
|
|
1437
|
+
html += `<p style="color:#6b7280;font-size:14px;margin-bottom:16px;">Every GESF operation performed via CLI or MCP is recorded here — the single source of truth for what GESF did to your project.</p>`;
|
|
1438
|
+
html += `<div class="grid grid-4" style="margin-bottom:20px;">`;
|
|
1439
|
+
html += `<div class="card stat"><div class="num">${entries.length}</div><div class="label">Total Operations</div></div>`;
|
|
1440
|
+
html += `<div class="card stat"><div class="num" style="color:#22c55e;">${byStatus.success}</div><div class="label">Successful</div></div>`;
|
|
1441
|
+
html += `<div class="card stat"><div class="num" style="color:#ef4444;">${byStatus.failed + byStatus.partial}</div><div class="label">Failed/Partial</div></div>`;
|
|
1442
|
+
html += `<div class="card stat"><div class="num" style="color:#0f766e;">${bySource.cli}</div><div class="label">CLI Source</div></div>`;
|
|
1443
|
+
html += `</div>`;
|
|
1444
|
+
html += `<div class="grid grid-2" style="margin-bottom:20px;">`;
|
|
1445
|
+
html += `<div class="card"><div class="card-title">Operations by Type</div>`;
|
|
1446
|
+
for (const [action, count] of Object.entries(byAction).sort((a, b) => b[1] - a[1])) {
|
|
1447
|
+
const label = actionLabels[action] || action;
|
|
1448
|
+
const color = actionColors[action] || "#6b7280";
|
|
1449
|
+
html += `<div class="framework-row"><div class="framework-name" style="min-width:160px;font-size:13px;"><span class="badge" style="background:${color};font-size:10px;margin-right:6px;">${label}</span></div><div style="flex:1;"></div><div class="pct-text">${count}</div></div>`;
|
|
1450
|
+
}
|
|
1451
|
+
html += `</div>`;
|
|
1452
|
+
html += `<div class="card"><div class="card-title">Sources & Status</div>`;
|
|
1453
|
+
html += `<div style="display:flex;flex-wrap:wrap;gap:16px;margin-top:8px;">`;
|
|
1454
|
+
html += `<div class="stat"><div class="num" style="font-size:20px;">${bySource.cli}</div><div class="label">CLI</div></div>`;
|
|
1455
|
+
html += `<div class="stat"><div class="num" style="font-size:20px;">${bySource.mcp}</div><div class="label">MCP</div></div>`;
|
|
1456
|
+
html += `<div class="stat"><div class="num" style="font-size:20px;color:#22c55e;">${byStatus.success}</div><div class="label">Success</div></div>`;
|
|
1457
|
+
html += `<div class="stat"><div class="num" style="font-size:20px;color:#ef4444;">${byStatus.failed}</div><div class="label">Failed</div></div>`;
|
|
1458
|
+
html += `</div></div>`;
|
|
1459
|
+
html += `</div>`;
|
|
1460
|
+
html += `<div class="card"><div class="card-title">Timeline (newest first)</div>`;
|
|
1461
|
+
html += `<table><thead><tr><th>Time</th><th>Source</th><th>Action</th><th>Status</th><th>Description</th><th>Impact</th></tr></thead><tbody>`;
|
|
1462
|
+
for (const entry of sorted) {
|
|
1463
|
+
const time = new Date(entry.timestamp).toLocaleString();
|
|
1464
|
+
const sourceBadge = entry.source === "mcp"
|
|
1465
|
+
? '<span class="badge" style="background:#7c3aed;font-size:10px;">MCP</span>'
|
|
1466
|
+
: '<span class="badge" style="background:#0f766e;font-size:10px;">CLI</span>';
|
|
1467
|
+
const actionLabel = actionLabels[entry.action] || entry.action;
|
|
1468
|
+
const actionColor = actionColors[entry.action] || "#6b7280";
|
|
1469
|
+
const actionBadge = `<span class="badge" style="background:${actionColor};font-size:10px;">${escapeHtml(actionLabel)}</span>`;
|
|
1470
|
+
const statusBadge = `<span class="badge badge-status" style="background:${statusColors[entry.status] || '#6b7280'};font-size:10px;">${entry.status.toUpperCase()}</span>`;
|
|
1471
|
+
const impactParts = [];
|
|
1472
|
+
if (entry.details.findings_count !== undefined)
|
|
1473
|
+
impactParts.push(`${entry.details.findings_count} findings`);
|
|
1474
|
+
if (entry.details.fixes_applied !== undefined)
|
|
1475
|
+
impactParts.push(`${entry.details.fixes_applied} fixes`);
|
|
1476
|
+
if (entry.details.packs_affected && entry.details.packs_affected.length > 0)
|
|
1477
|
+
impactParts.push(`Packs: ${entry.details.packs_affected.join(", ")}`);
|
|
1478
|
+
if (entry.details.controls_affected && entry.details.controls_affected.length > 0)
|
|
1479
|
+
impactParts.push(`Controls: ${entry.details.controls_affected.length}`);
|
|
1480
|
+
if (entry.details.files_created && entry.details.files_created.length > 0)
|
|
1481
|
+
impactParts.push(`${entry.details.files_created.length} files created`);
|
|
1482
|
+
if (entry.details.frameworks_added && entry.details.frameworks_added.length > 0)
|
|
1483
|
+
impactParts.push(`Added: ${entry.details.frameworks_added.join(", ")}`);
|
|
1484
|
+
if (entry.details.score !== undefined)
|
|
1485
|
+
impactParts.push(`Score: ${entry.details.score}%`);
|
|
1486
|
+
const impactHtml = impactParts.length > 0
|
|
1487
|
+
? impactParts.map(p => `<div style="font-size:11px;color:#6b7280;margin-bottom:2px;">${escapeHtml(p)}</div>`).join('')
|
|
1488
|
+
: '<span style="color:#9ca3af;">-</span>';
|
|
1489
|
+
html += `<tr>
|
|
1490
|
+
<td style="font-size:11px;white-space:nowrap;">${time}</td>
|
|
1491
|
+
<td>${sourceBadge}</td>
|
|
1492
|
+
<td>${actionBadge}</td>
|
|
1493
|
+
<td>${statusBadge}</td>
|
|
1494
|
+
<td>
|
|
1495
|
+
<div style="font-weight:600;font-size:13px;">${escapeHtml(entry.title)}</div>
|
|
1496
|
+
<div style="font-size:12px;color:#6b7280;">${escapeHtml(entry.description)}</div>
|
|
1497
|
+
</td>
|
|
1498
|
+
<td style="max-width:200px;">${impactHtml}</td>
|
|
1499
|
+
</tr>`;
|
|
1500
|
+
}
|
|
1501
|
+
html += `</tbody></table></div>`;
|
|
1502
|
+
return html;
|
|
1503
|
+
}
|
|
1366
1504
|
function escapeHtml(str) {
|
|
1367
1505
|
return str
|
|
1368
1506
|
.replace(/&/g, "&")
|
package/package.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"dependencies": {
|
|
3
|
-
"@greenarmor/ges-audit-engine": "1.2.
|
|
4
|
-
"@greenarmor/ges-core": "1.2.
|
|
5
|
-
"@greenarmor/ges-policy-engine": "1.2.
|
|
6
|
-
"@greenarmor/ges-scoring-engine": "1.2.
|
|
3
|
+
"@greenarmor/ges-audit-engine": "1.2.6",
|
|
4
|
+
"@greenarmor/ges-core": "1.2.6",
|
|
5
|
+
"@greenarmor/ges-policy-engine": "1.2.6",
|
|
6
|
+
"@greenarmor/ges-scoring-engine": "1.2.6"
|
|
7
7
|
},
|
|
8
8
|
"description": "GESF Web Dashboard - Visual compliance dashboard for teams",
|
|
9
9
|
"devDependencies": {
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
},
|
|
41
41
|
"type": "module",
|
|
42
42
|
"types": "./dist/index.d.ts",
|
|
43
|
-
"version": "1.2.
|
|
43
|
+
"version": "1.2.6",
|
|
44
44
|
"scripts": {
|
|
45
45
|
"build": "tsc",
|
|
46
46
|
"clean": "rm -rf dist tsconfig.tsbuildinfo",
|