@cdot65/prisma-airs-cli 4.3.0 → 4.4.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 +2 -2
- package/dist/cli/index.js +94 -38
- package/package.json +9 -7
package/README.md
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
[](https://github.com/cdot65/prisma-airs-cli/actions/workflows/ci.yml)
|
|
6
6
|
[](https://www.npmjs.com/package/@cdot65/prisma-airs-cli)
|
|
7
7
|
[](https://opensource.org/licenses/MIT)
|
|
8
|
-
[](https://nodejs.org/)
|
|
9
9
|
|
|
10
10
|
**Full operational coverage over Palo Alto Prisma AIRS AI security — guardrail refinement, runtime scanning, AI red teaming, AI Gateway, and model security.**
|
|
11
11
|
|
|
@@ -30,7 +30,7 @@ npm install -g @cdot65/prisma-airs-cli
|
|
|
30
30
|
airs --version
|
|
31
31
|
```
|
|
32
32
|
|
|
33
|
-
Requires **Node.js
|
|
33
|
+
Requires **Node.js 20.17+, 22.13+, or 24+** (exact engine range: `^20.17.0 || ^22.13.0 || >=23.5.0`). Also available via `pnpm add -g`, `npx`, or as a [Docker image](https://github.com/cdot65/prisma-airs-cli/pkgs/container/prisma-airs-cli). See the [installation guide](https://cdot65.github.io/prisma-airs-cli/getting-started/installation/) for details.
|
|
34
34
|
|
|
35
35
|
## Quick Start
|
|
36
36
|
|
package/dist/cli/index.js
CHANGED
|
@@ -4549,6 +4549,12 @@ function registerAiGatewayInventory(root) {
|
|
|
4549
4549
|
);
|
|
4550
4550
|
}
|
|
4551
4551
|
|
|
4552
|
+
// src/cli/commands/aigateway/telemetry.ts
|
|
4553
|
+
import {
|
|
4554
|
+
AI_GW_GROUP_COLUMNS,
|
|
4555
|
+
AI_GW_GROUP_DIMENSIONS
|
|
4556
|
+
} from "@cdot65/prisma-airs-sdk";
|
|
4557
|
+
|
|
4552
4558
|
// src/cli/commands/aigateway/telemetry-filters.ts
|
|
4553
4559
|
import { AIGatewayChartFiltersSchema as AIGatewayChartFiltersSchema2 } from "@cdot65/prisma-airs-sdk";
|
|
4554
4560
|
function addChartFilterOptions(command) {
|
|
@@ -4648,6 +4654,25 @@ function registerFilteredMetric(telemetry, name, description) {
|
|
|
4648
4654
|
await runDetail(command, opts, (client) => client.telemetry[name](options));
|
|
4649
4655
|
});
|
|
4650
4656
|
}
|
|
4657
|
+
var groupDimensions = [...AI_GW_GROUP_DIMENSIONS, "status_code", "users"];
|
|
4658
|
+
function isGroupDimension(value) {
|
|
4659
|
+
return groupDimensions.some((dimension) => dimension === value);
|
|
4660
|
+
}
|
|
4661
|
+
function isGroupColumn(value) {
|
|
4662
|
+
return AI_GW_GROUP_COLUMNS.some((column) => column === value);
|
|
4663
|
+
}
|
|
4664
|
+
function groupOptionsFrom(dimension, opts) {
|
|
4665
|
+
const options = { ...windowFrom(opts), ...chartFiltersFrom(opts) };
|
|
4666
|
+
if (opts.columns !== void 0) {
|
|
4667
|
+
if (dimension === "users") throw new CliUsageError("User grouping does not support --columns");
|
|
4668
|
+
const columns = opts.columns.split(",").map((column) => column.trim());
|
|
4669
|
+
if (!columns.every(isGroupColumn)) {
|
|
4670
|
+
throw new CliUsageError(`Invalid --columns: choose from ${AI_GW_GROUP_COLUMNS.join(", ")}`);
|
|
4671
|
+
}
|
|
4672
|
+
options.columns = columns;
|
|
4673
|
+
}
|
|
4674
|
+
return options;
|
|
4675
|
+
}
|
|
4651
4676
|
function registerAiGatewayTelemetryReads(telemetry) {
|
|
4652
4677
|
const cache = showHelpOnEmpty(telemetry.command("cache").description("Inspect cache telemetry"));
|
|
4653
4678
|
const cacheSummary = addWindowOptions(cache.command("summary").description("Get cache totals"));
|
|
@@ -4675,19 +4700,30 @@ function registerAiGatewayTelemetryReads(telemetry) {
|
|
|
4675
4700
|
(opts) => runDetail(command, opts, (client) => client.telemetry[method](windowFrom(opts)))
|
|
4676
4701
|
);
|
|
4677
4702
|
}
|
|
4678
|
-
const groupBy =
|
|
4679
|
-
|
|
4680
|
-
|
|
4681
|
-
groupBy.action(
|
|
4682
|
-
(dimension, opts) => runDetail(
|
|
4683
|
-
groupBy,
|
|
4684
|
-
opts,
|
|
4685
|
-
(client) => client.telemetry.groupBy(dimension, {
|
|
4686
|
-
...windowFrom(opts),
|
|
4687
|
-
...opts.columns ? { columns: opts.columns.split(",").map((value) => value.trim()) } : {}
|
|
4688
|
-
})
|
|
4703
|
+
const groupBy = addChartFilterOptions(
|
|
4704
|
+
addWindowOptions(
|
|
4705
|
+
telemetry.command("group-by <dimension>").description(`Aggregate telemetry by ${groupDimensions.join(", ")}`).option("--columns <names>", "Comma-separated aggregate columns (not supported for users)")
|
|
4689
4706
|
)
|
|
4690
4707
|
);
|
|
4708
|
+
groupBy.action(async (dimension, opts) => {
|
|
4709
|
+
let options;
|
|
4710
|
+
try {
|
|
4711
|
+
if (!isGroupDimension(dimension)) {
|
|
4712
|
+
throw new CliUsageError(
|
|
4713
|
+
`Invalid grouping dimension: choose from ${groupDimensions.join(", ")}`
|
|
4714
|
+
);
|
|
4715
|
+
}
|
|
4716
|
+
options = groupOptionsFrom(dimension, opts);
|
|
4717
|
+
} catch (error) {
|
|
4718
|
+
failAiGateway(error);
|
|
4719
|
+
}
|
|
4720
|
+
await runDetail(groupBy, opts, (client) => {
|
|
4721
|
+
if (dimension === "users") return client.telemetry.byUser(options);
|
|
4722
|
+
if (dimension === "status_code") return client.telemetry.byStatusCode(options);
|
|
4723
|
+
if (isGroupDimension(dimension)) return client.telemetry.groupBy(dimension, options);
|
|
4724
|
+
throw new CliUsageError("Invalid grouping dimension");
|
|
4725
|
+
});
|
|
4726
|
+
});
|
|
4691
4727
|
registerFilteredMetric(telemetry, "latency", "Get latency telemetry");
|
|
4692
4728
|
const logs = showHelpOnEmpty(telemetry.command("logs").description("Inspect request logs"));
|
|
4693
4729
|
const logsList = addWindowOptions(
|
|
@@ -5220,17 +5256,23 @@ import { randomUUID } from "crypto";
|
|
|
5220
5256
|
import { readFile as readFile4 } from "fs/promises";
|
|
5221
5257
|
import { init, Scanner } from "@cdot65/prisma-airs-sdk";
|
|
5222
5258
|
var DOCTOR_TIMEOUT_MS = 5e3;
|
|
5223
|
-
var
|
|
5259
|
+
var SUPPORTED_NODE_VERSIONS = "^20.17.0 || ^22.13.0 || >=23.5.0";
|
|
5224
5260
|
function checkNodeVersion(version = process.version) {
|
|
5225
|
-
const
|
|
5226
|
-
|
|
5227
|
-
|
|
5261
|
+
const match = /^v?(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$/.exec(version);
|
|
5262
|
+
const [major, minor, patch] = match ? match.slice(1).map(Number) : [];
|
|
5263
|
+
const supported = [major, minor, patch].every(Number.isSafeInteger) && (major === 20 && minor >= 17 || major === 22 && minor >= 13 || major === 23 && minor >= 5 || major >= 24);
|
|
5264
|
+
if (supported) {
|
|
5265
|
+
return {
|
|
5266
|
+
name: "Node.js version",
|
|
5267
|
+
status: "pass",
|
|
5268
|
+
detail: `${version} (${SUPPORTED_NODE_VERSIONS} required)`
|
|
5269
|
+
};
|
|
5228
5270
|
}
|
|
5229
5271
|
return {
|
|
5230
5272
|
name: "Node.js version",
|
|
5231
5273
|
status: "fail",
|
|
5232
|
-
detail: `${version}
|
|
5233
|
-
hint:
|
|
5274
|
+
detail: `${version} does not satisfy ${SUPPORTED_NODE_VERSIONS}`,
|
|
5275
|
+
hint: "Use a supported Node runtime: 20.17+, 22.13+, or 24+ (e.g. via nvm or your package manager)"
|
|
5234
5276
|
};
|
|
5235
5277
|
}
|
|
5236
5278
|
async function checkConfigFile(filePath) {
|
|
@@ -8489,6 +8531,30 @@ function register2(dlp) {
|
|
|
8489
8531
|
});
|
|
8490
8532
|
}
|
|
8491
8533
|
|
|
8534
|
+
// src/cli/renderer/dlp-generate.ts
|
|
8535
|
+
function renderGenerateSummary(summary, output) {
|
|
8536
|
+
if (output === "json") {
|
|
8537
|
+
console.log(JSON.stringify(summary, null, 2));
|
|
8538
|
+
return;
|
|
8539
|
+
}
|
|
8540
|
+
ui.header("DLP Test-File Generation");
|
|
8541
|
+
ui.keyValue([
|
|
8542
|
+
["Output", summary.out],
|
|
8543
|
+
["Seed", summary.seed],
|
|
8544
|
+
["Clean", summary.clean],
|
|
8545
|
+
["Dirty", summary.dirty],
|
|
8546
|
+
["Manifest", summary.manifestPath]
|
|
8547
|
+
]);
|
|
8548
|
+
ui.table(
|
|
8549
|
+
[
|
|
8550
|
+
{ key: "format", label: "Format" },
|
|
8551
|
+
{ key: "clean", label: "Clean" },
|
|
8552
|
+
{ key: "dirty", label: "Dirty" }
|
|
8553
|
+
],
|
|
8554
|
+
Object.entries(summary.byFormat).map(([format, counts]) => ({ format, ...counts }))
|
|
8555
|
+
);
|
|
8556
|
+
}
|
|
8557
|
+
|
|
8492
8558
|
// src/cli/commands/dlp/generate.ts
|
|
8493
8559
|
var ALL_FORMATS = ["pdf", "png", "jpeg", "svg", "docx"];
|
|
8494
8560
|
var OPTIONAL_DEPS_HINT = "DLP generate requires optional dependencies. Install them with: pnpm add sharp pdf-lib docx piexifjs";
|
|
@@ -8517,39 +8583,29 @@ function parseTypes(value) {
|
|
|
8517
8583
|
return types;
|
|
8518
8584
|
}
|
|
8519
8585
|
function register3(parent) {
|
|
8520
|
-
parent.command("generate").description(
|
|
8586
|
+
const command = parent.command("generate").description(
|
|
8521
8587
|
"Generate clean + dirty DLP test files (synthetic sensitive data) across PDF/PNG/JPEG/SVG/DOCX"
|
|
8522
|
-
).option("--types <list>", "Comma list: pdf,png,jpeg,svg,docx (or all)", "all").option("--count <n>", "Clean files per type", "1").option("--out <dir>", "Output base directory", "./temp").option("--techniques <list>", "all or comma list of technique ids", "all").option("--seed <n>", "
|
|
8588
|
+
).option("--types <list>", "Comma list: pdf,png,jpeg,svg,docx (or all)", "all").option("--count <n>", "Clean files per type", "1").option("--out <dir>", "Output base directory", "./temp").option("--techniques <list>", "all or comma list of technique ids", "all").option("--seed <n>", "Safe integer seed for reproducible payloads").option("--output <format>", "Summary format: pretty or json", "pretty").action(async (opts) => {
|
|
8523
8589
|
let types;
|
|
8524
8590
|
try {
|
|
8525
8591
|
types = parseTypes(opts.types);
|
|
8526
8592
|
} catch (err) {
|
|
8527
8593
|
usageError(err instanceof Error ? err.message : String(err));
|
|
8528
8594
|
}
|
|
8529
|
-
const count = Number
|
|
8530
|
-
if (!Number.
|
|
8531
|
-
usageError("--count must be a positive integer");
|
|
8595
|
+
const count = Number(opts.count);
|
|
8596
|
+
if (!/^\d+$/.test(opts.count) || !Number.isSafeInteger(count) || count < 1) {
|
|
8597
|
+
usageError("--count must be a positive safe integer");
|
|
8532
8598
|
}
|
|
8533
8599
|
const techniques = opts.techniques === "all" ? "all" : opts.techniques.split(",").map((t) => t.trim());
|
|
8534
|
-
const seed = opts.seed === void 0 ? void 0 : Number
|
|
8600
|
+
const seed = opts.seed === void 0 ? void 0 : Number(opts.seed);
|
|
8601
|
+
if (seed !== void 0 && (!/^-?\d+$/.test(opts.seed) || !Number.isSafeInteger(seed))) {
|
|
8602
|
+
usageError("--seed must be a safe integer");
|
|
8603
|
+
}
|
|
8535
8604
|
try {
|
|
8605
|
+
const output = await resolveOutput(command, opts, { allowed: ["pretty", "json"] });
|
|
8536
8606
|
const generateCorpus = await loadGenerateCorpus();
|
|
8537
8607
|
const summary = await generateCorpus({ types, count, out: opts.out, techniques, seed });
|
|
8538
|
-
|
|
8539
|
-
console.log(JSON.stringify(summary, null, 2));
|
|
8540
|
-
return;
|
|
8541
|
-
}
|
|
8542
|
-
ui.header("DLP Test-File Generation");
|
|
8543
|
-
ui.keyValue([
|
|
8544
|
-
["Output", summary.out],
|
|
8545
|
-
["Seed", summary.seed],
|
|
8546
|
-
["Clean", summary.clean],
|
|
8547
|
-
["Dirty", summary.dirty],
|
|
8548
|
-
["Manifest", summary.manifestPath]
|
|
8549
|
-
]);
|
|
8550
|
-
for (const [fmt, counts] of Object.entries(summary.byFormat)) {
|
|
8551
|
-
ui.dim(`${fmt.padEnd(5)} clean=${counts.clean} dirty=${counts.dirty}`);
|
|
8552
|
-
}
|
|
8608
|
+
renderGenerateSummary(summary, output);
|
|
8553
8609
|
} catch (err) {
|
|
8554
8610
|
fail(err);
|
|
8555
8611
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cdot65/prisma-airs-cli",
|
|
3
3
|
"packageManager": "pnpm@10.6.5",
|
|
4
|
-
"version": "4.
|
|
4
|
+
"version": "4.4.0",
|
|
5
5
|
"description": "CLI and library for Palo Alto Prisma AIRS — guardrail refinement, AI red teaming, model security scanning, profile audits",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"main": "dist/index.js",
|
|
@@ -14,11 +14,13 @@
|
|
|
14
14
|
},
|
|
15
15
|
"scripts": {
|
|
16
16
|
"build": "tsup",
|
|
17
|
-
"prepublishOnly": "pnpm run build",
|
|
17
|
+
"prepublishOnly": "pnpm audit:prod && pnpm run build",
|
|
18
18
|
"dev": "tsx src/cli/index.ts",
|
|
19
19
|
"test": "vitest run",
|
|
20
20
|
"test:watch": "vitest",
|
|
21
21
|
"test:coverage": "vitest run --coverage",
|
|
22
|
+
"test:release": "node --test .github/scripts/*.test.mjs",
|
|
23
|
+
"audit:prod": "pnpm audit --prod --audit-level low",
|
|
22
24
|
"test:e2e": "vitest run --config vitest.e2e.config.ts",
|
|
23
25
|
"test:e2e:aigateway": "vitest run --config vitest.e2e.config.ts tests/e2e/aigateway-cli.e2e.spec.ts",
|
|
24
26
|
"lint": "biome check --config-path config .",
|
|
@@ -49,7 +51,7 @@
|
|
|
49
51
|
"local:unlink": "pnpm uninstall --global @cdot65/prisma-airs-cli"
|
|
50
52
|
},
|
|
51
53
|
"engines": {
|
|
52
|
-
"node": "
|
|
54
|
+
"node": "^20.17.0 || ^22.13.0 || >=23.5.0"
|
|
53
55
|
},
|
|
54
56
|
"repository": {
|
|
55
57
|
"type": "git",
|
|
@@ -57,13 +59,13 @@
|
|
|
57
59
|
},
|
|
58
60
|
"license": "MIT",
|
|
59
61
|
"dependencies": {
|
|
60
|
-
"@cdot65/prisma-airs-sdk": "0.
|
|
62
|
+
"@cdot65/prisma-airs-sdk": "0.25.0",
|
|
61
63
|
"@inquirer/prompts": "^8.3.0",
|
|
62
64
|
"chalk": "^5.6.2",
|
|
63
65
|
"commander": "^14.0.3",
|
|
64
66
|
"dotenv": "^17.3.1",
|
|
65
|
-
"js-yaml": "^4.
|
|
66
|
-
"nanoid": "^5.
|
|
67
|
+
"js-yaml": "^4.3.2",
|
|
68
|
+
"nanoid": "^5.1.16",
|
|
67
69
|
"p-limit": "^6.0.0",
|
|
68
70
|
"zod": "^3.24.0"
|
|
69
71
|
},
|
|
@@ -84,6 +86,6 @@
|
|
|
84
86
|
"docx": "^9.6.1",
|
|
85
87
|
"pdf-lib": "^1.17.1",
|
|
86
88
|
"piexifjs": "^1.0.6",
|
|
87
|
-
"sharp": "^0.
|
|
89
|
+
"sharp": "^0.35.4"
|
|
88
90
|
}
|
|
89
91
|
}
|