@cdot65/prisma-airs-cli 5.9.1 → 5.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/index.js +46 -13
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -10178,7 +10178,8 @@ async function planDlpResourcesRestore(api, input2, destinationTsgId, options =
|
|
|
10178
10178
|
if (prefix.length > 64 || [...prefix].some((char) => char.charCodeAt(0) < 32 || char.charCodeAt(0) === 127))
|
|
10179
10179
|
throw new Error("Invalid --name-prefix");
|
|
10180
10180
|
const conflict = options.onConflict ?? "error";
|
|
10181
|
-
if (!["error", "verify", "skip"].includes(conflict))
|
|
10181
|
+
if (!["error", "verify", "skip", "reconcile"].includes(conflict))
|
|
10182
|
+
throw new Error("Invalid conflict policy");
|
|
10182
10183
|
const patternMap2 = options.patternMap ?? {};
|
|
10183
10184
|
uniqueNames(backup.dictionaries, "dictionary");
|
|
10184
10185
|
uniqueNames(backup.patterns, "data pattern");
|
|
@@ -10406,6 +10407,7 @@ async function planDlpResourcesRestore(api, input2, destinationTsgId, options =
|
|
|
10406
10407
|
patterns: patternPlans,
|
|
10407
10408
|
profiles: profilePlans,
|
|
10408
10409
|
unresolved,
|
|
10410
|
+
suffixOnConflict: conflict === "reconcile",
|
|
10409
10411
|
maxPages: maxPages3
|
|
10410
10412
|
};
|
|
10411
10413
|
const bindings = planBindings(plan);
|
|
@@ -10530,6 +10532,15 @@ function assertUnchanged(current, prior, label) {
|
|
|
10530
10532
|
if (canonical(current ?? null) !== canonical(prior ?? null))
|
|
10531
10533
|
throw new Error(`Destination ${label} changed after planning`);
|
|
10532
10534
|
}
|
|
10535
|
+
function defaultProfileSuffix() {
|
|
10536
|
+
return Math.floor(Math.random() * 16777216).toString(16).padStart(6, "0");
|
|
10537
|
+
}
|
|
10538
|
+
function suffixedProfileName(name, suffix) {
|
|
10539
|
+
const tail = `-${suffix}`;
|
|
10540
|
+
const room = 32 - tail.length;
|
|
10541
|
+
const base = name.length > room ? name.slice(0, room) : name;
|
|
10542
|
+
return `${base}${tail}`;
|
|
10543
|
+
}
|
|
10533
10544
|
async function restoreDlpResources(api, plan, options = {}) {
|
|
10534
10545
|
const result = {
|
|
10535
10546
|
sourceTsgId: plan.sourceTsgId,
|
|
@@ -10544,6 +10555,7 @@ async function restoreDlpResources(api, plan, options = {}) {
|
|
|
10544
10555
|
};
|
|
10545
10556
|
const crossTenant = plan.sourceTsgId !== plan.destinationTsgId;
|
|
10546
10557
|
const emit2 = options.onProgress;
|
|
10558
|
+
const suffixGenerator = options.suffixGenerator ?? defaultProfileSuffix;
|
|
10547
10559
|
const emitItem = (stage, name, action, index, total) => emit2?.({ phase: "item", stage, name, action, index, total });
|
|
10548
10560
|
try {
|
|
10549
10561
|
emit2?.({ phase: "recheck" });
|
|
@@ -10696,23 +10708,39 @@ async function restoreDlpResources(api, plan, options = {}) {
|
|
|
10696
10708
|
emitItem("profiles", item.name, "verified", result.profiles.length, plan.profiles.length);
|
|
10697
10709
|
continue;
|
|
10698
10710
|
}
|
|
10699
|
-
|
|
10700
|
-
|
|
10701
|
-
|
|
10711
|
+
let writeBody = body;
|
|
10712
|
+
let finalName = item.name;
|
|
10713
|
+
let created;
|
|
10714
|
+
try {
|
|
10715
|
+
created = await api.profiles.create(writeBody);
|
|
10716
|
+
} catch (error) {
|
|
10717
|
+
if (!plan.suffixOnConflict || error?.statusCode !== 409)
|
|
10718
|
+
throw error;
|
|
10719
|
+
finalName = suffixedProfileName(item.name, suffixGenerator());
|
|
10720
|
+
writeBody = { ...body, name: finalName };
|
|
10721
|
+
created = await api.profiles.create(writeBody);
|
|
10722
|
+
}
|
|
10723
|
+
if (!created.id) throw new Error(`Restored data profile did not verify: ${finalName}`);
|
|
10724
|
+
result.unverifiedCreates.push({ kind: "profiles", name: finalName, id: created.id });
|
|
10702
10725
|
const readBack = await api.profiles.get(created.id);
|
|
10703
|
-
const echo = compareEcho(
|
|
10704
|
-
if (!echo.matches || readBack.id !== created.id || readBack.name !==
|
|
10726
|
+
const echo = compareEcho(writeBody, readBack);
|
|
10727
|
+
if (!echo.matches || readBack.id !== created.id || readBack.name !== finalName || RETIRED_PROFILE.has(readBack.profile_status ?? "active"))
|
|
10705
10728
|
throw new Error(
|
|
10706
|
-
`Restored data profile did not verify: ${
|
|
10729
|
+
`Restored data profile did not verify: ${finalName} (created id ${created.id}; inspect it before retrying)`
|
|
10707
10730
|
);
|
|
10708
10731
|
if (echo.serverAdded.length)
|
|
10709
10732
|
result.serverAdded.push({
|
|
10710
|
-
resource: `data profile ${
|
|
10733
|
+
resource: `data profile ${finalName}`,
|
|
10711
10734
|
fields: echo.serverAdded
|
|
10712
10735
|
});
|
|
10713
|
-
result.profiles.push({
|
|
10736
|
+
result.profiles.push({
|
|
10737
|
+
name: finalName,
|
|
10738
|
+
action: "created",
|
|
10739
|
+
id: created.id,
|
|
10740
|
+
...finalName !== item.name ? { renamedFrom: item.name } : {}
|
|
10741
|
+
});
|
|
10714
10742
|
result.unverifiedCreates.pop();
|
|
10715
|
-
emitItem("profiles",
|
|
10743
|
+
emitItem("profiles", finalName, "created", result.profiles.length, plan.profiles.length);
|
|
10716
10744
|
}
|
|
10717
10745
|
result.complete = true;
|
|
10718
10746
|
} catch (error) {
|
|
@@ -10860,6 +10888,11 @@ ${markdown ? "\n" : ""}Destination TSG: ${value.destinationTsgId}`
|
|
|
10860
10888
|
)
|
|
10861
10889
|
);
|
|
10862
10890
|
}
|
|
10891
|
+
const renamed = rows("profiles").filter((p) => p.renamedFrom);
|
|
10892
|
+
if (renamed.length) {
|
|
10893
|
+
blocks.push(heading("Renamed around archived-name collisions"));
|
|
10894
|
+
for (const p of renamed) blocks.push(`${p.renamedFrom} -> ${p.name}`);
|
|
10895
|
+
}
|
|
10863
10896
|
if (rows("serverAdded").length) {
|
|
10864
10897
|
blocks.push(heading("Verified server-added fields (source omitted these)"));
|
|
10865
10898
|
for (const item of rows("serverAdded"))
|
|
@@ -10953,7 +10986,7 @@ function register6(dlp) {
|
|
|
10953
10986
|
"Restore DLP resources into the selected tenant, staged dictionaries \u2192 patterns \u2192 profiles; validate all dependencies before changes"
|
|
10954
10987
|
).option("--dry-run", "Read and validate a restore plan without changing destination resources").option("--name-prefix <prefix>", "Prefix restored custom resource names to avoid collisions").option(
|
|
10955
10988
|
"--on-conflict <policy>",
|
|
10956
|
-
"Existing data profile names: error, verify (resume without writes) or
|
|
10989
|
+
"Existing data profile names: error, verify (resume without writes), skip, or reconcile (verify active duplicates, and give archived-name collisions a unique suffix). Profile update is a live server error, so a divergent active profile still fails.",
|
|
10957
10990
|
"error"
|
|
10958
10991
|
).option(
|
|
10959
10992
|
"--pattern-map <source=destination>",
|
|
@@ -10980,8 +11013,8 @@ function register6(dlp) {
|
|
|
10980
11013
|
)
|
|
10981
11014
|
).action(async (file, opts) => {
|
|
10982
11015
|
try {
|
|
10983
|
-
if (!["error", "verify", "skip"].includes(opts.onConflict))
|
|
10984
|
-
throw new CliUsageError("--on-conflict must be error, verify or
|
|
11016
|
+
if (!["error", "verify", "skip", "reconcile"].includes(opts.onConflict))
|
|
11017
|
+
throw new CliUsageError("--on-conflict must be error, verify, skip or reconcile");
|
|
10985
11018
|
if (opts.force && !opts.expectTsg)
|
|
10986
11019
|
throw new CliUsageError("--force requires --expect-tsg <destination-tsg>");
|
|
10987
11020
|
const mappings = patternMap(opts.patternMap);
|
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": "5.
|
|
4
|
+
"version": "5.10.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",
|