create-filegrc 0.1.0 → 0.3.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/LICENSE +1 -1
- package/README.md +5 -3
- package/package.json +3 -3
- package/src/cli.js +30 -12
- package/src/defaults.js +6 -7
- package/src/index.js +88 -22
- package/template/AGENTS.md +63 -23
- package/template/README.md +49 -24
- package/template/WORKSPACE.md +41 -0
- package/template/data/AGENTS.md +12 -10
- package/template/data/action-items/AGENTS.md +2 -2
- package/template/data/audits/AGENTS.md +13 -2
- package/template/data/documents/document-business-continuity-disaster-recovery.json +0 -1
- package/template/data/documents/document-contractor-policy-acknowledgement.json +0 -1
- package/template/data/documents/document-contractor-training-acknowledgement.json +0 -1
- package/template/data/documents/document-data-retention-schedule.json +0 -1
- package/template/data/documents/document-employee-handbook-acknowledgement.json +0 -1
- package/template/data/documents/document-employee-policy-acknowledgement.json +0 -1
- package/template/data/documents/document-employee-training-acknowledgement.json +0 -1
- package/template/data/documents/document-incident-response-plan.json +0 -1
- package/template/data/documents/document-soc2-management-assertion.json +0 -3
- package/template/data/documents/document-soc2-management-representation.json +0 -3
- package/template/data/documents/document-soc2-period-completeness.json +0 -3
- package/template/data/documents/document-soc2-period-completeness.md +2 -2
- package/template/data/documents/document-soc2-system-description.json +0 -3
- package/template/data/documents/document-soc2-system-description.md +3 -3
- package/template/data/evidence/AGENTS.md +5 -3
- package/template/data/obligation-events/AGENTS.md +3 -3
- package/template/data/obligations/AGENTS.md +2 -1
- package/template/data/people/person-policy-owner.json +1 -1
- package/template/data/policies/AGENTS.md +3 -1
- package/template/data/policies/policy-anti-bribery-corruption.json +0 -1
- package/template/data/policies/policy-clear-desk-screen.json +0 -1
- package/template/data/policies/policy-data-protection-handling.json +0 -1
- package/template/data/policies/policy-employee-handbook.json +0 -1
- package/template/data/policies/policy-information-security.json +0 -1
- package/template/data/policies/policy-information-security.md +3 -3
- package/template/data/policies/policy-mobile-computing-communications.json +0 -1
- package/template/data/renderer.json +2 -1
- package/template/data/risk-assessments/AGENTS.md +1 -1
- package/template/data/systems/system-filegrc-program-repository.md +3 -3
- package/template/data/workspace.json +1 -1
- package/template/docs/filegrc-audit.png +0 -0
- package/template/docs/filegrc-home.png +0 -0
- package/template/docs/filegrc-social-preview.png +0 -0
- package/template/package.json +2 -2
- package/template-parameters.json +18 -2
- package/template/data/people/person-independent-approver.json +0 -10
package/LICENSE
CHANGED
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# create-filegrc
|
|
2
2
|
|
|
3
|
-
Create a Git-native
|
|
3
|
+
Create a Git-native filegrc workspace for a SOC 2 program:
|
|
4
4
|
|
|
5
5
|
```sh
|
|
6
6
|
npx create-filegrc@latest company-grc
|
|
@@ -9,10 +9,12 @@ npm run validate
|
|
|
9
9
|
npm run serve
|
|
10
10
|
```
|
|
11
11
|
|
|
12
|
-
Setup asks for the
|
|
12
|
+
Setup asks for the legal organization name, the initial policy owner and their email, a security reporting address, and the program timezone. The generated private project includes a starter Security program, Program Readiness, a policy-driven Work Queue, Policy Events that add linked tasks, later audit preparation, evidence packets, and one dependency: `filegrc`.
|
|
13
13
|
|
|
14
|
-
Generated workspaces also include layered `AGENTS.md` instructions and model-driven headless commands.
|
|
14
|
+
Generated workspaces also include layered `AGENTS.md` instructions and model-driven headless commands. `filegrc program-path` gives agents the same six steps, exact page guidance, current status, and next actions shown in the renderer. Agents can define program scope, approve policies, implement controls, test External Evidence, complete policy work, trigger event tasks, and prepare later audit packets through the same domain functions used by the renderer.
|
|
15
15
|
|
|
16
16
|
Git is initialized when needed. The browser can create local commits before a remote is configured.
|
|
17
17
|
|
|
18
|
+
Creation output reports the resolved filegrc version, program timezone, starter record counts, whether installation ran, and whether the target joined an existing Git worktree or received a new repository.
|
|
19
|
+
|
|
18
20
|
Use `npx create-filegrc@latest --help` for non-interactive options.
|
package/package.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-filegrc",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Create a
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"description": "Create a filegrc workspace for a SOC 2 program",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
8
|
-
"url": "git+https://github.com/Sunpeak-AI/
|
|
8
|
+
"url": "git+https://github.com/Sunpeak-AI/filegrc.git"
|
|
9
9
|
},
|
|
10
10
|
"type": "module",
|
|
11
11
|
"bin": {
|
package/src/cli.js
CHANGED
|
@@ -1,21 +1,35 @@
|
|
|
1
1
|
import { readFile } from "node:fs/promises";
|
|
2
|
-
import {
|
|
2
|
+
import { createFilegrc } from "./index.js";
|
|
3
3
|
|
|
4
4
|
export async function runCli(argv = process.argv.slice(2)) {
|
|
5
5
|
const { target, options } = parseArgs(argv);
|
|
6
6
|
if (options.help) return printHelp();
|
|
7
7
|
if (options.version) return printVersion();
|
|
8
|
-
const result = await
|
|
8
|
+
const result = await createFilegrc({ target, ...options });
|
|
9
9
|
console.log(`Created ${result.target}`);
|
|
10
|
+
console.log(`filegrc ${result.engineVersion}: ${result.install === "installed" ? "installed" : "installation skipped"}`);
|
|
11
|
+
console.log(`Git: ${result.gitMode === "existing-worktree" ? "joined existing worktree" : "initialized new repository"}`);
|
|
12
|
+
console.log(`Timezone: ${result.values.timezone}`);
|
|
13
|
+
console.log(
|
|
14
|
+
`Program baseline: ${result.resourceCounts.total} records, including ` +
|
|
15
|
+
`${result.resourceCounts.byType.requirement || 0} requirements, ` +
|
|
16
|
+
`${result.resourceCounts.byType.control || 0} controls, and ` +
|
|
17
|
+
`${result.resourceCounts.byType.obligation || 0} obligations.`
|
|
18
|
+
);
|
|
10
19
|
console.log("");
|
|
11
|
-
console.log(` cd ${result.target}`);
|
|
20
|
+
console.log(` cd ${shellQuote(result.target)}`);
|
|
12
21
|
if (options.install === false) console.log(" npm install");
|
|
22
|
+
console.log(" npx filegrc setup");
|
|
13
23
|
console.log(" npm run validate");
|
|
14
24
|
console.log(" npm run serve");
|
|
15
25
|
console.log("");
|
|
16
26
|
console.log("Review the starter drafts, then commit the approved baseline.");
|
|
17
27
|
}
|
|
18
28
|
|
|
29
|
+
function shellQuote(value) {
|
|
30
|
+
return `'${String(value).replaceAll("'", "'\\''")}'`;
|
|
31
|
+
}
|
|
32
|
+
|
|
19
33
|
function parseArgs(argv) {
|
|
20
34
|
let target;
|
|
21
35
|
const options = {};
|
|
@@ -34,7 +48,9 @@ function parseArgs(argv) {
|
|
|
34
48
|
else if (name === "no-install") options.install = false;
|
|
35
49
|
else if (name === "company-name") options.companyName = next();
|
|
36
50
|
else if (name === "policy-owner-name") options.policyOwnerName = next();
|
|
51
|
+
else if (name === "policy-owner-email") options.policyOwnerEmail = next();
|
|
37
52
|
else if (name === "security-contact-email") options.securityContactEmail = next();
|
|
53
|
+
else if (name === "timezone") options.timezone = next();
|
|
38
54
|
else if (name === "filegrc-version") options.filegrcVersion = next();
|
|
39
55
|
else throw new Error(`Unknown option "${value}".`);
|
|
40
56
|
}
|
|
@@ -44,18 +60,20 @@ function parseArgs(argv) {
|
|
|
44
60
|
function printHelp() {
|
|
45
61
|
console.log(`create-filegrc [directory] [options]
|
|
46
62
|
|
|
47
|
-
Create a
|
|
63
|
+
Create a filegrc workspace for a SOC 2 program.
|
|
48
64
|
|
|
49
65
|
Options:
|
|
50
|
-
--company-name <name>
|
|
51
|
-
--policy-owner-name <name>
|
|
52
|
-
--
|
|
66
|
+
--company-name <legal-name> Legal organization name
|
|
67
|
+
--policy-owner-name <name> Initial policy owner
|
|
68
|
+
--policy-owner-email <email> Policy owner's email address
|
|
69
|
+
--security-contact-email <email> Security reporting address
|
|
70
|
+
--timezone <iana-timezone> Program timezone, such as America/Chicago
|
|
53
71
|
--filegrc-version <version> Override resolved engine version
|
|
54
|
-
--no-install
|
|
55
|
-
--force
|
|
56
|
-
--yes
|
|
57
|
-
--version
|
|
58
|
-
--help
|
|
72
|
+
--no-install Write files and a preliminary lockfile only
|
|
73
|
+
--force Allow a non-empty target without overwriting files
|
|
74
|
+
--yes Use generic prompt defaults
|
|
75
|
+
--version Show the package version
|
|
76
|
+
--help Show this help`);
|
|
59
77
|
}
|
|
60
78
|
|
|
61
79
|
async function printVersion() {
|
package/src/defaults.js
CHANGED
|
@@ -4,7 +4,6 @@ import { mkdir, writeFile } from "node:fs/promises";
|
|
|
4
4
|
const FRAMEWORK_ID = "framework-aicpa-trust-services-criteria";
|
|
5
5
|
const DESCRIPTION_FRAMEWORK_ID = "framework-aicpa-soc2-description-criteria";
|
|
6
6
|
const OWNER_ID = "person-policy-owner";
|
|
7
|
-
const INDEPENDENT_APPROVER_ID = "person-independent-approver";
|
|
8
7
|
const OVERSIGHT_TEAM_ID = "team-security-risk-oversight";
|
|
9
8
|
const INFORMATION_SECURITY_POLICY_ID = "policy-information-security";
|
|
10
9
|
const DATA_POLICY_ID = "policy-data-protection-handling";
|
|
@@ -70,7 +69,7 @@ const controls = [
|
|
|
70
69
|
title: "Security governance",
|
|
71
70
|
statement: "Management assigns security responsibilities, and a reviewer who is separate from the policy owner and control operators independently reviews the program, risks, incidents, findings, policy approvals, and overdue work at least quarterly.",
|
|
72
71
|
requirements: ["CC1.1", "CC1.2", "CC1.3", "CC1.5"],
|
|
73
|
-
activity: "Assign an
|
|
72
|
+
activity: "Assign an independent reviewer who is separate from program ownership and record quarterly oversight decisions, approvals, and actions.",
|
|
74
73
|
controlType: "preventive",
|
|
75
74
|
operationMode: "manual",
|
|
76
75
|
frequency: "Quarterly",
|
|
@@ -963,21 +962,21 @@ export function baselineRecordFiles(effectiveDate) {
|
|
|
963
962
|
id: OVERSIGHT_TEAM_ID,
|
|
964
963
|
type: "team",
|
|
965
964
|
title: "Security and Risk Oversight",
|
|
966
|
-
status: "
|
|
965
|
+
status: "inactive",
|
|
967
966
|
purpose: "Provide independent oversight of the security program, risk register, incidents, findings, vendor and access reviews, policy changes, exercises, and overdue work. The chair must be separate from the policy owner and people who operate the controls under review.",
|
|
968
|
-
memberIds: [OWNER_ID
|
|
969
|
-
chairIds: [
|
|
967
|
+
memberIds: [OWNER_ID],
|
|
968
|
+
chairIds: [],
|
|
970
969
|
meetingCadence: calendar("month", 3, effectiveDate)
|
|
971
970
|
};
|
|
972
971
|
const programRepository = {
|
|
973
972
|
schemaVersion: 1,
|
|
974
973
|
id: "system-filegrc-program-repository",
|
|
975
974
|
type: "system",
|
|
976
|
-
title: "
|
|
975
|
+
title: "filegrc Program Repository",
|
|
977
976
|
status: "active",
|
|
978
977
|
criticality: "high",
|
|
979
978
|
ownerIds: [OWNER_ID],
|
|
980
|
-
description: "The Git repository that is authoritative for
|
|
979
|
+
description: "The Git repository that is authoritative for filegrc governance records, approvals, exceptions, findings, acknowledgements, evidence indexes, and their revision history.",
|
|
981
980
|
systemKind: "governance-system-of-record",
|
|
982
981
|
environment: "Git repository",
|
|
983
982
|
dataClassification: "Confidential",
|
package/src/index.js
CHANGED
|
@@ -10,18 +10,19 @@ const execute = promisify(execFile);
|
|
|
10
10
|
const packageRoot = dirname(dirname(fileURLToPath(import.meta.url)));
|
|
11
11
|
const textExtensions = new Set(["", ".json", ".md", ".txt", ".yml", ".yaml", ".gitignore"]);
|
|
12
12
|
|
|
13
|
-
export async function
|
|
13
|
+
export async function createFilegrc(options = {}) {
|
|
14
14
|
const parameterConfig = JSON.parse(await readFile(join(packageRoot, "template-parameters.json"), "utf8"));
|
|
15
15
|
const target = resolve(options.target ?? "filegrc-program");
|
|
16
16
|
await assertWritableTarget(target, Boolean(options.force));
|
|
17
17
|
if (options.force) await assertNoTemplateCollisions(target);
|
|
18
18
|
|
|
19
19
|
const prompted = await resolvePromptValues(parameterConfig.parameters, options);
|
|
20
|
-
const engineVersion = await
|
|
20
|
+
const engineVersion = await resolveFilegrcVersion(options.filegrcVersion);
|
|
21
21
|
const values = {
|
|
22
22
|
...prompted,
|
|
23
23
|
effective_date: options.effectiveDate ?? new Date().toISOString().slice(0, 10),
|
|
24
24
|
project_name: normalizePackageName(basename(target)),
|
|
25
|
+
filegrc_version: engineVersion,
|
|
25
26
|
filegrc_version_range: `^${engineVersion}`
|
|
26
27
|
};
|
|
27
28
|
|
|
@@ -29,17 +30,27 @@ export async function createFileGRC(options = {}) {
|
|
|
29
30
|
await copyTemplate(target);
|
|
30
31
|
await renderTemplate(target, parameterConfig, values);
|
|
31
32
|
await writeBaselineRecords(target, values.effective_date);
|
|
33
|
+
const resourceCounts = await summarizeResources(target);
|
|
32
34
|
|
|
33
|
-
|
|
35
|
+
const installed = options.install !== false;
|
|
36
|
+
if (installed) {
|
|
34
37
|
await run("npm", ["install", "--ignore-scripts"], target);
|
|
35
38
|
} else {
|
|
36
39
|
await writeMinimalLockfile(target, values.project_name, values.filegrc_version_range);
|
|
37
40
|
}
|
|
38
|
-
|
|
39
|
-
|
|
41
|
+
const joinedExistingWorktree = await isInsideGitWorktree(target);
|
|
42
|
+
if (!joinedExistingWorktree) await run("git", ["init"], target);
|
|
43
|
+
return {
|
|
44
|
+
target,
|
|
45
|
+
values,
|
|
46
|
+
engineVersion,
|
|
47
|
+
resourceCounts,
|
|
48
|
+
install: installed ? "installed" : "skipped",
|
|
49
|
+
gitMode: joinedExistingWorktree ? "existing-worktree" : "initialized"
|
|
50
|
+
};
|
|
40
51
|
}
|
|
41
52
|
|
|
42
|
-
export async function
|
|
53
|
+
export async function resolveFilegrcVersion(explicitVersion) {
|
|
43
54
|
if (explicitVersion) return cleanVersion(explicitVersion);
|
|
44
55
|
try {
|
|
45
56
|
const { stdout } = await execute("npm", ["view", "filegrc", "version", "--json"], {
|
|
@@ -58,7 +69,9 @@ async function resolvePromptValues(parameters, options) {
|
|
|
58
69
|
const mapped = {
|
|
59
70
|
company_name: options.companyName,
|
|
60
71
|
policy_owner_name: options.policyOwnerName,
|
|
61
|
-
|
|
72
|
+
policy_owner_email: options.policyOwnerEmail,
|
|
73
|
+
security_contact_email: options.securityContactEmail,
|
|
74
|
+
timezone: options.timezone
|
|
62
75
|
};
|
|
63
76
|
if (options.yes) {
|
|
64
77
|
mapped.company_name ??= "Example Company";
|
|
@@ -68,21 +81,29 @@ async function resolvePromptValues(parameters, options) {
|
|
|
68
81
|
for (const key of Object.keys(mapped)) {
|
|
69
82
|
if (mapped[key] !== undefined && mapped[key] !== null) mapped[key] = String(mapped[key]).trim();
|
|
70
83
|
}
|
|
71
|
-
|
|
72
|
-
if (missing.length) {
|
|
73
|
-
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
74
|
-
throw new Error(`Missing required values: ${missing.map(({ key }) => key).join(", ")}`);
|
|
75
|
-
}
|
|
84
|
+
if (process.stdin.isTTY && process.stdout.isTTY) {
|
|
76
85
|
const prompt = createInterface({ input: process.stdin, output: process.stdout });
|
|
77
86
|
try {
|
|
78
|
-
for (const parameter of
|
|
87
|
+
for (const parameter of parameters.filter(({ required, key }) => required && !mapped[key])) {
|
|
88
|
+
const defaultValue = parameterDefault(parameter, mapped);
|
|
89
|
+
const suffix = defaultValue ? ` [${defaultValue}]` : "";
|
|
79
90
|
let value = "";
|
|
80
|
-
while (!value)
|
|
91
|
+
while (!value) {
|
|
92
|
+
value = (await prompt.question(`${parameter.prompt}${suffix}: `)).trim() || defaultValue;
|
|
93
|
+
}
|
|
81
94
|
mapped[parameter.key] = value;
|
|
82
95
|
}
|
|
83
96
|
} finally {
|
|
84
97
|
prompt.close();
|
|
85
98
|
}
|
|
99
|
+
} else {
|
|
100
|
+
for (const parameter of parameters.filter(({ required, key }) => required && !mapped[key])) {
|
|
101
|
+
mapped[parameter.key] = parameterDefault(parameter, mapped);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
const missing = parameters.filter(({ key, required }) => required && !mapped[key]);
|
|
105
|
+
if (missing.length) {
|
|
106
|
+
throw new Error(`Missing required values: ${missing.map(({ key }) => key).join(", ")}`);
|
|
86
107
|
}
|
|
87
108
|
for (const key of ["company_name", "policy_owner_name"]) {
|
|
88
109
|
if (/[\u0000-\u001f\u007f]/.test(mapped[key])) {
|
|
@@ -93,12 +114,39 @@ async function resolvePromptValues(parameters, options) {
|
|
|
93
114
|
throw new Error(`${key} cannot contain template token syntax.`);
|
|
94
115
|
}
|
|
95
116
|
}
|
|
96
|
-
|
|
97
|
-
|
|
117
|
+
for (const [key, label] of [
|
|
118
|
+
["policy_owner_email", "Policy owner email"],
|
|
119
|
+
["security_contact_email", "Security contact email"]
|
|
120
|
+
]) {
|
|
121
|
+
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(mapped[key])) {
|
|
122
|
+
throw new Error(`${label} must be a valid email address.`);
|
|
123
|
+
}
|
|
98
124
|
}
|
|
125
|
+
if (!isTimezone(mapped.timezone)) throw new Error("Program timezone must be a valid IANA time zone.");
|
|
99
126
|
return mapped;
|
|
100
127
|
}
|
|
101
128
|
|
|
129
|
+
function parameterDefault(parameter, mapped) {
|
|
130
|
+
if (parameter.defaultFrom) return mapped[parameter.defaultFrom] || "";
|
|
131
|
+
if (parameter.defaultSource === "local-timezone") return localTimezone();
|
|
132
|
+
return "";
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function localTimezone() {
|
|
136
|
+
const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
137
|
+
return isTimezone(timezone) ? timezone : "UTC";
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function isTimezone(value) {
|
|
141
|
+
if (!value) return false;
|
|
142
|
+
try {
|
|
143
|
+
new Intl.DateTimeFormat("en-US", { timeZone: value }).format();
|
|
144
|
+
return true;
|
|
145
|
+
} catch {
|
|
146
|
+
return false;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
102
150
|
async function assertWritableTarget(target, force) {
|
|
103
151
|
try {
|
|
104
152
|
if ((await lstat(target)).isSymbolicLink()) {
|
|
@@ -167,9 +215,11 @@ async function renderTemplate(target, parameterConfig, values) {
|
|
|
167
215
|
|
|
168
216
|
async function templateDestinationPaths() {
|
|
169
217
|
const template = join(packageRoot, "template");
|
|
170
|
-
return (await collectFiles(template)).
|
|
218
|
+
return (await collectFiles(template)).flatMap((source) => {
|
|
171
219
|
const templatePath = relative(template, source);
|
|
172
|
-
|
|
220
|
+
if (templatePath === "README.md") return [];
|
|
221
|
+
if (templatePath === "WORKSPACE.md") return ["README.md"];
|
|
222
|
+
return [templatePath === "gitignore" ? ".gitignore" : templatePath];
|
|
173
223
|
});
|
|
174
224
|
}
|
|
175
225
|
|
|
@@ -177,7 +227,10 @@ async function copyTemplate(target) {
|
|
|
177
227
|
const template = join(packageRoot, "template");
|
|
178
228
|
for (const source of await collectFiles(template)) {
|
|
179
229
|
const templatePath = relative(template, source);
|
|
180
|
-
|
|
230
|
+
if (templatePath === "README.md") continue;
|
|
231
|
+
const destinationPath = templatePath === "WORKSPACE.md"
|
|
232
|
+
? "README.md"
|
|
233
|
+
: templatePath === "gitignore" ? ".gitignore" : templatePath;
|
|
181
234
|
const destination = join(target, destinationPath);
|
|
182
235
|
await assertNoSymlinkComponents(target, destinationPath);
|
|
183
236
|
await mkdir(dirname(destination), { recursive: true });
|
|
@@ -199,16 +252,29 @@ async function collectFiles(directory) {
|
|
|
199
252
|
return result;
|
|
200
253
|
}
|
|
201
254
|
|
|
255
|
+
async function summarizeResources(target) {
|
|
256
|
+
const counts = {};
|
|
257
|
+
let total = 0;
|
|
258
|
+
for (const path of await collectFiles(join(target, "data"))) {
|
|
259
|
+
if (extname(path) !== ".json") continue;
|
|
260
|
+
const record = JSON.parse(await readFile(path, "utf8"));
|
|
261
|
+
if (!record?.id || !record?.type || !record?.schemaVersion) continue;
|
|
262
|
+
total += 1;
|
|
263
|
+
counts[record.type] = (counts[record.type] || 0) + 1;
|
|
264
|
+
}
|
|
265
|
+
return { total, byType: counts };
|
|
266
|
+
}
|
|
267
|
+
|
|
202
268
|
async function writeMinimalLockfile(target, name, versionRange) {
|
|
203
269
|
const lock = {
|
|
204
270
|
name,
|
|
205
|
-
version: "0.
|
|
271
|
+
version: "0.3.0",
|
|
206
272
|
lockfileVersion: 3,
|
|
207
273
|
requires: true,
|
|
208
274
|
packages: {
|
|
209
275
|
"": {
|
|
210
276
|
name,
|
|
211
|
-
version: "0.
|
|
277
|
+
version: "0.3.0",
|
|
212
278
|
dependencies: { filegrc: versionRange }
|
|
213
279
|
}
|
|
214
280
|
}
|
|
@@ -237,7 +303,7 @@ async function run(command, args, cwd) {
|
|
|
237
303
|
function cleanVersion(value) {
|
|
238
304
|
const version = String(value ?? "").trim().replace(/^v/, "");
|
|
239
305
|
if (!/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/.test(version)) {
|
|
240
|
-
throw new Error(`Could not resolve a valid
|
|
306
|
+
throw new Error(`Could not resolve a valid filegrc version from "${value}".`);
|
|
241
307
|
}
|
|
242
308
|
return version;
|
|
243
309
|
}
|
package/template/AGENTS.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
#
|
|
1
|
+
# filegrc SOC 2 Workspace Instructions
|
|
2
2
|
|
|
3
3
|
## Purpose
|
|
4
4
|
|
|
5
|
-
This repository is {{company_name}}’s
|
|
5
|
+
This repository is {{company_name}}’s filegrc workspace for its SOC 2 program. Engineers and agents maintain the source records under `data/`. The `filegrc` package validates, searches, edits, and renders those files.
|
|
6
6
|
|
|
7
7
|
Using this repository does not establish compliance by itself. Records must match actual practice and evidence must prove that controls operated during the audit period.
|
|
8
8
|
|
|
@@ -12,11 +12,13 @@ Do not guess a resource type, field name, enum value, relationship, or file path
|
|
|
12
12
|
|
|
13
13
|
```sh
|
|
14
14
|
npx filegrc guide --json
|
|
15
|
+
npx filegrc program-path --json
|
|
15
16
|
npx filegrc guide risk-assessment --json
|
|
16
17
|
npx filegrc list person --json
|
|
18
|
+
npx filegrc program-readiness --summary --json
|
|
17
19
|
```
|
|
18
20
|
|
|
19
|
-
The
|
|
21
|
+
`program-path` gives agents the same six-step order, exact page Instructions, Use, Policy Basis, commands, current state, and next actions shown in the renderer. 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.
|
|
20
22
|
|
|
21
23
|
For a new record, generate a mutation envelope:
|
|
22
24
|
|
|
@@ -44,7 +46,7 @@ Read `data/AGENTS.md` before changing records. More specific instructions inside
|
|
|
44
46
|
- Let the local app generate IDs from each record’s name or title. When editing JSON directly, keep IDs globally unique, human-readable, and lowercase kebab-case.
|
|
45
47
|
- Use ISO 8601 dates and RFC 3339 timestamps.
|
|
46
48
|
- Store relationships as resource IDs.
|
|
47
|
-
- Put policies, plans, charters, procedures, meeting minutes, training, assertions, narratives, templates, and audit responses in Markdown beside their JSON records.
|
|
49
|
+
- Put policies, plans, charters, procedures, meeting minutes, training, assertions, narratives, templates, and audit responses in Markdown beside their JSON records. filegrc derives the Markdown name, so records do not contain file paths.
|
|
48
50
|
- Put signed forms, screenshots, third-party reports, and immutable exports behind evidence records. These files may be PDF, image, CSV, or another fixed format.
|
|
49
51
|
- Never fetch an external evidence reference automatically.
|
|
50
52
|
- Do not store secrets, credentials, session data, or personal data that may need to be erased from Git history.
|
|
@@ -93,7 +95,7 @@ Headless agents get the same protection by exporting an edit payload with `fileg
|
|
|
93
95
|
|
|
94
96
|
`data/renderer.json` stores committed renderer preferences. New workspaces set `showOnboarding` to `true`. Completing or skipping onboarding sets it to `false`; the app does not commit that change.
|
|
95
97
|
|
|
96
|
-
Onboarding explains the file and Git workflow,
|
|
98
|
+
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 a `system` record and stores the management goal and program scope on `workspace`. 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.
|
|
97
99
|
|
|
98
100
|
The renderer is optional. Agents may set `showOnboarding` to `false` and maintain all records headlessly. Restart onboarding from Repository when useful. Read-only builds never run it.
|
|
99
101
|
|
|
@@ -105,15 +107,15 @@ The generated workspace starts with the SOC 2 Security category:
|
|
|
105
107
|
- The 33 Common Criteria reference IDs from CC1.1 through CC9.2, without the licensed criteria text
|
|
106
108
|
- The nine Description Criteria reference IDs from DC1 through DC9, without the licensed criteria text
|
|
107
109
|
- Planned controls mapped to those references and the included policies
|
|
108
|
-
- A security and risk oversight team chaired by an
|
|
110
|
+
- A security and risk oversight team chaired by an independent reviewer who may be internal or external
|
|
109
111
|
- Recurring obligations for the reviews, scans, tests, training, and meetings required by the included policies
|
|
110
112
|
- A default 5x5 risk method and Public, Internal, Confidential, and Restricted data classifications
|
|
111
113
|
|
|
112
|
-
Treat every planned control as a proposal until its owner, scope,
|
|
114
|
+
Treat every planned control as a proposal until its owner, actual procedure in Record Markdown, system scope, cadence, authoritative evidence sources, implementation date, and mappings match actual practice. For a control linked to filegrc obligations, every non-retired Work Queue schedule must be enabled and its governing policies effective. Marking the control implemented starts eligible schedules. Do not mark a control implemented because a policy describes it. Add Availability, Processing Integrity, Confidentiality, or Privacy criteria only when they are in scope.
|
|
113
115
|
|
|
114
|
-
The recurring obligations mirror the fixed cadences in the starter policies. Update the policy, control, and obligation together when an approved cadence changes. Create separate completion records, such as meetings, reviews, scans, tests, exercises, and attestations, for each period.
|
|
116
|
+
The recurring obligations mirror the fixed cadences in the starter policies. They remain proposals until every governing policy is active and effective and, when they name controls, at least one linked control is implemented. Update the policy, control, and obligation together when an approved cadence changes. Create separate completion records, such as meetings, reviews, scans, tests, exercises, and attestations, for each period.
|
|
115
117
|
|
|
116
|
-
## Policy
|
|
118
|
+
## Work Queue and Policy Events
|
|
117
119
|
|
|
118
120
|
Run the same obligation planner used by the web app:
|
|
119
121
|
|
|
@@ -122,7 +124,9 @@ npx filegrc obligations --json
|
|
|
122
124
|
npx filegrc obligations --from 2026-01-01 --through 2026-12-31 --complete --json
|
|
123
125
|
```
|
|
124
126
|
|
|
125
|
-
|
|
127
|
+
Work Queue includes recurring obligations, Policy Event tasks, and every other open Action Item. Create an Action Item only when follow-up from a Finding, Risk, Incident, review, test, meeting, Exception, or request needs its own assignee, deadline, and completion proof. Point `sourceResourceId` to the record that produced the task. Use that source record’s Markdown for the report and observations.
|
|
128
|
+
|
|
129
|
+
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:
|
|
126
130
|
|
|
127
131
|
```sh
|
|
128
132
|
npx filegrc complete obligation-id completion-record.json
|
|
@@ -130,14 +134,14 @@ npx filegrc complete obligation-id completion-record.json
|
|
|
130
134
|
|
|
131
135
|
Keep prior completion links because the planner matches each dated record to its own period.
|
|
132
136
|
|
|
133
|
-
Event obligations are templates. Do not mark a template complete or replace it for each occurrence.
|
|
137
|
+
Event obligations are templates. Do not mark a template complete or replace it for each occurrence. Use Trigger Work on Step 5 or run:
|
|
134
138
|
|
|
135
139
|
```sh
|
|
136
140
|
npx filegrc trigger person-started --occurred-on 2026-07-25 --subject person-new-worker --json
|
|
137
141
|
npx filegrc trigger person-ended --occurred-at 2026-07-25T16:30:00-05:00 --subject person-departing-worker --json
|
|
138
142
|
```
|
|
139
143
|
|
|
140
|
-
The command creates one `obligation-event` and its
|
|
144
|
+
Run `npx filegrc obligations` first to preview every task, owner, deadline, and requested proof for each available Policy Event. The trigger command creates one `obligation-event` and adds its full set of Action Items to the Work Queue in a single validated write. Its success output names the event, task count, task IDs, and deadlines. Hour-based deadlines require an RFC 3339 event timestamp so an immediate or 24-hour cutoff is exact. Day-based deadlines use the event’s calendar date. Link the requested completion resources and evidence to each action item, then mark the actions done and the event complete. Every generated action has a cutoff. filegrc applies a 30-day deadline when a custom event obligation omits one.
|
|
141
145
|
|
|
142
146
|
Complete an event action and link its new proof in one validated write:
|
|
143
147
|
|
|
@@ -146,7 +150,7 @@ npx filegrc complete-action action-item-id completion-record.json --completed-on
|
|
|
146
150
|
npx filegrc complete-event obligation-event-id --completed-on 2026-07-25
|
|
147
151
|
```
|
|
148
152
|
|
|
149
|
-
|
|
153
|
+
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.
|
|
150
154
|
|
|
151
155
|
## Headless Markdown
|
|
152
156
|
|
|
@@ -157,11 +161,36 @@ npx filegrc content risk-assessment risk-assessment-2026 --json
|
|
|
157
161
|
npx filegrc content risk-assessment risk-assessment-2026 --write updated-assessment.md
|
|
158
162
|
```
|
|
159
163
|
|
|
160
|
-
Run `filegrc guide <type>` to get slot names. Policies use `content`, meetings use `agenda` and `minutes`, and implicit long-form work uses `record`.
|
|
164
|
+
Run `filegrc guide <type>` to get slot names. Policies use `content`, meetings use `agenda` and `minutes`, and implicit long-form work uses `record`. filegrc derives the path and rejects content that does not belong to the record.
|
|
165
|
+
|
|
166
|
+
## Program readiness and the candidate period
|
|
167
|
+
|
|
168
|
+
Prepare the management program before creating an audit engagement:
|
|
169
|
+
|
|
170
|
+
```sh
|
|
171
|
+
npx filegrc program-readiness
|
|
172
|
+
npx filegrc program-readiness --require-ready --summary --json
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
The Evidence Ready gate requires:
|
|
176
|
+
|
|
177
|
+
1. A management goal, selected systems, criteria, and controls.
|
|
178
|
+
2. Active policies with completed text, separate management approval, real approval and effective dates, and linked controls.
|
|
179
|
+
3. Implemented controls with an owner, actual procedure, scope, cadence, evidence source, mappings, implementation date, and every eligible linked Work Queue schedule running.
|
|
180
|
+
4. Active authoritative systems with evidence source roles, access owners, and repeatable extraction instructions in Record Markdown.
|
|
181
|
+
5. A verified `test-export` or `test-capture` evidence record for each selected control family that relies on evidence from outside filegrc.
|
|
182
|
+
|
|
183
|
+
Completing onboarding creates draft External Evidence records only for evidence that must come from other systems and does not already have a dedicated Step 5 record. filegrc-managed records, such as risk assessments, meetings, vendor reviews, attestations, vulnerability scans, penetration tests, backup tests, exercises, exceptions, and findings, do not need a separate collection test. Put any fixed external artifact in an External Evidence record and link it from the operating record. For each generated draft, choose its authoritative source System, attach or reference the real result, record its collector and classification, then have another person verify it. Run `npx filegrc evidence-test-drafts` to create any drafts needed after the control set changes.
|
|
184
|
+
|
|
185
|
+
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.
|
|
186
|
+
|
|
187
|
+
Maintain risk assessments and the risk register while the program operates. Complete assessments on schedule and after material changes, and add or update controls when the conclusions require a different response. Audit preparation still checks for a current, independently reviewed assessment.
|
|
188
|
+
|
|
189
|
+
Record complementary customer or subservice controls after the internal control set is defined. `complementary-control.relatedControlIds` is the source of truth for those links. filegrc derives the reverse connections for Control pages and evidence packets.
|
|
161
190
|
|
|
162
191
|
## Audit preparation and evidence packets
|
|
163
192
|
|
|
164
|
-
After
|
|
193
|
+
After engaging a CPA firm, create one audit record and set the firm-agreed Type 1 date or Type 2 period. Then initialize the engagement-specific management work:
|
|
165
194
|
|
|
166
195
|
```sh
|
|
167
196
|
npx filegrc prepare-audit audit-2026-type-2
|
|
@@ -169,13 +198,22 @@ npx filegrc audit-readiness audit-2026-type-2
|
|
|
169
198
|
npx filegrc audit-readiness audit-2026-type-2 --require-ready --json
|
|
170
199
|
```
|
|
171
200
|
|
|
201
|
+
The audit record’s `typeOneAsOf`, `periodStart`, and `periodEnd` are the dates agreed with the CPA firm. Keep the workspace candidate dates even when the formal period differs.
|
|
202
|
+
|
|
172
203
|
Preparation creates a separate system description, management assertion, and management representation document for the engagement from the local starter templates. Type 2 preparation also creates a period completeness statement and one `audit-population` record for each standard population. It is safe to run again and does not approve documents, mark controls implemented, or create evidence. Do not reuse one completed management document across engagements.
|
|
173
204
|
|
|
205
|
+
Review both evidence paths against the exact firm-agreed date or period:
|
|
206
|
+
|
|
207
|
+
1. filegrc Evidence consists of dated Step 5 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.
|
|
208
|
+
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.
|
|
209
|
+
|
|
210
|
+
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.
|
|
211
|
+
|
|
174
212
|
Near the end of fieldwork, link a verified fixed-format copy of the signed management representation letter to its engagement-specific document. Date it on or after the Type 1 date or Type 2 period end. A representation that is still marked for later blocks packet delivery.
|
|
175
213
|
|
|
176
|
-
Catalog each authoritative source under Systems and assign its `evidenceSourceKinds`. Name the people who can access
|
|
214
|
+
Catalog each authoritative source under Systems and assign its `evidenceSourceKinds`. A third-party application is still a System because it operates controls or produces evidence. Create a separate Vendor for its provider and connect the System through `vendorId`; keep contracts, due diligence, and supplier risk on the Vendor. Name the people who can access system reports and keep extraction instructions in the System's Record Markdown. For each Type 2 population, select one source system and export the exact audit period. Split a population when different systems or queries produce its items. Link a verified `population-export` evidence record that names the same source system and stores the query or report parameters, generation time, timezone, count, completeness check, and accuracy check. A zero count still requires the source export and query. A population linked to an in-scope control cannot be marked not applicable.
|
|
177
215
|
|
|
178
|
-
Every evidence record names its collector. Verified evidence also names its verifier and verification date. Use `sourceSystemId` for system exports, `sourceResourceIds` for
|
|
216
|
+
Every evidence record names its collector. Verified evidence also names its verifier and verification date. Use `sourceSystemId` for system exports, `sourceResourceIds` for filegrc records, and `sourceCommit` to bind the evidence to repository state.
|
|
179
217
|
|
|
180
218
|
Preview coverage before writing output:
|
|
181
219
|
|
|
@@ -185,21 +223,23 @@ npx filegrc evidence-packet --audit audit-2026-type-2
|
|
|
185
223
|
npx filegrc evidence-packet --audit audit-2026-type-2 --preview --require-ready
|
|
186
224
|
```
|
|
187
225
|
|
|
188
|
-
The packet includes records explicitly related to the selected engagement, its systems, controls, criteria, policies, evidence, and dependencies. It does not include unrelated dated records from the workspace. A Type 2 packet adds
|
|
226
|
+
The packet includes records explicitly related to the selected engagement, its systems, controls, criteria, policies, evidence, and dependencies. It does not include unrelated dated records from the workspace. A Type 2 packet adds filegrc Evidence, recurring obligation occurrences, event workflows, and management population reconciliations. Output includes a control matrix with separate filegrc Evidence and External Evidence columns, source-system index, external-evidence delivery index, population index, evidence index, committed historical source versions, and SHA-256 checksums. Output under `.filegrc/evidence-packets/` is derived and must not be hand-edited or committed.
|
|
189
227
|
|
|
190
|
-
Treat a packet as ready for management delivery only when its status is `delivery-ready`, its review list is clear, and its manifest names a clean Git revision. This means
|
|
228
|
+
Treat a packet as ready for management delivery only when its status is `delivery-ready`, its review list is clear, and its manifest names a clean Git revision. This means filegrc's management checks passed. It does not mean the engagement team found the evidence sufficient or appropriate. The generator copies raw records, Markdown, and local fixed attachments. It never fetches external references. Reconcile `external-evidence-index.csv` to the auditor portal or other approved delivery system before telling the engagement team that submission is complete.
|
|
191
229
|
|
|
192
|
-
Link a control test to its `audit-population` record when sampling applies. Link item-level sample evidence separately. Management owns population completeness and accuracy. The auditor owns sample selection, independent testing, exception evaluation, and the report opinion. The auditor or publisher also supplies the authoritative criteria and examination guidance.
|
|
230
|
+
Link a control test to its `audit-population` record when sampling applies. Link item-level sample evidence separately. Management owns population completeness and accuracy. The auditor owns sample selection, independent testing, exception evaluation, and the report opinion. The auditor or publisher also supplies the authoritative criteria and examination guidance. filegrc stores references and orientation text, not licensed criteria.
|
|
193
231
|
|
|
194
232
|
## Content and approvals
|
|
195
233
|
|
|
196
|
-
The seed policy owner is {{policy_owner_name}} and the reporting address is {{security_contact_email}}. Replace ownership or contacts when responsibilities change.
|
|
234
|
+
The seed policy owner is {{policy_owner_name}} at {{policy_owner_email}}, and the security reporting address is {{security_contact_email}}. Replace ownership or contacts when responsibilities change.
|
|
235
|
+
|
|
236
|
+
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.
|
|
197
237
|
|
|
198
|
-
The
|
|
238
|
+
The management reviewer and CPA auditor are different roles. Do not assign the CPA firm management or approval work without first confirming the firm's independence requirements.
|
|
199
239
|
|
|
200
240
|
Policy and training attestations must identify the exact Git revision of the content that a person acknowledged. Store signatures as evidence attachments and link their evidence IDs from the attestation.
|
|
201
241
|
|
|
202
|
-
Committee and risk meeting minutes are `meeting` resources with a primary Markdown companion. An optional `-agenda.md` companion holds the agenda. Record attendees, decisions, risks discussed
|
|
242
|
+
Committee and risk meeting minutes are `meeting` resources with a primary Markdown companion. An optional `-agenda.md` companion holds the agenda. Record attendees, decisions, and risks discussed on the Meeting. When follow-up needs separate tracking, create an `action-item` whose `sourceResourceId` points to the Meeting.
|
|
203
243
|
|
|
204
244
|
## Audit evidence
|
|
205
245
|
|