great-cto 2.23.0 → 2.25.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.
@@ -11,5 +11,5 @@
11
11
  * writeFileSync('agentshield.sarif', JSON.stringify(toSarif(report)));
12
12
  */
13
13
  export { scan, scanFile } from './scanner.js';
14
- export { loadRules, parseRulesFile } from './rules-loader.js';
14
+ export { loadRules, parseRulesFile, loadUserRules, userGuardrailsPath } from './rules-loader.js';
15
15
  export { SEVERITY_ORDER, severityRank } from './types.js';
@@ -5,9 +5,14 @@
5
5
  * each rule file is a list of dash-prefixed entries with key/value lines.
6
6
  * If we ever need real YAML (anchors, complex nesting), we'll add `yaml` as
7
7
  * a dep then.
8
+ *
9
+ * User-defined rules are loaded from ~/.great_cto/guardrails.yml and merged
10
+ * with the built-in rules. User rules use the same YAML format but include
11
+ * an optional `action: block | audit | redact` field.
8
12
  */
9
13
  import { readdirSync, readFileSync, existsSync } from 'node:fs';
10
14
  import { join, dirname } from 'node:path';
15
+ import { homedir } from 'node:os';
11
16
  import { fileURLToPath } from 'node:url';
12
17
  const __dirname = dirname(fileURLToPath(import.meta.url));
13
18
  /**
@@ -40,8 +45,39 @@ export function loadRules(rulesDir = defaultRulesDir()) {
40
45
  const text = readFileSync(join(rulesDir, f), 'utf8');
41
46
  rules.push(...parseRulesFile(text, f));
42
47
  }
48
+ // Merge user-defined rules from ~/.great_cto/guardrails.yml
49
+ const userRules = loadUserRules();
50
+ rules.push(...userRules);
43
51
  return rules;
44
52
  }
53
+ /**
54
+ * Default path for user-defined guardrail rules.
55
+ * Falls back to legacy .great_cto/guardrails.yml in the current project.
56
+ */
57
+ export function userGuardrailsPath() {
58
+ return join(homedir(), '.great_cto', 'guardrails.yml');
59
+ }
60
+ /**
61
+ * Load user-defined rules from ~/.great_cto/guardrails.yml.
62
+ * Returns [] silently if the file does not exist.
63
+ * Errors in user rules are surfaced as warnings (console.warn) but do not
64
+ * abort the scan — broken user rules should not block CI.
65
+ */
66
+ export function loadUserRules(path) {
67
+ const guardrailsPath = path ?? userGuardrailsPath();
68
+ if (!existsSync(guardrailsPath))
69
+ return [];
70
+ try {
71
+ const text = readFileSync(guardrailsPath, 'utf8');
72
+ const parsed = parseRulesFile(text, guardrailsPath);
73
+ // Mark all user rules as userDefined so scanners can handle action correctly
74
+ return parsed.map(r => ({ ...r, userDefined: true }));
75
+ }
76
+ catch (e) {
77
+ console.warn(`agentshield: warning — failed to load user guardrails from ${guardrailsPath}: ${e.message}`);
78
+ return [];
79
+ }
80
+ }
45
81
  /**
46
82
  * Parse a minimal YAML format:
47
83
  *
@@ -160,6 +196,7 @@ function parseBlock(block, filename) {
160
196
  throw new Error(`missing required field "${required}" in rule (block from ${filename})\nparsed: ${JSON.stringify(out)}`);
161
197
  }
162
198
  }
199
+ const action = out.action;
163
200
  return {
164
201
  id: out.id,
165
202
  scanner: out.scanner,
@@ -171,5 +208,6 @@ function parseBlock(block, filename) {
171
208
  patterns: out.patterns,
172
209
  file_globs: out.file_globs,
173
210
  negate: out.negate,
211
+ action: (action === 'block' || action === 'audit' || action === 'redact') ? action : undefined,
174
212
  };
175
213
  }
package/dist/bootstrap.js CHANGED
@@ -2,6 +2,7 @@
2
2
  // Safe: will NOT overwrite an existing PROJECT.md.
3
3
  import { existsSync, mkdirSync, writeFileSync, readFileSync } from "node:fs";
4
4
  import { join } from "node:path";
5
+ import { homedir } from "node:os";
5
6
  import { dim, success, warn } from "./ui.js";
6
7
  import { suggestJurisdictions } from "./jurisdictions.js";
7
8
  import { compileFlow, renderFlowMd } from "./flow.js";
@@ -145,6 +146,55 @@ when you actually start work:
145
146
  `;
146
147
  writeFileSync(projectMd, content, "utf-8");
147
148
  success(`created .great_cto/PROJECT.md ${dim(`(archetype: ${archetype})`)}`);
149
+ // Write ~/.great_cto/guardrails.yml example if it doesn't exist yet.
150
+ // This is the user-level agentshield config — applies across all projects.
151
+ const globalGreatCtoDir = join(homedir(), ".great_cto");
152
+ const guardrailsPath = join(globalGreatCtoDir, "guardrails.yml");
153
+ if (!existsSync(guardrailsPath)) {
154
+ mkdirSync(globalGreatCtoDir, { recursive: true });
155
+ const guardrailsContent = `# ~/.great_cto/guardrails.yml
156
+ # User-defined agentshield rules. Loaded and merged with built-in rules on every scan.
157
+ # Rules use the same YAML format as agentshield-rules/*.yaml.
158
+ #
159
+ # action field (user rules only):
160
+ # block — scan fails (treated as critical finding; blocks gate:ship)
161
+ # audit — finding reported but scan continues (warning only)
162
+ # redact — same as audit; marks pattern for future content redaction
163
+ #
164
+ # Example rule — customize with your org's patterns:
165
+ #
166
+ # - id: UG-001
167
+ # scanner: secrets-in-prompts
168
+ # title: Internal API token pattern
169
+ # severity: critical
170
+ # description: |
171
+ # Detects hardcoded internal API tokens matching the org pattern mytoken_*.
172
+ # remediation: |
173
+ # Move to environment variable. Reference via process.env.MY_TOKEN.
174
+ # patterns:
175
+ # - 'mytoken_[a-z0-9]{32}'
176
+ # action: block
177
+ #
178
+ # - id: UG-002
179
+ # scanner: prompt-injection
180
+ # title: Audit use of customer data in prompts
181
+ # severity: high
182
+ # description: |
183
+ # Flags when customer PII fields (email, phone, address) are embedded in prompts.
184
+ # remediation: |
185
+ # Replace PII with anonymized tokens before embedding in prompts.
186
+ # patterns:
187
+ # - 'customer\.(email|phone|address|ssn)'
188
+ # action: audit
189
+ # file_globs:
190
+ # - "**/*.ts"
191
+ # - "**/*.py"
192
+ #
193
+ # Add your own rules below:
194
+ `;
195
+ writeFileSync(guardrailsPath, guardrailsContent, "utf-8");
196
+ success(`created ~/.great_cto/guardrails.yml ${dim("(user-level agentshield rules — edit to add org-specific patterns)")}`);
197
+ }
148
198
  // Write FLOW.md — compiled delivery flow for agents and user
149
199
  const confidence = detectionMeta?.confidence ?? "medium";
150
200
  const size = (detection.projectSize ?? "medium");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "great-cto",
3
- "version": "2.23.0",
3
+ "version": "2.25.0",
4
4
  "description": "One command install for the great_cto Claude Code plugin. Auto-detects your stack, picks the right archetype, bootstraps PROJECT.md.",
5
5
  "keywords": [
6
6
  "claude-code",