@cdot65/prisma-airs-cli 3.3.0 → 4.0.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 +26 -2
- package/dist/{chunk-2VIUZRPB.js → chunk-W5YDJS7H.js} +124 -34
- package/dist/cli/index.js +834 -464
- package/dist/index.d.ts +83 -1
- package/dist/index.js +1 -1
- package/package.json +15 -3
package/README.md
CHANGED
|
@@ -21,6 +21,8 @@
|
|
|
21
21
|
- **AI Red Teaming** — adversarial scanning with static, dynamic, and custom prompt set attack modes
|
|
22
22
|
- **[AI Gateway](https://cdot65.github.io/prisma-airs-cli/cli/aigateway/workspaces/)** — scoped/admin workspace management and workspace cost telemetry
|
|
23
23
|
- **Model Security** — ML model supply chain scanning with security groups, rules, and violation tracking
|
|
24
|
+
- **Unified automation output** — every read command supports `pretty`, `table`, `markdown`, `csv`, `json`, and `yaml`, with pipe-safe stdout
|
|
25
|
+
- **Complete pagination** — consistent `--limit`, `--offset`, and `--all` traversal with a configurable safety cap
|
|
24
26
|
- **`airs doctor`** — one-command diagnostics for environment, credentials, and API connectivity
|
|
25
27
|
- **`airs config`** — manage `~/.prisma-airs/config.json` from the CLI (`list`, `get`, `set`, `unset`, `path`)
|
|
26
28
|
|
|
@@ -65,9 +67,31 @@ airs aigateway telemetry cost --workspace <workspace-slug> --days 30
|
|
|
65
67
|
|
|
66
68
|
# Model security
|
|
67
69
|
airs model-security scans create --config scan-config.json
|
|
70
|
+
|
|
71
|
+
# Pipe-safe read output and complete traversal
|
|
72
|
+
airs runtime profiles list --all --output json | jq '.[].profileName'
|
|
73
|
+
airs runtime topics list --all-versions --output markdown
|
|
68
74
|
```
|
|
69
75
|
|
|
70
|
-
Bulk scans preserve one output row per input prompt in input order, including all eight runtime detector flags. Work is processed as sequential logical batches (`--batch-size 25` by default), with SDK requests capped at 20 prompts. Item-level state makes accepted and pending work resumable without duplicating CSV rows, and active jobs are locked against overlapping resumes. Runtime actions are exactly `allow`, `block`, or `failed`; failed or timed-out prompts make the command exit 1.
|
|
76
|
+
Bulk scans preserve one output row per input prompt in input order, including all eight runtime detector flags. Work is processed as sequential logical batches (`--batch-size 25` by default), with SDK requests capped at 20 prompts. Item-level state makes accepted and pending work resumable without duplicating CSV rows, and active jobs are locked against overlapping resumes. Runtime actions are exactly `allow`, `block`, or `failed`; failed or timed-out prompts make the command exit 1. Version 4 uses `@cdot65/prisma-airs-sdk` 0.18.0 or later.
|
|
77
|
+
|
|
78
|
+
## Read Output and Pagination
|
|
79
|
+
|
|
80
|
+
Read commands share one contract:
|
|
81
|
+
|
|
82
|
+
- Formats: `pretty`, `table`, `markdown`, `csv`, `json`, and `yaml`.
|
|
83
|
+
- JSON/YAML lists are bare arrays of complete normalized records; detail reads are complete objects.
|
|
84
|
+
- Table, Markdown, and CSV are stable human-oriented projections. CSV uses RFC 4180 quoting.
|
|
85
|
+
- Data is written to stdout; status, paging hints, warnings, and errors are written to stderr.
|
|
86
|
+
- Output precedence is command `--output`, global `--output`, `defaultOutput`/`PANW_CLI_OUTPUT`, then `pretty`.
|
|
87
|
+
- Paginated lists use `--limit`, `--offset`, and `--all`. Complete traversal is capped at 10,000 records by default; change it with `--max`, or use `--max 0` for no cap.
|
|
88
|
+
- Profile and topic lists return only the latest revision by default. Use `--all-versions` or `--revision` when historical revisions are needed.
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
airs --output json runtime profiles list --all | jq '.[].profileName'
|
|
92
|
+
PANW_CLI_OUTPUT=yaml airs runtime topics get "My Topic"
|
|
93
|
+
airs model-security scans list --all --max 25000 --output csv > scans.csv
|
|
94
|
+
```
|
|
71
95
|
|
|
72
96
|
## Documentation
|
|
73
97
|
|
|
@@ -83,7 +107,7 @@ The full guides, complete CLI reference, configuration, and architecture live on
|
|
|
83
107
|
|
|
84
108
|
## Configuration
|
|
85
109
|
|
|
86
|
-
Credentials come from environment variables or `~/.prisma-airs/config.json`. At minimum: `PANW_AI_SEC_API_KEY` (scanning) and `PANW_MGMT_CLIENT_ID` / `PANW_MGMT_CLIENT_SECRET` / `PANW_MGMT_TSG_ID` (management). See [`.env.example`](.env.example) and the [configuration guide](https://cdot65.github.io/prisma-airs-cli/getting-started/configuration/) for the full list.
|
|
110
|
+
Credentials come from environment variables or `~/.prisma-airs/config.json`. At minimum: `PANW_AI_SEC_API_KEY` (scanning) and `PANW_MGMT_CLIENT_ID` / `PANW_MGMT_CLIENT_SECRET` / `PANW_MGMT_TSG_ID` (management). Set `defaultOutput` in the config file or `PANW_CLI_OUTPUT` in the environment to choose a default read format. See [`.env.example`](.env.example) and the [configuration guide](https://cdot65.github.io/prisma-airs-cli/getting-started/configuration/) for the full list.
|
|
87
111
|
|
|
88
112
|
## License
|
|
89
113
|
|
|
@@ -21,7 +21,14 @@ var SdkManagementService = class {
|
|
|
21
21
|
return { message: response.message };
|
|
22
22
|
}
|
|
23
23
|
async listTopics() {
|
|
24
|
-
|
|
24
|
+
return this.client.topics.listAll();
|
|
25
|
+
}
|
|
26
|
+
async listLatestTopics(opts) {
|
|
27
|
+
const response = await this.client.topics.list({
|
|
28
|
+
latestOnly: true,
|
|
29
|
+
limit: opts?.limit,
|
|
30
|
+
offset: opts?.offset
|
|
31
|
+
});
|
|
25
32
|
return response.custom_topics;
|
|
26
33
|
}
|
|
27
34
|
async getTopic(topicId) {
|
|
@@ -32,9 +39,9 @@ var SdkManagementService = class {
|
|
|
32
39
|
}
|
|
33
40
|
async getTopicByName(topicName) {
|
|
34
41
|
const topics = await this.listTopics();
|
|
35
|
-
const
|
|
36
|
-
if (
|
|
37
|
-
return topic;
|
|
42
|
+
const matches = topics.filter((topic) => topic.topic_name === topicName);
|
|
43
|
+
if (matches.length === 0) throw new Error(`Topic "${topicName}" not found`);
|
|
44
|
+
return matches.reduce((latest, topic) => topic.revision > latest.revision ? topic : latest);
|
|
38
45
|
}
|
|
39
46
|
/**
|
|
40
47
|
* Sets a single custom topic on a profile's topic-guardrails config.
|
|
@@ -53,7 +60,7 @@ var SdkManagementService = class {
|
|
|
53
60
|
* it defaults to revision 0 (original content), not the latest.
|
|
54
61
|
*/
|
|
55
62
|
async assignTopicsToProfile(profileName, topics, guardrailAction) {
|
|
56
|
-
const
|
|
63
|
+
const ai_profiles = await this.client.profiles.listAll();
|
|
57
64
|
const profile = ai_profiles.find((p) => p.profile_name === profileName);
|
|
58
65
|
if (!profile?.profile_id) {
|
|
59
66
|
throw new Error(`Profile "${profileName}" not found`);
|
|
@@ -102,7 +109,7 @@ var SdkManagementService = class {
|
|
|
102
109
|
});
|
|
103
110
|
}
|
|
104
111
|
async getProfileTopics(profileName) {
|
|
105
|
-
const
|
|
112
|
+
const ai_profiles = await this.client.profiles.listAll();
|
|
106
113
|
const profile = ai_profiles.find((p) => p.profile_name === profileName);
|
|
107
114
|
if (!profile?.profile_id) {
|
|
108
115
|
throw new Error(`Profile "${profileName}" not found`);
|
|
@@ -170,6 +177,12 @@ var SdkManagementService = class {
|
|
|
170
177
|
nextOffset: response.next_offset
|
|
171
178
|
};
|
|
172
179
|
}
|
|
180
|
+
async listAllProfiles(opts) {
|
|
181
|
+
const profiles = await this.client.profiles.listAll(opts);
|
|
182
|
+
return profiles.map(
|
|
183
|
+
(profile) => this.normalizeProfile(profile)
|
|
184
|
+
);
|
|
185
|
+
}
|
|
173
186
|
async createProfile(request) {
|
|
174
187
|
const response = await this.client.profiles.create(request);
|
|
175
188
|
return this.normalizeProfile(response);
|
|
@@ -208,6 +221,11 @@ var SdkManagementService = class {
|
|
|
208
221
|
nextOffset: raw.next_offset
|
|
209
222
|
};
|
|
210
223
|
}
|
|
224
|
+
async listAllApiKeys(opts) {
|
|
225
|
+
return (await this.client.apiKeys.listAll(opts)).map(
|
|
226
|
+
(key) => this.normalizeApiKey(key)
|
|
227
|
+
);
|
|
228
|
+
}
|
|
211
229
|
async createApiKey(request) {
|
|
212
230
|
const response = await this.client.apiKeys.create(request);
|
|
213
231
|
return this.normalizeApiKey(response);
|
|
@@ -240,6 +258,11 @@ var SdkManagementService = class {
|
|
|
240
258
|
nextOffset: raw.next_offset
|
|
241
259
|
};
|
|
242
260
|
}
|
|
261
|
+
async listAllCustomerApps(opts) {
|
|
262
|
+
return (await this.client.customerApps.listAll(opts)).map(
|
|
263
|
+
(app) => this.normalizeCustomerApp(app)
|
|
264
|
+
);
|
|
265
|
+
}
|
|
243
266
|
async getCustomerApp(appName) {
|
|
244
267
|
const response = await this.client.customerApps.get(appName);
|
|
245
268
|
return this.normalizeCustomerApp(response);
|
|
@@ -253,11 +276,18 @@ var SdkManagementService = class {
|
|
|
253
276
|
return this.normalizeCustomerApp(response);
|
|
254
277
|
}
|
|
255
278
|
async listConsumptionApps(opts) {
|
|
256
|
-
const
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
279
|
+
const limit = opts?.limit ?? 100;
|
|
280
|
+
let offset = opts?.offset ?? 0;
|
|
281
|
+
const items = [];
|
|
282
|
+
for (; ; ) {
|
|
283
|
+
const response = await this.client.dashboard.applicationsOverview({ limit, offset });
|
|
284
|
+
const page = response.items ?? [];
|
|
285
|
+
items.push(...page);
|
|
286
|
+
const total = response.pagination?.total_items;
|
|
287
|
+
offset += page.length;
|
|
288
|
+
if (page.length === 0 || page.length < limit || total != null && offset >= total) break;
|
|
289
|
+
}
|
|
290
|
+
return items.filter(
|
|
261
291
|
(item) => typeof item.id === "string" && typeof item.name === "string"
|
|
262
292
|
).map((item) => ({
|
|
263
293
|
appId: item.id,
|
|
@@ -525,6 +555,18 @@ var SdkModelSecurityService = class {
|
|
|
525
555
|
groups: raw.security_groups.map(normalizeGroup)
|
|
526
556
|
};
|
|
527
557
|
}
|
|
558
|
+
async listAllGroups(opts = {}) {
|
|
559
|
+
const rows = await this.client.securityGroups.listAll({
|
|
560
|
+
source_types: opts.sourceTypes,
|
|
561
|
+
search_query: opts.searchQuery,
|
|
562
|
+
sort_field: opts.sortField,
|
|
563
|
+
sort_dir: opts.sortDir,
|
|
564
|
+
enabled_rules: opts.enabledRules,
|
|
565
|
+
limit: opts.limit,
|
|
566
|
+
max: opts.max
|
|
567
|
+
});
|
|
568
|
+
return rows.map((row) => normalizeGroup(row));
|
|
569
|
+
}
|
|
528
570
|
async getGroup(uuid) {
|
|
529
571
|
const response = await this.client.securityGroups.get(uuid);
|
|
530
572
|
return normalizeGroup(response);
|
|
@@ -615,6 +657,15 @@ var SdkModelSecurityService = class {
|
|
|
615
657
|
rules: raw.rules.map(normalizeRule)
|
|
616
658
|
};
|
|
617
659
|
}
|
|
660
|
+
async listAllRules(opts = {}) {
|
|
661
|
+
const rows = await this.client.securityRules.listAll({
|
|
662
|
+
source_type: opts.sourceType,
|
|
663
|
+
search_query: opts.searchQuery,
|
|
664
|
+
limit: opts.limit,
|
|
665
|
+
max: opts.max
|
|
666
|
+
});
|
|
667
|
+
return rows.map((row) => normalizeRule(row));
|
|
668
|
+
}
|
|
618
669
|
async getRule(uuid) {
|
|
619
670
|
const response = await this.client.securityRules.get(uuid);
|
|
620
671
|
return normalizeRule(response);
|
|
@@ -641,6 +692,16 @@ var SdkModelSecurityService = class {
|
|
|
641
692
|
scans: raw.scans.map(normalizeScan)
|
|
642
693
|
};
|
|
643
694
|
}
|
|
695
|
+
async listAllScans(opts = {}) {
|
|
696
|
+
const rows = await this.client.scans.listAll({
|
|
697
|
+
eval_outcomes: opts.evalOutcome === void 0 ? void 0 : [opts.evalOutcome],
|
|
698
|
+
source_types: opts.sourceType === void 0 ? void 0 : [opts.sourceType],
|
|
699
|
+
search_query: opts.search,
|
|
700
|
+
limit: opts.limit,
|
|
701
|
+
max: opts.max
|
|
702
|
+
});
|
|
703
|
+
return rows.map((row) => normalizeScan(row));
|
|
704
|
+
}
|
|
644
705
|
async getScan(uuid) {
|
|
645
706
|
const response = await this.client.scans.get(uuid);
|
|
646
707
|
return normalizeScan(response);
|
|
@@ -743,6 +804,17 @@ var SdkModelSecurityService = class {
|
|
|
743
804
|
models: raw.models.map(normalizeModel)
|
|
744
805
|
};
|
|
745
806
|
}
|
|
807
|
+
async listAllModels(opts = {}) {
|
|
808
|
+
const rows = await this.client.models.listAllModels({
|
|
809
|
+
search: opts.search,
|
|
810
|
+
search_query: opts.searchQuery,
|
|
811
|
+
sort_field: opts.sortField,
|
|
812
|
+
sort_order: opts.sortOrder,
|
|
813
|
+
limit: opts.limit,
|
|
814
|
+
max: opts.max
|
|
815
|
+
});
|
|
816
|
+
return rows.map((row) => normalizeModel(row));
|
|
817
|
+
}
|
|
746
818
|
async getModel(uuid) {
|
|
747
819
|
const response = await this.client.models.getModel(uuid);
|
|
748
820
|
return normalizeModel(response);
|
|
@@ -1168,29 +1240,14 @@ var SdkRedTeamService = class {
|
|
|
1168
1240
|
return await this.client.targets.getTargetTemplates();
|
|
1169
1241
|
}
|
|
1170
1242
|
async listTargets() {
|
|
1171
|
-
const
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
all.push({
|
|
1180
|
-
uuid: t.uuid,
|
|
1181
|
-
name: t.name,
|
|
1182
|
-
status: t.status,
|
|
1183
|
-
targetType: t.target_type,
|
|
1184
|
-
active: t.active
|
|
1185
|
-
});
|
|
1186
|
-
}
|
|
1187
|
-
const pagination = body.pagination;
|
|
1188
|
-
if (!pagination || all.length >= (pagination.total ?? data.length) || data.length < limit) {
|
|
1189
|
-
break;
|
|
1190
|
-
}
|
|
1191
|
-
skip += limit;
|
|
1192
|
-
}
|
|
1193
|
-
return all;
|
|
1243
|
+
const targets = await this.client.targets.listAll({ limit: 100 });
|
|
1244
|
+
return targets.map((target) => ({
|
|
1245
|
+
uuid: target.uuid,
|
|
1246
|
+
name: target.name,
|
|
1247
|
+
status: String(target.status ?? ""),
|
|
1248
|
+
targetType: target.target_type == null ? void 0 : String(target.target_type),
|
|
1249
|
+
active: target.active
|
|
1250
|
+
}));
|
|
1194
1251
|
}
|
|
1195
1252
|
async getTarget(uuid) {
|
|
1196
1253
|
const response = await this.client.targets.get(uuid);
|
|
@@ -1262,11 +1319,22 @@ var SdkRedTeamService = class {
|
|
|
1262
1319
|
if (opts?.jobType) sdkOpts.job_type = opts.jobType;
|
|
1263
1320
|
if (opts?.targetId) sdkOpts.target_id = opts.targetId;
|
|
1264
1321
|
if (opts?.limit) sdkOpts.limit = opts.limit;
|
|
1322
|
+
if (opts?.offset !== void 0) sdkOpts.skip = opts.offset;
|
|
1265
1323
|
const response = await this.client.scans.list(sdkOpts);
|
|
1266
1324
|
return response.data.map(
|
|
1267
1325
|
normalizeJob
|
|
1268
1326
|
);
|
|
1269
1327
|
}
|
|
1328
|
+
async listAllScans(opts) {
|
|
1329
|
+
const sdkOpts = {};
|
|
1330
|
+
if (opts?.status) sdkOpts.status = opts.status;
|
|
1331
|
+
if (opts?.jobType) sdkOpts.job_type = opts.jobType;
|
|
1332
|
+
if (opts?.targetId) sdkOpts.target_id = opts.targetId;
|
|
1333
|
+
if (opts?.limit) sdkOpts.limit = opts.limit;
|
|
1334
|
+
if (opts?.max !== void 0) sdkOpts.max = opts.max;
|
|
1335
|
+
const rows = await this.client.scans.listAll(sdkOpts);
|
|
1336
|
+
return rows.map((row) => normalizeJob(row));
|
|
1337
|
+
}
|
|
1270
1338
|
async abortScan(jobId) {
|
|
1271
1339
|
await this.client.scans.abort(jobId);
|
|
1272
1340
|
}
|
|
@@ -1402,6 +1470,18 @@ var SdkRedTeamService = class {
|
|
|
1402
1470
|
totalItems: pagination?.total_items
|
|
1403
1471
|
};
|
|
1404
1472
|
}
|
|
1473
|
+
async listAllChannels(opts = {}) {
|
|
1474
|
+
const limit = opts.limit ?? 100;
|
|
1475
|
+
const cap = opts.max === 0 ? Number.POSITIVE_INFINITY : opts.max ?? 1e4;
|
|
1476
|
+
const rows = [];
|
|
1477
|
+
for (let offset = 0; rows.length < cap; offset += limit) {
|
|
1478
|
+
const page = await this.listChannels({ ...opts, limit, offset });
|
|
1479
|
+
rows.push(...page.channels.slice(0, cap - rows.length));
|
|
1480
|
+
if (page.channels.length < limit || rows.length >= (page.totalItems ?? Number.POSITIVE_INFINITY))
|
|
1481
|
+
break;
|
|
1482
|
+
}
|
|
1483
|
+
return rows;
|
|
1484
|
+
}
|
|
1405
1485
|
async getChannel(channelId) {
|
|
1406
1486
|
const raw = await this.client.networkBroker.getChannel(channelId);
|
|
1407
1487
|
return normalizeChannel(raw);
|
|
@@ -1474,6 +1554,14 @@ var SdkRedTeamService = class {
|
|
|
1474
1554
|
totalItems: pagination?.total_items
|
|
1475
1555
|
};
|
|
1476
1556
|
}
|
|
1557
|
+
async listAllAdapters(opts = {}) {
|
|
1558
|
+
const sdkOpts = {};
|
|
1559
|
+
if (opts.limit != null) sdkOpts.limit = opts.limit;
|
|
1560
|
+
if (opts.search) sdkOpts.search = opts.search;
|
|
1561
|
+
if (opts.max !== void 0) sdkOpts.max = opts.max;
|
|
1562
|
+
const rows = await this.client.adapters.listAll(sdkOpts);
|
|
1563
|
+
return rows.map((row) => normalizeAdapterListItem(row));
|
|
1564
|
+
}
|
|
1477
1565
|
async getAdapter(uuid) {
|
|
1478
1566
|
const raw = await this.client.adapters.get(uuid);
|
|
1479
1567
|
return normalizeAdapterDetail(raw);
|
|
@@ -2168,6 +2256,7 @@ var ConfigSchema = z.object({
|
|
|
2168
2256
|
aiGwTokenEndpoint: z.string().optional(),
|
|
2169
2257
|
// Tuning
|
|
2170
2258
|
scanConcurrency: z.coerce.number().int().min(1).max(20).default(5),
|
|
2259
|
+
defaultOutput: z.enum(["pretty", "table", "markdown", "csv", "json", "yaml"]).optional(),
|
|
2171
2260
|
// Persistence
|
|
2172
2261
|
dataDir: z.string().default("~/.prisma-airs/runs")
|
|
2173
2262
|
});
|
|
@@ -2200,6 +2289,7 @@ function fromEnv() {
|
|
|
2200
2289
|
aiGwAdminEndpoint: env.PANW_AI_GW_ADMIN_ENDPOINT,
|
|
2201
2290
|
aiGwTokenEndpoint: env.PANW_AI_GW_TOKEN_ENDPOINT,
|
|
2202
2291
|
scanConcurrency: env.SCAN_CONCURRENCY,
|
|
2292
|
+
defaultOutput: env.PANW_CLI_OUTPUT,
|
|
2203
2293
|
dataDir: env.DATA_DIR
|
|
2204
2294
|
};
|
|
2205
2295
|
}
|