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