create-filegrc 0.3.3 → 0.4.0
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 +6 -5
- package/package.json +1 -1
- package/src/cli.js +34 -5
- package/src/defaults.js +81 -112
- package/src/index.js +66 -14
- package/template/AGENTS.md +38 -16
- package/template/README.md +14 -5
- package/template/data/AGENTS.md +36 -12
- package/template/data/action-items/AGENTS.md +2 -2
- package/template/data/appointments/appointment-policy-owner.json +11 -0
- package/template/data/audits/AGENTS.md +1 -1
- package/template/data/documents/document-business-continuity-disaster-recovery.json +12 -12
- package/template/data/documents/document-business-continuity-disaster-recovery.md +1 -1
- package/template/data/documents/document-contractor-policy-acknowledgement.json +10 -11
- package/template/data/documents/document-contractor-training-acknowledgement.json +13 -12
- package/template/data/documents/document-data-retention-schedule.json +9 -11
- package/template/data/documents/document-employee-handbook-acknowledgement.json +10 -11
- package/template/data/documents/document-employee-policy-acknowledgement.json +10 -11
- package/template/data/documents/document-employee-training-acknowledgement.json +13 -12
- package/template/data/documents/document-incident-response-plan.json +12 -12
- package/template/data/documents/document-incident-response-plan.md +2 -2
- package/template/data/documents/document-soc2-management-assertion.json +2 -3
- package/template/data/documents/document-soc2-management-representation.json +2 -3
- package/template/data/documents/document-soc2-period-completeness.json +2 -3
- package/template/data/documents/document-soc2-system-description.json +2 -3
- package/template/data/evidence/AGENTS.md +3 -3
- package/template/data/obligation-events/AGENTS.md +2 -2
- package/template/data/obligations/AGENTS.md +1 -1
- package/template/data/people/person-program-lead.json +9 -0
- package/template/data/policies/AGENTS.md +1 -1
- package/template/data/policies/policy-anti-bribery-corruption.json +9 -15
- package/template/data/policies/policy-anti-bribery-corruption.md +1 -1
- package/template/data/policies/policy-clear-desk-screen.json +8 -14
- package/template/data/policies/policy-clear-desk-screen.md +1 -1
- package/template/data/policies/policy-data-protection-handling.json +10 -22
- package/template/data/policies/policy-data-protection-handling.md +2 -2
- package/template/data/policies/policy-employee-handbook.json +6 -16
- package/template/data/policies/policy-information-security.json +8 -40
- package/template/data/policies/policy-information-security.md +2 -2
- package/template/data/policies/policy-mobile-computing-communications.json +8 -18
- package/template/data/policies/policy-mobile-computing-communications.md +1 -1
- package/template/data/renderer.json +3 -1
- package/template/data/training/training-anti-bribery-high-risk-roles.json +1 -2
- package/template/data/training/training-privileged-sensitive-roles.json +1 -2
- package/template/data/training/training-secure-development.json +1 -2
- package/template/data/training/training-security-awareness.json +7 -9
- package/template/data/training/training-security-awareness.md +1 -1
- package/template/data/workspace.json +19 -8
- package/template/docs/filegrc-home.png +0 -0
- package/template/package.json +1 -1
- package/template-parameters.json +6 -1
- package/template/data/people/person-policy-owner.json +0 -10
package/src/index.js
CHANGED
|
@@ -16,6 +16,7 @@ export async function createFilegrc(options = {}) {
|
|
|
16
16
|
const parameterConfig = JSON.parse(await readFile(join(packageRoot, "template-parameters.json"), "utf8"));
|
|
17
17
|
const target = resolve(options.target ?? "filegrc-program");
|
|
18
18
|
const starter = normalizeStarterProfile(options.starter);
|
|
19
|
+
const repository = normalizeRepositoryOptions(options);
|
|
19
20
|
if (options.setup && options.install === false) {
|
|
20
21
|
throw new Error("Combined service setup requires installation. Remove --no-install or run filegrc setup after npm install.");
|
|
21
22
|
}
|
|
@@ -38,6 +39,7 @@ export async function createFilegrc(options = {}) {
|
|
|
38
39
|
await mkdir(target, { recursive: true });
|
|
39
40
|
await copyTemplate(target, starter);
|
|
40
41
|
await renderTemplate(target, parameterConfig, values, starter);
|
|
42
|
+
await writeRendererRepositorySettings(target, repository);
|
|
41
43
|
await writeBaselineRecords(target, values.effective_date, starter);
|
|
42
44
|
await applyStarterScope(target, starter, values.effective_date);
|
|
43
45
|
const initialResourceCounts = await summarizeResources(target);
|
|
@@ -49,7 +51,7 @@ export async function createFilegrc(options = {}) {
|
|
|
49
51
|
await writeMinimalLockfile(target, values.project_name, values.filegrc_version_range);
|
|
50
52
|
}
|
|
51
53
|
const joinedExistingWorktree = await isInsideGitWorktree(target);
|
|
52
|
-
if (!joinedExistingWorktree) await run("git", ["init"], target);
|
|
54
|
+
if (!joinedExistingWorktree) await run("git", ["init", `--initial-branch=${repository.authoritativeBranch}`], target);
|
|
53
55
|
const gitHead = await inspectGitHead(target);
|
|
54
56
|
const setup = options.setup ? await runCombinedSetup(target, options.setup) : null;
|
|
55
57
|
const resourceCounts = setup ? await summarizeResources(target) : initialResourceCounts;
|
|
@@ -67,7 +69,8 @@ export async function createFilegrc(options = {}) {
|
|
|
67
69
|
install: installed ? "installed" : "skipped",
|
|
68
70
|
gitMode: joinedExistingWorktree ? "existing-worktree" : "initialized",
|
|
69
71
|
gitBranch: gitHead.branch,
|
|
70
|
-
gitDetached: gitHead.detached
|
|
72
|
+
gitDetached: gitHead.detached,
|
|
73
|
+
repository
|
|
71
74
|
};
|
|
72
75
|
}
|
|
73
76
|
|
|
@@ -80,6 +83,54 @@ export function normalizeStarterProfile(value = "security") {
|
|
|
80
83
|
return normalized;
|
|
81
84
|
}
|
|
82
85
|
|
|
86
|
+
function normalizeRepositoryOptions(options) {
|
|
87
|
+
const mode = String(options.repositoryMode ?? "trunk").trim();
|
|
88
|
+
if (!["trunk", "manual"].includes(mode)) {
|
|
89
|
+
throw new Error("Repository mode must be trunk or manual.");
|
|
90
|
+
}
|
|
91
|
+
return {
|
|
92
|
+
mode,
|
|
93
|
+
authoritativeBranch: normalizeGitSetting(options.authoritativeBranch, "main", "authoritative branch"),
|
|
94
|
+
remote: normalizeGitSetting(options.repositoryRemote, "origin", "repository remote")
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function normalizeGitSetting(value, fallback, label) {
|
|
99
|
+
const result = String(value ?? fallback).trim();
|
|
100
|
+
if (!safeGitName(result)) {
|
|
101
|
+
throw new Error(`${label} must be a safe Git name.`);
|
|
102
|
+
}
|
|
103
|
+
return result;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function safeGitName(value) {
|
|
107
|
+
const segments = value.split("/");
|
|
108
|
+
return Boolean(value)
|
|
109
|
+
&& value !== "@"
|
|
110
|
+
&& value !== "HEAD"
|
|
111
|
+
&& !value.startsWith("-")
|
|
112
|
+
&& !value.includes("..")
|
|
113
|
+
&& !value.includes("@{")
|
|
114
|
+
&& !/[\s~^:?*[\]\\\u0000-\u001f\u007f]/.test(value)
|
|
115
|
+
&& segments.every((segment) => (
|
|
116
|
+
segment
|
|
117
|
+
&& !segment.startsWith(".")
|
|
118
|
+
&& !segment.endsWith(".")
|
|
119
|
+
&& !segment.endsWith(".lock")
|
|
120
|
+
));
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
async function writeRendererRepositorySettings(target, repository) {
|
|
124
|
+
const path = join(target, "data", "renderer.json");
|
|
125
|
+
const renderer = JSON.parse(await readFile(path, "utf8"));
|
|
126
|
+
await writeFile(path, `${JSON.stringify({
|
|
127
|
+
...renderer,
|
|
128
|
+
repositoryMode: repository.mode,
|
|
129
|
+
authoritativeBranch: repository.authoritativeBranch,
|
|
130
|
+
repositoryRemote: repository.remote
|
|
131
|
+
}, null, 2)}\n`, "utf8");
|
|
132
|
+
}
|
|
133
|
+
|
|
83
134
|
export async function resolveFilegrcVersion(explicitVersion) {
|
|
84
135
|
if (explicitVersion) return cleanVersion(explicitVersion);
|
|
85
136
|
try {
|
|
@@ -121,6 +172,7 @@ async function resolvePromptValues(parameters, options) {
|
|
|
121
172
|
const mapped = {
|
|
122
173
|
company_name: options.companyName,
|
|
123
174
|
policy_owner_name: options.policyOwnerName,
|
|
175
|
+
policy_owner_job_title: options.policyOwnerJobTitle,
|
|
124
176
|
policy_owner_email: options.policyOwnerEmail,
|
|
125
177
|
security_contact_email: options.securityContactEmail,
|
|
126
178
|
timezone: options.timezone
|
|
@@ -128,6 +180,7 @@ async function resolvePromptValues(parameters, options) {
|
|
|
128
180
|
if (options.yes) {
|
|
129
181
|
mapped.company_name ??= "Example Company";
|
|
130
182
|
mapped.policy_owner_name ??= "Security Owner";
|
|
183
|
+
mapped.policy_owner_job_title ??= "Chief Executive Officer";
|
|
131
184
|
mapped.security_contact_email ??= "security@example.com";
|
|
132
185
|
}
|
|
133
186
|
for (const key of Object.keys(mapped)) {
|
|
@@ -157,7 +210,7 @@ async function resolvePromptValues(parameters, options) {
|
|
|
157
210
|
if (missing.length) {
|
|
158
211
|
throw new Error(`Missing required values: ${missing.map(({ key }) => key).join(", ")}`);
|
|
159
212
|
}
|
|
160
|
-
for (const key of ["company_name", "policy_owner_name"]) {
|
|
213
|
+
for (const key of ["company_name", "policy_owner_name", "policy_owner_job_title"]) {
|
|
161
214
|
if (/[\u0000-\u001f\u007f]/.test(mapped[key])) {
|
|
162
215
|
throw new Error(`${key} must be a single line without control characters.`);
|
|
163
216
|
}
|
|
@@ -321,7 +374,7 @@ async function summarizeResources(target) {
|
|
|
321
374
|
for (const path of await collectFiles(join(target, "data"))) {
|
|
322
375
|
if (extname(path) !== ".json") continue;
|
|
323
376
|
const record = JSON.parse(await readFile(path, "utf8"));
|
|
324
|
-
if (!record?.id || !record?.type
|
|
377
|
+
if (!record?.id || !record?.type) continue;
|
|
325
378
|
total += 1;
|
|
326
379
|
counts[record.type] = (counts[record.type] || 0) + 1;
|
|
327
380
|
}
|
|
@@ -371,7 +424,7 @@ The generated workspace starts with five structural records:
|
|
|
371
424
|
|
|
372
425
|
- Workspace and renderer settings
|
|
373
426
|
- The initial active owner
|
|
374
|
-
-
|
|
427
|
+
- A planned security and risk oversight team that still needs an independent chair
|
|
375
428
|
- The filegrc Git repository as a governance system of record
|
|
376
429
|
- A default 5x5 risk method and Public, Internal, Confidential, and Restricted data classifications
|
|
377
430
|
|
|
@@ -414,23 +467,22 @@ The starter policies, controls, and obligations are proposals. They do not state
|
|
|
414
467
|
|
|
415
468
|
1. Run \`npx filegrc setup\` for guided service and goal setup, or use browser onboarding. Then finish Step 1 by adding the real reviewers and operators, finishing the oversight team, and confirming applicable criteria, commitments, material vendors, and in-scope systems.
|
|
416
469
|
2. Review the starter policies, appoint a reviewer who is separate from the policy owner, and activate only the policies that match current practice. The reviewer will usually be another person in the organization, but may be external.
|
|
417
|
-
3. Review the starter control set
|
|
418
|
-
4.
|
|
419
|
-
5.
|
|
420
|
-
6. Engage a CPA firm, record the separate firm-agreed period in an audit record, review filegrc Evidence and External Evidence, and prepare fieldwork.`
|
|
470
|
+
3. Review the starter control set and implement each applicable Control with its actual procedure, scope, cadence, and authoritative evidence source Systems. Confirm every source is active, has the required evidence role and current access owners, and includes repeatable retrieval instructions in Record Markdown. Add the implementation date, confirm any linked Work Queue schedules are enabled, then record any complementary customer or subservice controls.
|
|
471
|
+
4. Run \`npx filegrc program-readiness --require-ready\`, record the management candidate period start when reliable evidence collection begins, maintain risk assessments and risks, update controls when needed, use Work Queue for scheduled work, and trigger Policy Events when changes create required actions.
|
|
472
|
+
5. Engage a CPA firm, record the separate firm-agreed period in an audit record, review filegrc Evidence and External Evidence, and prepare fieldwork.`
|
|
421
473
|
};
|
|
422
474
|
}
|
|
423
475
|
|
|
424
476
|
function validateCombinedSetup(setup) {
|
|
425
477
|
if (!setup) return;
|
|
426
478
|
if (Array.isArray(setup) || typeof setup !== "object") throw new Error("setup must be a JSON object.");
|
|
427
|
-
const required = ["serviceName", "boundary", "criticality", "
|
|
479
|
+
const required = ["serviceName", "boundary", "criticality", "classificationId", "internetExposed"];
|
|
428
480
|
const missing = required.filter((name) => setup[name] === undefined || setup[name] === "");
|
|
429
481
|
if (missing.length) throw new Error(`Combined setup is missing: ${missing.join(", ")}.`);
|
|
430
482
|
}
|
|
431
483
|
|
|
432
484
|
async function runCombinedSetup(target, input) {
|
|
433
|
-
const setup = { programGoal: "none", ownerId: "person-
|
|
485
|
+
const setup = { programGoal: "none", ownerId: "person-program-lead", ...input };
|
|
434
486
|
const args = [
|
|
435
487
|
join(target, "node_modules", "filegrc", "bin", "filegrc.js"),
|
|
436
488
|
"setup",
|
|
@@ -438,7 +490,7 @@ async function runCombinedSetup(target, input) {
|
|
|
438
490
|
"--boundary", String(setup.boundary),
|
|
439
491
|
"--owner", String(setup.ownerId),
|
|
440
492
|
"--criticality", String(setup.criticality),
|
|
441
|
-
"--classification", String(setup.
|
|
493
|
+
"--classification", String(setup.classificationId),
|
|
442
494
|
"--internet-exposed", String(setup.internetExposed),
|
|
443
495
|
"--program-goal", String(setup.programGoal),
|
|
444
496
|
"--summary",
|
|
@@ -452,13 +504,13 @@ async function runCombinedSetup(target, input) {
|
|
|
452
504
|
async function writeMinimalLockfile(target, name, versionRange) {
|
|
453
505
|
const lock = {
|
|
454
506
|
name,
|
|
455
|
-
version: "0.
|
|
507
|
+
version: "0.4.0",
|
|
456
508
|
lockfileVersion: 3,
|
|
457
509
|
requires: true,
|
|
458
510
|
packages: {
|
|
459
511
|
"": {
|
|
460
512
|
name,
|
|
461
|
-
version: "0.
|
|
513
|
+
version: "0.4.0",
|
|
462
514
|
dependencies: { filegrc: versionRange }
|
|
463
515
|
}
|
|
464
516
|
}
|
package/template/AGENTS.md
CHANGED
|
@@ -18,7 +18,7 @@ npx filegrc list person --json
|
|
|
18
18
|
npx filegrc program-readiness --summary --json
|
|
19
19
|
```
|
|
20
20
|
|
|
21
|
-
`program-path --next --json` gives agents the current step and first action. Use `--summary` for all
|
|
21
|
+
`program-path --next --json` gives agents the current step and first action. Use `--summary` for all five step statuses or `--current` for the current step’s full renderer Instructions, Use, Policy Basis, commands, and next actions. The general guide lists every supported action and record type. A type guide repeats that page guidance and adds timing, required and conditional fields, current relationship candidates, JSON location, and Markdown slots.
|
|
22
22
|
|
|
23
23
|
For a new record, generate a mutation envelope:
|
|
24
24
|
|
|
@@ -56,11 +56,26 @@ Read `data/AGENTS.md` before changing records. More specific instructions inside
|
|
|
56
56
|
|
|
57
57
|
Git supplies file authors, commit timestamps, messages, diffs, and revisions. Do not add fields such as `createdAt`, `updatedAt`, `createdBy`, `updatedBy`, or a second change log.
|
|
58
58
|
|
|
59
|
-
Domain events still need explicit dates. Keep values such as `occurredOn`, `
|
|
59
|
+
Domain events still need explicit dates. Keep values such as `occurredOn`, `scheduledFor`, `approvedOn`, `completedOn`, and audit-period dates in their records.
|
|
60
60
|
|
|
61
|
-
|
|
61
|
+
Use a dedicated private repository for your FileGRC workspace. The browser commits and pushes each saved program change, so a standalone repository keeps the compliance audit trail separate from application development history.
|
|
62
62
|
|
|
63
|
-
|
|
63
|
+
- Prefer creating or cloning FileGRC as a standalone private repository.
|
|
64
|
+
- Run the editable browser from the authoritative branch's main checkout.
|
|
65
|
+
- Do not place a new FileGRC workspace inside an application monorepo unless the organization has explicitly chosen that structure.
|
|
66
|
+
- If FileGRC already lives in a monorepo, do not relocate it automatically.
|
|
67
|
+
- In a monorepo, never include application changes in FileGRC-generated commits.
|
|
68
|
+
- Treat detached and feature-branch copies as read-only unless an explicit development override is active.
|
|
69
|
+
|
|
70
|
+
New workspaces use trunk repository mode with `main` as the authoritative branch and `origin` as the remote. Each browser mutation checks the whole Git worktree, fetches the remote, fast-forwards only, rechecks the edited revision, writes through the normal domain function, validates the workspace, stages only this FileGRC workspace, creates a focused commit, and pushes it. Browser onboarding commits its related workspace, system, and renderer changes together.
|
|
71
|
+
|
|
72
|
+
The Repository page reports `Synced`, `Syncing`, `Not synced`, `Read-only checkout`, or `Git setup required`. Browser saves return after the validated local commit, then push in the background. Treat `Syncing` as locally durable but not yet durable on the remote, and wait for `Synced` before starting another write. A failed push keeps the local FileGRC commit and offers Retry sync when every ahead commit changes only this workspace. FileGRC never pushes an ahead commit that includes files outside this workspace, and it never merges, rebases, switches branches, resolves conflicts, or changes files outside the workspace.
|
|
73
|
+
|
|
74
|
+
Record lifecycle fields are the approval source. Draft, proposed, approved, and retired records may all live on the authoritative branch. Do not use Git branches to represent policy approval.
|
|
75
|
+
|
|
76
|
+
Manual mode requires an explicit `repositoryMode` in `data/renderer.json`. In manual mode, review the workspace diff and use the Repository controls or Git CLI. Agents and terminal users always own their Git synchronization and should pull, commit, and push directly. FileGRC does not replace repository authentication, authorization, branch protection, or review controls.
|
|
77
|
+
|
|
78
|
+
Use `npx filegrc serve --allow-non-authoritative-writes` only for local development in a task worktree. The override is visible in the UI and never commits or pushes.
|
|
64
79
|
|
|
65
80
|
Do not rewrite or remove committed records that explain prior audit periods. Close or retire them. Delete only mistakes and uncommitted drafts.
|
|
66
81
|
|
|
@@ -68,6 +83,14 @@ Do not rewrite or remove committed records that explain prior audit periods. Clo
|
|
|
68
83
|
|
|
69
84
|
`data/workspace.json` selects the model through `dataModelVersion`. The installed `filegrc` package owns the authoritative model. Do not copy or invent a local schema.
|
|
70
85
|
|
|
86
|
+
If the installed CLI reports that this workspace uses an unsupported model, start with:
|
|
87
|
+
|
|
88
|
+
```sh
|
|
89
|
+
npx filegrc migrate --to-model 2 --preview --json
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Resolve every missing value, conflict, and manual action in the preview before applying the migration with the same options and `--yes`.
|
|
93
|
+
|
|
71
94
|
Run these commands when working with records:
|
|
72
95
|
|
|
73
96
|
```sh
|
|
@@ -93,7 +116,7 @@ Headless agents get the same protection by exporting an edit payload with `fileg
|
|
|
93
116
|
|
|
94
117
|
## Renderer settings and onboarding
|
|
95
118
|
|
|
96
|
-
`data/renderer.json` stores committed renderer preferences. New workspaces set `showOnboarding` to `true
|
|
119
|
+
`data/renderer.json` stores committed renderer and repository preferences. New workspaces set `showOnboarding` to `true`, `repositoryMode` to `trunk`, `authoritativeBranch` to `main`, and `repositoryRemote` to `origin`. In trunk mode, completing or skipping onboarding commits the related change and starts its background push.
|
|
97
120
|
|
|
98
121
|
Onboarding explains the file and Git workflow, the program path, policy obligations, and Policy Events before covering report types and the final audit stage. It then collects the initial service boundary, owner, business criticality, highest data classification, internet exposure, and optional program goal. It creates or updates one `system` record and stores that selected system and the management goal on `workspace`. It does not select framework records, link controls to the service, or create evidence. Selecting Type 1 or Type 2 does not create an audit engagement. Completing onboarding opens the Step 1 overview so the user can add the real reviewers and operators, finish the oversight team, and confirm the criteria, commitments, vendors, and systems before approving policies.
|
|
99
122
|
|
|
@@ -115,12 +138,12 @@ Work Queue includes recurring obligations, Policy Event tasks, and every other o
|
|
|
115
138
|
A calendar obligation’s recurrence anchor starts its first allowed cycle. Unless `window` narrows that range, completion is allowed from the cycle start through the day before the next cycle, and the item becomes overdue on the next cycle’s first day. Use **Record work** in Work Queue, or create and link a completion atomically with:
|
|
116
139
|
|
|
117
140
|
```sh
|
|
118
|
-
npx filegrc complete obligation-id completion-record.json
|
|
141
|
+
npx filegrc complete obligation-id completion-record.json --expected-revision REVISION
|
|
119
142
|
```
|
|
120
143
|
|
|
121
144
|
Keep prior completion links because the planner matches each dated record to its own period.
|
|
122
145
|
|
|
123
|
-
Event obligations are templates. Do not mark a template complete or replace it for each occurrence. Use Trigger Work on Step
|
|
146
|
+
Event obligations are templates. Do not mark a template complete or replace it for each occurrence. Use Trigger Work on Step 4 or run:
|
|
124
147
|
|
|
125
148
|
```sh
|
|
126
149
|
npx filegrc trigger person-started --occurred-on 2026-07-25 --subject person-new-worker --json
|
|
@@ -132,11 +155,11 @@ Run `npx filegrc obligations` first to preview every task, owner, deadline, and
|
|
|
132
155
|
Complete an event action and link its new proof in one validated write:
|
|
133
156
|
|
|
134
157
|
```sh
|
|
135
|
-
npx filegrc complete-action action-item-id completion-record.json --completed-on 2026-07-25
|
|
136
|
-
npx filegrc complete-event obligation-event-id --completed-on 2026-07-25
|
|
158
|
+
npx filegrc complete-action action-item-id completion-record.json --completed-on 2026-07-25 --expected-revision REVISION
|
|
159
|
+
npx filegrc complete-event obligation-event-id --completed-on 2026-07-25 --expected-revision REVISION
|
|
137
160
|
```
|
|
138
161
|
|
|
139
|
-
filegrc rejects a completion resource whose type does not match the obligation. It will close the event only after every action has its requested proof.
|
|
162
|
+
Read `REVISION` from `npx filegrc get RESOURCE_ID --mutation`. filegrc rejects a completion resource whose type does not match the obligation. It will close the event only after every action has its requested proof.
|
|
140
163
|
|
|
141
164
|
## Headless Markdown
|
|
142
165
|
|
|
@@ -162,11 +185,10 @@ The Evidence Ready gate requires:
|
|
|
162
185
|
|
|
163
186
|
1. A management goal, selected systems, criteria, and controls.
|
|
164
187
|
2. Active policies with completed text, separate management approval, real approval and effective dates, and linked controls.
|
|
165
|
-
3. Implemented
|
|
166
|
-
4.
|
|
167
|
-
5. A verified `test-export` or `test-capture` evidence record for each selected control family that relies on evidence from outside filegrc.
|
|
188
|
+
3. Implemented Controls with an owner, actual procedure, scope, operation pattern, mappings, implementation date, and every required linked Work Queue schedule running.
|
|
189
|
+
4. Every selected Control mapped to active authoritative Systems with the required evidence source roles, current access owners, and repeatable extraction instructions in Record Markdown.
|
|
168
190
|
|
|
169
|
-
Onboarding does not create External Evidence records.
|
|
191
|
+
Onboarding does not create External Evidence records. Complete authoritative source Systems as part of Control implementation. For every incomplete family in Program Readiness, update the Control with its authoritative evidence source Systems, then give each source the required evidence role, current access owners, and repeatable retrieval instructions in Record Markdown. Use `npx filegrc evidence-map --json` when you want only those source checks. During Step 4, create External Evidence only when a real artifact exists. Select its source System, attach or reference the result, link the Controls and operating record it supports, record its collector and classification, then have another person verify it before audit use.
|
|
170
192
|
|
|
171
193
|
When the gate passes, set `workspace.candidatePeriodStart` to the date reliable evidence collection begins. Do not backdate it. `candidatePeriodStart` and `candidatePeriodEnd` express management’s target. They do not establish the final report period.
|
|
172
194
|
|
|
@@ -190,7 +212,7 @@ The audit record’s `typeOneAsOf`, `periodStart`, and `periodEnd` are the dates
|
|
|
190
212
|
|
|
191
213
|
Review both evidence paths against the exact firm-agreed date or period:
|
|
192
214
|
|
|
193
|
-
1. filegrc Evidence consists of dated Step
|
|
215
|
+
1. filegrc Evidence consists of dated Step 4 operating records. Complete each applicable record, link it to the Controls it supports, record the result in structured fields or Markdown, and link any external artifact needed to support that result.
|
|
194
216
|
2. External Evidence consists of verified `evidence` records from authoritative Systems. Confirm the source System, audit date or period, Control links, collector, verifier, and fixed attachment or approved external reference.
|
|
195
217
|
|
|
196
218
|
Audit Readiness reports coverage for both paths. The packet includes the matching filegrc records and Markdown with Git history, plus External Evidence records, retained attachments, delivery indexes, and checksums.
|
|
@@ -217,7 +239,7 @@ Link a control test to its `audit-population` record when sampling applies. Link
|
|
|
217
239
|
|
|
218
240
|
## Content and approvals
|
|
219
241
|
|
|
220
|
-
The
|
|
242
|
+
The initial program lead is {{policy_owner_name}}, {{policy_owner_job_title}}, at {{policy_owner_email}}. The separate Policy Owner Appointment records this person’s starting program authority, and the security reporting address is {{security_contact_email}}. Update the Person when their organizational position changes. End and replace Appointments when named authority moves to someone else.
|
|
221
243
|
|
|
222
244
|
Appoint an independent management reviewer during policy review, not as a condition of defining the service boundary. The reviewer must be separate from the policy owner and able to challenge the owner’s decisions. Most organizations assign another internal leader or manager. An external reviewer is also allowed, and a one-person company needs one because no second internal person is available. The reviewer chairs Security and Risk Oversight and approves policies and governed documents.
|
|
223
245
|
|
package/template/README.md
CHANGED
|
@@ -8,6 +8,8 @@ filegrc gives founder-led engineering teams one place to adopt policies, impleme
|
|
|
8
8
|
|
|
9
9
|
It is open source, MIT licensed, and runs locally.
|
|
10
10
|
|
|
11
|
+
Use a dedicated private repository for your FileGRC workspace. The browser commits and pushes each saved program change, so a standalone repository keeps the compliance audit trail separate from application development history.
|
|
12
|
+
|
|
11
13
|
```sh
|
|
12
14
|
npx create-filegrc@latest company-grc
|
|
13
15
|
cd company-grc
|
|
@@ -17,6 +19,8 @@ npm run serve
|
|
|
17
19
|
|
|
18
20
|
Requires Node.js 20 or newer and Git.
|
|
19
21
|
|
|
22
|
+
Existing model v1 workspaces must run `npx filegrc migrate --to-model 2 --preview --json` after installing a model v2 package. Resolve every reported item before applying the same migration with `--yes`.
|
|
23
|
+
|
|
20
24
|
## How it works
|
|
21
25
|
|
|
22
26
|
The repository is the program. There is no separate application database.
|
|
@@ -27,16 +31,21 @@ The repository is the program. There is no separate application database.
|
|
|
27
31
|
|
|
28
32
|
Use the same source through the local web app, a text editor, the CLI, or CI. Browser and CLI actions call the same rules, so engineers and agents see the same validation and readiness results.
|
|
29
33
|
|
|
34
|
+
New workspaces use `main` as the authoritative browser branch. Browser saves fetch and fast-forward from `origin`, validate the change, and create a focused local commit. The UI then unlocks for navigation while Git push continues in the background. Other writes remain locked until the Repository status confirms `Synced`; a failed push keeps the local commit and offers Retry sync. Draft, proposed, approved, and retired records all live on that branch because record status, not a Git branch, represents approval.
|
|
35
|
+
|
|
36
|
+
Detached and feature-branch checkouts are read-only in the browser by default. Developers can run `npx filegrc serve --allow-non-authoritative-writes` for local task-worktree edits; that override never commits or pushes. CLI and agent workflows continue to manage Git explicitly.
|
|
37
|
+
|
|
30
38
|
## One path from setup to audit
|
|
31
39
|
|
|
32
40
|

|
|
33
41
|
|
|
34
|
-
1. **Define scope.** Confirm
|
|
42
|
+
1. **Define scope.** Confirm people, dated appointments, teams, criteria, commitments, vendors, and in-scope systems. For Systems that produce evidence, add their source roles, access owners, and retrieval instructions.
|
|
35
43
|
2. **Approve policies.** Tailor the proposals and record separate owners and reviewers.
|
|
36
|
-
3. **Implement controls.** Add the real procedure, scope,
|
|
37
|
-
4. **
|
|
38
|
-
5. **
|
|
39
|
-
|
|
44
|
+
3. **Implement controls.** Add the real procedure, scope, operation pattern, and authoritative evidence Systems. Put calendar and event schedules in Obligations. Confirm every source is active and has the required role, access owners, and repeatable retrieval instructions before marking the Control implemented.
|
|
45
|
+
4. **Operate the program.** Work the queue, trigger Policy Events, maintain risks, and preserve dated evidence.
|
|
46
|
+
5. **Audit.** Record the CPA engagement and agreed period, support fieldwork, and build the packet.
|
|
47
|
+
|
|
48
|
+
Control implementation includes evidence-source readiness. Use `npx filegrc program-readiness --json` to find incomplete Control or System records. `npx filegrc evidence-map --json` remains available as a focused diagnostic. Create External Evidence during Step 4 only when a real export, report, screenshot, signed file, or approved external reference exists.
|
|
40
49
|
|
|
41
50
|
The Program Overview shows what is done, what is blocked, and what to do next.
|
|
42
51
|
|
package/template/data/AGENTS.md
CHANGED
|
@@ -15,7 +15,7 @@ npx filegrc list RESOURCE_TYPE --json
|
|
|
15
15
|
npx filegrc search "TERM" --json
|
|
16
16
|
```
|
|
17
17
|
|
|
18
|
-
Use `program-path --next --json` to find the current lifecycle step and first action. Use `--summary` for all
|
|
18
|
+
Use `program-path --next --json` to find the current lifecycle step and first action. Use `--summary` for all five step statuses or `--current` for the current step’s full renderer Instructions, Use, Policy Basis, commands, and next actions. Use `guide` before any unfamiliar create or status transition. It repeats the page guidance and reports required fields, fields required by a status, enum values, relationship types and candidates, Markdown slots, timing, and exact paths. Use `describe` only when you need the raw model definition.
|
|
19
19
|
|
|
20
20
|
## Choose the right record
|
|
21
21
|
|
|
@@ -24,7 +24,9 @@ Use `program-path --next --json` to find the current lifecycle step and first ac
|
|
|
24
24
|
- Work required on a schedule or event belongs in `obligation`.
|
|
25
25
|
- A dated instance of work belongs in its activity type, such as `meeting`, `risk-assessment`, `access-review`, `vulnerability-scan`, `backup-test`, or `exercise`.
|
|
26
26
|
- A fact that may change over time belongs in an inventory record, such as `person`, `system`, `asset`, `vendor`, or `access-grant`.
|
|
27
|
-
- A
|
|
27
|
+
- A Person’s `jobTitle` is their actual position in the organization. A named authority they hold, such as CISO, DPO, Policy Owner, or team chair, belongs in a dated `appointment` scoped to the workspace, team, or governed records.
|
|
28
|
+
- Team membership and chairs are authoritative on `team.memberIds` and `team.chairIds`.
|
|
29
|
+
- A dated Step 4 operating record proves that filegrc-managed work occurred. Put each fixed external artifact in an `evidence` record and link it from the operating record; never add an unexplained attachment.
|
|
28
30
|
- Follow-up work belongs in `action-item`. A gap belongs in `finding`, a known threat belongs in `risk`, and an approved temporary departure belongs in `exception`.
|
|
29
31
|
- An auditor request belongs in `audit-request`; the engagement itself belongs in `audit`.
|
|
30
32
|
|
|
@@ -43,7 +45,6 @@ The scaffold is a mutation envelope:
|
|
|
43
45
|
```json
|
|
44
46
|
{
|
|
45
47
|
"record": {
|
|
46
|
-
"schemaVersion": 1,
|
|
47
48
|
"id": "resource-type-human-name",
|
|
48
49
|
"type": "resource-type",
|
|
49
50
|
"title": "Human name"
|
|
@@ -93,7 +94,9 @@ JSON is for stable metadata used by validation, relationships, filters, schedule
|
|
|
93
94
|
|
|
94
95
|
If `guide` marks a Markdown slot recommended, fill it before treating the deliverable as complete. Keep observations and report details in the source record’s Markdown. Create a Finding only for a confirmed gap that needs its own remediation lifecycle. Create an Action Item only when follow-up needs a separate assignee, deadline, and completion proof. Set each child record’s `sourceResourceId` to the record that produced it; do not maintain reverse Finding or Action Item arrays on the source. Do not put a report’s entire variable structure into new JSON fields.
|
|
95
96
|
|
|
96
|
-
|
|
97
|
+
Store a relationship only on its authoritative record. Control Tests store `auditId`; External Evidence stores `auditIds`; Commitments store `systemIds` and `controlIds`; Controls store `policyIds` and `requirementIds`; Risks store `controlIds`; Systems store their direct `vendorId`. Use `references` to inspect derived inbound links.
|
|
98
|
+
|
|
99
|
+
Use explicit business dates. Git records when a file changed, but it does not replace `occurredOn`, `scheduledFor`, `completedOn`, `approvedOn`, or similar fields.
|
|
97
100
|
|
|
98
101
|
## Status changes
|
|
99
102
|
|
|
@@ -118,7 +121,7 @@ npx filegrc references RESOURCE_ID --json
|
|
|
118
121
|
Delete only an uncommitted draft or a mistake:
|
|
119
122
|
|
|
120
123
|
```sh
|
|
121
|
-
npx filegrc delete RESOURCE_TYPE RESOURCE_ID --yes
|
|
124
|
+
npx filegrc delete RESOURCE_TYPE RESOURCE_ID --yes --expected-revision REVISION
|
|
122
125
|
```
|
|
123
126
|
|
|
124
127
|
filegrc rejects deletion that breaks references and removes owned Markdown with the JSON. Retire, close, cancel, supersede, or replace committed records that explain historical operation.
|
|
@@ -137,7 +140,7 @@ The JSON uses `filePaths: ["evidence/evidence-example/source-export.csv"]`. Comm
|
|
|
137
140
|
Use the attachment command to copy a fixed file and update `filePaths` in one validated action:
|
|
138
141
|
|
|
139
142
|
```sh
|
|
140
|
-
npx filegrc attach EVIDENCE_ID /path/to/source-export.csv
|
|
143
|
+
npx filegrc attach EVIDENCE_ID /path/to/source-export.csv --expected-revision REVISION
|
|
141
144
|
```
|
|
142
145
|
|
|
143
146
|
It refuses symlinks, hidden destination names, and existing destination files.
|
|
@@ -145,24 +148,43 @@ It refuses symlinks, hidden destination names, and existing destination files.
|
|
|
145
148
|
Remove a local attachment explicitly before deleting its evidence record:
|
|
146
149
|
|
|
147
150
|
```sh
|
|
148
|
-
npx filegrc detach EVIDENCE_ID source-export.csv --yes
|
|
151
|
+
npx filegrc detach EVIDENCE_ID source-export.csv --yes --expected-revision REVISION
|
|
149
152
|
```
|
|
150
153
|
|
|
151
154
|
filegrc will not delete an evidence record that still has local attachments.
|
|
152
155
|
|
|
153
156
|
Never invent evidence, dates, approvals, results, people, or source-system details. If a required fact is unavailable, leave the record in a non-final state and report the missing input.
|
|
154
157
|
|
|
158
|
+
## Implement Controls and Their Evidence Sources
|
|
159
|
+
|
|
160
|
+
Finish each applicable Control and its authoritative source Systems together. Use Program Readiness as the completion check:
|
|
161
|
+
|
|
162
|
+
```sh
|
|
163
|
+
npx filegrc program-readiness --json
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
The Control stage reports both Control implementation items and evidence-family source checks. Resolve them through the source records:
|
|
167
|
+
|
|
168
|
+
1. Choose an existing System or scaffold the System that is authoritative for the family.
|
|
169
|
+
2. Set the System to `active`, add the matching `evidenceSourceKinds`, and name current `evidenceOwnerIds`.
|
|
170
|
+
3. Put the exact report, filters, date range, timezone, export format, and reconciliation steps in the System’s Record Markdown.
|
|
171
|
+
4. Add the System ID to `evidenceSourceIds` on every Control in the family that it supports.
|
|
172
|
+
5. Finish the Control’s owner, procedure, scope, operation pattern, mappings, and implementation date. Put every calendar or event schedule in an Obligation.
|
|
173
|
+
6. Run `program-readiness --json` again and resolve every failed Control or source check before marking the Controls implemented or starting the candidate period.
|
|
174
|
+
|
|
175
|
+
Use `get RESOURCE_ID --mutation` and `update` so JSON and Markdown change together. `evidence-map --json` remains available when you want only the evidence-family checks. Do not create an Evidence record while designing or implementing a Control. Create External Evidence during Step 4 only when the real export, report, screenshot, signed file, or approved external reference exists.
|
|
176
|
+
|
|
155
177
|
## Scheduled and event work
|
|
156
178
|
|
|
157
179
|
```sh
|
|
158
180
|
npx filegrc obligations --json
|
|
159
|
-
npx filegrc complete OBLIGATION_ID completion-mutation.json
|
|
181
|
+
npx filegrc complete OBLIGATION_ID completion-mutation.json --expected-revision REVISION
|
|
160
182
|
npx filegrc trigger EVENT_TYPE --occurred-on YYYY-MM-DD --subject RESOURCE_ID --json
|
|
161
|
-
npx filegrc complete-action ACTION_ITEM_ID completion-mutation.json --completed-on YYYY-MM-DD
|
|
162
|
-
npx filegrc complete-event OBLIGATION_EVENT_ID --completed-on YYYY-MM-DD
|
|
183
|
+
npx filegrc complete-action ACTION_ITEM_ID completion-mutation.json --completed-on YYYY-MM-DD --expected-revision REVISION
|
|
184
|
+
npx filegrc complete-event OBLIGATION_EVENT_ID --completed-on YYYY-MM-DD --expected-revision REVISION
|
|
163
185
|
```
|
|
164
186
|
|
|
165
|
-
Run `obligations` before `trigger` to preview every Policy Event task, owner, deadline, and requested proof. Triggering creates the event and adds all linked Action Items to the Work Queue atomically. `complete` and `complete-action` validate the expected completion type and link the new record atomically. `complete-event` refuses to close the workflow until every action has its requested proof. For hour-based deadlines use `--occurred-at` with an RFC 3339 timestamp and timezone.
|
|
187
|
+
Read `REVISION` from `npx filegrc get RESOURCE_ID --mutation`. Run `obligations` before `trigger` to preview every Policy Event task, owner, deadline, and requested proof. Triggering creates the event and adds all linked Action Items to the Work Queue atomically. `complete` and `complete-action` validate the expected completion type and link the new record atomically. `complete-event` refuses to close the workflow until every action has its requested proof. For hour-based deadlines use `--occurred-at` with an RFC 3339 timestamp and timezone.
|
|
166
188
|
|
|
167
189
|
## Audit work
|
|
168
190
|
|
|
@@ -173,7 +195,7 @@ npx filegrc audit-readiness AUDIT_ID --json
|
|
|
173
195
|
npx filegrc evidence-packet --audit AUDIT_ID --preview --json
|
|
174
196
|
```
|
|
175
197
|
|
|
176
|
-
Run Program Readiness before creating the normal audit engagement. It checks scope, effective policies, implemented controls,
|
|
198
|
+
Run Program Readiness before creating the normal audit engagement. It checks scope, effective policies, implemented controls, and evidence mapping without an audit ID. Fix readiness errors in the Control and System records. Do not edit packet output under `.filegrc/`. A delivery-ready filegrc packet means the management checks passed; the engagement team still judges evidence and performs the examination.
|
|
177
199
|
|
|
178
200
|
## Finish every change
|
|
179
201
|
|
|
@@ -185,3 +207,5 @@ git diff
|
|
|
185
207
|
```
|
|
186
208
|
|
|
187
209
|
Review every changed JSON, Markdown, and attachment. Confirm the diff contains no secrets, temporary files, source exports with prohibited data, or derived `.filegrc/` output. Make one focused commit whose message says why the compliance record changed.
|
|
210
|
+
|
|
211
|
+
These commands are for CLI and agent work, which continues to manage Git explicitly. Browser saves in trunk mode commit automatically from the configured authoritative branch, then push in the background while the UI reports `Syncing`. Do not start another write until it reports `Synced`. Do not use a feature branch as a record approval state, and never include application changes when this workspace lives in a monorepo.
|
|
@@ -5,7 +5,7 @@ Create an Action Item only when follow-up needs its own assignee, deadline, and
|
|
|
5
5
|
For an event-generated action, do not weaken or extend its policy deadline by hand. Create the requested completion resource and close the action atomically:
|
|
6
6
|
|
|
7
7
|
```sh
|
|
8
|
-
npx filegrc complete-action ACTION_ITEM_ID completion-mutation.json --completed-on YYYY-MM-DD
|
|
8
|
+
npx filegrc complete-action ACTION_ITEM_ID completion-mutation.json --completed-on YYYY-MM-DD --expected-revision REVISION
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
filegrc rejects the wrong completion type. Mark ordinary Action Items `done` only after the work occurred, set `completedOn`, and link the completion record or evidence. Use `blocked` while a named dependency prevents work, and link that dependency with `blockingResourceIds`.
|
|
11
|
+
Read `REVISION` from `npx filegrc get ACTION_ITEM_ID --mutation`. filegrc rejects the wrong completion type. Mark ordinary Action Items `done` only after the work occurred, set `completedOn`, and link the completion record or evidence. Use `blocked` while a named dependency prevents work, and link that dependency with `blockingResourceIds`.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "appointment-policy-owner",
|
|
3
|
+
"type": "appointment",
|
|
4
|
+
"title": "Policy Owner",
|
|
5
|
+
"status": "active",
|
|
6
|
+
"appointmentKind": "policy-owner",
|
|
7
|
+
"holderId": "person-program-lead",
|
|
8
|
+
"scopeResourceIds": ["workspace"],
|
|
9
|
+
"startsOn": "{{effective_date}}",
|
|
10
|
+
"responsibilities": "Own the information security program and the starter policies, controls, documents, training, and scheduled work until management assigns more specific accountable parties."
|
|
11
|
+
}
|
|
@@ -17,7 +17,7 @@ Preparation creates engagement-specific management documents and, for Type 2, po
|
|
|
17
17
|
|
|
18
18
|
Review both evidence paths for the exact formal date or period:
|
|
19
19
|
|
|
20
|
-
1. filegrc Evidence consists of dated Step
|
|
20
|
+
1. filegrc Evidence consists of dated Step 4 operating records. Complete the record, link it to the applicable Controls, record the result in its fields or Markdown, and link any external artifact needed to support that result.
|
|
21
21
|
2. External Evidence consists of verified `evidence` records from other Systems. Confirm the source System, date or period, Control links, collector, verifier, and fixed attachment or approved external reference.
|
|
22
22
|
|
|
23
23
|
The packet compiles both paths. It includes filegrc records and Markdown with Git history, plus External Evidence records, retained attachments, delivery indexes, and checksums.
|
|
@@ -1,21 +1,18 @@
|
|
|
1
1
|
{
|
|
2
|
-
"schemaVersion": 1,
|
|
3
2
|
"id": "document-business-continuity-disaster-recovery",
|
|
4
3
|
"type": "document",
|
|
5
4
|
"title": "Business Continuity and Disaster Recovery Plan",
|
|
6
5
|
"status": "draft",
|
|
7
6
|
"documentKind": "plan",
|
|
8
|
-
"ownerIds": [
|
|
7
|
+
"ownerIds": [
|
|
8
|
+
"appointment-policy-owner"
|
|
9
|
+
],
|
|
9
10
|
"version": "1.0",
|
|
10
11
|
"effectiveOn": "{{effective_date}}",
|
|
11
|
-
"
|
|
12
|
-
"
|
|
13
|
-
"
|
|
14
|
-
|
|
15
|
-
"anchorDate": "{{effective_date}}"
|
|
16
|
-
},
|
|
17
|
-
"classification": "internal",
|
|
18
|
-
"audience": ["employees", "contractors"],
|
|
12
|
+
"audience": [
|
|
13
|
+
"employees",
|
|
14
|
+
"contractors"
|
|
15
|
+
],
|
|
19
16
|
"acknowledgementRequired": true,
|
|
20
17
|
"controlIds": [
|
|
21
18
|
"control-policy-management",
|
|
@@ -23,6 +20,9 @@
|
|
|
23
20
|
"control-backup-restoration",
|
|
24
21
|
"control-continuity-exercise"
|
|
25
22
|
],
|
|
26
|
-
"relatedDocumentIds": [
|
|
27
|
-
|
|
23
|
+
"relatedDocumentIds": [
|
|
24
|
+
"document-incident-response-plan"
|
|
25
|
+
],
|
|
26
|
+
"evidenceIds": [],
|
|
27
|
+
"classificationId": "internal"
|
|
28
28
|
}
|
|
@@ -33,7 +33,7 @@ During a disruption, {{company_name}} will:
|
|
|
33
33
|
|
|
34
34
|
### Policy owner
|
|
35
35
|
|
|
36
|
-
|
|
36
|
+
The current Policy Owner owns this plan and keeps it current. The Policy Owner may delegate response duties but remains accountable for the plan.
|
|
37
37
|
|
|
38
38
|
### Security and risk oversight
|
|
39
39
|
|
|
@@ -1,20 +1,19 @@
|
|
|
1
1
|
{
|
|
2
|
-
"schemaVersion": 1,
|
|
3
2
|
"id": "document-contractor-policy-acknowledgement",
|
|
4
3
|
"type": "document",
|
|
5
4
|
"title": "Contractor Policy Acknowledgement",
|
|
6
5
|
"status": "draft",
|
|
7
6
|
"documentKind": "attestation-template",
|
|
8
|
-
"ownerIds": [
|
|
7
|
+
"ownerIds": [
|
|
8
|
+
"appointment-policy-owner"
|
|
9
|
+
],
|
|
9
10
|
"version": "1.0",
|
|
10
11
|
"effectiveOn": "{{effective_date}}",
|
|
11
|
-
"
|
|
12
|
-
"
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
"
|
|
16
|
-
|
|
17
|
-
"
|
|
18
|
-
"audience": ["contractors"],
|
|
19
|
-
"controlIds": ["control-workforce-expectations"]
|
|
12
|
+
"audience": [
|
|
13
|
+
"contractors"
|
|
14
|
+
],
|
|
15
|
+
"controlIds": [
|
|
16
|
+
"control-workforce-expectations"
|
|
17
|
+
],
|
|
18
|
+
"classificationId": "internal"
|
|
20
19
|
}
|