@xano/cli 1.3.3 → 1.3.5-beta.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/README.md +137 -3
- package/dist/base-command.d.ts +12 -1
- package/dist/base-command.js +36 -22
- package/dist/commands/branch/set_live/index.d.ts +14 -0
- package/dist/commands/branch/set_live/index.js +38 -0
- package/dist/commands/ephemeral/push/index.js +6 -1
- package/dist/commands/function/create/index.d.ts +15 -0
- package/dist/commands/function/create/index.js +39 -0
- package/dist/commands/function/edit/index.d.ts +15 -0
- package/dist/commands/function/edit/index.js +39 -0
- package/dist/commands/function/run/index.js +5 -11
- package/dist/commands/policy/catalogue/index.d.ts +16 -0
- package/dist/commands/policy/catalogue/index.js +60 -0
- package/dist/commands/policy/coverage/index.d.ts +17 -0
- package/dist/commands/policy/coverage/index.js +33 -0
- package/dist/commands/policy/create/index.d.ts +18 -0
- package/dist/commands/policy/create/index.js +44 -0
- package/dist/commands/policy/delete/index.d.ts +19 -0
- package/dist/commands/policy/delete/index.js +61 -0
- package/dist/commands/policy/evaluate/index.d.ts +32 -0
- package/dist/commands/policy/evaluate/index.js +71 -0
- package/dist/commands/policy/list/index.d.ts +14 -0
- package/dist/commands/policy/list/index.js +21 -0
- package/dist/commands/policy/parse/index.d.ts +19 -0
- package/dist/commands/policy/parse/index.js +18 -0
- package/dist/commands/policy/publish/index.d.ts +20 -0
- package/dist/commands/policy/publish/index.js +38 -0
- package/dist/commands/policy/runs/index.d.ts +54 -0
- package/dist/commands/policy/runs/index.js +273 -0
- package/dist/commands/policy/status/index.d.ts +21 -0
- package/dist/commands/policy/status/index.js +54 -0
- package/dist/commands/release/create/index.js +4 -0
- package/dist/commands/release/deploy/index.d.ts +19 -0
- package/dist/commands/release/deploy/index.js +68 -6
- package/dist/commands/release/import/index.js +5 -1
- package/dist/commands/release/list/index.d.ts +7 -0
- package/dist/commands/release/list/index.js +46 -9
- package/dist/commands/release/push/index.js +19 -1
- package/dist/commands/sandbox/delete/index.js +8 -0
- package/dist/commands/sandbox/pull/index.d.ts +7 -0
- package/dist/commands/sandbox/pull/index.js +49 -0
- package/dist/commands/sandbox/push/index.d.ts +4 -0
- package/dist/commands/sandbox/push/index.js +59 -2
- package/dist/commands/sandbox/reset/index.js +8 -0
- package/dist/commands/skills/pull/index.d.ts +30 -0
- package/dist/commands/skills/pull/index.js +118 -0
- package/dist/commands/tenant/create/index.d.ts +3 -0
- package/dist/commands/tenant/create/index.js +21 -0
- package/dist/commands/tenant/deploy_release/index.d.ts +40 -0
- package/dist/commands/tenant/deploy_release/index.js +182 -4
- package/dist/commands/tenant/edit/index.d.ts +2 -0
- package/dist/commands/tenant/edit/index.js +39 -4
- package/dist/commands/tenant_deploy_request/bypass/index.d.ts +31 -0
- package/dist/commands/tenant_deploy_request/bypass/index.js +109 -0
- package/dist/commands/tenant_deploy_request/create/index.d.ts +21 -0
- package/dist/commands/tenant_deploy_request/create/index.js +108 -0
- package/dist/commands/tenant_deploy_request/edit/index.d.ts +22 -0
- package/dist/commands/tenant_deploy_request/edit/index.js +103 -0
- package/dist/commands/tenant_deploy_request/get/index.d.ts +25 -0
- package/dist/commands/tenant_deploy_request/get/index.js +113 -0
- package/dist/commands/tenant_deploy_request/list/index.d.ts +20 -0
- package/dist/commands/tenant_deploy_request/list/index.js +122 -0
- package/dist/commands/tenant_deploy_request/revision/index.d.ts +21 -0
- package/dist/commands/tenant_deploy_request/revision/index.js +83 -0
- package/dist/commands/tenant_deploy_request/set_status/index.d.ts +38 -0
- package/dist/commands/tenant_deploy_request/set_status/index.js +159 -0
- package/dist/commands/workspace/pull/index.d.ts +17 -0
- package/dist/commands/workspace/pull/index.js +124 -7
- package/dist/commands/workspace/push/index.d.ts +23 -0
- package/dist/commands/workspace/push/index.js +188 -6
- package/dist/policy-command.d.ts +91 -0
- package/dist/policy-command.js +95 -0
- package/dist/utils/document-parser.d.ts +10 -0
- package/dist/utils/document-parser.js +21 -0
- package/dist/utils/export-compare.d.ts +52 -0
- package/dist/utils/export-compare.js +134 -0
- package/dist/utils/local-config.d.ts +23 -0
- package/dist/utils/local-config.js +62 -2
- package/dist/utils/multidoc-push.d.ts +124 -4
- package/dist/utils/multidoc-push.js +670 -49
- package/dist/utils/object-fetch.d.ts +86 -0
- package/dist/utils/object-fetch.js +265 -0
- package/dist/utils/policy/catalogue.d.ts +85 -0
- package/dist/utils/policy/catalogue.js +196 -0
- package/dist/utils/policy/coverage.d.ts +19 -0
- package/dist/utils/policy/coverage.js +93 -0
- package/dist/utils/policy/errors.d.ts +20 -0
- package/dist/utils/policy/errors.js +93 -0
- package/dist/utils/policy/feedback.d.ts +90 -0
- package/dist/utils/policy/feedback.js +210 -0
- package/dist/utils/policy/findings.d.ts +29 -0
- package/dist/utils/policy/findings.js +82 -0
- package/dist/utils/policy/gate.d.ts +138 -0
- package/dist/utils/policy/gate.js +193 -0
- package/dist/utils/policy/permission.d.ts +7 -0
- package/dist/utils/policy/permission.js +55 -0
- package/dist/utils/policy/push-gate.d.ts +5 -0
- package/dist/utils/policy/push-gate.js +43 -0
- package/dist/utils/policy/release.d.ts +85 -0
- package/dist/utils/policy/release.js +117 -0
- package/dist/utils/policy/request.d.ts +55 -0
- package/dist/utils/policy/request.js +119 -0
- package/dist/utils/policy/runs.d.ts +30 -0
- package/dist/utils/policy/runs.js +101 -0
- package/dist/utils/policy/status.d.ts +55 -0
- package/dist/utils/policy/status.js +127 -0
- package/dist/utils/policy/template.d.ts +66 -0
- package/dist/utils/policy/template.js +152 -0
- package/dist/utils/policy/types.d.ts +295 -0
- package/dist/utils/policy/types.js +1 -0
- package/dist/utils/reference-checker.d.ts +3 -0
- package/dist/utils/reference-checker.js +3 -2
- package/dist/utils/sandbox-marker.d.ts +9 -0
- package/dist/utils/sandbox-marker.js +119 -0
- package/dist/utils/sync-state.d.ts +71 -0
- package/dist/utils/sync-state.js +353 -0
- package/oclif.manifest.json +6472 -3181
- package/package.json +9 -1
|
@@ -5,6 +5,7 @@ import * as fs from 'node:fs';
|
|
|
5
5
|
import * as os from 'node:os';
|
|
6
6
|
import path from 'node:path';
|
|
7
7
|
import BaseCommand, { buildUserAgent } from '../../../base-command.js';
|
|
8
|
+
import { BLANK_POLICY_OVERRIDE, blankPolicyOverride, gateRefusal, publishRefusal, quoted } from '../../../utils/policy/gate.js';
|
|
8
9
|
export default class FunctionEdit extends BaseCommand {
|
|
9
10
|
static args = {
|
|
10
11
|
function_id: Args.string({
|
|
@@ -81,6 +82,7 @@ Name: my_function
|
|
|
81
82
|
options: ['summary', 'json'],
|
|
82
83
|
required: false,
|
|
83
84
|
}),
|
|
85
|
+
'policy-override': Flags.string({ description: 'Publish past a blocking live-branch publish policy gate with an audited reason (requires workspace:policy update)', hidden: true }),
|
|
84
86
|
publish: Flags.boolean({
|
|
85
87
|
default: true,
|
|
86
88
|
description: 'Publish the function after editing',
|
|
@@ -99,8 +101,18 @@ Name: my_function
|
|
|
99
101
|
required: false,
|
|
100
102
|
}),
|
|
101
103
|
};
|
|
104
|
+
/** Set once the publish policy gate refuses, the one failure that exits 2. */
|
|
105
|
+
refusedByPolicyGate = false;
|
|
106
|
+
/** Exit 2 is the publish gate's refusal; every other failure, flag errors included, exits 1. */
|
|
107
|
+
async catch(error) {
|
|
108
|
+
if (this.refusedByPolicyGate)
|
|
109
|
+
return super.catch(error);
|
|
110
|
+
return this.catchAsOperational(error);
|
|
111
|
+
}
|
|
102
112
|
async run() {
|
|
103
113
|
const { args, flags } = await this.parse(FunctionEdit);
|
|
114
|
+
if (blankPolicyOverride(flags['policy-override']))
|
|
115
|
+
this.error(BLANK_POLICY_OVERRIDE);
|
|
104
116
|
const { profile, profileName } = this.resolveProfile(flags);
|
|
105
117
|
// Validate flag combinations
|
|
106
118
|
if (flags.edit && !flags.file) {
|
|
@@ -175,6 +187,10 @@ Name: my_function
|
|
|
175
187
|
include_xanoscript: 'false',
|
|
176
188
|
publish: flags.publish ? 'true' : 'false',
|
|
177
189
|
});
|
|
190
|
+
// A reason from someone with workspace:policy update publishes past a blocking publish gate.
|
|
191
|
+
const overrideReason = flags['policy-override']?.trim();
|
|
192
|
+
if (overrideReason)
|
|
193
|
+
queryParams.set('override_reason', overrideReason);
|
|
178
194
|
const apiUrl = `${profile.instance_origin}/api:meta/workspace/${workspaceId}/function/${functionId}?${queryParams.toString()}`;
|
|
179
195
|
// Update function via API
|
|
180
196
|
try {
|
|
@@ -188,6 +204,9 @@ Name: my_function
|
|
|
188
204
|
method: 'PUT',
|
|
189
205
|
}, flags.verbose, profile.access_token);
|
|
190
206
|
if (!response.ok) {
|
|
207
|
+
const refused = await gateRefusal(response);
|
|
208
|
+
if (refused)
|
|
209
|
+
this.refusePublish(refused, flags, functionId);
|
|
191
210
|
const errorText = await response.text();
|
|
192
211
|
this.error(`API request failed with status ${response.status}: ${response.statusText}\n${errorText}`);
|
|
193
212
|
}
|
|
@@ -208,6 +227,8 @@ Name: my_function
|
|
|
208
227
|
}
|
|
209
228
|
}
|
|
210
229
|
catch (error) {
|
|
230
|
+
if (this.refusedByPolicyGate)
|
|
231
|
+
throw error;
|
|
211
232
|
if (error instanceof Error) {
|
|
212
233
|
this.error(`Failed to update function: ${error.message}`);
|
|
213
234
|
}
|
|
@@ -428,4 +449,22 @@ Name: my_function
|
|
|
428
449
|
process.stdin.resume();
|
|
429
450
|
});
|
|
430
451
|
}
|
|
452
|
+
/**
|
|
453
|
+
* The live-branch publish gate refused the update: the verdict on stdout (under `-o json`,
|
|
454
|
+
* `{updated: false, message, policy_gate}`), then the platform's message and how to proceed,
|
|
455
|
+
* exiting 2.
|
|
456
|
+
*/
|
|
457
|
+
refusePublish(refused, flags, functionId) {
|
|
458
|
+
const workspace = flags.workspace ? ` -w ${quoted(flags.workspace)}` : '';
|
|
459
|
+
const source = flags.file ? ` -f ${quoted(flags.file)}` : flags.stdin ? ' --stdin' : '';
|
|
460
|
+
const { lines, message } = publishRefusal(refused, {
|
|
461
|
+
json: flags.output === 'json',
|
|
462
|
+
outcome: 'updated',
|
|
463
|
+
rerun: `xano function edit ${quoted(functionId)}${workspace}${source} --policy-override "<why>"`,
|
|
464
|
+
});
|
|
465
|
+
for (const line of lines)
|
|
466
|
+
this.log(line);
|
|
467
|
+
this.refusedByPolicyGate = true;
|
|
468
|
+
this.error(message, { exit: 2 });
|
|
469
|
+
}
|
|
431
470
|
}
|
|
@@ -103,7 +103,6 @@ export default class FunctionRun extends BaseCommand {
|
|
|
103
103
|
branch,
|
|
104
104
|
name: functionName,
|
|
105
105
|
profile,
|
|
106
|
-
tenant: flags.tenant,
|
|
107
106
|
verbose: flags.verbose,
|
|
108
107
|
workspaceId,
|
|
109
108
|
});
|
|
@@ -115,8 +114,9 @@ export default class FunctionRun extends BaseCommand {
|
|
|
115
114
|
input = await this.promptForMissing(input, missingRequired);
|
|
116
115
|
}
|
|
117
116
|
}
|
|
118
|
-
// 3) Execute via the meta run endpoint.
|
|
119
|
-
const
|
|
117
|
+
// 3) Execute via the meta run endpoint, scoped to the tenant when one is given.
|
|
118
|
+
const tenantPath = flags.tenant ? `/tenant/${encodeURIComponent(flags.tenant)}` : '';
|
|
119
|
+
const apiUrl = `${profile.instance_origin}/api:meta/workspace/${workspaceId}${tenantPath}/function/run`;
|
|
120
120
|
const body = { input, name: functionName };
|
|
121
121
|
if (branch)
|
|
122
122
|
body.branch = branch;
|
|
@@ -132,9 +132,6 @@ export default class FunctionRun extends BaseCommand {
|
|
|
132
132
|
const dataSource = flags.datasource?.trim();
|
|
133
133
|
if (dataSource)
|
|
134
134
|
headers['X-Data-Source'] = dataSource;
|
|
135
|
-
// X-Tenant runs the function on that tenant instead of the workspace.
|
|
136
|
-
if (flags.tenant)
|
|
137
|
-
headers['X-Tenant'] = flags.tenant;
|
|
138
135
|
const response = await this.verboseFetch(apiUrl, {
|
|
139
136
|
body: JSON.stringify(body),
|
|
140
137
|
headers,
|
|
@@ -177,16 +174,13 @@ export default class FunctionRun extends BaseCommand {
|
|
|
177
174
|
* transient introspection problem never blocks an otherwise-valid run.
|
|
178
175
|
*/
|
|
179
176
|
async fetchFunctionByName(opts) {
|
|
180
|
-
const { branch, name, profile,
|
|
177
|
+
const { branch, name, profile, verbose, workspaceId } = opts;
|
|
181
178
|
const params = new URLSearchParams({ per_page: '100', search: name });
|
|
182
179
|
if (branch)
|
|
183
180
|
params.set('branch', branch);
|
|
184
181
|
const url = `${profile.instance_origin}/api:meta/workspace/${workspaceId}/function?${params.toString()}`;
|
|
185
|
-
const headers = { accept: 'application/json', Authorization: `Bearer ${profile.access_token}` };
|
|
186
|
-
if (tenant)
|
|
187
|
-
headers['X-Tenant'] = tenant;
|
|
188
182
|
try {
|
|
189
|
-
const response = await this.verboseFetch(url, { headers, method: 'GET' }, verbose, profile.access_token);
|
|
183
|
+
const response = await this.verboseFetch(url, { headers: { accept: 'application/json', Authorization: `Bearer ${profile.access_token}` }, method: 'GET' }, verbose, profile.access_token);
|
|
190
184
|
if (!response.ok)
|
|
191
185
|
return undefined;
|
|
192
186
|
const page = (await response.json());
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import PolicyCommand from '../../../policy-command.js';
|
|
2
|
+
export default class PolicyCatalogue extends PolicyCommand {
|
|
3
|
+
static description: string;
|
|
4
|
+
static examples: string[];
|
|
5
|
+
static flags: {
|
|
6
|
+
check: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
7
|
+
recipe: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
8
|
+
branch: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
9
|
+
output: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
10
|
+
workspace: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
|
+
config: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
12
|
+
profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
13
|
+
verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
14
|
+
};
|
|
15
|
+
run(): Promise<void>;
|
|
16
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { Flags } from '@oclif/core';
|
|
2
|
+
import PolicyCommand from '../../../policy-command.js';
|
|
3
|
+
import { policyCatalogueSummary, policyNotCheckableSummary, policyRecipeSummary, policyTemplateSummary, selectCatalogueCheck, selectCatalogueRecipe, } from '../../../utils/policy/catalogue.js';
|
|
4
|
+
import { listItems } from '../../../utils/policy/request.js';
|
|
5
|
+
/** A served list, or none when the instance serves something else (an older one serves no recipes). */
|
|
6
|
+
const array = (value) => Array.isArray(value) ? value : [];
|
|
7
|
+
export default class PolicyCatalogue extends PolicyCommand {
|
|
8
|
+
static description = 'List built-in policy checks with their uses and parameter schemas, the templates a policy can start from, the recipes that combine them and what no check can see';
|
|
9
|
+
static examples = [
|
|
10
|
+
'$ xano policy catalogue -o json',
|
|
11
|
+
'$ xano policy catalogue --check object.auth_required',
|
|
12
|
+
'$ xano policy catalogue --recipe protect_card_data',
|
|
13
|
+
];
|
|
14
|
+
static flags = {
|
|
15
|
+
...PolicyCommand.policyFlags,
|
|
16
|
+
check: Flags.string({ description: 'Show only this check id (an unknown id names the closest matches)', exclusive: ['recipe'] }),
|
|
17
|
+
recipe: Flags.string({ description: 'Show only this recipe id and what it cannot see (an unknown id names the closest matches)', exclusive: ['check'] }),
|
|
18
|
+
};
|
|
19
|
+
async run() {
|
|
20
|
+
const { flags } = await this.parse(PolicyCatalogue);
|
|
21
|
+
const { request } = this.policyTarget(flags);
|
|
22
|
+
const result = await request('/check');
|
|
23
|
+
const sections = (result && typeof result === 'object' ? result : {});
|
|
24
|
+
const recipeContext = {
|
|
25
|
+
frameworks: array(sections.template_frameworks),
|
|
26
|
+
frameworksNote: sections.template_frameworks_note ?? '',
|
|
27
|
+
notCheckable: array(sections.not_checkable),
|
|
28
|
+
templates: array(sections.templates),
|
|
29
|
+
};
|
|
30
|
+
if (flags.check) {
|
|
31
|
+
// `--check` narrows both output modes, so `-o json` stays pipeable for one check too.
|
|
32
|
+
const selected = selectCatalogueCheck(listItems(result), flags.check);
|
|
33
|
+
this.log(flags.output === 'json' ? JSON.stringify(selected, null, 2) : policyCatalogueSummary(selected).join('\n'));
|
|
34
|
+
}
|
|
35
|
+
else if (flags.recipe) {
|
|
36
|
+
// `--recipe` narrows both output modes too; the summary adds the topics the recipe cannot see.
|
|
37
|
+
const selected = selectCatalogueRecipe(array(sections.recipes), flags.recipe);
|
|
38
|
+
if (flags.output === 'json') {
|
|
39
|
+
this.log(JSON.stringify(selected, null, 2));
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
const cited = new Set(selected.flatMap(recipe => array(recipe.limits)));
|
|
43
|
+
const limits = recipeContext.notCheckable.filter(entry => cited.has(entry.id));
|
|
44
|
+
this.log([...policyRecipeSummary(selected, recipeContext), ...(limits.length > 0 ? ['', ...policyNotCheckableSummary(limits)] : [])].join('\n'));
|
|
45
|
+
}
|
|
46
|
+
else if (flags.output === 'json') {
|
|
47
|
+
this.log(JSON.stringify(result, null, 2));
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
for (const line of policyCatalogueSummary(listItems(result)))
|
|
51
|
+
this.log(line);
|
|
52
|
+
const templates = policyTemplateSummary(recipeContext.templates, array(sections.template_categories), recipeContext.frameworks, recipeContext.frameworksNote);
|
|
53
|
+
const recipes = policyRecipeSummary(array(sections.recipes), recipeContext);
|
|
54
|
+
const notCheckable = policyNotCheckableSummary(recipeContext.notCheckable);
|
|
55
|
+
for (const section of [templates, recipes, notCheckable])
|
|
56
|
+
if (section.length > 0)
|
|
57
|
+
this.log(['', ...section].join('\n'));
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import PolicyCommand from '../../../policy-command.js';
|
|
2
|
+
export default class PolicyCoverage extends PolicyCommand {
|
|
3
|
+
static args: {
|
|
4
|
+
object: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
|
|
5
|
+
};
|
|
6
|
+
static description: string;
|
|
7
|
+
static examples: string[];
|
|
8
|
+
static flags: {
|
|
9
|
+
branch: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
10
|
+
output: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
|
+
workspace: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
12
|
+
config: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
13
|
+
profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
14
|
+
verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
15
|
+
};
|
|
16
|
+
run(): Promise<void>;
|
|
17
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { Args } from '@oclif/core';
|
|
2
|
+
import PolicyCommand from '../../../policy-command.js';
|
|
3
|
+
import { coverageLines, parseObjectRef } from '../../../utils/policy/coverage.js';
|
|
4
|
+
export default class PolicyCoverage extends PolicyCommand {
|
|
5
|
+
static args = {
|
|
6
|
+
object: Args.string({
|
|
7
|
+
description: 'The object, written type:id as findings name it (query:42, function:12, table:7); for workspace, the workspace\'s own id (workspace:17)',
|
|
8
|
+
required: true,
|
|
9
|
+
}),
|
|
10
|
+
};
|
|
11
|
+
static description = "Show which active policies apply to one object, why, and the object's findings in the branch's latest run, "
|
|
12
|
+
+ 'with each fix hint. Needs only the read permission for that object kind (workspace:api for an endpoint, '
|
|
13
|
+
+ 'workspace:function for a function, …), not workspace:policy; the answer leaves out policy details that need it.';
|
|
14
|
+
static examples = [
|
|
15
|
+
'$ xano policy coverage query:42',
|
|
16
|
+
'$ xano policy coverage function:12 -b dev -o json',
|
|
17
|
+
];
|
|
18
|
+
static flags = { ...PolicyCommand.policyFlags };
|
|
19
|
+
async run() {
|
|
20
|
+
const { args, flags } = await this.parse(PolicyCoverage);
|
|
21
|
+
const object = parseObjectRef(args.object);
|
|
22
|
+
if (!object)
|
|
23
|
+
this.error(`Name the object as type:id, as findings name it (for example query:42); got "${args.object}".`);
|
|
24
|
+
const { request } = this.policyTarget(flags);
|
|
25
|
+
const answer = (await request('/object/coverage', 'GET', undefined, { id: String(object.id), type: object.type }));
|
|
26
|
+
if (flags.output === 'json') {
|
|
27
|
+
this.log(JSON.stringify(answer, null, 2));
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
for (const line of coverageLines(answer))
|
|
31
|
+
this.log(line);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import PolicyCommand from '../../../policy-command.js';
|
|
2
|
+
export default class PolicyCreate extends PolicyCommand {
|
|
3
|
+
static description: string;
|
|
4
|
+
static examples: string[];
|
|
5
|
+
static flags: {
|
|
6
|
+
key: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
7
|
+
param: import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
8
|
+
template: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
9
|
+
message: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
10
|
+
branch: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
|
+
output: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
12
|
+
workspace: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
13
|
+
config: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
14
|
+
profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
15
|
+
verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
16
|
+
};
|
|
17
|
+
run(): Promise<void>;
|
|
18
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { Flags } from '@oclif/core';
|
|
2
|
+
import PolicyCommand from '../../../policy-command.js';
|
|
3
|
+
import { listAllPolicies } from '../../../utils/policy/request.js';
|
|
4
|
+
import { policyFromTemplate, recipeRefusal } from '../../../utils/policy/template.js';
|
|
5
|
+
export default class PolicyCreate extends PolicyCommand {
|
|
6
|
+
static description = 'Create an independent policy from a platform template';
|
|
7
|
+
static examples = [
|
|
8
|
+
'$ xano policy create --template endpoints_need_login -b dev',
|
|
9
|
+
`$ xano policy create --template no_pii_in_responses --key DATA-020 --param 'fields=["email","ssn"]'`,
|
|
10
|
+
];
|
|
11
|
+
static flags = {
|
|
12
|
+
...PolicyCommand.policyFlags,
|
|
13
|
+
...PolicyCommand.publishFlags,
|
|
14
|
+
key: Flags.string({ description: 'New policy key (default: the template key with a free numeric suffix if taken)' }),
|
|
15
|
+
param: Flags.string({ description: 'Parameter override: N.path=JSON, 1-based rule number; path=JSON for a single-rule template', multiple: true }),
|
|
16
|
+
template: Flags.string({ description: 'Template id from policy catalogue', required: true }),
|
|
17
|
+
};
|
|
18
|
+
async run() {
|
|
19
|
+
const { flags } = await this.parse(PolicyCreate);
|
|
20
|
+
const target = this.policyTarget(flags);
|
|
21
|
+
const { request } = target;
|
|
22
|
+
const catalogue = await request('/check');
|
|
23
|
+
const template = catalogue.templates?.find(item => item.id === flags.template);
|
|
24
|
+
// A recipe is data with no create action: name the templates it points at instead.
|
|
25
|
+
const recipe = template || !Array.isArray(catalogue.recipes) ? undefined : catalogue.recipes.find(item => item.id === flags.template);
|
|
26
|
+
if (recipe)
|
|
27
|
+
this.error(recipeRefusal(recipe));
|
|
28
|
+
if (!template)
|
|
29
|
+
this.error(`Unknown template "${flags.template}". Run xano policy catalogue to see this instance's templates.`);
|
|
30
|
+
const keys = (await listAllPolicies(request)).map(policy => policy.key);
|
|
31
|
+
// The key is checked against the pattern this instance's catalogue serves, and an unfilled need's
|
|
32
|
+
// example falls back to its check's own.
|
|
33
|
+
const document = policyFromTemplate(template, keys, { checks: catalogue.items, key: flags.key, keyRule: catalogue.document?.key, overrides: flags.param });
|
|
34
|
+
const parsed = await this.parseSource(request, { data: document });
|
|
35
|
+
const saved = await request('', 'POST', { data: { source: parsed.source }, ...(flags.message ? { message: flags.message } : {}) });
|
|
36
|
+
if (typeof saved?.id !== 'number')
|
|
37
|
+
this.error('The platform did not return the saved policy. Check xano policy list before retrying.');
|
|
38
|
+
this.logRuleWarnings(parsed);
|
|
39
|
+
if (flags.output === 'json')
|
|
40
|
+
this.log(JSON.stringify(saved, null, 2));
|
|
41
|
+
else
|
|
42
|
+
this.log(`Created ${parsed.policy.key} (Version ${saved.version}) in ${this.where(target)}.`);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import PolicyCommand from '../../../policy-command.js';
|
|
2
|
+
export default class PolicyDelete extends PolicyCommand {
|
|
3
|
+
static args: {
|
|
4
|
+
policy: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
|
|
5
|
+
};
|
|
6
|
+
static description: string;
|
|
7
|
+
static examples: string[];
|
|
8
|
+
static flags: {
|
|
9
|
+
force: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
10
|
+
branch: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
|
+
output: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
12
|
+
workspace: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
13
|
+
config: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
14
|
+
profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
15
|
+
verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
16
|
+
};
|
|
17
|
+
/** `workspace push` is additive, so deleting a policy file leaves the policy in place; this removes it. */
|
|
18
|
+
run(): Promise<void>;
|
|
19
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { Args, Flags } from '@oclif/core';
|
|
2
|
+
import PolicyCommand from '../../../policy-command.js';
|
|
3
|
+
import { confirm } from '../../../utils/multidoc-push.js';
|
|
4
|
+
import { listAllPolicies, sameKey } from '../../../utils/policy/request.js';
|
|
5
|
+
export default class PolicyDelete extends PolicyCommand {
|
|
6
|
+
static args = {
|
|
7
|
+
policy: Args.string({
|
|
8
|
+
description: 'Policy key (AUTH-001) or ID to delete',
|
|
9
|
+
required: true,
|
|
10
|
+
}),
|
|
11
|
+
};
|
|
12
|
+
static description = 'Delete a policy from a branch; its Version History is kept. It needs the workspace:policy delete permission only, an active, blocking policy included.';
|
|
13
|
+
static examples = [
|
|
14
|
+
`$ xano policy delete TMP-DX-001
|
|
15
|
+
Delete policy TMP-DX-001 (ID: 922, Version 2) from workspace 3? Its Version History is kept. (y/N) y
|
|
16
|
+
Deleted policy TMP-DX-001 (ID: 922) from workspace 3.
|
|
17
|
+
`,
|
|
18
|
+
'$ xano policy delete 922 --force',
|
|
19
|
+
'$ xano policy delete TMP-DX-001 -w 3 --force -o json',
|
|
20
|
+
];
|
|
21
|
+
static flags = {
|
|
22
|
+
...PolicyCommand.policyFlags,
|
|
23
|
+
force: Flags.boolean({
|
|
24
|
+
char: 'f',
|
|
25
|
+
default: false,
|
|
26
|
+
description: '[IMPORTANT] NEVER run without explicit user confirmation. Skips the confirmation prompt.',
|
|
27
|
+
}),
|
|
28
|
+
};
|
|
29
|
+
/** `workspace push` is additive, so deleting a policy file leaves the policy in place; this removes it. */
|
|
30
|
+
async run() {
|
|
31
|
+
const { args, flags } = await this.parse(PolicyDelete);
|
|
32
|
+
const target = this.policyTarget(flags);
|
|
33
|
+
const wanted = args.policy.trim();
|
|
34
|
+
if (!wanted)
|
|
35
|
+
this.error('Provide the policy key or ID to delete.');
|
|
36
|
+
const policies = await listAllPolicies(target.request);
|
|
37
|
+
const matched = policies.find((policy) => sameKey(policy.key, wanted))
|
|
38
|
+
?? (/^\d+$/.test(wanted) ? policies.find((policy) => policy.id === Number(wanted)) : undefined);
|
|
39
|
+
if (!matched) {
|
|
40
|
+
const known = policies.map((policy) => policy.key).sort();
|
|
41
|
+
this.error(`No policy "${wanted}" on ${this.where(target)}.${known.length > 0 ? ` This branch has: ${known.join(', ')}.` : ' This branch has no policies.'}`);
|
|
42
|
+
}
|
|
43
|
+
if (!flags.force) {
|
|
44
|
+
// Nobody can answer the prompt: refuse rather than report a cancel as success.
|
|
45
|
+
if (!process.stdin.isTTY)
|
|
46
|
+
this.error('Non-interactive environment detected. Use --force to skip confirmation.');
|
|
47
|
+
const confirmed = await confirm(`Delete policy ${matched.key} (ID: ${matched.id}, Version ${matched.version}) from ${this.where(target)}? Its Version History is kept.`);
|
|
48
|
+
if (!confirmed) {
|
|
49
|
+
this.log('Deletion cancelled.');
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
// The policy as listed is what the prompt named: a delete of a policy changed since is refused (`policy_stale`).
|
|
54
|
+
const staleCheck = matched.updated_at ? { last_updated_at: String(matched.updated_at) } : {};
|
|
55
|
+
await target.request(`/${matched.id}`, 'DELETE', undefined, staleCheck);
|
|
56
|
+
if (flags.output === 'json')
|
|
57
|
+
this.log(JSON.stringify({ deleted: true, id: matched.id, key: matched.key }, null, 2));
|
|
58
|
+
else
|
|
59
|
+
this.log(`Deleted policy ${matched.key} (ID: ${matched.id}) from ${this.where(target)}.`);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import PolicyCommand from '../../../policy-command.js';
|
|
2
|
+
/** One rule's clear objects in a trial's answer: the first ones and how many there are. */
|
|
3
|
+
type TrialClear = {
|
|
4
|
+
items?: Array<{
|
|
5
|
+
name?: string;
|
|
6
|
+
}>;
|
|
7
|
+
policy_key?: string;
|
|
8
|
+
rule_id?: string;
|
|
9
|
+
total?: number;
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* What a trial says it is, then each rule's clear objects: the objects it examined and found
|
|
13
|
+
* nothing on. A trial is never stored, so its answer is all there is to show.
|
|
14
|
+
*/
|
|
15
|
+
export declare function trialLines(key: string, clear: TrialClear[] | undefined): string[];
|
|
16
|
+
export default class PolicyEvaluate extends PolicyCommand {
|
|
17
|
+
static description: string;
|
|
18
|
+
static examples: string[];
|
|
19
|
+
static flags: {
|
|
20
|
+
policy: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
21
|
+
'run-detail': import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
22
|
+
summary: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
23
|
+
branch: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
24
|
+
output: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
25
|
+
workspace: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
26
|
+
config: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
27
|
+
profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
28
|
+
verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
29
|
+
};
|
|
30
|
+
run(): Promise<void>;
|
|
31
|
+
}
|
|
32
|
+
export {};
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { Flags } from '@oclif/core';
|
|
2
|
+
import PolicyCommand from '../../../policy-command.js';
|
|
3
|
+
import { evaluationEvidence, notStoredLine, policyCheckWarning, policyExitCode, policySummary, } from '../../../utils/policy/feedback.js';
|
|
4
|
+
/** How many clear objects a trial names per rule; the rest are counted. */
|
|
5
|
+
const TRIAL_CLEAR_NAMES = 5;
|
|
6
|
+
/**
|
|
7
|
+
* What a trial says it is, then each rule's clear objects: the objects it examined and found
|
|
8
|
+
* nothing on. A trial is never stored, so its answer is all there is to show.
|
|
9
|
+
*/
|
|
10
|
+
export function trialLines(key, clear) {
|
|
11
|
+
const lines = [`Trial of ${key}: this policy alone, evaluated without storing a run. It is not a report and gates nothing.`];
|
|
12
|
+
if (!clear?.length)
|
|
13
|
+
return lines;
|
|
14
|
+
lines.push('Clear objects (examined, nothing found):');
|
|
15
|
+
for (const rule of clear) {
|
|
16
|
+
const names = (rule.items ?? []).map((item) => item.name ?? '').filter(Boolean).slice(0, TRIAL_CLEAR_NAMES);
|
|
17
|
+
const total = rule.total ?? 0;
|
|
18
|
+
const more = total > names.length ? `${names.length > 0 ? ', ' : ''}${total - names.length} more` : '';
|
|
19
|
+
lines.push(` ${rule.rule_id ?? ''} ${total}${names.length > 0 || more ? ` ${names.join(', ')}${more}` : ''}`);
|
|
20
|
+
}
|
|
21
|
+
return lines;
|
|
22
|
+
}
|
|
23
|
+
export default class PolicyEvaluate extends PolicyCommand {
|
|
24
|
+
static description = 'Evaluate active branch policies and report their findings; blocking findings exit 2. With --policy, try one policy alone, whatever its state, without storing a run';
|
|
25
|
+
static examples = ['$ xano policy evaluate -o json', '$ xano policy evaluate --run-detail', '$ xano policy evaluate --summary -o json', '$ xano policy evaluate --policy AUTH-001'];
|
|
26
|
+
static flags = {
|
|
27
|
+
...PolicyCommand.policyFlags,
|
|
28
|
+
policy: Flags.string({
|
|
29
|
+
description: 'Try this one policy (its key) alone, whatever its state: nothing is stored, and the answer lists each rule\'s clear objects',
|
|
30
|
+
}),
|
|
31
|
+
'run-detail': Flags.boolean({
|
|
32
|
+
default: false,
|
|
33
|
+
description: 'Also print what this run recorded: each policy description and the settings each rule ran with',
|
|
34
|
+
}),
|
|
35
|
+
summary: Flags.boolean({
|
|
36
|
+
default: false,
|
|
37
|
+
description: 'Answer the run summary and its first 50 findings instead of the whole run; `xano policy runs <id>` pages through the rest',
|
|
38
|
+
}),
|
|
39
|
+
};
|
|
40
|
+
async run() {
|
|
41
|
+
const { flags } = await this.parse(PolicyEvaluate);
|
|
42
|
+
if (flags.policy !== undefined && !flags.policy.trim())
|
|
43
|
+
this.error('--policy must name a policy key.');
|
|
44
|
+
const { request } = this.policyTarget(flags);
|
|
45
|
+
// The evaluation answers with its run, so the run's own snapshot names any unnamed rule. The
|
|
46
|
+
// summary answer carries the same snapshot and results, and only the first findings.
|
|
47
|
+
const body = { ...(flags.summary ? { answer: 'summary' } : {}), ...(flags.policy ? { policy: flags.policy } : {}) };
|
|
48
|
+
const result = (await request('/evaluate', 'POST', Object.keys(body).length > 0 ? body : undefined));
|
|
49
|
+
if (flags.output === 'json')
|
|
50
|
+
this.log(JSON.stringify(result, null, 2));
|
|
51
|
+
else {
|
|
52
|
+
for (const line of policySummary(result.policy_check, evaluationEvidence(result)))
|
|
53
|
+
this.log(line);
|
|
54
|
+
// A trial is never stored by design; the "cannot record runs" note is about credentials.
|
|
55
|
+
const notStored = flags.policy ? null : notStoredLine(result);
|
|
56
|
+
if (notStored)
|
|
57
|
+
this.log(notStored);
|
|
58
|
+
if (flags.policy)
|
|
59
|
+
for (const line of trialLines(flags.policy, result.clear))
|
|
60
|
+
this.log(line);
|
|
61
|
+
if (flags['run-detail'])
|
|
62
|
+
this.logRunDetail(result);
|
|
63
|
+
}
|
|
64
|
+
const warning = policyCheckWarning(result.policy_check);
|
|
65
|
+
if (warning)
|
|
66
|
+
this.warn(warning);
|
|
67
|
+
const code = policyExitCode(result.policy_check);
|
|
68
|
+
if (code)
|
|
69
|
+
process.exitCode = code;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import PolicyCommand from '../../../policy-command.js';
|
|
2
|
+
export default class PolicyList extends PolicyCommand {
|
|
3
|
+
static description: string;
|
|
4
|
+
static examples: string[];
|
|
5
|
+
static flags: {
|
|
6
|
+
branch: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
7
|
+
output: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
8
|
+
workspace: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
9
|
+
config: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
10
|
+
profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
|
+
verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
12
|
+
};
|
|
13
|
+
run(): Promise<void>;
|
|
14
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import PolicyCommand from '../../../policy-command.js';
|
|
2
|
+
import { listItems } from '../../../utils/policy/request.js';
|
|
3
|
+
export default class PolicyList extends PolicyCommand {
|
|
4
|
+
static description = 'List workspace policies and their descriptions';
|
|
5
|
+
static examples = ['$ xano policy list -o json'];
|
|
6
|
+
static flags = { ...PolicyCommand.policyFlags };
|
|
7
|
+
async run() {
|
|
8
|
+
const { flags } = await this.parse(PolicyList);
|
|
9
|
+
const { request } = this.policyTarget(flags);
|
|
10
|
+
const result = await request();
|
|
11
|
+
if (flags.output === 'json') {
|
|
12
|
+
this.log(JSON.stringify(result, null, 2));
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
const policies = listItems(result);
|
|
16
|
+
if (policies.length === 0)
|
|
17
|
+
this.log('No policies found.');
|
|
18
|
+
for (const policy of policies)
|
|
19
|
+
this.log(`${policy.key} ${policy.active ? `Active, ${policy.enforcement === 'blocking' ? 'Blocking' : 'Advisory'}` : 'Inactive'} ${policy.title ?? ''} (ID: ${policy.id}, Version ${policy.version})`);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import PolicyCommand from '../../../policy-command.js';
|
|
2
|
+
export default class PolicyParse extends PolicyCommand {
|
|
3
|
+
static args: {
|
|
4
|
+
file: import("@oclif/core/interfaces").Arg<string | undefined, Record<string, unknown>>;
|
|
5
|
+
};
|
|
6
|
+
static description: string;
|
|
7
|
+
static examples: string[];
|
|
8
|
+
static flags: {
|
|
9
|
+
file: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
10
|
+
stdin: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
11
|
+
branch: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
12
|
+
output: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
13
|
+
workspace: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
14
|
+
config: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
15
|
+
profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
16
|
+
verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
17
|
+
};
|
|
18
|
+
run(): Promise<void>;
|
|
19
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import PolicyCommand from '../../../policy-command.js';
|
|
2
|
+
export default class PolicyParse extends PolicyCommand {
|
|
3
|
+
static args = { ...PolicyCommand.sourceArgs };
|
|
4
|
+
static description = 'Validate and format policy XanoScript using the platform parser, with rule warnings against the branch';
|
|
5
|
+
static examples = [
|
|
6
|
+
'$ xano policy parse policies/AUTH-001.xs',
|
|
7
|
+
'$ xano policy parse --file policies/AUTH-001.xs',
|
|
8
|
+
];
|
|
9
|
+
static flags = { ...PolicyCommand.policyFlags, ...PolicyCommand.sourceFlags };
|
|
10
|
+
async run() {
|
|
11
|
+
const { args, flags } = await this.parse(PolicyParse);
|
|
12
|
+
const { request } = this.policyTarget(flags);
|
|
13
|
+
const parsed = await this.parseSource(request, { source: this.readSource(flags, args.file) });
|
|
14
|
+
this.log(flags.output === 'json' ? JSON.stringify(parsed, null, 2) : parsed.source);
|
|
15
|
+
if (flags.output !== 'json')
|
|
16
|
+
this.logRuleWarnings(parsed);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import PolicyCommand from '../../../policy-command.js';
|
|
2
|
+
export default class PolicyPublish extends PolicyCommand {
|
|
3
|
+
static args: {
|
|
4
|
+
file: import("@oclif/core/interfaces").Arg<string | undefined, Record<string, unknown>>;
|
|
5
|
+
};
|
|
6
|
+
static description: string;
|
|
7
|
+
static examples: string[];
|
|
8
|
+
static flags: {
|
|
9
|
+
message: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
10
|
+
file: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
|
+
stdin: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
12
|
+
branch: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
13
|
+
output: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
14
|
+
workspace: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
15
|
+
config: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
16
|
+
profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
17
|
+
verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
18
|
+
};
|
|
19
|
+
run(): Promise<void>;
|
|
20
|
+
}
|