iso27001-mcp 0.8.3 → 0.8.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/README.md +1 -1
- package/dist/index.js +303 -16
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
**Turn Claude into an ISO 27001 compliance assistant** — controls, risk register, policies, evidence tracking, SoA generation, and full audit workflows in one local encrypted MCP server.
|
|
4
4
|
|
|
5
|
-
[](https://socket.dev/npm/package/iso27001-mcp/overview/0.8.5)
|
|
6
6
|
[](https://npmjs.com/package/iso27001-mcp)
|
|
7
7
|
[](https://npmjs.com/package/iso27001-mcp)
|
|
8
8
|
[](https://github.com/Sushegaad/MCP-Server-for-ISO27001/actions/workflows/ci.yml)
|
package/dist/index.js
CHANGED
|
@@ -24797,7 +24797,8 @@ var require_package = __commonJS({
|
|
|
24797
24797
|
"package.json"(exports2, module2) {
|
|
24798
24798
|
module2.exports = {
|
|
24799
24799
|
name: "iso27001-mcp",
|
|
24800
|
-
version: "0.8.
|
|
24800
|
+
version: "0.8.5",
|
|
24801
|
+
mcpName: "io.github.Sushegaad/iso27001-mcp",
|
|
24801
24802
|
description: "ISO 27001 compliance workspace for Claude \u2014 controls, risks, policies, evidence, audits, and SoA in one local encrypted MCP server",
|
|
24802
24803
|
license: "MIT",
|
|
24803
24804
|
repository: {
|
|
@@ -26259,13 +26260,24 @@ var MIGRATION_0006 = `-- =======================================================
|
|
|
26259
26260
|
-- without a default; prev_hash is nullable (NULL = first in chain).
|
|
26260
26261
|
ALTER TABLE audit_log ADD COLUMN prev_hash TEXT;
|
|
26261
26262
|
`;
|
|
26263
|
+
var MIGRATION_0007 = `-- ============================================================
|
|
26264
|
+
-- iso27001-mcp Migration 0007 \u2014 Organisation Profile Branding
|
|
26265
|
+
-- Adds optional branding and document personalisation fields.
|
|
26266
|
+
-- !! Never edit this file after it has been applied !!
|
|
26267
|
+
-- ============================================================
|
|
26268
|
+
ALTER TABLE organization_profile ADD COLUMN logo_url TEXT;
|
|
26269
|
+
ALTER TABLE organization_profile ADD COLUMN primary_color TEXT;
|
|
26270
|
+
ALTER TABLE organization_profile ADD COLUMN document_footer TEXT;
|
|
26271
|
+
ALTER TABLE organization_profile ADD COLUMN certification_body TEXT;
|
|
26272
|
+
`;
|
|
26262
26273
|
var MIGRATIONS = [
|
|
26263
26274
|
{ filename: "0001_initial.sql", sql: MIGRATION_0001 },
|
|
26264
26275
|
{ filename: "0002_fts_index.sql", sql: MIGRATION_0002 },
|
|
26265
26276
|
{ filename: "0003_org_profile_procedures.sql", sql: MIGRATION_0003 },
|
|
26266
26277
|
{ filename: "0004_management_review_improvement.sql", sql: MIGRATION_0004 },
|
|
26267
26278
|
{ filename: "0005_evidence_documents.sql", sql: MIGRATION_0005 },
|
|
26268
|
-
{ filename: "0006_audit_log_hmac.sql", sql: MIGRATION_0006 }
|
|
26279
|
+
{ filename: "0006_audit_log_hmac.sql", sql: MIGRATION_0006 },
|
|
26280
|
+
{ filename: "0007_org_profile_branding.sql", sql: MIGRATION_0007 }
|
|
26269
26281
|
];
|
|
26270
26282
|
|
|
26271
26283
|
// src/security/secrets.ts
|
|
@@ -33674,8 +33686,6 @@ var paginationLimit = import_zod.z.number().int().min(1).max(100).optional().def
|
|
|
33674
33686
|
var paginationOffset = import_zod.z.number().int().min(0).optional().default(0);
|
|
33675
33687
|
var versionEnum = import_zod.z.enum(["2022", "2013"]);
|
|
33676
33688
|
var formatMarkdownCsvJson = import_zod.z.enum(["markdown", "csv", "json"]);
|
|
33677
|
-
var formatMarkdownCsv = import_zod.z.enum(["markdown", "csv"]);
|
|
33678
|
-
var formatMarkdownJson = import_zod.z.enum(["markdown", "json"]);
|
|
33679
33689
|
var riskLevelEnum = import_zod.z.enum(["Low", "Medium", "High", "Critical"]);
|
|
33680
33690
|
var likelihood1to5 = import_zod.z.number().int().min(1).max(5);
|
|
33681
33691
|
var roleEnum = import_zod.z.enum(["viewer", "analyst", "admin"]);
|
|
@@ -33934,7 +33944,7 @@ var UpdateSoaEntrySchema = import_zod.z.object({
|
|
|
33934
33944
|
});
|
|
33935
33945
|
var ExportSoaSchema = import_zod.z.object({
|
|
33936
33946
|
soa_id: uuid,
|
|
33937
|
-
format:
|
|
33947
|
+
format: import_zod.z.enum(["markdown", "csv", "html"])
|
|
33938
33948
|
});
|
|
33939
33949
|
var CreateAuditSchema = import_zod.z.object({
|
|
33940
33950
|
name: shortText(200),
|
|
@@ -33971,7 +33981,7 @@ var UpdateCorrectiveActionSchema = import_zod.z.object({
|
|
|
33971
33981
|
});
|
|
33972
33982
|
var GenerateAuditReportSchema = import_zod.z.object({
|
|
33973
33983
|
audit_id: uuid,
|
|
33974
|
-
format:
|
|
33984
|
+
format: import_zod.z.enum(["markdown", "json", "html"])
|
|
33975
33985
|
});
|
|
33976
33986
|
var RegisterEvidenceSchema = import_zod.z.object({
|
|
33977
33987
|
control_id: import_zod.z.string().min(1).max(20),
|
|
@@ -34036,7 +34046,11 @@ var SetOrganizationProfileSchema = import_zod.z.object({
|
|
|
34036
34046
|
isms_manager: shortText(200).optional(),
|
|
34037
34047
|
internal_auditor: shortText(200).optional()
|
|
34038
34048
|
}).optional(),
|
|
34039
|
-
review_cadence_months: import_zod.z.number().int().min(1).max(36).optional().default(12)
|
|
34049
|
+
review_cadence_months: import_zod.z.number().int().min(1).max(36).optional().default(12),
|
|
34050
|
+
logo_url: import_zod.z.string().url().max(2e3).optional(),
|
|
34051
|
+
primary_color: import_zod.z.string().regex(/^#[0-9a-fA-F]{6}$/, "must be 6-digit hex e.g. #1e3a5f").optional(),
|
|
34052
|
+
document_footer: import_zod.z.string().max(500).optional(),
|
|
34053
|
+
certification_body: import_zod.z.string().max(200).optional()
|
|
34040
34054
|
});
|
|
34041
34055
|
var GetOrganizationProfileSchema = import_zod.z.object({});
|
|
34042
34056
|
var procedureTypeEnum = import_zod.z.enum([
|
|
@@ -34090,7 +34104,7 @@ var ListProceduresSchema = import_zod.z.object({
|
|
|
34090
34104
|
});
|
|
34091
34105
|
var ExportProcedureSchema = import_zod.z.object({
|
|
34092
34106
|
procedure_id: uuid,
|
|
34093
|
-
format:
|
|
34107
|
+
format: import_zod.z.enum(["markdown", "json", "html"])
|
|
34094
34108
|
});
|
|
34095
34109
|
var reviewStatusEnum = import_zod.z.enum(["planned", "in_progress", "completed"]);
|
|
34096
34110
|
var reviewInputCategoryEnum = import_zod.z.enum([
|
|
@@ -35415,6 +35429,171 @@ function stripFrontmatter(raw) {
|
|
|
35415
35429
|
}
|
|
35416
35430
|
return { template, clauseMappings, controlMappings };
|
|
35417
35431
|
}
|
|
35432
|
+
function markdownToHtml(md) {
|
|
35433
|
+
const lines = md.split("\n");
|
|
35434
|
+
const out = [];
|
|
35435
|
+
let inTable = false;
|
|
35436
|
+
let inList = false;
|
|
35437
|
+
let tableHeaderDone = false;
|
|
35438
|
+
const esc = (s) => s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
35439
|
+
const inline = (s) => s.replace(/\*\*([^*]+)\*\*/g, "<strong>$1</strong>").replace(/\*([^*]+)\*/g, "<em>$1</em>").replace(/`([^`]+)`/g, "<code>$1</code>");
|
|
35440
|
+
for (const line of lines) {
|
|
35441
|
+
const h1 = line.match(/^# (.+)/);
|
|
35442
|
+
const h2 = line.match(/^## (.+)/);
|
|
35443
|
+
const h3 = line.match(/^### (.+)/);
|
|
35444
|
+
if (h1 || h2 || h3) {
|
|
35445
|
+
if (inList) {
|
|
35446
|
+
out.push("</ul>");
|
|
35447
|
+
inList = false;
|
|
35448
|
+
}
|
|
35449
|
+
if (inTable) {
|
|
35450
|
+
out.push("</tbody></table>");
|
|
35451
|
+
inTable = false;
|
|
35452
|
+
tableHeaderDone = false;
|
|
35453
|
+
}
|
|
35454
|
+
if (h1) {
|
|
35455
|
+
out.push(`<h1>${inline(esc(h1[1]))}</h1>`);
|
|
35456
|
+
continue;
|
|
35457
|
+
}
|
|
35458
|
+
if (h2) {
|
|
35459
|
+
out.push(`<h2>${inline(esc(h2[1]))}</h2>`);
|
|
35460
|
+
continue;
|
|
35461
|
+
}
|
|
35462
|
+
if (h3) {
|
|
35463
|
+
out.push(`<h3>${inline(esc(h3[1]))}</h3>`);
|
|
35464
|
+
continue;
|
|
35465
|
+
}
|
|
35466
|
+
}
|
|
35467
|
+
if (/^---+$/.test(line.trim())) {
|
|
35468
|
+
if (inList) {
|
|
35469
|
+
out.push("</ul>");
|
|
35470
|
+
inList = false;
|
|
35471
|
+
}
|
|
35472
|
+
if (inTable) {
|
|
35473
|
+
out.push("</tbody></table>");
|
|
35474
|
+
inTable = false;
|
|
35475
|
+
tableHeaderDone = false;
|
|
35476
|
+
}
|
|
35477
|
+
out.push("<hr>");
|
|
35478
|
+
continue;
|
|
35479
|
+
}
|
|
35480
|
+
if (line.trim().startsWith("|")) {
|
|
35481
|
+
if (inList) {
|
|
35482
|
+
out.push("</ul>");
|
|
35483
|
+
inList = false;
|
|
35484
|
+
}
|
|
35485
|
+
if (/^\|[-| :]+\|$/.test(line.trim())) {
|
|
35486
|
+
if (!tableHeaderDone) {
|
|
35487
|
+
out.push("<tbody>");
|
|
35488
|
+
tableHeaderDone = true;
|
|
35489
|
+
}
|
|
35490
|
+
continue;
|
|
35491
|
+
}
|
|
35492
|
+
const cells = line.split("|").slice(1, -1).map((c) => c.trim());
|
|
35493
|
+
if (!inTable) {
|
|
35494
|
+
out.push("<table><thead><tr>");
|
|
35495
|
+
cells.forEach((c) => out.push(`<th>${inline(esc(c))}</th>`));
|
|
35496
|
+
out.push("</tr></thead>");
|
|
35497
|
+
inTable = true;
|
|
35498
|
+
tableHeaderDone = false;
|
|
35499
|
+
continue;
|
|
35500
|
+
}
|
|
35501
|
+
out.push("<tr>");
|
|
35502
|
+
cells.forEach((c) => out.push(`<td>${inline(esc(c))}</td>`));
|
|
35503
|
+
out.push("</tr>");
|
|
35504
|
+
continue;
|
|
35505
|
+
}
|
|
35506
|
+
if (inTable) {
|
|
35507
|
+
out.push("</tbody></table>");
|
|
35508
|
+
inTable = false;
|
|
35509
|
+
tableHeaderDone = false;
|
|
35510
|
+
}
|
|
35511
|
+
if (/^[-*] /.test(line)) {
|
|
35512
|
+
if (!inList) {
|
|
35513
|
+
out.push("<ul>");
|
|
35514
|
+
inList = true;
|
|
35515
|
+
}
|
|
35516
|
+
out.push(`<li>${inline(esc(line.replace(/^[-*] /, "")))}</li>`);
|
|
35517
|
+
continue;
|
|
35518
|
+
}
|
|
35519
|
+
if (inList && line.trim() === "") {
|
|
35520
|
+
out.push("</ul>");
|
|
35521
|
+
inList = false;
|
|
35522
|
+
}
|
|
35523
|
+
if (line.trim() === "") {
|
|
35524
|
+
out.push("");
|
|
35525
|
+
continue;
|
|
35526
|
+
}
|
|
35527
|
+
if (!inList) out.push(`<p>${inline(esc(line))}</p>`);
|
|
35528
|
+
}
|
|
35529
|
+
if (inList) out.push("</ul>");
|
|
35530
|
+
if (inTable) out.push("</tbody></table>");
|
|
35531
|
+
return out.join("\n");
|
|
35532
|
+
}
|
|
35533
|
+
function renderHtmlDocument(bodyHtml, meta) {
|
|
35534
|
+
const color = meta.primary_color ?? "#1e3a5f";
|
|
35535
|
+
const footer = meta.document_footer ?? meta.organisation_name;
|
|
35536
|
+
const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
35537
|
+
return `<!DOCTYPE html>
|
|
35538
|
+
<html lang="en">
|
|
35539
|
+
<head>
|
|
35540
|
+
<meta charset="UTF-8">
|
|
35541
|
+
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
|
35542
|
+
<title>${meta.title} \u2014 ${meta.organisation_name}</title>
|
|
35543
|
+
<style>
|
|
35544
|
+
*,*::before,*::after{box-sizing:border-box}
|
|
35545
|
+
body{font-family:'Segoe UI',Arial,sans-serif;font-size:11pt;line-height:1.6;color:#1a1a1a;margin:0;padding:0;background:#fff}
|
|
35546
|
+
.doc-header{background:${color};color:#fff;padding:24px 40px 18px;display:flex;align-items:center;gap:20px}
|
|
35547
|
+
.doc-header-logo{max-height:48px;max-width:160px;object-fit:contain}
|
|
35548
|
+
.doc-header-text{flex:1}
|
|
35549
|
+
.doc-header h1{margin:0 0 4px;font-size:18pt;font-weight:700;color:#fff}
|
|
35550
|
+
.doc-header-sub{font-size:10pt;opacity:0.85}
|
|
35551
|
+
.doc-meta-bar{background:#f5f7fa;border-bottom:2px solid ${color};padding:10px 40px;display:flex;gap:32px;font-size:9pt;color:#444}
|
|
35552
|
+
.doc-meta-item strong{color:#111}
|
|
35553
|
+
.doc-body{padding:28px 40px;max-width:900px}
|
|
35554
|
+
h1{font-size:16pt;color:${color};margin-top:24px;border-bottom:2px solid ${color};padding-bottom:4px}
|
|
35555
|
+
h2{font-size:13pt;color:${color};margin-top:20px}
|
|
35556
|
+
h3{font-size:11pt;color:#333;margin-top:16px}
|
|
35557
|
+
table{width:100%;border-collapse:collapse;margin:14px 0;font-size:10pt}
|
|
35558
|
+
th{background:${color};color:#fff;padding:7px 10px;text-align:left;font-weight:600}
|
|
35559
|
+
td{padding:6px 10px;border-bottom:1px solid #e0e4ea;vertical-align:top}
|
|
35560
|
+
tr:nth-child(even) td{background:#f8f9fb}
|
|
35561
|
+
ul{padding-left:20px;margin:8px 0}
|
|
35562
|
+
li{margin:3px 0}
|
|
35563
|
+
hr{border:none;border-top:1px solid #dce1ea;margin:20px 0}
|
|
35564
|
+
p{margin:8px 0}
|
|
35565
|
+
code{background:#f0f2f5;padding:1px 5px;border-radius:3px;font-family:monospace;font-size:10pt}
|
|
35566
|
+
.doc-footer{margin-top:40px;padding:14px 40px;background:#f5f7fa;border-top:2px solid ${color};font-size:9pt;color:#666;display:flex;justify-content:space-between}
|
|
35567
|
+
@media print{
|
|
35568
|
+
body{font-size:10pt}
|
|
35569
|
+
.doc-header,.doc-meta-bar,th,.doc-footer{-webkit-print-color-adjust:exact;print-color-adjust:exact}
|
|
35570
|
+
h1,h2{page-break-after:avoid}
|
|
35571
|
+
table{page-break-inside:avoid}
|
|
35572
|
+
}
|
|
35573
|
+
</style>
|
|
35574
|
+
</head>
|
|
35575
|
+
<body>
|
|
35576
|
+
<div class="doc-header">
|
|
35577
|
+
${meta.logo_url ? `<img class="doc-header-logo" src="${meta.logo_url}" alt="${meta.organisation_name} logo">` : ""}
|
|
35578
|
+
<div class="doc-header-text">
|
|
35579
|
+
<h1>${meta.title}</h1>
|
|
35580
|
+
<div class="doc-header-sub">${meta.organisation_name}${meta.doc_type ? ` \xB7 ${meta.doc_type}` : ""}</div>
|
|
35581
|
+
</div>
|
|
35582
|
+
</div>
|
|
35583
|
+
<div class="doc-meta-bar">
|
|
35584
|
+
${meta.version ? `<span><strong>Version:</strong> ${meta.version}</span>` : ""}
|
|
35585
|
+
${meta.effective_date ? `<span><strong>Effective:</strong> ${meta.effective_date}</span>` : ""}
|
|
35586
|
+
${meta.owner ? `<span><strong>Owner:</strong> ${meta.owner}</span>` : ""}
|
|
35587
|
+
<span><strong>Generated:</strong> ${today}</span>
|
|
35588
|
+
</div>
|
|
35589
|
+
<div class="doc-body">${bodyHtml}</div>
|
|
35590
|
+
<div class="doc-footer">
|
|
35591
|
+
<span>${footer}</span>
|
|
35592
|
+
<span>INTERNAL \u2014 ISMS Controlled Document \xB7 Generated ${today}</span>
|
|
35593
|
+
</div>
|
|
35594
|
+
</body>
|
|
35595
|
+
</html>`;
|
|
35596
|
+
}
|
|
35418
35597
|
|
|
35419
35598
|
// src/tools/org-profile.ts
|
|
35420
35599
|
var ORG_PROFILE_ID = "00000000-0000-4000-8000-000000000001";
|
|
@@ -35422,11 +35601,15 @@ function ok4(data) {
|
|
|
35422
35601
|
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }], isError: false };
|
|
35423
35602
|
}
|
|
35424
35603
|
function loadOrgProfileDefaults(db) {
|
|
35425
|
-
const row = db.prepare("SELECT legal_entity_name, isms_scope_statement FROM organization_profile WHERE id = ?").get(ORG_PROFILE_ID);
|
|
35604
|
+
const row = db.prepare("SELECT legal_entity_name, isms_scope_statement, logo_url, primary_color, document_footer, certification_body FROM organization_profile WHERE id = ?").get(ORG_PROFILE_ID);
|
|
35426
35605
|
if (!row) return null;
|
|
35427
35606
|
return {
|
|
35428
35607
|
organisation_name: row.legal_entity_name,
|
|
35429
|
-
scope: row.isms_scope_statement
|
|
35608
|
+
scope: row.isms_scope_statement,
|
|
35609
|
+
logo_url: row.logo_url,
|
|
35610
|
+
primary_color: row.primary_color,
|
|
35611
|
+
document_footer: row.document_footer,
|
|
35612
|
+
certification_body: row.certification_body
|
|
35430
35613
|
};
|
|
35431
35614
|
}
|
|
35432
35615
|
function handleSetOrganizationProfile(args2) {
|
|
@@ -35438,17 +35621,22 @@ function handleSetOrganizationProfile(args2) {
|
|
|
35438
35621
|
isms_scope_statement,
|
|
35439
35622
|
declared_exclusions,
|
|
35440
35623
|
raci_roles,
|
|
35441
|
-
review_cadence_months = 12
|
|
35624
|
+
review_cadence_months = 12,
|
|
35625
|
+
logo_url,
|
|
35626
|
+
primary_color,
|
|
35627
|
+
document_footer,
|
|
35628
|
+
certification_body
|
|
35442
35629
|
} = args2;
|
|
35443
35630
|
const ts = now();
|
|
35444
35631
|
getDb().prepare(`
|
|
35445
35632
|
INSERT OR REPLACE INTO organization_profile
|
|
35446
35633
|
(id, legal_entity_name, registered_jurisdiction, regulatory_licences,
|
|
35447
35634
|
in_scope_activities, isms_scope_statement, declared_exclusions,
|
|
35448
|
-
raci_roles, review_cadence_months, created_at, updated_at
|
|
35635
|
+
raci_roles, review_cadence_months, created_at, updated_at,
|
|
35636
|
+
logo_url, primary_color, document_footer, certification_body)
|
|
35449
35637
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, COALESCE(
|
|
35450
35638
|
(SELECT created_at FROM organization_profile WHERE id = ?), ?
|
|
35451
|
-
), ?)
|
|
35639
|
+
), ?, ?, ?, ?, ?)
|
|
35452
35640
|
`).run(
|
|
35453
35641
|
ORG_PROFILE_ID,
|
|
35454
35642
|
legal_entity_name,
|
|
@@ -35461,7 +35649,11 @@ function handleSetOrganizationProfile(args2) {
|
|
|
35461
35649
|
review_cadence_months,
|
|
35462
35650
|
ORG_PROFILE_ID,
|
|
35463
35651
|
ts,
|
|
35464
|
-
ts
|
|
35652
|
+
ts,
|
|
35653
|
+
logo_url ?? null,
|
|
35654
|
+
primary_color ?? null,
|
|
35655
|
+
document_footer ?? null,
|
|
35656
|
+
certification_body ?? null
|
|
35465
35657
|
);
|
|
35466
35658
|
return ok4({
|
|
35467
35659
|
id: ORG_PROFILE_ID,
|
|
@@ -35829,6 +36021,44 @@ function handleExportSoa(args2) {
|
|
|
35829
36021
|
`).all(soa.isms_version, soa_id);
|
|
35830
36022
|
const includedCount = entries.filter((e) => e.included === 1).length;
|
|
35831
36023
|
const excludedCount = entries.filter((e) => e.included === 0).length;
|
|
36024
|
+
const ORG_PROFILE_ID_SOA = "00000000-0000-4000-8000-000000000001";
|
|
36025
|
+
if (format === "html") {
|
|
36026
|
+
const profileRow = db.prepare(
|
|
36027
|
+
"SELECT legal_entity_name, logo_url, primary_color, document_footer FROM organization_profile WHERE id = ?"
|
|
36028
|
+
).get(ORG_PROFILE_ID_SOA);
|
|
36029
|
+
const tableRows = entries.map((e) => {
|
|
36030
|
+
const inc = e.included === 1;
|
|
36031
|
+
return `<tr>
|
|
36032
|
+
<td><strong>${e.control_id}</strong></td>
|
|
36033
|
+
<td>${e.control_name ?? "\u2014"}</td>
|
|
36034
|
+
<td>${e.theme ?? "\u2014"}</td>
|
|
36035
|
+
<td style="color:${inc ? "#065f46" : "#991b1b"};font-weight:600">${inc ? "\u2713 Yes" : "\u2717 No"}</td>
|
|
36036
|
+
<td>${e.justification}</td>
|
|
36037
|
+
<td>${e.status ?? "\u2014"}</td>
|
|
36038
|
+
<td>${e.responsible_party ?? "\u2014"}</td>
|
|
36039
|
+
</tr>`;
|
|
36040
|
+
}).join("\n");
|
|
36041
|
+
const bodyHtml = `
|
|
36042
|
+
<p><strong>ISMS Version:</strong> ISO 27001:${soa.isms_version} \xB7
|
|
36043
|
+
<strong>Total:</strong> ${entries.length} \xB7
|
|
36044
|
+
<strong>Included:</strong> ${includedCount} \xB7
|
|
36045
|
+
<strong>Excluded:</strong> ${excludedCount}</p>
|
|
36046
|
+
<table>
|
|
36047
|
+
<thead>
|
|
36048
|
+
<tr><th>Control ID</th><th>Name</th><th>Theme</th><th>Included</th><th>Justification</th><th>Status</th><th>Responsible Party</th></tr>
|
|
36049
|
+
</thead>
|
|
36050
|
+
<tbody>${tableRows}</tbody>
|
|
36051
|
+
</table>`;
|
|
36052
|
+
const html = renderHtmlDocument(bodyHtml, {
|
|
36053
|
+
title: "Statement of Applicability",
|
|
36054
|
+
organisation_name: profileRow?.legal_entity_name ?? "Organisation",
|
|
36055
|
+
logo_url: profileRow?.logo_url,
|
|
36056
|
+
primary_color: profileRow?.primary_color,
|
|
36057
|
+
document_footer: profileRow?.document_footer,
|
|
36058
|
+
doc_type: `ISO 27001:${soa.isms_version}`
|
|
36059
|
+
});
|
|
36060
|
+
return ok6({ format: "html", content: html });
|
|
36061
|
+
}
|
|
35832
36062
|
if (format === "csv") {
|
|
35833
36063
|
const header = "control_id,name,theme,included,justification,status,responsible_party";
|
|
35834
36064
|
const rows = entries.map(
|
|
@@ -36062,6 +36292,46 @@ function handleGenerateAuditReport(args2) {
|
|
|
36062
36292
|
carsByFinding.set(car.finding_id, existing);
|
|
36063
36293
|
}
|
|
36064
36294
|
const countByType = (type) => findings.filter((f) => f.type === type).length;
|
|
36295
|
+
if (args2.format === "html") {
|
|
36296
|
+
const mdLines = [
|
|
36297
|
+
`# ${audit.name}`,
|
|
36298
|
+
``,
|
|
36299
|
+
`**Auditor:** ${audit.auditor} `,
|
|
36300
|
+
`**Planned Date:** ${audit.planned_date} `,
|
|
36301
|
+
`**Status:** ${audit.status} `,
|
|
36302
|
+
`**Scope:** ${audit.scope}`,
|
|
36303
|
+
``,
|
|
36304
|
+
`## Findings (${findings.length})`,
|
|
36305
|
+
``,
|
|
36306
|
+
`| ID | Type | Severity | Clause/Control | Description |`,
|
|
36307
|
+
`|---|---|---|---|---|`,
|
|
36308
|
+
...findings.map(
|
|
36309
|
+
(f) => `| ${f.id.slice(-8)} | ${f.type.toUpperCase()} | ${f.severity ?? "\u2014"} | ${f.clause_or_control} | ${f.description.slice(0, 80)} |`
|
|
36310
|
+
),
|
|
36311
|
+
``,
|
|
36312
|
+
`## Corrective Actions (${cars.length})`,
|
|
36313
|
+
``,
|
|
36314
|
+
`| CAR ID | Finding | Owner | Due Date | Status | Verified |`,
|
|
36315
|
+
`|---|---|---|---|---|---|`,
|
|
36316
|
+
...cars.map(
|
|
36317
|
+
(c) => `| ${c.id.slice(-8)} | ${c.finding_id.slice(-8)} | ${c.owner} | ${c.due_date} | ${c.status} | ${c.effectiveness_verified ? "\u2713 Yes" : "No"} |`
|
|
36318
|
+
)
|
|
36319
|
+
];
|
|
36320
|
+
const db2 = getDb();
|
|
36321
|
+
const ORG_PROFILE_ID_AUDIT = "00000000-0000-4000-8000-000000000001";
|
|
36322
|
+
const profileRow = db2.prepare(
|
|
36323
|
+
"SELECT legal_entity_name, logo_url, primary_color, document_footer FROM organization_profile WHERE id = ?"
|
|
36324
|
+
).get(ORG_PROFILE_ID_AUDIT);
|
|
36325
|
+
const html = renderHtmlDocument(markdownToHtml(mdLines.join("\n")), {
|
|
36326
|
+
title: audit.name,
|
|
36327
|
+
organisation_name: profileRow?.legal_entity_name ?? "Organisation",
|
|
36328
|
+
logo_url: profileRow?.logo_url,
|
|
36329
|
+
primary_color: profileRow?.primary_color,
|
|
36330
|
+
document_footer: profileRow?.document_footer,
|
|
36331
|
+
doc_type: "Internal Audit Report"
|
|
36332
|
+
});
|
|
36333
|
+
return ok7({ format: "html", content: html });
|
|
36334
|
+
}
|
|
36065
36335
|
if (format === "json") {
|
|
36066
36336
|
return ok7({
|
|
36067
36337
|
audit: shapeAudit(audit),
|
|
@@ -36657,6 +36927,23 @@ function handleExportProcedure(args2) {
|
|
|
36657
36927
|
const db = getDb();
|
|
36658
36928
|
const row = db.prepare("SELECT * FROM procedures WHERE id = ?").get(procedure_id);
|
|
36659
36929
|
if (!row) throw notFound("procedure", procedure_id);
|
|
36930
|
+
if (format === "html") {
|
|
36931
|
+
const db2 = getDb();
|
|
36932
|
+
const defaults = loadOrgProfileDefaults(db2);
|
|
36933
|
+
const bodyHtml = markdownToHtml(row.content);
|
|
36934
|
+
const html = renderHtmlDocument(bodyHtml, {
|
|
36935
|
+
title: row.procedure_type.replace(/_/g, " ").replace(/\b\w/g, (l) => l.toUpperCase()) + " Procedure",
|
|
36936
|
+
organisation_name: row.organisation_name,
|
|
36937
|
+
logo_url: defaults?.logo_url,
|
|
36938
|
+
primary_color: defaults?.primary_color,
|
|
36939
|
+
document_footer: defaults?.document_footer,
|
|
36940
|
+
version: String(row.version),
|
|
36941
|
+
effective_date: row.effective_date,
|
|
36942
|
+
owner: row.owner,
|
|
36943
|
+
doc_type: "Procedure"
|
|
36944
|
+
});
|
|
36945
|
+
return ok9({ format: "html", content: html });
|
|
36946
|
+
}
|
|
36660
36947
|
if (format === "markdown") {
|
|
36661
36948
|
const relatedControls = fromJsonArray(row.related_controls);
|
|
36662
36949
|
const controlsSection = relatedControls.length > 0 ? `
|
|
@@ -38626,12 +38913,12 @@ ${divider}
|
|
|
38626
38913
|
} else {
|
|
38627
38914
|
try {
|
|
38628
38915
|
const rows = openDb(dbPath2).prepare("SELECT filename FROM _migrations ORDER BY id").all();
|
|
38629
|
-
const passed2 = rows.length >=
|
|
38916
|
+
const passed2 = rows.length >= 7;
|
|
38630
38917
|
check(
|
|
38631
38918
|
"Migrations",
|
|
38632
38919
|
passed2,
|
|
38633
38920
|
false,
|
|
38634
|
-
`${rows.length}/
|
|
38921
|
+
`${rows.length}/7 applied${passed2 ? "" : " \u2014 DB may need re-initialisation"}`
|
|
38635
38922
|
);
|
|
38636
38923
|
record(passed2);
|
|
38637
38924
|
} catch {
|
package/package.json
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "iso27001-mcp",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.5",
|
|
4
|
+
"mcpName": "io.github.Sushegaad/iso27001-mcp",
|
|
4
5
|
"description": "ISO 27001 compliance workspace for Claude — controls, risks, policies, evidence, audits, and SoA in one local encrypted MCP server",
|
|
5
6
|
"license": "MIT",
|
|
6
7
|
"repository": {
|