@red-codes/policy 1.0.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 +190 -0
- package/dist/composer.d.ts +41 -0
- package/dist/composer.d.ts.map +1 -0
- package/dist/composer.js +55 -0
- package/dist/composer.js.map +1 -0
- package/dist/evaluator.d.ts +182 -0
- package/dist/evaluator.d.ts.map +1 -0
- package/dist/evaluator.js +335 -0
- package/dist/evaluator.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -0
- package/dist/loader.d.ts +13 -0
- package/dist/loader.d.ts.map +1 -0
- package/dist/loader.js +151 -0
- package/dist/loader.js.map +1 -0
- package/dist/pack-loader.d.ts +51 -0
- package/dist/pack-loader.d.ts.map +1 -0
- package/dist/pack-loader.js +169 -0
- package/dist/pack-loader.js.map +1 -0
- package/dist/pack-version.d.ts +51 -0
- package/dist/pack-version.d.ts.map +1 -0
- package/dist/pack-version.js +129 -0
- package/dist/pack-version.js.map +1 -0
- package/dist/policy-trust.d.ts +38 -0
- package/dist/policy-trust.d.ts.map +1 -0
- package/dist/policy-trust.js +119 -0
- package/dist/policy-trust.js.map +1 -0
- package/dist/yaml-loader.d.ts +53 -0
- package/dist/yaml-loader.d.ts.map +1 -0
- package/dist/yaml-loader.js +573 -0
- package/dist/yaml-loader.js.map +1 -0
- package/package.json +41 -0
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
// Policy pack loader — resolves, loads, validates, and merges policy packs.
|
|
2
|
+
// Supports local directory packs and npm-style package references.
|
|
3
|
+
// Includes version compatibility checking for policy packs.
|
|
4
|
+
//
|
|
5
|
+
// A policy pack is a YAML or JSON policy file that can be referenced via the
|
|
6
|
+
// `extends` key in a policy definition. Packs are loaded and their rules are
|
|
7
|
+
// merged with the local policy, with local rules taking precedence.
|
|
8
|
+
import { readFileSync, existsSync } from 'node:fs';
|
|
9
|
+
import { resolve, join } from 'node:path';
|
|
10
|
+
import { loadYamlPolicy } from './yaml-loader.js';
|
|
11
|
+
import { validatePolicy } from './loader.js';
|
|
12
|
+
import { parsePackReference, checkCompatibility, satisfiesRange } from './pack-version.js';
|
|
13
|
+
/** Candidate filenames when resolving a pack directory */
|
|
14
|
+
const PACK_MANIFEST_CANDIDATES = [
|
|
15
|
+
'agentguard-pack.yaml',
|
|
16
|
+
'agentguard-pack.yml',
|
|
17
|
+
'agentguard-pack.json',
|
|
18
|
+
'agentguard.yaml',
|
|
19
|
+
'agentguard.yml',
|
|
20
|
+
];
|
|
21
|
+
/**
|
|
22
|
+
* Resolve a single pack reference to an absolute file path.
|
|
23
|
+
*
|
|
24
|
+
* Supports three reference styles:
|
|
25
|
+
* 1. Relative path — `"./packs/strict"` or `"./packs/strict.yaml"`
|
|
26
|
+
* 2. Absolute path — `"/home/user/packs/strict.yaml"`
|
|
27
|
+
* 3. npm package — `"@agentguard/security-pack"` resolved from node_modules
|
|
28
|
+
*
|
|
29
|
+
* References may include a version constraint suffix (e.g., `"./pack@^1.2.0"`),
|
|
30
|
+
* which is stripped before path resolution.
|
|
31
|
+
*/
|
|
32
|
+
export function resolvePackPath(ref, baseDir) {
|
|
33
|
+
// 1. Direct file reference (relative or absolute)
|
|
34
|
+
const directPath = resolve(baseDir, ref);
|
|
35
|
+
if (existsSync(directPath)) {
|
|
36
|
+
// If it's a file, use it directly
|
|
37
|
+
if (directPath.endsWith('.yaml') ||
|
|
38
|
+
directPath.endsWith('.yml') ||
|
|
39
|
+
directPath.endsWith('.json')) {
|
|
40
|
+
return directPath;
|
|
41
|
+
}
|
|
42
|
+
// If it's a directory, look for manifest files
|
|
43
|
+
for (const candidate of PACK_MANIFEST_CANDIDATES) {
|
|
44
|
+
const candidatePath = join(directPath, candidate);
|
|
45
|
+
if (existsSync(candidatePath)) {
|
|
46
|
+
return candidatePath;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
// Try with common extensions if the direct path didn't work
|
|
51
|
+
for (const ext of ['.yaml', '.yml', '.json']) {
|
|
52
|
+
const withExt = directPath + ext;
|
|
53
|
+
if (existsSync(withExt)) {
|
|
54
|
+
return withExt;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
// 2. npm package reference — search node_modules
|
|
58
|
+
const nodeModulesPath = join(baseDir, 'node_modules', ref);
|
|
59
|
+
if (existsSync(nodeModulesPath)) {
|
|
60
|
+
for (const candidate of PACK_MANIFEST_CANDIDATES) {
|
|
61
|
+
const candidatePath = join(nodeModulesPath, candidate);
|
|
62
|
+
if (existsSync(candidatePath)) {
|
|
63
|
+
return candidatePath;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Load a single policy pack from a resolved file path.
|
|
71
|
+
*/
|
|
72
|
+
export function loadPackFile(filePath) {
|
|
73
|
+
const content = readFileSync(filePath, 'utf8');
|
|
74
|
+
if (filePath.endsWith('.yaml') || filePath.endsWith('.yml')) {
|
|
75
|
+
return loadYamlPolicy(content, `pack:${filePath}`);
|
|
76
|
+
}
|
|
77
|
+
try {
|
|
78
|
+
const parsed = JSON.parse(content);
|
|
79
|
+
const result = validatePolicy(parsed);
|
|
80
|
+
if (!result.valid) {
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
return {
|
|
84
|
+
id: parsed.id || `pack:${filePath}`,
|
|
85
|
+
name: parsed.name || 'JSON Pack',
|
|
86
|
+
description: parsed.description,
|
|
87
|
+
rules: parsed.rules,
|
|
88
|
+
severity: parsed.severity ?? 3,
|
|
89
|
+
version: parsed.version,
|
|
90
|
+
agentguardVersion: parsed.agentguardVersion,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Resolve and load all policy packs from an `extends` list.
|
|
99
|
+
*
|
|
100
|
+
* @param extends_ - Array of pack references (paths, npm package names, or versioned refs like "pack@^1.0.0")
|
|
101
|
+
* @param baseDir - Directory to resolve relative paths from
|
|
102
|
+
* @param options - Optional settings including the current AgentGuard version for compatibility checks
|
|
103
|
+
* @returns Loaded pack policies, errors, and version warnings
|
|
104
|
+
*/
|
|
105
|
+
export function resolveExtends(extends_, baseDir, options) {
|
|
106
|
+
const policies = [];
|
|
107
|
+
const errors = [];
|
|
108
|
+
const warnings = [];
|
|
109
|
+
const seenIds = new Set();
|
|
110
|
+
for (const rawRef of extends_) {
|
|
111
|
+
const { ref, versionConstraint } = parsePackReference(rawRef);
|
|
112
|
+
const resolvedPath = resolvePackPath(ref, baseDir);
|
|
113
|
+
if (!resolvedPath) {
|
|
114
|
+
errors.push(`Pack not found: "${ref}" (searched from ${baseDir})`);
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
const pack = loadPackFile(resolvedPath);
|
|
118
|
+
if (!pack) {
|
|
119
|
+
errors.push(`Failed to load pack: "${ref}" (${resolvedPath})`);
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
if (seenIds.has(pack.id)) {
|
|
123
|
+
errors.push(`Duplicate pack ID: "${pack.id}" from "${ref}"`);
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
// Check version pin constraint (from extends reference like "pack@^1.2.0")
|
|
127
|
+
if (versionConstraint) {
|
|
128
|
+
if (pack.version) {
|
|
129
|
+
if (!satisfiesRange(pack.version, versionConstraint)) {
|
|
130
|
+
warnings.push(`Pack "${pack.id}" version ${pack.version} does not satisfy ` +
|
|
131
|
+
`pinned constraint ${versionConstraint} (from "${rawRef}")`);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
else {
|
|
135
|
+
warnings.push(`Pack "${pack.id}" has no version field but version pin ` +
|
|
136
|
+
`${versionConstraint} was requested (from "${rawRef}")`);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
// Check AgentGuard version compatibility
|
|
140
|
+
if (pack.agentguardVersion && options?.currentAgentguardVersion) {
|
|
141
|
+
const compat = checkCompatibility(pack.agentguardVersion, options.currentAgentguardVersion);
|
|
142
|
+
if (!compat.compatible) {
|
|
143
|
+
errors.push(`Pack "${pack.id}" is incompatible: ${compat.reason}`);
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
seenIds.add(pack.id);
|
|
148
|
+
policies.push(pack);
|
|
149
|
+
}
|
|
150
|
+
return { policies, errors, warnings };
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Merge pack policies with a local policy.
|
|
154
|
+
*
|
|
155
|
+
* Precedence: local rules override pack rules. Within packs, earlier entries
|
|
156
|
+
* in the `extends` list take precedence over later entries.
|
|
157
|
+
*
|
|
158
|
+
* The merge strategy is:
|
|
159
|
+
* 1. Collect all rules from packs (in extends order)
|
|
160
|
+
* 2. Append local rules (which take precedence during evaluation since
|
|
161
|
+
* the evaluator checks deny rules first, then allow rules)
|
|
162
|
+
* 3. Return a single merged policy array
|
|
163
|
+
*/
|
|
164
|
+
export function mergePolicies(localPolicy, packPolicies) {
|
|
165
|
+
// Pack policies come first (lower precedence in evaluation order)
|
|
166
|
+
// Local policy comes last (highest precedence)
|
|
167
|
+
return [...packPolicies, localPolicy];
|
|
168
|
+
}
|
|
169
|
+
//# sourceMappingURL=pack-loader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pack-loader.js","sourceRoot":"","sources":["../src/pack-loader.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
export interface SemVer {
|
|
2
|
+
major: number;
|
|
3
|
+
minor: number;
|
|
4
|
+
patch: number;
|
|
5
|
+
}
|
|
6
|
+
export interface CompatibilityResult {
|
|
7
|
+
compatible: boolean;
|
|
8
|
+
reason?: string;
|
|
9
|
+
}
|
|
10
|
+
export interface ParsedPackReference {
|
|
11
|
+
ref: string;
|
|
12
|
+
versionConstraint?: string;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Parse a semver string into its components.
|
|
16
|
+
* Returns null if the string is not a valid semver.
|
|
17
|
+
*/
|
|
18
|
+
export declare function parseSemver(version: string): SemVer | null;
|
|
19
|
+
/**
|
|
20
|
+
* Compare two semver versions.
|
|
21
|
+
* Returns -1 if a < b, 0 if a === b, 1 if a > b.
|
|
22
|
+
*/
|
|
23
|
+
export declare function compareSemver(a: SemVer, b: SemVer): -1 | 0 | 1;
|
|
24
|
+
/**
|
|
25
|
+
* Check if a version satisfies a version range.
|
|
26
|
+
*
|
|
27
|
+
* Supported range formats:
|
|
28
|
+
* - Exact: `"1.2.3"` — must match exactly
|
|
29
|
+
* - Greater-or-equal: `">=1.2.3"` — version must be >= range
|
|
30
|
+
* - Caret: `"^1.2.3"` — compatible with (same major, >= minor.patch)
|
|
31
|
+
* - Tilde: `"~1.2.3"` — reasonably close (same major.minor, >= patch)
|
|
32
|
+
*/
|
|
33
|
+
export declare function satisfiesRange(version: string, range: string): boolean;
|
|
34
|
+
/**
|
|
35
|
+
* Check if a policy pack is compatible with the current AgentGuard version.
|
|
36
|
+
*
|
|
37
|
+
* @param packAgentguardVersion — the `agentguardVersion` range from the pack (e.g., ">=2.0.0")
|
|
38
|
+
* @param currentVersion — the running AgentGuard version (e.g., "2.2.0")
|
|
39
|
+
*/
|
|
40
|
+
export declare function checkCompatibility(packAgentguardVersion: string, currentVersion: string): CompatibilityResult;
|
|
41
|
+
/**
|
|
42
|
+
* Parse a pack reference that may include a version constraint.
|
|
43
|
+
*
|
|
44
|
+
* Supports `"pack-name@^1.2.0"` syntax where the `@version` suffix
|
|
45
|
+
* specifies a version pin for the pack's own version.
|
|
46
|
+
*
|
|
47
|
+
* Scoped npm packages (e.g., `@agentguard/security-pack@^1.0.0`) are
|
|
48
|
+
* handled by splitting on the last `@`.
|
|
49
|
+
*/
|
|
50
|
+
export declare function parsePackReference(ref: string): ParsedPackReference;
|
|
51
|
+
//# sourceMappingURL=pack-version.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pack-version.d.ts","sourceRoot":"","sources":["../src/pack-version.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,MAAM;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,mBAAmB;IAClC,UAAU,EAAE,OAAO,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,mBAAmB;IAClC,GAAG,EAAE,MAAM,CAAC;IACZ,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAID;;;GAGG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAQ1D;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAK9D;AAED;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAoCtE;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAChC,qBAAqB,EAAE,MAAM,EAC7B,cAAc,EAAE,MAAM,GACrB,mBAAmB,CAgBrB;AAED;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,mBAAmB,CAoBnE"}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
// Policy pack versioning — semver parsing, range checking, and compatibility validation.
|
|
2
|
+
// Zero external dependencies. Supports the subset of semver needed for policy packs.
|
|
3
|
+
const SEMVER_REGEX = /^(\d+)\.(\d+)\.(\d+)$/;
|
|
4
|
+
/**
|
|
5
|
+
* Parse a semver string into its components.
|
|
6
|
+
* Returns null if the string is not a valid semver.
|
|
7
|
+
*/
|
|
8
|
+
export function parseSemver(version) {
|
|
9
|
+
const match = version.trim().match(SEMVER_REGEX);
|
|
10
|
+
if (!match)
|
|
11
|
+
return null;
|
|
12
|
+
return {
|
|
13
|
+
major: parseInt(match[1], 10),
|
|
14
|
+
minor: parseInt(match[2], 10),
|
|
15
|
+
patch: parseInt(match[3], 10),
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Compare two semver versions.
|
|
20
|
+
* Returns -1 if a < b, 0 if a === b, 1 if a > b.
|
|
21
|
+
*/
|
|
22
|
+
export function compareSemver(a, b) {
|
|
23
|
+
if (a.major !== b.major)
|
|
24
|
+
return a.major < b.major ? -1 : 1;
|
|
25
|
+
if (a.minor !== b.minor)
|
|
26
|
+
return a.minor < b.minor ? -1 : 1;
|
|
27
|
+
if (a.patch !== b.patch)
|
|
28
|
+
return a.patch < b.patch ? -1 : 1;
|
|
29
|
+
return 0;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Check if a version satisfies a version range.
|
|
33
|
+
*
|
|
34
|
+
* Supported range formats:
|
|
35
|
+
* - Exact: `"1.2.3"` — must match exactly
|
|
36
|
+
* - Greater-or-equal: `">=1.2.3"` — version must be >= range
|
|
37
|
+
* - Caret: `"^1.2.3"` — compatible with (same major, >= minor.patch)
|
|
38
|
+
* - Tilde: `"~1.2.3"` — reasonably close (same major.minor, >= patch)
|
|
39
|
+
*/
|
|
40
|
+
export function satisfiesRange(version, range) {
|
|
41
|
+
const trimmed = range.trim();
|
|
42
|
+
if (trimmed.startsWith('>=')) {
|
|
43
|
+
const rangeVer = parseSemver(trimmed.slice(2));
|
|
44
|
+
const ver = parseSemver(version);
|
|
45
|
+
if (!rangeVer || !ver)
|
|
46
|
+
return false;
|
|
47
|
+
return compareSemver(ver, rangeVer) >= 0;
|
|
48
|
+
}
|
|
49
|
+
if (trimmed.startsWith('^')) {
|
|
50
|
+
const rangeVer = parseSemver(trimmed.slice(1));
|
|
51
|
+
const ver = parseSemver(version);
|
|
52
|
+
if (!rangeVer || !ver)
|
|
53
|
+
return false;
|
|
54
|
+
// Same major, >= minor.patch
|
|
55
|
+
if (ver.major !== rangeVer.major)
|
|
56
|
+
return false;
|
|
57
|
+
if (ver.minor > rangeVer.minor)
|
|
58
|
+
return true;
|
|
59
|
+
if (ver.minor === rangeVer.minor)
|
|
60
|
+
return ver.patch >= rangeVer.patch;
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
if (trimmed.startsWith('~')) {
|
|
64
|
+
const rangeVer = parseSemver(trimmed.slice(1));
|
|
65
|
+
const ver = parseSemver(version);
|
|
66
|
+
if (!rangeVer || !ver)
|
|
67
|
+
return false;
|
|
68
|
+
// Same major.minor, >= patch
|
|
69
|
+
if (ver.major !== rangeVer.major)
|
|
70
|
+
return false;
|
|
71
|
+
if (ver.minor !== rangeVer.minor)
|
|
72
|
+
return false;
|
|
73
|
+
return ver.patch >= rangeVer.patch;
|
|
74
|
+
}
|
|
75
|
+
// Exact match
|
|
76
|
+
const rangeVer = parseSemver(trimmed);
|
|
77
|
+
const ver = parseSemver(version);
|
|
78
|
+
if (!rangeVer || !ver)
|
|
79
|
+
return false;
|
|
80
|
+
return compareSemver(ver, rangeVer) === 0;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Check if a policy pack is compatible with the current AgentGuard version.
|
|
84
|
+
*
|
|
85
|
+
* @param packAgentguardVersion — the `agentguardVersion` range from the pack (e.g., ">=2.0.0")
|
|
86
|
+
* @param currentVersion — the running AgentGuard version (e.g., "2.2.0")
|
|
87
|
+
*/
|
|
88
|
+
export function checkCompatibility(packAgentguardVersion, currentVersion) {
|
|
89
|
+
const current = parseSemver(currentVersion);
|
|
90
|
+
if (!current) {
|
|
91
|
+
return { compatible: true, reason: `Cannot parse current version "${currentVersion}"` };
|
|
92
|
+
}
|
|
93
|
+
if (!satisfiesRange(currentVersion, packAgentguardVersion)) {
|
|
94
|
+
return {
|
|
95
|
+
compatible: false,
|
|
96
|
+
reason: `Pack requires AgentGuard ${packAgentguardVersion} ` +
|
|
97
|
+
`but current version is ${currentVersion}`,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
return { compatible: true };
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Parse a pack reference that may include a version constraint.
|
|
104
|
+
*
|
|
105
|
+
* Supports `"pack-name@^1.2.0"` syntax where the `@version` suffix
|
|
106
|
+
* specifies a version pin for the pack's own version.
|
|
107
|
+
*
|
|
108
|
+
* Scoped npm packages (e.g., `@agentguard/security-pack@^1.0.0`) are
|
|
109
|
+
* handled by splitting on the last `@`.
|
|
110
|
+
*/
|
|
111
|
+
export function parsePackReference(ref) {
|
|
112
|
+
// Find the last '@' that is not at position 0 (scoped packages start with @)
|
|
113
|
+
const lastAt = ref.lastIndexOf('@');
|
|
114
|
+
if (lastAt <= 0) {
|
|
115
|
+
// No version constraint, or the only @ is the scope prefix
|
|
116
|
+
return { ref };
|
|
117
|
+
}
|
|
118
|
+
const possibleVersion = ref.slice(lastAt + 1);
|
|
119
|
+
// Check if what follows the @ looks like a version constraint
|
|
120
|
+
if (/^[~^>=]*\d/.test(possibleVersion)) {
|
|
121
|
+
return {
|
|
122
|
+
ref: ref.slice(0, lastAt),
|
|
123
|
+
versionConstraint: possibleVersion,
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
// Not a version constraint (e.g., just part of a package name)
|
|
127
|
+
return { ref };
|
|
128
|
+
}
|
|
129
|
+
//# sourceMappingURL=pack-version.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pack-version.js","sourceRoot":"","sources":["../src/pack-version.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
export type RiskLevel = 'danger' | 'warning' | 'info';
|
|
2
|
+
export interface RiskFlag {
|
|
3
|
+
level: RiskLevel;
|
|
4
|
+
message: string;
|
|
5
|
+
pattern: string;
|
|
6
|
+
}
|
|
7
|
+
export type TrustClass = 'implicitly_trusted' | 'trust_gated';
|
|
8
|
+
export interface PolicyTrustResult {
|
|
9
|
+
trustClass: TrustClass;
|
|
10
|
+
status: 'trusted' | 'untrusted' | 'content_changed';
|
|
11
|
+
riskFlags: RiskFlag[];
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Pure function. Regex-based pattern matching on policy YAML content to detect
|
|
15
|
+
* risky configurations. Returns an array of risk flags (empty for safe policies).
|
|
16
|
+
*/
|
|
17
|
+
export declare function analyzePolicyRisk(content: string): RiskFlag[];
|
|
18
|
+
/**
|
|
19
|
+
* Pure function. Determines trust classification based on path location.
|
|
20
|
+
* - Home dir paths → implicitly_trusted
|
|
21
|
+
* - Explicit CLI flag (--policy) → implicitly_trusted
|
|
22
|
+
* - Project-local files → trust_gated
|
|
23
|
+
*/
|
|
24
|
+
export declare function classifyPolicyLocation(policyPath: string, options?: {
|
|
25
|
+
isExplicitCliFlag?: boolean;
|
|
26
|
+
}): TrustClass;
|
|
27
|
+
/**
|
|
28
|
+
* Non-interactive trust gate. Reads trust store + file hash.
|
|
29
|
+
* No stdin/stdout prompts — interactive prompting lives in the CLI trust command.
|
|
30
|
+
*
|
|
31
|
+
* 1. Classify location → if implicitly_trusted, return early with status: 'trusted'
|
|
32
|
+
* 2. If trust_gated, check trust store via verifyTrust() from @red-codes/core
|
|
33
|
+
* 3. CI override: if isCiTrustOverride() returns true, treat as 'trusted'
|
|
34
|
+
*/
|
|
35
|
+
export declare function verifyPolicyTrust(policyPath: string, options?: {
|
|
36
|
+
isExplicitCliFlag?: boolean;
|
|
37
|
+
}): Promise<PolicyTrustResult>;
|
|
38
|
+
//# sourceMappingURL=policy-trust.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"policy-trust.d.ts","sourceRoot":"","sources":["../src/policy-trust.ts"],"names":[],"mappings":"AAQA,MAAM,MAAM,SAAS,GAAG,QAAQ,GAAG,SAAS,GAAG,MAAM,CAAC;AAEtD,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAE,SAAS,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,MAAM,UAAU,GAAG,oBAAoB,GAAG,aAAa,CAAC;AAE9D,MAAM,WAAW,iBAAiB;IAChC,UAAU,EAAE,UAAU,CAAC;IACvB,MAAM,EAAE,SAAS,GAAG,WAAW,GAAG,iBAAiB,CAAC;IACpD,SAAS,EAAE,QAAQ,EAAE,CAAC;CACvB;AA+DD;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,QAAQ,EAAE,CAW7D;AAMD;;;;;GAKG;AACH,wBAAgB,sBAAsB,CACpC,UAAU,EAAE,MAAM,EAClB,OAAO,CAAC,EAAE;IAAE,iBAAiB,CAAC,EAAE,OAAO,CAAA;CAAE,GACxC,UAAU,CAUZ;AAMD;;;;;;;GAOG;AACH,wBAAsB,iBAAiB,CACrC,UAAU,EAAE,MAAM,EAClB,OAAO,CAAC,EAAE;IAAE,iBAAiB,CAAC,EAAE,OAAO,CAAA;CAAE,GACxC,OAAO,CAAC,iBAAiB,CAAC,CAyB5B"}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
// Policy trust verification with risk analysis.
|
|
2
|
+
// Non-interactive — no stdin/stdout prompts.
|
|
3
|
+
// Interactive prompting lives in the CLI trust command.
|
|
4
|
+
import { readFileSync } from 'node:fs';
|
|
5
|
+
import { homedir } from 'node:os';
|
|
6
|
+
import { verifyTrust, isCiTrustOverride } from '@red-codes/core';
|
|
7
|
+
const RISK_PATTERNS = [
|
|
8
|
+
{
|
|
9
|
+
level: 'danger',
|
|
10
|
+
regex: /allow\s*:\s*["']?\*["']?/,
|
|
11
|
+
message: 'Wildcard allow detected — all actions will be permitted',
|
|
12
|
+
patternStr: 'allow: "*"',
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
level: 'danger',
|
|
16
|
+
regex: /(?:secret_exposure|protected_branches|blast_radius|test_before_push|no_force_push)\s*:\s*false/,
|
|
17
|
+
message: 'Disabled security invariant detected',
|
|
18
|
+
patternStr: '<invariant>: false',
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
level: 'danger',
|
|
22
|
+
regex: /enabled\s*:\s*false/,
|
|
23
|
+
message: 'Invariant explicitly disabled via enabled: false',
|
|
24
|
+
patternStr: 'enabled: false',
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
level: 'warning',
|
|
28
|
+
regex: /scope\s*:\s*["']?\*\*["']?/,
|
|
29
|
+
message: 'Broad scope pattern "**" matches all paths',
|
|
30
|
+
patternStr: 'scope: "**"',
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
level: 'warning',
|
|
34
|
+
regex: /files\s*:\s*\[["']?\*\*["']?\]/,
|
|
35
|
+
message: 'Broad files pattern ["**"] matches all files',
|
|
36
|
+
patternStr: 'files: ["**"]',
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
level: 'warning',
|
|
40
|
+
regex: /lockdownThreshold\s*:\s*(\d+)/,
|
|
41
|
+
message: 'High lockdown threshold — escalation to LOCKDOWN will be delayed',
|
|
42
|
+
patternStr: 'lockdownThreshold: >20',
|
|
43
|
+
customCheck: (content) => {
|
|
44
|
+
const match = /lockdownThreshold\s*:\s*(\d+)/.exec(content);
|
|
45
|
+
if (!match)
|
|
46
|
+
return false;
|
|
47
|
+
return parseInt(match[1], 10) > 20;
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
];
|
|
51
|
+
// ---------------------------------------------------------------------------
|
|
52
|
+
// analyzePolicyRisk
|
|
53
|
+
// ---------------------------------------------------------------------------
|
|
54
|
+
/**
|
|
55
|
+
* Pure function. Regex-based pattern matching on policy YAML content to detect
|
|
56
|
+
* risky configurations. Returns an array of risk flags (empty for safe policies).
|
|
57
|
+
*/
|
|
58
|
+
export function analyzePolicyRisk(content) {
|
|
59
|
+
const flags = [];
|
|
60
|
+
for (const rp of RISK_PATTERNS) {
|
|
61
|
+
const matched = rp.customCheck ? rp.customCheck(content) : rp.regex.test(content);
|
|
62
|
+
if (matched) {
|
|
63
|
+
flags.push({ level: rp.level, message: rp.message, pattern: rp.patternStr });
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return flags;
|
|
67
|
+
}
|
|
68
|
+
// ---------------------------------------------------------------------------
|
|
69
|
+
// classifyPolicyLocation
|
|
70
|
+
// ---------------------------------------------------------------------------
|
|
71
|
+
/**
|
|
72
|
+
* Pure function. Determines trust classification based on path location.
|
|
73
|
+
* - Home dir paths → implicitly_trusted
|
|
74
|
+
* - Explicit CLI flag (--policy) → implicitly_trusted
|
|
75
|
+
* - Project-local files → trust_gated
|
|
76
|
+
*/
|
|
77
|
+
export function classifyPolicyLocation(policyPath, options) {
|
|
78
|
+
if (options?.isExplicitCliFlag)
|
|
79
|
+
return 'implicitly_trusted';
|
|
80
|
+
const home = homedir();
|
|
81
|
+
if (policyPath.startsWith('~') || policyPath.startsWith('$HOME') || policyPath.startsWith(home)) {
|
|
82
|
+
return 'implicitly_trusted';
|
|
83
|
+
}
|
|
84
|
+
return 'trust_gated';
|
|
85
|
+
}
|
|
86
|
+
// ---------------------------------------------------------------------------
|
|
87
|
+
// verifyPolicyTrust
|
|
88
|
+
// ---------------------------------------------------------------------------
|
|
89
|
+
/**
|
|
90
|
+
* Non-interactive trust gate. Reads trust store + file hash.
|
|
91
|
+
* No stdin/stdout prompts — interactive prompting lives in the CLI trust command.
|
|
92
|
+
*
|
|
93
|
+
* 1. Classify location → if implicitly_trusted, return early with status: 'trusted'
|
|
94
|
+
* 2. If trust_gated, check trust store via verifyTrust() from @red-codes/core
|
|
95
|
+
* 3. CI override: if isCiTrustOverride() returns true, treat as 'trusted'
|
|
96
|
+
*/
|
|
97
|
+
export async function verifyPolicyTrust(policyPath, options) {
|
|
98
|
+
const trustClass = classifyPolicyLocation(policyPath, options);
|
|
99
|
+
// Read file content for risk analysis
|
|
100
|
+
let content = '';
|
|
101
|
+
try {
|
|
102
|
+
content = readFileSync(policyPath, 'utf8');
|
|
103
|
+
}
|
|
104
|
+
catch {
|
|
105
|
+
// If file can't be read, proceed without risk analysis
|
|
106
|
+
}
|
|
107
|
+
const riskFlags = analyzePolicyRisk(content);
|
|
108
|
+
if (trustClass === 'implicitly_trusted') {
|
|
109
|
+
return { trustClass, status: 'trusted', riskFlags };
|
|
110
|
+
}
|
|
111
|
+
// CI override: treat project-local policy as trusted in CI environments
|
|
112
|
+
if (isCiTrustOverride()) {
|
|
113
|
+
return { trustClass, status: 'trusted', riskFlags };
|
|
114
|
+
}
|
|
115
|
+
// Delegate to trust store for trust-gated locations
|
|
116
|
+
const storeStatus = await verifyTrust(policyPath);
|
|
117
|
+
return { trustClass, status: storeStatus, riskFlags };
|
|
118
|
+
}
|
|
119
|
+
//# sourceMappingURL=policy-trust.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"policy-trust.js","sourceRoot":"","sources":["../src/policy-trust.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { LoadedPolicy, PersonaCondition, ForecastCondition } from './evaluator.js';
|
|
2
|
+
import type { AgentPersona } from '@red-codes/core';
|
|
3
|
+
export interface YamlPersonaDef {
|
|
4
|
+
model?: string;
|
|
5
|
+
provider?: string;
|
|
6
|
+
runtime?: string;
|
|
7
|
+
version?: string;
|
|
8
|
+
trustTier?: string;
|
|
9
|
+
autonomy?: string;
|
|
10
|
+
riskTolerance?: string;
|
|
11
|
+
role?: string;
|
|
12
|
+
tags?: string[];
|
|
13
|
+
}
|
|
14
|
+
export interface YamlPolicyDef {
|
|
15
|
+
id?: string;
|
|
16
|
+
name?: string;
|
|
17
|
+
description?: string;
|
|
18
|
+
severity?: number;
|
|
19
|
+
version?: string;
|
|
20
|
+
agentguardVersion?: string;
|
|
21
|
+
extends?: string[];
|
|
22
|
+
persona?: YamlPersonaDef;
|
|
23
|
+
rules?: YamlRule[];
|
|
24
|
+
/** Kernel invariant IDs to disable (human-operator override) */
|
|
25
|
+
disabledInvariants?: string[];
|
|
26
|
+
/** Top-level enforcement mode: 'monitor' (warn) or 'enforce' (block) */
|
|
27
|
+
mode?: 'monitor' | 'enforce';
|
|
28
|
+
/** Named policy pack to apply (e.g., 'essentials', 'strict') */
|
|
29
|
+
pack?: string;
|
|
30
|
+
/** Per-invariant mode overrides: invariant ID → 'monitor' | 'enforce' */
|
|
31
|
+
invariantModes?: Record<string, 'monitor' | 'enforce'>;
|
|
32
|
+
}
|
|
33
|
+
interface YamlRule {
|
|
34
|
+
action?: string | string[];
|
|
35
|
+
effect?: string;
|
|
36
|
+
target?: string;
|
|
37
|
+
branches?: string[];
|
|
38
|
+
reason?: string;
|
|
39
|
+
limit?: number;
|
|
40
|
+
requireTests?: boolean;
|
|
41
|
+
requireFormat?: boolean;
|
|
42
|
+
requireWorktree?: boolean;
|
|
43
|
+
persona?: PersonaCondition;
|
|
44
|
+
intervention?: string;
|
|
45
|
+
forecast?: ForecastCondition;
|
|
46
|
+
}
|
|
47
|
+
export declare function parseYamlPolicy(yaml: string): YamlPolicyDef;
|
|
48
|
+
/** Convert a YamlPersonaDef to an AgentPersona (for policy defaults). */
|
|
49
|
+
export declare function yamlPersonaToAgentPersona(def: YamlPersonaDef): AgentPersona;
|
|
50
|
+
export declare function loadYamlPolicy(yaml: string, defaultId?: string): LoadedPolicy;
|
|
51
|
+
export declare function loadYamlPolicies(yaml: string): LoadedPolicy[];
|
|
52
|
+
export {};
|
|
53
|
+
//# sourceMappingURL=yaml-loader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"yaml-loader.d.ts","sourceRoot":"","sources":["../src/yaml-loader.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAc,YAAY,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACpG,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAEpD,MAAM,WAAW,cAAc;IAC7B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;CACjB;AAED,MAAM,WAAW,aAAa;IAC5B,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,OAAO,CAAC,EAAE,cAAc,CAAC;IACzB,KAAK,CAAC,EAAE,QAAQ,EAAE,CAAC;IACnB,gEAAgE;IAChE,kBAAkB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC9B,wEAAwE;IACxE,IAAI,CAAC,EAAE,SAAS,GAAG,SAAS,CAAC;IAC7B,gEAAgE;IAChE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,yEAAyE;IACzE,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,GAAG,SAAS,CAAC,CAAC;CACxD;AAED,UAAU,QAAQ;IAChB,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAC3B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,OAAO,CAAC,EAAE,gBAAgB,CAAC;IAC3B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,iBAAiB,CAAC;CAC9B;AAiJD,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,aAAa,CA6Q3D;AAgHD,yEAAyE;AACzE,wBAAgB,yBAAyB,CAAC,GAAG,EAAE,cAAc,GAAG,YAAY,CAiB3E;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,YAAY,CA0C7E;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,EAAE,CAE7D"}
|