@atlasauth/pca-policy-ci 0.0.0-stage → 0.1.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/dist/cli.d.ts +27 -0
- package/dist/cli.js +166 -0
- package/dist/cli.js.map +1 -0
- package/dist/deps.d.ts +9 -0
- package/dist/deps.js +23 -0
- package/dist/deps.js.map +1 -0
- package/dist/index.d.ts +51 -0
- package/dist/index.js +165 -0
- package/dist/index.js.map +1 -0
- package/dist/normalize.d.ts +55 -0
- package/dist/normalize.js +130 -0
- package/dist/normalize.js.map +1 -0
- package/dist/rules.d.ts +17 -0
- package/dist/rules.js +276 -0
- package/dist/rules.js.map +1 -0
- package/dist/types.d.ts +80 -0
- package/dist/types.js +32 -0
- package/dist/types.js.map +1 -0
- package/dist/util.d.ts +25 -0
- package/dist/util.js +73 -0
- package/dist/util.js.map +1 -0
- package/package.json +34 -3
- package/README.md +0 -3
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* pca-policy-ci — CLI for the PCA policy-safety gate.
|
|
4
|
+
*
|
|
5
|
+
* Usage:
|
|
6
|
+
* pca-policy-ci <files...> [--max-severity error|warn|info] [--format text|github] [--quiet]
|
|
7
|
+
*
|
|
8
|
+
* Each file is a JSON policy document: an `Envelope`, a minted grant (`Capability`), a compiled
|
|
9
|
+
* policy, a bare `{ predicates, caveats }`, or a JSON ARRAY (treated as a delegation chain). Findings
|
|
10
|
+
* are printed and the process exits non-zero when any finding is at or above `--max-severity`
|
|
11
|
+
* (default `error`). `--format github` emits GitHub-Actions workflow annotations.
|
|
12
|
+
*/
|
|
13
|
+
import { type Severity } from './index';
|
|
14
|
+
interface CliArgs {
|
|
15
|
+
files: string[];
|
|
16
|
+
maxSeverity: Severity;
|
|
17
|
+
format: 'text' | 'github';
|
|
18
|
+
quiet: boolean;
|
|
19
|
+
help: boolean;
|
|
20
|
+
}
|
|
21
|
+
export declare function parseArgs(argv: readonly string[]): {
|
|
22
|
+
args: CliArgs;
|
|
23
|
+
} | {
|
|
24
|
+
error: string;
|
|
25
|
+
};
|
|
26
|
+
export declare function run(argv: readonly string[], write?: (s: string) => void): number;
|
|
27
|
+
export {};
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
/**
|
|
4
|
+
* pca-policy-ci — CLI for the PCA policy-safety gate.
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* pca-policy-ci <files...> [--max-severity error|warn|info] [--format text|github] [--quiet]
|
|
8
|
+
*
|
|
9
|
+
* Each file is a JSON policy document: an `Envelope`, a minted grant (`Capability`), a compiled
|
|
10
|
+
* policy, a bare `{ predicates, caveats }`, or a JSON ARRAY (treated as a delegation chain). Findings
|
|
11
|
+
* are printed and the process exits non-zero when any finding is at or above `--max-severity`
|
|
12
|
+
* (default `error`). `--format github` emits GitHub-Actions workflow annotations.
|
|
13
|
+
*/
|
|
14
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
15
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
16
|
+
};
|
|
17
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
|
+
exports.parseArgs = parseArgs;
|
|
19
|
+
exports.run = run;
|
|
20
|
+
const node_fs_1 = require("node:fs");
|
|
21
|
+
const node_process_1 = __importDefault(require("node:process"));
|
|
22
|
+
const index_1 = require("./index");
|
|
23
|
+
const SEVERITIES = ['error', 'warn', 'info'];
|
|
24
|
+
function isSeverity(x) {
|
|
25
|
+
return x === 'error' || x === 'warn' || x === 'info';
|
|
26
|
+
}
|
|
27
|
+
function parseArgs(argv) {
|
|
28
|
+
const files = [];
|
|
29
|
+
let maxSeverity = 'error';
|
|
30
|
+
let format = 'text';
|
|
31
|
+
let quiet = false;
|
|
32
|
+
let help = false;
|
|
33
|
+
for (let i = 0; i < argv.length; i++) {
|
|
34
|
+
const a = argv[i];
|
|
35
|
+
if (a === undefined)
|
|
36
|
+
continue;
|
|
37
|
+
if (a === '--help' || a === '-h') {
|
|
38
|
+
help = true;
|
|
39
|
+
}
|
|
40
|
+
else if (a === '--quiet' || a === '-q') {
|
|
41
|
+
quiet = true;
|
|
42
|
+
}
|
|
43
|
+
else if (a === '--max-severity') {
|
|
44
|
+
const v = argv[++i];
|
|
45
|
+
if (v === undefined || !isSeverity(v))
|
|
46
|
+
return { error: `--max-severity expects one of ${SEVERITIES.join('|')}` };
|
|
47
|
+
maxSeverity = v;
|
|
48
|
+
}
|
|
49
|
+
else if (a.startsWith('--max-severity=')) {
|
|
50
|
+
const v = a.slice('--max-severity='.length);
|
|
51
|
+
if (!isSeverity(v))
|
|
52
|
+
return { error: `--max-severity expects one of ${SEVERITIES.join('|')}` };
|
|
53
|
+
maxSeverity = v;
|
|
54
|
+
}
|
|
55
|
+
else if (a === '--format') {
|
|
56
|
+
const v = argv[++i];
|
|
57
|
+
if (v !== 'text' && v !== 'github')
|
|
58
|
+
return { error: `--format expects 'text' or 'github'` };
|
|
59
|
+
format = v;
|
|
60
|
+
}
|
|
61
|
+
else if (a.startsWith('--format=')) {
|
|
62
|
+
const v = a.slice('--format='.length);
|
|
63
|
+
if (v !== 'text' && v !== 'github')
|
|
64
|
+
return { error: `--format expects 'text' or 'github'` };
|
|
65
|
+
format = v;
|
|
66
|
+
}
|
|
67
|
+
else if (a.startsWith('-')) {
|
|
68
|
+
return { error: `unknown option '${a}'` };
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
files.push(a);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return { args: { files, maxSeverity, format, quiet, help } };
|
|
75
|
+
}
|
|
76
|
+
const HELP = `pca-policy-ci — statically catch unsafe PCA policies before they ship.
|
|
77
|
+
|
|
78
|
+
Usage:
|
|
79
|
+
pca-policy-ci <files...> [options]
|
|
80
|
+
|
|
81
|
+
Options:
|
|
82
|
+
--max-severity <error|warn|info> Lowest severity that fails the gate (default: error)
|
|
83
|
+
--format <text|github> Output format (default: text; 'github' = workflow annotations)
|
|
84
|
+
--quiet, -q Only print findings and the summary line
|
|
85
|
+
--help, -h Show this help
|
|
86
|
+
|
|
87
|
+
Each file is a JSON policy (Envelope / grant / compiled / { predicates, caveats }) or a JSON array
|
|
88
|
+
treated as a delegation chain. Exits non-zero when any finding is at or above --max-severity.`;
|
|
89
|
+
function lintFile(file) {
|
|
90
|
+
let text;
|
|
91
|
+
try {
|
|
92
|
+
text = (0, node_fs_1.readFileSync)(file, 'utf8');
|
|
93
|
+
}
|
|
94
|
+
catch (e) {
|
|
95
|
+
return [{ rule: 'malformed-policy', severity: 'error', message: `cannot read file: ${e instanceof Error ? e.message : String(e)}` }];
|
|
96
|
+
}
|
|
97
|
+
let doc;
|
|
98
|
+
try {
|
|
99
|
+
doc = JSON.parse(text);
|
|
100
|
+
}
|
|
101
|
+
catch (e) {
|
|
102
|
+
return [{ rule: 'malformed-policy', severity: 'error', message: `invalid JSON: ${e instanceof Error ? e.message : String(e)}` }];
|
|
103
|
+
}
|
|
104
|
+
return (0, index_1.lint)(doc);
|
|
105
|
+
}
|
|
106
|
+
const GH_LEVEL = { error: 'error', warn: 'warning', info: 'notice' };
|
|
107
|
+
const TEXT_TAG = { error: 'ERROR', warn: 'WARN', info: 'INFO' };
|
|
108
|
+
function printText(results, quiet, write) {
|
|
109
|
+
for (const r of results) {
|
|
110
|
+
if (!quiet)
|
|
111
|
+
write(`\n${r.label}:`);
|
|
112
|
+
if (r.findings.length === 0) {
|
|
113
|
+
if (!quiet)
|
|
114
|
+
write(' (clean)');
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
for (const f of r.findings) {
|
|
118
|
+
const loc = f.where !== undefined ? ` [${f.where}]` : '';
|
|
119
|
+
write(` ${TEXT_TAG[f.severity].padEnd(5)} ${f.rule}${loc}: ${f.message}`);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
function escapeGh(s) {
|
|
124
|
+
return s.replace(/%/g, '%25').replace(/\r/g, '%0D').replace(/\n/g, '%0A');
|
|
125
|
+
}
|
|
126
|
+
function printGithub(results, write) {
|
|
127
|
+
for (const r of results) {
|
|
128
|
+
for (const f of r.findings) {
|
|
129
|
+
const props = [`file=${escapeGh(r.label)}`, `title=${escapeGh(f.rule)}`].join(',');
|
|
130
|
+
const loc = f.where !== undefined ? `${f.where}: ` : '';
|
|
131
|
+
write(`::${GH_LEVEL[f.severity]} ${props}::${escapeGh(`${loc}${f.message}`)}`);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
function run(argv, write = (s) => node_process_1.default.stdout.write(s + '\n')) {
|
|
136
|
+
const parsed = parseArgs(argv);
|
|
137
|
+
if ('error' in parsed) {
|
|
138
|
+
write(parsed.error);
|
|
139
|
+
write(HELP);
|
|
140
|
+
return 2;
|
|
141
|
+
}
|
|
142
|
+
const { args } = parsed;
|
|
143
|
+
if (args.help) {
|
|
144
|
+
write(HELP);
|
|
145
|
+
return 0;
|
|
146
|
+
}
|
|
147
|
+
if (args.files.length === 0) {
|
|
148
|
+
write('error: no policy files given');
|
|
149
|
+
write(HELP);
|
|
150
|
+
return 2;
|
|
151
|
+
}
|
|
152
|
+
const results = args.files.map((file) => ({ label: file, findings: lintFile(file) }));
|
|
153
|
+
const summary = (0, index_1.summarize)(results, { maxSeverity: args.maxSeverity });
|
|
154
|
+
if (args.format === 'github')
|
|
155
|
+
printGithub(results, write);
|
|
156
|
+
else
|
|
157
|
+
printText(results, args.quiet, write);
|
|
158
|
+
write(`\n${summary.ok ? 'PASS' : 'FAIL'} — ${summary.counts.error} error(s), ${summary.counts.warn} warning(s), ${summary.counts.info} info across ${results.length} policy/policies (fail threshold: ${summary.maxSeverity})`);
|
|
159
|
+
return summary.exitCode;
|
|
160
|
+
}
|
|
161
|
+
// Entry point (skipped when imported by tests). Guarded so it is inert under a test runner that may
|
|
162
|
+
// not provide the CommonJS `require`/`module` globals.
|
|
163
|
+
if (typeof require !== 'undefined' && typeof module !== 'undefined' && require.main === module) {
|
|
164
|
+
node_process_1.default.exit(run(node_process_1.default.argv.slice(2)));
|
|
165
|
+
}
|
|
166
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
package/dist/deps.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Centralized re-exports of the two upstream packages this gate is built on. The CI gate does NOT
|
|
3
|
+
* re-implement any static analysis — it drives the real `@atlasauth/pca-analyzer` decision procedure
|
|
4
|
+
* and reads the real `@atlasauth/pca` policy shapes.
|
|
5
|
+
*/
|
|
6
|
+
export { alwaysAllows, alwaysDenies, disjoint, equivalent, reachable, subsumes, predicateNumericSatisfiable, tightenInterval, intervalEmpty, FULL_INTERVAL, } from '@atlasauth/pca-analyzer';
|
|
7
|
+
export type { AnalyzerAction, AnalyzerOptions, PolicyInput, NumInterval, ReachableResult, AlwaysAllowsResult, AlwaysDeniesResult, SubsumesResult, } from '@atlasauth/pca-analyzer';
|
|
8
|
+
export { ENVELOPE_CAVEAT, isLeafCondition } from '@atlasauth/pca';
|
|
9
|
+
export type { Capability, Caveat, Condition, Envelope, LeafCondition, Predicate, RiskPolicy } from '@atlasauth/pca';
|
package/dist/deps.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Centralized re-exports of the two upstream packages this gate is built on. The CI gate does NOT
|
|
4
|
+
* re-implement any static analysis — it drives the real `@atlasauth/pca-analyzer` decision procedure
|
|
5
|
+
* and reads the real `@atlasauth/pca` policy shapes.
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.isLeafCondition = exports.ENVELOPE_CAVEAT = exports.FULL_INTERVAL = exports.intervalEmpty = exports.tightenInterval = exports.predicateNumericSatisfiable = exports.subsumes = exports.reachable = exports.equivalent = exports.disjoint = exports.alwaysDenies = exports.alwaysAllows = void 0;
|
|
9
|
+
var pca_analyzer_1 = require("@atlasauth/pca-analyzer");
|
|
10
|
+
Object.defineProperty(exports, "alwaysAllows", { enumerable: true, get: function () { return pca_analyzer_1.alwaysAllows; } });
|
|
11
|
+
Object.defineProperty(exports, "alwaysDenies", { enumerable: true, get: function () { return pca_analyzer_1.alwaysDenies; } });
|
|
12
|
+
Object.defineProperty(exports, "disjoint", { enumerable: true, get: function () { return pca_analyzer_1.disjoint; } });
|
|
13
|
+
Object.defineProperty(exports, "equivalent", { enumerable: true, get: function () { return pca_analyzer_1.equivalent; } });
|
|
14
|
+
Object.defineProperty(exports, "reachable", { enumerable: true, get: function () { return pca_analyzer_1.reachable; } });
|
|
15
|
+
Object.defineProperty(exports, "subsumes", { enumerable: true, get: function () { return pca_analyzer_1.subsumes; } });
|
|
16
|
+
Object.defineProperty(exports, "predicateNumericSatisfiable", { enumerable: true, get: function () { return pca_analyzer_1.predicateNumericSatisfiable; } });
|
|
17
|
+
Object.defineProperty(exports, "tightenInterval", { enumerable: true, get: function () { return pca_analyzer_1.tightenInterval; } });
|
|
18
|
+
Object.defineProperty(exports, "intervalEmpty", { enumerable: true, get: function () { return pca_analyzer_1.intervalEmpty; } });
|
|
19
|
+
Object.defineProperty(exports, "FULL_INTERVAL", { enumerable: true, get: function () { return pca_analyzer_1.FULL_INTERVAL; } });
|
|
20
|
+
var pca_1 = require("@atlasauth/pca");
|
|
21
|
+
Object.defineProperty(exports, "ENVELOPE_CAVEAT", { enumerable: true, get: function () { return pca_1.ENVELOPE_CAVEAT; } });
|
|
22
|
+
Object.defineProperty(exports, "isLeafCondition", { enumerable: true, get: function () { return pca_1.isLeafCondition; } });
|
|
23
|
+
//# sourceMappingURL=deps.js.map
|
package/dist/deps.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deps.js","sourceRoot":"","sources":["../src/deps.ts"],"names":[],"mappings":";AAAA;;;;GAIG;;;AAEH,wDAWiC;AAV/B,4GAAA,YAAY,OAAA;AACZ,4GAAA,YAAY,OAAA;AACZ,wGAAA,QAAQ,OAAA;AACR,0GAAA,UAAU,OAAA;AACV,yGAAA,SAAS,OAAA;AACT,wGAAA,QAAQ,OAAA;AACR,2HAAA,2BAA2B,OAAA;AAC3B,+GAAA,eAAe,OAAA;AACf,6GAAA,aAAa,OAAA;AACb,6GAAA,aAAa,OAAA;AAcf,sCAAkE;AAAzD,sGAAA,eAAe,OAAA;AAAE,sGAAA,eAAe,OAAA"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @atlasauth/pca-policy-ci — a CI gate that STATICALLY catches unsafe PCA policies before they ship.
|
|
3
|
+
*
|
|
4
|
+
* This is the "shift-left policy safety" boundary. It drives the real `@atlasauth/pca-analyzer`
|
|
5
|
+
* decision procedure (reachability, totality/always-allow, delegation-safety subsumption, numeric
|
|
6
|
+
* satisfiability) over a PCA `Envelope` / `grant` / chain and reports a list of {@link Finding}s. Five
|
|
7
|
+
* rules, each toggleable:
|
|
8
|
+
*
|
|
9
|
+
* - **over-broad-grant** — a wildcard verb/resource, a semantically-proven always-allow, or a
|
|
10
|
+
* dangerous verb granted with no narrowing `where` or scope-narrowing caveat.
|
|
11
|
+
* - **privilege-escalation** — a delegation hop whose effective authority is NOT a subset of its
|
|
12
|
+
* parent (a hard error if the analyzer proves it; attenuation should make this impossible).
|
|
13
|
+
* - **redundant-caveat** — a caveat (or `where` condition) that can never bite: a duplicate, one
|
|
14
|
+
* subsumed by a stricter one of the same type, an unsatisfiable predicate, a non-binding bound.
|
|
15
|
+
* - **dangerous-reachability** — a dangerous (verb, resource) the analyzer PROVES reachable with no
|
|
16
|
+
* budget / rate / step-up gate.
|
|
17
|
+
* - **missing-safety-floor** — no expiry, or no blast/rate/budget gate on a non-trivial grant.
|
|
18
|
+
*
|
|
19
|
+
* SOUNDNESS: a hard `error` is only ever emitted from a PROVEN analyzer verdict. Any undecidable
|
|
20
|
+
* feature (a `re:` regex resource, a cross-field `ref`, an over-large grid) fails safe to a
|
|
21
|
+
* `warn`/`info` that says it is undecidable — the gate never fails a build on a false positive, and
|
|
22
|
+
* never silently passes a provably-unsafe policy.
|
|
23
|
+
*/
|
|
24
|
+
import type { AnalyzerOptions } from './deps';
|
|
25
|
+
import { type Finding, type LabeledFindings, type LintRules, type LintSummary, type SummaryOptions } from './types';
|
|
26
|
+
export * from './types';
|
|
27
|
+
export { normalizePolicyInput, normalizeChain, isCapability, isEnvelope, type NormalizedPolicy } from './normalize';
|
|
28
|
+
export interface LintOptions extends AnalyzerOptions {
|
|
29
|
+
rules?: LintRules;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Lint a single PCA policy: an `Envelope`, a minted `grant` (`Capability`), a `compilePolicy` result,
|
|
33
|
+
* or a bare `{ predicates, caveats? }`. Returns all findings (empty = clean).
|
|
34
|
+
*/
|
|
35
|
+
export declare function lintPolicy(input: unknown, opts?: LintOptions): Finding[];
|
|
36
|
+
/**
|
|
37
|
+
* Lint a delegation chain (root -> leaf): a `Capability[]` (a real minted chain) or an array of
|
|
38
|
+
* already-effective `{ predicates, caveats }` hops. Runs the privilege-escalation check across every
|
|
39
|
+
* consecutive pair, then runs the single-policy rules on the acting LEAF's effective authority.
|
|
40
|
+
*/
|
|
41
|
+
export declare function lintChain(chain: readonly unknown[], opts?: LintOptions): Finding[];
|
|
42
|
+
/** Dispatch: an array input is a delegation chain ({@link lintChain}); anything else is a single policy. */
|
|
43
|
+
export declare function lint(input: unknown, opts?: LintOptions): Finding[];
|
|
44
|
+
export interface LintItem {
|
|
45
|
+
label?: string;
|
|
46
|
+
policy: unknown;
|
|
47
|
+
}
|
|
48
|
+
/** Lint a list of labeled policies (or chains) and roll the findings into a {@link LintSummary}. */
|
|
49
|
+
export declare function lintPolicies(items: readonly LintItem[], opts?: LintOptions & SummaryOptions): LintSummary;
|
|
50
|
+
/** Roll a set of labeled findings into counts + an exit-code mapping (`error` -> non-zero). */
|
|
51
|
+
export declare function summarize(results: LabeledFindings[], opts?: SummaryOptions): LintSummary;
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @atlasauth/pca-policy-ci — a CI gate that STATICALLY catches unsafe PCA policies before they ship.
|
|
4
|
+
*
|
|
5
|
+
* This is the "shift-left policy safety" boundary. It drives the real `@atlasauth/pca-analyzer`
|
|
6
|
+
* decision procedure (reachability, totality/always-allow, delegation-safety subsumption, numeric
|
|
7
|
+
* satisfiability) over a PCA `Envelope` / `grant` / chain and reports a list of {@link Finding}s. Five
|
|
8
|
+
* rules, each toggleable:
|
|
9
|
+
*
|
|
10
|
+
* - **over-broad-grant** — a wildcard verb/resource, a semantically-proven always-allow, or a
|
|
11
|
+
* dangerous verb granted with no narrowing `where` or scope-narrowing caveat.
|
|
12
|
+
* - **privilege-escalation** — a delegation hop whose effective authority is NOT a subset of its
|
|
13
|
+
* parent (a hard error if the analyzer proves it; attenuation should make this impossible).
|
|
14
|
+
* - **redundant-caveat** — a caveat (or `where` condition) that can never bite: a duplicate, one
|
|
15
|
+
* subsumed by a stricter one of the same type, an unsatisfiable predicate, a non-binding bound.
|
|
16
|
+
* - **dangerous-reachability** — a dangerous (verb, resource) the analyzer PROVES reachable with no
|
|
17
|
+
* budget / rate / step-up gate.
|
|
18
|
+
* - **missing-safety-floor** — no expiry, or no blast/rate/budget gate on a non-trivial grant.
|
|
19
|
+
*
|
|
20
|
+
* SOUNDNESS: a hard `error` is only ever emitted from a PROVEN analyzer verdict. Any undecidable
|
|
21
|
+
* feature (a `re:` regex resource, a cross-field `ref`, an over-large grid) fails safe to a
|
|
22
|
+
* `warn`/`info` that says it is undecidable — the gate never fails a build on a false positive, and
|
|
23
|
+
* never silently passes a provably-unsafe policy.
|
|
24
|
+
*/
|
|
25
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
26
|
+
if (k2 === undefined) k2 = k;
|
|
27
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
28
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
29
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
30
|
+
}
|
|
31
|
+
Object.defineProperty(o, k2, desc);
|
|
32
|
+
}) : (function(o, m, k, k2) {
|
|
33
|
+
if (k2 === undefined) k2 = k;
|
|
34
|
+
o[k2] = m[k];
|
|
35
|
+
}));
|
|
36
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
37
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
38
|
+
};
|
|
39
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
40
|
+
exports.isEnvelope = exports.isCapability = exports.normalizeChain = exports.normalizePolicyInput = void 0;
|
|
41
|
+
exports.lintPolicy = lintPolicy;
|
|
42
|
+
exports.lintChain = lintChain;
|
|
43
|
+
exports.lint = lint;
|
|
44
|
+
exports.lintPolicies = lintPolicies;
|
|
45
|
+
exports.summarize = summarize;
|
|
46
|
+
const normalize_1 = require("./normalize");
|
|
47
|
+
const rules_1 = require("./rules");
|
|
48
|
+
const types_1 = require("./types");
|
|
49
|
+
__exportStar(require("./types"), exports);
|
|
50
|
+
var normalize_2 = require("./normalize");
|
|
51
|
+
Object.defineProperty(exports, "normalizePolicyInput", { enumerable: true, get: function () { return normalize_2.normalizePolicyInput; } });
|
|
52
|
+
Object.defineProperty(exports, "normalizeChain", { enumerable: true, get: function () { return normalize_2.normalizeChain; } });
|
|
53
|
+
Object.defineProperty(exports, "isCapability", { enumerable: true, get: function () { return normalize_2.isCapability; } });
|
|
54
|
+
Object.defineProperty(exports, "isEnvelope", { enumerable: true, get: function () { return normalize_2.isEnvelope; } });
|
|
55
|
+
function resolveRules(rules) {
|
|
56
|
+
const r = rules ?? {};
|
|
57
|
+
const ob = r.overBroadGrant;
|
|
58
|
+
const dr = r.dangerousReachability;
|
|
59
|
+
const sf = r.missingSafetyFloor;
|
|
60
|
+
return {
|
|
61
|
+
overBroadGrant: {
|
|
62
|
+
enabled: ob !== false,
|
|
63
|
+
dangerousVerbs: typeof ob === 'object' && ob.dangerousVerbs ? ob.dangerousVerbs : types_1.DEFAULT_DANGEROUS_VERBS,
|
|
64
|
+
},
|
|
65
|
+
privilegeEscalation: r.privilegeEscalation !== false,
|
|
66
|
+
redundantCaveat: r.redundantCaveat !== false,
|
|
67
|
+
dangerousReachability: {
|
|
68
|
+
enabled: dr !== false,
|
|
69
|
+
dangerousVerbs: typeof dr === 'object' && dr.dangerousVerbs ? dr.dangerousVerbs : types_1.DEFAULT_DANGEROUS_VERBS,
|
|
70
|
+
targets: typeof dr === 'object' && dr.targets ? dr.targets : [],
|
|
71
|
+
},
|
|
72
|
+
missingSafetyFloor: {
|
|
73
|
+
enabled: sf !== false,
|
|
74
|
+
requireExpiry: typeof sf === 'object' ? sf.requireExpiry !== false : true,
|
|
75
|
+
requireScopeGate: typeof sf === 'object' ? sf.requireScopeGate !== false : true,
|
|
76
|
+
},
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
function analyzerOpts(opts) {
|
|
80
|
+
return opts?.maxCombinations !== undefined ? { maxCombinations: opts.maxCombinations } : {};
|
|
81
|
+
}
|
|
82
|
+
// ---- lintPolicy / lintChain / lint ----------------------------------------------------------------
|
|
83
|
+
/**
|
|
84
|
+
* Lint a single PCA policy: an `Envelope`, a minted `grant` (`Capability`), a `compilePolicy` result,
|
|
85
|
+
* or a bare `{ predicates, caveats? }`. Returns all findings (empty = clean).
|
|
86
|
+
*/
|
|
87
|
+
function lintPolicy(input, opts = {}) {
|
|
88
|
+
const norm = (0, normalize_1.normalizePolicyInput)(input);
|
|
89
|
+
if (!norm.ok)
|
|
90
|
+
return [{ rule: 'malformed-policy', severity: 'error', message: norm.reason }];
|
|
91
|
+
const cfg = resolveRules(opts.rules);
|
|
92
|
+
const ao = analyzerOpts(opts);
|
|
93
|
+
const policy = norm.policy;
|
|
94
|
+
const out = [];
|
|
95
|
+
if (cfg.overBroadGrant.enabled)
|
|
96
|
+
out.push(...(0, rules_1.ruleOverBroadGrant)(policy, cfg.overBroadGrant.dangerousVerbs, ao));
|
|
97
|
+
if (cfg.redundantCaveat)
|
|
98
|
+
out.push(...(0, rules_1.ruleRedundantCaveat)(policy));
|
|
99
|
+
if (cfg.dangerousReachability.enabled) {
|
|
100
|
+
out.push(...(0, rules_1.ruleDangerousReachability)(policy, cfg.dangerousReachability.dangerousVerbs, cfg.dangerousReachability.targets, ao));
|
|
101
|
+
}
|
|
102
|
+
if (cfg.missingSafetyFloor.enabled) {
|
|
103
|
+
out.push(...(0, rules_1.ruleMissingSafetyFloor)(policy, cfg.missingSafetyFloor, cfg.overBroadGrant.dangerousVerbs));
|
|
104
|
+
}
|
|
105
|
+
return out;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Lint a delegation chain (root -> leaf): a `Capability[]` (a real minted chain) or an array of
|
|
109
|
+
* already-effective `{ predicates, caveats }` hops. Runs the privilege-escalation check across every
|
|
110
|
+
* consecutive pair, then runs the single-policy rules on the acting LEAF's effective authority.
|
|
111
|
+
*/
|
|
112
|
+
function lintChain(chain, opts = {}) {
|
|
113
|
+
const norm = (0, normalize_1.normalizeChain)(chain);
|
|
114
|
+
if (!norm.ok)
|
|
115
|
+
return [{ rule: 'malformed-policy', severity: 'error', message: norm.reason }];
|
|
116
|
+
const cfg = resolveRules(opts.rules);
|
|
117
|
+
const ao = analyzerOpts(opts);
|
|
118
|
+
const hops = norm.hops;
|
|
119
|
+
const out = [];
|
|
120
|
+
if (cfg.privilegeEscalation)
|
|
121
|
+
out.push(...(0, rules_1.rulePrivilegeEscalation)(hops, ao));
|
|
122
|
+
const leaf = hops[hops.length - 1];
|
|
123
|
+
if (leaf !== undefined) {
|
|
124
|
+
if (cfg.overBroadGrant.enabled)
|
|
125
|
+
out.push(...(0, rules_1.ruleOverBroadGrant)(leaf, cfg.overBroadGrant.dangerousVerbs, ao));
|
|
126
|
+
if (cfg.redundantCaveat)
|
|
127
|
+
out.push(...(0, rules_1.ruleRedundantCaveat)(leaf));
|
|
128
|
+
if (cfg.dangerousReachability.enabled) {
|
|
129
|
+
out.push(...(0, rules_1.ruleDangerousReachability)(leaf, cfg.dangerousReachability.dangerousVerbs, cfg.dangerousReachability.targets, ao));
|
|
130
|
+
}
|
|
131
|
+
if (cfg.missingSafetyFloor.enabled) {
|
|
132
|
+
out.push(...(0, rules_1.ruleMissingSafetyFloor)(leaf, cfg.missingSafetyFloor, cfg.overBroadGrant.dangerousVerbs));
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return out;
|
|
136
|
+
}
|
|
137
|
+
/** Dispatch: an array input is a delegation chain ({@link lintChain}); anything else is a single policy. */
|
|
138
|
+
function lint(input, opts = {}) {
|
|
139
|
+
return Array.isArray(input) ? lintChain(input, opts) : lintPolicy(input, opts);
|
|
140
|
+
}
|
|
141
|
+
/** Lint a list of labeled policies (or chains) and roll the findings into a {@link LintSummary}. */
|
|
142
|
+
function lintPolicies(items, opts = {}) {
|
|
143
|
+
const results = items.map((item, i) => ({
|
|
144
|
+
label: item.label ?? `policy[${i}]`,
|
|
145
|
+
findings: lint(item.policy, opts),
|
|
146
|
+
}));
|
|
147
|
+
return summarize(results, opts);
|
|
148
|
+
}
|
|
149
|
+
/** Roll a set of labeled findings into counts + an exit-code mapping (`error` -> non-zero). */
|
|
150
|
+
function summarize(results, opts = {}) {
|
|
151
|
+
const maxSeverity = opts.maxSeverity ?? 'error';
|
|
152
|
+
const counts = { error: 0, warn: 0, info: 0 };
|
|
153
|
+
let failing = 0;
|
|
154
|
+
const threshold = types_1.SEVERITY_RANK[maxSeverity];
|
|
155
|
+
for (const r of results) {
|
|
156
|
+
for (const f of r.findings) {
|
|
157
|
+
counts[f.severity] += 1;
|
|
158
|
+
if (types_1.SEVERITY_RANK[f.severity] >= threshold)
|
|
159
|
+
failing += 1;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
const ok = failing === 0;
|
|
163
|
+
return { results, counts, maxSeverity, ok, exitCode: ok ? 0 : 1 };
|
|
164
|
+
}
|
|
165
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Input detection + normalization.
|
|
3
|
+
*
|
|
4
|
+
* `lintPolicy` accepts anything a principal might hand a CI gate: a bare predicate/caveat set, a
|
|
5
|
+
* compiled policy, a signed `Envelope`, or a minted `grant` (`Capability`). They all reduce to the
|
|
6
|
+
* same effective authority — a predicate set plus the runtime caveat (gate) set, and (when known) the
|
|
7
|
+
* risk policy. The grant/chain reduction is the subtle part and is documented on {@link fromGrant}.
|
|
8
|
+
*/
|
|
9
|
+
import { type Capability, type Caveat, type Envelope, type Predicate, type RiskPolicy } from '@atlasauth/pca';
|
|
10
|
+
export type PolicyKind = 'bare' | 'compiled' | 'envelope' | 'grant' | 'chain-hop';
|
|
11
|
+
/** The effective authority of one policy (or one chain hop), ready for the static analyzer. */
|
|
12
|
+
export interface NormalizedPolicy {
|
|
13
|
+
predicates: Predicate[];
|
|
14
|
+
/** Runtime gate caveats (expires / rate / max_blast_radius / reversibility_max / ...). */
|
|
15
|
+
caveats: Caveat[];
|
|
16
|
+
/** The risk policy, when the input carried one (envelope / grant / compiled). */
|
|
17
|
+
riskPolicy?: RiskPolicy;
|
|
18
|
+
kind: PolicyKind;
|
|
19
|
+
}
|
|
20
|
+
export type NormalizeResult = {
|
|
21
|
+
ok: true;
|
|
22
|
+
policy: NormalizedPolicy;
|
|
23
|
+
} | {
|
|
24
|
+
ok: false;
|
|
25
|
+
reason: string;
|
|
26
|
+
};
|
|
27
|
+
/** A minted grant / delegation hop: has the content-addressed, signed shape of a `Capability`. */
|
|
28
|
+
export declare function isCapability(x: unknown): x is Capability;
|
|
29
|
+
/** A signed policy envelope (the Root Intent Grant payload). */
|
|
30
|
+
export declare function isEnvelope(x: unknown): x is Envelope;
|
|
31
|
+
/**
|
|
32
|
+
* Reduce a grant to its effective authority. The grant's predicates and baseline gate caveats live
|
|
33
|
+
* INSIDE its first `envelope` caveat (signed into the grant). Delegation then APPENDS further gate
|
|
34
|
+
* caveats to the grant's top-level `caveats` array. So the effective gates are the envelope's own
|
|
35
|
+
* caveats PLUS the appended (non-envelope) top-level caveats, and the predicates come from the
|
|
36
|
+
* envelope. A malformed / unreadable envelope is a hard failure the caller surfaces as a finding.
|
|
37
|
+
*/
|
|
38
|
+
export declare function fromGrant(grant: Capability): NormalizeResult;
|
|
39
|
+
/** Normalize any single-policy input to its effective authority. */
|
|
40
|
+
export declare function normalizePolicyInput(input: unknown): NormalizeResult;
|
|
41
|
+
export type ChainNormalizeResult = {
|
|
42
|
+
ok: true;
|
|
43
|
+
hops: NormalizedPolicy[];
|
|
44
|
+
} | {
|
|
45
|
+
ok: false;
|
|
46
|
+
reason: string;
|
|
47
|
+
};
|
|
48
|
+
/**
|
|
49
|
+
* Normalize a delegation chain to one {@link NormalizedPolicy} per hop (root -> leaf). A chain of
|
|
50
|
+
* `Capability`s uses the grant reduction: the predicates are shared (from the root envelope) and each
|
|
51
|
+
* hop's effective gate caveats are the envelope's caveats plus that hop's appended caveats, so a
|
|
52
|
+
* well-formed (append-only) chain yields a monotonically narrowing authority. A chain of already
|
|
53
|
+
* effective `{ predicates, caveats }` hops is taken as-is (useful for testing an escalation directly).
|
|
54
|
+
*/
|
|
55
|
+
export declare function normalizeChain(chain: readonly unknown[]): ChainNormalizeResult;
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Input detection + normalization.
|
|
4
|
+
*
|
|
5
|
+
* `lintPolicy` accepts anything a principal might hand a CI gate: a bare predicate/caveat set, a
|
|
6
|
+
* compiled policy, a signed `Envelope`, or a minted `grant` (`Capability`). They all reduce to the
|
|
7
|
+
* same effective authority — a predicate set plus the runtime caveat (gate) set, and (when known) the
|
|
8
|
+
* risk policy. The grant/chain reduction is the subtle part and is documented on {@link fromGrant}.
|
|
9
|
+
*/
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
exports.isCapability = isCapability;
|
|
12
|
+
exports.isEnvelope = isEnvelope;
|
|
13
|
+
exports.fromGrant = fromGrant;
|
|
14
|
+
exports.normalizePolicyInput = normalizePolicyInput;
|
|
15
|
+
exports.normalizeChain = normalizeChain;
|
|
16
|
+
const pca_1 = require("@atlasauth/pca");
|
|
17
|
+
const util_1 = require("./util");
|
|
18
|
+
function isRecord(x) {
|
|
19
|
+
return x !== null && typeof x === 'object' && !Array.isArray(x);
|
|
20
|
+
}
|
|
21
|
+
/** A minted grant / delegation hop: has the content-addressed, signed shape of a `Capability`. */
|
|
22
|
+
function isCapability(x) {
|
|
23
|
+
if (!isRecord(x))
|
|
24
|
+
return false;
|
|
25
|
+
return (typeof x.id === 'string' &&
|
|
26
|
+
typeof x.issuer === 'string' &&
|
|
27
|
+
typeof x.holder === 'string' &&
|
|
28
|
+
typeof x.body_digest === 'string' &&
|
|
29
|
+
typeof x.sig === 'string' &&
|
|
30
|
+
Array.isArray(x.caveats));
|
|
31
|
+
}
|
|
32
|
+
/** A signed policy envelope (the Root Intent Grant payload). */
|
|
33
|
+
function isEnvelope(x) {
|
|
34
|
+
if (!isRecord(x))
|
|
35
|
+
return false;
|
|
36
|
+
return (typeof x.goal_commit === 'string' &&
|
|
37
|
+
Array.isArray(x.predicates) &&
|
|
38
|
+
Array.isArray(x.caveats) &&
|
|
39
|
+
isRecord(x.agent_binding) &&
|
|
40
|
+
isRecord(x.risk_policy));
|
|
41
|
+
}
|
|
42
|
+
/** A `compilePolicy` result. */
|
|
43
|
+
function isCompiledPolicy(x) {
|
|
44
|
+
if (!isRecord(x))
|
|
45
|
+
return false;
|
|
46
|
+
return Array.isArray(x.predicates) && Array.isArray(x.caveats) && isRecord(x.riskPolicy) && Array.isArray(x.actions);
|
|
47
|
+
}
|
|
48
|
+
/** A bare `{ predicates, caveats? }` policy. */
|
|
49
|
+
function isBarePolicy(x) {
|
|
50
|
+
return isRecord(x) && Array.isArray(x.predicates);
|
|
51
|
+
}
|
|
52
|
+
/** Drop the envelope-carrying caveat; keep the runtime gate caveats (and any appended attenuation). */
|
|
53
|
+
function runtimeCaveats(caveats) {
|
|
54
|
+
return caveats.filter((c) => c !== null && typeof c === 'object' && c.type !== util_1.ENVELOPE_CAVEAT_TYPE);
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Reduce a grant to its effective authority. The grant's predicates and baseline gate caveats live
|
|
58
|
+
* INSIDE its first `envelope` caveat (signed into the grant). Delegation then APPENDS further gate
|
|
59
|
+
* caveats to the grant's top-level `caveats` array. So the effective gates are the envelope's own
|
|
60
|
+
* caveats PLUS the appended (non-envelope) top-level caveats, and the predicates come from the
|
|
61
|
+
* envelope. A malformed / unreadable envelope is a hard failure the caller surfaces as a finding.
|
|
62
|
+
*/
|
|
63
|
+
function fromGrant(grant) {
|
|
64
|
+
const env = (0, pca_1.readEnvelope)(grant);
|
|
65
|
+
if (env === null) {
|
|
66
|
+
return { ok: false, reason: 'grant has no readable `envelope` caveat (malformed or unsigned payload)' };
|
|
67
|
+
}
|
|
68
|
+
const appended = runtimeCaveats(Array.isArray(grant.caveats) ? grant.caveats : []);
|
|
69
|
+
return {
|
|
70
|
+
ok: true,
|
|
71
|
+
policy: {
|
|
72
|
+
predicates: env.predicates,
|
|
73
|
+
caveats: [...env.caveats, ...appended],
|
|
74
|
+
riskPolicy: env.risk_policy,
|
|
75
|
+
kind: 'grant',
|
|
76
|
+
},
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
/** Normalize any single-policy input to its effective authority. */
|
|
80
|
+
function normalizePolicyInput(input) {
|
|
81
|
+
if (isCapability(input))
|
|
82
|
+
return fromGrant(input);
|
|
83
|
+
if (isEnvelope(input)) {
|
|
84
|
+
return { ok: true, policy: { predicates: input.predicates, caveats: input.caveats, riskPolicy: input.risk_policy, kind: 'envelope' } };
|
|
85
|
+
}
|
|
86
|
+
if (isCompiledPolicy(input)) {
|
|
87
|
+
return { ok: true, policy: { predicates: input.predicates, caveats: input.caveats, riskPolicy: input.riskPolicy, kind: 'compiled' } };
|
|
88
|
+
}
|
|
89
|
+
if (isBarePolicy(input)) {
|
|
90
|
+
const caveats = Array.isArray(input.caveats) ? input.caveats : [];
|
|
91
|
+
return { ok: true, policy: { predicates: input.predicates, caveats, kind: 'bare' } };
|
|
92
|
+
}
|
|
93
|
+
return { ok: false, reason: 'unrecognized policy shape (expected an envelope, grant, compiled policy, or { predicates, caveats })' };
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Normalize a delegation chain to one {@link NormalizedPolicy} per hop (root -> leaf). A chain of
|
|
97
|
+
* `Capability`s uses the grant reduction: the predicates are shared (from the root envelope) and each
|
|
98
|
+
* hop's effective gate caveats are the envelope's caveats plus that hop's appended caveats, so a
|
|
99
|
+
* well-formed (append-only) chain yields a monotonically narrowing authority. A chain of already
|
|
100
|
+
* effective `{ predicates, caveats }` hops is taken as-is (useful for testing an escalation directly).
|
|
101
|
+
*/
|
|
102
|
+
function normalizeChain(chain) {
|
|
103
|
+
if (chain.length === 0)
|
|
104
|
+
return { ok: false, reason: 'empty chain' };
|
|
105
|
+
if (chain.every((c) => isCapability(c))) {
|
|
106
|
+
const caps = chain;
|
|
107
|
+
const root = caps[0];
|
|
108
|
+
if (root === undefined)
|
|
109
|
+
return { ok: false, reason: 'empty chain' };
|
|
110
|
+
const env = (0, pca_1.readEnvelope)(root);
|
|
111
|
+
if (env === null)
|
|
112
|
+
return { ok: false, reason: 'root grant has no readable `envelope` caveat' };
|
|
113
|
+
const hops = caps.map((cap) => ({
|
|
114
|
+
predicates: env.predicates,
|
|
115
|
+
caveats: [...env.caveats, ...runtimeCaveats(Array.isArray(cap.caveats) ? cap.caveats : [])],
|
|
116
|
+
riskPolicy: env.risk_policy,
|
|
117
|
+
kind: 'chain-hop',
|
|
118
|
+
}));
|
|
119
|
+
return { ok: true, hops };
|
|
120
|
+
}
|
|
121
|
+
const hops = [];
|
|
122
|
+
for (let i = 0; i < chain.length; i++) {
|
|
123
|
+
const r = normalizePolicyInput(chain[i]);
|
|
124
|
+
if (!r.ok)
|
|
125
|
+
return { ok: false, reason: `hop ${i}: ${r.reason}` };
|
|
126
|
+
hops.push({ ...r.policy, kind: 'chain-hop' });
|
|
127
|
+
}
|
|
128
|
+
return { ok: true, hops };
|
|
129
|
+
}
|
|
130
|
+
//# sourceMappingURL=normalize.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"normalize.js","sourceRoot":"","sources":["../src/normalize.ts"],"names":[],"mappings":"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"}
|
package/dist/rules.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The five policy-safety rules, each driving the real static analyzer. Every rule is SOUND: a verdict
|
|
3
|
+
* is only ever reported as a hard `error` when the analyzer PROVES it (its result is not
|
|
4
|
+
* `approximate`); an undecidable policy feature downgrades to a `warn`/`info` that says so, so the
|
|
5
|
+
* gate never fails the build on a false positive.
|
|
6
|
+
*/
|
|
7
|
+
import { type AnalyzerOptions } from './deps';
|
|
8
|
+
import type { NormalizedPolicy } from './normalize';
|
|
9
|
+
import { type DangerousTarget, type Finding } from './types';
|
|
10
|
+
export declare function ruleOverBroadGrant(policy: NormalizedPolicy, dangerous: readonly string[], opts: AnalyzerOptions): Finding[];
|
|
11
|
+
export declare function rulePrivilegeEscalation(hops: readonly NormalizedPolicy[], opts: AnalyzerOptions): Finding[];
|
|
12
|
+
export declare function ruleRedundantCaveat(policy: NormalizedPolicy): Finding[];
|
|
13
|
+
export declare function ruleDangerousReachability(policy: NormalizedPolicy, dangerous: readonly string[], extraTargets: readonly DangerousTarget[], opts: AnalyzerOptions): Finding[];
|
|
14
|
+
export declare function ruleMissingSafetyFloor(policy: NormalizedPolicy, cfg: {
|
|
15
|
+
requireExpiry: boolean;
|
|
16
|
+
requireScopeGate: boolean;
|
|
17
|
+
}, dangerous: readonly string[]): Finding[];
|
package/dist/rules.js
ADDED
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* The five policy-safety rules, each driving the real static analyzer. Every rule is SOUND: a verdict
|
|
4
|
+
* is only ever reported as a hard `error` when the analyzer PROVES it (its result is not
|
|
5
|
+
* `approximate`); an undecidable policy feature downgrades to a `warn`/`info` that says so, so the
|
|
6
|
+
* gate never fails the build on a false positive.
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.ruleOverBroadGrant = ruleOverBroadGrant;
|
|
10
|
+
exports.rulePrivilegeEscalation = rulePrivilegeEscalation;
|
|
11
|
+
exports.ruleRedundantCaveat = ruleRedundantCaveat;
|
|
12
|
+
exports.ruleDangerousReachability = ruleDangerousReachability;
|
|
13
|
+
exports.ruleMissingSafetyFloor = ruleMissingSafetyFloor;
|
|
14
|
+
const deps_1 = require("./deps");
|
|
15
|
+
const pca_1 = require("@atlasauth/pca");
|
|
16
|
+
const util_1 = require("./util");
|
|
17
|
+
function analyzerInput(p) {
|
|
18
|
+
return { predicates: p.predicates, caveats: p.caveats };
|
|
19
|
+
}
|
|
20
|
+
function find(rule, severity, message, where) {
|
|
21
|
+
return where === undefined ? { rule, severity, message } : { rule, severity, message, where };
|
|
22
|
+
}
|
|
23
|
+
/** Every concrete verb named across the predicate set, plus whether any predicate grants `'*'`. */
|
|
24
|
+
function declaredVerbs(predicates) {
|
|
25
|
+
const concrete = [];
|
|
26
|
+
let hasWildcard = false;
|
|
27
|
+
for (const p of predicates) {
|
|
28
|
+
if (p === null || typeof p !== 'object')
|
|
29
|
+
continue;
|
|
30
|
+
for (const v of (0, util_1.verbsOf)(p.verb)) {
|
|
31
|
+
if (v === '*')
|
|
32
|
+
hasWildcard = true;
|
|
33
|
+
else
|
|
34
|
+
concrete.push(v);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return { concrete: (0, util_1.uniq)(concrete), hasWildcard };
|
|
38
|
+
}
|
|
39
|
+
/** Does the policy carry a budget / step-up gate that bounds a dangerous action? */
|
|
40
|
+
function gateDescription(policy) {
|
|
41
|
+
const gates = [];
|
|
42
|
+
if ((0, util_1.hasCaveatOfType)(policy.caveats, util_1.BUDGET_GATE_CAVEATS)) {
|
|
43
|
+
gates.push(policy.caveats.filter((c) => util_1.BUDGET_GATE_CAVEATS.includes(c.type)).map((c) => c.type).join('/'));
|
|
44
|
+
}
|
|
45
|
+
const rp = policy.riskPolicy;
|
|
46
|
+
if (rp !== undefined && Number.isFinite(rp.theta2) && rp.theta2 < 1)
|
|
47
|
+
gates.push('risk step-up (theta2 < 1)');
|
|
48
|
+
return gates.length > 0 ? gates.join(', ') : undefined;
|
|
49
|
+
}
|
|
50
|
+
// ---- 1. over-broad grant --------------------------------------------------------------------------
|
|
51
|
+
function ruleOverBroadGrant(policy, dangerous, opts) {
|
|
52
|
+
const out = [];
|
|
53
|
+
const input = analyzerInput(policy);
|
|
54
|
+
const hasScopeNarrowing = (0, util_1.hasCaveatOfType)(policy.caveats, util_1.SCOPE_NARROWING_CAVEATS);
|
|
55
|
+
policy.predicates.forEach((p, i) => {
|
|
56
|
+
if (p === null || typeof p !== 'object')
|
|
57
|
+
return;
|
|
58
|
+
const verbs = (0, util_1.verbsOf)(p.verb);
|
|
59
|
+
const where = `predicates[${i}]`;
|
|
60
|
+
const hasWhere = Array.isArray(p.where) && p.where.length > 0;
|
|
61
|
+
if (verbs.includes('*')) {
|
|
62
|
+
out.push(find('over-broad-grant', 'error', `predicate grants a WILDCARD verb '*' — authority over any action`, where));
|
|
63
|
+
}
|
|
64
|
+
if ((0, util_1.isWildcardResource)(p.resource)) {
|
|
65
|
+
const dangerousVerbs = verbs.filter((v) => (0, util_1.isDangerousVerb)(v, dangerous));
|
|
66
|
+
if (dangerousVerbs.length > 0) {
|
|
67
|
+
out.push(find('over-broad-grant', 'error', `dangerous verb(s) ${JSON.stringify(dangerousVerbs)} granted on a WILDCARD resource ('*')`, where));
|
|
68
|
+
}
|
|
69
|
+
else if (!verbs.includes('*')) {
|
|
70
|
+
out.push(find('over-broad-grant', 'warn', `predicate matches ANY resource ('*') — consider a resource prefix`, where));
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
for (const v of verbs) {
|
|
74
|
+
if (v !== '*' && (0, util_1.isDangerousVerb)(v, dangerous) && !hasWhere && !hasScopeNarrowing) {
|
|
75
|
+
out.push(find('over-broad-grant', 'error', `dangerous verb '${v}' is granted with NO narrowing \`where\` and no scope-narrowing caveat (${util_1.SCOPE_NARROWING_CAVEATS.join('/')})`, where));
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
// Semantic always-allow: a verb the analyzer PROVES is total (admits every action of that verb).
|
|
80
|
+
const { concrete } = declaredVerbs(policy.predicates);
|
|
81
|
+
for (const v of concrete) {
|
|
82
|
+
const aa = (0, deps_1.alwaysAllows)(input, v, opts);
|
|
83
|
+
if (aa.alwaysAllows && aa.approximate !== true) {
|
|
84
|
+
out.push(find('over-broad-grant', 'error', `grants EVERY action of '${v}' — the authority has no effective constraint (always-allow)`));
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return out;
|
|
88
|
+
}
|
|
89
|
+
// ---- 2. privilege escalation in a chain -----------------------------------------------------------
|
|
90
|
+
function rulePrivilegeEscalation(hops, opts) {
|
|
91
|
+
const out = [];
|
|
92
|
+
for (let i = 1; i < hops.length; i++) {
|
|
93
|
+
const parent = hops[i - 1];
|
|
94
|
+
const child = hops[i];
|
|
95
|
+
if (parent === undefined || child === undefined)
|
|
96
|
+
continue;
|
|
97
|
+
// A well-attenuated child admits a SUBSET of its parent, i.e. the parent subsumes the child.
|
|
98
|
+
const s = (0, deps_1.subsumes)(analyzerInput(parent), analyzerInput(child), opts);
|
|
99
|
+
const where = `hop[${i}]`;
|
|
100
|
+
if (s.subsumes)
|
|
101
|
+
continue;
|
|
102
|
+
if (s.approximate === true) {
|
|
103
|
+
out.push(find('privilege-escalation', 'warn', `could not prove hop ${i} attenuation (undecidable${s.reason ? `: ${s.reason}` : ''}) — attenuation not verified`, where));
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
const ex = s.counterexample !== undefined ? ` — child admits '${(0, util_1.describeAction)(s.counterexample)}' which its parent denies` : '';
|
|
107
|
+
out.push(find('privilege-escalation', 'error', `delegation hop ${i} ESCALATES authority: its effective authority is not a subset of its parent (attenuation violated)${ex}`, where));
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return out;
|
|
111
|
+
}
|
|
112
|
+
// For each type: which numeric field carries its bound, and whether the STRICTEST (binding) one is the
|
|
113
|
+
// min or the max. Any non-binding caveat of the same type can never bite — it is dead policy.
|
|
114
|
+
const ORDERED_CAVEATS = {
|
|
115
|
+
expires: { field: 'at', keep: 'min' }, // earliest expiry dominates
|
|
116
|
+
not_before: { field: 'at', keep: 'max' }, // latest not-before dominates
|
|
117
|
+
max_blast_radius: { field: 'max', keep: 'min' }, // smallest radius dominates
|
|
118
|
+
delegation_depth: { field: 'max', keep: 'min' }, // smallest depth dominates
|
|
119
|
+
budget_alloc: { field: 'limit', keep: 'min' }, // smallest allocation dominates
|
|
120
|
+
};
|
|
121
|
+
function caveatKey(cv) {
|
|
122
|
+
try {
|
|
123
|
+
return JSON.stringify(cv);
|
|
124
|
+
}
|
|
125
|
+
catch {
|
|
126
|
+
return `${cv.type}:<unserializable>`;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
function ruleRedundantCaveat(policy) {
|
|
130
|
+
const out = [];
|
|
131
|
+
// (a) exact-duplicate caveats.
|
|
132
|
+
const seen = new Map();
|
|
133
|
+
policy.caveats.forEach((cv, i) => {
|
|
134
|
+
if (cv === null || typeof cv !== 'object')
|
|
135
|
+
return;
|
|
136
|
+
const k = caveatKey(cv);
|
|
137
|
+
const first = seen.get(k);
|
|
138
|
+
if (first !== undefined) {
|
|
139
|
+
out.push(find('redundant-caveat', 'warn', `caveat ${i} ('${cv.type}') is an exact duplicate of caveat ${first} — dead policy`, `caveats[${i}]`));
|
|
140
|
+
}
|
|
141
|
+
else {
|
|
142
|
+
seen.set(k, i);
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
// (b) same-type domination: a looser caveat that a stricter one of the same type always subsumes.
|
|
146
|
+
for (const [type, spec] of Object.entries(ORDERED_CAVEATS)) {
|
|
147
|
+
const entries = policy.caveats
|
|
148
|
+
.map((cv, i) => ({ cv, i }))
|
|
149
|
+
.filter((e) => e.cv !== null && typeof e.cv === 'object' && e.cv.type === type);
|
|
150
|
+
const vals = entries.map((e) => ({ ...e, v: (0, util_1.num)(e.cv[spec.field]) })).filter((e) => e.v !== undefined);
|
|
151
|
+
if (vals.length < 2)
|
|
152
|
+
continue;
|
|
153
|
+
const binding = spec.keep === 'min' ? Math.min(...vals.map((e) => e.v)) : Math.max(...vals.map((e) => e.v));
|
|
154
|
+
for (const e of vals) {
|
|
155
|
+
if (e.v !== binding) {
|
|
156
|
+
out.push(find('redundant-caveat', 'warn', `caveat ${e.i} ('${type}' ${spec.field}=${e.v}) is subsumed by a stricter '${type}' (${spec.field}=${binding}) — it can never bite`, `caveats[${e.i}]`));
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
// (c) a predicate whose numeric `where` conjunction is UNSATISFIABLE never matches — dead policy.
|
|
161
|
+
policy.predicates.forEach((p, i) => {
|
|
162
|
+
if (p === null || typeof p !== 'object' || !Array.isArray(p.where))
|
|
163
|
+
return;
|
|
164
|
+
if (!(0, deps_1.predicateNumericSatisfiable)(p.where)) {
|
|
165
|
+
out.push(find('redundant-caveat', 'warn', `predicate ${i} can never match — its numeric \`where\` constraints are unsatisfiable (dead policy)`, `predicates[${i}]`));
|
|
166
|
+
}
|
|
167
|
+
else {
|
|
168
|
+
out.push(...nonBindingConditions(p.where, i));
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
return out;
|
|
172
|
+
}
|
|
173
|
+
const NUMERIC_OPS = new Set(['lt', 'lte', 'gt', 'gte', 'eq']);
|
|
174
|
+
function intervalEq(a, b) {
|
|
175
|
+
return a.lo === b.lo && a.loInclusive === b.loInclusive && a.hi === b.hi && a.hiInclusive === b.hiInclusive;
|
|
176
|
+
}
|
|
177
|
+
/** A numeric leaf condition that does not tighten its field's interval is redundant (never binds). */
|
|
178
|
+
function nonBindingConditions(where, predIndex) {
|
|
179
|
+
const byField = new Map();
|
|
180
|
+
where.forEach((c, i) => {
|
|
181
|
+
if (!(0, pca_1.isLeafCondition)(c))
|
|
182
|
+
return;
|
|
183
|
+
if (c.ref !== undefined)
|
|
184
|
+
return;
|
|
185
|
+
if (typeof c.op !== 'string' || !NUMERIC_OPS.has(c.op))
|
|
186
|
+
return;
|
|
187
|
+
if ((0, util_1.num)(c.value) === undefined)
|
|
188
|
+
return;
|
|
189
|
+
const arr = byField.get(c.field) ?? [];
|
|
190
|
+
arr.push({ c, i });
|
|
191
|
+
byField.set(c.field, arr);
|
|
192
|
+
});
|
|
193
|
+
const out = [];
|
|
194
|
+
for (const [field, conds] of byField) {
|
|
195
|
+
if (conds.length < 2)
|
|
196
|
+
continue;
|
|
197
|
+
const full = conds.reduce((iv, e) => (0, deps_1.tightenInterval)(iv, e.c.op, e.c.value), deps_1.FULL_INTERVAL);
|
|
198
|
+
if ((0, deps_1.intervalEmpty)(full))
|
|
199
|
+
continue; // the whole-predicate unsat case is reported in (c)
|
|
200
|
+
for (const e of conds) {
|
|
201
|
+
const without = conds.filter((x) => x !== e).reduce((iv, x) => (0, deps_1.tightenInterval)(iv, x.c.op, x.c.value), deps_1.FULL_INTERVAL);
|
|
202
|
+
if (intervalEq(without, full)) {
|
|
203
|
+
out.push(find('redundant-caveat', 'info', `condition on '${field}' (${e.c.op} ${String(e.c.value)}) does not tighten the authority — redundant with the other bounds on this field`, `predicates[${predIndex}].where[${e.i}]`));
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
return out;
|
|
208
|
+
}
|
|
209
|
+
// ---- 4. dangerous reachability --------------------------------------------------------------------
|
|
210
|
+
function ruleDangerousReachability(policy, dangerous, extraTargets, opts) {
|
|
211
|
+
const out = [];
|
|
212
|
+
const input = analyzerInput(policy);
|
|
213
|
+
const { concrete } = declaredVerbs(policy.predicates);
|
|
214
|
+
const derived = concrete.filter((v) => (0, util_1.isDangerousVerb)(v, dangerous)).map((verb) => ({ verb }));
|
|
215
|
+
const targets = [...derived, ...extraTargets];
|
|
216
|
+
const gate = gateDescription(policy);
|
|
217
|
+
for (const t of targets) {
|
|
218
|
+
let proven = false;
|
|
219
|
+
let approximate = false;
|
|
220
|
+
let reason;
|
|
221
|
+
let witness;
|
|
222
|
+
if (t.resource !== undefined) {
|
|
223
|
+
const r = (0, deps_1.reachable)(input, { verb: t.verb, resource: t.resource }, opts);
|
|
224
|
+
proven = r.reachable && r.approximate !== true;
|
|
225
|
+
approximate = r.approximate === true;
|
|
226
|
+
reason = r.reason;
|
|
227
|
+
witness = `${t.verb} ${t.resource}`;
|
|
228
|
+
}
|
|
229
|
+
else {
|
|
230
|
+
const ad = (0, deps_1.alwaysDenies)(input, t.verb, opts);
|
|
231
|
+
proven = ad.alwaysDenies === false && ad.approximate !== true;
|
|
232
|
+
approximate = ad.approximate === true;
|
|
233
|
+
reason = ad.reason;
|
|
234
|
+
witness = ad.witness !== undefined ? (0, util_1.describeAction)(ad.witness) : t.verb;
|
|
235
|
+
}
|
|
236
|
+
if (approximate) {
|
|
237
|
+
out.push(find('dangerous-reachability', 'warn', `could not decide reachability of dangerous verb '${t.verb}' (undecidable${reason ? `: ${reason}` : ''})`));
|
|
238
|
+
}
|
|
239
|
+
else if (proven) {
|
|
240
|
+
if (gate === undefined) {
|
|
241
|
+
out.push(find('dangerous-reachability', 'error', `dangerous action '${witness}' is REACHABLE with no budget / rate / step-up gate`));
|
|
242
|
+
}
|
|
243
|
+
else {
|
|
244
|
+
out.push(find('dangerous-reachability', 'info', `dangerous action '${witness}' is reachable but gated by ${gate}`));
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
return out;
|
|
249
|
+
}
|
|
250
|
+
// ---- 5. missing safety floor ----------------------------------------------------------------------
|
|
251
|
+
/** A grant is non-trivial when any predicate grants a dangerous/wildcard verb, a wildcard resource, or is unconstrained. */
|
|
252
|
+
function isNonTrivial(policy, dangerous) {
|
|
253
|
+
return policy.predicates.some((p) => {
|
|
254
|
+
if (p === null || typeof p !== 'object')
|
|
255
|
+
return false;
|
|
256
|
+
const verbs = (0, util_1.verbsOf)(p.verb);
|
|
257
|
+
const hasWhere = Array.isArray(p.where) && p.where.length > 0;
|
|
258
|
+
return (verbs.includes('*') ||
|
|
259
|
+
verbs.some((v) => (0, util_1.isDangerousVerb)(v, dangerous)) ||
|
|
260
|
+
(0, util_1.isWildcardResource)(p.resource) ||
|
|
261
|
+
!hasWhere);
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
function ruleMissingSafetyFloor(policy, cfg, dangerous) {
|
|
265
|
+
const out = [];
|
|
266
|
+
if (policy.predicates.length === 0)
|
|
267
|
+
return out; // nothing is granted; default-deny needs no floor
|
|
268
|
+
if (cfg.requireExpiry && !(0, util_1.hasCaveatOfType)(policy.caveats, ['expires'])) {
|
|
269
|
+
out.push(find('missing-safety-floor', 'warn', 'grant has no `expires` caveat — the authority never expires'));
|
|
270
|
+
}
|
|
271
|
+
if (cfg.requireScopeGate && isNonTrivial(policy, dangerous) && !(0, util_1.hasCaveatOfType)(policy.caveats, util_1.BUDGET_GATE_CAVEATS)) {
|
|
272
|
+
out.push(find('missing-safety-floor', 'warn', `non-trivial grant has no blast-radius / rate / budget gate (${util_1.BUDGET_GATE_CAVEATS.join('/')})`));
|
|
273
|
+
}
|
|
274
|
+
return out;
|
|
275
|
+
}
|
|
276
|
+
//# sourceMappingURL=rules.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rules.js","sourceRoot":"","sources":["../src/rules.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public types for @atlasauth/pca-policy-ci — the shift-left policy-safety CI gate.
|
|
3
|
+
*
|
|
4
|
+
* A {@link Finding} is one problem the linter found in a PCA policy. Every rule is toggleable and
|
|
5
|
+
* every verdict is SOUND: where the underlying static analyzer cannot decide a question exactly it
|
|
6
|
+
* fails safe to a `warn`/`info` that says it is undecidable, and NEVER raises a false hard `error`.
|
|
7
|
+
*/
|
|
8
|
+
/** Finding severity, in increasing order of concern (`info` < `warn` < `error`). */
|
|
9
|
+
export type Severity = 'error' | 'warn' | 'info';
|
|
10
|
+
/** Stable rule identifiers (one per implemented check, plus the structural `malformed-policy`). */
|
|
11
|
+
export type RuleId = 'over-broad-grant' | 'privilege-escalation' | 'redundant-caveat' | 'dangerous-reachability' | 'missing-safety-floor' | 'malformed-policy';
|
|
12
|
+
/** A single problem the linter found. `where` is a human-readable location hint inside the policy. */
|
|
13
|
+
export interface Finding {
|
|
14
|
+
rule: RuleId;
|
|
15
|
+
severity: Severity;
|
|
16
|
+
message: string;
|
|
17
|
+
/** Location hint, e.g. `predicates[0]`, `predicates[0].where[2]`, `caveats[1]`, `hop[2]`. */
|
|
18
|
+
where?: string;
|
|
19
|
+
}
|
|
20
|
+
/** Default set of verb substrings treated as dangerous (case-insensitive `includes` match). */
|
|
21
|
+
export declare const DEFAULT_DANGEROUS_VERBS: readonly string[];
|
|
22
|
+
/** A dangerous (verb, resource) target to probe for reachability. `resource` omitted = any resource. */
|
|
23
|
+
export interface DangerousTarget {
|
|
24
|
+
verb: string;
|
|
25
|
+
resource?: string;
|
|
26
|
+
}
|
|
27
|
+
export interface OverBroadConfig {
|
|
28
|
+
/** Verb substrings treated as dangerous. Defaults to {@link DEFAULT_DANGEROUS_VERBS}. */
|
|
29
|
+
dangerousVerbs?: readonly string[];
|
|
30
|
+
}
|
|
31
|
+
export interface DangerousReachabilityConfig {
|
|
32
|
+
dangerousVerbs?: readonly string[];
|
|
33
|
+
/** Explicit (verb, resource) targets to probe in addition to the derived dangerous verbs. */
|
|
34
|
+
targets?: DangerousTarget[];
|
|
35
|
+
}
|
|
36
|
+
export interface SafetyFloorConfig {
|
|
37
|
+
/** Flag a grant with no `expires` caveat. Default true. */
|
|
38
|
+
requireExpiry?: boolean;
|
|
39
|
+
/** Flag a non-trivial grant with no `max_blast_radius` / `rate` / `budget_alloc` gate. Default true. */
|
|
40
|
+
requireScopeGate?: boolean;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Per-rule toggles. For each rule: omit / `true` = enabled with defaults, `false` = disabled, an
|
|
44
|
+
* object = enabled with that configuration. All rules are enabled by default.
|
|
45
|
+
*/
|
|
46
|
+
export interface LintRules {
|
|
47
|
+
overBroadGrant?: boolean | OverBroadConfig;
|
|
48
|
+
privilegeEscalation?: boolean;
|
|
49
|
+
redundantCaveat?: boolean;
|
|
50
|
+
dangerousReachability?: boolean | DangerousReachabilityConfig;
|
|
51
|
+
missingSafetyFloor?: boolean | SafetyFloorConfig;
|
|
52
|
+
}
|
|
53
|
+
/** One labeled policy's findings (used by {@link LintSummary} for multi-policy runs). */
|
|
54
|
+
export interface LabeledFindings {
|
|
55
|
+
label: string;
|
|
56
|
+
findings: Finding[];
|
|
57
|
+
}
|
|
58
|
+
export interface SummaryOptions {
|
|
59
|
+
/**
|
|
60
|
+
* The lowest severity that fails the gate (contributes a non-zero exit code). `error` (default)
|
|
61
|
+
* fails on errors only; `warn` fails on warnings and errors; `info` fails on everything.
|
|
62
|
+
*/
|
|
63
|
+
maxSeverity?: Severity;
|
|
64
|
+
}
|
|
65
|
+
export interface LintSummary {
|
|
66
|
+
results: LabeledFindings[];
|
|
67
|
+
counts: {
|
|
68
|
+
error: number;
|
|
69
|
+
warn: number;
|
|
70
|
+
info: number;
|
|
71
|
+
};
|
|
72
|
+
/** The threshold used to compute `ok` / `exitCode`. */
|
|
73
|
+
maxSeverity: Severity;
|
|
74
|
+
/** True when nothing at or above `maxSeverity` was found. */
|
|
75
|
+
ok: boolean;
|
|
76
|
+
/** 0 when `ok`, else 1. */
|
|
77
|
+
exitCode: number;
|
|
78
|
+
}
|
|
79
|
+
/** Severity ordering used for thresholds. Higher = more severe. */
|
|
80
|
+
export declare const SEVERITY_RANK: Record<Severity, number>;
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Public types for @atlasauth/pca-policy-ci — the shift-left policy-safety CI gate.
|
|
4
|
+
*
|
|
5
|
+
* A {@link Finding} is one problem the linter found in a PCA policy. Every rule is toggleable and
|
|
6
|
+
* every verdict is SOUND: where the underlying static analyzer cannot decide a question exactly it
|
|
7
|
+
* fails safe to a `warn`/`info` that says it is undecidable, and NEVER raises a false hard `error`.
|
|
8
|
+
*/
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.SEVERITY_RANK = exports.DEFAULT_DANGEROUS_VERBS = void 0;
|
|
11
|
+
// ---- rule configuration ---------------------------------------------------------------------------
|
|
12
|
+
/** Default set of verb substrings treated as dangerous (case-insensitive `includes` match). */
|
|
13
|
+
exports.DEFAULT_DANGEROUS_VERBS = [
|
|
14
|
+
'delete',
|
|
15
|
+
'destroy',
|
|
16
|
+
'drop',
|
|
17
|
+
'remove',
|
|
18
|
+
'purge',
|
|
19
|
+
'transfer',
|
|
20
|
+
'payout',
|
|
21
|
+
'wire',
|
|
22
|
+
'withdraw',
|
|
23
|
+
'admin',
|
|
24
|
+
'grant',
|
|
25
|
+
'revoke',
|
|
26
|
+
'deploy',
|
|
27
|
+
'shutdown',
|
|
28
|
+
'rotate',
|
|
29
|
+
];
|
|
30
|
+
/** Severity ordering used for thresholds. Higher = more severe. */
|
|
31
|
+
exports.SEVERITY_RANK = { info: 1, warn: 2, error: 3 };
|
|
32
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":";AAAA;;;;;;GAMG;;;AAuBH,sGAAsG;AAEtG,+FAA+F;AAClF,QAAA,uBAAuB,GAAsB;IACxD,QAAQ;IACR,SAAS;IACT,MAAM;IACN,QAAQ;IACR,OAAO;IACP,UAAU;IACV,QAAQ;IACR,MAAM;IACN,UAAU;IACV,OAAO;IACP,OAAO;IACP,QAAQ;IACR,QAAQ;IACR,UAAU;IACV,QAAQ;CACA,CAAC;AAiEX,mEAAmE;AACtD,QAAA,aAAa,GAA6B,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC"}
|
package/dist/util.d.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Small shared helpers for the linter rules. Pure; no analyzer calls here.
|
|
3
|
+
*/
|
|
4
|
+
import { type AnalyzerAction } from './deps';
|
|
5
|
+
import type { Caveat, Predicate } from '@atlasauth/pca';
|
|
6
|
+
/** The caveat `type` that carries the signed envelope payload inside a grant. */
|
|
7
|
+
export declare const ENVELOPE_CAVEAT_TYPE = "envelope";
|
|
8
|
+
/** Scope-narrowing caveat types (constrain WHAT/HOW MUCH, not merely WHEN). */
|
|
9
|
+
export declare const SCOPE_NARROWING_CAVEATS: readonly string[];
|
|
10
|
+
/** Blast/rate/budget gate caveat types (the "safety floor" gates distinct from a time bound). */
|
|
11
|
+
export declare const BUDGET_GATE_CAVEATS: readonly string[];
|
|
12
|
+
/** Normalize a predicate's `verb` (string | string[] | '*') to a concrete string array. */
|
|
13
|
+
export declare function verbsOf(verb: Predicate['verb']): string[];
|
|
14
|
+
/** Finite-number accessor for an `unknown` caveat field. */
|
|
15
|
+
export declare function num(x: unknown): number | undefined;
|
|
16
|
+
/** Case-insensitive `includes` match of a verb against a dangerous-substring set. */
|
|
17
|
+
export declare function isDangerousVerb(verb: string, dangerous: readonly string[]): boolean;
|
|
18
|
+
/** A resource matcher that admits every resource: omitted or `'*'`. */
|
|
19
|
+
export declare function isWildcardResource(resource: Predicate['resource']): boolean;
|
|
20
|
+
/** Whether a caveat list contains any caveat of one of the given types. */
|
|
21
|
+
export declare function hasCaveatOfType(caveats: readonly Caveat[], types: readonly string[]): boolean;
|
|
22
|
+
/** A one-line, log-friendly rendering of an analyzer witness/counterexample action. */
|
|
23
|
+
export declare function describeAction(a: AnalyzerAction): string;
|
|
24
|
+
/** Unique, order-preserving strings. */
|
|
25
|
+
export declare function uniq(xs: readonly string[]): string[];
|
package/dist/util.js
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Small shared helpers for the linter rules. Pure; no analyzer calls here.
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.BUDGET_GATE_CAVEATS = exports.SCOPE_NARROWING_CAVEATS = exports.ENVELOPE_CAVEAT_TYPE = void 0;
|
|
7
|
+
exports.verbsOf = verbsOf;
|
|
8
|
+
exports.num = num;
|
|
9
|
+
exports.isDangerousVerb = isDangerousVerb;
|
|
10
|
+
exports.isWildcardResource = isWildcardResource;
|
|
11
|
+
exports.hasCaveatOfType = hasCaveatOfType;
|
|
12
|
+
exports.describeAction = describeAction;
|
|
13
|
+
exports.uniq = uniq;
|
|
14
|
+
const deps_1 = require("./deps");
|
|
15
|
+
/** The caveat `type` that carries the signed envelope payload inside a grant. */
|
|
16
|
+
exports.ENVELOPE_CAVEAT_TYPE = deps_1.ENVELOPE_CAVEAT;
|
|
17
|
+
/** Scope-narrowing caveat types (constrain WHAT/HOW MUCH, not merely WHEN). */
|
|
18
|
+
exports.SCOPE_NARROWING_CAVEATS = [
|
|
19
|
+
'max_blast_radius',
|
|
20
|
+
'rate',
|
|
21
|
+
'reversibility_max',
|
|
22
|
+
'budget_alloc',
|
|
23
|
+
'delegation_depth',
|
|
24
|
+
];
|
|
25
|
+
/** Blast/rate/budget gate caveat types (the "safety floor" gates distinct from a time bound). */
|
|
26
|
+
exports.BUDGET_GATE_CAVEATS = ['max_blast_radius', 'rate', 'budget_alloc'];
|
|
27
|
+
/** Normalize a predicate's `verb` (string | string[] | '*') to a concrete string array. */
|
|
28
|
+
function verbsOf(verb) {
|
|
29
|
+
if (typeof verb === 'string')
|
|
30
|
+
return [verb];
|
|
31
|
+
if (Array.isArray(verb))
|
|
32
|
+
return verb.filter((x) => typeof x === 'string');
|
|
33
|
+
return [];
|
|
34
|
+
}
|
|
35
|
+
/** Finite-number accessor for an `unknown` caveat field. */
|
|
36
|
+
function num(x) {
|
|
37
|
+
return typeof x === 'number' && Number.isFinite(x) ? x : undefined;
|
|
38
|
+
}
|
|
39
|
+
/** Case-insensitive `includes` match of a verb against a dangerous-substring set. */
|
|
40
|
+
function isDangerousVerb(verb, dangerous) {
|
|
41
|
+
const v = verb.toLowerCase();
|
|
42
|
+
return dangerous.some((kw) => v.includes(kw.toLowerCase()));
|
|
43
|
+
}
|
|
44
|
+
/** A resource matcher that admits every resource: omitted or `'*'`. */
|
|
45
|
+
function isWildcardResource(resource) {
|
|
46
|
+
return resource === undefined || resource === '*';
|
|
47
|
+
}
|
|
48
|
+
/** Whether a caveat list contains any caveat of one of the given types. */
|
|
49
|
+
function hasCaveatOfType(caveats, types) {
|
|
50
|
+
return caveats.some((c) => c !== null && typeof c === 'object' && typeof c.type === 'string' && types.includes(c.type));
|
|
51
|
+
}
|
|
52
|
+
/** A one-line, log-friendly rendering of an analyzer witness/counterexample action. */
|
|
53
|
+
function describeAction(a) {
|
|
54
|
+
const parts = [a.verb, a.resource];
|
|
55
|
+
if (a.params && Object.keys(a.params).length > 0)
|
|
56
|
+
parts.push(JSON.stringify(a.params));
|
|
57
|
+
if (a.reversibilityClass !== undefined)
|
|
58
|
+
parts.push(`[${a.reversibilityClass}]`);
|
|
59
|
+
return parts.join(' ');
|
|
60
|
+
}
|
|
61
|
+
/** Unique, order-preserving strings. */
|
|
62
|
+
function uniq(xs) {
|
|
63
|
+
const seen = new Set();
|
|
64
|
+
const out = [];
|
|
65
|
+
for (const x of xs) {
|
|
66
|
+
if (!seen.has(x)) {
|
|
67
|
+
seen.add(x);
|
|
68
|
+
out.push(x);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return out;
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=util.js.map
|
package/dist/util.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"util.js","sourceRoot":"","sources":["../src/util.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,37 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atlasauth/pca-policy-ci",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "CI gate for PCA policies: statically flag over-broad grants, privilege-escalation in delegation chains, unused/redundant caveats, and reachability of dangerous actions — fail the build before an unsafe policy ships.",
|
|
5
|
+
"bin": {
|
|
6
|
+
"pca-policy-ci": "./dist/cli.js"
|
|
7
|
+
},
|
|
8
|
+
"main": "./dist/index.js",
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"default": "./dist/index.js"
|
|
14
|
+
},
|
|
15
|
+
"./package.json": "./package.json"
|
|
16
|
+
},
|
|
17
|
+
"dependencies": {
|
|
18
|
+
"@atlasauth/pca-analyzer": "0.1.0",
|
|
19
|
+
"@atlasauth/pca": "0.1.0"
|
|
20
|
+
},
|
|
21
|
+
"devDependencies": {
|
|
22
|
+
"typescript": "^5.6.3",
|
|
23
|
+
"vitest": "^2.1.4"
|
|
24
|
+
},
|
|
25
|
+
"files": [
|
|
26
|
+
"dist",
|
|
27
|
+
"README.md"
|
|
28
|
+
],
|
|
29
|
+
"publishConfig": {
|
|
30
|
+
"access": "public"
|
|
31
|
+
},
|
|
32
|
+
"scripts": {
|
|
33
|
+
"build": "tsc -p tsconfig.build.json",
|
|
34
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
35
|
+
"test": "vitest run"
|
|
36
|
+
}
|
|
6
37
|
}
|
package/README.md
DELETED