@greenarmor/ges-web-dashboard 1.2.4 → 1.2.5
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 +59 -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,40 @@ 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 = frameworks.length > 0
|
|
166
|
+
? frameworks
|
|
167
|
+
: getFrameworksFromControls(controls);
|
|
168
|
+
const freshScore = generateScoreFile(controls, scoreFrameworks, findings);
|
|
168
169
|
score = freshScore;
|
|
169
170
|
}
|
|
170
171
|
catch {
|
|
@@ -176,6 +177,7 @@ export function collectDashboardData(projectPath) {
|
|
|
176
177
|
const installedPacks = getInstalledPackIds(projectPath, config || undefined);
|
|
177
178
|
const packs = allPacks.map(p => buildPackSummary(p, controls, findings, installedPacks));
|
|
178
179
|
const fixHistory = loadFixHistory(projectPath);
|
|
180
|
+
const activityLog = loadActivityLog(projectPath);
|
|
179
181
|
const metadataPath = path.join(projectPath, ".ges", "metadata.json");
|
|
180
182
|
let lastAudit = "";
|
|
181
183
|
try {
|
|
@@ -185,16 +187,22 @@ export function collectDashboardData(projectPath) {
|
|
|
185
187
|
catch {
|
|
186
188
|
lastAudit = new Date().toISOString();
|
|
187
189
|
}
|
|
190
|
+
const allFrameworks = new Set(frameworks);
|
|
191
|
+
for (const c of controls) {
|
|
192
|
+
if (c.framework)
|
|
193
|
+
allFrameworks.add(c.framework);
|
|
194
|
+
}
|
|
188
195
|
return {
|
|
189
196
|
projectName: config?.project_name || "Unknown Project",
|
|
190
197
|
projectType: config?.project_type || "unknown",
|
|
191
|
-
frameworks:
|
|
192
|
-
gesfVersion: "1.2.
|
|
198
|
+
frameworks: [...allFrameworks],
|
|
199
|
+
gesfVersion: "1.2.5",
|
|
193
200
|
score,
|
|
194
201
|
controls,
|
|
195
202
|
findings,
|
|
196
203
|
packs,
|
|
197
204
|
fixHistory,
|
|
205
|
+
activityLog,
|
|
198
206
|
lastAudit,
|
|
199
207
|
};
|
|
200
208
|
}
|
|
@@ -203,7 +211,9 @@ export function collectPackDetail(projectPath, packId) {
|
|
|
203
211
|
if (!pack)
|
|
204
212
|
return null;
|
|
205
213
|
const config = loadConfig(projectPath);
|
|
206
|
-
const baseControls = config
|
|
214
|
+
const baseControls = config
|
|
215
|
+
? loadControlsForConfig(projectPath, config)
|
|
216
|
+
: loadControlsFromDisk(projectPath);
|
|
207
217
|
const findings = loadFindings(projectPath);
|
|
208
218
|
const controls = updateControlsFromFindings(baseControls, findings);
|
|
209
219
|
const packControlIds = new Set(pack.controls.map(c => c.id));
|
|
@@ -281,9 +291,9 @@ export function collectPackDetail(projectPath, packId) {
|
|
|
281
291
|
}
|
|
282
292
|
export function collectControlDetail(projectPath, controlId) {
|
|
283
293
|
const config = loadConfig(projectPath);
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
294
|
+
const baseControls = config
|
|
295
|
+
? loadControlsForConfig(projectPath, config)
|
|
296
|
+
: loadControlsFromDisk(projectPath);
|
|
287
297
|
const findings = loadFindings(projectPath);
|
|
288
298
|
const controls = updateControlsFromFindings(baseControls, findings);
|
|
289
299
|
const control = controls.find(c => c.id === controlId);
|
|
@@ -380,6 +390,16 @@ export function startDashboard(options) {
|
|
|
380
390
|
}
|
|
381
391
|
return;
|
|
382
392
|
}
|
|
393
|
+
if (pathname === "/api/activity-log") {
|
|
394
|
+
try {
|
|
395
|
+
const data = collectDashboardData(options.projectPath);
|
|
396
|
+
jsonResponse(res, data.activityLog);
|
|
397
|
+
}
|
|
398
|
+
catch (err) {
|
|
399
|
+
jsonError(res, err instanceof Error ? err.message : String(err));
|
|
400
|
+
}
|
|
401
|
+
return;
|
|
402
|
+
}
|
|
383
403
|
const packMatch = pathname.match(/^\/api\/packs\/([a-z0-9-]+)$/);
|
|
384
404
|
if (packMatch) {
|
|
385
405
|
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.5",
|
|
4
|
+
"@greenarmor/ges-core": "1.2.5",
|
|
5
|
+
"@greenarmor/ges-policy-engine": "1.2.5",
|
|
6
|
+
"@greenarmor/ges-scoring-engine": "1.2.5"
|
|
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.5",
|
|
44
44
|
"scripts": {
|
|
45
45
|
"build": "tsc",
|
|
46
46
|
"clean": "rm -rf dist tsconfig.tsbuildinfo",
|