filegrc 0.9.0 → 0.9.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/model/v6.json +1 -1
- package/package.json +1 -1
- package/src/collection-revision.js +8 -3
- package/src/collection-scope.js +32 -8
- package/src/policy-library/information-security-policy-v2.md +2 -2
- package/src/policy-library.js +37 -8
- package/src/program-path.js +1 -1
- package/src/program-readiness.js +16 -4
package/model/v6.json
CHANGED
|
@@ -1128,7 +1128,7 @@
|
|
|
1128
1128
|
"pluralTitle": "Systems",
|
|
1129
1129
|
"group": "systems-vendors",
|
|
1130
1130
|
"collection": "systems",
|
|
1131
|
-
"description": "The complete bounded system being governed or examined, including its services, boundary, information, Components, Controls, dependencies, and continuity objectives.",
|
|
1131
|
+
"description": "The complete bounded system being governed or examined, including its services, boundary, information, Components, Controls, dependencies, and any applicable continuity objectives.",
|
|
1132
1132
|
"guidance": {
|
|
1133
1133
|
"policyBasis": "A SOC 2 program starts with the bounded System and the service commitments and system requirements the company has chosen to meet.",
|
|
1134
1134
|
"cadence": "Define before selecting Components and Controls. Review after material service, boundary, architecture, information, continuity, or audit-scope changes.",
|
package/package.json
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import { readFileSync } from "node:fs";
|
|
3
3
|
import {
|
|
4
|
+
authoritativeSourceRevisionValue,
|
|
4
5
|
collectionRevisionInputs,
|
|
5
6
|
collectionScopeRevisionFacts
|
|
6
7
|
} from "./collection-scope.js";
|
|
@@ -16,15 +17,19 @@ export function collectionRevision(loaded, resourceType, options = {}) {
|
|
|
16
17
|
.map((input) => [input.record.id, input]));
|
|
17
18
|
const authoritativeSource = loaded.resources.find(({ id }) => id === options.authoritativeSourceId);
|
|
18
19
|
if (authoritativeSource) {
|
|
19
|
-
inputs.set(authoritativeSource.id, {
|
|
20
|
+
inputs.set(authoritativeSource.id, {
|
|
21
|
+
record: authoritativeSource,
|
|
22
|
+
value: authoritativeSourceRevisionValue(authoritativeSource),
|
|
23
|
+
includeContent: true
|
|
24
|
+
});
|
|
20
25
|
}
|
|
21
26
|
const records = [...inputs.values()]
|
|
22
|
-
.map(({ record, value }) => ({
|
|
27
|
+
.map(({ record, value, includeContent }) => ({
|
|
23
28
|
id: record.id,
|
|
24
29
|
revision: createHash("sha256")
|
|
25
30
|
.update(JSON.stringify(canonicalRecordValue(loaded.model, record.type, value)))
|
|
26
31
|
.digest("hex"),
|
|
27
|
-
contentRevisions: markdownEntries(loaded.model, record).flatMap(({ path }) => {
|
|
32
|
+
contentRevisions: (includeContent ? markdownEntries(loaded.model, record) : []).flatMap(({ path }) => {
|
|
28
33
|
try {
|
|
29
34
|
const content = readFileSync(resolveDataPath(loaded.root, path), "utf8");
|
|
30
35
|
return [{ path, revision: createHash("sha256").update(content).digest("hex") }];
|
package/src/collection-scope.js
CHANGED
|
@@ -39,7 +39,7 @@ export function scopedCollectionRecords(loaded, resourceType, program) {
|
|
|
39
39
|
export function collectionRevisionInputs(loaded, resourceType, program) {
|
|
40
40
|
const reviewed = scopedCollectionRecords(loaded, resourceType, program);
|
|
41
41
|
if (!modelSupports(loaded.model, "program-scope")) {
|
|
42
|
-
return reviewed.map((record) => ({ record, value: record }));
|
|
42
|
+
return reviewed.map((record) => ({ record, value: record, includeContent: true }));
|
|
43
43
|
}
|
|
44
44
|
const byId = new Map(loaded.resources.map((record) => [record.id, record]));
|
|
45
45
|
const records = new Map(reviewed.map((record) => [record.id, record]));
|
|
@@ -90,7 +90,9 @@ export function collectionRevisionInputs(loaded, resourceType, program) {
|
|
|
90
90
|
record,
|
|
91
91
|
value: reviewedIds.has(record.id)
|
|
92
92
|
? record
|
|
93
|
-
: dependencyRevisionValue(resourceType, record)
|
|
93
|
+
: dependencyRevisionValue(resourceType, record),
|
|
94
|
+
includeContent: reviewedIds.has(record.id)
|
|
95
|
+
|| dependencyContentAffectsRevision(resourceType, record.type)
|
|
94
96
|
}));
|
|
95
97
|
}
|
|
96
98
|
|
|
@@ -138,9 +140,20 @@ export function collectionScopeRevisionFacts(loaded, resourceType, program) {
|
|
|
138
140
|
return common;
|
|
139
141
|
}
|
|
140
142
|
|
|
143
|
+
export function authoritativeSourceRevisionValue(record) {
|
|
144
|
+
return Object.fromEntries(Object.entries(record).filter(([field]) => (
|
|
145
|
+
!authoritativeSourceBookkeepingFields.has(field)
|
|
146
|
+
)));
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const authoritativeSourceBookkeepingFields = new Set([
|
|
150
|
+
"tags",
|
|
151
|
+
"statusTransition"
|
|
152
|
+
]);
|
|
153
|
+
|
|
141
154
|
const dependencyFields = {
|
|
142
155
|
framework: {
|
|
143
|
-
system: ["id", "type", "status", "purpose", "servicesProvided", "boundary", "exclusions", "informationTypeIds", "classificationId", "internetExposed"],
|
|
156
|
+
system: ["id", "type", "status", "purpose", "servicesProvided", "boundary", "exclusions", "criticality", "informationTypeIds", "classificationId", "internetExposed", "continuityObjectives"],
|
|
144
157
|
requirement: ["id", "type", "title", "frameworkId", "reference", "description", "parentRequirementId"]
|
|
145
158
|
},
|
|
146
159
|
vendor: {
|
|
@@ -154,22 +167,29 @@ const dependencyFields = {
|
|
|
154
167
|
},
|
|
155
168
|
component: {
|
|
156
169
|
system: ["id", "type", "status", "purpose", "servicesProvided", "boundary", "exclusions", "criticality", "informationTypeIds", "classificationId", "internetExposed", "continuityObjectives"],
|
|
157
|
-
control: ["id", "type", "status", "
|
|
158
|
-
vendor: ["id", "type", "status", "category", "criticality", "description", "
|
|
170
|
+
control: ["id", "type", "status", "systemIds", "componentIds", "evidenceSourceComponentIds"],
|
|
171
|
+
vendor: ["id", "type", "status", "category", "criticality", "description", "startDate", "endDate"],
|
|
159
172
|
classification: ["id", "type", "status", "rank", "description", "handlingRequirements"],
|
|
160
173
|
"information-type": ["id", "type", "status", "classificationId", "description"]
|
|
161
174
|
},
|
|
162
175
|
"complementary-control": {
|
|
163
|
-
system: ["id", "type", "status", "purpose", "servicesProvided", "boundary", "exclusions", "informationTypeIds", "classificationId", "internetExposed"],
|
|
164
|
-
control: ["id", "type", "status", "statement", "activity", "
|
|
176
|
+
system: ["id", "type", "status", "purpose", "servicesProvided", "boundary", "exclusions", "criticality", "informationTypeIds", "classificationId", "internetExposed", "continuityObjectives"],
|
|
177
|
+
control: ["id", "type", "status", "statement", "activity", "systemIds", "componentIds", "evidenceSourceComponentIds"],
|
|
165
178
|
vendor: ["id", "type", "status", "category", "criticality", "description", "standardAgreement", "agreementDocumentId", "startDate", "endDate", "classificationId", "informationTypeIds"],
|
|
166
179
|
requirement: ["id", "type", "title", "frameworkId", "reference", "description", "parentRequirementId"],
|
|
167
180
|
commitment: ["id", "type", "status", "commitmentKind", "statement", "systemIds", "requirementIds", "controlIds", "customerFacing", "effectiveOn"],
|
|
168
181
|
document: ["id", "type", "status", "documentKind", "version", "effectiveOn", "approvedOn", "approvedContentRevisions", "systemIds", "controlIds", "componentIds", "classificationId"],
|
|
169
|
-
component: ["id", "type", "status", "componentKind", "description", "vendorId", "systemUses", "informationUses", "internetExposed"]
|
|
182
|
+
component: ["id", "type", "status", "componentKind", "description", "criticality", "vendorId", "systemUses", "informationUses", "internetExposed", "classificationId", "continuityObjectives"]
|
|
170
183
|
}
|
|
171
184
|
};
|
|
172
185
|
|
|
186
|
+
const dependencyContentTypes = {
|
|
187
|
+
framework: new Set(["system"]),
|
|
188
|
+
vendor: new Set(["document"]),
|
|
189
|
+
component: new Set(["system"]),
|
|
190
|
+
"complementary-control": new Set(["system", "control", "document", "component"])
|
|
191
|
+
};
|
|
192
|
+
|
|
173
193
|
function dependencyRevisionValue(resourceType, record) {
|
|
174
194
|
const fields = dependencyFields[resourceType]?.[record.type];
|
|
175
195
|
if (!fields) return { id: record.id, type: record.type };
|
|
@@ -178,6 +198,10 @@ function dependencyRevisionValue(resourceType, record) {
|
|
|
178
198
|
.map((field) => [field, record[field]]));
|
|
179
199
|
}
|
|
180
200
|
|
|
201
|
+
function dependencyContentAffectsRevision(resourceType, dependencyType) {
|
|
202
|
+
return dependencyContentTypes[resourceType]?.has(dependencyType) || false;
|
|
203
|
+
}
|
|
204
|
+
|
|
181
205
|
function sorted(values) {
|
|
182
206
|
return [...(values || [])].sort();
|
|
183
207
|
}
|
|
@@ -242,13 +242,13 @@ Reported events receive an owner, assessment, and documented resolution or escal
|
|
|
242
242
|
|
|
243
243
|
## Business Continuity and Disaster Recovery Policy
|
|
244
244
|
|
|
245
|
-
Each important System records
|
|
245
|
+
Each important System records recovery priorities, dependencies, responsible people, alternate communication and access needs, and a backup or alternate recovery approach suited to its commitments, business impact, data risk, dependencies, and technical capability. Numeric recovery targets are required only when an approved customer commitment, included Availability criterion, or management risk decision calls for them.
|
|
246
246
|
|
|
247
247
|
The Security Incident and Recovery Plan records activation, communication, response, recovery, and return-to-normal responsibilities. Management tests continuity and disaster recovery on the approved schedule, records results and findings, and tracks follow-up work.
|
|
248
248
|
|
|
249
249
|
## Backup and Restoration Policy
|
|
250
250
|
|
|
251
|
-
Important Systems use backups or an approved alternate recovery approach
|
|
251
|
+
Important Systems use backups or an approved alternate recovery approach suited to their recovery needs and any approved recovery targets. Management documents backup or alternate-recovery scope, frequency, retention, encryption and access needs, monitoring, failure response, procedures, and test schedules.
|
|
252
252
|
|
|
253
253
|
Backup or recovery access is limited to authorized people and protected from the failures it is intended to address. Restoration or alternate recovery is validated on the approved schedule and after material change when prior results no longer represent the System. Policy adoption does not assert that every System uses daily backups or a fixed retention period.
|
|
254
254
|
|
package/src/policy-library.js
CHANGED
|
@@ -6,7 +6,7 @@ import { serializeWorkspaceMutation } from "./mutation.js";
|
|
|
6
6
|
import { resolveDataPath } from "./paths.js";
|
|
7
7
|
import { loadWorkspace } from "./workspace.js";
|
|
8
8
|
|
|
9
|
-
export const INFORMATION_SECURITY_LIBRARY_PROPOSAL_ID = "consolidated-information-security-policy-
|
|
9
|
+
export const INFORMATION_SECURITY_LIBRARY_PROPOSAL_ID = "consolidated-information-security-policy-v4";
|
|
10
10
|
export const STRONG_AUTHENTICATION_LIBRARY_PROPOSAL_ID = INFORMATION_SECURITY_LIBRARY_PROPOSAL_ID;
|
|
11
11
|
|
|
12
12
|
const POLICY_ID = "policy-information-security";
|
|
@@ -42,28 +42,33 @@ const DOCUMENT_CONTENT_UPDATES = [
|
|
|
42
42
|
id: "document-data-retention-schedule",
|
|
43
43
|
path: "documents/document-data-retention-schedule.md",
|
|
44
44
|
priorRevision: "45a408e8139bd57f42dda5ca5ae5c8cd4480b4e7bf08834f60058148a3a63475",
|
|
45
|
-
|
|
45
|
+
additionalPriorRevisions: new Set(["d80b99ce53d1012cc169bbbc2afab8d0597bfbe9f30ac0812a8d5bbeb2ed9f90"]),
|
|
46
|
+
currentRevision: "dd11857ae7d881f176bd93947ef3031c33c75ee41e3c0435198fd60c67a94cf7",
|
|
46
47
|
replacements: [
|
|
47
48
|
["FileGRC detects the bracketed prompts as approval blockers. Remove each prompt only after replacing it with a reviewed fact.", "Remove each bracketed prompt only after replacing it with a reviewed fact."],
|
|
48
|
-
["Record the authority, scope, owner, start date, and release decision outside this public template.", "Record the authority, scope, owner, start date, and release decision in controlled legal-hold records."]
|
|
49
|
+
["Record the authority, scope, owner, start date, and release decision outside this public template.", "Record the authority, scope, owner, start date, and release decision in controlled legal-hold records."],
|
|
50
|
+
["| Production backups or alternate recovery copies | [Complete before approval: Systems or Components] | [Complete before approval: owner] | Backup or recovery-copy creation | [Confirm or replace proposed default before approval: 30 days, adjusted to approved System recovery objectives] | [Complete before approval: expiration or disposal action] | [Complete before approval: continuity objective or risk decision] |", "| Production backups or alternate recovery copies | [Complete before approval: Systems or Components] | [Complete before approval: owner] | Backup or recovery-copy creation | [Confirm or replace proposed default before approval: 30 days, adjusted to approved System recovery needs] | [Complete before approval: expiration or disposal action] | [Complete before approval: recovery need, commitment, or risk decision] |"]
|
|
49
51
|
],
|
|
50
|
-
summary: "Keep FileGRC prompt handling in document guidance
|
|
52
|
+
summary: "Keep FileGRC prompt handling in document guidance, make the Retention Schedule standalone, and avoid assuming numeric recovery objectives."
|
|
51
53
|
},
|
|
52
54
|
{
|
|
53
55
|
id: "document-security-incident-recovery-plan",
|
|
54
56
|
path: "documents/document-security-incident-recovery-plan.md",
|
|
55
57
|
priorRevision: "339ff564fa0ec26503c2498e8d3c44529957113cf782aa03ddb5b4e64c8f0404",
|
|
56
|
-
|
|
58
|
+
additionalPriorRevisions: new Set(["51a19e22f3e0b31196371676297bfa843f96295d2f6c72e81a969ce7bafc36ae"]),
|
|
59
|
+
currentRevision: "558b6fa71eaabf4ea3cfe77eaa24f827ad66a19a059085b2090e3c4a4d79cb50",
|
|
57
60
|
replacements: [
|
|
58
61
|
["Controls, Components, Systems, Obligations, and Evidence hold the actual technical configuration and proof of operation.", "Supporting procedures, system records, and retained evidence document the actual technical configuration and operation."],
|
|
59
62
|
["If an incident raises a legal, privacy, or insurance question, the incident lead gets suitable advice at that time. FileGRC does not require pre-arranged counsel, in-house counsel, or a standing legal retainer.", "If an incident raises a legal, privacy, or insurance question, the incident lead obtains suitable advice at that time. A pre-arranged counsel relationship or standing legal retainer is required only when management determines that the organization's obligations and risk warrant one."],
|
|
60
63
|
["[Complete before activation: Link every important System and record its approved recovery time objective, recovery point objective, maximum tolerable downtime, dependencies, owner, and critical customer commitments in the System record.]", "[Complete before activation: Document every important System's approved recovery time objective, recovery point objective, maximum tolerable downtime, dependencies, owner, and critical customer commitments.]"],
|
|
64
|
+
["[Complete before activation: Document every important System's approved recovery time objective, recovery point objective, maximum tolerable downtime, dependencies, owner, and critical customer commitments.]", "[Complete before activation: Identify every important System's recovery priority, dependencies, owner, backup or alternate recovery approach, and critical customer commitments. Record numeric recovery targets only when an approved commitment, included Availability criterion, or risk decision requires them.]"],
|
|
61
65
|
["For each important System, the applicable Control, Component, System, and Obligation records must identify:", "Supporting recovery documentation for each important System must identify:"],
|
|
62
66
|
["- Restore-validation method and governed schedule", "- Restore-validation method and approved schedule"],
|
|
67
|
+
["- Dependencies, fallback paths, and validation steps", "- Dependencies, fallback paths, and validation steps\n- Any recovery targets required by an approved commitment, included Availability criterion, or risk decision"],
|
|
63
68
|
["[Confirm or replace before activation: The starter proposal for important production data is a daily backup, 30-day retention period, and annual restore validation. Record the approved choice for every important System in its Control, Component, System, Retention Schedule, and Obligation records.]", "[Confirm or replace before activation: The proposed starting point for important production data is a daily backup, 30-day retention period, and annual restore validation. Document the approved choice for every important System in its recovery procedures and the Data Retention Schedule.]"],
|
|
64
69
|
["on the approved governed schedule", "on the approved schedule"]
|
|
65
70
|
],
|
|
66
|
-
summary: "Express incident and recovery requirements as a standalone plan
|
|
71
|
+
summary: "Express incident and recovery requirements as a standalone plan, keep FileGRC record-entry guidance outside it, and make numeric recovery targets conditional."
|
|
67
72
|
},
|
|
68
73
|
{
|
|
69
74
|
id: "document-soc2-management-representation",
|
|
@@ -108,7 +113,8 @@ const PRIOR_STARTER_POLICY_REVISIONS = new Set([
|
|
|
108
113
|
"18f5e7b7e417b494a5cd241751269b3aafeb3ad9129002308c84c1c84419ee31",
|
|
109
114
|
"432ba5797c2d23e02a6b09d07f1a33db29337b4bcb3ee7d68ce8495dc7c7c01c",
|
|
110
115
|
"6775e6907c2a094c5f48090b12af14cb78b20d1e45178cbb5d3bf02cabde59c6",
|
|
111
|
-
"992ef090273ae9bd25e729fe4a7a20817926f345cf7a01f242b877f319c4e342"
|
|
116
|
+
"992ef090273ae9bd25e729fe4a7a20817926f345cf7a01f242b877f319c4e342",
|
|
117
|
+
"355cad2754691f7a11ec10ae6ef0a6abeab6c428c51b49d32dfa3c12a2262e96"
|
|
112
118
|
]);
|
|
113
119
|
|
|
114
120
|
const CONTROL_UPDATES = [
|
|
@@ -325,6 +331,28 @@ const CONTROL_UPDATES = [
|
|
|
325
331
|
},
|
|
326
332
|
summary: "Add conditional service-health and capacity monitoring without prescribing a monitoring product."
|
|
327
333
|
},
|
|
334
|
+
{
|
|
335
|
+
id: "control-backup-restoration",
|
|
336
|
+
prior: [{
|
|
337
|
+
statement: "Each important System has backup scope, frequency, retention, monitoring, and restore validation that meet its approved recovery objectives, or a documented alternate recovery approach when backups are not the chosen safeguard.",
|
|
338
|
+
activity: "Record System recovery objectives and backup or alternate recovery procedures, monitor the chosen safeguards, and validate recovery on the approved schedule."
|
|
339
|
+
}],
|
|
340
|
+
next: {
|
|
341
|
+
statement: "Each important System has risk-based backup or alternate recovery scope, frequency, retention, monitoring, and restore validation suited to its recovery needs and any approved recovery targets.",
|
|
342
|
+
activity: "Record backup or alternate recovery procedures, monitor the chosen safeguards, and validate recovery on the approved schedule."
|
|
343
|
+
},
|
|
344
|
+
summary: "Keep backup and recovery risk-based without requiring numeric recovery targets for Security-only scope."
|
|
345
|
+
},
|
|
346
|
+
{
|
|
347
|
+
id: "control-continuity-exercise",
|
|
348
|
+
prior: [{
|
|
349
|
+
activity: "Maintain the plan, contacts, recovery objectives, exercises, results, and follow-up work."
|
|
350
|
+
}],
|
|
351
|
+
next: {
|
|
352
|
+
activity: "Maintain the plan, contacts, recovery priorities, exercises, results, and follow-up work."
|
|
353
|
+
},
|
|
354
|
+
summary: "Keep continuity exercises focused on recovery priorities without assuming numeric recovery targets."
|
|
355
|
+
},
|
|
328
356
|
{
|
|
329
357
|
id: "control-vendor-due-diligence",
|
|
330
358
|
prior: [{
|
|
@@ -566,7 +594,8 @@ async function buildPolicyLibraryPlan(loaded) {
|
|
|
566
594
|
skipped.push(skippedItem(documentUpdate.id, "current", "The governed Document already contains the current standalone starter language."));
|
|
567
595
|
continue;
|
|
568
596
|
}
|
|
569
|
-
if (sourceRevision !== documentUpdate.priorRevision
|
|
597
|
+
if (sourceRevision !== documentUpdate.priorRevision
|
|
598
|
+
&& !documentUpdate.additionalPriorRevisions?.has(sourceRevision)) {
|
|
570
599
|
skipped.push(skippedItem(documentUpdate.id, "customized", "The governed Document differs from the recognized prior starter, so FileGRC will not rewrite it."));
|
|
571
600
|
continue;
|
|
572
601
|
}
|
package/src/program-path.js
CHANGED
|
@@ -3,7 +3,7 @@ export const RESOURCE_INSTRUCTIONS = {
|
|
|
3
3
|
person: "Record each person’s actual organizational job title. Keep named program authority, such as CISO, DPO, Policy Owner, or team chair, in dated Appointment records.",
|
|
4
4
|
appointment: "Record one person’s dated appointment to a named organizational or program responsibility. Scope it to the workspace, a team, or the records governed by that appointment.",
|
|
5
5
|
team: "Review the starter Security and Risk Oversight team, including its members and chair. Membership and chairs are authoritative on the Team record.",
|
|
6
|
-
system: "Start with the complete bounded System management governs or the auditor will examine. Record its purpose, services, boundary, exclusions, Information Types, owners, and continuity objectives.",
|
|
6
|
+
system: "Start with the complete bounded System management governs or the auditor will examine. Record its purpose, services, boundary, exclusions, Information Types, owners, and any applicable continuity objectives.",
|
|
7
7
|
component: "Add a Component only when it materially delivers a selected System, supports a Control, produces authoritative Evidence, or supports relevant operations. Give every System use a role and rationale.",
|
|
8
8
|
vendor: "Catalog material external provider relationships. Link a supplied Component when it meets the Component inclusion rules, but do not mirror every Vendor into a Component.",
|
|
9
9
|
classification: "Define an ordered information-handling category used by inventory and Evidence Artifacts.",
|
package/src/program-readiness.js
CHANGED
|
@@ -632,6 +632,8 @@ async function policiesStage(scope, records, byId, readMarkdown, model) {
|
|
|
632
632
|
const source = await readMarkdown(document);
|
|
633
633
|
const placeholderCount = openPlaceholderCount(source);
|
|
634
634
|
const isSecurityIncidentRecoveryPlan = document.id === "document-security-incident-recovery-plan";
|
|
635
|
+
const systemContinuityObjectivesRequired = isSecurityIncidentRecoveryPlan
|
|
636
|
+
&& scope.requirements.some(isAvailabilityRequirement);
|
|
635
637
|
const systemsWithCompleteContinuityObjectives = scope.systems.filter(({ continuityObjectives }) => (
|
|
636
638
|
Number.isInteger(continuityObjectives?.recoveryTimeHours)
|
|
637
639
|
&& Number.isInteger(continuityObjectives?.recoveryPointHours)
|
|
@@ -646,7 +648,10 @@ async function policiesStage(scope, records, byId, readMarkdown, model) {
|
|
|
646
648
|
linkedControls: (document.controlIds || []).some((id) => scope.controls.some((control) => control.id === id)),
|
|
647
649
|
contentComplete: substantiveMarkdown(source) && placeholderCount === 0,
|
|
648
650
|
...(isSecurityIncidentRecoveryPlan
|
|
649
|
-
? {
|
|
651
|
+
? {
|
|
652
|
+
systemContinuityObjectives: !systemContinuityObjectivesRequired
|
|
653
|
+
|| systemsWithCompleteContinuityObjectives.length === scope.systems.length && scope.systems.length > 0
|
|
654
|
+
}
|
|
650
655
|
: {})
|
|
651
656
|
};
|
|
652
657
|
const missing = Object.entries(checks)
|
|
@@ -665,10 +670,13 @@ async function policiesStage(scope, records, byId, readMarkdown, model) {
|
|
|
665
670
|
placeholderCount,
|
|
666
671
|
...(isSecurityIncidentRecoveryPlan
|
|
667
672
|
? {
|
|
673
|
+
systemContinuityObjectivesRequired,
|
|
668
674
|
continuityObjectiveSystemIds: systemsWithCompleteContinuityObjectives.map(({ id }) => id),
|
|
669
|
-
missingContinuityObjectiveSystemIds:
|
|
670
|
-
|
|
671
|
-
|
|
675
|
+
missingContinuityObjectiveSystemIds: systemContinuityObjectivesRequired
|
|
676
|
+
? scope.systems
|
|
677
|
+
.filter(({ id }) => !systemsWithCompleteContinuityObjectives.some((system) => system.id === id))
|
|
678
|
+
.map(({ id }) => id)
|
|
679
|
+
: []
|
|
672
680
|
}
|
|
673
681
|
: {}),
|
|
674
682
|
commands: [
|
|
@@ -1582,6 +1590,10 @@ function isDescriptionRequirement(requirement) {
|
|
|
1582
1590
|
|| /^DC\d+/i.test(requirement?.reference || "");
|
|
1583
1591
|
}
|
|
1584
1592
|
|
|
1593
|
+
function isAvailabilityRequirement(requirement) {
|
|
1594
|
+
return /^A1\./i.test(requirement?.reference || "");
|
|
1595
|
+
}
|
|
1596
|
+
|
|
1585
1597
|
function isSecurityRequirement(requirement) {
|
|
1586
1598
|
const tags = requirement?.tags || [];
|
|
1587
1599
|
return tags.includes("security") || tags.includes("common-criteria") || /^CC\d+(?:\.|$)/i.test(requirement?.reference || "");
|