@xano/cli 1.3.1-beta.1 → 1.3.1
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/README.md +1 -269
- package/dist/base-command.d.ts +1 -12
- package/dist/base-command.js +22 -36
- package/dist/commands/branch/set_live/index.d.ts +0 -22
- package/dist/commands/branch/set_live/index.js +1 -60
- package/dist/commands/ephemeral/push/index.js +1 -6
- package/dist/commands/function/create/index.d.ts +0 -15
- package/dist/commands/function/create/index.js +0 -40
- package/dist/commands/function/edit/index.d.ts +0 -15
- package/dist/commands/function/edit/index.js +0 -40
- package/dist/commands/release/create/index.js +0 -7
- package/dist/commands/release/deploy/index.d.ts +0 -28
- package/dist/commands/release/deploy/index.js +6 -90
- package/dist/commands/release/import/index.js +1 -5
- package/dist/commands/release/list/index.d.ts +0 -7
- package/dist/commands/release/list/index.js +10 -54
- package/dist/commands/release/push/index.js +1 -19
- package/dist/commands/sandbox/push/index.js +1 -6
- package/dist/commands/tenant/create/index.d.ts +0 -3
- package/dist/commands/tenant/create/index.js +0 -21
- package/dist/commands/tenant/deploy_release/index.d.ts +0 -40
- package/dist/commands/tenant/deploy_release/index.js +4 -186
- package/dist/commands/tenant/edit/index.d.ts +0 -2
- package/dist/commands/tenant/edit/index.js +4 -39
- package/dist/commands/workspace/pull/index.d.ts +0 -10
- package/dist/commands/workspace/pull/index.js +7 -70
- package/dist/commands/workspace/push/index.d.ts +0 -19
- package/dist/commands/workspace/push/index.js +5 -124
- package/dist/utils/document-parser.d.ts +0 -10
- package/dist/utils/document-parser.js +0 -21
- package/dist/utils/multidoc-push.d.ts +2 -55
- package/dist/utils/multidoc-push.js +27 -103
- package/oclif.manifest.json +3142 -6141
- package/package.json +1 -7
- package/dist/commands/policy/catalogue/index.d.ts +0 -15
- package/dist/commands/policy/catalogue/index.js +0 -29
- package/dist/commands/policy/create/index.d.ts +0 -18
- package/dist/commands/policy/create/index.js +0 -39
- package/dist/commands/policy/delete/index.d.ts +0 -19
- package/dist/commands/policy/delete/index.js +0 -61
- package/dist/commands/policy/evaluate/index.d.ts +0 -32
- package/dist/commands/policy/evaluate/index.js +0 -71
- package/dist/commands/policy/list/index.d.ts +0 -14
- package/dist/commands/policy/list/index.js +0 -21
- package/dist/commands/policy/parse/index.d.ts +0 -19
- package/dist/commands/policy/parse/index.js +0 -18
- package/dist/commands/policy/publish/index.d.ts +0 -20
- package/dist/commands/policy/publish/index.js +0 -38
- package/dist/commands/policy/runs/index.d.ts +0 -54
- package/dist/commands/policy/runs/index.js +0 -273
- package/dist/commands/policy/status/index.d.ts +0 -21
- package/dist/commands/policy/status/index.js +0 -54
- package/dist/commands/skills/pull/index.d.ts +0 -30
- package/dist/commands/skills/pull/index.js +0 -118
- package/dist/commands/tenant_deploy_request/bypass/index.d.ts +0 -31
- package/dist/commands/tenant_deploy_request/bypass/index.js +0 -109
- package/dist/commands/tenant_deploy_request/create/index.d.ts +0 -21
- package/dist/commands/tenant_deploy_request/create/index.js +0 -108
- package/dist/commands/tenant_deploy_request/edit/index.d.ts +0 -22
- package/dist/commands/tenant_deploy_request/edit/index.js +0 -103
- package/dist/commands/tenant_deploy_request/get/index.d.ts +0 -25
- package/dist/commands/tenant_deploy_request/get/index.js +0 -116
- package/dist/commands/tenant_deploy_request/list/index.d.ts +0 -20
- package/dist/commands/tenant_deploy_request/list/index.js +0 -122
- package/dist/commands/tenant_deploy_request/revision/index.d.ts +0 -21
- package/dist/commands/tenant_deploy_request/revision/index.js +0 -83
- package/dist/commands/tenant_deploy_request/set_status/index.d.ts +0 -38
- package/dist/commands/tenant_deploy_request/set_status/index.js +0 -159
- package/dist/policy-command.d.ts +0 -90
- package/dist/policy-command.js +0 -94
- package/dist/utils/policy/catalogue.d.ts +0 -7
- package/dist/utils/policy/catalogue.js +0 -72
- package/dist/utils/policy/errors.d.ts +0 -12
- package/dist/utils/policy/errors.js +0 -61
- package/dist/utils/policy/feedback.d.ts +0 -68
- package/dist/utils/policy/feedback.js +0 -165
- package/dist/utils/policy/findings.d.ts +0 -23
- package/dist/utils/policy/findings.js +0 -65
- package/dist/utils/policy/gate.d.ts +0 -161
- package/dist/utils/policy/gate.js +0 -201
- package/dist/utils/policy/goal.d.ts +0 -46
- package/dist/utils/policy/goal.js +0 -95
- package/dist/utils/policy/permission.d.ts +0 -23
- package/dist/utils/policy/permission.js +0 -84
- package/dist/utils/policy/push-gate.d.ts +0 -5
- package/dist/utils/policy/push-gate.js +0 -29
- package/dist/utils/policy/release.d.ts +0 -85
- package/dist/utils/policy/release.js +0 -115
- package/dist/utils/policy/request.d.ts +0 -55
- package/dist/utils/policy/request.js +0 -115
- package/dist/utils/policy/runs.d.ts +0 -30
- package/dist/utils/policy/runs.js +0 -101
- package/dist/utils/policy/status.d.ts +0 -47
- package/dist/utils/policy/status.js +0 -117
- package/dist/utils/policy/types.d.ts +0 -185
- package/dist/utils/policy/types.js +0 -1
|
@@ -1,165 +0,0 @@
|
|
|
1
|
-
import { findingLine, hasNoObjects, policyResultSummary, snapshotRules } from './findings.js';
|
|
2
|
-
/**
|
|
3
|
-
* Feedback printed as a headline: a pass or fail that says whether its findings block, or a branch
|
|
4
|
-
* with no active policy to evaluate. Everything else is reported by `policyCheckWarning`.
|
|
5
|
-
*/
|
|
6
|
-
function isHeadlined(check) {
|
|
7
|
-
if (check.status === 'not_applicable')
|
|
8
|
-
return true;
|
|
9
|
-
return ['fail', 'pass'].includes(check.status ?? '') && typeof check.blocking === 'boolean';
|
|
10
|
-
}
|
|
11
|
-
/** 2 whenever the platform says a finding blocks, whatever the status; every other outcome is 0. */
|
|
12
|
-
export function policyExitCode(check) {
|
|
13
|
-
return check?.blocking === true ? 2 : 0;
|
|
14
|
-
}
|
|
15
|
-
/**
|
|
16
|
-
* The one warning line for feedback without a headline (`disabled`, `forbidden`, `unavailable`,
|
|
17
|
-
* `error`, an unknown status, or a pass or fail that does not say whether it blocks): the server's
|
|
18
|
-
* own status and message, or that none came back. `null` for headlined feedback.
|
|
19
|
-
*/
|
|
20
|
-
export function policyCheckWarning(check) {
|
|
21
|
-
if (!check)
|
|
22
|
-
return 'Policy check: no policy feedback returned.';
|
|
23
|
-
if (isHeadlined(check))
|
|
24
|
-
return null;
|
|
25
|
-
const status = typeof check.status === 'string' && check.status.trim() !== '' ? check.status.trim() : 'unknown';
|
|
26
|
-
const message = typeof check.message === 'string' && check.message.trim() !== ''
|
|
27
|
-
? check.message.trim()
|
|
28
|
-
: (['fail', 'pass'].includes(status) ? 'the server did not say whether its findings block.' : 'no message returned.');
|
|
29
|
-
return `Policy check ${status}: ${message}`;
|
|
30
|
-
}
|
|
31
|
-
/**
|
|
32
|
-
* What happened to the policy documents a push carried. The preview says, per policy, whether it was
|
|
33
|
-
* created, updated or left unchanged. Without a preview only the number sent is known: the import
|
|
34
|
-
* response reports an unchanged policy exactly as it reports a saved one.
|
|
35
|
-
*/
|
|
36
|
-
export function policyDocumentSummary(preview, sentPolicies) {
|
|
37
|
-
const operations = (preview?.operations ?? []).filter((op) => op.type === 'policy');
|
|
38
|
-
if (operations.length === 0) {
|
|
39
|
-
return sentPolicies > 0
|
|
40
|
-
? [`Policy documents: ${sentPolicies} sent without a preview, so which of them changed is not known`]
|
|
41
|
-
: [];
|
|
42
|
-
}
|
|
43
|
-
const named = (action) => operations.filter((op) => op.action === action).map((op) => op.name).sort();
|
|
44
|
-
const created = named('create');
|
|
45
|
-
const updated = named('update');
|
|
46
|
-
const unchanged = named('unchanged');
|
|
47
|
-
const parts = [
|
|
48
|
-
created.length > 0 && `${created.length} created (${created.join(', ')})`,
|
|
49
|
-
updated.length > 0 && `${updated.length} updated (${updated.join(', ')})`,
|
|
50
|
-
unchanged.length > 0 && `${unchanged.length} unchanged`,
|
|
51
|
-
].filter(Boolean);
|
|
52
|
-
return parts.length > 0 ? [`Policy documents: ${parts.join(', ')}`] : [];
|
|
53
|
-
}
|
|
54
|
-
/**
|
|
55
|
-
* A push answers no run, so its `policy_check` carries the findings and results itself: the first
|
|
56
|
-
* 100 of each list, with `counts` and `total` saying what they are out of.
|
|
57
|
-
*/
|
|
58
|
-
export function pushEvidence(check) {
|
|
59
|
-
return {
|
|
60
|
-
blocking: new Set((check?.blocking_findings ?? []).map(finding => finding.id ?? '').filter(Boolean)),
|
|
61
|
-
...(check?.truncated && check.counts
|
|
62
|
-
? { cut: { blocking: check.counts.blocking, findings: check.total ?? check.counts.findings, runId: check.run_id ?? 0 } }
|
|
63
|
-
: {}),
|
|
64
|
-
findings: check?.findings ?? [],
|
|
65
|
-
results: check?.results ?? [],
|
|
66
|
-
snapshot: [],
|
|
67
|
-
};
|
|
68
|
-
}
|
|
69
|
-
/**
|
|
70
|
-
* An evaluation answers the run, and its verdict names the blocking findings by id. Its summary
|
|
71
|
-
* answer (`--summary`) lists the first findings, and its counts say what they are out of.
|
|
72
|
-
*/
|
|
73
|
-
export function evaluationEvidence(evaluation) {
|
|
74
|
-
const { counts } = evaluation;
|
|
75
|
-
return {
|
|
76
|
-
blocking: new Set(evaluation.policy_check?.blocking_finding_ids ?? []),
|
|
77
|
-
...(evaluation.truncated && counts
|
|
78
|
-
? { cut: { blocking: counts.blocking, findings: evaluation.total ?? counts.findings, runId: evaluation.stored === false ? 0 : (evaluation.id ?? 0) } }
|
|
79
|
-
: {}),
|
|
80
|
-
findings: evaluation.findings ?? [],
|
|
81
|
-
results: evaluation.results ?? [],
|
|
82
|
-
snapshot: evaluation.policies ?? [],
|
|
83
|
-
};
|
|
84
|
-
}
|
|
85
|
-
/** A list's count, or `first N of M` when the answer lists only the first of them. */
|
|
86
|
-
function listed(count, all) {
|
|
87
|
-
return all !== undefined && all > count ? `first ${count} of ${all}` : String(count);
|
|
88
|
-
}
|
|
89
|
-
/** The headline's outcome. A pass whose rules inspected no object is never headlined as a plain pass. */
|
|
90
|
-
function outcome(check, results) {
|
|
91
|
-
if (check.status === 'fail')
|
|
92
|
-
return check.blocking ? 'fail (blocking findings)' : 'advisory findings (not blocking)';
|
|
93
|
-
if (check.status !== 'pass')
|
|
94
|
-
return check.status ?? '';
|
|
95
|
-
const unchecked = results.filter((result) => hasNoObjects(result)).length;
|
|
96
|
-
if (unchecked === 0)
|
|
97
|
-
return 'pass';
|
|
98
|
-
return unchecked === results.length
|
|
99
|
-
? 'pass, but no objects were checked'
|
|
100
|
-
: `pass, but ${unchecked} rule${unchecked === 1 ? '' : 's'} checked no objects`;
|
|
101
|
-
}
|
|
102
|
-
/**
|
|
103
|
-
* The feedback on stdout: a headline with the server's message, then any findings, errors and
|
|
104
|
-
* warnings. Feedback without a headline is reported by `policyCheckWarning` instead.
|
|
105
|
-
*/
|
|
106
|
-
export function policySummary(check, evidence) {
|
|
107
|
-
if (!check)
|
|
108
|
-
return [];
|
|
109
|
-
const lines = [];
|
|
110
|
-
if (isHeadlined(check)) {
|
|
111
|
-
lines.push(`Policy check: ${outcome(check, evidence.results)}`);
|
|
112
|
-
if (check.message)
|
|
113
|
-
lines.push(check.message);
|
|
114
|
-
}
|
|
115
|
-
const rules = snapshotRules(evidence.snapshot);
|
|
116
|
-
const { blocking, cut, findings } = evidence;
|
|
117
|
-
// A cut list is out of the counts; the ones the answer lists come first, blocking first.
|
|
118
|
-
const allBlocking = cut?.blocking;
|
|
119
|
-
const allAdvisory = cut ? cut.findings - cut.blocking : undefined;
|
|
120
|
-
if (cut)
|
|
121
|
-
lines.push(`Findings: ${cut.findings} (${cut.blocking} blocking, ${allAdvisory} advisory)`);
|
|
122
|
-
// The list splits only when some findings block and others do not.
|
|
123
|
-
const separated = blocking.size > 0 && findings.some(finding => !blocking.has(finding.id ?? ''));
|
|
124
|
-
if (separated) {
|
|
125
|
-
const blocked = findings.filter(finding => blocking.has(finding.id ?? ''));
|
|
126
|
-
const advisory = findings.filter(finding => !blocking.has(finding.id ?? ''));
|
|
127
|
-
lines.push(`Blocking findings (${listed(blocked.length, allBlocking)}) — active mandatory policies; a gate refuses a change that introduces one or changes its object:`, ...blocked.map(finding => findingLine(finding, rules)), `Advisory findings (${listed(advisory.length, allAdvisory)}) — reported, not blocking:`, ...advisory.map(finding => findingLine(finding, rules)));
|
|
128
|
-
}
|
|
129
|
-
else {
|
|
130
|
-
if (blocking.size > 0 && findings.length > 0)
|
|
131
|
-
lines.push(`Blocking findings (${listed(findings.length, allBlocking)}) — active mandatory policies; a gate refuses a change that introduces one or changes its object:`);
|
|
132
|
-
// Without a headline (an `error` status, say) nothing else says what these findings are.
|
|
133
|
-
else if (findings.length > 0 && !isHeadlined(check))
|
|
134
|
-
lines.push(`Advisory findings (${listed(findings.length, allAdvisory)}) — reported, not blocking:`);
|
|
135
|
-
lines.push(...findings.map(finding => findingLine(finding, rules)));
|
|
136
|
-
}
|
|
137
|
-
if (cut) {
|
|
138
|
-
lines.push(`Listed: the first ${findings.length} of ${cut.findings}; ${cut.runId > 0
|
|
139
|
-
? `\`xano policy runs ${cut.runId}\` has them all.`
|
|
140
|
-
: 'this evaluation was not stored, so the rest cannot be listed.'}`);
|
|
141
|
-
}
|
|
142
|
-
lines.push(...policyResultSummary(evidence.results));
|
|
143
|
-
return lines;
|
|
144
|
-
}
|
|
145
|
-
/**
|
|
146
|
-
* The platform's one notice that a push or its dry run left policy files out
|
|
147
|
-
* (`policies_skipped.message`): a push to a remote tenant or to a tenant without a policy table, a
|
|
148
|
-
* `release push` by a caller without `workspace:policy` create and update (its release carries the
|
|
149
|
-
* live branch's policies instead), and any push while the Policies feature is off. `null` when it
|
|
150
|
-
* left none out.
|
|
151
|
-
*/
|
|
152
|
-
export function policiesSkippedNotice(answer) {
|
|
153
|
-
const skipped = answer && typeof answer === 'object' ? answer.policies_skipped : undefined;
|
|
154
|
-
const message = skipped && typeof skipped === 'object' ? skipped.message : undefined;
|
|
155
|
-
return typeof message === 'string' && message.trim() !== '' ? message.trim() : null;
|
|
156
|
-
}
|
|
157
|
-
/**
|
|
158
|
-
* The line for an evaluation that ran but was not recorded (the credential may read, not write), or
|
|
159
|
-
* `null`. A branch with nothing to evaluate stores nothing either, and its headline says so.
|
|
160
|
-
*/
|
|
161
|
-
export function notStoredLine(evaluation) {
|
|
162
|
-
if (evaluation.stored !== false || !['error', 'fail', 'pass'].includes(evaluation.policy_check?.status ?? ''))
|
|
163
|
-
return null;
|
|
164
|
-
return 'Not stored: this credential can run checks but not record runs, so `xano policy status` and `xano policy runs` will not show this one.';
|
|
165
|
-
}
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import type { PolicyFinding, PolicyRuleResult, PolicySnapshotPolicy, PolicySnapshotRule } from './types.js';
|
|
2
|
-
/**
|
|
3
|
-
* The platform's naming rule, shared with Studio: the author's title, then the check's human
|
|
4
|
-
* label, then the rule id. Pass `label` from the run snapshot, or from the catalogue entry for
|
|
5
|
-
* `check` when the caller already holds the catalogue; never fetch one just to name a rule.
|
|
6
|
-
*/
|
|
7
|
-
export declare function policyRuleName(rule: PolicySnapshotRule): string;
|
|
8
|
-
/** Collision-free regardless of what a key or rule id contains. */
|
|
9
|
-
export declare const ruleKey: (policyKey?: string, ruleId?: string) => string;
|
|
10
|
-
export declare function snapshotRules(policies: PolicySnapshotPolicy[]): Map<string, PolicySnapshotRule>;
|
|
11
|
-
/**
|
|
12
|
-
* One finding line. The rule id leads it, because the check's label is shared by every
|
|
13
|
-
* rule that uses that check — with two rules of one policy failing, the labels alone do
|
|
14
|
-
* not tell them apart, and the id is what `--run-detail` and the JSON cite.
|
|
15
|
-
*/
|
|
16
|
-
export declare function findingLine(finding: PolicyFinding, rules: Map<string, PolicySnapshotRule>): string;
|
|
17
|
-
/** A completed rule with no objects checked, including older stored pass + checked 0 results. */
|
|
18
|
-
export declare function hasNoObjects(result: PolicyRuleResult): boolean;
|
|
19
|
-
/**
|
|
20
|
-
* Rule errors ("this rule could not run") and scope warnings ("part of your scope selected
|
|
21
|
-
* nothing") under their own headings, then the rules that inspected nothing.
|
|
22
|
-
*/
|
|
23
|
-
export declare function policyResultSummary(results?: PolicyRuleResult[]): string[];
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The platform's naming rule, shared with Studio: the author's title, then the check's human
|
|
3
|
-
* label, then the rule id. Pass `label` from the run snapshot, or from the catalogue entry for
|
|
4
|
-
* `check` when the caller already holds the catalogue; never fetch one just to name a rule.
|
|
5
|
-
*/
|
|
6
|
-
export function policyRuleName(rule) {
|
|
7
|
-
return rule.title?.trim() || rule.label?.trim() || rule.id?.trim() || '';
|
|
8
|
-
}
|
|
9
|
-
/** Collision-free regardless of what a key or rule id contains. */
|
|
10
|
-
export const ruleKey = (policyKey = '', ruleId = '') => JSON.stringify([policyKey, ruleId]);
|
|
11
|
-
export function snapshotRules(policies) {
|
|
12
|
-
return new Map(policies.flatMap(policy => (policy.rules ?? []).map(rule => [ruleKey(policy.key, rule.id), rule])));
|
|
13
|
-
}
|
|
14
|
-
/** A finding names its rule the way the platform does, falling back to what the run recorded. */
|
|
15
|
-
function findingName(finding, rules) {
|
|
16
|
-
const recorded = rules.get(ruleKey(finding.policy_key, finding.rule_id)) ?? {};
|
|
17
|
-
return policyRuleName({
|
|
18
|
-
...recorded,
|
|
19
|
-
id: finding.rule_id ?? recorded.id,
|
|
20
|
-
title: finding.rule_title || recorded.title,
|
|
21
|
-
}) || 'finding';
|
|
22
|
-
}
|
|
23
|
-
/**
|
|
24
|
-
* One finding line. The rule id leads it, because the check's label is shared by every
|
|
25
|
-
* rule that uses that check — with two rules of one policy failing, the labels alone do
|
|
26
|
-
* not tell them apart, and the id is what `--run-detail` and the JSON cite.
|
|
27
|
-
*/
|
|
28
|
-
export function findingLine(finding, rules) {
|
|
29
|
-
// A rule the author left unnamed is called by its check's label, and only then by its id.
|
|
30
|
-
const rule = findingName(finding, rules);
|
|
31
|
-
const id = finding.rule_id?.trim();
|
|
32
|
-
const policy = finding.policy_title || finding.policy_key || 'policy';
|
|
33
|
-
const severity = finding.severity?.trim() ? ` [${finding.severity.trim()}]` : '';
|
|
34
|
-
return ` ${[id, id === rule ? '' : rule].filter(Boolean).join(' ')}${severity} (${policy}) ${finding.object?.type ?? ''} ${finding.object?.name ?? ''}: ${finding.message ?? ''}`;
|
|
35
|
-
}
|
|
36
|
-
/** A completed rule with no objects checked, including older stored pass + checked 0 results. */
|
|
37
|
-
export function hasNoObjects(result) {
|
|
38
|
-
return ['no_objects', 'pass'].includes(result.status ?? '') && (result.checked ?? 0) === 0;
|
|
39
|
-
}
|
|
40
|
-
/**
|
|
41
|
-
* Rule errors ("this rule could not run") and scope warnings ("part of your scope selected
|
|
42
|
-
* nothing") under their own headings, then the rules that inspected nothing.
|
|
43
|
-
*/
|
|
44
|
-
export function policyResultSummary(results = []) {
|
|
45
|
-
const where = (result) => `${result.policy_key ?? 'policy'} ${result.check_id ?? 'rule'}`;
|
|
46
|
-
const errors = results.filter(result => result.status === 'error')
|
|
47
|
-
.map(result => ` ${where(result)}: ${result.message ?? 'No diagnostic returned.'}`);
|
|
48
|
-
const warnings = results.flatMap(result => (result.warnings ?? []).map(warning => ` ${where(result)}: ${warning}`));
|
|
49
|
-
const lines = [];
|
|
50
|
-
if (errors.length > 0)
|
|
51
|
-
lines.push('Errors:', ...errors);
|
|
52
|
-
if (warnings.length > 0)
|
|
53
|
-
lines.push('Warnings:', ...warnings);
|
|
54
|
-
const checked = results.reduce((sum, result) => sum + (result.checked ?? 0), 0);
|
|
55
|
-
// When no rule checked anything, one sentence about the run says it all.
|
|
56
|
-
if (results.length > 0 && checked === 0) {
|
|
57
|
-
lines.push('No objects checked; this run does not demonstrate coverage.');
|
|
58
|
-
return lines;
|
|
59
|
-
}
|
|
60
|
-
const empty = results.filter(result => hasNoObjects(result));
|
|
61
|
-
if (empty.length > 0) {
|
|
62
|
-
lines.push('No objects checked (proves nothing about coverage):', ...empty.map(result => ` ${where(result)}: no objects checked`));
|
|
63
|
-
}
|
|
64
|
-
return lines;
|
|
65
|
-
}
|
|
@@ -1,161 +0,0 @@
|
|
|
1
|
-
import type { ProfileConfig } from '../../base-command.js';
|
|
2
|
-
import type { PolicyFinding } from './types.js';
|
|
3
|
-
/** The release the tenant deploy gate judges. */
|
|
4
|
-
export interface GateRelease {
|
|
5
|
-
/** Whether the release was cut with its policies; one cut without them has no check. */
|
|
6
|
-
carried?: boolean;
|
|
7
|
-
counts?: null | {
|
|
8
|
-
advisory?: number;
|
|
9
|
-
blocking?: number;
|
|
10
|
-
errors?: number;
|
|
11
|
-
findings?: number;
|
|
12
|
-
};
|
|
13
|
-
id?: number;
|
|
14
|
-
name?: string;
|
|
15
|
-
/** The release's stored policy run, 0 when it has none. */
|
|
16
|
-
run_id?: number;
|
|
17
|
-
status?: string;
|
|
18
|
-
}
|
|
19
|
-
/**
|
|
20
|
-
* The tenant deploy gate's answer: what `GET tenant/{name}/policy_gate` previews, and the payload of
|
|
21
|
-
* the 403 a refused deploy answers (with `code: policy_gate`). The gate judges the release alone, by
|
|
22
|
-
* its own policy check, so the same release gets the same verdict on every tenant; only `gated`
|
|
23
|
-
* differs. The findings come only to a credential that reads the workspace's policies.
|
|
24
|
-
*/
|
|
25
|
-
export interface PolicyGateAnswer {
|
|
26
|
-
can_override?: boolean;
|
|
27
|
-
code?: string;
|
|
28
|
-
/** The first of the release's blocking findings. */
|
|
29
|
-
findings?: PolicyFinding[];
|
|
30
|
-
/** `tenant_deploy`, or `tenant_approve` when an approval completes the deploy. */
|
|
31
|
-
gate?: string;
|
|
32
|
-
/** False for an ephemeral or sandbox tenant: the verdict is reported but never refuses. */
|
|
33
|
-
gated?: boolean;
|
|
34
|
-
message?: string;
|
|
35
|
-
override_denied?: boolean;
|
|
36
|
-
release?: GateRelease | null;
|
|
37
|
-
run_id?: number;
|
|
38
|
-
/** `pass`, `blocked`, `overridden`, `not_applicable`, `not_carried`, `disabled` or `unavailable`. */
|
|
39
|
-
status?: string;
|
|
40
|
-
/** Every blocking finding the release has, listed or not. */
|
|
41
|
-
total?: number;
|
|
42
|
-
truncated?: boolean;
|
|
43
|
-
}
|
|
44
|
-
/**
|
|
45
|
-
* A gate that measures a change against the live branch (set-live, publish, a live-branch save): its
|
|
46
|
-
* blocking findings split by why they block.
|
|
47
|
-
*/
|
|
48
|
-
export interface LiveGateAnswer extends Omit<PolicyGateAnswer, 'gated' | 'release'> {
|
|
49
|
-
changed?: number;
|
|
50
|
-
existing?: number;
|
|
51
|
-
introduced?: number;
|
|
52
|
-
}
|
|
53
|
-
/** The refusal code a policy gate's 403 carries in `payload.code`. */
|
|
54
|
-
export declare const GATE_REFUSAL = "policy_gate";
|
|
55
|
-
/** 2 when the gate refuses this deploy (blocked on a gated tenant); every other verdict is 0. */
|
|
56
|
-
export declare function gateExitCode(answer?: null | PolicyGateAnswer): number;
|
|
57
|
-
/**
|
|
58
|
-
* The gate's verdict on stdout: a headline with its sentence, the release it judged, how many of its
|
|
59
|
-
* findings block, and the first of them. Every blocking finding of the release counts, whatever the
|
|
60
|
-
* tenant runs today.
|
|
61
|
-
*/
|
|
62
|
-
export declare function gateLines(answer: PolicyGateAnswer): string[];
|
|
63
|
-
/**
|
|
64
|
-
* The line a completed deploy prints about its gate. A blocked verdict on an ephemeral or sandbox
|
|
65
|
-
* tenant refused nothing (the deploy went ahead), so it says so rather than reading as a failure.
|
|
66
|
-
*/
|
|
67
|
-
export declare function deployedGateLine(answer: PolicyGateAnswer): string;
|
|
68
|
-
/**
|
|
69
|
-
* The verdict of a gate that measures a change against the live branch (set-live, publish): its
|
|
70
|
-
* status, what blocks, and the first blocking findings. `subject` is what changes: a branch set live,
|
|
71
|
-
* the branch a release was deployed as (`release deploy --set_live`), or an object saved to live.
|
|
72
|
-
*/
|
|
73
|
-
export declare function liveGateLines(answer: LiveGateAnswer, subject: 'branch' | 'release' | 'save'): string[];
|
|
74
|
-
/**
|
|
75
|
-
* A save the live-branch publish gate refused (`function create`, `function edit`): what goes on
|
|
76
|
-
* stdout (the verdict, or under `-o json` `{created|updated: false, message, policy_gate}`) and the
|
|
77
|
-
* error the command exits 2 with, saying nothing was saved and how to proceed (`rerun` is the same
|
|
78
|
-
* command with `--policy-override`).
|
|
79
|
-
*/
|
|
80
|
-
export declare function publishRefusal(refused: GateRefused, options: {
|
|
81
|
-
json: boolean;
|
|
82
|
-
outcome: 'created' | 'updated';
|
|
83
|
-
rerun: string;
|
|
84
|
-
}): {
|
|
85
|
-
lines: string[];
|
|
86
|
-
message: string;
|
|
87
|
-
};
|
|
88
|
-
/** The refusal of a `--policy-override` given without a reason, before any request is made. */
|
|
89
|
-
export declare const BLANK_POLICY_OVERRIDE = "--policy-override needs a reason: say why this change may proceed past the policy gate.";
|
|
90
|
-
/** True when `--policy-override` was given but holds only whitespace. */
|
|
91
|
-
export declare function blankPolicyOverride(reason?: string): boolean;
|
|
92
|
-
/** A value quoted for the shell when it needs it. */
|
|
93
|
-
export declare function quoted(value: string): string;
|
|
94
|
-
/**
|
|
95
|
-
* What to do about a refusal: override it (the credential may, and `command` is how), or ask for
|
|
96
|
-
* the permission. A refusal whose override was denied already says so in its message.
|
|
97
|
-
*/
|
|
98
|
-
export declare function gateOverrideHint(answer: PolicyGateAnswer, command: string): string;
|
|
99
|
-
/** A deploy the policy gate refused: the platform's sentence and the gate's answer. */
|
|
100
|
-
export interface GateRefused {
|
|
101
|
-
answer: PolicyGateAnswer;
|
|
102
|
-
message: string;
|
|
103
|
-
}
|
|
104
|
-
/**
|
|
105
|
-
* What a command the gate refused prints on stdout: the verdict, or under `-o json` the refusal as
|
|
106
|
-
* `{deployed: false, message, policy_gate}`. The command then exits 2 with the message.
|
|
107
|
-
*/
|
|
108
|
-
export declare function gateRefusalOutput(refused: GateRefused, json: boolean): string[];
|
|
109
|
-
/** A policy gate's refusal read from a failed response: its message and answer, or `null` for any other failure. */
|
|
110
|
-
export declare function gateRefusal(response: Response): Promise<GateRefused | null>;
|
|
111
|
-
/**
|
|
112
|
-
* A change refused because it weakens a policy active and mandatory on the branch, from a caller
|
|
113
|
-
* without the `workspace:policy` update permission (`policy_weakening_permission_required`). A
|
|
114
|
-
* release deploy or archive import with set live adds the branch it landed, which stays.
|
|
115
|
-
*/
|
|
116
|
-
export interface WeakeningRefused {
|
|
117
|
-
message: string;
|
|
118
|
-
/** `unavailable`: the policies could not be read to check it, so only the permission proceeds (`policies` is empty). */
|
|
119
|
-
payload: {
|
|
120
|
-
branch?: {
|
|
121
|
-
id?: number;
|
|
122
|
-
label?: string;
|
|
123
|
-
};
|
|
124
|
-
code: string;
|
|
125
|
-
gate?: string;
|
|
126
|
-
level?: string;
|
|
127
|
-
permission?: string;
|
|
128
|
-
policies?: string[];
|
|
129
|
-
unavailable?: boolean;
|
|
130
|
-
};
|
|
131
|
-
}
|
|
132
|
-
/** The weakening refusal read from a failed response, or `null` for any other failure. */
|
|
133
|
-
export declare function weakeningRefusal(response: Response): Promise<null | WeakeningRefused>;
|
|
134
|
-
/** What a gate preview needs from the command that asks for it. */
|
|
135
|
-
export interface GateHost {
|
|
136
|
-
verboseFetch(url: string, options: RequestInit, verbose: boolean, authToken?: string): Promise<Response>;
|
|
137
|
-
}
|
|
138
|
-
/** A preview the platform could not answer: the HTTP status, its message and the refusal's payload. */
|
|
139
|
-
export interface GateUnavailable {
|
|
140
|
-
message: string;
|
|
141
|
-
payload?: unknown;
|
|
142
|
-
status: number;
|
|
143
|
-
}
|
|
144
|
-
/**
|
|
145
|
-
* `GET workspace/{ws}/tenant/{tenant}/policy_gate?release_name=`: what the gate would decide for the
|
|
146
|
-
* release on the tenant, without deploying. A refusal (the Policies feature is off, the credential
|
|
147
|
-
* cannot read policies, no such tenant or release) is answered as `unavailable` rather than thrown.
|
|
148
|
-
*/
|
|
149
|
-
export declare function fetchPolicyGate(host: GateHost, target: {
|
|
150
|
-
profile: ProfileConfig;
|
|
151
|
-
release: string;
|
|
152
|
-
tenant: string;
|
|
153
|
-
verbose: boolean;
|
|
154
|
-
workspace: string;
|
|
155
|
-
}): Promise<{
|
|
156
|
-
answer: PolicyGateAnswer;
|
|
157
|
-
} | {
|
|
158
|
-
unavailable: GateUnavailable;
|
|
159
|
-
}>;
|
|
160
|
-
/** Why a preview is not available, in a few words for a line that must not fail its command. */
|
|
161
|
-
export declare function unavailableReason(unavailable: GateUnavailable): string;
|
|
@@ -1,201 +0,0 @@
|
|
|
1
|
-
import { describePolicyError } from './errors.js';
|
|
2
|
-
import { findingLine } from './findings.js';
|
|
3
|
-
import { WEAKENING_REFUSAL } from './permission.js';
|
|
4
|
-
/** The refusal code a policy gate's 403 carries in `payload.code`. */
|
|
5
|
-
export const GATE_REFUSAL = 'policy_gate';
|
|
6
|
-
/** 2 when the gate refuses this deploy (blocked on a gated tenant); every other verdict is 0. */
|
|
7
|
-
export function gateExitCode(answer) {
|
|
8
|
-
return answer?.status === 'blocked' && answer.gated !== false ? 2 : 0;
|
|
9
|
-
}
|
|
10
|
-
function releaseLine(release, status) {
|
|
11
|
-
if (!release)
|
|
12
|
-
return ' Release: unknown';
|
|
13
|
-
const name = release.name?.trim() || (release.id ? `#${release.id}` : 'unknown');
|
|
14
|
-
const detail = status === 'not_carried' || release.status === 'not_carried'
|
|
15
|
-
? 'cut without its policies'
|
|
16
|
-
: [release.run_id ? `policy run ${release.run_id}` : '', release.status ?? ''].filter(Boolean).join(', ');
|
|
17
|
-
return ` Release: ${name}${detail ? ` (${detail})` : ''}`;
|
|
18
|
-
}
|
|
19
|
-
/** The first of the release's blocking findings, and where the rest of its findings are. */
|
|
20
|
-
function findingLines(answer) {
|
|
21
|
-
const findings = answer.findings ?? [];
|
|
22
|
-
if (findings.length === 0) {
|
|
23
|
-
const withheld = answer.findings === undefined && (answer.total ?? 0) > 0;
|
|
24
|
-
return withheld ? [' The findings are listed only for a credential that reads policies (workspace:policy read).'] : [];
|
|
25
|
-
}
|
|
26
|
-
const total = answer.total ?? findings.length;
|
|
27
|
-
const releaseName = answer.release?.name?.trim();
|
|
28
|
-
return [
|
|
29
|
-
` Blocking findings (${total > findings.length ? `first ${findings.length} of ${total}` : findings.length}):`,
|
|
30
|
-
...findings.map(finding => ` ${findingLine(finding, new Map())}`),
|
|
31
|
-
...(releaseName ? [` Every finding of the release: xano policy runs --release ${quoted(releaseName)}`] : []),
|
|
32
|
-
];
|
|
33
|
-
}
|
|
34
|
-
/**
|
|
35
|
-
* The gate's verdict on stdout: a headline with its sentence, the release it judged, how many of its
|
|
36
|
-
* findings block, and the first of them. Every blocking finding of the release counts, whatever the
|
|
37
|
-
* tenant runs today.
|
|
38
|
-
*/
|
|
39
|
-
export function gateLines(answer) {
|
|
40
|
-
const status = answer.status?.trim() || 'unknown';
|
|
41
|
-
const lines = [`Policy gate: ${status}`];
|
|
42
|
-
if (answer.message?.trim())
|
|
43
|
-
lines.push(` ${answer.message.trim()}`);
|
|
44
|
-
if (answer.release !== undefined)
|
|
45
|
-
lines.push(releaseLine(answer.release, status));
|
|
46
|
-
// Listed findings carry their own count; otherwise say how many there are.
|
|
47
|
-
if (['blocked', 'overridden', 'pass'].includes(status) && typeof answer.total === 'number' && (answer.findings ?? []).length === 0) {
|
|
48
|
-
lines.push(` Blocking findings in this release: ${answer.total}`);
|
|
49
|
-
}
|
|
50
|
-
lines.push(...findingLines(answer));
|
|
51
|
-
if (answer.gated === false)
|
|
52
|
-
lines.push(' Not gated: this tenant is ephemeral or a sandbox, so the verdict never refuses a deploy to it.');
|
|
53
|
-
return lines;
|
|
54
|
-
}
|
|
55
|
-
/**
|
|
56
|
-
* The line a completed deploy prints about its gate. A blocked verdict on an ephemeral or sandbox
|
|
57
|
-
* tenant refused nothing (the deploy went ahead), so it says so rather than reading as a failure.
|
|
58
|
-
*/
|
|
59
|
-
export function deployedGateLine(answer) {
|
|
60
|
-
const status = answer.status?.trim() || 'unknown';
|
|
61
|
-
return status === 'blocked' && answer.gated === false
|
|
62
|
-
? 'Policy gate: blocked (not gated: this tenant is ephemeral or a sandbox, so the deploy went ahead)'
|
|
63
|
-
: `Policy gate: ${status}`;
|
|
64
|
-
}
|
|
65
|
-
/**
|
|
66
|
-
* The verdict of a gate that measures a change against the live branch (set-live, publish): its
|
|
67
|
-
* status, what blocks, and the first blocking findings. `subject` is what changes: a branch set live,
|
|
68
|
-
* the branch a release was deployed as (`release deploy --set_live`), or an object saved to live.
|
|
69
|
-
*/
|
|
70
|
-
export function liveGateLines(answer, subject) {
|
|
71
|
-
const lines = [`Policy gate: ${answer.status?.trim() || 'unknown'}`];
|
|
72
|
-
if (typeof answer.total === 'number') {
|
|
73
|
-
lines.push(` Blocking findings: ${answer.total} (${answer.introduced ?? 0} introduced, ${answer.changed ?? 0} on objects the ${subject} changes); ${answer.existing ?? 0} already on the live branch never block`);
|
|
74
|
-
}
|
|
75
|
-
const findings = answer.findings ?? [];
|
|
76
|
-
if (findings.length > 0) {
|
|
77
|
-
lines.push(...findings.map(finding => ` ${findingLine(finding, new Map())}`));
|
|
78
|
-
if (answer.truncated)
|
|
79
|
-
lines.push(' Only the first findings are listed.');
|
|
80
|
-
}
|
|
81
|
-
else if (answer.findings === undefined && (answer.total ?? 0) > 0) {
|
|
82
|
-
lines.push(' The findings are listed only for a credential that reads policies (workspace:policy read).');
|
|
83
|
-
}
|
|
84
|
-
return lines;
|
|
85
|
-
}
|
|
86
|
-
/**
|
|
87
|
-
* A save the live-branch publish gate refused (`function create`, `function edit`): what goes on
|
|
88
|
-
* stdout (the verdict, or under `-o json` `{created|updated: false, message, policy_gate}`) and the
|
|
89
|
-
* error the command exits 2 with, saying nothing was saved and how to proceed (`rerun` is the same
|
|
90
|
-
* command with `--policy-override`).
|
|
91
|
-
*/
|
|
92
|
-
export function publishRefusal(refused, options) {
|
|
93
|
-
const lines = options.json
|
|
94
|
-
? [JSON.stringify({ message: refused.message, [options.outcome]: false, policy_gate: refused.answer }, null, 2)]
|
|
95
|
-
: liveGateLines(refused.answer, 'save');
|
|
96
|
-
return { lines, message: `Nothing was saved. ${refused.message}${gateOverrideHint(refused.answer, options.rerun)}` };
|
|
97
|
-
}
|
|
98
|
-
/** The refusal of a `--policy-override` given without a reason, before any request is made. */
|
|
99
|
-
export const BLANK_POLICY_OVERRIDE = '--policy-override needs a reason: say why this change may proceed past the policy gate.';
|
|
100
|
-
/** True when `--policy-override` was given but holds only whitespace. */
|
|
101
|
-
export function blankPolicyOverride(reason) {
|
|
102
|
-
return reason !== undefined && !reason.trim();
|
|
103
|
-
}
|
|
104
|
-
/** A value quoted for the shell when it needs it. */
|
|
105
|
-
export function quoted(value) {
|
|
106
|
-
return /^[\w.@%+=:,/-]+$/.test(value) ? value : `"${value.replaceAll(/(["\\$`])/g, String.raw `\$1`)}"`;
|
|
107
|
-
}
|
|
108
|
-
/**
|
|
109
|
-
* What to do about a refusal: override it (the credential may, and `command` is how), or ask for
|
|
110
|
-
* the permission. A refusal whose override was denied already says so in its message.
|
|
111
|
-
*/
|
|
112
|
-
export function gateOverrideHint(answer, command) {
|
|
113
|
-
if (answer.override_denied)
|
|
114
|
-
return '';
|
|
115
|
-
if (answer.can_override)
|
|
116
|
-
return `\nTo proceed past the policy gate with a reason (audited): ${command}`;
|
|
117
|
-
return '\nOverriding the policy gate needs the `workspace:policy` update permission; ask someone who has it.';
|
|
118
|
-
}
|
|
119
|
-
/**
|
|
120
|
-
* What a command the gate refused prints on stdout: the verdict, or under `-o json` the refusal as
|
|
121
|
-
* `{deployed: false, message, policy_gate}`. The command then exits 2 with the message.
|
|
122
|
-
*/
|
|
123
|
-
export function gateRefusalOutput(refused, json) {
|
|
124
|
-
return json
|
|
125
|
-
? [JSON.stringify({ deployed: false, message: refused.message, policy_gate: refused.answer }, null, 2)]
|
|
126
|
-
: gateLines(refused.answer);
|
|
127
|
-
}
|
|
128
|
-
/** A 403 whose `payload.code` is `code`: its trimmed message (`''` for none) and payload, or `null` for any other failure. The body is left unread. */
|
|
129
|
-
async function coded403(response, code) {
|
|
130
|
-
if (response.status !== 403)
|
|
131
|
-
return null;
|
|
132
|
-
let body;
|
|
133
|
-
try {
|
|
134
|
-
body = JSON.parse(await response.clone().text());
|
|
135
|
-
}
|
|
136
|
-
catch {
|
|
137
|
-
return null;
|
|
138
|
-
}
|
|
139
|
-
const payload = body && typeof body === 'object' ? body.payload : undefined;
|
|
140
|
-
if (!payload || typeof payload !== 'object' || payload.code !== code)
|
|
141
|
-
return null;
|
|
142
|
-
const { message } = body;
|
|
143
|
-
return { message: typeof message === 'string' ? message.trim() : '', payload: payload };
|
|
144
|
-
}
|
|
145
|
-
/** A policy gate's refusal read from a failed response: its message and answer, or `null` for any other failure. */
|
|
146
|
-
export async function gateRefusal(response) {
|
|
147
|
-
const refused = await coded403(response, GATE_REFUSAL);
|
|
148
|
-
if (!refused)
|
|
149
|
-
return null;
|
|
150
|
-
const answer = refused.payload;
|
|
151
|
-
return { answer, message: refused.message || (answer.message ?? 'Refused by the policy gate.') };
|
|
152
|
-
}
|
|
153
|
-
/** The weakening refusal read from a failed response, or `null` for any other failure. */
|
|
154
|
-
export async function weakeningRefusal(response) {
|
|
155
|
-
const refused = await coded403(response, WEAKENING_REFUSAL);
|
|
156
|
-
if (!refused)
|
|
157
|
-
return null;
|
|
158
|
-
return { message: refused.message || 'Refused: the change weakens a mandatory policy.', payload: refused.payload };
|
|
159
|
-
}
|
|
160
|
-
/**
|
|
161
|
-
* `GET workspace/{ws}/tenant/{tenant}/policy_gate?release_name=`: what the gate would decide for the
|
|
162
|
-
* release on the tenant, without deploying. A refusal (the Policies feature is off, the credential
|
|
163
|
-
* cannot read policies, no such tenant or release) is answered as `unavailable` rather than thrown.
|
|
164
|
-
*/
|
|
165
|
-
export async function fetchPolicyGate(host, target) {
|
|
166
|
-
const { profile, release, tenant, verbose, workspace } = target;
|
|
167
|
-
const query = new URLSearchParams({ release_name: release });
|
|
168
|
-
const url = `${profile.instance_origin}/api:meta/workspace/${workspace}/tenant/${encodeURIComponent(tenant)}/policy_gate?${query}`;
|
|
169
|
-
const response = await host.verboseFetch(url, { headers: { accept: 'application/json', Authorization: `Bearer ${profile.access_token}` }, method: 'GET' }, verbose, profile.access_token);
|
|
170
|
-
const text = (await response.text()).replaceAll(profile.access_token, '[REDACTED]');
|
|
171
|
-
if (!response.ok) {
|
|
172
|
-
const { message, payload } = describePolicyError(text, response.status, url);
|
|
173
|
-
return { unavailable: { message, payload, status: response.status } };
|
|
174
|
-
}
|
|
175
|
-
try {
|
|
176
|
-
return { answer: JSON.parse(text) };
|
|
177
|
-
}
|
|
178
|
-
catch {
|
|
179
|
-
return { unavailable: { message: `The policy gate answered a ${response.status} that is not JSON.`, status: response.status } };
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
/** Why a preview is not available, in a few words for a line that must not fail its command. */
|
|
183
|
-
export function unavailableReason(unavailable) {
|
|
184
|
-
const code = unavailable.payload && typeof unavailable.payload === 'object' ? unavailable.payload.code : undefined;
|
|
185
|
-
switch (code) {
|
|
186
|
-
case 'policy_feature_disabled': {
|
|
187
|
-
return 'the Policies feature is off on this instance';
|
|
188
|
-
}
|
|
189
|
-
case 'policy_permission_required': {
|
|
190
|
-
return 'your role lacks the `workspace:policy` read permission';
|
|
191
|
-
}
|
|
192
|
-
case 'policy_scope_required': {
|
|
193
|
-
return 'this Metadata API token lacks the `workspace:policy` scope';
|
|
194
|
-
}
|
|
195
|
-
default: {
|
|
196
|
-
if (unavailable.status === 401)
|
|
197
|
-
return 'the Metadata API token was not accepted';
|
|
198
|
-
return unavailable.message.replace(/^[a-z_]+: /i, '') || `the server answered ${unavailable.status}`;
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
}
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
/** Goals and sparse parameter seeds come from the instance's catalogue. */
|
|
2
|
-
export interface PolicyGoal {
|
|
3
|
-
goal: string;
|
|
4
|
-
id: string;
|
|
5
|
-
key: string;
|
|
6
|
-
rules: Array<{
|
|
7
|
-
check: string;
|
|
8
|
-
needs: string[];
|
|
9
|
-
params: Record<string, unknown>;
|
|
10
|
-
title: string;
|
|
11
|
-
}>;
|
|
12
|
-
severity: string;
|
|
13
|
-
summary: string;
|
|
14
|
-
}
|
|
15
|
-
/** The catalogue's `document.key`: the platform's key pattern, and its refusal with `%s` for the key. */
|
|
16
|
-
export interface PolicyKeyRule {
|
|
17
|
-
message?: string;
|
|
18
|
-
pattern?: string;
|
|
19
|
-
}
|
|
20
|
-
/** How a goal becomes a policy: the key, the catalogue's key rule and the `--param` assignments. */
|
|
21
|
-
export interface GoalPolicyOptions {
|
|
22
|
-
/** `--key`: the new policy's key; refused when a policy already holds it. */
|
|
23
|
-
key?: string;
|
|
24
|
-
/** The fetched catalogue's `document.key`; the offline pattern stands in for an instance that serves none. */
|
|
25
|
-
keyRule?: null | PolicyKeyRule;
|
|
26
|
-
/** `--param N.path=JSON` assignments. */
|
|
27
|
-
overrides?: string[];
|
|
28
|
-
}
|
|
29
|
-
/** The policy document a goal becomes, for the parse route's `data`. */
|
|
30
|
-
export interface GoalPolicyDocument {
|
|
31
|
-
key: string;
|
|
32
|
-
rules: Array<{
|
|
33
|
-
check: string;
|
|
34
|
-
params: Record<string, unknown>;
|
|
35
|
-
title: string;
|
|
36
|
-
}>;
|
|
37
|
-
severity: string;
|
|
38
|
-
statement: string;
|
|
39
|
-
title: string;
|
|
40
|
-
}
|
|
41
|
-
/**
|
|
42
|
-
* The policy document for a goal: its sparse settings with the `--param` assignments applied, every
|
|
43
|
-
* `needs` path filled, and a key no policy on the branch holds (compared case-insensitively). A taken
|
|
44
|
-
* goal key gets a free numeric suffix; a taken or malformed `--key` is refused.
|
|
45
|
-
*/
|
|
46
|
-
export declare function policyFromGoal(goal: PolicyGoal, takenKeys: string[], options?: GoalPolicyOptions): GoalPolicyDocument;
|